linux - ELF64 加载程序如何知道更新 .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 的调用printf
withp 'printf@got.plt'
给出
$1 = (<text from jump slot in .got.plt, no debug info>) 0x1036 <printf@plt+6>
这是相应 PLT 条目中的第二条指令相对于节开头的偏移量。
启动并链接程序后starti
,p 'printf@got.plt'
现在给出
$2 = (<text from jump slot in .got.plt, no debug info>) 0x555555555036 <printf@plt+6>
这是相应PLT条目中第二条指令的绝对地址。
我明白发生了什么以及为什么。我的问题是动态链接器/加载器如何知道将节偏移量(0x1036)更新为绝对地址(0x555555555036)?
p &'printf@got.plt'
链接前的 A
$1 = (<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的阅读,第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 绑定时(默认情况)。
推荐阅读
- python - 原始错误:导入运行时时 DLL 加载失败:
- python - 如何在 python 中将蛋白质序列转换为一种热编码?
- vb.net - 密码生成器。在 ListBox 中显示 8 个不重复的密码
- c# - JsonSchema - Newtonsoft.Json 的绑定重定向问题
- c# - Xamarin 形式:文本从右到左方向时的跨度轻击手势识别器
- google-cloud-platform - 保护 GCP 计算实例
- reactjs - stripejs 自动填充在 chrome 移动设备(三星 Galaxy)上不起作用
- javascript - 根据文件名打开文件夹中的所有文件并在创建的文件夹目录中重新保存
- bash - 如何使用 PowerShell 在 WSL 中启动 Jupyter Notebook?
- scala - ScalaPB:compilePlugin 生成不合适的类