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 .dynamic

.text

.data

As one can easily see, each segment contains more than just theandsections.

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.