Anatomy of an elf
ELF segments
The first thing Haiku does when it encounters something that resembles a file system driver is to load it and inspect the list of modules provided.
Normally, loading a Haiku add-on means identifying three program headers from the ELF add-on image: the ones corresponding to the .text
(code) section, the .data
section and a third segment where dynamic linking information can be found.
We can find information about these segments running:
$ readelf -l generated/objects/haiku/x86/release/add-ons/kernel/file_systems/iso9660/iso9660 Elf file type is DYN (Shared object file) Entry point 0x1070 There are 3 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00000000 0x00000000 0x04440 0x04440 R E 0x1000 LOAD 0x004440 0x00005440 0x00005440 0x00364 0x00384 RW 0x1000 DYNAMIC 0x004454 0x00005454 0x00005454 0x000c0 0x000c0 RW 0x4 Section to Segment mapping: Segment Sections... 00 .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame 01 .ctors .dtors .data.rel.ro .dynamic .got .data .bss 02 .dynamicAs one can easily see, each segment contains more than just the
.text
and .data
sections.
The PT_LOAD
segment corresponding to the .text
section is not writable, while the PT_LOAD
segment corresponding to the .data
section is not executable. These are simple protections against certain types of exploits: e.g. changing the code at runtime to code written by an attacker, or executing (code masked as) data received from the attacker.
Haiku uses the segment's protection information to differentiate between the .text
section and the .data
sections.
LKL based ELFs
Images that link to LKL (at least at this moment) generate a single PT_LOAD
segment which encapsulates (among others) both the .text
and the .data
sections:
$ readelf -l generated/objects/haiku/x86/release/add-ons/kernel/file_systems/lklhaikufs/lklhaikufs Elf file type is DYN (Shared object file) Entry point 0x18790 There are 3 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00000000 0x00000000 0x134520 0x13d310 RWE 0x1000 DYNAMIC 0x12c5a4 0x0012c5a4 0x0012c5a4 0x000c8 0x000c8 RW 0x4 NOTE 0x000094 0x00000094 0x00000094 0x00024 0x00024 R 0x4 Section to Segment mapping: Segment Sections... 00 .note.gnu.build-id .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .cpuinit.text .meminit.text .fini .rodata .eh_frame .ctors .dtors .data.rel.ro .dynamic .got .data .cpuinit.data .meminit.data .bss 01 .dynamic 02 .note.gnu.build-id
As we need to be able to execute the .text
section and to write data from the .data
section at the same time, this PT_LOAD
segment must be mapped as RWE
.
At first glance, the fix appeared trivial: if a section is executable we'll consider it the .text
section and we'll treat it as a special writable .text
section. This trick however does not work on all architectures as Ingo Weinhold informed me:
bonefish@backbone:~/develop/haiku/haiku/generated-ppc> readelf --segments objects/haiku/ppc/release/add-ons/kernel/bus_managers/pci/pci Elf file type is DYN (Shared object file) Entry point 0x3f270 There are 3 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00000000 0x00000000 0xa5c50 0xa5c50 R E 0x1000 LOAD 0x0a5c50 0x000a6c50 0x000a6c50 0x464d0 0x46678 RWE 0x1000 DYNAMIC 0x0a5c6c 0x000a6c6c 0x000a6c6c 0x000c0 0x000c0 RW 0x4 Section to Segment mapping: Segment Sections... 00 .hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .text .fini .rodata 01 .eh_frame .ctors .dtors .data.rel.ro .dynamic .data .got .sdata .sbss .plt .bss 02 .dynamic
On PPC the .data
section has the same protection bits as our LKL-based module on x86, namely RWE. Because the PT_LOAD
program headers can appear in any order in the ELF file, on PPC my solution could have treated .data
as program code, and ignore the real program code.
The problem has been properly fixed, but this means R1/Alpha 1, or R1/Alpha 2 users cannot use LKL.