目标文件中关于文本部分的每个机器代码都有地址,它将从0到数字.
当链接器链接所有目标文件时,有关指令的地址将发生变化.
我无法看到链接器是否会逐个读取有关文本部分的指令以更改每个指令地址.
Disassembly of section .text:
00000000 <_start>:
0: bf 00 00 00 00 mov $0x0,%edi
5: 8b 04 bd 00 00 00 00 mov 0x0(,%edi,4),%eax
c: 89 c3 mov %eax,%ebx
Run Code Online (Sandbox Code Playgroud)
通过链接
08048074 <_start>:
8048074: bf 00 00 00 00 mov $0x0,%edi
8048079: 8b 04 bd a0 90 04 08 mov 0x80490a0(,%edi,4),%eax
8048080: 89 c3 mov %eax,%ebx
Run Code Online (Sandbox Code Playgroud)
就像0→8048074等等.
好吧,我假设您正在使用一些基于 UNIX 的系统,因为这看起来像是 objdump 命令的输出,但据我所知,这与 ELF 和 PE 文件相关。
让我们开始吧,首先,当您使用 c 时,您将一些模型编译成目标文件,并最终将它们链接在一起,如前所述。例如:
m1.c->m1.om2.c->m2.omain.c+ m2.o+ m1.o->main.exe我们有一些名为m1.c/的 C 程序m2.c,它定义了一些函数,这些函数由 main.c 调用,最终所有函数都被链接并编译到 main.exe 中,这是完全可执行的。
现在,让我们深入了解幕后发生了什么。首先,我想从一个非常重要的开始,在最终的可执行文件中,在我们的示例(main.exe)中,所有地址都是完全解析的虚拟地址(这不一定是真的,因为有一些称为 PIE / PIC 的概念,但目前我们先不谈这个)
因此,在您的可执行文件中,foo其中的函数m1.o将具有一些已解析的地址(例如 0x400100),在 main.exe 中,当调用 foo 时,您将在反汇编中看到诸如
call 0x400100
Run Code Online (Sandbox Code Playgroud)
现在这就是概念上发生的情况,现在让我们看看实际发生的情况。当获取指令时,例如jmp或call指令,某个地址被作为操作数给出,然后处理器的指令寄存器被更改为作为操作数给出的地址,所以你的问题很聪明,如果链接器逐条指令执行,找到需要的指令改了又改吗?不,链接器根本不这样做,它比那聪明得多。
首先,在编译时,编译器生成跳转并调用内部模块(例如,jmp某些应该已经属于内部模块的地址)m1.o首先,在编译时,编译器会生成相对于当前执行指令的这意味着什么?假设我们有一些 if 语句,它将被编译为跳转到某些地址,编译器足够聪明,可以使用相对跳转操作数并在命令之间放置偏移量,因此在链接时链接器甚至不必更改这些地址,代码加载到哪个地址无关,因为调用与当前指令相关,并且某些目标文件的命令之间的偏移量在链接阶段保持静态。
现在事情变得有点复杂了,我们已经介绍了链接器如何避免更改 中的地址m1.o,现在如果两个m2.o中定义的调用函数m1.o都是可执行文件,并且编译器根本无法将它们之间的偏移量假设为他们都不知道他们会链接到多少其他模型,这个问题如何解决?引入了符号表和重定位表。
您以前可能听说过这些,但现在我向您解释一下。在开始讨论之前,我需要警告一下,我更熟悉 ELF 格式文件,但据我所知,概念上 PE 文件的工作方式是相同的。
让我们看一下这个示例代码
#include <stdio.h>
/** file: m1.c **/
extern void goo();
void foo()
{
printf("I am foo()!\n");
goo();
}
Run Code Online (Sandbox Code Playgroud)
和
#include <stdio.h>
/** file: m2.c **/
void goo()
{
printf("I am goo()!\n");
}
Run Code Online (Sandbox Code Playgroud)
在目标文件中编译时m1.o,会有一些表说这样的话
符号:foo-> 在文件内的偏移量 X 处,goo-> 未定义的重定位:goo-> 在文件内的偏移量 Y 处,
现在这意味着编译器生成一个表,收集模型使用的所有函数并确定它们是否已定义 - 它给出了函数在文件中定义的偏移量,如果未定义,则会说明它,
它还会指出,在这个模型中,goo 在偏移量 X 处被调用,并且需要重新定位(我们会明白我的观点,这是你问题的答案!)
当链接到可执行文件时,链接器获取所有目标文件的所有符号,解析其中的某些地址,然后遍历每个目标文件的每个符号表,查找并确定哪些符号尚未定义,然后遍历重定位表并查看对未定义的符号进行了哪些调用,进入文件中的该位置,然后简单地将调用的地址重写为已解析的地址,因此如果之前我们在m1.o
call 0x000000 ;undefined goo address
Run Code Online (Sandbox Code Playgroud)
符号解析后,链接器可能会在重定位表上有一些条目,表示您需要在 X 行上重定位 goo 地址,我们将导致
call 0x400100 ;actual goo address
Run Code Online (Sandbox Code Playgroud)
仅供参考,当出现未定义的引用链接器错误时,这意味着您的符号表中有一些未定义的符号,并且链接器无法解析它的匹配函数定义...另外,如果我没有说清楚,这完全可以工作对于全局变量和静态变量来说也是如此,它们也被认为是符号