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
- 在Android下启动gdbserver
端口可以随意调整,本文以9999为例
#手工跑
./gdbserver 0.0.0.0:9999 ./test_program args
#附加进程
./gdbserver --attach 0.0.0.0:9999 pid
- 在调试机上运行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调试
- 和前面gdb一样,需要在本地下载完整所有相关的库,特别是libc和libm。
- 以一个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)
……后续省略……
- 最直接的方法是使用add2line翻译成源码:
arm-linux-androideabi-addr2line --exe=libSipStack.so -i 0x00083774
可得到以下结果:
/opt/code/xxx/SipStack/Src/ezsip/jresponse.cpp:227 (discriminator 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
……后续省略……