从静态库、动态库到 ELF 加载与 GOT 机制一、为什么需要库现实中每个程序都要依赖很多基础的底层库不可能每个人的代码都从零开始。库本质上是一种可执行代码的二进制形式可以被操作系统载入内存执行。Linux 下主要有两种库类型Linux 后缀Windows 后缀链接时机静态库.a.lib编译链接时动态库.so.dll程序运行时二、静态库2.1 特点静态库在编译链接时把库代码链接到可执行文件中程序运行时不再需要静态库。删除.a文件后程序照样可以运行。注意gcc 默认优先使用动态库只有找不到.so时才采用同名静态库。也可以使用-static强制静态链接。2.2 生成静态库使用arGNU 归档工具打包.o文件libmystdio.a: my_stdio.o my_string.o ar -rc $ $^ %.o: %.c gcc -c $ar的常用参数rreplace替换或添加ccreate创建t列出库中文件vverbose显示详细信息$ ar-tvlibmystdio.a rw-rw-r--1000/10002848Oct2914:352024my_stdio.o rw-rw-r--1000/10001272Oct2914:352024my_string.o2.3 使用静态库# 场景1头文件和库文件安装到系统路径gcc main.c-lmystdio# 场景2库文件在当前目录gcc main.c -L.-lmystdio# 场景3有独立路径gcc main.c -I头文件路径 -L库文件路径-lmystdio关键选项-I指定头文件搜索路径-L指定库文件搜索路径-l指定库名去掉lib前缀和.a/.so后缀三、动态库3.1 特点动态库在程序运行时才被链接多个程序可以共享同一份库代码因此可执行文件更小节省磁盘空间物理内存中一份动态库可被多个进程共用便于更新维护实现二进制级别代码复用3.2 生成动态库libmystdio.so: my_stdio.o my_string.o gcc -o $ $^ -shared %.o: %.c gcc -fPIC -c $两个关键参数-shared生成共享库格式-fPIC产生位置无关码Position Independent Code3.3 使用动态库gcc main.c -L.-lmystdio./a.out但此时直接运行可能会失败$ ldd a.out linux-vdso.so.1(0x00007fff4d396000)libmystdio.sonot found libc.so.6/lib64/libc.so.6(0x00007fa2aef30000)3.4 动态库运行搜索路径解决方法有四种拷贝到系统库路径/usr/lib、/usr/local/lib、/lib64等建立软链接到系统库路径修改环境变量LD_LIBRARY_PATH修改配置文件/etc/ld.so.conf.d/xxx.conf然后执行ldconfig重新加载# /etc/ld.so.conf.d/bit.conf/root/tools/linux# 生效ldconfig四、ELF 文件格式4.1 四种 ELF 文件类型说明可重定位文件.o文件可与其他目标文件链接可执行文件可运行的程序共享目标文件.so文件内核转储core dumps进程执行上下文4.2 ELF 的四大组成ELF 头描述文件主要特性定位其他部分程序头表列举所有有效段segments节头表包含对节sections的描述节基本组成单位如代码节、数据节等4.3 常见节Section节名作用.text机器指令代码.data已初始化的全局变量、静态变量.bss未初始化的全局变量、静态变量预留位置.rodata只读数据如字符串常量.symtab符号表记录函数名、变量名.got全局偏移表.plt过程链接表4.4 链接视图 vs 执行视图ELF 提供两种视角链接视图对应节头表粒度更细静态链接分析时关注执行视图对应程序头表告诉操作系统如何加载运行时关注为什么要把 section 合并成 segment为了减少页面碎片提高内存使用效率。例如.text为 4097 字节、.init为 512 字节若不合并占用 3 个页面每页 4KB合并后只需 2 个页面。同时相同属性的 section 合并成一个 segment 后可以实现统一的访问权限控制。五、ELF 形成与加载5.1 ELF 形成可执行文件的两个步骤编译将 C/C 源代码翻译成.o目标文件链接将多份.o文件的 section 进行合并5.2 静态链接研究静态链接本质就是研究.o如何链接。看一个例子// hello.c#includestdio.hvoidrun();intmain(){printf(hello world!\n);run();return0;}$ objdump-dhello.o 0000000000000000main:0: f3 0f 1e fa endbr644:55push %rbp... f: e8 00 00 00 00 callq14main0x14# printf 地址为 0...19: e8 00 00 00 00 callq 1emain0x1e# run 地址为 0可以看到call指令的跳转地址都被设为 0。这是因为编译hello.o时编译器完全不知道printf和run的地址。这个地址会在链接时被修正通过readelf -s code.o可以看到符号表中run是GLOBAL DEFAULT 1已定义puts是GLOBAL DEFAULT UND未定义链接时链接器根据.o文件中的重定位表找到需要重定位的函数和全局变量修正它们的地址。这个过程叫做静态重定位。链接后反汇编main.exe可以看到$ objdump-dmain.exe 000000000001149run:...1158: e8 f3 fe ff ff callq1050putsplt# 地址已被修正000000000001160main:... 116f: e8dcfe ff ff callq1050putsplt1179: e8 cb ff ff ff callq1149run# run 地址已被修正5.3 虚拟地址与进程地址空间关键问题一个 ELF 程序在没有被加载到内存时有没有地址答案有当代计算机采用平坦模式工作ELF 对自己的代码和数据进行统一编址。所以虚拟地址机制不光 OS 要支持编译器也要支持。ELF 在编译好后会把程序入口地址记录在 ELF Header 的Entry字段中。进程mm_struct、vm_area_struct在进程创建时的初始化数据就是从 ELF 各个 segment 来的——每个 segment 有自己的起始地址和长度用来初始化内核结构中的[start, end]范围数据并填充页表。六、动态链接原理6.1 动态链接的工作流程动态链接实际上将链接的整个过程推迟到了程序加载的时候。操作系统将程序的数据代码连同用到的动态库先加载到内存每个动态库的加载地址都不固定由 OS 根据地址空间使用情况动态分配库加载后修正库中的函数跳转地址6.2 程序启动时的流程C/C 程序开始执行时首先跳转到的不是main而是_start由 glibc 或 ld 提供的特殊函数设置堆栈为程序创建初始堆栈环境初始化数据段复制初始化数据、清零未初始化数据动态链接调用动态链接器解析并加载依赖的动态库进行符号解析和重定位调用__libc_start_main执行信号处理、线程库初始化等调用main函数处理返回值main返回后调用_exit终止程序6.3 动态链接器动态链接器如ld-linux.so负责在程序运行时加载动态库。它通过以下方式查找动态库环境变量LD_LIBRARY_PATH配置文件/etc/ld.so.conf及其子配置缓存文件/etc/ld.so.cache提高加载效率6.4 全局偏移表 GOT核心问题代码段是只读的但动态库加载地址不固定如何修改函数跳转地址解决方案在.data可读写中预留一片区域存放跳转地址——全局偏移表 GOTGlobal Offset Table。GOT 中每一项都是本运行模块要引用的一个全局变量或函数的地址。[24] .got PROGBITS 0000000000003fb8 00002fb8 0000000000000048 0000000000000008 WA 0 0 8关键点代码段只读不能直接修改代码段。有了 GOT代码可以被所有进程共享不同进程的地址空间中动态库的绝对地址不同所以每个进程的每个动态库都有独立的 GOT 表在单个.so中GOT 与.text的相对位置是固定的可以用相对寻址找到 GOT调用函数时首先查表然后根据表中的地址跳转这种方式实现的动态链接叫做PIC位置无关代码。所以编译动态库时必须加-fPIC参数。PIC 相对编址 GOT6.5 PLT 与延迟绑定由于动态链接在程序加载时需要对大量函数进行重定位这一步非常耗时。为了降低开销操作系统引入了延迟绑定Lazy Binding也叫PLTProcedure Linkage Table过程链接表。核心思想与其在程序一开始就对所有函数重定位不如将这个过程推迟到函数第一次被调用时。GOT 中的跳转地址默认指向一段辅助代码桩代码/stub第一次调用桩代码负责查询真正函数的跳转地址并更新 GOT 表后续调用直接跳转到动态库中真正的函数实现看一个 PLT 的例子0000000000001050 putsplt: 1050: f3 0f 1e fa endbr64 1054: f2 ff 25 75 2f 00 00 bnd jmpq *0x2f75(%rip) # 3fd0 putsGLIBC_2.2.56.6 库间依赖不仅可执行程序调用库库也会调用其他库库之间是有依赖的。库中也有.got和可执行文件一样——这就是为什么大家都是 ELF 格式的原因。解析依赖关系时就是加载并完善互相之间 GOT 表的过程。七、总结对比特性静态链接动态链接链接时机编译链接时程序加载时重定位类型编译重定位静态重定位运行重定位动态地址重定位可执行文件大小大包含库代码小只包含函数入口地址表内存占用每个进程一份多进程共享一份更新维护需重新编译整个程序只更新库文件运行速度快略慢需动态解析依赖无依赖动态库存在动态链接的代价牺牲了一定的性能和程序加载时间。动态链接的价值更高效利用磁盘空间和内存资源方便代码更新维护实现了二进制级别的代码复用。八、常用命令速查命令作用ar -rc libxxx.a *.o创建静态库ar -tv libxxx.a列出静态库内容gcc -shared -fPIC -o libxxx.so *.o创建动态库gcc main.c -L. -lxxx链接库ldd a.out查看依赖库readelf -h a.out查看 ELF 头readelf -S a.out查看节头表readelf -l a.out查看程序头表readelf -s a.out查看符号表objdump -d a.out反汇编代码段objdump -S a.out反汇编并显示源码ldconfig重新加载动态库搜索路径file hello.o查看文件类型
企业数字化 ERP 产品动态
相关推荐
3个西沃客车项目避坑:版本升级API全变,性能优化实战指南 3个西沃客车项目避坑:版本升级API全变,性能优化实战指南 版本升级后 API 全变了,代码直接崩?西沃客车调度系统一跑就卡,性能优化无从下手? 别慌,这坑我踩了十年,今天把血泪经验全抖出来。 坑的现象:升级即崩溃,API 面目全非… · 2026/9/23 19:17:46
谷歌浏览器设置入门到精通:3个技巧解决卡顿 谷歌浏览器设置入门到精通:3个技巧解决卡顿 版本升级后 API 全变了,你的脚本还在报错吗?很多老手发现,以前好用的自动化工具在 Chrome 120+… · 2026/9/23 19:17:33
Apache Arrow Flight SQL 协议详解:RPC 命令、执行模型与会话管理 Apache Arrow Flight SQL 协议详解:RPC 命令、执行模型与会话管理 【免费下载链接】arrow Apache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing 项目地址: https://gitcode.com/gh_mirrors/arrow12/arrow
… · 2026/9/23 19:17:32
2026最新黑莓手机官网中国官网实战项目源码拆解 2026最新黑莓手机官网中国官网实战项目源码拆解 刚学会Python语法,盯着空白的IDE发呆?这是2026年很多开发者的真实写照。你背下了 for 循环和类定义,却连一个能跑通的最小可用项目都搭不起来。别急,今天我们拿… · 2026/9/23 19:47:59
2FSK调制解调从原理到FPGA落地:连续相位、非相干解调与避坑指南 简介:一份用于2FSK(二进制频率移键控)调制解调仿真的MATLAB脚本,面向通信原理、数字通信课程学习者及需要完成相关课程设计的学生。该m文件对应常见2FSK调制流程:先生成随机二进制序列,再按照“0”用较低载… · 2026/9/23 19:47:52
3步搞定sophone官网配置,附完整示例避坑指南 3步搞定sophone官网配置,附完整示例避坑指南 刚拿到 sophone 官网的技术文档,是不是感觉像在读天书?很多兄弟跟我吐槽, 学会语法却不知怎么搭项目 ,对着屏幕干瞪眼。别慌,今天咱们不整虚的,直接上 完整示例… · 2026/9/23 19:47:45
3步搞定百度帐号注册底层逻辑,面试必问的防刷原理 3步搞定百度帐号注册底层逻辑,面试必问的防刷原理 配置环境就卡半天,是不是觉得注册个账号还得写代码?别急,这行里有个老生常谈的话题:为什么大厂都要搞验证码?为什么有时候注册失败不是因为密码错,而是因为你“太快”了?这不仅是产品需求,更是… · 2026/9/23 19:47:39
能碳管理系统 开源!| 碳排放核算软件如何建全国通用制造业单耗核算模型 碳排放核算软件建全国通用制造业单耗核算模型,第一优先级是把组织边界、分母版本、能源分项与锁账snapshot写进同一schema——装备、化工、轻工差异在字典而非公式本身。结论:按开工五清单与30天步骤验收模型,而不是先看曲线大屏;… · 2026/9/23 19:47:39
3秒破局:面试被问原理答不上?一文搞懂申购新股的技巧 3秒破局:面试被问原理答不上?一文搞懂申购新股的技巧 面试现场,面试官抛出一个看似基础实则深坑的问题:“说说你对申购新股的理解,别背八股文,讲点实战里的门道。”你脑子一嗡,除了“顶格申购”四个字,脑子里一片空白。那种 面试被问原理答不上来… · 2026/9/23 19:47:14
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29