动态库加载路径之RPATH与RUNPATH

本文原地址: http://www.feitianzhi.com/boke/index.php/archives/21/

转载请注明出处,有疑问或错误请发邮件到[email protected] 或加QQ群:869598376


参考文章:https://www.oschina.net/question/234345_52687

用例子说话

二进制

对应源码

有一个程序

a.out

main.c

需要加载插件A

libA.so

liba.c

A需要另一个动态库

libB.so

libB1.c 或 libB2.c

本文的关注点就是:到底是哪一个libB.so被加载

目录结构:

/home/debao/ttt/a.out
/home/debao/ttt/libA.so
/home/debao/ttt/libB.so
/usr/lib/libB.so
具体源码
main.c ==> ./a.out

include <stdio.h>

include <dlfcn.h>

typedef int (*funcA)(int, int);
int main()
{

void * plugin = dlopen("./libA.so", RTLD_LAZY);
funcA f = (funcA)dlsym(plugin, "funcA");
printf("main: %d\n", f(3,4));
return 0;

}
liba.c ==> ./libA.so

include <stdio.h>

int funcB(int, int);
int funcA(int a, int b)
{

printf("hello from funcA\n");
return funcB(a, b);

}
libb1.c ==> ./libB.so

include <stdio.h>

int funcB(int a, int b)
{

printf("Hello from funcB 1\n");
return a*b;

}
libb2.c ==> /usr/lib/libB.so

include <stdio.h>

int funcB(int a, int b)
{

printf("Hello from funcB 2\n");
return a*b;

}
编译库文件
编译动态库libB.so

$ gcc -shared -fPIC libb2.c -o libB2.so
$ sudo mv libB2.so /usr/lib/libB.so
$ gcc -shared -fPIC libb.c -o libB.so
编译动态库libA.so

$ gcc -shared -fPIC liba.c -o libA.so -L. -lB
顺便看看该elf文件的头部信息:

$ readelf libA.so -d

Dynamic section at offset 0xf20 contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libB.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]
...
恩,只有库的文件名信息,而没有路径信息。

编译程序
第一次编译运行(什么路径都不加)

$ gcc main.c -ldl
$ ./a.out
hello from funcA
Hello from funcB 2
main: 12
程序:dlopen从当前目录找到libA.so,然后却在/usr/lib/中找到libB.so(没有使用当前目录的libB.so,这是我们需要的么?)

第二次编译运行(使用DT_RPATH)

$ gcc main.c -ldl -Wl,--rpath=.
$ ./a.out
hello from funcA
Hello from funcB 1
main: 12
恩,使用当前目录的libB.so,很理想的东西

可是,由于DT_RPATH无法被环境变量LD_LIBRARY_PATH覆盖,不是不建议被使用,而是建议使用DT_RUNPATH么?

第三次编译运行(使用DT_RUNPATH)

$ gcc main.c -ldl -Wl,--rpath=.,--enable-new-dtags
$ ./a.out
hello from funcA
Hello from funcB 2
main: 12
问题重新出现,使用的系统路径中的libB.so 而不是当前目录下的。

程序头部信息
通过下列命令可以查看:

$ readelf -d a.out
为了完整起见,列出前面3次编译的程序的信息:

没有rpath和runpath

Dynamic section at offset 0xf20 contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000c (INIT) 0x8048360
...
包含rpath

Dynamic section at offset 0xf18 contains 22 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000f (RPATH) Library rpath: [.]
0x0000000c (INIT) 0x8048360
....
包含rpath和runpath

Dynamic section at offset 0xf10 contains 23 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libdl.so.2]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000f (RPATH) Library rpath: [.]
0x0000001d (RUNPATH) Library runpath: [.]
原因
RPATH and RUNPATH给出这个问题的答案:

Unless loading object has RUNPATH:

RPATH of the loading object,
    then the RPATH of its loader (unless it has a RUNPATH), ...,
    until the end of the chain, which is either the executable
    or an object loaded by dlopen
Unless executable has RUNPATH:
    RPATH of the executable

LD_LIBRARY_PATH
RUNPATH of the loading object
ld.so.cache
default dirs
用它解释第一个程序:

libA.so 没有RUNPATH,故而
使用其RPATH (没有)
递归查找其loader直到链条的顶端(可执行程序或被dlopen打开的对象)的RPATH或者遇RUNPATH退出 (没有命中)
可执行程序没有RUNPATH,故而
使用其RPATH (没有)
环境变量LD_LIBRARY_PATH,(没有)
libA.so 的RUNPATH (没有)
ld.so.cache (没有命中)
默认路径/usr/lib (命中)
用它解释第二个程序:

libA.so 没有RUNPATH,故而
使用其RPATH (没有)
递归查找其loader直到链条的顶端(可执行程序或被dlopen打开的对象)的RPATH或者遇RUNPATH退出 (没有命中)
可执行程序没有RUNPATH,故而
使用其RPATH (命中)
用它解释第三个程序:

libA.so 没有RUNPATH,故而
使用其RPATH (没有)
递归查找其loader直到链条的顶端(可执行程序或被dlopen打开的对象)的RPATH或者遇RUNPATH退出 (没有命中)
可执行程序有RUNPATH,(继续前行)
环境变量LD_LIBRARY_PATH,(没有)
libA.so 的RUNPATH (没有)
ld.so.cache (没有命中)
默认路径/usr/lib (命中)
有意思的就是这个程序了,可执行程序的RUNPATH是一个重要的判断条件,却并不被做为这儿搜索路径!!