Thursday, April 7, 2022

[SOLVED] How to control the amount of contents loaded into memory from disk by grub?

Issue

I am working on a toy OS that is booted by grub.

However, some of the sections in the kernel images (especially which are objcopy-ed to original kernel image) are not loaded into physical memory at boot time. (i.e., after jumping to ENTRY defined by kernel elf)

Here are some questions.

What determines the amount of the size of the contents that is loaded into memory from the disk by the grub? Can I configure it?

Or the size is fixed, and should I just read the rest of them manually from the disk?

################ Updates ################

There are 21 section headers, starting at offset 0x279bfc:

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        01000000 001000 009a79 00  AX  0   0 16
  [ 2] .rodata           PROGBITS        0100a000 00b000 0012b0 00   A  0   0 4096
  [ 3] .eh_frame         PROGBITS        0100b2b0 00c2b0 0041b0 00   A  0   0  4
  [ 4] .data             PROGBITS        02000000 011000 002400 00  WA  0   0 4096
  [ 5] .bss              NOBITS          02002400 013400 108430 00  WA  0   0 1024
  [ 6] .percpu_data      PROGBITS        0210a880 11b880 002880 00  WA  0   0 4096
  [ 7] .comment          PROGBITS        00000000 11e100 000011 01  MS  0   0  1
  [ 8] .debug_aranges    PROGBITS        00000000 11e118 000620 00      0   0  8
  [ 9] .debug_info       PROGBITS        00000000 11e738 011352 00      0   0  1
  [10] .debug_abbrev     PROGBITS        00000000 12fa8a 004750 00      0   0  1
  [11] .debug_line       PROGBITS        00000000 1341da 00577b 00      0   0  1
  [12] .debug_str        PROGBITS        00000000 139955 00403c 01  MS  0   0  1
  [13] .debug_loc        PROGBITS        00000000 13d991 008966 00      0   0  1
  [14] .debug_ranges     PROGBITS        00000000 1462f7 000840 00      0   0  1
  [15] .part1            PROGBITS        0c000000 147000 096ba8 00   A  0   0  1
  [16] .part2            PROGBITS        0c100000 1de000 096b58 00   A  0   0  1
  [17] .srtos_conf       PROGBITS        0c1a0000 275000 00064f 00   A  0   0  1
  [18] .symtab           SYMTAB          00000000 275650 002780 10     19 157  4
  [19] .strtab           STRTAB          00000000 277dd0 001d64 00      0   0  1
  [20] .shstrtab         STRTAB          00000000 279b34 0000c7 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)

Elf file type is EXEC (Executable file)
Entry point 0x1000038
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x01000000 0x01000000 0x0f460 0x0f460 R E 0x1000
  LOAD           0x011000 0x02000000 0x02000000 0x10d100 0x10d100 RW  0x1000

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata .eh_frame 
   01     .data .bss .percpu_data 

In particular, I want to have GRUB load .part1, .part2 and .srtos_conf sections.

I guess they are not loaded as they are not part of program headers.

How can I add these sections as program headers?

Currently, these are merged to the kernel using objcopy --add-section XXX and the section flags are alloc,readonly,load,contents.

Which option do I have to use to add these sections to program headers?


Solution

The objcopy method to add new sections to the ELF appears to be too limited. https://reverseengineering.stackexchange.com/a/14780 suggests it is impossible to add program header entries this way.

I would instead suggest to create object files for .part1 etc. using objcopy, and to then specify these as additional inputs to your linker when building your kernel.elf. Objcopy will place the file contents in a .data section for you, and will create symbols _binary_x_start /_binary_x_end / _binary_x_size for convenience.

As an example:

Creating an object file out of any type of file (substitute your target triplet):

i686-elf-objcopy --input binary -B i386 -O elf32-i386 x x.o

Adding x.o to the linker inputs expands the .data output section to contain the file contents, and naturally results in it being loaded by the loader.

If needed, you can get control over where the file is loaded by using a linker script. (objcopy with the right arguments may also do this for you, but a linker script is usually a cleaner approach).

For example, to place a file specifically at 0x200000, you might do:

/* ... */

.data : {
    *(EXCLUDE_FILE(x.o) .data)
}

. = 0x0200000;
.foo : {
    x.o (.data)
}

/* ... */

Of course you can also rename the section in your object file to avoid the EXCLUDE_FILE here.


Another option you may want to look at is using multiboot modules. Having the files as modules can be an easier approach, but gives you less control over where/how they are loaded.
It also ties you in to multiboot more than you may like (I like my bootloaders simple and replaceable ;-).



Answered By - Chris Smeele
Answer Checked By - Katrina (WPSolving Volunteer)