在ELF世界里,“符号”就是编译器和链接器用来定位东西的名字标签。
它可以是:
本质:
符号 = 名字 + 地址 + 类型 + 绑定属性流程
.c -> gcc -c -> .o -> ar -> .a -> ld -> 可执行文件ELF结构里有:
用:
nm file.o
readelf -s file.o
objdump -t file.o静态库本质就是:一堆 .o 的打包集合(不是合并)
命令
ar rcs libxxx.a a.o b.o c.o可以
at t libxxx.a查看里面有哪些 .o 。
看 nm 输出
00000000 T func
U printf
00000000 D global_var
00000000 B uninit_var| 字母 | 含义 |
|---|---|
| T | 在 text 段的已定义函数 |
| U | 未定义符号(需要外部解析) |
| D | 已初始化全局变量 |
| B | 未初始化全局变量 |
| W | 弱符号 |
| t | 局部函数 |
| d | 局部数据 |
这是静态库最容易踩的坑。
强符号(Strong)
弱符号(Weak)
__attribute__((weak))规则:
这东西在 libc 里大量使用。
链接器不是把整个 .a 拿进来
它是:只把需要的 .o 拿进来。
举例:
lib.a
a.o (提供 foo)
b.o (提供 bar)如果你程序只用 foo:
这带来一个致命规则:链接顺序重要!
静态库是“单向扫描、按需装载”的。
链接器的行为是:
重点在于:它不会回头。链接器没有记忆力。它只活在当下。
动态库(.so)是整体加入,不是按 .o 拆分。它不会按需拆 .o。 所以顺序影响没那么大。
链接器工作流程:
静态库不能循环依赖。
解决方式
gcc a.o -lfoo -lbar -lfoo
# 或者
-Wl,--start-group ... --end-groupC++符号会被 mangling
void foo(int)变成
_Z3fooi取消修饰
extern "C" void foo(int).o 里函数地址不是最终地址。
它是,可重定位符号 + relocation表。
链接时:
这一步叫:符号解析 + 重定位。
undefined reference
multiple definition
静态变量符号不可见
static int x;变成局部符号,外部访问不到。
遇到问题,按这个流程:
nm -C xxx.o
readelf -s xxx.o
ar t libxxx.a
nm -C libxxx.a
ldd (动态库用)
objdump -d静态库:一堆可重定位目标文件
链接器:符号匹配器 + 重定位修补器
符号:编译世界里的身份证