NDK移植编译&心得&ADB/GDB简单应用

I. 基本流程

1.设置环境变量

建议去官网下载最新版本的NDK,老版本很多宏定义缺失,比较麻烦。同时,之前用android-ndk-r10d,需要安装32位库环境。

yum install libstdc++-devel.i686
yum install libzip.i686 
#!/bin/bash
# environment setting
export ANDROID_NDK_PATH=/opt/android-ndk-r14b
export TOOLCHAINS_PATH=${ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin
export PATH=${PATH}:${TOOLCHAINS_PATH}
export SYSROOT_PATH=${ANDROID_NDK_PATH}/platforms/android-22/arch-arm
export LIBSTD_PATH=${ANDROID_NDK_PATH}/sources/cxx-stl/gnu-libstdc++/4.9

export CC=arm-linux-androideabi-gcc
export CXX=arm-linux-androideabi-g++
export AR=arm-linux-androideabi-ar
export CFLAGS="-DANDROID -fPIC  -std=c++11 -ffunction-sections -funwind-tables -fstack-protector  -no-canonical-prefixes -fexceptions -frtti -marm -march=armv7-a -mtune=cortex-a8 -mfloat-abi=softfp -mfpu=neon -mthumb -fno-strict-aliasing -finline-limit=64 -Wa,--noexecstack -Wformat -Werror=format-security -g -Wall --sysroot=${SYSROOT_PATH} -I${LIBSTD_PATH}/include -I${LIBSTD_PATH}/libs/armeabi/include -I${ANDROID_NDK_PATH}/sources/android/cpufeatures/"
export LDFLAGS="--sysroot=${SYSROOT_PATH} -L${LIBSTD_PATH}/libs/armeabi -lgnustl_shared -lgcc -no-canonical-prefixes -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -lstdc++ -lsupc++ -lc -lm -pthread"

只要事先运行脚本,基本能应对一般的库(包括开源库)的编译。
因为C编译CFLAGS中不存在-std=c++11和-frtti,也不需要STL库gnustl_shared和C++库supc++ ,所以编译纯C库的时候建议移除他们

2.修改Makefile模板,针对EXE情况做特殊处理

GCC=$(CROSS)gcc
GXX=$(CROSS)g++
AR=$(CROSS)ar

ifeq ($(CROSS),arm-linux-androideabi-)
ifdef EXE_TARGET
CFLAGS += -pie -fPIE
LDFLAGS += -pie -fPIE
endif
endif

##3.PTLIB的configure参数
注意,这里的configure文件我做了少量修改。仅个人记录用。

./configure --enable-exceptions --host=armv7-a-android --disable-openssl --disable-shmaudio --enable-shmvideo=no --disable-resolver --enable-serial=no --disable-v4l2

II. 心得

1.主要的头文件目录

/opt/android-ndk-r14b/platforms/android-22/arch-arm/usr/include
/opt/android-ndk-r14b/sources/cxx-stl/gnu-libstdc++/4.9/include

##2.编译错误解决
1)部分原来是/usr/include/sys下的头文件路径可能有变化,直接在incude目录下。
2)出现链接错误时,务必用arm-linux-androideabi-nm检查下是否没实现。存在头文件与库文件不一致的现象。

2.ifaddrs.h不存在

1)使用libuv库内的 android-ifaddrs即可。部分版本可能有坑。
**注意更新,libuv也迭代了几个版本过。

3。dlopen失败

Android8以后的版本,插件形式的程序在正常使用dlopen加载动态库时,可能碰到下面的问题

library "xxx.so"  needed or dlopened by "xxxapp" is not accessible for the namespace

原因是Android8以后对于动态库的位置有约束,只能出现在某些目录下,不光是APP,NDK编译的直接运行的程序也一样约束。具体的细节可以看这片文章
《Namespace based Dynamic Linking - Isolating Native Library of Application and System in Android》

解决方法也很简单,将需要的动态库所在的目录加入LD_LIBRARY_PATH,即使是你指定绝对路径的。
在RK3568下测试,并不需要修改/etc/public.libraries.txt 将动态库加入白名单,个人理解是只有APP需要这些白名单。

III. ADB使用

以Linux版本的adb为例;Win的版本也可以,但是总体操作Linux下的方便很多

#!/bin/bash
#连接
adb connect <ip>
#进入shell
adb shell
#如果有多个链路,可以这样选择,端口缺省5555
adb -s <ip>:<port> shell
#传输文件
adb push 1.txt /storage/emulated/0/
#所有动态库需要放置在/system/lib目录下,需要赋予/system目录读写权限,或设置LD_LIBRARY_PATH
mount -o remount,rw /system
#有不少机型采用这种方案即可
adb root && adb remount
#大多数命令需要busybox驱动,例如vi,ifconfig等
busybox vi 1.txt

IV.查看依赖(类似于ldd)

使用readelf函数可以读取动态库或EXE的依赖,下面是示例:

arm-linux-androideabi-readelf -d libjanus_streaming.so 

Dynamic section at offset 0x25bd8 contains 42 entries:
  Tag        Type                         Name/Value
 0x00000003 (PLTGOT)                     0x26d80
 0x00000002 (PLTRELSZ)                   1256 (bytes)
 0x00000017 (JMPREL)                     0x2784
 0x00000014 (PLTREL)                     REL
 0x00000011 (REL)                        0x22b4
 0x00000012 (RELSZ)                      1232 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   145
 0x00000006 (SYMTAB)                     0x148
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0xd88
 0x0000000a (STRSZ)                      3651 (bytes)
 0x00000004 (HASH)                       0x1bcc
 0x00000001 (NEEDED)                     Shared library: [libcurl.so]
 0x00000001 (NEEDED)                     Shared library: [libssl.so.1.1]
 0x00000001 (NEEDED)                     Shared library: [libcrypto.so.1.1]
 0x00000001 (NEEDED)                     Shared library: [libz.so]
 0x00000001 (NEEDED)                     Shared library: [libglib-2.0.so.0]
 0x00000001 (NEEDED)                     Shared library: [libpcre.so]
 0x00000001 (NEEDED)                     Shared library: [libintl.so]
 0x00000001 (NEEDED)                     Shared library: [libiconv.so]
 0x00000001 (NEEDED)                     Shared library: [libjansson.so]
 0x00000001 (NEEDED)                     Shared library: [libgnustl_shared.so]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x0000000e (SONAME)                     Library soname: [libjanus_streaming.so.0]
 0x0000001a (FINI_ARRAY)                 0x26bcc
 0x0000001c (FINI_ARRAYSZ)               8 (bytes)
 0x00000019 (INIT_ARRAY)                 0x26bd4
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000000f (RPATH)                      Library rpath: [/opt/android-libs/lib]
 0x00000010 (SYMBOLIC)                   0x0
 0x0000001e (FLAGS)                      SYMBOLIC BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x6ffffff0 (VERSYM)                     0x20f0
 0x6ffffffc (VERDEF)                     0x2278
 0x6ffffffd (VERDEFNUM)                  1
 0x6ffffffe (VERNEED)                    0x2294
 0x6fffffff (VERNEEDNUM)                 1
 0x00000000 (NULL)                       0x0

V.GDB使用

android下使用gdbserver和gdb配套使用。两个版本必须一致。建议用你NDK目录下的那一对。
整体思路和一般的ARM设备调试类似。

拷贝gdbserver到Android设备上

cd /opt/android-ndk-r14b/prebuilt/android-arm/gdbserver
adb push gdbserver /sdcard/share
  1. 在Android下启动gdbserver

端口可以随意调整,本文以9999为例

#手工跑
./gdbserver 0.0.0.0:9999 ./test_program args
#附加进程
./gdbserver --attach 0.0.0.0:9999 pid
  1. 在调试机上运行gdb
    可以在Linux下也可以在Windows下运行,以Linux下运行为例:
cd  /opt/android-ndk-r14b/prebuilt/linux-x86_64/bin
#windows下的话就是D:\android-ndk-r14b\prebuilt\windows-x86_64\bin
adb pull /system/lib/libc.so .
adb pull /system/lib/libm.so .
……
需要把所有依赖的动态库,都放置到当前准备调试的目录下
系统相关的动态库也必须补齐,否则堆栈无法正确分析
……
./gdb
target remote <android设备IP>:9999
c

后面就和普通的gdb无差别了

VI. tombstone调试

  1. 和前面gdb一样,需要在本地下载完整所有相关的库,特别是libc和libm。
  2. 以一个tombstone调试为例:
** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'HiSTBAndroidV6/Hi3798CV200/Hi3798CV200:5.1.1/LMY48W/1056:eng/test-keys'
Revision: '0'
ABI: 'arm'
pid: 22573, tid: 22585, name: StdsipGateway  >>> ./StdsipGateway <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xe4
    r0 b4407e08  r1 000000e8  r2 00000002  r3 00000000
    r4 b571db50  r5 b44fcf40  r6 00000702  r7 b3ffee10
    r8 b4499c08  r9 b4499c08  sl b5247e85  fp b3fffdb0
    ip b529a7e4  sp b3ffee00  lr b527ac97  pc b56b4774  cpsr 800f0030
    d0  2e3638312e333432  d1  0000000000000000
    d2  0000000000000000  d3  0000000000000000
    d4  6635643933203420  d5  3466333234666439
    d6  3038663038663631  d7  3536326161393637
    d8  0000000000000000  d9  0000000000000000
    d10 0000000000000000  d11 0000000000000000
    d12 0000000000000000  d13 0000000000000000
    d14 0000000000000000  d15 0000000000000000
    d16 0000000000000000  d17 0000000000000001
    d18 da82799977c34831  d19 5a8279995a827999
    d20 5a8279995a827999  d21 5a8279995a827999
    d22 2c5989d7dfc3424b  d23 28090df64848018b
    d24 33b17c02ecea05b7  d25 26d41627e8866a96
    d26 9928b1de7e0ad8b5  d27 65b31a726c7c6c31
    d28 5a8279995a827999  d29 5a8279995a827999
    d30 0000000000000000  d31 0000000000000000
    scr 00000000

backtrace:
    #00 pc 00083774  /system/lib/libSipStack.so (complete_answer_that_establish_a_dialog+727)
    #01 pc 0006a1bf  /system/lib/libSipStack.so
    #02 pc 0006b619  /system/lib/libSipStack.so
    #03 pc 0006ce7f  /system/lib/libSipStack.so (_eXosip_handle_incoming_message+2742)
    #04 pc 00062a29  /system/lib/libSipStack.so
    #05 pc 00062dbb  /system/lib/libSipStack.so
    #06 pc 00063c49  /system/lib/libSipStack.so
    #07 pc 0006d36b  /system/lib/libSipStack.so (eXosip_read_message+1022)
    #08 pc 00094365  /system/lib/libSipStack.so
    #09 pc 00016ea3  /system/lib/libc.so (_ZL15__pthread_startPv+30)
    #10 pc 00014deb  /system/lib/libc.so (__start_thread+6)

	……后续省略……
  1. 最直接的方法是使用add2line翻译成源码:
arm-linux-androideabi-addr2line --exe=libSipStack.so -i 0x00083774
可得到以下结果:
/opt/code/xxx/SipStack/Src/ezsip/jresponse.cpp:227 (discriminator 1)
  1. 也可以使用ndk-stack一键翻译整个tombstone吗,可以打出多个线程和堆栈。本质就是自动调用addr2line
cd /opt/android-ndk-r14b/prebuilt/linux-x86_64/bin
./ndk-stack -sym . -dump tombstone_08

可得到以下结果:
********** Crash dump: **********
Build fingerprint: 'HiSTBAndroidV6/Hi3798CV200/Hi3798CV200:5.1.1/LMY48W/1056:eng/test-keys'
pid: 22573, tid: 22585, name: StdsipGateway  >>> ./StdsipGateway <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xe4
Stack frame #00 pc 00083774  /system/lib/libSipStack.so (complete_answer_that_establish_a_dialog+727): Routine complete_answer_that_establish_a_dialog at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/jresponse.cpp:227 (discriminator 1)
Stack frame #01 pc 0006a1bf  /system/lib/libSipStack.so: Routine eXosip_process_cancel at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/udp.cpp:664
Stack frame #02 pc 0006b619  /system/lib/libSipStack.so: Routine eXosip_process_newrequest at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/udp.cpp:1265
Stack frame #03 pc 0006ce7f  /system/lib/libSipStack.so (_eXosip_handle_incoming_message+2742): Routine _eXosip_handle_incoming_message at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/udp.cpp:2046 
(discriminator 1)
Stack frame #04 pc 00062a29  /system/lib/libSipStack.so: Routine handle_messages at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/eXtl_tcp.cpp:507
Stack frame #05 pc 00062dbb  /system/lib/libSipStack.so: Routine _tcp_tl_recv at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/eXtl_tcp.cpp:614
Stack frame #06 pc 00063c49  /system/lib/libSipStack.so: Routine tcp_tl_poll_read_message at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/eXtl_tcp.cpp:909
Stack frame #07 pc 0006d36b  /system/lib/libSipStack.so (eXosip_read_message+1022): Routine eXosip_read_message at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/udp.cpp:2259 (discriminator 6)
Stack frame #08 pc 00094365  /system/lib/libSipStack.so: Routine eXosip_set_externalIP_and_proxyIpOrDomain at /opt/code/server_saida_android_clean/SipStack/Src/ezsip/eXconf.cpp:1154
Stack frame #09 pc 00016ea3  /system/lib/libc.so (_ZL15__pthread_startPv+30): Routine __pthread_start(void*) at pthread_create.cpp:?
Stack frame #10 pc 00014deb  /system/lib/libc.so (__start_thread+6): Routine __start_thread at libgcc2.c:?

-->这个是另外一个栈

Stack frame #00 pc 000133bc  /system/lib/libc.so (syscall+28): Routine syscall at ??:?
Stack frame #01 pc 00016d75  /system/lib/libc.so (_Z33__pthread_cond_timedwait_relativeP14pthread_cond_tP15pthread_mutex_tPK8timespec+56): Routine __pthread_cond_timedwait_relative(pthread_cond_t*, pthread_mutex_t*, timespec const*) at libgcc2.c:?
Stack frame #02 pc 0016ac67  /system/lib/libpt_r.so (_ZN10PSyncPoint4WaitEv+50): Routine PSemaphore::Wait() at /opt/code/server_saida_android_clean/ptlib-2.12.8/src/ptlib/unix/tlibthrd.cxx:1192
……后续省略……
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值