环境:
os:Ubuntu 14.04 64bit
使用命令自制交叉编译器 这里指定:
API 为21
armeabi指令集
$NDKROOT/build/tools/make-standalone-toolchain.sh --arch=arm --platform=android-21 --install-dir=/tmp/.tmp-toolchain
之后在 /tmp/.tmp-toolchaine/bin/
可以看到制作好的交叉编译器
使用该编译器编译一个动态库
/tmp/.tmp-toolchain/bin/arm-linux-androideabi-gcc test.c -fPIC -shared -o libtest.so
使用命令查看该库的信息
readelf -d librest.so
Dynamic section at offset 0xee4 contains 28 entries:
Tag Type Name/Value
0x00000003 (PLTGOT) 0x1fec
0x00000002 (PLTRELSZ) 16 (bytes)
0x00000017 (JMPREL) 0x278
0x00000014 (PLTREL) REL
0x00000011 (REL) 0x270
0x00000012 (RELSZ) 8 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 1
0x00000006 (SYMTAB) 0x128
0x0000000b (SYMENT) 16 (bytes)
0x00000005 (STRTAB) 0x198
0x0000000a (STRSZ) 91 (bytes)
0x00000004 (HASH) 0x1f4
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x0000001a (FINI_ARRAY) 0x1ed8
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x00000019 (INIT_ARRAY) 0x1ee0
0x0000001b (INIT_ARRAYSZ) 4 (bytes)
0x00000010 (SYMBOLIC) 0x0
0x0000001e (FLAGS) SYMBOLIC BIND_NOW
0x6ffffffb (FLAGS_1) Flags: NOW
0x6ffffff0 (VERSYM) 0x224
0x6ffffffc (VERDEF) 0x234
0x6ffffffd (VERDEFNUM) 1
0x6ffffffe (VERNEED) 0x250
0x6fffffff (VERNEEDNUM) 1
0x00000000 (NULL) 0x0
使用该库作为依赖库使用NDK进行编译
Android.mk 如下:
1 LOCAL_PATH :=$(call my-dir)
2
3 include $(CLEAR_VARS)
4 LOCAL_MODULE := ttt
5 LOCAL_SRC_FILES := libtest.so
6 include $(PREBUILT_SHARED_LIBRARY)
7
8
9
10 include $(CLEAR_VARS)
11 LOCAL_MODULE := rrpp
12 LOCAL_CFLAGS := -fPIC
13 LOCAL_LDFLAGS := -llog
14 HAS_SOLIB_VERSION ?= 1
15 LOCAL_SRC_FILES:= $(LOCAL_PATH)/tool.c
16
17 LOCAL_C_INCLUDES:=$(LOCAL_PATH)/
18
19 LOCAL_SHARED_LIBRARIES := ttt
20
21
22 LOCAL_CPP_FEATURES := rtti
23 include $(BUILD_SHARED_LIBRARY)
24
上面的意思是使用前面编译的动态库作为预构建库进一步编译NDK库
使用ndk-budild命令进行编译 得到librrpp.so
readelf -d librrpp.so
Dynamic section at offset 0x2e40 contains 32 entries:
Tag Type Name/Value
0x00000003 (PLTGOT) 0x3f84
0x00000002 (PLTRELSZ) 224 (bytes)
0x00000017 (JMPREL) 0xc20
0x00000014 (PLTREL) REL
0x00000011 (REL) 0xbe0
0x00000012 (RELSZ) 64 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 3
0x00000006 (SYMTAB) 0x158
0x0000000b (SYMENT) 16 (bytes)
0x00000005 (STRTAB) 0x4d8
0x0000000a (STRSZ) 1247 (bytes)
0x00000004 (HASH) 0x9b8
0x00000001 (NEEDED) Shared library: [/home/hostname/ndk-test/hello-jni/obj/local/armeabi/libtest.so]
0x00000001 (NEEDED) Shared library: [liblog.so]
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000001 (NEEDED) Shared library: [libm.so]
0x00000001 (NEEDED) Shared library: [libstdc++.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x0000000e (SONAME) Library soname: [librrpp.so]
0x0000001a (FINI_ARRAY) 0x3e34
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x00000019 (INIT_ARRAY) 0x3e3c
0x0000001b (INIT_ARRAYSZ) 4 (bytes)
0x0000001e (FLAGS) BIND_NOW
0x6ffffffb (FLAGS_1) Flags: NOW
0x6ffffff0 (VERSYM) 0xb34
0x6ffffffc (VERDEF) 0xba4
0x6ffffffd (VERDEFNUM) 1
0x6ffffffe (VERNEED) 0xbc0
0x6fffffff (VERNEEDNUM) 1
0x00000000 (NULL) 0x0
可以注意到我们依赖的库有一个是带路径的
0x00000001 (NEEDED) Shared library: [/home/jiangc/ndk-test/hello-jni/obj/local/armeabi/libtest.so]
这时候将该库放到Android 工程的libs下面会出现
linker: /data/app/xxx/lib/arm/libtest.so :is missing DT_SONAME will use basename as a replacemont: "libtest.so"
linker: /data/app/xxx/lib/arm/librrpp.so: library his invalid DT_NEEDED entry '/home/jiangc/ndk-test/hello-jni/obj/local/armeabi/libtest.so'
回去看前面libtest.so库的信息,里面是没有 SONAME 条目的 文档说明:
无效的DT_NEEDED条目(自API 23强制执行) 虽然库依赖性(ELF头文件中的DT_NEEDED条目)可以是绝对路径,但在Android上没有意义,因为您无法控制系统将安装库的位置。一个DT_NEEDED条目应该和所需的库的SONAME相同,把在运行时找到库的业务留给动态链接器。
在API 23之前,Android的动态链接器忽略了完整的路径,并且在查找所需的库时只使用了基本名称(最后一个’/’之后的部分)。由于API 23,运行时链接程序将严格遵守DT_NEEDED,所以如果它不在设备上的确切位置,它将无法加载库。
更糟糕的是,一些构建系统有错误,导致他们插入指向构建主机上的文件的DT_NEEDED条目,这是在设备上找不到的。
解决办法:在编译依赖库的时候添加 -Wl,-soname,libxxx.so 编译选项,这样编译出来的库会有 SONAME 条目
当我们把这个选项加上重新编译libtest.so
/tmp/.tmp-toolchain/bin/arm-linux-androideabi-gcc test.c -fPIC -shared -Wl,-soname,libtest.so -o libtest.so
在使用 命令查看库信息
readelf -d librest.so
Dynamic section at offset 0xedc contains 29 entries:
Tag Type Name/Value
0x00000003 (PLTGOT) 0x1fec
0x00000002 (PLTRELSZ) 16 (bytes)
0x00000017 (JMPREL) 0x278
0x00000014 (PLTREL) REL
0x00000011 (REL) 0x270
0x00000012 (RELSZ) 8 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 1
0x00000006 (SYMTAB) 0x128
0x0000000b (SYMENT) 16 (bytes)
0x00000005 (STRTAB) 0x198
0x0000000a (STRSZ) 91 (bytes)
0x00000004 (HASH) 0x1f4
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x0000000e (SONAME) Library soname: [libtest.so]
0x0000001a (FINI_ARRAY) 0x1ed0
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x00000019 (INIT_ARRAY) 0x1ed8
0x0000001b (INIT_ARRAYSZ) 4 (bytes)
0x00000010 (SYMBOLIC) 0x0
0x0000001e (FLAGS) SYMBOLIC BIND_NOW
0x6ffffffb (FLAGS_1) Flags: NOW
0x6ffffff0 (VERSYM) 0x224
0x6ffffffc (VERDEF) 0x234
0x6ffffffd (VERDEFNUM) 1
0x6ffffffe (VERNEED) 0x250
0x6fffffff (VERNEEDNUM) 1
0x00000000 (NULL) 0x0
和第一次编译的信息对比,多了一个SONAME 条目,后main的名字是我们指定的。
我们再来看使用NDK依赖这个有SONAME条目的库编译一个新的库:
readelf -d librrpp.so
Dynamic section at offset 0x2e40 contains 32 entries:
Tag Type Name/Value
0x00000003 (PLTGOT) 0x3f84
0x00000002 (PLTRELSZ) 224 (bytes)
0x00000017 (JMPREL) 0xbf0
0x00000014 (PLTREL) REL
0x00000011 (REL) 0xbb0
0x00000012 (RELSZ) 64 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 3
0x00000006 (SYMTAB) 0x158
0x0000000b (SYMENT) 16 (bytes)
0x00000005 (STRTAB) 0x4d8
0x0000000a (STRSZ) 1197 (bytes)
0x00000004 (HASH) 0x988
0x00000001 (NEEDED) Shared library: [libtest.so]
0x00000001 (NEEDED) Shared library: [liblog.so]
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000001 (NEEDED) Shared library: [libm.so]
0x00000001 (NEEDED) Shared library: [libstdc++.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x0000000e (SONAME) Library soname: [librrpp.so]
0x0000001a (FINI_ARRAY) 0x3e34
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x00000019 (INIT_ARRAY) 0x3e3c
0x0000001b (INIT_ARRAYSZ) 4 (bytes)
0x0000001e (FLAGS) BIND_NOW
0x6ffffffb (FLAGS_1) Flags: NOW
0x6ffffff0 (VERSYM) 0xb04
0x6ffffffc (VERDEF) 0xb74
0x6ffffffd (VERDEFNUM) 1
0x6ffffffe (VERNEED) 0xb90
0x6fffffff (VERNEEDNUM) 1
0x00000000 (NULL) 0x0
和上面的信息对比可以发现,librrpp.so库虽然依赖libtest.so,但是没有了原来的绝对路径。和文档说明对比就明白了为什么没有SONAME条目的依赖库找不到的原因了
为什么NDK编译的库会有这个SONAME条目?
新版NDK会自动加入 -Wl,-soname,libxxx.so 这个选项。所以不用关心这个问题