vmlinux.lds 是 Linux 內核鏈接階段的核心腳本,定義內核映像的虛擬地址佈局、段屬性、符號邊界。位於 arch/<arch>/kernel/vmlinux.lds.S,通過預處理器生成最終 vmlinux.lds,再由鏈接器 ld 解析。本文以 ARM 架構為例,解析通用模板 asm-generic/vmlinux.lds.h 的宏定義與段組織邏輯。
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(stext) /* 內核入口點 */
SECTIONS
{
. = TEXT_OFFSET; /* 內核加載地址,ARM 通常 0x80008000 */
/* ===== 初始化段(啟動後可釋放)===== */
__init_begin = .;
HEAD_TEXT_SECTION /* head.S 入口代碼 */
INIT_TEXT_SECTION(PAGE_SIZE)/* .text.init 初始化函數 */
INIT_DATA_SECTION(...) /* .data.init 初始化數據 */
PERCPU(PAGE_SIZE) /* per-CPU 變量 */
__init_end = .;
/* ===== 正文段(只讀代碼)===== */
_stext = .;
TEXT_SECTION = 0 /* .text 普通代碼 */
_etext = .;
/* ===== 數據段 ===== */
_sdata = .;
RO_DATA_SECTION(PAGE_SIZE) /* .rodata 只讀數據 */
RW_DATA_SECTION(...) /* .data 可讀寫數據 */
_edata = .;
/* ===== 特殊段 ===== */
EXCEPTION_TABLE(...) /* 異常表 */
NOTES /* 構建元數據 */
/* ===== BSS 段(零初始化)===== */
BSS_SECTION(0, 0, 0)
_end = .; /* 內核映像結束地址 */
/* ===== 調試信息 ===== */
STABS_DEBUG
DWARF_DEBUG
/* ===== 丟棄段 ===== */
DISCARDS /* 必須放最後:.exit.text, .comment 等 */
}
| 宏 | 展開內容 | 說明 |
|---|---|---|
| HEAD_TEXT_SECTION | *(.head.text) | 架構相關啟動代碼(head.S) |
| INIT_TEXT_SECTION(align) | *(.init.text) *(.init.text.*) | 標記 __init 的函數,啟動後釋放 |
| INIT_DATA_SECTION(align) | *(.init.data) *(.init.data.*) | 標記 __initdata 的數據 |
| PERCPU(align) | *(.data.percpu) | 每 CPU 變量區 |
| TEXT_SECTION | *(.text) *(.text.*) *(.gnu.linkonce.t.*) | 常駐代碼段 |
| RO_DATA_SECTION(align) | *(.rodata) *(.rodata.*) | 只讀數據,映射為只讀頁 |
| RW_DATA_SECTION(align) | *(.data) *(.data.*) | 讀寫數據 |
| BSS_SECTION(page, align, stop) | *(.bss) *(.bss.*) COMMON | 零頁,運行時清零 |
| DISCARDS | *(.exit.text) *(.exit.data) *(.comment) | 鏈接後丟棄,不進入映像 |
__init_begin / __init_end:初始化段範圍,free_initmem() 釋放。_stext / _etext:代碼段邊界,kallsyms、模塊加載器校驗用。_sdata / _edata:已初始化數據段邊界。_end:內核映像物理結束地址,啟動分配器起始位置。__initramfs_start / __initramfs_end:內嵌 initramfs 範圍。__nosave_begin / __nosave_end:休眠不保存內存區域。0x80008000(物理 0x80000000 + 0x8000),x86 為 0x100000。OUTPUT_FORMAT 指定 ELF 類型(32/64 位、大小端)。ENTRY(stext) 對應 arch/arm/kernel/head.S 入口標號。arch/arm/kernel/module.lds 定義模塊鏈接佈局。# 查看生成的鏈接腳本
cat vmlinux.lds
# 反彙編驗證段分佈
objdump -h vmlinux | head -30
# 關鍵符號地址
nm vmlinux | grep -E '^(__init_begin|_stext|_end|_sdata)'
.text 超過架構限制(如 ARM 32MB),需調整 CONFIG_KERNEL_TEXT_OFFSET 或啟用 CONFIG_RELOCATABLE。PERCPU、RO_DATA 未按 PAGE_SIZE 對齊導致頁屬性衝突。__initcall_start 未正確收集,檢查 initcall 宏定義。