飞行日志
常用链接
韭菜搞钱
三省吾身
知行合一
📂系统:库与二进制
type
status
date
slug
summary
tags
category
icon
password
本篇以 gcc 编译器为例,讨论 Linux 中库、编译和二进制的关系,介绍了静态、动态库的区别与使用。
1. 库
1.1. 库的概念
库:实现了某一类功能的若干函数和二进制代码的集合。库的后缀名在不同平台上表现不同: Windows:静态库:xxx.lib && 动态库:xxx.dll Linux:静态库:xxx.a && 动态库:xxx.so
1.2. 库的分类
库分为静态库和动态库,与之对应的操作是静态链接和动态链接,静态库不能采用动态链接,同理,动态库也不能采用静态链接。 静态库:在链接的时候,函数库被完整的拷贝到可执行文件中,对应的链接方式成为静态链接,采用 gcc -static 指令。这里的可执行文件可以使单纯的一个 a.out,也可以是一整个 app 或者程序。 动态库:相对于静态库,动态库并没有将库中所有的数据复制到可执行文件中,在程序运行的时候才会被动态载入。
2. 区别和特点
2.1. 静态库
- 一旦链接完成,执行程序就和函数库没有任何关联;
- 占用空间和资源,拷贝多次就会占用多份资源;
- 会导致升级不便,一个地方修改就需要全量更新;
2.2. 动态库
- 可以实现进程之间的资源共享;
- 动态库把对一些库函数的链接载入推迟到程序运行的时期;
- 将一些程序升级变得简单;
- 可以通过显示调用做到链接载入完全由程序员在程序代码中控制;
- 动态库的创建直接使用编译器即可创建动态库,不需要打包工具;
3. 示例演示
这里试用 gcc 编译器 + C 语言作为演示,其他编译器配合 C/C++ 类似。
3.1. 编译过程简介
gcc 编译器在 c 语言到 可执行文件中,主要包含四步:
- 预处理 Pre-Processing:aaa.c -> aaa.i
- 编译 Compiling:aaa.i -> aaa.s
- 汇编 Assembling:aaa.s -> aaa.o
- 链接 Linking:bbb.so/ccc.a + aaa.o -> aaa
动静态库区别,主要在第四步链接的方式不同。
3.2. 原理演示
文件 mymath.h
文件 mymath.c
文件 main.c
测试脚本:
3.3. 输出与分析
可以看到,动态链接生成的二进制 dynamic-main 文件,在运行时依赖 libmymath.so,这里 not found 是因为没有把 libmymath.so 放到系统索引目录里,只在编译时用了 -L. 参数;静态编译不依赖任何 so。
4. 概念补充
4.1. ELF
4.1.1. ELF 介绍
ELF,全称 executable and linkeable file,是一种对可执行文件、目标文件和库使用的文件格式,跟 Windows 下的 PE 文件格式类似。ELF 格式是 Unix 系统实验室作为 ABI 而开发和发布的。
ELF 主要包括三类文件:
- 可重定位文件:编译生成的 .o 文件;
- 可执行文件:Linker 对 .o 处理进行输出的文件;
- 共享对象文件:动态库文件。
4.1.2. ELF 布局
ELF 布局如下:
- ELF header:描述体系结构和操作系统的基本信息,指出 section header table 和 program header table;
- program header table:给出了各个 segment 的信息,从运行角度来看 ELF 文件;
- section header table:给出了各个 section 的信息,从编译和链接的角度来看 ELF 文件;
- sections:各个节区;
- segments:各个段区;
可以注意到 sections 和segments 占用的地方是一样的,左边的视图是从编译和链接的角度看,右边则是从运行的角度看。
4.1.3. ELF 解读
在 Linux 下,一般使用 readelf 工具读取 elf 文件:
4.2. ldd
ldd 是 List Dynamic Dependencies 的缩写,它列出了应用程序所需的所有共享对象。它通过设置特殊的环境变量来调用动态链接器。使用二进制文件的位置运行 ldd 时,它会返回一个输出,其中包含依赖关系的列表、它们的位置以及表示它们加载到内存的十六进制值。
5. 混合编译
5.1. 举例
假设目前有一个 test.c 文件,它的编译依赖静态库头文件 include/headerb.h,动态库 /usr/lib/libdynamica.so,lib/libdynamicb.so,lib/libstatica.a 等。
- 第一行:编译与输出文件名;
- 第二行:链接系统动态库 dynamica;
- 第三行:链接本地动态库 dynamicb;
- 第四行:链接本地头文件 include,链接本地静态库 statica
5.2. 说明
- gcc 编译器中,-I 代表头文件路径,-L 代表库路径,-l 代表库名称,静动态库相同;
- gcc 编译器中,一旦编译选项中出现 -static,后面的所有选项自动转为静态库;
6. 参考
- 《ELF 文件格式简介》https://terenceli.github.io/技术/2014/11/02/elf