1

While working on my bootloader project, I noticed that the resulting binary file is larger than the sum of the sizes of each section in the original ELF file.

After linking the bootloader image via ld, the ELF file is structured as follows:

$ readelf -S elfboot.elf 
There are 10 section headers, starting at offset 0xa708:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00007e00 000e00 004bc7 00  AX  0   0 16
  [ 2] .rodata           PROGBITS        0000c9d0 0059d0 000638 00   A  0   0 32
  [ 3] .initcalls        PROGBITS        0000ec20 007c20 00000c 00  WA  0   0  4
  [ 4] .exitcalls        PROGBITS        0000ec30 007c30 00000c 00  WA  0   0  4
  [ 5] .data             PROGBITS        0000ec40 007c40 0001e0 00  WA  0   0 32
  [ 6] .bss              NOBITS          0000ee20 007e20 00025c 00  WA  0   0 32
  [ 7] .symtab           SYMTAB          00000000 007e20 0016e0 10      8 166  4
  [ 8] .strtab           STRTAB          00000000 009500 0011bb 00      0   0  1
  [ 9] .shstrtab         STRTAB          00000000 00a6bb 00004a 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

I noticed that there is a rather big gap (~7KB) between the sections .rodata and .initcalls. I also checked out the content of each section:

$ objdump -s elfboot.elf
[...]
Contents of section .rodata:
[...]
 cfc0 23cf0000 80000000 2fcf0000 00010000  #......./.......
 cfd0 3bcf0000 00020000 47cf0000 00040000  ;.......G.......
 cfe0 54cf0000 00080000 61cf0000 00100000  T.......a.......
 cff0 6ecf0000 00200000 7bcf0000 00400000  n.... ..{....@..
 d000 89cf0000 00800000                    ........        
Contents of section .initcalls:
 ec20 02b50000 6db50000 feac0000           ....m.......    
Contents of section .exitcalls:
[...]

My linker script tells to align every section at a 16 byte (0x10) boundary. After

objcopy -O binary elfboot.elf elfboot.bin

the resulting binary contains a huge chunk of zero filled bytes at offset 0x5200 (0xd000 - 0x7e00) all the way to offset 0x6e20 (0xec20 - 0x7e00). Now that I know where the zero filled bytes come from, how do I remove them? For reference I added the verbose output of the linker step including the linker script used:

  i686-elfboot-gcc -o elfboot.elf -T elfboot.ld    ./src/arch/x86/bootstrap.o ./src/arch/x86/a20.o ./src/arch/x86/bda.o ./src/arch/x86/bios.o ./src/arch/x86/copy.o ./src/arch/x86/e820.o ./src/arch/x86/entry.o ./src/arch/x86/idt.o ./src/arch/x86/realmode_jmp.o ./src/arch/x86/setup.o ./src/arch/x86/opmode.o ./src/arch/x86/pic.o ./src/arch/x86/ptrace.o ./src/arch/x86/video.o ./src/core/bdev.o ./src/core/cdev.o ./src/core/elf.o ./src/core/input.o ./src/core/interrupt.o ./src/core/loader.o ./src/core/main.o ./src/core/module.o ./src/core/pci.o ./src/core/printf.o ./src/core/string.o ./src/core/symbol.o ./src/crypto/crc32.o     ./src/drivers/ide/ide.o  ./src/fs/fs.o ./src/fs/file.o ./src/fs/super.o ./src/fs/ramfs/ramfs.o ./src/fs/isofs/isofs.o  ./src/lib/ata/libata.o ./src/lib/tmg/libtmg.o  ./src/mm/memblock.o ./src/mm/page_alloc.o ./src/mm/slub.o ./src/mm/util.o -O2 -Wl,--verbose -nostdlib -lgcc
GNU ld (GNU Binutils) 2.31.1
  Supported emulations:
   elf_i386
   elf_iamcu
opened script file elfboot.ld
using external linker script:
==================================================
OUTPUT_FORMAT(elf32-i386)
ENTRY(_arch_start)

SECTIONS
{
    /*
     * The boot stack starts at 0x6000 and grows towards lower addresses.
     * The stack is designed to be only one page in size, which should be
     * sufficient for the bootstrap stage.
     */

    . = 0x6000;
    __stack_start = .;

    /*
     * Buffer for reading files from the boot device. Usually boot devices
     * are designed to read data in chunks of 0x200 (512) or 0x800 (2048)
     * bytes. The buffer is large enough to read 4 512 or 2 2048 chunks of
     * data from the disk.
     */

    __buffer_start = .;

    . = 0x7000;
    __buffer_end = .;

    /*
     * Actual bootstrap stage code and data.
     */

    . = 0x7E00;
    __bootstrap_start = .;

    .text ALIGN(0x10) : {
        __text_start = .;
        *(.text*)
        __text_end = .;
    }

    .rodata ALIGN(0x10) : {
        __rodata_start = .;
        *(.rodata*)
        __rodata_end = .;
    }

    /*
     * This section is dedicated to all built-in modules. If a module will
     * be included in the elfboot binary, the module_init function pointer
     * of that module is placed in this section.
     *
     * Make sure to initialize built-in filesystems first as we need it to
     * setup the root file system node.
     */

    .initcalls ALIGN(0x10) : {
        __initcalls_vfs_start = .;

        /* Built-in file systems */
        *(.initcalls_vfs*)

        __initcalls_vfs_end = .;
        __initcalls_dev_start = .;

        /* Built-in devices */
        *(.initcalls_dev*)

        __initcalls_dev_end = .;
        __initcalls_start = .;

        /* Built-in modules */
        *(.initcalls*)

        __initcalls_end = .;
    }

    .exitcalls ALIGN(0x10) : {
        __exitcalls_start = .;
        *(.exitcalls*)
        __exitcalls_end = .;
    }

    .data ALIGN(0x10) : {
        __data_start = .;
        *(.data*)
        __data_end = .;
    }

    .bss ALIGN(0x10) : {
        __bss_start = .;
        *(.bss*)
        __bss_end = .;
    }

    __bootstrap_end = .;
}

==================================================
attempt to open ./src/arch/x86/bootstrap.o succeeded
./src/arch/x86/bootstrap.o
attempt to open ./src/arch/x86/a20.o succeeded
./src/arch/x86/a20.o
attempt to open ./src/arch/x86/bda.o succeeded
./src/arch/x86/bda.o
attempt to open ./src/arch/x86/bios.o succeeded
./src/arch/x86/bios.o
attempt to open ./src/arch/x86/copy.o succeeded
./src/arch/x86/copy.o
attempt to open ./src/arch/x86/e820.o succeeded
./src/arch/x86/e820.o
attempt to open ./src/arch/x86/entry.o succeeded
./src/arch/x86/entry.o
attempt to open ./src/arch/x86/idt.o succeeded
./src/arch/x86/idt.o
attempt to open ./src/arch/x86/realmode_jmp.o succeeded
./src/arch/x86/realmode_jmp.o
attempt to open ./src/arch/x86/setup.o succeeded
./src/arch/x86/setup.o
attempt to open ./src/arch/x86/opmode.o succeeded
./src/arch/x86/opmode.o
attempt to open ./src/arch/x86/pic.o succeeded
./src/arch/x86/pic.o
attempt to open ./src/arch/x86/ptrace.o succeeded
./src/arch/x86/ptrace.o
attempt to open ./src/arch/x86/video.o succeeded
./src/arch/x86/video.o
attempt to open ./src/core/bdev.o succeeded
./src/core/bdev.o
attempt to open ./src/core/cdev.o succeeded
./src/core/cdev.o
attempt to open ./src/core/elf.o succeeded
./src/core/elf.o
attempt to open ./src/core/input.o succeeded
./src/core/input.o
attempt to open ./src/core/interrupt.o succeeded
./src/core/interrupt.o
attempt to open ./src/core/loader.o succeeded
./src/core/loader.o
attempt to open ./src/core/main.o succeeded
./src/core/main.o
attempt to open ./src/core/module.o succeeded
./src/core/module.o
attempt to open ./src/core/pci.o succeeded
./src/core/pci.o
attempt to open ./src/core/printf.o succeeded
./src/core/printf.o
attempt to open ./src/core/string.o succeeded
./src/core/string.o
attempt to open ./src/core/symbol.o succeeded
./src/core/symbol.o
attempt to open ./src/crypto/crc32.o succeeded
./src/crypto/crc32.o
attempt to open ./src/drivers/ide/ide.o succeeded
./src/drivers/ide/ide.o
attempt to open ./src/fs/fs.o succeeded
./src/fs/fs.o
attempt to open ./src/fs/file.o succeeded
./src/fs/file.o
attempt to open ./src/fs/super.o succeeded
./src/fs/super.o
attempt to open ./src/fs/ramfs/ramfs.o succeeded
./src/fs/ramfs/ramfs.o
attempt to open ./src/fs/isofs/isofs.o succeeded
./src/fs/isofs/isofs.o
attempt to open ./src/lib/ata/libata.o succeeded
./src/lib/ata/libata.o
attempt to open ./src/lib/tmg/libtmg.o succeeded
./src/lib/tmg/libtmg.o
attempt to open ./src/mm/memblock.o succeeded
./src/mm/memblock.o
attempt to open ./src/mm/page_alloc.o succeeded
./src/mm/page_alloc.o
attempt to open ./src/mm/slub.o succeeded
./src/mm/slub.o
attempt to open ./src/mm/util.o succeeded
./src/mm/util.o
attempt to open /home/croemheld/Repositories/elfboot/elfboot-toolchain/lib/gcc/i686-elfboot/8.2.0/libgcc.so failed
attempt to open /home/croemheld/Repositories/elfboot/elfboot-toolchain/lib/gcc/i686-elfboot/8.2.0/libgcc.a succeeded
(/home/croemheld/Repositories/elfboot/elfboot-toolchain/lib/gcc/i686-elfboot/8.2.0/libgcc.a)_umoddi3.o
(/home/croemheld/Repositories/elfboot/elfboot-toolchain/lib/gcc/i686-elfboot/8.2.0/libgcc.a)_udivmoddi4.o

Please note that I am not able to load the sections individually into memory, since my first stage bootloader (boot sector) simply loads the entire file at a specified offset from the boot device. To reduce the size of the second stage bootloader image, I want to try to remove the gap at link time or also if possible at all, at post link time (objcopy, ...).

CRoemheld
  • 889
  • 7
  • 26
  • I would suggest attaching the linker script to the question, as that is a likely culprit in situations like these. – Chris Smeele Aug 24 '20 at 14:21
  • @ChrisSmeele Added the entire verbose output of the linker step including the linker script used. – CRoemheld Aug 24 '20 at 14:51
  • I'm not certain enough to post an answer, but my hunch is that the difference in writability between .rodata and .initcalls is causing ld to align (sort-of?) the next section. Perhaps try adding `--omagic` to the linker flags? – Chris Smeele Aug 24 '20 at 15:22
  • @ChrisSmeele I tried experimenting with the linker script and it turns out that when moving the `.rodata` section to the end of the file, the padding completely disappears. – CRoemheld Aug 24 '20 at 19:54
  • I know you gave us the readelf output but can you add the output of `objdump -x elfboot.elf` to your question. – Michael Petch Aug 25 '20 at 03:38

0 Answers0