目标文件中关于文本部分的每个机器代码都有地址,它将从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
链接
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
就像0→8048074等等。
答案 0 :(得分:2)
好吧,所以我假设您正在使用一些基于unix的系统,因为这看起来像是objdump命令的输出,但我知道这对于ELF和PE文件都是相关的。
所以让我们开始吧,首先当你使用c时,你将一些模型编译成目标文件并最终将它们链接在一起,如前所述。 e.g:
m1.c
- &gt; m1.o
m2.c
- &gt; m2.o
main.c
+ m2.o
+ m1.o
- &gt; main.exe
我们有一些名为m1.c
/ m2.c
的c程序定义了一些由main.c调用的函数,最终所有函数都被链接并编译成一个完全可执行的main.exe。 / p>
现在,让我们潜入并了解引擎盖下发生的事情。首先,我想从一个非常重要的开始开始,在最终的可执行文件中,在我们的示例中(main.exe)所有地址是完全解析的虚拟地址(这不一定是真的,因为一些名为PIE / PIC的概念,但现在让我们不要进入它)
因此在你可执行的范围内,foo
中的函数m1.o
会有一些已解析的地址(例如0x400100),当你调用foo时你会在main.exe中看到反汇编诸如
call 0x400100
现在这就是概念上发生的事情,现在让我们进入实际发生的事情。
在获取指令时,例如jmp
或call
指令,某个地址作为操作数给出,然后处理器的指令寄存器被更改为以操作数给出的地址,所以你的问题很聪明,应该是链接器按指令进行指令,找到需要更改的内容并进行更改?好吧,链接器根本就不这样做,它比那更聪明。
首先,在编译时,编译器生成跳转并调用内部模块(例如jmp
到相对于当前指令执行应该已经属于我们示例中的m1.o
的某个地址)。那是什么意思?
让我们说我们有一些if语句,它将被编译为跳转到某些地址,编译器足够智能使用相对跳转操作数并在命令之间放置偏移量,因此当链接链接器时不会甚至必须改变它们,它与加载代码的地址无关,因为调用是相对于当前指令的,并且某些目标文件的命令之间的偏移在链接阶段保持静态。
现在,事情变得更加复杂,我们已经介绍了链接器如何避免更改m1.o
内的地址,现在如果m2.o
调用m1.o
中定义的函数{1}}两者都是可执行文件,并且地球上没有办法让编译器可以假设它们之间的偏移,因为它们都不知道它们将链接多少其他模型,这是如何解决的?引入符号和重定位表。
您之前可能已经听说过这些,但现在我将向您解释这些问题。 在进入它之前,我需要警告我对ELF格式文件比较熟悉,但据我所知,概念上PE文件的工作方式相同。
让我们看看这个示例代码
#include <stdio.h>
/** file: m1.c **/
extern void goo();
void foo()
{
printf("I am foo()!\n");
goo();
}
和
#include <stdio.h>
/** file: m2.c **/
void goo()
{
printf("I am goo()!\n");
}
在目标文件中编译m1.o
时,会出现一些类似这样的表
符号:foo
- &gt;在文件中的偏移量X,goo
- &gt;未定义
重新定位:goo
- &gt;在文件中的偏移量Y,
现在这意味着编译器会生成一个表,该表收集模型使用的所有函数并确定它们是否已定义 - 它给出了函数在字段中定义的偏移量,如果它不是定义它会陈述它,
它也会说在这个模型中goo被调用偏移X并且它需要被重新定位(我们已经达到了我的观点,它是你问题的答案!)
当链接到可执行文件时,链接器获取所有目标文件的所有符号,解析其中的一些地址,然后遍历每个目标文件的每个符号表,查看并确定哪些符号尚未定义,然后通过重定位表并查看对未定义的符号进行哪些调用,将其放在文件中的那个位置,然后简单地将调用的地址重新写入已解析的地址,这样如果我们在{{{ 1}}
m1.o
在符号解析之后,链接器可能在重定位表上有一些条目,说你需要重新定位第X行的goo地址,我们将导致
call 0x000000 ;undefined goo address
仅供参考,当有一个未定义的引用链接器错误时,它意味着你的符号表中有一些未定义的符号,并且链接器无法为它解析匹配的函数定义...如果我还没有弄清楚自己,这对全局变量和静态变量的作用完全相同,它们也被认为是符号