The ELF loader is reading section headers rather than the program headers
eryjus opened this issue · comments
multiboot.c is parsing the section headers in order to load an elf executable. I believe the program headers should drive the load.
https://github.com/jncronin/rpi-boot/blob/master/multiboot.c#L340
I believe it is a better practice to read and process the program headers to load a program rather than the section headers. The reason I am calling out this concern is I have the following ELF kernel, with the following sections:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .loader PROGBITS 00100000 001000 0003e0 00 WAX 0 0 4
[ 2] .text PROGBITS 80000000 002000 004ba0 00 AX 0 0 8
[ 3] .rodata PROGBITS 80005000 007000 001910 00 A 0 0 4
[ 4] .stab PROGBITS 80007000 009000 033cad 00 A 0 0 4
[ 5] .stack PROGBITS 8003b000 03d000 000001 00 WA 0 0 1
[ 6] .data PROGBITS 8003c000 03e000 000220 00 WA 0 0 4
[ 7] .bss NOBITS 8003d000 03e220 036c14 00 WA 0 0 4096
[ 8] .ARM.attributes ARM_ATTRIBUTES 00000000 03e220 00003b 00 0 0 1
[ 9] .symtab SYMTAB 00000000 03e25c 003880 10 10 762 4
[10] .strtab STRTAB 00000000 041adc 00152d 00 0 0 1
[11] .shstrtab STRTAB 00000000 043009 000059 00 0 0 1
However, the program header is simpler and more accurate to how to load the program:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x00100000 0x00100000 0x003e0 0x003e0 RWE 0x1000
LOAD 0x002000 0x80000000 0x00101000 0x3acad 0x3acad R E 0x1000
LOAD 0x03d000 0x8003b000 0x0013c000 0x01220 0x38c14 RW 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
In particular, notice the addresses in the section header (which are virtual addresses) versus the program header which indicates the virtual addresses and the expected physical addresses. In particular, multiboot.c is attempting to allocate addresses in the 0x80000000
+ address space, which will never exist.
I am actually taking great care to make sure that the physical address is accurately specified in my kernel:
PHYS = 0x00100000; /* We will need this value for the loader */
KERN = 0x80000000; /* We will need this value for the loader */
SECTIONS {
. = PHYS; /* Multiboot will place the kernel here */
_loaderStart = .;
.loader : { /* This is where the loader will be located -- things to be reclaimed: */
*(.mboot) /* -- multiboot header comes first */
*(.ldrtext) /* -- loader text (code) */
*(.ldrrodata) /* -- loader rodata (like strings) */
*(.ldrdata) /* -- loader data (things that will not get passed to the kernel */
phys_loc = .; /* -- provide variable `phys_loc` with the value of PHYS */
LONG(PHYS);
kern_loc = .; /* -- provide variable `kern_loc` wiht the value of KERN */
LONG(KERN);
*(.ldrbss) /* -- loader bss (again, things that will not get passed to the kernel */
}
. = ALIGN(4096);
_loaderEnd = .;
PHYS_OFFSET = .; /* -- this is used to get the physical addresses correct for the elf loader */
. = KERN;
.text : AT(ADDR(.text) - KERN + PHYS_OFFSET) {
*(.init)
*(.text .text.* .stub .gnu.linkonce.t.*)
}
. = ALIGN(4096);
It is my understanding that the program headers should drive reading the binary and loading it in memory while the section headers should dictate permissions against the resulting image in memory.
Please let me know your thoughts.
Adam
Hi,
I generally agree with you that the program headers should be used to load an ELF file. Indeed, this is what the 'kernel' command does (see method_kernel()). In general, the section headers are only used for static linking and should play no part in executable loading.
The multiboot command was designed to strictly follow the multiboot 0.6.96 standard, which states that all sections should be loaded (here it is implied they should be loaded even if not specified as a loadable section) and their load addresses returned to the kernel in the multiboot structure. This is the behaviour rpi-boot tries to emulate.
Having said that, the grub legacy 0.97 source loads using the program headers (particularly the physical address), then additionally loads all currently unloaded sections, which is probably the best way to go. I'll look in to it.
Thanks,
John.
Program headers are now used first, then unloaded segments.
Please let me know if you have any issues.
Thanks,
John.
Thanks for the quick turn-around on this. This is working for me now and can be closed.
As a side note, I recently changed host systems and re-tooled in the process. I was not able to compile the source with the latest version of gcc:
$ armv7-rpi2-linux-gnueabihf-gcc --version
armv7-rpi2-linux-gnueabihf-gcc (crosstool-NG 1.23.0.580-eb72b4e) 8.2.0
I did have to rebuild a tool set at version 6.5.0 (with binutils 2.28.1) to be able to compile without any fulling around and get it to run:
$ arm-eabi-gcc --version
arm-eabi-gcc (crosstool-NG 1.23.0.580-eb72b4e) 6.5.0
My old host system used version 6.3.0.
Anyway, I believe a note in the readme about the compiler version restriction would help.