Anatomy of an elf

Blog post by lucian on Thu, 2010-07-08 03:30

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