专家
公告
财富商城
电子网
旗下网站
首页
问题库
专栏
标签库
话题
专家
NEW
门户
发布
提问题
发文章
嵌入式
- RPATH RUNPATH 和 LD_LIBRARY_PATH
2019-07-13 08:34
发布
生成海报
站内文章
/
嵌入式Linux
13060
0
1074
Linux
下链接编译和
Windows
绝对有很大的区别。
比如应用程序如何加载一个
so
?为什么我当前目录下有
so
加载不上能?怎么着我的应用程序就加载到别的路径的
so
了呢?
我们从加载讨论起: 影响加载的东西:
首先是
ELF
中的变量
RPATH
和
RUNPATH
然后是环境变量
LD_PRELOAD
和
LD_LIBRARY_PATH
LD_PRELOAD
属于强制加载,有点
Injection
的感觉,这里暂时不讨论,
LD_PRELOAD
似乎总是最强势的……
我们从一个应用程序 a 链接一个 lib_b.so 开始
下面是
a
和
b
的代码以及编译链接过程
a.c:
#include
void
test_a() { test_b(); }
int
main() { test_a();
return
0
; }
b.c: #include
void
test_b() { }
编译: [root@vm153 01]#
gcc
b.c -shared -fPIC -o lib/
lib_b.so [root@vm153 01]#
gcc
-o a a.c -Llib -
l_b 因为lib_b.so 在 lib 目录下 所以需要
-Llib 运行结果如下: [root@vm153 01]# ./
a .
/a: error
while
loading shared libraries: lib_b.so: cannot open shared
object
file
: No such
file
or directory 失败,解决方案如下:
把 lib_b.so 挪到 /lib 或 /usr/lib 下面,但是这个不太靠谱,毕竟是系统的目录
使用LD_LIBRARY_PATH 把当前 lib 目录加进来,执行OK
[root@vm153 01]# LD_LIBRARY_PATH=lib ./a
重新编译a 加入RPATH,执行 OK,注意用readelf 可以查看RPATH (原来-Wl,-rpath= 的作用就是在目标文件里加入了RPATH)
[root@vm153 01]#
gcc
-o a a.c -Llib -l_b -Wl,-rpath=
lib [root@vm153 01]# .
/
a [root@vm153 01]# readelf
-d a |
grep
PATH
0x0000000f
(RPATH) Library rpath: [lib] [root@vm153 01]#
那么
LD_LIBRARY_PATH
和
RPATH
同时提供的情况下,结果会如何呢?
我们要做一个简单的实验,我们尝试提供两个lib_b.so 并且在test_b代码中给出打印。
b1.c 和 b2.c 代码一致 如下:
#include
void
test_b() { printf(
"
test_b in: %s
"
, __FILE__); }
编译过程如下: [root@localhost 02]#
gcc
b1.c -shared -fPIC -o lib1/
lib_b.so [root@localhost 02]#
gcc
b2.c -shared -fPIC -o lib2/
lib_b.so [root@localhost 02]#
gcc
-o a a.c -Llib1 -l_b -Wl,-rpath=lib1 显然 a 的 RPATH 已经被设置成了 lib1 , 然后我们使用 LD_LIBRARY_PATH 设置为lib2 来看看 a究竟加载那个 lib_b [root@localhost 02]# LD_LIBRARY_PATH=lib2 ./
a test_b
in
: b1.c [root@localhost 02]#
很明显是RPATH 起了作用。其实没有必要加入打印,用ldd 一样可以验证具体加载了哪个 so [root@localhost
02
]# LD_LIBRARY_PATH=lib2 ldd a linux
-vdso.so.
1
=> (
0x00007fffc51c8000
) lib_b.so
=>
lib1/lib_b.so
(
0x00007f5b6d268000
) libc.so.
6
=> /lib64/libc.so.
6
(
0x00007f5b6cebe000
)
/lib64/
ld
-linux-x86-
64
.so.
2
(
0x00007f5b6d46a000
) [root@localhost
02
]#
加上RUNPATH 吧!
再复杂一点的情况, RUNPATH 我们用的不太多,但是有的系统上 -Wl,-rpath 会一起加上RUNPATH,重新编译a 使用--enable-new-dtags 加入RUNPATH
[root@localhost
02
]#
gcc
-o a a.c -Llib1 -l_b -Wl,-rpath=lib1,--enable-new-
dtags [root@localhost
02
]# readelf -d a |
grep
PATH
0x000000000000000f
(
RPATH
) Library rpath: [lib1]
0x000000000000001d
(
RUNPATH
) Library runpath: [lib1] [root@localhost
02
]# LD_LIBRARY_PATH=
lib2 ldd a linux
-vdso.so.
1
=> (
0x00007fffa4591000
) lib_b.so
=>
lib2/lib_b.so
(
0x00007f8779855000
) libc.so.
6
=> /lib64/libc.so.
6
(
0x00007f87794ab000
)
/lib64/
ld
-linux-x86-
64
.so.
2
(
0x00007f8779a57000
) [root@localhost
02
]#
恩~~ 这个有点困惑了。 其实逻辑是这样的:加载器首先看有没有 RUNPATH, 如果有那么就尝试从LD_LIBRARY_PATH 加载,如果还是失败再从RUNPATH加载。当然最后如果还是不行,会尝试 /lib 和 /usr/lib。 这个RUNPATH 真的有点二,设置了一个值,但是不会马上启用,而且还干掉了 RPATH 成全了LD_LIBRARY_PATH……
如何创建只有RUNPATH 的 ELF 文件?
按照上面的推论,当RPATH 和 RUNPATH 同时存在的话,那么RPATH 实际就没有用了(这点逻辑应该能理解吧),可是ld 链接出来的要么没有 RPATH 和 RUNPATH,要么只有RPATH, 要么两个都有(我想可能是为了兼容一下老版本的loader 吧)。 简单的方案,利用第三方工具 patchelf (
http://nixos.org/patchelf.html
)。先编译出一个没有RPATH RUNPATH的 ELF 文件,然后 用 patchelf --set-rpath 来添加,这样就只会添加 RNPATH。当然这个工具还有好些个其他选项……
小结一下一个ELF 文件自身加载 so的情况:
其实这三者的关系概括起来没有几点:
LD_LIBRARY_PATH 是个环境变量 可以用来指定加载so 的路径,并且优先级高于系统默认的。
RPATH 是 ELF 格式里面的一个数据,他的优先级比 LD_LIBRARY_PATH 还要高
但是有一个很二 的 RUNPATH,也是 ELF 格式中的,如果他出现了 RPATH 就躲起来了,LD_LIBRARY_PATH 又成了首选
整理出了一张表,供参考 ELF 中 RPATH ELF 中 RUNPATH LD_LIBRARY_PATH 变量 尝试加载目录的顺序 未设置
未设置
未设置
/lib => /usr/lib
未设置
未设置
设置
${
LD_LIBRARY_PATH
} => /lib => /usr/lib
设置
未设置
未
设置 ${RPATH} =>
/lib => /usr/lib
设置
未设置
设置
${RPATH} =>
${
LD_LIBRARY_PATH
} =>
/lib => /usr/lib
设置
或
未设置
设置
设置
${LD_LIBRARY_PATH} => ${RUN_PATH} => /lib => /usr/lib
设置
或
未设置
设置
未
设置
${RUN_PATH} => /lib => /usr/lib
Ta的文章
更多
>>
- RPATH RUNPATH 和 LD_LIBRARY_PATH
0 个评论
热门文章
×
关闭
举报内容
检举类型
检举内容
检举用户
检举原因
广告推广
恶意灌水
回答内容与提问无关
抄袭答案
其他
检举说明(必填)
提交
关闭
×
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮