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

动态加载器负责:

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

所以:

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

核心方向是对的。

但更准确一点是:

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

相关推荐
三十..1 小时前
华为云全栈:网络/存储/运维高能实战
运维·华为云
Vick_Zhang1 小时前
ubuntu上rabbitmq
服务器·ubuntu·rabbitmq
kongba0071 小时前
ttyd Web终端安装指南(OpenCloudOS 9)
linux·前端
lolo大魔王1 小时前
Linux 文件权限超详细详解(读懂权限标识、数字权限、特殊权限、chmod/chown)
linux·运维·服务器
qq3186929962 小时前
ThinkPHP + Supervisor 队列任务丢失:僵尸 Worker 排查全记录
服务器·thinkphp·宝塔
Plastic garden2 小时前
Docker(3)Docker 镜像 & Dockerfile
运维·docker·容器
console.log('npc')2 小时前
Windows试用期重置工具,纯官方 / 安全教程
运维·自动化·typora·脚本
月夜的风吹雨2 小时前
Linux 基础开发工具详解:从 yum 到 gdb 实战指南
linux·git·ubuntu·centos·vim
小马爱打代码2 小时前
MySQL高可用与扩展:主从复制、读写分离、分库分表
服务器·数据库·mysql