为什么 ELF 可执行文件可以有 4 个 LOAD 段?
有一个远程 64 位 *nix 服务器可以编译用户提供的代码(应该用 Rust 编写,但我认为这并不重要,因为它使用 LLVM)。我不知道它使用哪个编译器/链接器标志,但是编译后的 ELF 可执行文件看起来很奇怪 - 它有 4 个 LOAD 段:
$ readelf -e executable
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
...
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000004138 0x0000000000004138 R 0x1000
LOAD 0x0000000000005000 0x0000000000005000 0x0000000000005000
0x00000000000305e9 0x00000000000305e9 R E 0x1000
LOAD 0x0000000000036000 0x0000000000036000 0x0000000000036000
0x000000000000d808 0x000000000000d808 R 0x1000
LOAD 0x0000000000043da0 0x0000000000044da0 0x0000000000044da0
0x0000000000002290 0x00000000000024a0 RW 0x1000
...
在我自己的系统上,我正在查看的所有可执行文件只有 2 个 LOAD 段:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
...
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000003000c0 0x00000000003000c0 R E 0x200000
LOAD 0x00000000003002b0 0x00000000005002b0 0x00000000005002b0
0x00000000000776c8 0x000000000009b200 RW 0x200000
...
- 什么情况(编译器/链接器版本、标志等),编译器可以在其下构建具有 4 个 LOAD 段的 ELF?
- 有 4 个 LOAD 段有什么意义?我想拥有一个具有读取但不执行权限的段可能有助于防止某些攻击,但为什么要有两个这样的段呢?
There is a remote 64-bit *nix server that can compile a user-provided code (which should be written in Rust, but I don't think it matters since it uses LLVM). I don't know which compiler/linker flags it uses, but the compiled ELF executable looks weird - it has 4 LOAD segments:
$ readelf -e executable
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
...
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000004138 0x0000000000004138 R 0x1000
LOAD 0x0000000000005000 0x0000000000005000 0x0000000000005000
0x00000000000305e9 0x00000000000305e9 R E 0x1000
LOAD 0x0000000000036000 0x0000000000036000 0x0000000000036000
0x000000000000d808 0x000000000000d808 R 0x1000
LOAD 0x0000000000043da0 0x0000000000044da0 0x0000000000044da0
0x0000000000002290 0x00000000000024a0 RW 0x1000
...
On my own system all executables that I was looking at only have 2 LOAD segments:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
...
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000003000c0 0x00000000003000c0 R E 0x200000
LOAD 0x00000000003002b0 0x00000000005002b0 0x00000000005002b0
0x00000000000776c8 0x000000000009b200 RW 0x200000
...
- What are the circumstances (compiler/linker versions, flags etc) under which a compiler might build an ELF with 4 LOAD segments?
- What is the point of having 4 LOAD segments? I imagine that having a segment with read but not execute permission might help against certain exploits, but why have two such segments?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
典型的 BFD-ld 或 Gold 链接的 Linux 可执行文件有 2 个可加载段,其中
ELF
标头与.text
和.rodata
合并到第一个段中RE
段,以及.data
、.bss
等可写段合并到第二个RW
段中。这是典型的段到段映射:
这优化了内核加载此类可执行文件必须执行的
mmap
数量,但会付出安全代价:.rodata
中的数据> 不应该是可执行的,但却是(因为它与.text
合并,后者必须是可执行的)。这可能会显着增加试图劫持进程的人的攻击面。较新的 Linux 系统,特别是使用
LLD
链接二进制文件,优先考虑安全性而不是速度,并将 ELF 标头和.rodata
放入第一个R
-仅分段,产生 3 个负载分段并提高安全性。下面是一个典型的映射:为了不被抛在后面,较新的 BFD-ld(我的版本是 2.31.1)还使
ELF
标头和.rodata
变为只读,但无法将两个仅限 R 的段合并为一个,从而产生 4 个可加载段:最后,其中一些选择受到
--(no)rosegment
(或-Wl,z,noseparate-code
用于 BFDld
) 链接器选项。A typical BFD-ld or Gold linked Linux executable has 2 loadable segments, with the
ELF
header merged with.text
and.rodata
into the firstRE
segment, and.data
,.bss
and other writable sections merged into the secondRW
segment.Here is the typical section to segment mapping:
This optimizes the number of
mmap
s that the kernel must perform to load such executable, but at a security cost: the data in.rodata
shouldn't be executable, but is (because it's merged with.text
, which must be executable). This may significantly increase the attack surface for someone trying to hijack a process.Newer Linux systems, in particular using
LLD
to link binaries, prioritize security over speed, and put ELF header and.rodata
into the firstR
-only segment, resulting in 3 load segments and improved security. Here is a typical mapping:Not to be left behind, the newer BFD-ld (my version is 2.31.1) also makes
ELF
header and.rodata
read-only, but fails to merge twoR
-only segments into one, resulting in 4 loadable segments:Finally, some of these choices are affected by the
--(no)rosegment
(or-Wl,z,noseparate-code
for BFDld
) linker option.