使用内联汇编时出现分段错误(核心转储)错误 [英] segmentation fault(core dumped) error while using inline assembly

查看:87
本文介绍了使用内联汇编时出现分段错误(核心转储)错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在GCC中使用内联汇编.我想将变量内容向左旋转2位(我将变量移至rax寄存器,然后将其旋转2次).我在下面编写了代码,但遇到了分段错误(核心转储)错误. 如果您能帮助我,我将不胜感激.

I'm using inline assembly in GCC. I want to rotate a variable content 2 bits to the left (I moved the variable to the rax register and then rotate it 2 times). I wrote the code below but I faced segmentation fault(core dumped) error. I would be grateful if you could help me.

uint64_t X = 13835058055282163712U;
 asm volatile(
            "movq %0 , %%rax\n"
            "rol %%rax\n"
            "rol %%rax\n"
            :"=r"(X)
            :"r"(X)
         );
printf("%" PRIu64 "\n" , X);

推荐答案

了解内联asm的关键是要了解每个asm语句都有两个部分:

The key to understanding inline asm is to understand that each asm statement has two parts:

  1. 实际的汇编程序材料的文本,编译器将在其中进行文本替换,但不理解.

这是文档中的 AssemblerTemplate (一切由__asm__()中的第一个:决定.)

This is the AssemblerTemplate in the documentation (everything up to the first : in the __asm__()).

用编译器理解的术语描述汇编程序的工作.

A description of what the assembler stuff does, in terms that the compiler does understand.

文档中的 : OutputOperands : InputOperands : Clobbers .

这必须告诉编译器汇编器如何适合编译器围绕它生成的所有代码.代码生成正忙于分配寄存器来保存值,确定执行操作的顺序,将内容移出循环,消除未使用的代码片段,丢弃不再需要的值,等等.

This must tell the compiler how the assembler fits in with all the code which the compiler is generating around it. The code generation is busy allocating registers to hold values, deciding what order to do things in, moving things out of loops, eliminating unused fragments of code, discarding values it no longer needs, and so on.

实际的汇编器是一个黑匣子,它接收此处描述的输入,产生所描述的输出,并且副作用可能是堆积"一些寄存器和/或内存. 必须完整地描述了汇编程序的工作...否则,编译器在模板周围生成的asm将与其冲突并依赖错误的假设.

The actual assembler is a black box which takes the inputs described here, produces the outputs described and as a side effect may 'clobber' some registers and/or memory. This must be a complete description of what the assembler does... otherwise the compiler-generated asm around your template will clash with it and rely on false assumptions.

使用此信息,编译器可以决定汇编器可以使用的寄存器,您应该让其执行.

Armed with this information the compiler can decide what registers the assembler can use, and you should let it do that.

因此,您的片段:

 asm volatile(
            "movq %0 , %%rax\n"
            "rol %%rax\n"
            "rol %%rax\n"
            :"=r"(X)
            :"r"(X)
         );

有一些问题":

  • 您可能基于asm()类似于函数的结果为结果选择了%rax,并且可能希望在%rax中返回结果-但这不是.
  • 您继续使用%rax,编译器可能已经将其分配给了其他内容...因此,实际上,您正在'clobbering'%rax,但是您没有将其告知编译器!
  • 您指定了=r(X)( OutputOperand ),它告诉编译器期望某个寄存器中的输出,并且该输出将是变量X的新值. AssemblerTemplate 中的%0将被为输出选择的寄存器替换.可悲的是,您的程序集将%0视为输入:-(并且输出实际上是在%rax中-如上所述,编译器没有意识到.
  • 您还指定了r(X)( InputOperand ),它告诉编译器安排将变量X的当前值放入某个寄存器中,以供汇编器使用.这将是 AssemblerTemplate 中的%1.不幸的是,您的程序集不使用此输入.

  • you may have chosen %rax for the result on the basis that the asm() is like a function, and may be expected to return a result in %rax -- but that isn't so.
  • you went ahead and used %rax, which the compiler may (well) already have allocated to something else... so you are, effectively, 'clobbering' %rax but you have failed to tell the compiler about it !
  • you specified =r(X) (OutputOperand) which tells the compiler to expect an output in some register and that output will be the new value of the variable X. The %0 in the AssemblerTemplate will be replaced by the register selected for the output. Sadly, your assembly treats %0 as the input :-( And the output is, in fact, in %rax -- which, as above, the compiler is unaware of.
  • you also specified r(X) (InputOperand) which tells the compiler to arrange for the current value of the variable X to be placed in some register for the assembler to use. This would be %1 in the AssemblerTemplate. Sadly, your assembly does not use this input.

即使输出和输入操作数都引用了X,编译器也可能不会将%0设置为与%1相同的寄存器. (这允许它将asm块用作非破坏性操作,使输入的原始值保持不变.如果这不是模板的工作方式,请不要这样写.

Even though the output and input operands both refer to X, the compiler may not make %0 the same register as %1. (This allows it to use the asm block as a non-destructive operation that leaves the original value of an input unmodified. If this isn't how your template works, don't write it that way.

除此之外,一切都很棒.以下内容是安全的,并且避免了mov指令:

Apart from that, things were wonderful. The following is safe, and avoids a mov instruction:

 asm("rol %0\n"
     "rol %0\n"   : "+r"(X));

其中"+r"(X)表示需要一个组合的输入和输出寄存器,取旧值X并返回一个新值.

where "+r"(X) says that there is one combined input and output register required, taking the old value of X and returning a new one.

现在,如果您不想替换X,那么假设结果为Y,您可以:

Now, if you don't want to replace X, then assuming Y is to be the result, you could:

 asm("mov %1, %0\n"
     "rol %0\n"
     "rol %0\n"   : "=r"(Y) : "r"(X));

但是最好让编译器决定是否需要mov或是否只能破坏输入.

But it's better to leave it up to the compiler to decide whether it needs to mov or whether it can just let an input be destroyed.

关于 InputOperands 的一些规则值得一提:

There are a couple of rules about InputOperands which are worth mentioning:

  • 汇编器绝不能覆盖任何 InputOperands -编译器正在跟踪其在哪些寄存器中具有哪些值,并且期望 InputOperands 要保存.

  • The assembler must not overwrite any of the InputOperands -- the compiler is tracking which values it has in which registers, and is expecting InputOperands to be preserved.

编译器希望在任何 OutputOperand 写入之前读取所有 InputOperands .当编译器知道在asm()之后不再使用给定的 InputOperand 并因此可以将 InputOperand 的寄存器分配给时,这一点很重要. OutputOperand .有一种叫做 earcclobber (=&r(foo))的东西可以处理这种小皱纹.

The compiler expects all InputOperands to be read before any OutputOperand is written. This is important when the compiler knows that a given InputOperand is not used again after the asm(), and it can therefore allocate the InputOperand's register to an OutputOperand. There is a thing called earlyclobber (=&r(foo)) to deal with this little wrinkle.

在上面,如果您实际上不再次使用X,则编译器可以将%0%1分配给同一寄存器!但是(冗余)仍将被汇编-记住编译器确实不理解 AssemblerTemplate .因此,通常最好改掉C中的值,而不是asm().请参见 https://gcc.gnu.org/wiki/DontUseInlineAsm C++中的循环移位(旋转)操作的最佳做​​法

In the above, if you don't in fact use X again the compiler could allocate %0 and %1 to the same register! But the (redundant) mov will still be assembled -- remembering that the compiler really doesn't understand the AssemblerTemplate. So, you are in general better off shuffling values around in the C, not the asm(). See https://gcc.gnu.org/wiki/DontUseInlineAsm and Best practices for circular shift (rotate) operations in C++

因此,这是一个主题的四个变体,并生成了代码(gcc -O2):

So here are four variations on a theme, and the code generated (gcc -O2):

// (1) uses both X and Y in the printf() -- does mov %1, %0 in asm()
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi   # address of format string
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi       # X = 99
  X = 99 ;                                     rol    %rsi            # 1st asm
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             mov    %rsi,%rdx       # 2nd asm, compiler using it as a copy-and-rotate
      ) ;                                      rol    %rdx
                                               rol    %rdx
  __asm__("\t mov  %1, %0\n"                   jmpq   0x4010a0 <printf@plt>  # tailcall printf
          "\t rol  %0\n"
          "\t rol  %0\n" : "=r"(Y) : "r"(X)
      ) ;

  printf("%lx %lx\n", X, Y) ;
}

// (2) uses both X and Y in the printf() -- does Y = X in 'C'
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi
  X = 99 ;                                     rol    %rsi       # 1st asm
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             mov    %rsi,%rdx  # compiler-generated mov
      ) ;                                      rol    %rdx       # 2nd asm
                                               rol    %rdx
  Y = X ;                                      jmpq   0x4010a0 <printf@plt>
  __asm__("\t rol  %0\n"
          "\t rol  %0\n" : "+r"(Y)
      ) ;

  printf("%lx %lx\n", X, Y) ;
}

// (3) uses only Y in the printf() -- does mov %1, %0 in asm()
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi
  X = 99 ;                                     rol    %rsi
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             mov    %rsi,%rsi   # redundant instruction because of mov in the asm template
      ) ;                                      rol    %rsi
                                               rol    %rsi
  __asm__("\t mov  %1, %0\n"                   jmpq   0x4010a0 <printf@plt>
          "\t rol  %0\n"
          "\t rol  %0\n" : "=r"(Y) : "r"(X)
      ) ;

  printf("%lx\n", Y) ;
}

// (4) uses only Y in the printf() -- does Y = X in 'C'
void Never_Inline footle(void)               Dump of assembler code for function footle:
{                                              mov    $0x492782,%edi
  unsigned long  X, Y ;                        xor    %eax,%eax
                                               mov    $0x63,%esi
  X = 99 ;                                     rol    %rsi
  __asm__("\t rol  %0\n"                       rol    %rsi
          "\t rol  %0\n" : "+r"(X)             rol    %rsi    # no wasted mov, compiler picked %0=%1=%rsi
      ) ;                                      rol    %rsi
                                               jmpq   0x4010a0 <printf@plt>
  Y = X ;
  __asm__("\t rol  %0\n"
          "\t rol  %0\n" : "+r"(Y)
      ) ;

  printf("%lx\n", Y) ;
}

它有望演示编译器忙于将值分配给寄存器,跟踪其需要保留的值,最大程度地减少寄存器/寄存器的移动,并且通常都很聪明.

which, hopefully, demonstrates the compiler busily allocating values to registers, tracking which values it needs to hold on to, minimizing register/register moves, and generally being clever.

因此,诀窍是与编译器一起工作,要了解: OutputOperands : InputOperands : Clobbers 是您在哪里描述汇编程序在做什么.

So the trick is to work with the compiler, understanding that the :OutputOperands:InputOperands:Clobbers is where you are describing what the assembler is doing.

这篇关于使用内联汇编时出现分段错误(核心转储)错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆