当前位置:首页>Linux>深入理解Linux动态库加载、链接与运行的全过程

深入理解Linux动态库加载、链接与运行的全过程

  • 2026-08-24 04:35:14
深入理解Linux动态库加载、链接与运行的全过程
往期精选文章
本地部署DeepSeek大模型原来这么简单!
深入探究c++中符号冲突导致的未预期行为
教你学会使用Jenkins
C++性能分析-perf火焰图

引言

在Linux系统中,动态库(又称共享库)是现代软件架构的核心组成部分。与静态库相比,动态库具有节省内存、便于更新、减少磁盘空间等优势。那么,一个动态库从磁盘文件到被程序使用的完整过程是怎样的呢?本文将深入探讨Linux动态库的装载机制。

一、动态库装载的基本概念

1.1 动态库与静态库的区别

特性
静态库
动态库
链接时机
编译时
运行时
文件大小
可执行文件较大
可执行文件较小
内存占用
每个进程独立副本
多个进程共享
更新
需重新编译
替换库文件即可
常见后缀
.a.so

1.2 关键参与者

  • 动态链接器 (ld.so 或 ld-linux.so): 负责加载和链接动态库

  • 动态段 (.dynamic): ELF文件中存储动态链接信息的部分

  • 全局符号表 (Global Symbol Table): 用于符号解析的数据结构

二、动态库装载的完整流程

2.1 流程概览

程序启动 → 加载动态链接器 → 解析程序动态段 → 加载依赖库 → 符号解析 → 重定位 → 初始化 → 程序执行

2.2 详细步骤解析

步骤1:程序启动与动态链接器加载

当执行一个动态链接的程序时,内核首先检查ELF文件的程序头表,找到解释器段(INTERP),该段指定了动态链接器的路径:如下图所示

内核加载动态链接器到内存,并将控制权移交给它。

步骤2:解析程序依赖关系

动态链接器读取程序的动态段,获取所有依赖的共享库

步骤3:库文件搜索与加载

动态链接器按照特定顺序搜索库文件:

  1. RPATH/RUNPATH: 编译时指定的库搜索路径

  2. LD_LIBRARY_PATH: 环境变量指定的路径

  3. /etc/ld.so.cache: 缓存的库配置

  4. 默认路径/lib/usr/lib 等

如下所示查看RPATH/RUNPATH

步骤4:内存映射与段加载

动态链接器使用mmap系统调用将库文件映射到进程的地址空间:

// 简化的映射过程void* addr = mmap(NULL, lib_size, PROT_READ|PROT_EXEC,                   MAP_PRIVATE, fd, offset);

不同的段(代码段、数据段)根据权限要求被映射到不同的内存区域:

  • 代码段: 只读、可执行(通常开启ASLR)

  • 数据段: 可读写(包含全局变量等)

步骤5:符号解析与重定位

这是动态链接的核心环节:

  1. 符号解析: 将未定义的符号引用绑定到具体的定义

  2. 重定位: 修改代码中的地址引用

// 可执行程序源代码#include<stdio.h>#include"third.h"intmain(int argc, char** argv){  int result = sum(16);  printf("result  = %d\n", result);  return 0;}
// sum函数所在的动态库源代码#include"third.h"intsum(int a, int b){  return a + b;}
// 编译可执行程序g++ --o main main2.cpp -I./ -L./ -lthird -Wl,--rpath=/home/candy/Desktop/Project/gdb
// 编译动态库g++ -fpic -g -shared -o libthird.so third.cpp
查看可执行程序main中需要重定位的函数符号sum
查看动态库的重定位信息
  • R_X86_64_RELATIVE: 库内部数据的相对地址调整

  • R_X86_64_JUMP_SLO: 对printf等外部函数的跳转槽需要填充

查看符号表
延迟绑定(PLT/GOT机制):为提高性能,Linux采用延迟绑定技术。第一次调用函数时通过过程链接表(PLT)和全局偏移表(GOT)进行动态解析,后续调用直接跳转。

步骤6:初始化与终止处理

库的初始化函数按照特定顺序执行:

  1. 构造函数: 标记为__attribute__((constructor))的函数

  2. .init_array段: 函数指针数组

  3. .preinit_array段: 更早的初始化函数

// 示例:构造函数__attribute__((constructor))void init_function() {    printf("Library initialized\n");}
终止时,相应调用.fini_array中的函数。

三、动态链接器的内部机制

3.1 动态段的关键结构

查看详细的动态段信息

3.2 链接器脚本与配置文件

  • /etc/ld.so.conf: 额外的库搜索路径配置

  • /etc/ld.so.preload: 预加载的库列表

  • 链接器脚本: 控制内存布局的脚本文件

3.3 查看动态库在进程的内存布局

注意:本文主要讲解借助动态链接器去装载链接库,当然也可以在程序中使用dlopen主动去加载动态库并取用其中的函数、变量进行使用!

最新文章

随机文章