在目标文件中分配指令的地址

当我们使用gcc -c编译任何c代码,并执行objdump -d <filename>.o我们会看到

 Disassembly of section .text: 0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: 48 8d 45 fc lea -0x4(%rbp),%rax c: 48 89 c7 mov %rax,%rdi f: b8 00 00 00 00 mov $0x0,%eax . . . 

但是在链接之后,偏移改变为gcc -o prog -L/library/path -llibrary *.o

 0000000000400644 <main>: 400644: 55 push %rbp 400645: 48 89 e5 mov %rsp,%rbp 400648: 48 83 ec 10 sub $0x10,%rsp 40064c: 48 8d 45 fc lea -0x4(%rbp),%rax 400650: 48 89 c7 mov %rax,%rdi 400653: b8 00 00 00 00 mov $0x0,%eax 

链接完成后,如何计算偏移量?

我们基本上得到3组地址,1.编译之后2.链接之后3.加载之后

上述地址如何相关?

gcc -c会运行编译器本身(某些cc1 ),它会产生汇编代码,然后汇编 ( as )。 使用gcc -v -c来查看究竟发生了什么。

编译器(实际上是cc1 )正在将你的C代码翻译成汇编代码。

然后汇编程序( as )正在将汇编代码翻译成目标文件 ( 可执行和可链接的格式 ,即ELF)。

ELF对象文件包含重定位指令或指令的字节段(例如代码或.text段或.data段),并定义(&使用)符号引用。 重定位是依赖于处理器的,例如参见重定位类型列表和x86-64 ABI规范 – 用于应用程序二进制接口 。 这些重定位由链接器 ( ld ,由gcc启动)处理。 阅读Levine的Linkers和Loader书 。 所以ELF对象文件包含带重定位指令和符号表的字节。

链接器将根据重定位代码修改一些机器指令(或其他数据)。

你必须记住,目标文件只包含你的代码,所以它将始终在零偏移。

当你链接你从其他来源添加模块,如运行时初始化和库函数。 您不知道这些对象的大小,或者它们将放置在生成的可执行文件中的位置,因此无法亲自计算代码的不同部分的偏移量。 另外,如果你有多个目标文件,链接器可能会重新排列它们。

代码在运行时的确切虚拟地址是什么,部分取决于链接器,但主要取决于操作系统以及地址空间随机化等。

每个平台/架构都有其特定的可执行内存布局。 其链接器的任务是以适合您的平台/体系结构的方式组合/重定位目标文件。

使用gcc工具链,这个适配是通过链接器脚本完成的。 如果您好奇,可以查看实现(默认链接脚本通常位于名为ldscripts的目录中,并带有.xbn文件扩展名)。