尝试理解汇编代码中的调用过程

Try to understand calling process in assembly code

我用C写了一个很简单的程序,试着理解函数调用过程。

#include "stdio.h"

void Oh(unsigned x) {
    printf("%u\n", x);
}

int main(int argc, char const *argv[])
{
    Oh(0x67611c8c);
    return 0;
}

而它的汇编代码好像是

0000000100000f20 <_Oh>:
   100000f20:   55                      push   %rbp
   100000f21:   48 89 e5                mov    %rsp,%rbp
   100000f24:   48 83 ec 10             sub    [=11=]x10,%rsp
   100000f28:   48 8d 05 6b 00 00 00    lea    0x6b(%rip),%rax        # 100000f9a <_printf$stub+0x20>
   100000f2f:   89 7d fc                mov    %edi,-0x4(%rbp)
   100000f32:   8b 75 fc                mov    -0x4(%rbp),%esi
   100000f35:   48 89 c7                mov    %rax,%rdi
   100000f38:   b0 00                   mov    [=11=]x0,%al
   100000f3a:   e8 3b 00 00 00          callq  100000f7a <_printf$stub>
   100000f3f:   89 45 f8                mov    %eax,-0x8(%rbp)
   100000f42:   48 83 c4 10             add    [=11=]x10,%rsp
   100000f46:   5d                      pop    %rbp
   100000f47:   c3                      retq
   100000f48:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
   100000f4f:   00

0000000100000f50 <_main>:
   100000f50:   55                      push   %rbp
   100000f51:   48 89 e5                mov    %rsp,%rbp
   100000f54:   48 83 ec 10             sub    [=11=]x10,%rsp
   100000f58:   b8 8c 1c 61 67          mov    [=11=]x67611c8c,%eax
   100000f5d:   c7 45 fc 00 00 00 00    movl   [=11=]x0,-0x4(%rbp)
   100000f64:   89 7d f8                mov    %edi,-0x8(%rbp)
   100000f67:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
   100000f6b:   89 c7                   mov    %eax,%edi
   100000f6d:   e8 ae ff ff ff          callq  100000f20 <_Oh>
   100000f72:   31 c0                   xor    %eax,%eax
   100000f74:   48 83 c4 10             add    [=11=]x10,%rsp
   100000f78:   5d                      pop    %rbp
   100000f79:   c3                      retq

嗯,我不太明白参数传递过程,因为只有一个参数传递给Oh函数,我可以理解这个

100000f58:  b8 8c 1c 61 67          mov    [=12=]x67611c8c,%eax

下面的代码有什么作用?为什么是限制性商业惯例?它不是在 X86-64 汇编中被遗弃了吗?如果是 x86 风格的程序集,如何使用 clang 生成 x86-64 风格的程序集?如果是x86,没关系,谁能帮我逐行解释下面的代码?

100000f5d:  c7 45 fc 00 00 00 00    movl   [=13=]x0,-0x4(%rbp)
100000f64:  89 7d f8                mov    %edi,-0x8(%rbp)
100000f67:  48 89 75 f0             mov    %rsi,-0x10(%rbp)
100000f6b:  89 c7                   mov    %eax,%edi
100000f6d:  e8 ae ff ff ff          callq  100000f20 <_Oh>

如果打开优化,您可能会获得更清晰的代码,也可能不会。但是,这就是它的作用。

%rbp寄存器被用作帧指针,即指向原始栈顶的指针。它保存在堆栈中,存储并在最后恢复。它远没有在 x86_64 中被删除,而是被添加到那里; 32 位等效项是 %ebp.

保存此值后,程序通过从堆栈指针中减去堆栈分配十六个字节。

然后是一系列非常低效的拷贝,将Oh()的第一个参数设置为printf()的第二个参数和格式字符串的常量地址(相对于指令指针)作为 printf() 的第一个参数。请记住,在此调用约定中,第一个参数在 %rdi 中传递(或 %edi 用于 32 位操作数),第二个参数在 %rsi 中传递。这可以简化为两条指令。

调用printf()后,程序(不必要地)将return值保存在栈上,恢复栈和帧指针,returns.

main()中,有类似的代码来设置堆栈帧,然后程序保存argcargv(不必要),然后将常量参数移动到Oh 通过 %eax 进入它的第一个参数。这本可以优化为一条指令。然后调用 Oh()。在 return 上,它将其 return 值设置为 0,清理堆栈,然后 returns.

您询问的代码执行以下操作:将常量 32 位值 0 存储在堆栈上,将 32 位值 argc 保存在堆栈上,将 64 位指针 argv 放在堆栈上(main() 的第一个和第二个参数),并将它要调用的函数的第一个参数设置为 %eax,它之前已经加载了一个常量。这对这个程序来说都是不必要的,但如果在调用后需要使用 argcargv ,那么这些寄存器将被破坏,这将是必要的。没有充分的理由使用两步而不是一步来加载常量。

正如 Jester 提到的,您仍然有帧指针(以帮助调试),因此逐步执行 main:

0000000100000f50 <_main>:

首先我们进入一个新的栈帧,我们必须保存基址指针并将栈移动到新的基址。此外,在 x86_64 中,堆栈帧必须与 16 字节边界对齐(因此将堆栈指针移动 0x10)。

       100000f50:   push   %rbp        
       100000f51:   mov    %rsp,%rbp
       100000f54:   sub    [=11=]x10,%rsp

正如你所说,x86_64通过寄存器传递参数,所以将参数加载到寄存器中:

       100000f58:   mov    [=12=]x67611c8c,%eax

???需要帮助

       100000f5d:   movl   [=13=]x0,-0x4(%rbp)

来自here:"Registers RBP, RBX, and R12-R15 are callee-save registers",所以如果我们想拯救其他抵抗者,那么我们必须自己动手....

       100000f64:   mov    %edi,-0x8(%rbp)
       100000f67:   mov    %rsi,-0x10(%rbp)

不太清楚为什么我们不把它加载到 %edi 中,它需要在调用开始的地方,但我们现在最好把它移到那里。

       100000f6b:   mov    %eax,%edi

调用函数:

       100000f6d:   callq  100000f20 <_Oh>

这是 return 值(在 %eax 中传递),xor 是比加载 0 更小的指令,因此是 cmmon 优化:

       100000f72:   xor    %eax,%eax

清理我们之前添加的堆栈帧(不太确定为什么我们在不使用它们时将这些寄存器保存在上面)

       100000f74:   add    [=18=]x10,%rsp
       100000f78:   pop    %rbp
       100000f79:   retq