从目标代码到可执行文件

问题描述 投票:0回答:3

我想知道当我们使用链接器获取目标代码的可执行版本时会发生什么。

我认为 Linux 和 Windows 的链接器工作不一样,我在 Linux 上。

assembly linker
3个回答
2
投票

目标代码缺乏有关全局的信息。它包含函数的可执行代码,但对其他外部函数以及全局数据的所有引用都不能成为实际指令的一部分,因为它们的地址未知。因此,所有这些引用都留空(例如,仅在目标代码中填充零字节)并用符号名称进行注释。

链接器的工作是查看所有丢失的符号名称并将它们与所有导出的名称(即目标文件提供的函数和全局数据)进行匹配,然后为每个数据找到永久位置,最后重写所有代码将零字节替换为最终存储数据(函数和全局变量)的实际地址。


例如,考虑这段 C 代码:

extern int a;
extern int bar(int);     // "extern" is redundant here
static int zip(int);

int foo(int x, int y)
{
    return 2 * x + 3 * y + zip(x - y) + a * bar(x + y);
}

int zip(int n)
{
    return 2 * (n + 1) - (n - 1) / 2;
}

此代码导出一个符号

foo
,将其提供给链接到此翻译单元的任何人。它还缺少两个符号:
a
bar
。在实现
foo
的代码中,对
a
bar
的引用留空,只有当链接器知道这些实际数据所在的位置时才能由链接器填充。

这是 GCC 使用

-O3
为 x86 生成的机器代码:

0000000000000000 <foo>:
   0:   89 f9                   mov    ecx,edi
   2:   8d 04 76                lea    eax,[rsi+rsi*2]
   5:   53                      push   rbx
   6:   29 f1                   sub    ecx,esi
   8:   8d 51 ff                lea    edx,[rcx-0x1]
   b:   8d 1c 78                lea    ebx,[rax+rdi*2]
   e:   01 f7                   add    edi,esi
  10:   89 d0                   mov    eax,edx
  12:   c1 e8 1f                shr    eax,0x1f
  15:   01 c2                   add    edx,eax
  17:   d1 fa                   sar    edx,1
  19:   f7 da                   neg    edx
  1b:   8d 44 4a 02             lea    eax,[rdx+rcx*2+0x2]
  1f:   01 c3                   add    ebx,eax
  21:   e8 00 00 00 00          call   26 <foo+0x26>
  22:                                  R_X86_64_PC32       bar-0x4
  26:   0f af 05 00 00 00 00    imul   eax,DWORD PTR [rip+0x0]        # 2d <foo+0x2d>
  29:                                  R_X86_64_PC32       a-0x4
  2d:   01 d8                   add    eax,ebx
  2f:   5b                      pop    rbx
  30:   c3                      ret    

注意字节 22 和 29:操作数保留为零,但有一个注释告诉链接器要填充的符号名称。


1
投票

除了 Kerrek 的回答之外:链接器的工作在某种程度上依赖于操作系统。例如,处理外部引用(来自 .so 或 .dll 文件)的方式取决于操作系统,不同的段(数据、代码等)如何放置在文件中也可能取决于操作系统。

可执行文件的标头(也由链接器生成)是特定于操作系统的,定义文件类型以及在哪里可以找到不同的段。 Linux 中的可执行文件以“ELF”标头开头,在 Windows 中以“MZ”标头开头(这些是可以在文件开头找到的标识字符)。


0
投票

我认为 Linux 和 Windows 的链接器工作不一样

只是对 Kerrek SB 的答案进行一些补充:

链接器在所有操作系统上的工作方式都相同。只是对象文件和二进制文件的文件格式不同。

© www.soinside.com 2019 - 2024. All rights reserved.