关于NDK使用预构建库编译找不到依赖库的问题

环境:
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 这个选项。所以不用关心这个问题

你可能感兴趣的:(嵌入式)