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

标签 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 单步执行它,它工作正常。

如有任何见解,我们将不胜感激。

最佳答案

从 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 所说,只有 a 寄存器 (al) 的低 8 位被 lodsb 修改。您应该屏蔽其他位(eax 和 0xff),清除 rax(xor %rax,%rax)或将 eax 声明为 char

如果这让您感到惊讶,您还应该找到有关 x86_64 程序集的一般资源。

关于c - 在 gcc 内联汇编中加载寄存器? (简单的?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9069369/

相关文章:

linux - ld 在将 .asm 文件汇编和链接到 64 位可执行文件后找不到符号 _start 错误

C++ 从 'char' 到 'const char' 的无效转换

c++ - 如何在 autoconf 中检查 clang 的支持编译标志

c - 通过 C 中的几个函数传递结构?

linux - Scons:如何强制重建?

c - GCC 如何阻止程序内的系统调用?

c - c 中的 "assignment makes integer from pointer without a cast "警告

linux - 使用 $() 将多个命令输出到变量 Bash 脚本中

php - 如何逐行读取 system() 输出的 PHP

c++ - 如何修复警告 : extended initializer lists?