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

动态加载器负责:

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

所以:

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

核心方向是对的。

但更准确一点是:

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

相关推荐
GeW12 小时前
新质生产力路线图Linux、数据库、国产化替代一次讲透
linux
aningx14 小时前
cachyos 从 GRUB 切换到 rEFInd
linux
第404号工位15 小时前
40块钱二手瘦客户端,如何跑满家庭物联网中枢?(拒绝Docker爆盘实录)
linux·架构·node.js
对象存储与RustFS15 小时前
网盘限速逼你开会员?用 RustFS 自己搭一个不限速的“私人网盘
服务器·后端·docker
骑着蜗牛撵大象32717 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
众壹新能源科技17 小时前
逆变器自带无功不够用,加装5MVar SVG后功率因数从0.85回到0.99
运维·人工智能·自动化
..Dauntless..17 小时前
【Linux】一切皆文件和缓冲区
linux·运维·服务器
裕晟资质规划17 小时前
三类资质变更报告时限差在哪?军工 15 日、集成印制 10 日
大数据·服务器·网络
QMCY_jason17 小时前
x86 ubuntu2204 无gui 安装 openeuler 2203 aarch64版本
linux·运维·服务器
重生之小比特17 小时前
【Linux系统】12 进程程序替换 自定义Shell 命令行解释器
linux·运维·服务器