为何访问已初始化数组触发段错误?Brainfuck编译器ELF问题
结合你提供的ELF程序头代码、触发段错误的汇编指令,核心问题出在ELF加载器无法正确映射你定义的数据段,导致访问0x804a001时触发内存错误,具体原因和修复方向如下:
核心问题点
1. p_offset超出文件实际有效范围
数据段的p_offset被设置为sizeof(ELF_Header) + 2 * sizeof(Program_Header) + machineCodeLength,这个值代表该段在ELF文件中的起始偏移。但ELF加载器要求:即便p_filesz=0(文件中无该段数据),p_offset也必须落在文件的有效长度范围内(不能超过文件实际大小)。
如果你的ELF文件总长度(ELF头+2个程序头+机器码)小于这个p_offset,加载器会无法识别该段的合法位置,直接导致内存映射失败,访问对应地址时触发段错误。你之前添加空字节成功一次,本质是刚好把文件长度补到了大于等于p_offset的数值,让加载器认为这个偏移是有效的。
2. 段对齐违反ELF规范
32位ELF中,当可加载段的p_align设为0x1000(页面大小)时,必须满足(p_vaddr - p_offset) % p_align == 0。你的p_vaddr是0x0804a000(刚好是0x1000的倍数),但p_offset由ELF头、程序头和机器码长度累加而来,若这个值不是0x1000的倍数,就违反了对齐要求,加载器可能无法正确映射内存区域。
3. p_filesz=0的兼容性问题
虽然ELF规范允许p_filesz < p_memsz(加载器会将超出部分初始化为0),但部分加载器对p_filesz=0的情况处理存在兼容问题,更稳妥的方式是让p_filesz至少为1(比如在文件末尾加一个空字节),同时保证p_offset指向这个字节的位置。
修复步骤
- 确保
p_offset不超限:计算ELF文件总长度(sizeof(ELF_Header) + 2*sizeof(Program_Header) + machineCodeLength),将p_offset设为这个总长度,生成ELF文件时在末尾填充空字节,保证文件长度至少等于p_offset。 - 修正段对齐:将机器码的长度填充到
0x1000的倍数,或者调整p_vaddr,使得p_vaddr - p_offset是0x1000的整数倍。比如32位ELF头是52字节、每个程序头32字节,那么52 + 2*32 = 116,你需要让116 + machineCodeLength是0x1000的倍数,这样p_offset就和p_vaddr对齐,符合规范。 - 验证ELF结构:用
readelf -l your_program查看程序头信息,确认每个段的Offset、VirtAddr、FileSiz、MemSiz、Align是否符合预期;用gdb调试时,通过info proc mappings查看0x0804a000附近的内存是否被正确映射为可读写区域。
内容的提问来源于stack exchange,提问作者Been

