本文要点
- 报错不是「文件丢了」,是「名字搜不到」:14432 字节的库文件明明在
/tmp/ldso-demo/libs里,程序照样报error while loading shared libraries(exit=127);LD_LIBRARY_PATH一指过去,同一个二进制立刻能跑 - 查找顺序固定,且可以打印出来:
DT_RPATH→LD_LIBRARY_PATH→DT_RUNPATH→/etc/ld.so.cache→ 内置目录。只差RPATH/RUNPATH的两个二进制,LD_DEBUG=libs打出的搜索阶段顺序正好相反 - 找不到一个库的代价是几十次 open:cache 命中时
/bin/ls只开 3 次;不在 cache 里的名字,glibc 把每个内置目录展开成 19 个候选,4 个目录 76 次,叠加 RPATH 与 LD_LIBRARY_PATH 后实测 114 次 - ldd 不是魔法,是个 bash 脚本:
file /usr/bin/ldd=Bourne-Again shell script,第 103 行就是add_env="LD_TRACE_LOADED_OBJECTS=1 …";自己设这个变量,输出和ldd一字不差 - 永远不要动 /lib 下的真实 .so:这台机器
/usr/bin+/usr/sbin共 1377 个二进制,129 个依赖libselinux.so.1(含mv、cp、ls),静态链接的只有 busybox 一个。挪走它,连「改回来」的那条mv都会在 main 之前死掉——本文用 /tmp 副本完整重演了这个死局和它的解法
Linux 动态链接库查找机制:ld.so 到底按什么顺序找库,以及为什么不能动 /lib 下的 .so
error while loading shared libraries: libxxx.so.1: cannot open shared object file: No such file or directory——这条报错几乎所有 Linux 用户都见过,但大多数人对它的理解停留在「装一下就好了」。
这个理解有两个盲区:一是文件在磁盘上不等于找得到,ld.so 只认它自己搜索路径里的名字;二是它发生在程序自己的 main 之前,程序没有机会做任何事——「缺库」不是功能坏了,是根本没启动。这两点平时无所谓,出事的时候(尤其你去动 /lib 的时候)就是生与死的区别。
本文在 Ubuntu 22.04 LTS 演示机(glibc 2.35-0ubuntu3.15,内核 5.15.0-30-generic)上把这套机制实测一遍:造报错、看查找顺序、拆开 ldd 的真身,最后一章复盘一次真实事故——有人把 /lib 下的一个 .so 改名,机器就此失联。所有涉及「坏依赖」的演示都在 /tmp/ldso-demo/ 下用 patchelf(apt 装的 0.14.3)改出来的副本完成,没有动过任何系统文件。
先造一个报错:这条信息到底在说什么
patchelf --add-needed 可以给一个二进制加一条不存在的依赖,比手工编译更直接:
mkdir -p /tmp/ldso-demo/libs
cp /bin/ls /tmp/ldso-demo/ls-broken
patchelf --add-needed libghostdemo.so.1 /tmp/ldso-demo/ls-broken
readelf -d /tmp/ldso-demo/ls-broken | grep NEEDED
/tmp/ldso-demo/ls-broken /tmp/ldso-demo/libs 2>&1
echo "exit=$?"
cp /lib/x86_64-linux-gnu/libutil.so.1 /tmp/ldso-demo/libs/libghostdemo.so.1
echo "--- 库文件现在就在磁盘上 ---"
ls -l /tmp/ldso-demo/libs/libghostdemo.so.1
/tmp/ldso-demo/ls-broken /tmp/ldso-demo/libs 2>&1
echo "--- 告诉 ld.so 去哪找,立刻能跑 ---"
LD_LIBRARY_PATH=/tmp/ldso-demo/libs /tmp/ldso-demo/ls-broken /tmp/ldso-demo/libs 2>&1
echo "--- 诊断口径:ldd 报的 not found ---"
ldd /tmp/ldso-demo/ls-broken 2>&1 | grep -E 'ghost|not found'
拆开这段输出:
- exit=127。程序一行代码都没执行——动态链接发生在
main之前,由加载器完成,失败就直接退出。 - 文件在磁盘上不等于找得到。
libghostdemo.so.1明明躺在/tmp/ldso-demo/libs里(14432 字节),程序照样报No such file or directory,因为ld.so只认自己的搜索结果,不会扫磁盘。 - 修复只需要一个环境变量。
LD_LIBRARY_PATH一指过去,同一个二进制立刻正常——问题从头到尾都只在「搜索路径」这一层。 ldd的口径和运行时一致:libghostdemo.so.1 => not found,用的就是同一套查找逻辑。
ld.so 是程序的第 0 号依赖
先回答「谁在做查找」。内核加载 ELF 程序时只做一件事:读 PT_INTERP,把里面指的程序解释器映射进内存,把控制权交给它。之后解析 NEEDED、搜库、重定位,全是这个解释器(ld.so)的事。
readlink -f /bin
readlink -f /lib
readlink -f /lib64
readelf -l /bin/ls 2>/dev/null | grep -m1 'Requesting'
ls -l /lib64/ld-linux-x86-64.so.2
readelf -d /bin/ls | grep -E 'NEEDED|RUNPATH|RPATH'实测输出:
/usr/bin
/usr/lib
/usr/lib64
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
lrwxrwxrwx 1 root root 42 Sep 3 14:20 /lib64/ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]三个细节:
/bin、/lib、/lib64都是指向/usr的符号链接(merged-usr 布局)。所谓「/lib 下的文件」实际在/usr/lib里——但改名的效果一模一样,因为查找走的是路径。ls的NEEDED只有两条,可ldd /bin/ls显示 4 个库。NEEDED是直接依赖,ldd展示的是闭包:libpcre2-8是libselinux自己的依赖,被递归加载进来。libc.so.6本身也是被ld.so加载的——动态链接是「自举」的:内核只认解释器这一个文件,剩下全靠它。
查找顺序:LD_DEBUG=libs 把它打印出来
网上流传的顺序(RPATH → LD_LIBRARY_PATH → RUNPATH → cache → 默认目录)不用背,glibc 自带开关能原样打出来:
LD_DEBUG=libs /bin/ls >/dev/null 2>/tmp/ldso-demo/o1.log
echo "--- cache 命中时每个库只试一次 ---"
grep -A2 'find library=' /tmp/ldso-demo/o1.log | head -9
echo "cache 命中时 open 次数:$(grep -c 'trying file' /tmp/ldso-demo/o1.log)"
LD_LIBRARY_PATH=/tmp/ldso-demo/libs LD_DEBUG=libs /bin/ls >/dev/null 2>/tmp/ldso-demo/o2.log
echo "--- 加了 LD_LIBRARY_PATH:先在那里扑空,再回落 cache ---"
grep -A3 'find library=libselinux' /tmp/ldso-demo/o2.log | head -6
LD_DEBUG=libs /tmp/ldso-demo/ls-broken >/dev/null 2>/tmp/ldso-demo/o3.log
echo "--- 彻底找不到:cache 之后还要扫内置目录 ---"
echo "open 尝试次数:$(grep -c 'trying file' /tmp/ldso-demo/o3.log)"
grep -m1 'search path=' /tmp/ldso-demo/o3.log | grep -o '(system search path)'
tail -1 /tmp/ldso-demo/o3.log 2>&1
cache 命中路径:三次 open
/bin/ls 的三个库都在 /etc/ld.so.cache 里,每个库的搜索只有两行:search cache=/etc/ld.so.cache → trying file=/lib/x86_64-linux-gnu/libselinux.so.1。cache 是张索引表,直接给出完整路径,一步命中。
cache 没有时:回落到内置目录,代价是几十次 open
libghostdemo.so.1 不在 cache 里,ld.so 只能扫内置目录。这台机器的内置目录是 4 个(/lib/x86_64-linux-gnu、/usr/lib/x86_64-linux-gnu、/lib、/usr/lib),而且每个目录都被展开成 19 个候选——前面加各种 CPU 特性子目录:
1 <目录>/glibc-hwcaps/x86-64-v4
2 <目录>/glibc-hwcaps/x86-64-v3
3 <目录>/glibc-hwcaps/x86-64-v2
4 <目录>/tls/haswell/avx512_1/x86_64
…
19 <目录> (目录本身排在最后)4 个目录 × 19 = 76 次 open,全部失败之后才报错;把 RPATH 和 LD_LIBRARY_PATH 也算上(各 19 个候选),实测 114 次。展开数量与 glibc 版本和 CPU 有关,不用背——重点是:一个不存在的库名,代价是几十次无效的系统调用,这正是 cache 存在的意义。
RPATH 与 RUNPATH:一字之差,优先级完全不同
用 patchelf --force-rpath 和普通 --set-rpath 分别做两个二进制(依赖同一个假库,且库在 RPATH 和 LD_LIBRARY_PATH 两个位置都有),看 LD_DEBUG 打出来的搜索阶段顺序:
ls-rpath(DT_RPATH):
(RPATH from file /tmp/ldso-demo/ls-rpath)
(LD_LIBRARY_PATH)
search cache=/etc/ld.so.cache
(system search path)
ls-runpath(DT_RUNPATH):
(LD_LIBRARY_PATH)
(RUNPATH from file /tmp/ldso-demo/ls-runpath)
search cache=/etc/ld.so.cache
(system search path)结论就是那张表:
| 优先级 | 来源 | 谁设置 | 实测行为 |
|---|---|---|---|
| 1 | DT_RPATH | 旧式链接器 -Wl,-rpath(无 --enable-new-dtags) | 先于 LD_LIBRARY_PATH,且会被所有子进程继承 |
| 2 | LD_LIBRARY_PATH | 环境变量 | 覆盖 cache,测试新库的正规手段 |
| 3 | DT_RUNPATH | 现代链接器默认 | 只影响这个二进制自己,不传给子进程 |
| 4 | /etc/ld.so.cache | ldconfig 生成 | 命中则一步到位 |
| 5 | 内置目录 | 编译进 glibc | /lib/x86_64-linux-gnu 等,永远最后兜底 |
日常最该记住的是第 2 行:LD_LIBRARY_PATH 能覆盖系统库。它是排查「新编的库到底有没有被加载」的最快手段,也是把系统搞挂的常见入口。
cache 从哪来:/etc/ld.so.conf 与 ldconfig
cat /etc/ld.so.conf
ls -1 /etc/ld.so.conf.d/
cat /etc/ld.so.conf.d/libc.conf
cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf
ldconfig -p | wc -l
ldconfig -p | grep 'libselinux.so.1$'
ldconfig -p | grep -c ghostdemo
ls -l /etc/ld.so.cache实测输出:
include /etc/ld.so.conf.d/*.conf
libc.conf
x86_64-linux-gnu.conf
# libc default configuration
/usr/local/lib
# Multiarch support
/usr/local/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
318
libselinux.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libselinux.so.1
0
-rw-r--r-- 1 root root 19920 Oct 1 06:52 /etc/ld.so.cache要点:
/etc/ld.so.conf只有一行include /etc/ld.so.conf.d/*.conf,真正的目录列表在 conf.d 里:/usr/local/lib、/usr/local/lib/x86_64-linux-gnu、/lib/x86_64-linux-gnu、/usr/lib/x86_64-linux-gnu。- cache 里 318 行(316 个库),文件 19920 字节,修改时间
Oct 1 06:52——是上次ldconfig生成的快照,不是实时索引。装了新库没跑ldconfig,cache 里就没有(grep -c ghostdemo为 0 就是因为它只存在于 /tmp)。 ldconfig -p只读缓存,无副作用,随便跑;但ldconfig本身(重建 cache)是系统级操作,别在生产的/lib上做实验。
ldd 不是魔法,它就是个 bash 脚本
很多人把 ldd 当成「系统直接知道每个程序依赖什么」的魔法。拆开看:
type ldd
file "$(type -p ldd)"
grep -n 'LD_TRACE_LOADED_OBJECTS' "$(type -p ldd)"
echo "--- ldd 做的事,一个环境变量就能复现 ---"
LD_TRACE_LOADED_OBJECTS=1 /bin/ls 2>&1
echo "--- 对照:ldd /bin/ls ---"
ldd /bin/ls 2>&1
实测输出:
ldd is /usr/bin/ldd
/usr/bin/ldd: Bourne-Again shell script, ASCII text executable
103:add_env="LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now"
--- ldd 做的事,一个环境变量就能复现 ---
linux-vdso.so.1 (0x00007ffee98e0000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f03f3a24000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f03f37fb000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f03f3764000)
/lib64/ld-linux-x86-64.so.2 (0x00007f03f3a57000)
--- 对照:ldd /bin/ls ---
linux-vdso.so.1 (0x00007ffcb43ce000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f14beb04000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f14be8db000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f14be844000)
/lib64/ld-linux-x86-64.so.2 (0x00007f14beb5b000)ldd 是个 shell 脚本,它做的事只有一件:设 LD_TRACE_LOADED_OBJECTS=1,然后让加载器自己跑一遍并打印。两条命令的输出完全一致,只有地址列不同(ASLR,每次运行都变)。
理解了这一点,ldd 的三个边界就都解释得通了:
- 它「运行」了目标程序。
ldd不是静态解析 ELF(那是readelf -d的事),它真的把加载过程跑了一遍。所以手册页明确建议不要对不可信的二进制用ldd;来路不明的文件用readelf/objdump。深度分析见 Linux 二进制分析:从 file 类型识别到 ELF 结构与 objdump 反汇编。 - 它只回答「能不能找到」,不回答「符号齐不齐」。缺符号版本、
dlopen动态加载的库,ldd都看不出来,要靠LD_DEBUG=bindings、strace(见 strace 系统调用追踪:从卡死进程到缺失文件定位)或直接跑一遍看报错。 - setuid 程序的
ldd结果可能骗人。glibc 对特权程序启用 secure-execution 模式,会忽略LD_LIBRARY_PATH与 RUNPATH(见ld.so(8)的 Security 一节),但ldd在你的非特权环境里跑看不到这个限制——「ldd 说找得到、一跑就报错」是真实存在的场景。
排查清单
按这个顺序走,动态链接类问题基本一轮定位:
ldd <程序>—— 先看哪个库not found,把「程序坏了」缩小成「哪个库没找到」。ls -l <那个库的路径>—— 路径存在吗?不存在是搜索路径问题,存在还报错看下一步。LD_DEBUG=libs <程序> 2>&1 | grep -A3 'find library=<库名>'—— 直接看它按什么顺序搜了哪些目录。LD_LIBRARY_PATH=<库所在目录> <程序>—— 能跑通就只是路径问题;跑不通是库本身的问题(符号缺失、版本不符),换LD_DEBUG=bindings。- 长期方案用
DT_RUNPATH,或把库装进 conf.d 列出的目录后ldconfig—— 不要用LD_LIBRARY_PATH做长期方案。
安全上记住一句就够:1377 个系统二进制里 129 个依赖 libselinux.so.1,静态链接的只有 busybox 一个。任何「挪开一个真实 .so 试试」的想法,都可能把整台机器变成下面这样。
真实事故复盘:为什么永远不要动 /lib 下的真实 .so
这不是假设性的警告。本文演示机所在的那台机器,曾经发生过一次事故:为了「临时绕开」一个 SELinux 相关的问题,把 /lib 下的 libselinux.so.1 用 mv 改了名——打算马上改回来。结果机器当场失去响应,SSH 再也连不上,重启也没用,最后只能从带外控制台进去处理。机制并不神秘,拆开看全是前面讲过的东西。
第一层:你以为改的是一个文件,其实是 129 个程序的命脉
readelf -d /usr/bin/mv | grep NEEDED
ldd /usr/bin/mv 2>&1 | grep -E 'selinux|acl|attr'
ls -l /lib/x86_64-linux-gnu/libselinux.so.1
dpkg -S /lib/x86_64-linux-gnu/libselinux.so.1
n=0; for f in /usr/bin/* /usr/sbin/*; do [ -f "$f" ] && readelf -d "$f" 2>/dev/null | grep -q 'libselinux.so.1' && n=$((n+1)); done; echo "libselinux_users=$n"
for f in /usr/bin/* /usr/sbin/*; do [ -f "$f" ] && echo x; done | wc -l
for f in /usr/bin/* /usr/sbin/*; do [ -f "$f" ] && file -b "$f" 2>/dev/null | grep -q 'statically linked' && echo x; done | wc -l实测输出:
0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1]
0x0000000000000001 (NEEDED) Shared library: [libacl.so.1]
0x0000000000000001 (NEEDED) Shared library: [libattr.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f156e6d3000)
libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007f156e6c9000)
libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007f156e6c1000)
-rw-r--r-- 1 root root 166280 Mar 17 2022 /lib/x86_64-linux-gnu/libselinux.so.1
libselinux1:amd64: /lib/x86_64-linux-gnu/libselinux.so.1
libselinux_users=129
1377
1mv 自己的 NEEDED 第一条就是 libselinux.so.1。全系统 1377 个二进制里 129 个依赖它——包括 mv、cp、ls 这些你以为「最基础」的命令,它们全是动态链接的;静态链接的只有 busybox 一个。
第二层:为什么「马上挪回来」救不了你
在 /tmp 里用副本把这个死局完整重演一遍(全程没动任何系统文件):
D=/tmp/ldso-demo
mkdir -p "$D/rescue"
cp /bin/mv "$D/mv-ghost"
patchelf --add-needed libghostdemo.so.1 "$D/mv-ghost"
cp /lib/x86_64-linux-gnu/libutil.so.1 "$D/rescue/libghostdemo.so.1"
LD_LIBRARY_PATH="$D/rescue" "$D/mv-ghost" --version 2>&1 | head -2
/bin/mv "$D/rescue/libghostdemo.so.1" "$D/rescue/libghostdemo.so.1.bak"
echo "--- 库不见了(只是改了名,还在 /tmp 里)---"
ls -l "$D/rescue" | cat
echo "--- 于是这个 mv 连自己都用不了了 ---"
LD_LIBRARY_PATH="$D/rescue" "$D/mv-ghost" --version 2>&1
echo "rc=$?"
LD_LIBRARY_PATH="$D/rescue" "$D/mv-ghost" 2>&1
echo "rc=$?"
echo "--- 救场:静态链接的 busybox 不依赖任何 .so ---"
file /usr/bin/busybox 2>&1 | cut -c1-120
ldd /usr/bin/busybox 2>&1
/usr/bin/busybox mv "$D/rescue/libghostdemo.so.1.bak" "$D/rescue/libghostdemo.so.1"
echo "restore_rc=$?"
LD_LIBRARY_PATH="$D/rescue" "$D/mv-ghost" --version 2>&1 | head -2实测输出:
mv (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
--- 库不见了(只是改了名,还在 /tmp 里)---
total 16
-rw-r--r-- 1 root root 14432 Oct 3 05:00 libghostdemo.so.1.bak
--- 于是这个 mv 连自己都用不了了 ---
/tmp/ldso-demo/mv-ghost: error while loading shared libraries: libghostdemo.so.1: cannot open shared object file: No such file or directory
rc=127
/tmp/ldso-demo/mv-ghost: error while loading shared libraries: libghostdemo.so.1: cannot open shared object file: No such file or directory
rc=127
--- 救场:静态链接的 busybox 不依赖任何 .so ---
/usr/bin/busybox: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=166d9d43…
not a dynamic executable
restore_rc=0
mv (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.死局的逻辑就四步:
- 库在的时候,这个
mv一切正常(--version输出 coreutils 8.32)。 - 把库改个名(文件还在磁盘上,就差一个名字),
mv立刻报错,退出码 127;不带参数、加--help都一样死。报错发生在main之前,程序没有任何机会「降级运行」——这不是mv写得不好,是动态链接的设计:库必须在加载阶段全部就位。 - 于是你唯一想做的动作——
mv改回来——用的恰恰是那个已经起不来的程序。修复工具本身被你要修的问题弄坏了,这就是「自我锁死」。 - 救生索是静态链接的程序:
busybox不依赖任何.so(ldd直接说not a dynamic executable),它的mv在坏状态下照样执行(restore_rc=0),文件一改回来,之前那个mv立刻复活。
套回真实事故:libselinux.so.1 一被改名,129 个二进制同时死掉,其中就包括所有能改文件名的工具;还能用的只剩 shell 内建命令(cd、echo 这类不加载外部程序的)和静态程序。而 sshd 拉起的登录 shell 里,你习惯敲的命令几乎全是外部程序——这就是「连不上、也修不了」的全部原因。
第三层:为什么连重启都救不回来
重启能解决「进程坏了」,解决不了「磁盘上的文件名就是错的」:改名是写进文件系统的持久化变更,重启后坏状态原样还在。更麻烦的是,开机早期的 systemd、udev 同样是动态链接的,机器可能连多用户模式都进不去。这类事故总是以「从带外控制台进救援模式」收场,原因就在这:只有绕开根文件系统上的动态链接环境,才能获得一组还能用的工具。
正确的做法
- 改系统库的唯一正道是包管理器。
libselinux.so.1属于libselinux1包(dpkg -S可确认),重装、降级、修复都走apt。任何时候都不要用mv/rm/chmod直接操作/lib、/usr/lib、/lib64下的真实文件。 - 需要「隔离一个库」时用不碰系统文件的手段:
LD_LIBRARY_PATH指向自己的目录、复制到 /tmp 再patchelf改副本、或者用容器。三条都能达到目的,而且全部可逆。 - 动
/lib之前先问「谁在用它」:ldd、readelf -d | grep NEEDED、dpkg -S。答案是「一百多个程序」时,这不是能手动改的东西。同时给重要机器留一条静态救生索:确认busybox在、是静态的,并确认带外控制台真的能进——事故里「最后的工具」平时毫无存在感,用上时就是全部。
小结
error while loading shared libraries是加载阶段的失败,发生在main之前——程序没有「部分启动」这个状态,退出码通常是 127。- 文件在磁盘上 ≠ 找得到。
ld.so只认自己的搜索路径:DT_RPATH→LD_LIBRARY_PATH→DT_RUNPATH→/etc/ld.so.cache→ 内置目录,用LD_DEBUG=libs就能原样打印。 - cache 命中一次
trying file就解决;不在 cache 里要扫内置目录,每个目录展开成 19 个候选,实测一个库失败的代价是 76 次 open,叠上 RPATH/LD_LIBRARY_PATH 后 114 次。 ldd是个 bash 脚本,本体是LD_TRACE_LOADED_OBJECTS=1;它只回答「能不能找到」,不回答「符号齐不齐」,对 setuid 程序还可能失真。/lib下的真实.so是 129 个二进制(含mv/cp/ls)的共同命脉,静态救生索全场只有 busybox 一个。挪走它,连「改回来」的那条mv自己都起不来,而且是重启救不回来的持久化损坏。
相关阅读:
- Linux 二进制分析:从 file 类型识别到 ELF 结构与 objdump 反汇编——
readelf/objdump深入看依赖与符号 - strace 系统调用追踪:从卡死进程到缺失文件定位——
ldd看不到的dlopen、符号问题怎么抓 - Linux 环境变量:从 printenv 到 export 与持久化配置——
LD_LIBRARY_PATH、LD_DEBUG是怎么传进进程的 - Linux 软链接与硬链接:从 inode 原理到 ln 命令——
.so.1 → .so.1.0.0链条与 merged-usr 的/lib → /usr/lib
参考资料:
- ld.so(8) — Linux manual page——搜索顺序、
LD_DEBUG、secure-execution 模式的官方描述 - GNU C Library 手册——glibc 动态链接行为说明
评论 (0)
暂无评论,快来抢沙发吧!