【 linux 】动静态库的制作
目录
1. 认识动静态库
2. 理解动静态库
3. 动静态库的制作与对比
1.静态库的制作
2. 动态库的制作
3. 对比
4.认识ELF格式
1. 认识动静态库
库本质上是一组预先编译好的代码、函数或资源的集合,供其他程序调用,以避免重复造轮子。根据链接时机和加载方式的不同,库主要分为静态库和动态库
静态库:在程序的编译链接阶段,链接器会将静态库中的代码完整复制并合并到最终的可执行文件中。linux/macOS中后缀是.a,windows中后缀是.lib
静态库的特点是独立性强,生成的可执行文件包含了所有依赖代码,运行时不需要外部库文件,部署方便。但是需要将代码完整复制一遍到可执行文件中更耗费资源,如果库进行更新的话所有的可执行文件都要重新编译链接非常不方便
动态库:在编译时不将代码复制到可执行文件中,仅记录引用信息。代码在程序运行时由操作系统动态加载到内存中。linux中后缀是.so,macOS中是.dylib,windows中是.dll
动态库更节省空间,多个进程可以共享内存中的同一份库代码,显著减少磁盘和内存占用。只要接口(ABI)兼容,替换库文件即可生效,无需重新编译主程序
2. 理解动静态库
所有的库本质上都是源文件对应的.o,库也是要被安装到系统中的,可以以C标准库举例,/lib64/libc.so.6动态库文件,是程序运行时真正加载的文件,/usr/lib64/libc.a静态库版本
不同发行版路径可能不同,可以用以下命令确认
# 查看 libc 的实际路径 ldconfig -p | grep libc.so # 或者直接查找 find /lib* /usr/lib* -name "libc.so*" 2>/dev/nullC标准库是操作系统的核心组件,在安装操作系统时就已经预装,它是系统启动和用户空间程序运行的先决条件
3. 动静态库的制作与对比
1.静态库的制作
静态库的本质就是打一个包,我们可以自己写两个头文件和源文件,定义为mystdio.h,mystring.h,mystdio.c,mystring.c
将.c文件编译生成.o文件,在将.o文件打包生成.a文件
.PHONY: all clean all: libmy.a libmy.a: mystdio.o mystring.o ar rcs $@ $^ %.o: %.c gcc -c $< -o $@ clean: rm -f *.o libmy.a目录结构是这样的
project/ ├── lib/ ← 静态库目录 │ ├── include/ ← 头文件(对外接口) │ │ ├── mystdio.h │ │ └── mystring.h │ ├── src/ ← 库源码(内部实现) │ │ ├── mystdio.c │ │ ├── mystring.c │ │ └── Makefile ← 只负责生成 libmy.a │ └── libmy.a ← 生成的静态库产物 │ └── app/ ← 主程序目录 ├── main.c ← 主函数入口 └── Makefile ← 只负责编译 main.c 并链接 libmy.a编译链接生成可执行文件:
gcc main.c -I ..lib/include -L ../lib -l my -o app
介绍一下这几个命令都是干什么的,打包时需要用到ar -rc命令,rc是replace and create,存在同名.o替换,不存在插入,.a不存在创建
-大写i是查找头文件,连接任何的非C/C++标注库都需要指明-L,-l,小写l后面是.a文件名(去掉lib)
2. 动态库的制作
动态库的制作和静态库类似,编译阶段加上-fPIC命令产生位置无关码,gcc -c -fPIC *.c -o *.o
打包成库时加上-shared生成共享库格式,gcc -shared *.o -o .so
主程序的命令和静态库相同,对于静态库来说链接器需要将.a文件代码拷贝到main.c中,对于动态库链接器记录依赖,根据路径实时加载.so到内存
3. 对比
gcc/g++默认使用动态库,想用静态库需要加上-static(只存在静态库可执行程序只能静态链接)
linux系统中默认安装的大部分库都是动态库
库:应用程序=1:n
4.认识ELF格式
动静态库,可执行程序,.o文件都是ELF格式,ELF格式主要有以下几部分
ELF Header记录文件特征,Program Header Table记录段(segment)的信息,Section Header Table记录节(Section)的信息
库形成可执行程序需要进行Section的合并,相同属性合并成一个段(segment),关于如何合并的信息存储在Program Header Table中,所有需要被加载到内存中执行的 ELF 文件(包括可执行文件和共享库)都有 Segment;而不需要加载的文件(如.o目标文件、静态库.a)只有 Section,没有 Segment
实际过程
链接脚本(Linker Script)定义了 Output Section 的排列顺序和属性 → 链接器根据这些属性(R/W/X + ALLOC)将多个 Input Section 分配到不同的 Program Header 条目中 → 同时生成节头表和程序头表
没有 Segment 的文件,也没有 Program Header Table,可以执行readelf -l hello.o查看,可以用read -S hello.o读取Section Header节表头
终
