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?
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
linker option.