找回密码
 立即注册
首页 业界区 业界 编译器细节:动态链接与静态链接行为分析 ...

编译器细节:动态链接与静态链接行为分析

芮梦月 4 天前
gcc 与ld.so (以 Alpine 为例)


  • 背景:Alpine Linux 是一个基于 musl libc 和 busybox 构建的轻量级 Linux 发行版,专注于安全性、资源效率和简洁性。它被广泛用于 Docker 容器、嵌入式系统和云计算环境。
基本概念:gcc 和 ld.so 分别是什么?

ld.so(dynamic linker/loader)是程序运行时的动态链接器。gcc 在编译期决定程序如何被加载,而 ld.so 在运行时执行加载。
1. gcc(GNU Compiler Collection)


  • 功能:将 C/C++ 源代码编译成目标文件(.o),并调用链接器(ld)生成可执行文件。
  • 它本身不直接做动态链接,但它会:

    • 调用汇编器(as)
    • 调用链接器(ld,来自 binutils)
    • 在链接时指定使用哪个 动态链接器(dynamic linker)

  1. 示例:当你运行 `gcc main.c -o app`,gcc 实际上做了:
  2. 源码 .c
  3.    ↓
  4. 预处理 (cpp)        → #include, #define 展开
  5.    ↓
  6. 编译 (cc1)          → 生成汇编代码(.s)
  7.    ↓
  8. 汇编 (as)           → 生成目标文件(.o)
  9.    ↓
  10. 链接 (ld)           → 合并所有 .o 和库,生成可执行文件
复制代码
2. ld.so / ld-musl-*(动态链接器,Dynamic Linker/Loader)


  • 功能

    • 程序启动时由内核加载
    • 负责加载程序依赖的共享库(如 libc.so, libpthread.so 等)
    • 进行符号解析、重定位
    • 然后跳转到程序入口 _start 或 main

⚠️ 注意:ld.so 是 GNU glibc 中对动态链接器的称呼,在 musl 中它叫 ld-musl-*,但作用相同。
一、动态链接:编译期 vs 运行时

gcc 默认进行动态链接
阶段参与者职责编译/链接期gcc + ld(链接器)将源码编译为可执行文件,并在其中嵌入一个名为 .interp 的段,用于指定运行时的动态链接器路径。运行期内核 + ld.so内核加载程序,读取 .interp 段,然后加载并执行指定的动态链接器(如 ld-musl-* 或 ld-linux-*)。动态链接器负责加载所有依赖的共享库,并启动程序。它们通过 动态链接 建立联系:
  1. gcc hello.c -o hello
复制代码

  • 预处理 + 编译
    gcc 把 hello.c 编译成目标文件 hello.o
  • 链接(Linking)
    gcc 调用 ld(GNU linker,属于 binutils)进行链接
  • 关键一步:嵌入“解释器”路径(INTERP segment)

    • 链接器会在最终的 ELF 可执行文件中写入一个特殊的段:.interp
    1. $ readelf -l hello | grep -A 2 'INTERP'
    2. INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
    3.                0x000000000000001c 0x000000000000001c  R      0x1
    4.       [Requesting program interpreter: /lib/ld-musl-x86_64.so.1]
    复制代码

    • 内容是动态链接器的路径,例如:/lib/ld-musl-x86_64.so.1
    • 这个路径是在链接时由 gcc 使用的 "specs" 文件或 crt 对象文件决定的

      • 打印gcc spec: gcc -dumpspecs


  • 运行时:内核发现 .interp 后,先加载 ld-musl,再由它加载程序和 libc
    1. execve("hello", ...)
    2.   → 内核读取 ELF 的 .interp: "/lib/ld-musl-x86_64.so.1"
    3.   → 先加载 ld-musl
    4.   → ld-musl 加载 libmusl.so(即 libc 实现)
    5.   → 解析符号,完成重定位
    6.   → 跳转到 _start → main()
    复制代码
✅ 所以:
gcc 编译时,将 ld.so 的路径写入.interp 段,告诉内核在运行时使用哪个动态链接器。
二、静态链接:无运行时依赖

静态链接通过 -static 标志实现,它将所有依赖库的代码直接复制到最终的可执行文件中。
  1. $ gcc --verbose -static hello.c -o hello
  2. #include "..." search starts here:
  3. #include <...> search starts here:
  4. /usr/include
  5. End of search list.
  6. ...
  7. COLLECT_GCC_OPTIONS='--verbose' '-static' '-mtune=generic' ...
  8. /usr/lib/gcc/x86_64-linux-gnu/11/cc1 -quiet -imultiarch x86_64-linux-gnu ...
  9. as ... -o /tmp/ccXXXXXX.o
  10. ...
  11. /usr/bin/ld --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 \
  12.      -static [... lots of .a files...] \
  13.      -o hello-static
  14. # 只是 ld 多了一个 -static 参数,表示要静态链接
  15. # 链接器加载了 libc.a, libgcc.a, crt*.o 等静态对象
复制代码
意味着:

  • 所有需要的库代码(包括 libc 中的 printf, malloc 等)都被直接复制进最终的二进制文件中
  • 不再依赖外部 .so 文件
  • 没有 .interp 段
  • 不需要动态链接器参与启动过程
此时程序结构更简单:
  1. 用户执行 ./hello
  2.     → 内核直接加载整个程序映像(包含所有代码)
  3.     → 直接跳转到入口点 _start → main()
复制代码
你可以通过以下命令验证一个程序是否为静态链接:
  1. # 输出类似 "statically linked" 的信息
  2. file ./hello
  3. # 如果输出 "not a dynamic executable",则是静态链接
  4. ldd ./hello
  5. # 如果没有输出,则表示没有 .interp 段
  6. readelf -l ./hello | grep 'INTERP'
复制代码
三、Alpine (musl) vs. 主流发行版 (glibc)

Alpine 的 gcc 是为 musl C特别配置的,这与 Ubuntu/CentOS 等使用 glibc 的系统有本质区别。
项目Alpine Linux (musl)Ubuntu/CentOS (glibc)C 标准库musl libcglibc动态链接器/lib/ld-musl-*.so.1/lib/ld-linux-*.so.2target triplet*-alpine-linux-musl*-pc-linux-gnu可移植性musl 静态链接的程序通常具有更好的跨发行版可移植性。glibc 静态链接的程序可能因依赖 NSS 等机制而无法在 Alpine 上运行。
编译器名称通常:----
具体差异在哪里?

1. GCC 的 “target triplet” 和 “specs”

spec file: /lib/gcc/x86_64-alpine-linux-musl/11.2.1/specs
  1. # Alpine 的 GCC 被编译为:
  2. x86_64-alpine-linux-musl
  3. # 而不是常见的:
  4. x86_64-pc-linux-gnu
复制代码
这意味着:

  • 默认包含头文件路径指向 Alpine 特有的位置
  • 默认使用 musl-gcc 行为(即使命令叫 gcc)
  • 自动设置 .interp 为 /lib/ld-musl-x86_64.so.1
2. CRT(C Runtime Startup)对象不同


  • 使用 crt1.o, crti.o, crtn.o 来自 musl,而非 glibc
  • 这些对象定义了 _start 符号和初始化流程
3. 链接脚本和默认库不同


  • 默认链接 -lc 时,链接的是 libc.a 或 libc.so 来自 musl,不是 glibc
  • 不支持某些 glibc 特有的 symbol(如 __stack_chk_fail_guard)
四、如何验证

1. 查看 gcc 默认链接了什么


  • Alpine (musl) 下的例子
  1. $ gcc -Wl,--verbose | grep 'SEARCH_DIR\|libc\.'
  2. # 你会看到它搜索 musl 的库路径,比如:
  3. SEARCH_DIR("/usr/x86_64-alpine-linux-musl/lib")
复制代码

  • WSL2 Ubuntu (glibc) 下的例子
  1. $ gcc -Wl,--verbose
  2. SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
  3. attempt to open /usr/lib/gcc/x86_64-linux-gnu/11/libc.so failed
  4. attempt to open /usr/lib/gcc/x86_64-linux-gnu/11/libc.a failed
  5. attempt to open /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libc.so succeeded
  6. opened script file /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libc.so
  7. /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libc.so
  8. opened script file /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libc.so
  9. attempt to open /lib/x86_64-linux-gnu/libc.so.6 succeeded
  10. /lib/x86_64-linux-gnu/libc.so.6
  11. /usr/bin/ld: ld-linux-x86-64.so.2 needed by /lib/x86_64-linux-gnu/libc.so.6
  12. /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
  13. (.text+0x1b): undefined reference to `main'
  14. /usr/bin/ld: link errors found, deleting executable `a.out'
  15. collect2: error: ld returned 1 exit status
复制代码
2. 查看 gcc 目标架构
  1. $ gcc -v
  2. # 输出中会有类似:
  3. Target: x86_64-alpine-linux-musl
  4. Configured with: /path/to/configure --target=x86_64-alpine-linux-musl ...
复制代码

  • 如果你想进一步探索:

    • 什么是 ABI(Application Binary Interface)?
    • 使用 cross 和 none 工具链
    • 了解 newlib、glibc、musl 等 C 库?


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册