Rust开发操作系统时linker(链接器)的含义、功能及连接对象是什么
关于目标系统的确认
你在x86_64-os.json中自定义的编译目标,就是你计划开发的自研x86_64架构操作系统。
Rust官方内置的编译目标均对应已有的成熟运行环境(比如x86_64-unknown-linux-gnu对应Linux用户态环境、x86_64-pc-windows-msvc对应Windows环境),自研操作系统没有现成的适配目标,因此需要通过自定义JSON配置告诉rustc目标环境的各项参数:架构尺寸、大小端、是否有操作系统依赖、使用什么链接器等,最终用这个目标编译出的产物就是可以直接在你自研OS上运行的内核二进制。
链接器的核心功能与连接对象
程序编译不是一次性生成完整可执行文件的,整个流程可以拆成两个独立阶段:
- 编译阶段:rustc会把每个独立的代码单元(包括你自己写的内核代码、引用的Rust核心库/alloc库代码)分别编译为独立的目标文件(.o格式)。这些目标文件里已经包含了对应模块的机器码、常量、全局变量,但所有地址都是临时的相对地址,跨模块调用的函数、跨模块访问的全局变量都只留了占位标记,没有绑定真实的内存地址,单独的目标文件根本无法直接运行。
- 链接阶段:链接器的核心工作就是把编译阶段产出的所有零散目标文件、需要静态打包进内核的库文件全部组装成一个完整可运行的二进制文件,具体要做三件事:
- 段合并:把所有目标文件里同类型的段(比如存执行代码的
.text段、存只读常量的.rodata段、存已初始化全局变量的.data段、存未初始化全局变量的.bss段)按规则合并,消除重复内容 - 符号解析:匹配所有跨模块引用的符号(函数名、全局变量名都是符号),把每个符号引用指向它实际定义的内存位置
- 地址重定位:按照你指定的内核内存布局,给所有代码、数据分配最终的虚拟内存地址,把之前目标文件里留空的占位地址全部替换为真实可寻址的地址,最终生成可以直接加载到内存启动的内核镜像。
- 段合并:把所有目标文件里同类型的段(比如存执行代码的
简单说,链接器连接的核心对象就是各个独立编译产出的目标文件、静态依赖库,输出的是可以直接在目标环境运行的完整二进制。
为什么选择rust-lld作为链接器
系统默认的链接器(比如Linux平台默认的GNU ld)是为宿主平台的程序设计的,链接时会默认依赖宿主系统的动态库、按照宿主平台的用户态程序内存布局生成二进制,完全不适配无操作系统依赖的freestanding内核开发场景,甚至会直接链接失败。
而Rust自带的rust-lld是LLVM工具链提供的跨平台链接器,不依赖宿主系统的库与链接规则,可以完全遵循你自定义的目标配置、链接脚本要求生成符合自研OS启动要求的内核二进制,不会引入多余的宿主系统内容。
补充提示:你贴出的配置片段存在JSON语法错误,
"executables": true行末尾缺少逗号,修正后的完整配置片段如下:
{ "llvm-target": "x86_64-unknown-none", "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", "target-c-int-width": "32", "os": "none", "executables": true, "linker-flavor": "ld.lld", "linker": "rust-lld" }
内容的提问来源于stack exchange,提问作者izzy
相关产品推荐
相关产品推荐

