4.2 KiB
4.2 KiB
Линкер-скрипты: архитектурные детали
Назначение
Линкер-скрипты управляют расположением секций ELF-образа ядра. Для каждой архитектуры — свой скрипт, но все они следуют одной схеме.
Общая структура
OUTPUT_FORMAT(...) ← формат ELF (зависит от архитектуры)
ENTRY(kmain) ← точка входа
PHDRS ← сегменты (program headers)
{
text PT_LOAD;
rodata PT_LOAD;
data PT_LOAD;
}
SECTIONS ← расположение секций
{
. = 0xffffffff80000000; ← база higher half
.text : { *(.text .text.*) } :text
. = ALIGN(MAXPAGESIZE);
.rodata : { *(.rodata .rodata.*) } :rodata
. = ALIGN(MAXPAGESIZE);
.data : {
*(.data .data.*)
KEEP(*(.requests_start_marker))
KEEP(*(.requests))
KEEP(*(.requests_end_marker))
} :data
.bss : { *(.bss .bss.*) *(COMMON) } :data
/DISCARD/ : { *(.eh_frame*) *(.note .note.*) }
}
Детали
OUTPUT_FORMAT
| Архитектура | Формат |
|---|---|
| x86_64 | elf64-x86-64 |
| aarch64 | elf64-littleaarch64 |
| riscv64 | elf64-littleriscv |
| loongarch64 | elf64-loongarch |
Базовый адрес: 0xFFFFFFFF80000000
Ядро размещается в higher half — верхней 2 GiB виртуального адресного пространства. Это стандартная практика для x86-64:
0x0000_0000_0000_00000 ─── user space (не используется ядром)
0xFFFF_8000_0000_00000 ─── kernel space (higher half)
0xFFFF_FFFF_FFFF_FFFF ─── конец
Любой адрес в области 0xFFFF800000000000 — 0xFFFFFFFFFFFFFFFF корректен; 0xFFFFFFFF80000000 выбран как начало typical higher half региона.
PHDRS: PT_LOAD сегменты
Bootloader загружает только PT_LOAD сегменты. Их три:
- text: код + inline-константы.
- rodata: неизменяемые данные (строки, таблицы).
- data: изменяемые данные + BSS.
Section alignment
. = ALIGN(CONSTANT(MAXPAGESIZE));
MAXPAGESIZE = 0x1000 (4 KiB). Каждая секция начинается с новой страницы, что даёт bootloader'у правильные MMU permissionы:
- text = read + execute (no write)
- rodata = read (no write, no execute)
- data = read + write (no execute)
Limine Requests в .data
KEEP(*(.requests_start_marker))
KEEP(*(.requests))
KEEP(*(.requests_end_marker))
KEEP— запрещает линкеру выбрасывать эти секции (dead code elimination)..requests_start_markerи.requests_end_marker— маркеры границ.- Bootloader сканирует память между ними, чтобы найти requests.
BSS
.bss : {
*(.bss .bss.*)
*(COMMON)
} :data
- BSS — неинициализированные глобальные переменные.
- Занимает место в виртуальной памяти, но не в ELF-файле.
- Bootloader обнуляет BSS перед передачей управления.
DISCARD
/DISCARD/ : {
*(.eh_frame*)
*(.note .note.*)
}
.eh_frame*— исключительные фреймы C++/Rust unwinding..note.*— ELF notes.- Не нужны bare-metal ядру, могут вызвать проблемы.
Архитектурные различия
RISC-V
.data : {
*(.data .data.*)
KEEP(*(.requests_start_marker))
KEEP(*(.requests))
KEEP(*(.requests_end_marker))
*(.sdata .sdata.*) ← RISC-V: small data
} :data
.bss : {
*(.sbss .sbss.*) ← RISC-V: small BSS
*(.bss .bss.*)
*(COMMON)
} :data
RISC-V имеет .sdata/.sbss секции для small data (GP-relative addressing).
AArch64 и LoongArch64
Идентичны x86_64, за исключением OUTPUT_FORMAT.