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 = 外挂代码模块

动态加载器负责:

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

所以:

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

核心方向是对的。

但更准确一点是:

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

相关推荐
进阶的猪14 分钟前
Linux 内核移植过程学习
linux·学习
不会调制解调的猫2 小时前
笔记 | 什么是 rp_filter(反向路径过滤)?
服务器·网络·笔记
问简2 小时前
【共享盘】ubuntu、windows
linux·windows·ubuntu
Splashtop高性能远程控制软件2 小时前
聊天工具与专业远程支持软件:企业 IT 远程支持工具选型的五个评估维度
运维·远程工作·splashtop
小生不才yz3 小时前
Shell脚本精读 · S13-01 | POSIX sh 与 Bash:语法对照表
linux
阿拉斯攀登3 小时前
平台设备驱动:platform bus 与设备树
linux·嵌入式硬件·嵌入式·linux驱动·字符设备驱动
阿拉斯攀登3 小时前
并发与同步:自旋锁、信号量、互斥锁
linux·网络·嵌入式硬件·linux驱动·字符设备驱动
网络小白不怕黑3 小时前
10.邮件服务器搭建并脚本实现监控httpd状态
linux·运维·服务器
TTBIGDATA3 小时前
【Ambari Plus】15.Livy 安装
大数据·运维·hadoop·ambari·hdp·cdh·bigtop
CN_HW4 小时前
Nginx 流量镜像配置文档-双轨国产化
服务器·前端·网络