
mold 链接器核心概念全解析从 DSO、重定位到符号解析的术语级源码导读【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/moldmold 是一个现代链接器它将编译器产出的多个目标文件组合成可执行文件或共享库。本文以 docs/glossary.md 中的术语表为骨架逐条解读理解 mold 源码所必需的链接器核心概念DSO、目标文件、虚拟地址空间、重定位、静态库、符号并逐一给出 src/ 与 lib/ 中对应的实现证据帮助读者在阅读 mold 源码之前先建立完整的概念地图。DSO动态共享对象DSODynamic Shared Object即.so文件常被称为共享库shared library、动态库dynamic library或共享对象shared object。它包含多个可执行文件与/或其他 DSO 共同使用的函数和数据。在运行时一个 DSO 被加载到虚拟地址空间中的一段连续区域。从源码结构看mold 对 DSO 的处理遍布整个链接流程mold需要区分普通目标文件与 DSO动态库并为 DSO 生成.dynsym、.dynstr、.gnu.version等动态节相关逻辑集中在 src/elf.cc 与 src/output-chunks.cc 中。文档中DSO 必须被链接成可加载到虚拟地址空间任意地址的形式这一结论正是下文虚拟地址空间一节中 MMU 存在性的直接推论。Object file目标文件Object file目标文件.o文件包含机器代码和数据但它不是自包含的因此无法直接执行。文档以printf为例做了精辟说明你在 C 源码中#include stdio.h编译器只学到了printf的类型签名并不知道printf的实现因此编译产物中不会包含printf的函数代码只会留下一个对printf的引用。要使其可执行必须把目标文件与其他目标文件或共享库链接起来。这正是链接器存在的根本原因编译期到链接期的信息鸿沟。编译器负责把单个源码文件翻译为机器码链接器负责把分散的机器码拼装为完整的程序。Virtual address space虚拟地址空间指针的值如0x803020并不是物理内存地址。现代 CPU 内置MMUMemory Management Unit内存管理单元所有内存访问都会先经 MMU 翻译为物理地址翻译前的地址即虚拟地址。除内核编程外用户态程序接触到的所有地址都是虚拟地址。操作系统内核控制 MMU使每个进程都独占整个虚拟地址空间两个进程即使使用相同的虚拟地址也会被映射到不同的物理地址互不冲突。MMU 的存在对链接器有直接影响这是本文档中最重要的推论之一主可执行文件在进程启动时是第一个被映射的此时虚拟地址空间中尚无任何代码和数据因此映射总是成功链接器可以把它链接到某个固定地址而 DSO 在主可执行文件以及可能的其他 DSO之后才被加载位置不确定因此共享库必须被链接成可加载到任意地址的形式。这一结论在 mold 源码中有直接体现src/mold.h 中Context的image_base默认值为0x200000即默认的可执行文件加载基址src/cmdline.cc 提供了--image-base与--physical-image-base命令行参数来调整该基址并在 src/cmdline.cc 中按页大小向下对齐在 src/cmdline.cc 中校验基址是否页对齐。可执行文件之所以可以钉死在固定地址正是因为它是虚拟地址空间的第一个映射者。Relocation重定位Relocation重定位是交给链接器的、关于如何链接目标文件或动态对象的信息。目标文件可以引用其他目标文件中的函数或数据。文档给出了一个 x86-64 的经典反汇编示例26: e8 00 00 00 00 callq 2b bar0xb 27: R_X86_64_PLT32 foo-0x4解读如下callq的机器码操作码是0xe8其后四个字节是位移displacement即分支目标相对该指令末尾的偏移注意位移是 0——编译器不知道foo在运行时位于何处因此写入 0 作为占位符并额外写入一条重定位记录R_X86_64_PLT32关联符号foo链接器读取这条重定位计算 call 指令与函数foo之间的偏移用真实位移覆盖占位符 0。重定位类型非常多。例如若想用符号的绝对地址而非位移来修补就需要使用R_X86_64_ABS64。在 mold 源码中重定位的处理是各架构后端文件的核心工作。以 x86-64 为例src/elf.h 定义了R_X86_64_PLT32 4等枚举值src/arch-x86-64.cc 等处对R_X86_64_PLT32、R_X86_64_PC32、R_X86_64_GOTPCREL、R_X86_64_GOTPCRELX等类型逐一实现修复逻辑——例如 src/arch-x86-64.cc 展示了把call __tls_get_addr这类代价较高的指令序列松弛relax为更便宜的 TLS 指令序列时对重定位类型的逐一改写src/elf.cc 则把各重定位类型的名称登记在案用于错误诊断输出。此外src/passes.cc 注释指出 x86-64 用R_X86_64_PLT32这类不取地址的重定位来区分函数引用场景这是链接器判断是否需要生成 PLT 桩代码的重要依据。Static library静态库Static library静态库.a文件常被称为归档文件archive file或归档archive。它本质上是一个容器与 tar 或 zip 类似——文档甚至指出技术上并没有理由不能用 tar 或未压缩的zip只是传统上链接器采用.a格式。静态库包含目标文件可以与其他目标文件/归档一起传给链接器。其关键行为是链接器仅在需要解析未定义符号时才从归档中抽取目标文件也就是说归档中的目标文件默认不参与链接只作为补充来提供缺失的定义与之相反直接传给链接器的目标文件总是被链接到输出中。这正是库的理想形态只有当你真正使用了某个库函数时它才会被链接进来。为了最大化这一收益常用于静态库的库文档举例为 musl libc 的src/stdio目录通常被拆分为许多小文件每个函数一个目标文件这样你只引入真正用到的函数。mold 的 src/archive-file.cc 开头的注释完整复述了这一语义并补充了细节归档分为常规归档与瘦归档thin archive前者直接内嵌目标文件内容后者只包含路径名实际内容需按路径读取src/archive-file.cc文档提到mold 不使用静态库的符号表若符号表存在则直接忽略——这一点在 src/archive-file.cc 中可见端倪ArHdr结构把//字符串表与/、/SYM64/符号表的成员识别出来并跳过mold 靠直接扫描各成员的目标文件符号来按需抽取而非依赖归档内置符号表。Symbol符号Symbol符号是为输入文件或输出文件中某个具体位置分配的标签。例如定义了函数foo并编译后目标文件中会有一个指向foo机器码起始位置的符号foo。关于符号文档提炼了以下要点命名符号名通常就是函数名或变量名对于匿名对象如字符串字面量编译器会生成唯一符号通常以.开头以避免与用户符号冲突。C 名字改编manglingC 符号名是复杂的改编mangled名字。因为foo这样的简单名字在 C 中无法唯一定位函数或数据——它可能位于某个命名空间、可能是某类的静态成员也可能是重载函数——编译器必须把命名空间名、类型信息等附加到标识符上。例如命名空间bar中的函数int foo(int)被改编为_ZN3bar3fooEi。定义符号与未定义符号定义符号指向文件中某个位置可能包含函数的机器码或变量的初始值未定义符号不指向任何地方必须在链接期与同名定义符号合并这个过程称为名字解析name resolution。例如使用printf的程序通常含有一个printf未定义符号必须与libc.a或libc.so中printf的定义符号链接才能构成完整程序。mold 源码中的印证src/mold.h 定义了模板类Symbol其成员originsrc/mold.h 附近用TaggedPtr同时指向输入节、输出节、节片段或空绝对符号value存放符号值绝对符号时即地址否则相对于originis_weak、is_imported、is_exported等标志位src/mold.h精确刻画了符号的链接语义——特别是符号由某个文件拥有若多个文件定义同名符号则由最强定义者拥有这一注释src/mold.h正是文档中名字解析的工程实现弱符号与强符号的优先级在 src/input-files.cc 的排序键中体现STB_WEAK的弱符号会被标记src/input-files.ccC 名字改编的解码在 lib/demangle.cc 中实现当符号名以_Z开头时调用abi::__cxa_demangle还原为可读形式这与文档给出的_ZN3bar3fooEi例子完全对应_Z即 Itanium C ABI 改编名的前缀。术语之间的内在联系把这六个术语串起来就是一次完整链接的核心链路编译器产出目标文件.o其中充满重定位记录与符号表链接器读取符号执行名字解析把未定义符号与定义符号配对必要时从静态库.a中按需抽取目标文件或者与DSO.so建立动态依赖链接器确定输出布局可执行文件可以钉在虚拟地址空间的固定基址mold 默认0x200000而 DSO 必须位置无关最后链接器根据每条重定位记录逐一把占位值修补为真实地址产出可执行文件或共享库。mold 的源码正是沿着这条链路组织的InputFile负责解析目标文件与归档Symbol及其解析逻辑负责名字解析各arch-*.cc文件负责架构相关的重定位修复OutputChunk负责输出布局。理解了本文的术语再进入 src/mold.h 与 src/elf.cc 阅读源码将不再被这些基础概念所阻。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考