jncronin / rpi-boot

A second stage bootloader for the Raspberry Pi

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.