尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
【Linux指南】动静态库系列(七):符号表与重定位:链接器如何把函数调用接起来
文章目录一、先看一个跨文件调用二、什么是符号三、用 nm 查看符号四、用 readelf -s 查看符号表五、undefined reference 的本质六、什么是重定位七、查看重定位表八、用 objdump 观察代码里的占位九、静态链接到底做了什么十、静态库为什么更新后程序必须重新链接十一、静态库链接顺序问题十二、符号表、重定位和库的关系十三、常用命令总结十四、总结上一篇我们认识了 ELF知道.o、.so、可执行文件都不是普通文件而是带有结构的二进制文件。ELF 中有代码、有数据、有 section、有 segment还有一个对链接非常重要的东西符号表。这篇文章要解决的问题是当main.c调用code.c里的run函数时编译器单独编译main.c时明明看不到run的实现为什么最后程序还能正确跳转到run答案就在符号解析和重定位中。一、先看一个跨文件调用准备两个文件。hello.c#includestdio.hexternvoidrun(void);intmain(){printf(hello\n);run();return0;}code.c#includestdio.hvoidrun(void){printf(run\n);}单独编译gcc-chello.c-ohello.o gcc-ccode.c-ocode.o此时hello.o中调用了run但run的实现并不在hello.o里。问题来了hello.o 如何知道 run 最终在哪里答案是它暂时不知道。它只是留下一个“未解决的符号引用”等链接阶段再解决。二、什么是符号在链接视角下函数名和全局变量名都可以看作符号。例如voidrun(void){}intg_val10;这里的run和g_val都是符号。符号大致可以分为两类定义符号当前文件提供了实现或实体 未定义符号当前文件用到了它但实现不在当前文件在上面的例子中hello.o 中run 是未定义符号 code.o 中run 是已定义符号链接器的工作之一就是把这些引用和定义匹配起来。三、用 nm 查看符号nm可以查看目标文件中的符号。查看hello.onm hello.o可看到0000000000000000 T main U printf U run这里的关键是T main U runT表示符号定义在代码段.text中说明main在hello.o里有实现。U表示 undefined说明run在hello.o中只是被引用还没有定义。再看code.onm code.o可能看到0000000000000000 T run U printf这里run是T说明code.o提供了run的函数实现。链接器看到hello.o 需要 run code.o 提供 run于是就可以把它们连接起来。四、用 readelf -s 查看符号表readelf -s也可以查看符号表readelf-shello.o你会看到符号的更多信息例如Num: Value Size Type Bind Vis Ndx Name ... FUNC GLOBAL DEFAULT UND run FUNC GLOBAL DEFAULT ... main其中UND表示 undefined也就是未定义符号。符号表告诉链接器这个目标文件定义了哪些符号 引用了哪些外部符号 每个符号大致属于什么类型 符号将来需要如何参与链接。五、undefined reference 的本质如果只链接hello.ogcc hello.o-omain会报错undefined reference to run这不是编译错误而是链接错误。因为编译hello.c时编译器只需要知道externvoidrun(void);它就可以相信run这个函数将来会存在。但链接时链接器必须真的找到run的实现。如果所有输入文件和库里都没有run链接器就无法生成最终可执行程序。所以undefined reference 链接器找不到某个符号的定义常见原因忘记链接某个.o文件。忘记写-lxxx。-L路径不对库没找到。库里根本没有这个函数实现。C 名字修饰导致符号名不匹配。六、什么是重定位符号解析解决了“函数在哪里定义”的问题但还不够。链接器还要解决一个更具体的问题调用指令中的地址应该填多少编译hello.c时编译器看到run();但它不知道run最终在可执行程序中的地址。于是它只能先留下一个需要修正的位置并记录到重定位表里。等链接器把hello.o、code.o合并成最终程序时它就知道run的最终地址了于是回头修正调用位置。这个过程就是重定位。通俗理解编译阶段先留坑 链接阶段填地址七、查看重定位表可以使用readelf-rhello.o你可能看到类似Relocation section .rela.text Offset Info Type Sym. Name ... ... ... ... run ... ... ... printf这说明hello.o的.text代码段中有一些位置需要在链接时被修正。重定位表记录的信息大致包括哪里需要修正 修正和哪个符号有关 用什么方式修正链接器根据这些信息把未确定的地址修正为最终地址。八、用 objdump 观察代码里的占位可以反汇编目标文件objdump-dhello.o你会看到call指令但目标地址可能还不是最终地址。因为在.o阶段run的最终位置还没确定。链接后再反汇编最终程序gcc hello.o code.o-omain objdump-dmain这时call run的地址就已经被链接器修正好了。这就是静态链接中非常核心的一步。九、静态链接到底做了什么现在我们可以重新理解静态链接。静态链接不是简单地把文件拼接到一起而是包含多个工作收集所有输入目标文件。扫描符号表。匹配未定义符号和已定义符号。合并同类 section例如.text、.data。分配最终地址。根据重定位表修正代码和数据中的地址。生成最终可执行文件。如果涉及静态库.a链接器还会从库中抽取需要的目标文件参与链接。所以静态库中的 .o本质上也会参与上述链接过程。十、静态库为什么更新后程序必须重新链接假设你用libmyc.a生成了程序main。静态链接时库中相关代码已经进入main。后来你修改my_string.c重新生成新的libmyc.a但不重新链接main。此时运行旧main行为不会变化。原因是旧 main 里已经包含旧版本库代码 新 libmyc.a 不会自动影响旧 main。只有重新链接gcc main.c -I./include -L./lib-lmyc-omain新库实现才会进入新的可执行程序。十一、静态库链接顺序问题在某些情况下静态库链接顺序也会影响结果。一般建议把依赖库放在使用它的目标文件后面gcc main.o -L.-lmyc-omain而不要写成gcc -L.-lmycmain.o-omain原因是传统链接器通常从左到右扫描输入。当它扫描到库时如果前面还没有未解决的符号引用可能不会从库中抽取目标文件。动态库场景下这个问题不一定表现得同样明显但学习静态链接时应该养成正确顺序习惯。十二、符号表、重定位和库的关系现在把它们串起来.c 源文件 - 编译成 .o - .o 中包含符号表和重定位表 - 多个 .o 或静态库参与链接 - 链接器解析符号并修正地址 - 生成可执行文件静态库.a只是把多个.o管理起来。真正起作用的仍然是里面.o的符号表和重定位信息。动态库.so的符号解析和重定位更复杂因为很多工作会推迟到加载和运行阶段。后面讲动态链接时会继续深入。十三、常用命令总结查看符号nm hello.o readelf-shello.o查看重定位信息readelf-rhello.o查看反汇编objdump-dhello.o objdump-dmain链接目标文件gcc hello.o code.o-omain查看未定义符号nm hello.o|grep U 十四、总结链接器最核心的工作可以概括为两件事符号解析找到每个外部符号的定义 重定位把代码和数据中暂时未知的地址修正为最终地址这也是undefined reference、静态库更新必须重新链接、链接顺序可能影响结果的根本原因。下一篇我们从链接继续走向加载一个 ELF 可执行文件还没运行时为什么里面已经记录了地址操作系统又是如何根据 ELF 的 segment 初始化进程地址空间的
RELATED

相关推荐

GEO检测系统源码 AI可见性检测平台 支持14个AI引擎

GEO检测系统源码 AI可见性检测平台 支持14个AI引擎

一、系统是做什么的 这是一套「AI 回答引擎可见性检测」服务。企业关心的不再是百度排第几,而是用户问 ChatGPT、豆包、Kimi 这类 AI「有什么推荐的品牌」时,AI 的回答里有没有提到自己、排在第几句、说的是好话还是坏话、AI 是引用哪些网站得出的结论。…

📅 2026/10/3 21:17:30
Babylon.js 9.0 完全解析 · 第 5 篇:Gaussian Splatting 全家桶——从“splat 查看器“到生产管线

Babylon.js 9.0 完全解析 · 第 5 篇:Gaussian Splatting 全家桶——从“splat 查看器“到生产管线

如果说 9.0 有一个投入最猛的方向,那一定是 Gaussian Splatting。官方发布稿用了"take it to the next level"来形容,并且特别鸣谢了 Adobe 的贡献。本篇梳理这条从 7.0 一路铺到 9.x 的完整技术线:五种格式、阴影、三角泼溅、多部…

📅 2026/10/5 13:53:50
[光学原理与应用-605]:如果把光学元器件看成是 “系统”,“光” 看成是 “信号”。那么信号与系统的分析方法,处理适用电学模拟信号系统,也适用光学系统,也适用数字信号处理系统。他们高度的统一。

[光学原理与应用-605]:如果把光学元器件看成是 “系统”,“光” 看成是 “信号”。那么信号与系统的分析方法,处理适用电学模拟信号系统,也适用光学系统,也适用数字信号处理系统。他们高度的统一。

信号‑系统视角:电学、光学、数字信号处理的底层统一把光学器件当作系统,光场当作输入输出信号,我们会发现:模拟电路、光学系统、数字信号处理,三者共享同一套「信号与系统」理论框架,物理载体不同&#xf…

📅 2026/10/22 9:21:44
MORE NEWS

更多资讯

📰

nfc能读取电工证吗合格标准与通过率

电工证还能用NFC读取?三门峡工人必看保姆级教程 工地太忙根本没时间复习考试,你是不是也经常听到身边人抱怨?现在连考个电工证都得抽出时间复习,生怕一次不过浪费钱和时间。但最近不少工友在问:“ nfc能读取电工证吗…

📰

青羊区低压电工证官方回应:学历不高也能报,但得看这些细节

青羊区低压电工证官方回应:学历不高也能报,但得看这些细节 你是不是也在担心, 学历不高怕报不上名 ?别急,官方回应来了!青羊区低压电工证的报名门槛其实并不高,只要你符合基本条件,即使学历不是特别高,也有机会拿到这张含金量很高的证书。下面我们…

📰

特种工施工重点与官方报名入口全攻略

特种工施工重点与官方报名入口全攻略 工地太忙根本没时间复习考试,这几乎是每个施工队长、项目负责人的共同困扰。特别是特种作业操作证的考试内容繁杂,复习时间又有限,稍有不慎就可能导致考试失败,耽误项目进度。作为平顶山中小施工企业的负责人,你可能…

📰

徐勇军高压电工证在哪考?工地太忙根本没时间复习考试怎么办

徐勇军高压电工证在哪考?工地太忙根本没时间复习考试怎么办 工地太忙根本没时间复习考试,这是很多一线电工的共同烦恼,尤其是像你这样在安阳房建项目上奋战的施工人员。徐勇军高压电工证是从事高压电气作业必须的“通行证”,但很多人对考试流程、报名条件…

📰

nfc能读取电工证吗 低压电工证多久拿证

nfc能读取电工证吗 低压电工证多久拿证 学历不高怕报不上名?别慌,电工证报考门槛其实没你想象的高,关键是你得知道怎么走流程。很多人担心自己没有高学历、没有相关经验,就以为自己肯定报不了名,其实不然,只要满足基本条件,哪怕是初中毕业,也有机…

📰

高压电工证公司盖章怎么考?学历不高怕报不上名?上岗必备流程全解析

高压电工证公司盖章怎么考?学历不高怕报不上名?上岗必备流程全解析 你是不是也担心自己学历不高,考高压电工证公司盖章怕被拒?别慌,很多一线工人都是从零基础起步的,关键是你得搞清楚流程、准备材料和考试内容。高压电工证不是高门槛,而是 上岗必备…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬