总结一下原因:
1. 编译链接时
1> 库的搜索路径不对
a. 默认路径是:/lib, /usr/lib/, /usr/local/lib
b. -L: 指定连接时动态库的路径
c. LD_LIBRARY_PATH: 指定连接路径
一般编译时用-L指明路径即可
2> 链接库之间有依赖关系,链接时顺序书写不对 // 在编译可执行程序或静态库时,编译动态库时不会出错,但运行时出错
假设liba.a 依赖于libb.b,目标test需要liba和libb,那么生成test的命令应该为
gcc -o test -la -lb 而不是gcc -o test -lb -la
解决办法有两种,一是书写顺序正确,二是使用--start-group xxx --end-group(或“-(”和“-)”),start-group和end-group之间的库会循环查找依赖
建议书写规则如下:
g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
即先.o文件,再是依赖的lib,lib顺序从最上层到最底层,最后是输出目标
2. 运行时
1> 库的搜索路径不对
a: ELF可执行文件中动态段中DT_RPATH所指定的路径。这实际上是通过一种不算很常用,却比较实用的方法所设置的:编译目标代码时,可以对gcc加入链接参数“-Wl,-rpath”指定动态库搜索路径;
b: 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
c: /etc/ld.so.cache中所缓存的动态库路径(如果支持ld.so.cache的话)。这可以通过修改配置文件/etc/ld.so.conf中指定的动态库搜索路径来改变;
d: 默认的动态库搜索路径/lib; // linux平台, android/win 平台不同
e: 默认的动态库搜索路径/usr/lib // linux平台, android/win 平台不同
-Wl,-rpath在嵌入式平台下常用
2> 依赖的so有问题
在1的2>中已说明,如果编译so时,so所依赖的动态库顺序不对,会造成运行时出错
例如:libtest.so依赖libxxx.so,编译链接命令如下:
g++ -fPIC -shared -lxxx test.o -o libtest.so
显然lxxx放在的test.o的前面,可以编译通过,但是运行就会报错了
解决办法: 一是保证lxxx在test.o的右边,而是使用-Wl,--no-as-needed
即
g++ -fPIC -shared test.o-lxxx -o libtest.so
或
g++ -fPIC -shared -Wl,--no-as-needed -lxxx test.o -o libtest.so
no-as-needed的解释
Always set DT_NEEDED for following dynamic libs // 总是把后面的libs放到DT_NEEDED中,可以通过readelf -d来查看是否已经将lxxx加入了DT_NEEDED中
3> dlopen打开的so用到了主程序或宿主so的api
编译时需要-rdynamic -whole-archive参数,保证主程序或宿主
参考http://blog.youkuaiyun.com/fingding/article/details/46906773
dlopen遇到的undefined reference to: 问题
具体问题和这位仁兄是一样的
https://groups.google.com/a/chromium.org/forum/#!searchin/chromium-dev/dlopen/chromium-dev/tCdkayOKBlk/B8d5MgOiPEMJ
解决方法主要参考,主要还是动态库中DT_NEEDED的问题
http://m.blog.youkuaiyun.com/blog/Pun_C/39231175
一般性问题:
dlopen两个不同的so,假设分别为liba.so和libb.so,轻重liba.so依赖与libb.so,而且是直接调用libb.so中的函数。
如果dlopen时使用参数RTLD_GLOBAL,也就没问题了。但现在是如果不使用RTLD_GLOBAL,怎么保证不出现undefined reference错误(应用场景就是android平台下,System.loadlibrary或System.load都是调用了dlopen且参数是RTLD_LAZY,如何保证android平台下通过System.loadlibrary打开的两个so能够互相访问(直接调用函数))
解决方法:
编译liba.so时加入libb.so依赖,且保证liba.so的dynamic section的NEEDED有libb.so,另外就是保证运行是在系统搜索路径能找到libb.so
这一点在在4.3版本的代码里,nativeLoad这个方法接受多一个参数ldLibraryPath(即app安装路径,如/data/data/org.chromium.content_shell/lib,参考3)
所以
g++ -fPIC -shared -a.o -L. -lb -o liba.so
readelf -d liba.so 就能查看是否有(NEEDED) Shared library: [libb.so]
参考:
1 http://www.cnblogs.com/OCaml/archive/2012/06/18/2554086.html#sec-1-1
2 http://blog.youkuaiyun.com/fingding/article/details/39547123
3 http://m.blog.youkuaiyun.com/blog/Pun_C/39231175