Как контролировать объем содержимого, загружаемого в память с диска с помощью grub?

#osdev #grub

#osdev #grub

Вопрос:

Я работаю над игрушечной ОС, которая загружается с помощью grub.

Однако некоторые разделы в образах ядра (особенно те, которые скопированы на исходный образ ядра) не загружаются в физическую память во время загрузки. (т.Е. после перехода к ЗАПИСИ, определенной elf ядра)

Вот несколько вопросов.

От чего зависит величина размера содержимого, загружаемого в память с диска с помощью grub? Могу ли я это настроить?

Или размер фиксирован, и я должен просто прочитать остальные вручную с диска?

################ Обновления ################

 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 
  

В частности, я хочу, чтобы у GRUB были разделы load .part1 , .part2 и .srtos_conf .

Я предполагаю, что они не загружены, поскольку они не являются частью заголовков программы.

Как я могу добавить эти разделы в качестве заголовков программы?

В настоящее время они объединены с ядром с помощью objcopy --add-section XXX и флагов разделов alloc,readonly,load,contents .

Какую опцию я должен использовать, чтобы добавить эти разделы в заголовки программы?

Комментарии:

1. Используете ли вы мультизагрузку? Являются ли ваше ядро и ELF исполняемыми?

2. @MichaelPetch Да, я понял, что могу настроить заголовок multiboot2 для определения load_addr, load_end_addr и bss_end_addr. Но, если я хочу загрузить ненулевое содержимое за пределы bss_end_addr, что мне делать? Ручная загрузка содержимого с диска в память — единственный способ сделать это?

3. @shpark Вам не должны понадобиться эти _addr опции, если вы используете ELF. GRUB должен учитывать заголовки программы в вашем двоичном файле для получения этой информации. Не могли бы вы пояснить, что вы подразумеваете под «особенно теми, которые копируются в исходный образ ядра»? Мне также любопытно, каков ваш вывод readelf -lS <yourkernel.elf> , поскольку он показывает именно то, что вы указываете загрузчику поместить в память.

4. @ChrisSmeele О, я не знал, что _addr это не рекомендуется. Я добавил больше деталей, включая readelf выходные данные. Спасибо за вашу помощь!

Ответ №1:

Метод objcopy для добавления новых разделов в ELF кажется слишком ограниченным. https://reverseengineering.stackexchange.com/a/14780 предполагается, что таким образом невозможно добавить записи заголовка программы.

Вместо этого я бы предложил создавать объектные файлы для .part1 и т.д. С помощью objcopy, а затем указывать их в качестве дополнительных входных данных для вашего компоновщика при сборке вашего kernel.elf. Objcopy поместит содержимое файла в .data раздел для вас и создаст символы _binary_x_start / _binary_x_end / _binary_x_size для удобства.

В качестве примера:

Создание объектного файла из файла любого типа (замените свой целевой триплет):

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

Добавление x.o ко входам компоновщика расширяет .data раздел вывода, содержащий
содержимое файла, и, естественно, приводит к его загрузке загрузчиком.

При необходимости вы можете получить контроль над тем, куда загружается файл, используя скрипт компоновщика. (objcopy с правильными аргументами также может сделать это за вас, но сценарий компоновщика обычно является более чистым подходом).

Например, чтобы поместить файл конкретно в 0x200000, вы могли бы сделать:

 /* ... */

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

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

/* ... */
  

Конечно, вы также можете переименовать раздел в вашем объектном файле, чтобы избежать
EXCLUDE_FILE здесь.


Другой вариант, который вы, возможно, захотите рассмотреть, — это использование мультизагрузочных модулей. Использование файлов в виде модулей может быть более простым подходом, но дает вам меньше контроля над тем, где / как они загружаются.
Это также привязывает вас к мультизагрузке больше, чем вам может понравиться (мне нравятся мои загрузчики, простые и заменяемые ;-).

Комментарии:

1. Спасибо! Я воспользуюсь этим подходом.