为什么简单的 ELF 二进制文件中存在重叠和未对齐的段?

问题描述 投票:0回答:1

我正在写一个ELF加载器并研究ELF格式。我有一个简单的 hello world 二进制文件,是在 Fedora 38 中使用 Clang 16 创建的,它可以工作,而且我没有使用任何特定选项进行编译/链接 (

clang hello.c -o hello
)。我用
readelf
检查了程序头,发现一些内容与我对 LOAD 和 DYNAMIC 头的理解不符。

例如:


Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
…
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000004f0 0x00000000000004f0  R      0x1000
  LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x000000000000016d 0x000000000000016d  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000402000 0x0000000000402000
                 0x00000000000000dc 0x00000000000000dc  R      0x1000
  LOAD           0x0000000000002df8 0x0000000000403df8 0x0000000000403df8
                 0x0000000000000214 0x0000000000000218  RW     0x1000
  DYNAMIC        0x0000000000002e08 0x0000000000403e08 0x0000000000403e08
                 0x00000000000001d0 0x00000000000001d0  RW     0x8

有两个问题我无法解决。请注意前三个 LOAD 标头如何对齐虚拟地址,这对我来说很有意义。但最后一个 LOAD 标头和 DYNAMIC 标头重叠且未对齐。可执行文件在 Linux 中加载得很好,但 Linux 似乎也解决了这个问题。这是

/proc/<pid>/maps

00400000-00401000 r--p 00000000 00:23 16905823                         /path/to/hello
00401000-00402000 r-xp 00001000 00:23 16905823                         /path/to/hello
00402000-00403000 r--p 00002000 00:23 16905823                         /path/to/hello
00403000-00404000 r--p 00002000 00:23 16905823                         /path/to/hello
00404000-00405000 rw-p 00003000 00:23 16905823                         /path/to/hello

所以我有几个问题:

  • 为什么我的链接器会产生未对齐的段?依赖操作系统来修复对齐似乎是一个坏主意。
  • 为什么我的链接器会创建重叠的段?同样,依靠操作系统来解决这个问题似乎是一个坏主意。
  • Linux 是否实现了特定的约定、标准或算法来纠正此问题?感觉这里有一些未记录的行为或我无法找到的已记录的行为。
c linux linker clang elf
1个回答
0
投票

为什么我的链接器会产生未对齐的段?

事实并非如此。要求是应该可以直接

mmap
LOAD
段,为此,以下 必须 为真:
p_vaddr % pagesize == p_offset % pagesize
。对于输出中的所有
LOAD
段都是如此。

为什么我的链接器会创建重叠的段?

这没有什么问题——相同的文件内容会在内存中出现两次。

另一种方法是在文件中插入一个大洞,浪费磁盘空间。

另请参阅此答案

Linux 是否实现了特定的约定、标准或算法来纠正此问题?

无需修正。仔细阅读 man mmap

由于

offset
必须页面对齐,因此动态加载程序将 both
p_vaddr
p_offset
向下舍入以进行页面对齐,并使用两者的页面对齐值执行
mmap(..., MAP_FIXED, ...)
。这“覆盖”了一点数据,但保证代码或数据最终准确地位于其链接的地址处。

© www.soinside.com 2019 - 2024. All rights reserved.