手动构建x64 ELF文件加载时ld-linux-x86-64.so段错误求助
手动构建x64 ELF文件在新发行版加载崩溃的修复方案
问题背景
- 手动构建的x64 ELF文件
p64在Ubuntu 20.04.x稳定运行10年,近3个月在Ubuntu 22.04、Mint 19.3、Fedora 36、MX Linux MX21上加载崩溃 dmesg显示崩溃发生在ld-linux-x86-64.so.2中,触发地址0的段错误- 二进制包含10种老旧/罕见重定位格式,未使用GOT/PLT结构
- 测试现象:
- 直接运行
./p64崩溃 - 通过
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2直接启动可正常运行 - 带参数
-c -test启动时仍崩溃
- 直接运行
核心原因分析
新发行版的ld-linux动态链接器对老旧重定位格式的兼容性逻辑发生了变化,尤其是处理无GOT/PLT结构的二进制时,在命令行参数加载流程中未正确处理罕见重定位,导致空指针引用(地址0的段错误)。
修复方案:添加伪GOT/PLT结构
步骤1:准备工具
使用objcopy、readelf、objdump等ELF分析工具,配合二进制编辑工具(如hexedit或自定义Python脚本)。
步骤2:调整段表,预留GOT/PLT空间
- 执行
readelf -l p64查看现有段表,找到空闲虚拟地址区域,或扩展现有可执行/可读写段的大小,预留出至少64字节的空间(足够基础GOT/PLT结构使用) - 添加两个新段:
.got段:设置权限为可读写(SHF_WRITE | SHF_ALLOC),虚拟地址对齐到8字节.plt段:设置权限为可执行(SHF_EXECINSTR | SHF_ALLOC),虚拟地址对齐到16字节
步骤3:添加GOT/PLT重定位项
- 执行
readelf -r p64列出所有现有重定位条目 - 为每个需要动态解析的符号,添加
R_X86_64_GLOB_DAT(用于数据引用)或R_X86_64_JUMP_SLOT(用于函数调用)类型的重定位,指向新创建的GOT表项
步骤4:写入PLT存根代码
在.plt段写入标准x86_64 PLT存根模板:
.plt: jmp QWORD PTR [rip+0x0] ; 跳转到GOT中存储的函数地址 push 0x0 ; 对应符号的重定位索引 jmp .plt+0x10 ; 跳转到动态链接器的PLT解析入口
同时将GOT表的第一个条目设置为动态链接器的PLT解析器地址(可从正常ELF文件的readelf -d输出中获取)。
步骤5:更新动态段信息
修改.dynamic段,添加DT_PLTGOT条目,指向新创建的GOT段起始地址,确保动态链接器能识别GOT/PLT结构。
步骤6:验证修复
- 执行
readelf -l -r -d p64确认GOT/PLT段、重定位项和动态段条目已正确配置 - 直接运行
./p64和./p64 -c -test验证是否崩溃 - 查看
dmesg确认无地址0的段错误输出
临时替代方案
如果暂时无法修改二进制,可使用Ubuntu 20.04的旧版动态链接器启动:
/path/to/ubuntu20.04/ld-linux-x86-64.so.2 ./p64 -c -test
此方案仅为临时 workaround,长期仍需修改二进制适配新链接器。
内容的提问来源于stack exchange,提问作者Pete Lomax
相关产品推荐
相关产品推荐

