我想知道它是如何工作的。
假设我们有以下代码段:
0000000000400400 <printf@plt-0x10>:
400400: ff 35 02 0c 20 00 pushq 0x200c02(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
400406: ff 25 04 0c 20 00 jmpq *0x200c04(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
40040c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400410 <printf@plt>:
400410: ff 25 02 0c 20 00 jmpq *0x200c02(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
400416: 68 00 00 00 00 pushq $0x0
40041b: e9 e0 ff ff ff jmpq 400400 <_init+0x20>
....
40053b: e8 d0 fe ff ff callq 400410 <printf@plt>
首先调用printf存根(printf @ plt),然后采用位于0x601018(在GOT内)的地址以跳入该地址。
假设这是第一次调用printf:我们找到的值将是0x400416,也就是说下一条指令,对吧?
遵循代码,将值0压入堆栈,然后跳转到0x400400。在这里,一个GOT地址被推送(0x601008),然后跳到下一个(0x601010):为什么?里面到底有什么?
此外:究竟何时调用动态链接器,如何调用?
最佳答案
您已经停止在答案处追踪;)
如果查看最终指针(0x601010
),您应该会看到它指向_dl_runtime_resolve
。 plt
条目中的第一个推送将重定位索引存储在堆栈中(这标识了要对其进行操作的条目),而第二次获得的推送是模块的链接映射。 _dl_runtime_resolve
通常是链接器中的汇编函数(对于x86-64,它驻留在glibc/sysdeps/x86_64/dl-trampoline.S中),在保留一些寄存器后,它会调用_dl_fixup
并完成所有解析工作(包括更新GOT中的指针) ,因此后续调用将直接转到已解析的函数)。最后,_dl_runtime_resolve
跳转到现在解析的函数,因此它实际上也被执行了:)