当前位置: 首页 > news >正文

链接与库

1. 概述

链接是将一系列目标文件合并为一个单一文件的过程,该文件可以被加载到内存中执行。链接的核心任务是符号解析重定位

  • 符号解析:将目标文件中的每个全局符号引用绑定到一个唯一的符号定义上。
  • 重定位:确定每个符号和指令的最终内存地址,并修改指令中对这些符号的引用。

根据链接发生的时间,链接主要分为静态链接(构建期)和动态链接(加载期或运行期)。

链接器的处理对象是目标文件,其格式通常为 ELF(Executable and Linkable Format)。目标文件主要分为三种形式:

  • 可重定位目标文件:编译器输出,包含二进制代码和数据,尚未链接。
  • 可执行目标文件:链接器输出,可直接运行。
  • 共享目标文件:动态库,可在加载或运行时被链接。

2. 基础构建块:ELF 格式深度解析

理解链接机制首先需要理解目标文件的结构。ELF 格式提供了两种视角:链接视角(节 Section)运行视角(段 Segment)

2.1 关键节

  • .text:已编译程序的机器代码。
  • .rodata:只读数据(如 printf 的格式化字符串、跳转表)。
  • .data:已初始化的全局变量和静态变量。
  • .bss:未初始化或初始化为 0 的全局变量和静态变量。不占用磁盘空间,仅在运行时占位。
  • .symtab:符号表,记录程序中定义和引用的函数和全局变量信息(位置、大小、类型)。
  • .rel.text / .rel.data:重定位条目,保存了代码段和数据段中需要修正的引用位置信息。

2.2 ELF 头

包含魔数(7f 45 4c 46)、字长(32/64位)、字节序、节头表偏移量等元信息。

3. 静态链接

静态链接由静态链接器(ld)在构建期完成,其核心逻辑是合并与修正

3.1 符号解析

链接器从左到右扫描输入文件(.o.a),维护“已定义符号集合”和“未定义符号集合”。

  • 强符号与弱符号规则

    • 强符号:函数定义、已初始化的全局变量。
    • 弱符号:未初始化的全局变量。
    • 处理原则:不允许有多个同名的强符号;若有一个强符号和多个弱符号,选强符号;若有多个弱符号,选占用空间最大的。
    • 库的链接顺序:如果库引用了某个符号,该符号的定义必须出现在引用它的文件或库之后。否则会报 undefined reference 错误。

3.2 重定位

符号解析完成后,链接器将相同属性的节合并(例如所有 .text 合并为一个大的 .text),并为每个符号分配唯一的运行时虚拟地址。

  • 重定位条目处理:链接器遍历 .rel.text 等节,根据指令类型进行补丁:

    • R_X86_64_PC32(相对地址重定位):计算公式为 S + A - P(目标地址 + 附加数 - 当前 PC 值)。
    • R_X86_64_32/64(绝对地址重定位):直接将符号的虚拟地址填入指令中。

4. 动态链接原理

静态链接将代码“拷贝”进可执行文件,而动态链接则将链接过程推迟到程序加载时或运行时

4.1 两种动态链接模式

模式 发生时机 机制 典型应用
加载时链接 程序启动时(main 执行前) 操作系统加载器根据可执行文件中的 .dynamic 段,将所需的 .so 映射到内存并进行符号解析。 大多数常规 Linux 程序依赖 libc.so
运行时链接 程序运行过程中 程序通过 API 主动加载库,获取符号地址,进行调用。 插件系统、浏览器插件、动态配置算法。
  • 运行时链接 API

    • dlopen():加载共享库。
    • dlsym():查找符号地址。
    • dlclose():卸载库。

4.2 关键疑问:为什么 .so 需要作为 ld 的输入?

这是理解动态链接构建流程的关键点。

对于加载时链接: 虽然动态库(.so)的代码和数据不会像静态库那样被复制到可执行文件中,但编译命令中仍然需要指定 .so(或通过 -l 指定)。

  • 原因:静态链接器 ld 需要在构建期进行符号决议。判断用到的符号是否存在定义?是否存在多个定义?是来自静态库还是动态库?如果没有发现定义,ld会报undefined reference。如果发现多个强符号定义,ld会报multiple definition。如果来自静态库,那么需要将对应模块合并到输出文件中,完成符号解析和重定位;如果是动态库,那么仅进行记录,链接过程推迟到加载时进行。

对于运行时链接: 使用 dlopen 加载的库,不需要作为 ld 的输入。

  • 原因:源代码中没有直接引用库中的符号,而是通过函数指针间接调用。静态链接器看不到这些引用,自然也不会去检查它们。这种灵活性也意味着直到 dlopen 运行时,程序才能发现库是否存在或符号是否正确。在使用dlopen将动态库手动加载到内存中后,又使用 dlsym 手动获取符号的内存地址,放入变量中,实际调用的时候是通过间接跳转进行调用的。如果dlopen或dlsym失败,都会返回NULL。PS:在Windows系统下,使用LoadLibrary和GetProcAddress进行运行时链接的过程是类似的。

5. 加载器与动态链接器的分工

当用户在 Shell 运行程序时,内核的加载器开始工作,但随后的细节分工如下:

  1. 内核加载器:读取 ELF 头,将可执行文件映射到内存(特别是加载解释器 /lib64/ld-linux.so),并将控制权转交给动态链接器。
  2. 动态链接器 (ld-linux.so)

    • 这是一个特殊的共享对象,它是自举的。
    • 映射依赖库:它通过系统调用 mmap 将程序依赖的其他 .so 文件映射到用户态虚拟内存。
    • 符号重定位:读取动态符号表和重定位表,修改程序内存中的 GOT(全局偏移表),填入符号的真实运行时地址。
    • 初始化:调用各 .so 的初始化函数(.init 段)。
    • 移交控制:最后将控制权转交给程序的 main 函数。

6. 位置无关代码 (PIC) 与 动态链接优化

动态链接的核心目标是让多个进程共享同一份物理内存中的库代码。为了实现这一点,必须保证代码段能在不同的虚拟地址空间中运行,这就是位置无关代码

6.1 核心思想:分离变化与不变

  • 代码段:是只读的,可以被多个进程共享。
  • 数据段:是进程私有的,包含需要重定位的绝对地址。

PIC 的核心是将“会变的地址引用”从代码段剥离,存放到数据段中。代码段通过相对寻址来访问数据段。

6.2 数据结构:GOT 和 PLT

  • GOT (Global Offset Table)

    • 位于数据段。
    • 存放外部全局变量和函数的运行时真实地址
    • 代码段不直接引用函数地址,而是引用 GOT 表中对应的条目。
    • PLT (Procedure Linkage Table)

    • 位于代码段(通常 .text 附近)。

    • 每个外部函数对应一个 PLT 条目(一段小存根代码)。
    • 作用:实现延迟绑定,提升程序启动速度。

6.3 延迟绑定流程

当程序第一次调用某个库函数时(如 printf):

  1. 程序跳转到对应的 PLT 条目。
  2. PLT 条目跳转到对应的 GOT 条目。
  3. 此时 GOT 中还没有真实地址,它指向 PLT 中的第二条指令(即“回跳”指令),并触发动态链接器去解析 printf 的真实地址。
  4. 动态链接器计算出地址后,将其填入 GOT。
  5. 控制流转回 printf 执行。

当程序第二次调用 printf 时:

  1. 跳转到 PLT。
  2. PLT 跳转到 GOT。
  3. 此时 GOT 已保存了真实地址,直接跳转执行,不再经过动态链接器。

这种机制极大地加速了动态链接程序的启动过程,因为只有被实际调用的函数才会进行解析。

http://www.jsqmd.com/news/471066/

相关文章:

  • 丹青幻境镜像免配置优势:预编译CUDA内核、diffusers版本锁、字体缓存
  • STM32 SPI DMA时序控制与低功耗自主通信实战指南
  • 探讨2026年比较好的焊工培训学校,品牌优势有哪些 - 工业设备
  • liquid源码分析之二:firpfbch
  • 通义千问3-4B-Instruct-2507 Agent实战:几行代码让AI调用工具,构建自动化工作流
  • Step3-VL-10B多模态推理实战:图文理解+数学推导+OCR文本结构化输出案例
  • 时序差分算法(一)
  • 深圳龙岗少儿体能训练机构怎么选?靠谱机构名单整理(2026参考) - 前沿公社
  • 中山性价比高的湘菜有哪些,口碑好的店该怎么选? - 工业品牌热点
  • SeqGPT-560M在Web开发中的应用:动态内容生成与优化
  • 效率提升秘籍:用快马AI自动生成数据库代码,专注核心业务逻辑设计
  • 2026年天津驾培性价比排行,解读晚上练车好处,盛康驾校值得选吗 - 工业推荐榜
  • cmake编译32位程序
  • 探讨口碑好的高纯高温煅烧α氧化铝粉企业排名,前十名有哪些? - myqiye
  • 【读书笔记】Introduction to Linear Algebra | 第 8 章:线性变换
  • 突破百度网盘限速壁垒:PDown高速下载工具深度评测
  • 鹰眼YOLOv8实战:智能会议室人数统计,AI帮你自动分析空间利用率
  • Youtu-2B响应截断?max_tokens参数调整实战
  • VSCode编写Markdown
  • 北京丰宝斋:回收名家字画无套路,免费上门,当场结算,藏家变现超安心 - 品牌排行榜单
  • Flux Sea Studio 效果展示:Transformer架构下的超写实海景作品集
  • 科学驭时,智赴前程——职大教育破解兴安盟高中生时间管理困局 - 企业推荐官【官方】
  • Linux容器基石:LXC核心概念与实践指南
  • ChatGPT从入门到精通PDF:AI辅助开发实战指南
  • 救命神器AI论文写作软件 千笔ai写作 VS WPS AI 研究生必备
  • “Missing CLIPVision model“错误解析:从根源修复到预防策略
  • ai赋能边缘设备,快马平台辅助生成stm32语音识别项目代码
  • STM32H7 ADC共用寄存器原理与多ADC同步工程实践
  • Tao-8k模型与ComfyUI可视化工作流结合:复杂任务编排实践
  • LDO环路稳定性:从极点分布到ESR补偿的实战解析