观察调用动态链接库中函数的过程
重定位问题
在程序运行阶段函数A调用函数B时,需要知道函数B位于内存当中的地址,并执行对应区域内的指令才可以完成函数调用,这会面临函数A如何获得函数B地址的问题,想要解决这个问题需要编译器、链接器、动态链接器的共同协作。
编译期
在编译期,编译器只负责将文件内容翻译成指令,它是无法知晓函数B加载内存后的地址,因为无法给call
指令一个明确的地址。在这个阶段,编译器会给一个函数B的假地址,用于给后续阶段提供重定位的位置。
main.o文件反汇编结果: 000000000000000f <main>: f: f3 0f 1e fa endbr64 13: 55 push %rbp 14: 48 89 e5 mov %rsp,%rbp 17: 48 83 ec 10 sub $0x10,%rsp 1b: e8 00 00 00 00 call 20 <main+0x11> 假地址,指向下一条指令 20: 89 45 fc mov %eax,-0x4(%rbp) 23: 8b 45 fc mov -0x4(%rbp),%eax 26: 89 c6 mov %eax,%esi 28: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 2f <main+0x20> 2f: 48 89 c7 mov %rax,%rdi 32: b8 00 00 00 00 mov $0x0,%eax 37: e8 00 00 00 00 call 3c <main+0x2d> 假地址,指向下一条指令 3c: e8 00 00 00 00 call 41 <main+0x32> 假地址,指向下一条指令 41: 89 45 fc mov %eax,-0x4(%rbp) 44: 8b 45 fc mov -0x4(%rbp),%eax 47: c9 leave 48: c3 ret
除了给假地址之外,编译器还会生成包含重定位信息的节。
main.o文件查看重定位信息结果: Relocation section '.rela.text' at offset 0x248 contains 4 entries: Offset Info Type Sym. Value Sym. Name + Addend 00000000001c 000400000004 R_X86_64_PLT32 0000000000000000 test_func1 - 4 00000000002b 000300000002 R_X86_64_PC32 0000000000000000 .rodata - 4 000000000038 000600000004 R_X86_64_PLT32 0000000000000000 printf - 4 00000000003d 000700000004 R_X86_64_PLT32 0000000000000000 test_func2 - 4
链接期
在链接期,链接器根据目标文件中的符号节获取完整的符号信息,通过这些符号信息可以看到与main函数处于同一文件中的test_func1函数有明确的类型(SECTION、1)、查找方式是借助局部符号表(LOCAl),而与main函数处于不同文件的printf函数和test_func2函数就没有明确的类型(NOTYPE、UND-undefined),查找方式是借