三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

编译-链接过程备忘录

编译-链接过程备忘录

目录

1. 静态库(.a / .lib)—— 不是链接器生成的

2. 动态库(.so / .dylib / .dll)—— 由链接器生成

3. 可执行文件(app / exe)—— 由链接器生成

4.动态库内部藏着两张“符号表”

5,为什么 strip“通常”没事?(以及“有事”的例外)


1. 静态库(.a / .lib)—— 不是链接器生成的

  • 生成工具归档器(ar),而不是链接器(ld)。

  • 过程:编译器先把源码编译成目标文件(.o / .obj),然后ar命令把这些 .o 文件打包成一个静态库文件。这一步只是“打包”,不进行符号解析和重定位(即没有“连接”过程)。

  • 命令ar rcs libmylib.a file1.o file2.o

2. 动态库(.so / .dylib / .dll)—— 由链接器生成

  • 生成工具链接器(ld),带上特殊参数(如-shared-dynamiclib)。

  • 过程:链接器将多个 .o 文件或依赖的静态库进行部分链接。它会解析内部符号,但会故意保留外部依赖符号为“未定义”,生成一个可重定位且位置无关(PIC)的共享对象文件。

  • 命令gcc -shared -o libmylib.so file1.o file2.o(后台实际调用 ld)

3. 可执行文件(app / exe)—— 由链接器生成

  • 生成工具链接器(ld),默认模式。

  • 过程:链接器将所有 .o 文件、静态库(复制代码)、动态库(登记导入表)进行完整链接。这一步会把所有静态符号的地址固定下来(或留作重定位),生成操作系统可以直接加载运行的文件格式(ELF / PE / Mach-O)。

  • 命令gcc -o myapp main.o -lmylib(后台调用 ld)

直观变换流程:

  1. 源码编译.c/.cpp——(编译器 cc1plus)——>.o目标文件(二进制机器码,但地址是乱的)

  2. 打包成静态库.o文件们——(ar 归档器)——>.a静态库(相当于一个 .o 的压缩包,没有链接

  3. 链接生成动态库.o/.a——(ld 链接器 + -shared)——>.so动态库(半链接状态,保留外部跳转钩子)

  4. 链接生成可执行文件.o/.a/.so——(ld 链接器,默认)——>app可执行文件(全链接状态,具备运行入口)


4.动态库内部藏着两张“符号表”

  • 第一张:动态符号表(.dynsym——这是命根子

    • 这张表记录了对外公开的函数名(比如int add(int a, int b))。

    • 作用:当你的可执行文件或其他库要调用这个库时,动态链接器就靠这张表里的名字去查找地址。如果这张表没了,外部程序就找不到入口,加载时会直接报错“undefined symbol”

  • 第二张:调试符号表(.symtab+ 行号信息)——这是说明书

    • 这张表记录了变量名、源码文件名、代码在第几行、甚至函数参数名。

    • 作用:仅用于 GDB 调试、反汇编时看函数名、或者崩溃时打印堆栈。运行时完全不需要它


5,为什么 strip“通常”没事?(以及“有事”的例外)

  • 标准 strip 行为(Linux):大多数 Linux 上的strip命令默认只删除调试符号表(第二张),而保留动态符号表(第一张)。所以你在 Linux 上直接strip libxxx.so,库能正常跑,因为命根子还在。这叫“安全瘦身”。

← 返回列表