在gcc内联汇编中加载寄存器? (简单?)

时间:2012-01-30 19:12:24

标签: c linux gcc assembly x86

所以对于任何熟悉汇编的人来说,这似乎是一个非常简单的问题,但是我希望有人可以向我解释下面两段代码之间的区别是什么,因为这会导致分段错误,另一个没有,但(对我来说)他们似乎应该在逻辑上等同。

工作正常:

char *src1; int esi_out, eax;
__asm__
  __volatile__(
     "lodsb\n\t;"
     : "=&S" (esi_out), "=&a" (eax)
     : "0" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x\n", *src1, src1, esi_out, eax);

并打印:

src1 w @ 0x7fffce186959, esi_out: ce18695a, eax: ce186977

所以我理解这段代码应该将src1(这是一个地址)的值加载到ESI中,将该值复制到EAX中,将ESI中的地址递增1个字节,然后在退出时将这些值输出到本地C变量esi_out和eax。 src1和esi_out看起来是正确的,但是eax似乎已经关闭了。这是怎么回事?

代码的第二位是我们看到一个我无法理解的段错误:

__asm__
  __volatile__(
        "movl %%ebx, %%esi\n\t;"
        //"lodsb\n\t;"                                                                                                                  
        : "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
        : "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n", 
                        *src1, src1, esi_out, eax, ebx);

将lodsb命令注释掉后,它会产生:

src1 w @ 0x7ffff093b959, esi_out: f093b959, eax: f093b959, ebx: f093b959

并且没有注释掉lodsb命令,它就是段错误。根据我的想法,直接加载ESI值,如上面的第一种情况,并将其加载到EBX然后将其移入ESI应该是等价的,不是吗?

我错过了什么?为什么写入EAX的值会被看作?我将等效的程序直接编写到程序集中并使用gdb逐步完成它并且工作正常。

非常感谢任何见解。

3 个答案:

答案 0 :(得分:3)

从printf中%p的输出外观来看,你正在编译64位,但你的asm代码假定为32位。尝试

__asm__
  __volatile__(
        "movl %%rbx, %%rsi\n\t;"
        "lodsb\n\t;"    
        : "=&S" (esi_out), "=&b" (ebx), "=&a" (eax)
        : "1" (src1)
);
printf("src1 %c @ %p, esi_out: %x, eax: %x, ebx: %x\n", 
                        *src1, src1, esi_out, eax, ebx);

您还应将esi_outebx声明为指针类型(void*char*)或uintptr_t

发生的事情是lodsb在64位模式下使用RSI作为其源地址,但是你只将指针值的低32位放入RSI,因此它不包含有效地址,因此是段错误。正如Slagh所说,只有寄存器(al)的低8位被lodsb修改。您应该屏蔽其他位(eax和0xff),清除rax(xor%rax,%rax)或将eax声明为char

如果您感到意外,您还应该找到有关x86_64程序集的资源。

答案 1 :(得分:1)

你的第一个问题 - lodsb正在修改AL但不修改EAX的剩余部分。您的EAX值ce186977中的最后一个字节是0x77,对于小写的“w”是十六进制。

我很遗憾不熟悉GCC汇编语法 - 当您运行代码并跨过movl时,哪个寄存器是目标?对我来说,看起来EBX正在编写ESI。你的代码之前在EBX中有什么用?

答案 2 :(得分:1)

您有一些错误:

您使用的是64位架构,int不足以容纳指针。 esi_out变量应为64位宽,或者更好地使用ptrdiff_t。你应该将它命名为rsi_out; - )

64位模式下的lodsb指令隐含地引用rsi而不是esi。在movl %%ebx, %%esi之前的指令中,您只设置rsi的下半部分,上半部分被清除。将其更改为movq %%rbx, %%rsi