Linux 内核 Kbuild 中的 ld 调用机制

链接

在 Linux 内核及其衍生项目(如 Jailhouse、Xen、U-Boot)中,链接(link)阶段并不是通过显式的 ld 命令完成的,而是由 Kbuild 构建系统通过一套高度抽象、模板化的机制来统一管理。

本文将围绕两个核心问题展开:

  1. Kbuild 中的 ld 是在哪里、如何被调用的?
  2. 在 Kbuild 体系下使用链接器脚本(.lds)时,有哪些容易踩坑的地方?

一、Kbuild 中并不存在"直接调用 ld 的函数"

在阅读某些 Makefile 时,我们可能会困惑于如下规则:

make 复制代码
$(obj)/hypervisor.o: hypervisor.lds $(objs)
	$(call if_changed,ld)

问题是:

这里并没有看到 ld -T hypervisor.lds ...,那链接器到底是怎么被调用的?

答案是:

  • Kbuild 中并不存在一个叫"ld"的函数
  • ld 是一个 命令模板(command template)名 ,由 if_changed 间接展开

二、if_changed,ld 的真正入口在哪里?

if_changed 宏的定义

if_changed 系列宏定义在内核源码中,这里不深入:

复制代码
scripts/Kbuild.include

其核心职责是:

  • 判断命令行或依赖是否发生变化
  • 只有在变化时才执行对应命令

ld 对应的命令模板在哪里?

真正定义 如何调用链接器 的地方在:

复制代码
scripts/Makefile.lib

(不同内核版本略有差异,但结构一致)

概念化后的定义大致如下:

make 复制代码
quiet_cmd_ld = LD      $@
cmd_ld = $(LD) $(LDFLAGS_$(@F)) $(LDFLAGS) \
         -o $@ $(real-prereqs)

关键点在这里:

  • cmd_ld 才是真正的 ld 命令
  • if_changed,ld 实际上就是执行 cmd_ld
  • 所有参数都来自 Make 变量拼接,而非写死

三、为什么使用 $(@F) 而不是 $@

这是理解 Kbuild 的关键点之一

GNU make 自动变量回顾

变量 含义
$@ 目标完整路径
$(@F) 目标文件名(不含路径)
$(@D) 目标目录

例如:

make 复制代码
$(obj)/arch/riscv/hypervisor.o
  • $@build/arch/riscv/hypervisor.o
  • $(@F)hypervisor.o

Kbuild 的设计约定

Kbuild 规定:

按目标文件名(不含路径)设置专属 flags

make 复制代码
LDFLAGS_hypervisor.o := --whole-archive -T

因此在模板中只能使用:

make 复制代码
$(LDFLAGS_$(@F))

否则变量根本匹配不到。


四、链接器脚本 .lds 是如何"传给 ld 的"?

这是一个非常容易被误解的地方

常见 Kbuild 写法

make 复制代码
LDFLAGS_hypervisor.o := -T

$(obj)/hypervisor.o: hypervisor.lds $(objs)
	$(call if_changed,ld)

你会注意到:

  • -T hypervisor.lds 并没有显式写在命令中
  • .lds 只是作为 依赖文件

真正发生了什么?

Kbuild 的行为
  • $(LDFLAGS_$(@F)) 原样拼到 ld 命令前
  • $^(所有依赖)原样拼到命令后
  • 不解析 .lds,也不理解 -T 的语义
GNU ld 的行为

GNU ld 的规则是:

text 复制代码
-T script
    Use script as the linker script

即:

-T 后面的"下一个参数"会被当作 linker script

由于 .lds 通常是依赖列表中的第一个真实文件,最终形成的命令类似:

bash 复制代码
ld -T hypervisor.lds -o hypervisor.o a.o b.o

这是 ld 的命令行语义,而不是 Kbuild 的特殊规则


因此,严格来说:

Kbuild 只是按模板拼接参数;
-T.lds 的配对完全依赖 GNU ld 的命令行解析规则。


五、为什么一定要把 .lds 写进依赖?

这是一个工程级的设计决策

正确做法

make 复制代码
hypervisor.o: hypervisor.lds

原因

  1. 修改链接脚本会触发重新链接
  2. 保证布局、段地址、对齐变化不会被 silently 忽略
  3. 支持 make 的增量构建语义

如果你只在命令行里写 -T hypervisor.lds,但不写依赖,会有以下问题:

  • 修改 .lds 可能 不会触发重建,这是非常危险的系统级 bug

六、在 Kbuild 中使用链接器脚本的注意事项

1. 永远把 .lds 放进依赖列表

make 复制代码
target.o: target.lds $(objs)

2. 使用 LDFLAGS_<target>,不要写死命令

make 复制代码
LDFLAGS_target.o := -T --whole-archive

3. 注意 .lds 在依赖列表中的顺序

  • 通常放在最前
  • 避免被 -o 或其他参数"吃掉"

相关推荐
摸鱼仙人~8 分钟前
RAG 系统中的 TOC Enhance:用“目录增强”提升检索与生成效果
linux·运维·服务器
xingzhemengyou111 分钟前
Linux dmesg 查看系统启动日志
linux
华如锦12 分钟前
一.2部署——大模型服务快速部署vLLM GPU 安装教程 (Linux)
java·linux·运维·人工智能·后端·python·vllm
Jacob程序员15 分钟前
Linux scp命令:高效远程文件传输指南
linux·运维·服务器
corpse201019 分钟前
Transparent Huge Pages(透明大页)对redis的影响
linux·redis
Cx330❀20 分钟前
Linux进程前言:从冯诺依曼体系到操作系统的技术演进
linux·运维·服务器
阿巴~阿巴~21 分钟前
帧长、MAC与ARP:解密局域网通信的底层逻辑与工程权衡
linux·服务器·网络·网络协议·tcp/ip·架构·以太网帧
oMcLin22 分钟前
如何在 Manjaro Linux 上实现高效的 Ceph 存储集群,提升大规模文件存储的冗余性与性能?
linux·运维·ceph
Chennnng29 分钟前
ubuntu重装系统但是不改动文件的方法
linux·运维·ubuntu
2023自学中32 分钟前
linux虚拟机 CentOS 7 , yum 前端工具的使用,安装 命令行多媒体播放器
linux