链接前在汇编语言中的函数调用

Function call in the assembly language before linking

我正在查看编译器生成的汇编代码。我正在使用 C 编程语言和 GCC 编译器。

我用 C 编写了一个函数,它通过调用另一个函数将两个数字相加,并将结果存储在作为参数传递给该函数的指针所指向的变量中。

void add_two_num(int x, int y, int * dest)
{
  int val;

  val = dummy(x, y);
  *dest = val;
}

我将源代码编译为目标代码(链接未完成),然后使用 objdump -d

反汇编代码

call d <add_two_num+0x9> 行中的数字 +0x9 是什么意思?
当该行将被实际函数调用替换时,这在链接阶段有用吗?

file format elf64-x86-64

0000000000000004 <add_two_num>:
   4:   53                      push   %rbx
   5:   48 89 d3                mov    %rdx,%rbx
   8:   e8 00 00 00 00          call   d <add_two_num+0x9>
   d:   89 03                   mov    %eax,(%rbx)
   f:   5b                      pop    %rbx
  10:   c3                      ret  

注意调用行上的代码字节;立即操作数全为零。这显然是链接器的占位符。

add_two_num+9 是因为 call 上的直接操作数是调用目标相对于调用指令末尾的偏移量。因此零操作数意味着调用目标是调用后的下一条指令,恰好是 add_two_num 偏移量 9 处的 mov。反汇编器尽力解释调用目标的含义,并且它看到调用目标(技术上)在 add_two_num.

您正在查看目标文件。这个文件还没有链接,外部函数的地址也没有填写。您可以在指令编码中看到这一点:00 00 00 00 是稍后要修补的实际调用目标的虚拟对象。

不幸的是,objdump 不够聪明,无法在 x86 上了解这一点,因此它会反汇编,就好像偏移量实际上是 00 00 00 00,即调用转到下一条指令。该指令在最后一个标签之后 0x9 字节,因此您看到它将此地址解释为 add_two_num+0x9.

您可以将 -r 选项传递给 objdump 以让它显示您的搬迁信息。这样你就知道实际调用的是什么函数。它看起来像这样:

0000000000000000 <add_two_num>:
   0:   53                      push   %rbx
   1:   48 89 d3                mov    %rdx,%rbx
   4:   e8 00 00 00 00          call   9 <add_two_num+0x9>
            5: R_X86_64_PLT32   dummy-0x4
   9:   89 03                   mov    %eax,(%rbx)
   b:   5b                      pop    %rbx
   c:   c3                      ret