为何设置LD_PRELOAD会改变重定位处理顺序并引发段错误?
LD_PRELOAD插入共享对象导致ld-linux.so段错误(重定位顺序异常)
问题背景
我开发了一款I/O插桩工具,通过LD_PRELOAD将共享对象libiot.so注入进程调用栈。设置该环境变量后,动态链接器的重定位处理顺序发生异常,最终导致ld-linux.so触发SEGFAULT(段错误)。
诊断细节
我通过两种方式定位问题根源:
- 启用
LD_DEBUG=all日志分析动态链接过程 - 自行编译glibc-2.28源码,在
ld-linux.so中添加调试打印,通过grep筛选关键流程:- 未设置LD_PRELOAD时:重定位顺序为
libc.so.6→libdl.so.2→libirc.so,程序运行无异常; - 设置LD_PRELOAD=libiot.so时:重定位首先处理
libirc.so,当解析memmove符号时,绑定到了尚未完成重定位的libc.so.6(此时l_relocated=0x0),触发RELINK逻辑后直接崩溃。
- 未设置LD_PRELOAD时:重定位顺序为
相关代码片段(glibc-2.28/sysdeps/x86_64/dl-machine.h)
// 触发问题的关键代码片段 static inline ElfW(Addr) elf_machine_fixup_plt (struct link_map *map, const ElfW(Rela) *reloc, ElfW(Addr) *reloc_addr, ElfW(Addr) value) { ElfW(Addr) *got = (ElfW(Addr) *) reloc_addr; if (*got == value) return *got; /* This is the first time we have resolved this PLT entry. */ if (__glibc_unlikely (GLRO(dl_rtld_map).l_relocated == 0)) { /* If we are relocating the dynamic linker itself, we can't call functions yet. */ *got = value; } else { /* Perform the relocation. */ *got = value; /* XXX We should probably flush the instruction cache here. */ } return *got; }
相关文件ldd输出
# libiot.so的ldd输出 libiot.so: linux-vdso.so.1 (0x00007ffd3a5f3000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9d7c400000) /lib64/ld-linux-x86-64.so.2 (0x00007f9d7c620000) # libirc.so的ldd输出 libirc.so: linux-vdso.so.1 (0x00007ffd3a5f3000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9d7c3f6000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9d7c400000) /lib64/ld-linux-x86-64.so.2 (0x00007f9d7c620000)
预期与诉求
我预期libc.so.6应始终作为第一个被重定位的共享对象,请问如何调整LD_PRELOAD的注入逻辑或动态链接器配置,解决重定位顺序异常引发的段错误?
内容的提问来源于stack exchange,提问作者bauerjlb
相关产品推荐
相关产品推荐

