#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. Спасибо! Я воспользуюсь этим подходом.