因此,对于任何精通汇编的人来说,这似乎是一个非常简单的问题,但我希望有人能向我解释以下两段代码之间的区别,因为其中一段会导致段错误,并且另一个没有,但(对我来说)它们似乎在逻辑上应该是等价的。
工作正常:
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 单步执行它,它工作正常。
如有任何见解,我们将不胜感激。
最佳答案
从 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_out
和 ebx
声明为指针类型(void*
或 char*
)或uintptr_t
.
发生的事情是 lodsb 在 64 位模式下使用 RSI 作为其源地址,但您只将指针值的低 32 位放入 RSI,因此它不包含有效地址,因此出现段错误。正如 Slagh 所说,只有 a 寄存器 (al) 的低 8 位被 lodsb 修改。您应该屏蔽其他位(eax 和 0xff),清除 rax(xor %rax,%rax)或将 eax 声明为 char
。
如果这让您感到惊讶,您还应该找到有关 x86_64 程序集的一般资源。
关于c - 在 gcc 内联汇编中加载寄存器? (简单的?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9069369/