要求对齐.text与.data

时间:2017-09-08 12:54:58

标签: linux x86 elf

我一直在玩ELFIO库。特别是One of the examples允许从头开始创建一个ELF文件 - 定义部分,段,入口点,并为相关部分提供二进制内容。

我注意到当选择的代码段对齐小于页面大小(0x1000)时,以这种方式创建的程序会出现段错误:

// Create a loadable segment
segment* text_seg = writer.segments.add();
text_seg->set_type( PT_LOAD );
text_seg->set_virtual_address( 0x08048000 );
text_seg->set_physical_address( 0x08048000 );
text_seg->set_flags( PF_X | PF_R );
text_seg->set_align( 0x1000 ); // can't change this

请注意,.text 部分仅在同一示例中与0x10的倍数对齐:

section* text_sec = writer.sections.add( ".text" );
text_sec->set_type( SHT_PROGBITS );
text_sec->set_flags( SHF_ALLOC | SHF_EXECINSTR );
text_sec->set_addr_align( 0x10 );

但是,数据段虽然通过相同的机制单独加载,但没有这个问题:

segment* data_seg = writer.segments.add();
data_seg->set_type( PT_LOAD );
data_seg->set_virtual_address( 0x08048020 );
data_seg->set_physical_address( 0x08048020 );
data_seg->set_flags( PF_W | PF_R );
data_seg->set_align( 0x10 ); // note here!

现在在这种特定情况下,数据在已经分配的页面内按设计拟合。不确定这是否有任何区别,但我将其虚拟地址更改为0x8148020,结果仍然正常。

以下是readelf的输出:

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000001000 0x0000000008048000 0x0000000008048000
                 0x000000000000001d 0x000000000000001d  R E    1000
  LOAD           0x0000000000001020 0x0000000008148020 0x0000000008148020
                 0x000000000000000e 0x000000000000000e  RW     10

当可执行段的对齐不是0x1000的倍数但为数据0x10没有问题时,为什么程序无法执行?

更新:不知何故第二次尝试text_seg->set_align( 0x100 );也有效,text_seg->set_align( 0x10 );失败。页面大小为0x1000,有趣的是,工作程序的VirtAddr在任一段中都不遵守它:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000100 0x08048100 0x08048100 0x0001d 0x0001d R E 0x100
  LOAD           0x000120 0x08148120 0x08148120 0x0000e 0x0000e RW  0x10

SIGSEGV的一个:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000080 0x08048100 0x08048100 0x0001d 0x0001d R E 0x10
  LOAD           0x0000a0 0x08148120 0x08148120 0x0000e 0x0000e RW  0x10

产生的ELF为here

2 个答案:

答案 0 :(得分:2)

(对不起,更多的是评论而非答案)

有关ELF可执行文件应该是什么的一些规范。请特别阅读elf(5),最重要的是相关的ABI规范(另请参阅this问题),例如在https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI

AFAIU,这些规范要求代码和数据段都是页面对齐的,但您需要检查一下,特别是在ABI规范的第5章(程序加载和动态链接)中。

生成ELF可执行文件(特别是binutils)的当前工具正在努力遵守这些规范。如果您编写一些ELF生成器,您也应该努力遵守这些规范(因此测试生成的ELF显然有效 )。

内核正在实施execve(2)dynamic loading也使用ld-linux(8) mmap(2)。由于某些原因(可能性能),它不会检查可执行文件是否符合所有规范。

(当然,内核人员希望通常生成的ELF可执行文件成功execve - d)

在某些极端情况下(就像你观察到的那样),内核不会失败execve并使用构造不良的ELF文件做一些事情。

但恕我直言,这并不能保证。未来的内核和未来的x86-64处理器可能会在这种构造错误的ELF文件上失败。

我的感觉是你处于一些灰色地带,有些不确定的行为" execve。如果它碰巧运作,那就是运气不好。

  

当可执行段的对齐不是0x1000的倍数但是对于数据0x10没有问题时,为什么程序无法执行?

要准确理解原因,您需要深入研究特定内核的源代码(与execve相关)。而且我相信它可能会在未来发生变化(未来版本的内核)。

内核社区或多或少具有过去的兼容性,但这与规范有关。可能会发生一些格式错误的ELF可执行文件可能是Linux {3.1},而不是Linux 4.13或未来的Linux 5 {/ 1}}。

(我确实读过一些过去的内核已经能够execve - 一些生成错误的ELF可执行文件,但我忘记了细节,也许是与堆栈指针的16字节对齐有关的东西)< / SUP>

答案 1 :(得分:2)

ELF ABI不要求VirtAddrPhysAddr页面对齐。 (我相信)它只需要

({Virt,Phys}Addr - Offset) % PageSize == 0

对于两个工作二进制文件都是如此,对于非工作二进制文件也是如此。

<强>更新

  

我不知道后者如何失败。

我们有:VirtAddr == 0x08048100Offset == 0x80(以及PageSize == 4096 == 0x1000)。

(0x08048100 - 0x80) % 0x1000 == 0x80 != 0
  当p = = 0x10时,

必须同意,不是吗?

否:它必须同意页面大小(正如我之前所说),否则内核将无法mmap该段。