加载ELF64头引发段错误的原因排查
问题原因分析
1. 普通用户的内存映射安全限制
Linux内核默认禁止普通用户进程映射虚拟地址空间最起始的低地址区域(通常是0x0到0x1000左右),这是一项安全机制——防止空指针引用意外访问到可读写内存,从而被利用来触发漏洞。当你手动创建的ELF中,Program Header #1作为PT_LOAD段指定p_vaddr=0x0时,普通用户执行execve时内核会直接拒绝这个映射请求,返回EPERM错误,随后进程因无法正确加载代码段触发SIGSEGV。而root用户拥有更高权限,不受此限制,因此能正常映射0x0地址并执行程序。
2. 系统二进制的加载地址误区
你观察到的“系统二进制文件能正常加载ELF头到虚拟地址0x0”是误解:系统二进制的ELF头在文件中的偏移是0x0,但它们的PT_LOAD段的p_vaddr通常是较高的合法地址(比如x86_64下常见的0x400000)。加载时内核只会把PT_LOAD描述的文件内容映射到指定虚拟地址,ELF头本身如果不在PT_LOAD段范围内,不会被加载到内存;如果在范围内,也是映射到p_vaddr对应的偏移位置,而非0x0虚拟地址。
3. PT_LOAD改PT_NULL后的生效逻辑
将Program Header #1改为PT_NULL后,内核会忽略这个无效段。此时你的ELF中应该存在另一个正确的PT_LOAD段(包含调用exit的汇编代码),其p_vaddr不在内核禁止普通用户映射的低地址范围,因此普通用户可以正常映射该地址,程序能顺利执行exit系统调用并退出。
验证与修复建议
- 检查手动创建的ELF的PT_LOAD段
p_vaddr值,确保它不是0x0或内核限制的低地址范围,x86_64 Linux下可使用0x400000作为起始加载地址(与系统二进制规范一致)。 - 用
readelf -l your_program查看程序头信息,确认PT_LOAD段的虚拟地址、文件偏移、权限等参数合规:readelf -l your_program - 确保PT_LOAD段的
p_flags设置正确:代码段需PF_R|PF_X(可读可执行),数据段需PF_R|PF_W(可读可写)。
内容的提问来源于stack exchange,提问作者Andreas Lund
相关产品推荐
相关产品推荐

