Linux动态加载器,loader,dynamic linker

preface

export LD_LIBRARY_PATH=...

  • LD 是 Loader / 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 = 外挂代码模块

动态加载器负责:

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

所以:

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

核心方向是对的。

但更准确一点是:

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

相关推荐
xbzb1 小时前
自定义 yum 软件源:Linux 上 createrepo,Windows 上纯 Python 也能造
linux·运维·windows·python·yum·createrepo
Android系统攻城狮1 小时前
Linux FFmpeg深度解析之avio_open调用流程与实战(三十八)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
独隅1 小时前
WebStorm Linux 版安装实战与核心效能展示
linux·ide·webstorm
瑞克-桑切斯3 小时前
ERP 企业生产管理系统部署手册
运维·docker·云原生·容器·eureka
江屿风3 小时前
【Git-组件】【Git入门:从文档灾难到版本掌控】流食般投喂
运维·笔记·git·组件·版本控制
我不会起名字3223 小时前
K8s 节点磁盘打满导致 Pod 被驱逐:4 类占用与回收顺序
运维·kubernetes·k8s·diskpressure·evicted
雪落漂泊4 小时前
Linux进程(五):进程地址空间篇
linux·运维·服务器
瑞克-桑切斯4 小时前
docker mail商城
运维·docker·容器
AR-26710-4 小时前
Linux Day32——网络,ROCE-IB与以太网
linux·运维
不庸.5 小时前
WPScan 实战入门:从 Docker 靶场到弱密码爆破
运维·安全·web安全·docker·容器·安全性测试·wpscan