Linux动态加载器,loader,dynamic linker

preface

export LD_LIBRARY_PATH=...

  • LDLoader / Linker Dynamic 的缩写
  • 更准确地说,通常指 Linux 的 动态链接器(dynamic linker / loader)

LD_LIBRARY_PATH

可以理解成:

"动态链接器查找共享库(.so 文件)的路径"


例如:

bash 复制代码
export LD_LIBRARY_PATH=/opt/mylib/lib

意思是:

告诉 Linux 的动态加载器:

运行程序时,除了系统默认目录,

还去 /opt/mylib/lib 里面找 .so 动态库。


Linux 程序很多都会依赖:

text 复制代码
libstdc++.so
libssl.so
libcuda.so

这些 .so 文件类似 Windows 的 .dll

程序启动时:

  1. 内核启动程序
  2. 动态链接器(ld-linux.so)开始工作
  3. 它去找程序依赖的 .so
  4. 找不到就报错:
text 复制代码
error while loading shared libraries:
libxxx.so: cannot open shared object file

一、区分两个ld

Linux 里 "ld" 常见含义

1. ld 命令

Linux 还有一个真正叫:

bash 复制代码
ld

的程序:

它是 GNU linker(链接器)。

例如:

bash 复制代码
ld a.o -o app

负责把 .o 文件链接成可执行文件。


2. 动态加载器

比如:

text 复制代码
/lib64/ld-linux-x86-64.so.2

这个也是 ld

它负责:

  • 加载动态库
  • 解析符号
  • 启动 ELF 程序

很多时候:

bash 复制代码
LD_PRELOAD
LD_LIBRARY_PATH

这些环境变量都是给它用的。


常见 LD 环境变量

变量 作用
LD_LIBRARY_PATH 额外动态库搜索路径
LD_PRELOAD 强制预加载某个 .so
LD_DEBUG 输出动态链接调试信息

你还可以用:

bash 复制代码
ldd 程序名

查看程序依赖哪些动态库。

例如:

bash 复制代码
ldd /bin/ls

会显示:

text 复制代码
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6

这里的 ldd 其实也是 "list dynamic dependencies"。

二、loader(动态链接库)加载其他程序(例如库函数)的代码到本程序

很接近底层原理理解

动态加载器会把"其他共享库里的代码和数据"加载到当前进程的内存空间里,让本程序可以调用它们。

例如:

c 复制代码
#include <stdio.h>

int main() {
    printf("hello\n");
}

这几行代码,并没有 printf() 的真正代码实现。

真正代码在:

text 复制代码
libc.so.6

里面。


当你运行程序时:

bash 复制代码
./app

Linux 会:

1. 先启动动态加载器

例如:

text 复制代码
/lib64/ld-linux-x86-64.so.2

2. 动态加载器读取 ELF 信息

它发现:

text 复制代码
这个程序依赖 libc.so.6

可以用:

bash 复制代码
ldd app

看到。


3. 把 libc.so.6 映射进当前进程内存

注意:

这里通常不是"复制代码"。

而是:

用 mmap 把共享库映射(map)到进程地址空间。

所以多个程序可以共享同一份物理内存里的 libc 代码。

这也是 .so 叫:

text 复制代码
shared object

(共享对象)的原因。


4. 修正函数地址(动态链接)

程序里原本只有:

text 复制代码
printf 在 libc 某处

但不知道真实地址。

动态加载器会:

  • 找到 printf
  • 得到真实内存地址
  • 填入 GOT/PLT 等表

这样:

c 复制代码
printf()

才能真正跳转到:

text 复制代码
libc.so.6 里的 printf 实现

5. 才开始执行 main()

所以实际上:

text 复制代码
main()

运行之前:

动态加载器已经做了很多工作。


你可以把它理解成:

text 复制代码
你的程序 = 主程序
libc.so = 外挂代码模块

动态加载器负责:

  • 找模块
  • 装进内存
  • 接上线
  • 然后程序才能调用。

所以:

"加载其他程序(例如库函数)的代码到本程序"

核心方向是对的。

但更准确一点是:

加载共享库到"当前进程的地址空间",而不是把代码复制进可执行文件本身。

相关推荐
ONLYOFFICE1 小时前
ONLYOFFICE成为Linux操作系统 – Winux的预装办公套件
linux·运维·服务器
其实防守也摸鱼1 小时前
OpenClaw Windows 踩坑实战指南
运维·服务器·数据库·windows·github·copilot
mounter6251 小时前
从内核安全到可编程基础设施:Meta 的 eBPF 实践与形式化验证探索
linux·ebpf·linux kernel·kernel
新时代牛马1 小时前
嵌入式 Linux WiFi 框架完整篇:从cfg80211、mac80211 到wpa_supplicant
linux·运维·服务器
吴声子夜歌2 小时前
Linux命令——打印
linux·运维·服务器
Jae den2 小时前
负载均衡到底解决什么问题?
运维·负载均衡
企鹅的蚂蚁2 小时前
Linux 开发板串口调试:picocom 从识别到退出的完整用法
linux·运维·服务器
山岚的运维笔记2 小时前
mysql 专业笔记 -- 第 17 章:连接:连接三个具有相同名称 ID 的表
运维·数据库·笔记·后端·学习·mysql·dba
bksczm2 小时前
从 “什么是 MySQL“ 到库与表的完整操作(MySQL基础篇)
linux·数据库·mysql
企鹅的蚂蚁2 小时前
LubanCat RK3588 实时内核移植第一步:先确认硬件与系统版本
linux·lubancat·嵌入式linux·freempt_rt