编译出的动态库libavxcore.so在Android Studio加载时报错,错误如下:
java.lang.UnsatisfiedLinkError: dlopen failed: /data/app/com.skylight.testlibs-2/lib/arm/libavxcore.so: has text relocations
问题分析:
在之前的安卓版本中,如果应用程序要求系统加载一个带有text relocations的共享库,系统会显示一个警告,但仍然允许加载库。从API 23(Android 6.0)开始,如果应用的目标SDK版本是23或更高版本,系统会拒绝加载包含text relocations的共享库。
造成这种问题的根本原因是动态库的代码并非PIC(Position independent code),TEXTREL表示代码段重定位表地址,PIC的共享对象不会包含任何代码段重定位表。
解决方法一:
1. 在Linux平台,使用readelf命令详细查看动态链接文件(.so)文件
readelf -a libavxcore.so | grep TEXTREL
执行上边命令后,如果输出为以下内容。则说明这个动态库不是PIC。
2. 如果这个动态库不是PIC。查看编译选项是否有-fPIC,如果没有此编译参数,加上-fPIC重新编译后查看动态库是不是PIC。
3. 如果动态库有-fPIC编译参数,使用readelf命令查看动态库所依赖的第三方库有哪些,再使用以上命令定位哪个第三方库带有text relocations。
readelf -a libavxcore.so
4. 重新编译带有text relocations的动态库,在编译时添加-fPIC 或者–enable-pic参数(Android.mk 加上LOCAL_CFLAGS := -fPIC),在编译FFmpeg时还需添加–disable-asm参数。
解决方法二:
编译.so文件时使用了较低版本sdk,而project 中的配置 targetSdkVersion 大于so编译时使用的sdkversion,所以只需要把功能中 的targetSdkVersiontargetSdkVersion设为小于23。
关于PIC :
PIC使.so文件的代码段变为真正意义上的共享
如果不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy。每个copy都不一样,取决于 这个.so文件代码段和数据段内存映射的位置。
也就是不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的。(因为它里面的代码并不是位置无关代码)
如果被多个应用程序共同使用,那么它们必须每个程序维护一份.so的代码副本了。(因为.so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)
-fpic与 -fPIC这两个参数都是gcc 产生地址无关代码,唯一的区别是“-fPIC”产生的代码要大,而“-fpic”产生的代码相对较小,而且较快,但是fPIC在某些硬件平台上会有一些限制,比如全局符号的数量或者代码长度等而是“-fPIC”则没有这样的限制,所以为了方便都使用“-fPIC”参数来产生地址无关代码。