@bit::Shadow
✧(≖ ◡ ≖✿
目录
[gcc/g++ -c code.c -o code.o -Ⅰ 头文件处 -L 库处 -l真实库名 【无/-static】](#gcc/g++ -c code.c -o code.o -Ⅰ [头文件处] -L [库处] -l[真实库名] 【无/-static】)
库
静态库(.a) Linux .lib Windows
动态库(.so) Linux .dll Windows // 常在".so"后加以".n"(n为数字),表示版本号。这是Linux的版本管理机制,由于解决版本兼容问题。

"库"其实就是大量.o文件构成的集合加以ar指令处理。
库的命名规范
"lib"+"真实名称"+"后缀"
像:libc.a的真实库名就是'c'。
.o文件
动静态库与.o文件均是ELF格式。
内容查询指令: readelf 选项 文件名/库名
.o文件形成流程:.cpp/.c ----> .o ---> 可执行文件/库文件(.a/.so)
内容:机器码 + 符号表 + 重定位信息 + 调试信息等等。
部分内容:
UND:undefined的缩写

协作时库的合作
前言:*.o + 对应所需 *.h 链接*.o:
gcc/g++ *.o ---> a.out
执行:./a.out
当多人协作传递时: .h 作为接口文件且说明文件,供以了解接口。
.o 作为"自己开发的 .o 文件 + 协作成员的.o"。
三者构成依赖关系以形成可执行文件。
协作下库打包与更新
ar -rc 库全称 *.o(打包对象) # r--replace(存在就更新) c--creat(不存在就创建)
例如:
bash
ar -rc libmyc.a *.o
常用结构:

压缩: tar czf lib.tgz lib
解压: tar xzf lib.tgz lib
Linux下自动配置的库(C/C++)及头文件的查询:
Linux下头文件路径: /usr/include
Linux下库文件路径: /lib64
ls查询库文件:

指定头文件下gcc/g++编译
-Ⅰ 头文件路径:头文件可能不会与.c/cpp文件在同一目录下,此时需要指定头文件位置的选项。(当前目录且非下级目录的.h文件则无需加'-I')。
-L 库文件路径 -l 真实库名称(小写kl的l):形成可执行文件的"库"定位选项,库没有在默认寻找位置:
-
/usr/lib -
/usr/local/lib -
/libbash$ gcc -I /usr/include/ -L /lib64 -lc -c code.c
演示效果:

动态库
ldd
ldd 查询一个可执行文件/共享库依赖哪些动态链接库。

fPIC
f:编译选项前缀由于控制代码生成、优化等。
PIC:Position independent code位置无关码。
-fPIC是gcc编译选项,是形成用于链接动态库的目标**.o文件**必备选项。
bash
gcc -c -fPIC file.c -o file.o
gcc/g++ -c code.c -o code.o -Ⅰ 头文件处 -L 库处 -l真实库名 【无/-static】
-static
1.若无-static默认动态链接,若无动态库只能链接静态库。
2.-static是强制链接静态库命令若无静态库则报错。
3.IDE集成开发环境VS2022在安装过程中就存在配置库的环节。
| 对比项 | 静态库 (.a) | 动态库 (.so) |
|---|---|---|
| 链接时机 | 编译时 | 运行时 |
| 文件后缀 | .a (archive) |
.so (shared object) |
| 程序体积 | 大(包含库代码) | 小(不含库代码) |
| 内存占用 | 高(每个程序独立一份) | 低(多程序共享一份) |
| 依赖关系 | 无依赖,可独立运行 | 需要库文件存在 |
| 更新库 | 需重新编译程序 | 替换库文件即可 |
| 加载速度 | 快(无需查找) | 稍慢(需动态链接) |
| 符号冲突 | 容易冲突 | 隔离性好 |
| 编译命令 | gcc -c -fPIC *.c ar rcs lib.a *.o |
gcc -c -fPIC *.c gcc -shared -o lib.so *.o |
| 使用命令 | gcc main.c -L. -lfoo |
gcc main.c -L. -lfoo 需设置 LD_LIBRARY_PATH |

ELF
动静态库、.o文件(可重定向目标文件)均是"ELF"格式(一种二进制格式)

四部分
• ELF Header:文件最开头,标识文件类型、机器架构、入口点、程序头表/节头表的位置和大小等。
Program Header Table:只有可执行文件和共享对象需要,描述如何将文件映射到进程地址空间。.o文件无此区域
Section(节):真正的内容数据,如++代码、数据、符号表、重定位表++等。
Section Header Table:描述各个Section节的信息,编译器、链接器使用。
一个 hello.o 文件内部大致如下(顺序可能因编译器而异):
| Section (节) | 内容说明 | 常见标记 |
|---|---|---|
| .text | 编译后的机器指令(代码) | 可读、可执行 |
| .data | 已初始化的全局/静态变量 | 可读写 |
| .bss | 未初始化的全局/静态变量(不占用文件空间,只记录大小) | 在加载时清零 |
| .rodata | 只读数据,如字符串常量、const 变量 | 只读 |
| .symtab | 符号表(全局、外部符号定义/引用) | 链接器使用 |
| .strtab | 符号表中符号名称的字符串表 | |
| .rel.text | 对 .text 的重定位条目(修正地址) | |
| .rela.text | 同上,但使用 rela 格式(带 addend) | |
| .comment | 编译器版本信息 | |
| .note.* | 各种注释/属性(如 GNU-stack) | |
| .shstrtab | 节名称字符串表 |
各个.o文件链接成为可执行时会进行文件合并:

Section区域合并成为Secment
- 合并原则:相同属性。如:只读、可读写。
- 这样即便是不同的Section,加载到内存时,也可能加载到一起。
- 这种合并原则在形成ELF时就在形成了。被记录于(Program Header Table内)。
☝Section 是逻辑划分,给链接器 用;Segment 是物理映射,给操作系统用。
ELF形成可执行
step1-将多份C/C++代码翻译为.o文件与配置的动静态库(ELF)。
step2-将多份.o文件section进行合并。

*为什么要合并为Secment?
section连续分布每个可存储最大4096,若有4097个字节必须另开一个节。这样降低了空间利用率,使用合并方式减少了内存碎片提供内存利用率。
理解向动态库链接和加载
未链接的.o文件,并未确定call的目标具体体现为call的目标地址为00:


在进行动态库的调用(如:printf()、外部函数run())前在反汇编层面(如:main的main.o 、 main.s文件)
均是call:00 00 00 00
*.o链接库成为可执行文件
1.软链接
sudo ln -fs 链接者 链接目标库
2.export链接
LD_LIBRARY_PATH 是 Linux 下用于临时指定共享库搜索路径的环境变量。
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:追加路径 #追加式配置搜索路径
./a.out
运行程序时,会去 LD_LIBRARY_PATH 里找库
倾囊相授,持续深耕Linux
欢迎关注
