链接动态库
如何程序在连接时使用了共享库,就必须在运行的时候能够找到共享库的位置。linux的可执行程序在执行的时候默认是先搜索/lib和/usr/lib这两个目录,然后按照/etc/ld.so.conf里面的配置搜索绝对路径。同时,Linux也提供了环境变量LDLIBRARYPATH供用户选择使用,用户可以通过设定它来查找除默认路径之外的其他路径,如查找/work/lib路径,你可以在/etc/rc.d/rc.local或其他系统启动后即可执行到的脚本添加如下语句:LDLIBRARYPATH =/work/lib:$(LDLIBRARYPATH)。并且LDLIBRARYPATH路径优先于系统默认路径之前查找(详细参考《使用LDLIBRARYPATH》)。
不过LDLIBRARYPATH的设定作用是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。(LDLIBRARYPATH的缺陷和使用准则,可以参考《Why LDLIBRARYPATH is bad》 )。通常情况下推荐还是使用gcc的-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路径查找库,避免了使用LDLIBRARYPATH环境变量查找。
链接选项和路径
现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。比如我们做嵌入式移植时#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉编译好的zlib库),将target编译好后我们只要把zlib库拷贝到开发板的系统默认路径下即可。或者通过-rpath(或-R )、LDLIBRARYPATH指定查找路径。
链接器ld的选项有 -L,-rpath 和 -rpath-link,看了下 man ld,大致是这个意思:
-L: “链接”的时候,去找的目录,也就是所有的 -lFOO 选项里的库,都会先从 -L 指定的目录去找,然后是默认的地方。编译时的-L选项并不影响环境变量LDLIBRARYPATH,-L只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径,系统还是会到默认路径下查找该程序所需要的库,如果找不到,还是会报错,类似cannot open shared object file。
-rpath-link:这个也是用于“链接”的时候的,例如你显示指定的需要 FOO.so,但是 FOO.so 本身是需要 BAR.so 的,后者你并没有指定,而是 FOO.so 引用到它,这个时候,会先从 -rpath-link 给的路径里找。
-rpath: “运行”的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找。对于交叉编译,交叉编译链接器需已经配置 --with-sysroot 选项才能起作用。也就是说,-rpath指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库。-rpath-link 则只用于链接时查找。
链接搜索顺序
直接man ld。The linker uses the following search paths to locate required shared libraries:
1. Any directories specified by -rpath-link options.
2. Any directories specified by -rpath options. The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers which have been configured with the --with-sysroot option.
3. On an ELF system, for native linkers, if the -rpath and -rpath-link options were not used, search the contents of the environment variable "LD_RUN_PATH".
4. On SunOS, if the -rpath option was not used, search any directories specified using -L options.
5. For a native linker, the search the contents of the environment variable "LD_LIBRARY_PATH".
6. For a native ELF linker, the directories in "DT_RUNPATH" or "DT_RPATH" of a shared library are searched for shared libraries needed by it. The "DT_RPATH" entries are ignored if "DT_RUNPATH" entries exist.
7. The default directories, normally /lib and /usr/lib.
8. For a native linker on an ELF system, if the file /etc/ld.so.conf exists, the list of directories found in that file.
If the required shared library is not found, the linker will issue a warning and continue with the link.
gcc和链接选项的使用
在gcc中使用ld链接选项时,需要在选项前面加上前缀-Wl(是字母l,不是1,我曾多次弄错),以区别不是编译器的选项。 if the linker is being invoked indirectly, via a compiler driver (e.g. gcc) then all the linker command line options should be prefixed by -Wl, (or whatever is appropriate for the particular compiler driver) like this:
1 |
gcc -Wl,--start-group foo.o bar.o -Wl,--end-group |
This is important, because otherwise the compiler driver program may silently drop the linker options, resulting in a bad link.
用例子说话
二进制 |
对应源码 |
|
有一个程序 |
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是一个重要的判断条件,却并不被做为这儿搜索路径!!
结束
本文是在kubuntu 11.10下编写测试的。为了尽可能简单,例子也都是认为制造的。而且我们看到,在使用RPATH的时候是正常的,RUNPATH一般来说,被推荐使用,但这儿它却不能正常工作。
所以,当使用RUNPATH时,我们需要明白:某些情况下可能需要设置环境变量 LD_LIBRARY_PATH
相关推荐
1. 赖库程动态库或可执件,是何找赖的库的是下录顺来• the RPATH binary header (set at build-time) of the l
rPath简化定制Linux.pdf
这实际上是通过一种不算很常用,却比较实用的方法所设置的:编译目标代码时,可以对gcc加入链接参数“-Wl,-rpath”指定动态库搜索路径; 2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径; 3./etc/ld.
更改可执行文件的动态加载程序(“ ELF解释程序”): $ patchelf --set-interpreter /lib/my-ld-linux.so.2 my-program 更改可执行文件和库的RPATH : $ patchelf --set-rpath /opt/my-libs/lib:/other-libs my...
更新OSX install_names和rpath以使代码从库的副本中加载 提供脚本: delocate-listdeps向库显示一棵树所依赖 delocate-path将树所依赖的库复制到树中并重新链接 delocate-wheel将已经复制并重新链接了库依赖...
PS:动态库和静态库文件同名的建议不要放在一起,不然 ld的时候有可能找错文件 2、动态库的用法 gcc -o test test_md5_hash.c -g -L. -lminmd5 -I. 编译完后,建议将so 文件copy 到/usr/lib 然后执行 ldconfig ...
change rpath/runpath in binaries 解压后进入目录 ./configure && make 进行编译,编译成功后可执行文件会生成在当前目录下
最后大致了解了一下,做这个项目最终要的就是需要移植好多的库,每一个库都需要配置,最后在交叉编译好动态库,然后在执行mediastreamer2的时候去调用这些动态库和头文件就OK了。 1、首先meidastream2是基于ortp库的...
全志R16平台编译linux系统V1.0.txt 2017/4/11 13:36 (编译请使用编译android的lichee的选项编译生成的.config文件,不然直接编译会报错!!!!) rootroot@cm-System-Product-Name:/home/wwt/linux_r16$ tar...
苹果的install_name_toolLinux端口该端口装饰了NixOS PatchElf功能,以在Linux上提供Apple的标准install_name_tool实用程序接口。 这样,我们可以在Linux上部署特定于MacOS的CMake逻辑,例如fixup_bundle 。建筑git ...
rootroot@cm-System-Product-Name:/home/wwt/linux_r16$ tar zxvf lichee_parrotv1.1_20161202.tar.gz rootroot@cm-System-Product-Name:/home/wwt/linux_r16$ rootroot@cm-System-Product-Name:/home/wwt/linux_...
政策二进制条不包括静态库不要在RPATH或RUNPATH中嵌入要搜索的库路径($前缀/ lib)嵌入未在RUNPATH中搜索的库路径(如有必要)所有必需的命令和库都在$前缀中生成的二进制文件不链接到标准库(/ lib或/ usr / lib)...
动态库编译 == >make so 静态库的使用方法 === gcc -o test.out test.c -L. -lmemlist -I. test.c 为用例 >make test 动态库的使用 === gcc -o test test.c -g -L. -lmemlist -I. -Wl,-rpath,. >make test2 ...
VectorAttackScanner 这是一个用于分析android,linux和Windows,检测意图,接收方,服务,进程和库的攻击点的工具。 此工具使用静态分析方法来执行此操作,由此工具建立的矢量攻击可以通过模糊测试方法加以攻击以...
路径递归路径遍历安装 $ npm install --save rpath用法 var rpath = require ( 'rpath' ) ;var allPaths = rpath . sync ( '/path/to/rpath' , false ) ;console . log ( allPaths ) ;rpath ( '/path/to/rpath' , ...
auditwheel repair :将这些外部共享库复制到wheel本身,并自动修改适当的RPATH条目,以便在运行时提取这些库。 就像在不更改构建系统的情况下静态链接库一样,这可以实现类似的结果。 建议打包者,像静态链接一样...
aclim1.0_rpath_EBS 从ACLIM 1.0开始运行白令海东部路径的代码和数据文件。 此回购包含ACLIM 1.0的东部白令海Rpath模拟的脚本和数据文件(Hollowed等,2020)。 请参阅Whitehouse等。 (2021),以获得这些模拟的...
libboost_thread.so.1.58.0,系统缺库的时候需要用到,ldd可以检测
hf_ctp_py_proxy 上期技术期货交易api之python封装,...执行以下指令, -Wl,rpath=指定so路径(需要与setup.py中的data_files配合使用) export VERSION=v6.5.1 代码生成 pip uninstall py-ctp -y && python generate/run
r.rpath.windows , r.rpath.mac , r.rpath.linux :用于启动R Language Server软件包的R二进制文件的路径(请参见下文)。 示例: /usr/bin/R (Linux / macOS), C:\\Program Files\\R\\R-3.5.2\\bin\\x64\\R....