80x86汇编编程-2(程序链接)

官方信息技术老师·14 页·深入(追求细节与边界)·0 次浏览·3 天前
链接器符号表重定位目标文件

80x86汇编编程-2(程序链

看清 .o 文件如何被拼成可执行:符号解析、重定位、库链接全拆解

按 空格/→ 演示下一步

1 / 14 页

全部页面点击任意一页,跳回舞台从这页播放

链接器符号表重定位目标文件

80x86汇编编程-2(程序链

看清 .o 文件如何被拼成可执行:符号解析、重定位、库链接全拆解

1第 1 页 · 80x86汇编编程-2(程序链

程序链接-1

前面我们看到编译器把单个 .c 编成 .obj——但实际程序往往有十几个源文件,每个都生成自己的 .obj。谁负责把它们拼成最后那个能跑的 .exe?

链接的本质
把若干 .obj 与库文件合并,生成一个可直接加载运行的 .exe
符号解析
一个文件里 CALL 引用的标签在另一文件里定义,链接器负责把它们对上
地址重定位
每个 .obj 都假设自己从地址 0 起装入,链接器算出真实地址并回填到指令中
库的处理
只从 .lib 中挑出被实际引用的目标模块,未被引用的代码不会进入 .exe
工厂用预制件装配房屋对应 →链接器合并目标文件

墙板来自不同车间,链接器像现场工长:对齐接口、接通管线、丢掉多余的零件

2第 2 页 · 程序链接-1

程序链接-2

上一页我们看到链接器把多个 .o 拼成一个可执行文件——但拼接远不是复制粘贴。两件棘手事必须解决:某个函数到底在哪个目标文件里?拼好后地址都挪了,原指令里的地址怎么办?

符号解析
把每个未定义的符号引用,与唯一定义位置绑定,建立引用→定义的映射
段合并
将各 .o 的同类段(.text、.data)按规则拼接成一个连续大段
重定位
根据合并后的实际地址,修正指令和数据中所有需要调整的地址字段
静态 vs 动态链接
静态在编译期完成;动态在程序运行时才装入并完成绑定
编辑团队协作编一本书对应 →链接过程

各作者交章节(.o),编辑统一页码(重定位),并把「参见第三章」改成新页码(符号解析)

3第 3 页 · 程序链接-2

程序链接-3

程序链接-3:定义、要点与典型应用

程序链接-3
程序链接-3:定义、要点与典型应用
4第 4 页 · 程序链接-3

程序链接-4

上一节我们看到静态链接把所有库代码打包进可执行文件。十台机器跑同一程序,内存里就有十份相同的 printf。动态链接正是为解决这一痛点——库代码运行时才加载,多个进程共享一份。

动态链接
运行时由动态链接器加载共享库,多个进程共用同一份库代码,节省内存且便于升级
共享库 .so
Linux 下 ELF 格式的可加载文件,存放可被多个进程复用的已链接代码
位置无关代码 PIC
库代码用相对地址(如 RIP-相对寻址)编写,可加载到任意地址而无需修改
PLT/GOT 延迟绑定
首次调用外部函数时才解析地址,加快启动;通过 PLT 跳板跳到 GOT 表项查真实地址
图书馆借阅对应 →动态链接机制

书统一存放,要用时再借,多人共享同一本;共享库也一样——运行时按需加载、多进程共享代码

5第 5 页 · 程序链接-4

程序链接-5

上页我们讲到多个进程能共享同一份共享库——但代价是这份代码必须在任何加载地址都能跑。传统的绝对寻址会立刻失效。这就是位置无关代码(PIC)要解决的问题。

PIC 定义
代码不绑定绝对加载地址,加载到内存何处都能正确运行
PC 相对寻址
跳转与分支用当前指令地址 + 偏移计算目标
GOT 全局表
全局变量与外部函数地址存进 GOT 表,代码通过表项间接访问
PLT 延迟绑定
函数调用经 PLT 桩跳转,首次调用才触发动态链接器解析真实地址
共享收益
多个进程可映射同一份代码页,真正节省物理内存
酒店房卡房间号对应 →PIC 相对寻址

房卡写『前台左转第3门』而非『XX路123号』:'前台'对应当前位置,'左转第3门'对应相对偏移

6第 6 页 · 程序链接-5

程序链接-6

链接-5里库代码段共享给多个进程,但库每次加载地址都不同——跳转指令的目标怎么写死?答案是让代码本身「位置无关」,跳转时再查一张可写的表。

PIC位置无关代码
代码不假设加载地址,用相对寻址生成可任意重定位的指令
GOT全局偏移表
数据段的一张可写表,每个外部符号占一个槽,运行时填入真实地址
PLT过程链接表
代码段的一组跳转桩,未解析时回跳触发绑定,解析后直跳真实地址
延迟绑定
符号地址等到第一次调用才解析,加快启动;可设LD_BIND_NOW关闭
酒店前台代客找房间对应 →PLT/GOT延迟绑定

客人(调用方)问前台(PLT),前台查登记本(GOT)报房号;第一次真查,后续直接报

7第 7 页 · 程序链接-6

程序链接-7

上一页我们把多个 OBJ 合成一个 EXE,但 Windows 里大量代码躺在 DLL 里——它们怎么被装进你的进程?这页拆开看动态链接的几个关键。

动态链接库
独立编译的代码模块,按需映射到进程地址空间
导入表与导出表
EXE 声明需要哪些函数,DLL 声明提供哪些函数
加载时重定位
DLL 首选地址被占时才换位置,并修补全部跳转
IAT 调用桩
EXE 先跳到 IAT 表项,运行时再被改写为真实地址
小区公共健身房对应 →DLL 共享代码

不必每户自建,拎包来用;冲突时换地方但功能不变

8第 8 页 · 程序链接-7

程序链接-8

链接器默认按段类型顺序塞进内存——但嵌入式固件常常要把启动代码钉死在 0x08000000、把中断向量表占据最低 256 字节。这些精细布局就要靠链接器脚本指挥。

MEMORY 命令
声明硬件内存区域,写出每段 ROM/RAM 的起始地址与长度
SECTIONS 命令
把输入段映射到内存区域,决定每个段的最终落位
位置计数器
用符号 . 代表当前地址,. + offset 表示相对偏移
符号导出
脚本里定义全局符号,C/汇编代码可 extern 引用
KEEP 与 ALIGN
KEEP 防关键段被 strip 掉,ALIGN 保证地址对齐
建筑总平面图对应 →链接器脚本

MEMORY 是地块划线(哪些地址可用),SECTIONS 把各段落到具体房间

9第 9 页 · 程序链接-8

程序链接-9

动态链接把共享库加载进内存后,程序怎么知道函数实际跑在哪里?靠 PIC 和 GOT/PLT 这套精巧设计:代码不挑地址,地址查表获取,首次调用才解析。

PIC位置无关代码
指令地址用相对偏移,不依赖加载位置,共享库可被加载到任意内存
GOT全局偏移表
存放动态库函数/变量实际地址,程序启动或首次调用时被填充
PLT过程链接表
跳板代码段,首次调用走解析流程,后续通过GOT直接跳转
延迟绑定
符号解析推迟到第一次调用,减少启动开销和未用符号浪费
外卖取餐码对应 →PLT/GOT延迟绑定

取餐码(PLT)是占位符,第一次去窗口查实际摊位(GOT解析),记下来下次直接喊

10第 10 页 · 程序链接-9

程序链接-10

链接器把多个 .o 文件拼成一个可执行文件。但「怎么拼」有两种思路——焊死还是留口?这就是本页要分清的事。

静态链接
把所有用到的库代码都复制进可执行文件,运行时不再依赖外部
动态链接
可执行文件只留接口表,运行时由加载器把共享库映射进进程
PIC 与重定位
动态库地址未定,代码须位置无关,加载时再做地址修正
权衡取舍
体积、启动速度、升级维护、版本兼容——两者各有所长
在家做饭 vs 点外卖对应 →静态链接 vs 动态链接

做饭把食材搬进厨房(焊死),外卖只下单菜现做(运行时拼)

11第 11 页 · 程序链接-10

程序链接-11

编译器把每个 .c 单独编译成 .o 时,不知道变量和函数最终落在内存哪里。它在引用地址处先挖坑、登记重定位表,链接器合并段后来填这些坑。

重定位需求
目标文件中地址引用是相对的,编译器无法预知最终位置
条目三要素
记录 offset(偏移)、type(类型)、symbol(符号)
R_386_32
绝对重定位:直接把符号最终地址写入4字节槽
R_386_PC32
相对重定位:写入符号地址相对当前指令的偏移
链接器修补
合并段后遍历条目表,按类型逐项 patch 机器码
装修预留的电源插座孔对应 →目标文件里的重定位条目

砌墙时孔位先留着,按实际面板型号最后补上

patch=S+AP\text{patch} = S + A - P
12第 12 页 · 程序链接-11

本节要点

  • 链接 = 符号解析 + 重定位,两阶段缺一不可
  • 未定义符号在目标文件中以占位形式存在
  • 同名同类段被链接器拼接为单一映像
  • 静态库只链入被引用到的目标文件
  • 重定位条目决定地址补丁的写入方式
延伸主题:动态链接与共享库链接器脚本定制段布局ELF / PE 文件格式细节
13第 13 页 · 本节要点

课后思考

先独立作答并写下理由,再逐条对照参考答案,检验自己能否说清链接中的关键步骤。

1为什么链接器必须先完成符号解析,才能确定重定位项的最终地址?

参考答案先确定外部符号的唯一归属,链接器才能把重定位项记录的偏移加到真实基址,并写回机器码。

2若两个目标文件都导出同名全局符号,链接器会怎样取舍?工程中如何提前避免冲突?

参考答案依工具链规则处理强、弱符号:多个强定义通常报歧义,弱定义才可能按规则覆盖。工程上应统一命名、少暴露全局符号。

3若改用链接脚本或调整段顺序,同一目标文件还能得到相同地址吗?为什么?

参考答案不能保证。链接器按脚本安排各段,再按符号绑定完成重定位;脚本、段顺序或符号归属一变,地址结果就可能改变。

14第 14 页 · 课后思考