ELF64 加载程序如何知道更新 .got.plt 中的初始地址?
How does the ELF64 loader know to update the initial addresses in .got.plt?
考虑以下程序hello.c
:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("hello");
return 0;
}
文件用gcc -o hello -Og -g hello.c
编译,然后用gdb hello
加载。
检查 GOT 以使用 p 'printf@got.plt'
调用 printf
得到
= (<text from jump slot in .got.plt, no debug info>) 0x1036 <printf@plt+6>
这是相应PLT条目中第二条指令相对于节开头的偏移量。
启动程序并将其与 starti
链接后,p 'printf@got.plt'
现在给出
= (<text from jump slot in .got.plt, no debug info>) 0x555555555036 <printf@plt+6>
对应PLT表项中第二条指令的绝对地址
我明白发生了什么事以及为什么。我的问题是动态linker/loader如何知道将节偏移量(0x1036)更新为绝对地址(0x555555555036)?
A p &'printf@got.plt'
链接前给出
= (<text from jump slot in .got.plt, no debug info> *) 0x4018 <printf@got.plt>
和readelf -r simple
显示此地址的重定位条目
Relocation section '.rela.plt' at offset 0x550 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000004018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
但我对 System V Application Binary Interface AMD64 Architecture Processor Supplement, p.76 的阅读是这些重定位条目仅在 LD_BIND_NOW
为非空时使用。还有其他我错过的搬迁条目吗?相对于 GOT 的最终地址重新设置偏移量的机制是什么?
根据 Drepper 的 How To Write Shared Libraries,动态链接器重定位两种依赖项:
- 相对重定位:对同一对象内位置的依赖。链接器只是将对象的加载地址添加到目标目的地的偏移量。
- 符号重定位:基于复杂符号解析算法的更复杂和昂贵的过程。
对于 PLT 的 GOT,Drepper 指出(§1.5.5)在启动时,动态链接器使用指向相应 PLT 条目的第二条指令的地址填充 GOT 槽。 阅读 glibc 源代码表明链接器确实循环遍历 R_X86_64_JUMP_SLOT
重定位 (elf/do-rel.h:elf_dynamic_do_Rel
) 并增加它们包含的偏移量 (sysdeps/x86_64/dl-machine.h:elf_machine_lazy_rel
):
if (__glibc_likely (r_type == R_X86_64_JUMP_SLOT))
{
/* Prelink has been deprecated. */
if (__glibc_likely (map->l_mach.plt == 0))
*reloc_addr += l_addr;
else
...
当使用惰性 PLT 绑定时(默认情况)。
考虑以下程序hello.c
:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("hello");
return 0;
}
文件用gcc -o hello -Og -g hello.c
编译,然后用gdb hello
加载。
检查 GOT 以使用 p 'printf@got.plt'
调用 printf
得到
= (<text from jump slot in .got.plt, no debug info>) 0x1036 <printf@plt+6>
这是相应PLT条目中第二条指令相对于节开头的偏移量。
启动程序并将其与 starti
链接后,p 'printf@got.plt'
现在给出
= (<text from jump slot in .got.plt, no debug info>) 0x555555555036 <printf@plt+6>
对应PLT表项中第二条指令的绝对地址
我明白发生了什么事以及为什么。我的问题是动态linker/loader如何知道将节偏移量(0x1036)更新为绝对地址(0x555555555036)?
A p &'printf@got.plt'
链接前给出
= (<text from jump slot in .got.plt, no debug info> *) 0x4018 <printf@got.plt>
和readelf -r simple
显示此地址的重定位条目
Relocation section '.rela.plt' at offset 0x550 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000004018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
但我对 System V Application Binary Interface AMD64 Architecture Processor Supplement, p.76 的阅读是这些重定位条目仅在 LD_BIND_NOW
为非空时使用。还有其他我错过的搬迁条目吗?相对于 GOT 的最终地址重新设置偏移量的机制是什么?
根据 Drepper 的 How To Write Shared Libraries,动态链接器重定位两种依赖项:
- 相对重定位:对同一对象内位置的依赖。链接器只是将对象的加载地址添加到目标目的地的偏移量。
- 符号重定位:基于复杂符号解析算法的更复杂和昂贵的过程。
对于 PLT 的 GOT,Drepper 指出(§1.5.5)在启动时,动态链接器使用指向相应 PLT 条目的第二条指令的地址填充 GOT 槽。 阅读 glibc 源代码表明链接器确实循环遍历 R_X86_64_JUMP_SLOT
重定位 (elf/do-rel.h:elf_dynamic_do_Rel
) 并增加它们包含的偏移量 (sysdeps/x86_64/dl-machine.h:elf_machine_lazy_rel
):
if (__glibc_likely (r_type == R_X86_64_JUMP_SLOT))
{
/* Prelink has been deprecated. */
if (__glibc_likely (map->l_mach.plt == 0))
*reloc_addr += l_addr;
else
...
当使用惰性 PLT 绑定时(默认情况)。