linkerldelfmultiboot

How to tell / force GNU ld to put a section/symbol in a specific part of the output ELF file?


That's going to be a long one, so grab some coffee / tea / yerba.

Summary

How to tell / force GNU ld to put a section/symbol in a specific part of the output ELF file?

Specifically, I'm not asking about the physical/load address of the symbol but about the offset within the file.

Background

I'm trying to take a real world BSD kernel and make it compatible with Multiboot and bootable by GRUB. In order to make an ELF image compatible with Multiboot and identifiable by GRUB one needs to embed a magic number (0x1BADB002) into the first 8KiB of the file.

I'm referring to Multiboot 0.6.96.

As the original kernel is quite a big piece of code, I'm going to use examples based on the Bare Bones kernel from OSDev wiki. As that kernel already is Multiboot compliant, I'm going to use an extra_symbol with value 0xCAFEBABE as the example for my question.

boot.s contains:

.set CAFEBABE, 0xCAFEBABE
; ... snip ...
.section .extras
extra_symbol:
    .long CAFEBABE

Extra symbol in .text

The easiest option would be to put a symbol with that value into .text section just before anything else:

.text BLOCK(4K) : ALIGN(4K)
{
    *(.extras)
    *(.multiboot)
    *(.text)
}

That's fine for the example, but in case of the real BSD kernel .text starts way after 0x2000 (8KiB) in the file. This approach is not an option.

Extra section before .text?

Another option is to put the whole .extras section before .text. In my naivety, I hoped that putting such a section before .text in the linker script would also make it appear earlier in the output ELF:

SECTIONS
{
    // ... some stuff ...

    .extras : { *(.extras) }

    .text BLOCK(4K) : ALIGN(4K)
    {
        *(.multiboot)
        *(.text)
    }

    // ... more stuff ...

}

That's not the case, though:

$ i586-elf-readelf -S myos.bin 
There are 10 section headers, starting at offset 0x6078:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .extras           PROGBITS        00100000 006010 000004 00      0   0  1
  [ 2] .comment          PROGBITS        00000000 006014 000011 01  MS  0   0  1
  [ 3] .text             PROGBITS        00001000 001000 0001e4 00  AX  0   0 4096
  [ 4] .rodata.str1.1    PROGBITS        000011e4 0011e4 000016 01 AMS  0   0  1
  [ 5] .eh_frame         PROGBITS        000011fc 0011fc 000104 00   A  0   0  4
  [ 6] .bss              PROGBITS        00002000 002000 004010 00  WA  0   0 4096
  [ 7] .shstrtab         STRTAB          00000000 006025 000050 00      0   0  1
  [ 8] .symtab           SYMTAB          00000000 006208 000210 10      9  19  4
  [ 9] .strtab           STRTAB          00000000 006418 000130 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

In fact, the section comes first in the section header table, but its offset in the file (0x6010) is way after .text. In fact, it comes even after .bss.

The question

The ELF specification explicitly states that:

Although the figure shows the program header table immediately after the ELF header, and the section header table following the sections, actual files may differ. Moreover, sections and segments have no specified order. Only the ELF header has a fixed position in the file.

How do I make use of that and tell GNU ld to place my extra_symbol (probably the whole .extras section) before any other section in the file, preferably just after the ELF header?


Solution

  • In order to put the section before .text, have you already tried to make it allocated (A) and executable (X)?

    If the .interp hack works, then that's fine as well, of course.