【问题标题】:Required alignment of .text versus .data.text 与 .data 所需的对齐方式
【发布时间】:2017-09-08 12:54:58
【问题描述】:

我一直在玩弄ELFIO 库。尤其是One of the examples,它允许人们从头开始创建 ELF 文件——定义节、段、入口点,并为相关节提供二进制内容。

I noticed that a program created this way segfaults when the code segment alignment is chosen less than the page size (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

【问题讨论】:

  • 你能发布两个生成的 .ELF 文件吗?
  • @MartinRosenau 当然。我一定犯了一个错误,0x100 也有效。但是 0x10 仍然中断。页面大小为 0x1000。这是两个文件进行比较,还有一些地址调整以使它们更小:(tinyupload.com)
  • 您需要仔细阅读execve(2)elf(5)。恕我直言,代码和数据段都应该是页面对齐的,execve 有时处理未对齐的段这一事实是一个实现细节(并且可能在未来的内核或未来的 x86-64 处理器中发生变化),甚至可能是未定义的行为。顺便说一句,这个问题是 linux 和处理器 (ISA) 特定的。
  • 如果您需要了解为什么内核的行为与您观察到的一样,您应该深入研究内核源代码。

标签: linux x86 elf


【解决方案1】:

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

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

这对于工作的二进制文件都是正确的,对于非工作的二进制文件是错误的。

更新:

我不明白后者是如何失败的。

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

(0x08048100 - 0x80) % 0x1000 == 0x80 != 0

在 align == 0x10 时必须同意,不是吗?

不:它必须与页面大小一致(正如我之前所说的),否则内核将无法mmap 段。

【讨论】:

  • 我不明白后者是如何失败的。当 align == 0x10 时,只有最后一个半字节 (0x0) 必须一致,不是吗?
  • 哦,我明白了,我已经在脑海中替换了“对齐”。这很有意义!
【解决方案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 相关)。而且我相信它可能会在未来发生变化(内核的未来版本)。

内核社区或多或少有希望过去的兼容性,但这与规范有关。在 Linux 3.10 中,某些格式错误的 ELF 可执行文件可能是 execve-d,但在 Linux 4.13 或某些未来的 Linux 5 中可能不是。

(我确实读过一些过去的内核已经能够execve-d 一些格式错误的 ELF 可执行文件,但我忘记了细节,可能与堆栈指针的 16 字节对齐有关)

【讨论】:

  • 一定是这样。我特别喜欢出于性能原因不检查其他所需限制的可能性背后的基本原理。我认为我的困惑的根源是 ELFIO 的示例程序不兼容 - 我会仔细检查您建议的来源并提醒开发人员。
  • 您会提醒哪些开发人员? ELFIO 不需要检查所有内容。 ,使用 ELFIO 发出 ELF 文件的人,应该努力遵循 ELF 规范。
  • 没错,但我怀疑their own example中的数据段还有一些问题。它与 0x10 对齐,这肯定小于任何 x86 上的页面大小(从 0x(...)020 开始),更重要的是,它与代码段重叠,因此需要对部分内存具有 RW 权限已经分配了RX。当然,合规性取决于图书馆的用户,但旁边提供的示例不应误导用户(如果我的怀疑被证明是正确的)。
  • 所以这将是一个文档错误。
  • muppetlabs.com/~breadbox/software/tiny/teensy.html 通过将程序头与 ELF 头重叠来生成一个微小的 45 字节 ELF 静态可执行文件。它说Linux碰巧忽略了phdr中的p_align字段,用于不可重定位的ELF可执行文件,以及对于当前Linux哪些字段可以/不能是垃圾的一些其他细节。这与你在最后一个脚注中的想法有关吗?,巴西尔? (@TheVee,你可能还想看看)
猜你喜欢
  • 2018-09-06
  • 2018-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 2013-08-13
  • 1970-01-01
相关资源
最近更新 更多