14

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?

erszcz
  • 1,630
  • 10
  • 16
  • Did you try to specify the load address of your section ? Like: .extras : { *(.extras) } >ROM AT> ROM Where ROM is defined in memory block like: MEMORY { ROM : ORIGIN = 0x1000, LENGTH = 0x1000 } You can even make your own memory block MEMORY { SIGNATURE : ORIGIN = 0x...., LENGTH = 4 } .extras : { *(.extras) } >SIGNATURE AT> SIGNATURE – Lilás Nov 12 '13 at 10:50
  • I tried a number of combinations of setting load and virtual addresses and using memory blocks. The relevant addresses in readelf output do change. The offset of 0xCAFEBABE in the binary as printed by xxd does not. I'll update the question with details on what combinations I tried (can't do that now). – erszcz Nov 12 '13 at 21:48
  • @erszcz Any updates on this one so far? – yeputons Dec 13 '15 at 15:31
  • @yeputons Unfortunately, I ended up using a hack to get the image to be recognized as Multiboot. [I placed the MB header in `.interp` which I observed to be (one of?) the first section(s) in the generated file](https://github.com/lavrin/DragonFlyBSD/blob/d9e6ed9132f413b3f06219d9f088edefbd7c452b/sys/platform/pc32/conf/ldscript.i386#L26). In this particular linker script, there's a lot of sections in the beginning of the file which result (I assume) from the toolchain in use - that's why the real `.init` and `.text` get pushed far behind the initial 8KiB checked by GRUB. – erszcz Dec 13 '15 at 15:48
  • That is, I still don't know how to script the linker to generate the layout properly - it might not be possible. The only way apart from the above hack I can think of is editing the ELF binary after it's been generated. – erszcz Dec 13 '15 at 15:51

2 Answers2

2

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.

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92
  • Thanks for the response, but I currently have no way to check it :( The question is almost 4 years old now. – erszcz Jul 18 '17 at 09:04
1

I've encountered the same problem -- multiboot signature was loaded after .text sections -- and with Florian's pointer, this is how I forced my example section to be loaded at the beginning of the ELF file.

.section .multiboot, "ax", @progbits
.align 4
.long MAGIC
.long FLAGS
...

i.e. by marking at allocatable and executable at the section declaration

travisjayday
  • 784
  • 6
  • 16