本文约2500字,今天继续沿着《一份靠谱的BSP开发学习路线》来学习BSP开发所需的知识--ARM Linux内核zImage链接脚本 (.lds) 。本文基于 AX615 系列 ARM 芯片的内核zImage 链接脚本vmlinux.lds(在AX615系列 4.19.125版本的内核源码上该文件的完整名称是vmlinux.lds.S)来逐行分析语法、段定义、内核特殊逻辑,帮我们底掌握 ARM 内核链接脚本的核心原理。
关注公众号, 即可获得与Linux相关的电子书籍以及常用开发工具,文末有文档清单。
在 ARM 架构 Linux 内核编译流程中,.lds链接脚本是决定内核镜像内存布局、段分配、符号定义的核心文件,它直接指挥链接器(ld)将编译后的目标文件拼接成可执行的zImage内核镜像。
在编译完内核后,会生成除了内核镜像外,还会有一个vmlimux的文件,vmlinux.lds是蓝图 / 规则,vmlinux是按蓝图生成的最终内核可执行文件,二者是 “链接脚本->链接器->内核镜像”的直接生产关系。
一 前置知识:.lds 文件是什么?
全称:Linker Script(链接脚本),是 GNU 链接器ld的配置文件;
核心作用:定义程序的内存地址布局、段(Section)分配规则、全局符号、输出格式;
内核场景:ARM 内核的.lds分为两类 ——vmlinux.lds(主内核链接脚本)、zImage.lds(压缩内核镜像链接脚本),本文解析的是zImage 压缩解压阶段的专用链接脚本。
二 脚本整体结构概览
AX615系列内核的vmlinux.lds.S脚本是ARM 32 位小端/大端兼容的 zImage 链接脚本,核心分为 5 部分:
[1].大小端宏定义适配
[2].输出架构 / 入口点声明
[3].段定义(代码段、数据段、表段、压缩内核段等)
[4].全局符号定义(内核签名、起止地址等)
[5].断言校验 + 调试段声明
三 逐段深度解析
[1]. 大小端适配宏定义
#ifdef CONFIG_CPU_ENDIAN_BE8#define ZIMAGE_MAGIC(x) ( (((x) >> 24) & 0x000000ff) | \(((x) >> 8) & 0x0000ff00) | \(((x) << 8) & 0x00ff0000) | \(((x) << 24) & 0xff000000) )#else#define ZIMAGE_MAGIC(x) (x)#endif
作用:适配 ARM CPU 的大端模式(BE8) 和小端模式(LE);
原理:大端模式下,32 位数据需要字节序反转,小端模式直接使用原值;
内核意义:zImage 的魔术字、地址信息必须和 CPU 端序一致,否则启动时无法识别。
[2]. 基础配置:输出架构 + 入口点
OUTPUT_ARCH(arm)ENTRY(_start)
OUTPUT_ARCH(arm):指定输出镜像的目标架构为ARM,链接器会校验目标文件的架构兼容性;
ENTRY(_start):定义镜像的入口函数为_start —— 这是 zImage 解压程序的第一条指令地址,CPU 复位后直接跳转到这里执行。
[3]. 关键段:/DISCARD/ 丢弃段
/DISCARD/ : {*(.ARM.exidx*)*(.ARM.extab*)*(.data)}
这是 zImage 脚本最特殊的段,作用是强制丢弃指定段,链接器不会将这些段写入最终镜像:
.ARM.exidx*/.ARM.extab*:ARM 异常处理表,zImage 是纯解压程序,无需异常处理;
*(.data):强制丢弃所有读写数据段;
内核设计目的:zImage 解压阶段必须是PIC(位置无关代码),读写数据会产生 GOTOFF 重定位,导致无法独立重定位运行,丢弃后链接器会直接报错(强制约束)。
[4]. 代码段 .text 定义
. = TEXT_START;_text = .;.text : {_start = .;*(.start)*(.text)*(.text.*)*(.fixup)*(.gnu.warning)*(.glue_7t)*(.glue_7)}
. = TEXT_START;:将当前链接地址设置为内核文本段起始地址(TEXT_START是内核配置的宏,如 0x80008000);
_text = .;:定义全局符号_text,标记代码段起始地址;
.text段:收集所有目标文件的代码段,包含:
.start:启动汇编代码(_start入口);
.fixup:内核地址修复代码;
.glue_7/.glue_7t:ARM/Thumb 指令集切换胶水代码。
[5]. 核心表段 .table(zImage 识别标识)
.table : ALIGN(4) {_table_start = .;LONG(ZIMAGE_MAGIC(2))LONG(ZIMAGE_MAGIC(0x5a534c4b))LONG(ZIMAGE_MAGIC(__piggy_size_addr - _start))LONG(ZIMAGE_MAGIC(_kernel_bss_size))LONG(0)_table_end = .;}
这是zImage 的核心标识表,Bootloader/CPU 通过这个表识别合法的内核镜像:
ALIGN(4):4 字节对齐(ARM32 位指令要求);
5 个 32 位长整型(LONG)核心字段:
2:zImage 版本号;
0x5a534c4b:魔术字(Magic),十六进制转 ASCII 为ZSLK,是 ARM zImage 的固定签名;
__piggy_size_addr - _start:压缩内核数据的偏移地址;
_kernel_bss_size:内核 BSS 段大小;
0:结束填充;
作用:启动时硬件 /bootloader 校验魔术字,确认这是合法的 ARM zImage。
[6]. 只读数据段 + 压缩内核数据段
.rodata : {*(.rodata)*(.rodata.*)*(.data.rel.ro)}.piggydata : {*(.piggydata)__piggy_size_addr = . - 4;}
.rodata:存放只读常量(字符串、常量数组);
.piggydata:压缩内核数据段 —— 内核源码编译后会被 gzip 压缩,存放在这个段,是 zImage 的核心数据;
__piggy_size_addr = . -4:记录压缩数据的大小地址,供解压程序读取。
[7]. GOT 表(位置无关代码核心)
.got.plt : { *(.got.plt) }_got_start = .;.got : { *(.got) }_got_end = .;
GOT(Global Offset Table):全局偏移表,是PIC 位置无关代码的核心;
作用:zImage 运行时无需固定地址,通过 GOT 表动态计算函数 / 变量地址,适配任意内存地址加载。
[8]. EFI STUB 适配段(可选)
#ifdef CONFIG_EFI_STUB.data : ALIGN(4096) {__pecoff_data_start = .;*(.data.efistub)__pecoff_data_end = .;. = ALIGN(512);}#endif
仅当内核开启EFI_STUB时生效,适配 EFI 启动模式;
保留 EFI 存根的读写数据,4KB 对齐,满足 PE/COFF 镜像格式要求。
[9]. BSS 段 + 栈段(内存清零核心)
. = BSS_START;__bss_start = .;.bss : { *(.bss) }_end = .;. = ALIGN(8);.stack : { *(.stack) }
.bss:存放未初始化的全局变量 / 静态变量,内核启动时会自动清零这段内存;
.stack:解压程序的栈空间,强制 8 字节对齐(ARM64 位兼容要求);
BSS_START:BSS 段起始地址,由内核内存布局配置。
[10]. 内核魔术字符号(启动校验)
_magic_sig = ZIMAGE_MAGIC(0x016f2818);_magic_start = ZIMAGE_MAGIC(_start);_magic_end = ZIMAGE_MAGIC(_edata);_magic_table = ZIMAGE_MAGIC(_table_start - _start);
0x016f2818:ARM Linux 内核标准魔术字,所有 ARM 内核通用,是启动的核心校验值;
记录内核起止地址、表偏移,供启动程序解析内核镜像。
[11]. 断言校验(防错机制)
ASSERT(_edata_real == _edata, "error: zImage file size is incorrect");链接器最后校验:镜像实际结束地址必须等于预期结束地址;
若不相等,直接报错终止编译,避免生成损坏的 zImage。
四 内核.lds 文件核心特性总结
强约束 PIC:通过丢弃.data段,保证 zImage 是位置无关代码,可在任意内存地址运行;
大小端兼容:适配 AX615 芯片的大小端模式,魔术字自动字节序反转;
启动校验机制:通过固定魔术字、表段,让 Bootloader / 硬件识别合法内核;
内存布局严格对齐:ARM 架构要求指令 / 数据 4/8 字节对齐,脚本全流程保证对齐;
容错校验:断言机制避免生成错误镜像,提升编译可靠性。
五 实际开发中的应用场景
[1].内核移植:修改TEXT_START/BSS_START适配 AX615 芯片的内存映射;
[2].启动问题排查:zImage 无法启动时,优先检查.table魔术字、ENTRY(_start)入口地址;
[3].裁剪内核:通过/DISCARD/丢弃多余段,减小 zImage 体积;
[4].EFI 启动适配:开启CONFIG_EFI_STUB,无需修改脚本即可支持 EFI 启动。
六 关键知识点速记
[1]. .lds = 链接脚本,定义内核镜像内存布局;
[2]. ENTRY(_start):内核第一条指令地址;
[3]. /DISCARD/:强制丢弃段,保证 PIC 特性;
[4]. 0x5a534c4b/0x016f2818:ARM zImage 固定魔术字(不同芯片的内核版本有差异);
[5]. .piggydata:存放压缩后的内核数据;
[6].断言ASSERT:编译期校验,防止生成坏镜像。
总结
这份AX615 的.lds是ARM zImage 专用链接脚本,核心服务于内核解压阶段,约束内存布局、保证位置无关运行、实现启动校验;
大小端适配、段丢弃、魔术字定义是 ARM 内核链接脚本的三大核心设计;
内核移植、启动调试时,.lds是必看文件,所有内存地址、段规则都由它定义。
以上为全文内容。

这里是女程序员的笔记本
15年+嵌入式软件工程师兼二胎宝妈
分享读书心得、工作经验,自我成长和生活方式。
希望我的文字能对你有所帮助