# Линкер-скрипты: архитектурные детали ## Назначение Линкер-скрипты управляют расположением секций 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 сегменты. Их три: 1. **text**: код + inline-константы. 2. **rodata**: неизменяемые данные (строки, таблицы). 3. **data**: изменяемые данные + BSS. ### Section alignment ```ld . = 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 ```ld KEEP(*(.requests_start_marker)) KEEP(*(.requests)) KEEP(*(.requests_end_marker)) ``` - `KEEP` — запрещает линкеру выбрасывать эти секции (dead code elimination). - `.requests_start_marker` и `.requests_end_marker` — маркеры границ. - Bootloader сканирует память между ними, чтобы найти requests. ### BSS ```ld .bss : { *(.bss .bss.*) *(COMMON) } :data ``` - BSS — неинициализированные глобальные переменные. - Занимает место в виртуальной памяти, но не в ELF-файле. - Bootloader обнуляет BSS перед передачей управления. ### DISCARD ```ld /DISCARD/ : { *(.eh_frame*) *(.note .note.*) } ``` - `.eh_frame*` — исключительные фреймы C++/Rust unwinding. - `.note.*` — ELF notes. - Не нужны bare-metal ядру, могут вызвать проблемы. ## Архитектурные различия ### RISC-V ```ld .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.