我正在写一个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实现的特定约定、标准或算法来纠正这一点?感觉这里有一些没有记录的行为,或者是我找不到的有记录的行为。
1条答案
按热度按时间cygmwpex1#
为什么我的连接器会产生未对齐的片段?
它没有。要求是应该可以直接
mmap
LOAD
段,为此,以下 * 必须 * 为真:p_vaddr % pagesize == p_offset % pagesize
。对于输出中的所有LOAD
段都是如此。为什么我的连接器会产生重叠的区段?
这没有什么问题--相同的文件内容将在内存中出现两次。
另一种方法是在文件中插入一个大洞,浪费磁盘空间。
参见this answer。
是否有Linux实现的特定约定、标准或算法来纠正这一点?
无需纠正。仔细阅读man mmap。
由于
offset
必须是页对齐的,因此动态加载器将 bothp_vaddr
和p_offset
向下舍入为页对齐,并使用两者的页对齐值执行mmap(..., MAP_FIXED, ...)
。这“覆盖”了一点数据,但保证代码或数据最终精确地到达它所链接的地址。更新:
为什么链接器不总是将p_offset和p_vaddr设置为运行时使用的实际值
一般情况下,这是不这样做的,因为静态链接器在运行时不知道页面大小。在
x86_64
上,pagesize
可以是4KiB、2 MiB或1GiB。然而,这个特定的二进制文件是用
0x1000
(4KiB)对齐链接的,所以(静态)链接器 * 可以 * 将.p_vaddar
和.p_offset
设置为它们的四舍五入值(并将.p_memsize
向上调整以补偿)。静态链接器中的代码对对齐和页面大小是不可知的,所以它没有将4KiB视为特殊的(完全合理的方法)。