对于Android Native进程大家是既爱又恨啊,爱的是它能为我们的Android世界带来别样的精彩,狠的是当它耍脾气奔溃或者是创造它的人不留神造歪了那后果可是天崩地裂而且还不能让人好好调试。除了常规手段debuggerd -b PID分析Native进程的traces信息或者待Crash后分析trace.txt日志外,也没有比较好的完善的调试手段。不,难道我们伟大的android世界就这么羸弱吗!当然不是它还有一个必杀技,可以通过gdb+gdbserver的经典组合来进行Android Native进程的调试。那么本篇的重任来了,我将和大伙一起探讨正确姿势使用GDB调试Android Native进程(主要介绍怎么开启GDB调试)。开干!
注意:本篇是以Android 8版本为基础进行讲解的。
在正式开干前,让我们先磨磨刀,磨刀不误砍柴工吗!先把基础打牢固了,还怕啥牛鬼蛇神的给解决不了。
这里不是经济频道说的不是GDP是GDB,GDB是GNU Project Debugger 的缩写,它也是很多开源软件的调试利器。它主要提供了如下几个功能点:
前期知识已经储备OK了,那么得准备好调试幻境了。不然环境不好,就不能正确的使用姿势调试了。让我们一一准备。
xxx@ubuntu:~/.android$ cat /proc/version
Linux version 3.19.0-25-generic (buildd@lgw01-20) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015
xxx@ubuntu:~/.android$
安装好Ubuntu操作系统后,必须取得Root权限,这个也不是本文的重点。
调试的终端可以在Ubuntu下环境通过adb进行真机调试,如果读者有不清楚怎么连接的,可以参考如下博客Ubuntu下正确姿势使用adb调试真机保证教会为止。
这个Android源码不管读者是偷的也好,抢的也好反正只要能搞到就行。譬如下面就是我的Android 8的源码环境,这里我是使用SSH将远程服务器挂载到我的虚拟机里里面的,至于怎么通过SSH挂载远程服务器目录可以参见篇章Ubuntu下使用SSH挂载远程服务器目录,其中hgfs就是我将远程服务器挂载的目录,如下所示:
[SPRD] xxx@ubuntu:~/hgfs$ pwd
/home/xxx/hgfs
[SPRD] xxx@ubuntu:~/hgfs$ ls
XXXX Code sections for review-XXX_20190708T1.docx ntfs.txt repo ssd vfat.txt
Code sections for review-xxx_20190708T1.docx ~$de sections for review-XXX_20190708T1.docx XxxxxManager.rar sourceisight Tools
[SPRD] xxx@ubuntu:~/hgfs$
[SPRD] xxx@ubuntu:~/hgfs$ cd ssd/
[SPRD] xxx@ubuntu:~/hgfs/ssd$ cd xxx/ap/idh.code/
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ ls
Android.bp bootstrap.bash compatibility device frameworks libcore packages prebuilts stack toolchain
art build cts docs hardware libnativehelper xxxdroid readme.md system tools
bionic chipram dalvik dump-all-packages.info imagefiles Makefile pdk sdk tags u-boot15
bootable code development external kernel out platform_testing XXXX.txt test vendor
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$
构建Android编译环境:
这里我已经在服务器端编译过一次Android源码了,所以Android源码已经编译完成了,这里我只是构建一下Android编译环境,具体操作步骤如下:
source build/envsetup.sh
lunch //根据实际情况选择
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=8.1.0
TARGET_PRODUCT=sl8541e_1h10_gofu_osll
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_PLATFORM_VERSION=OPM1
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=
TARGET_2ND_ARCH_VARIANT=
TARGET_2ND_CPU_VARIANT=
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.19.0-25-generic-x86_64-with-Ubuntu-14.04-trusty
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=OPM2.171019.012
OUT_DIR=out
AUX_OS_VARIANT_LIST=
XXX_PRODUCT_BUILD=SPRD
============================================
对于被调试的终端一定要获取如下的权限,否则就上天无门了,谁也救不了,具体需要的权限如下:
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ adb root
adbd is already running as root
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ adb remount
remount succeeded
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ adb shell
XXX:/ #
XXX:/ # setenforce 0
XXX:/ # getenforce
Permissive
XXX:/ #
通过adb 查看终端当前是否有gdbserver存在,一般情况下是不存在。这个也是终端厂商为了安全方便考虑故意裁剪的。
XXX:/system/bin # ls | gerp gdbserver
/system/bin/sh: gerp: not found
127|XXX:/system/bin #
不存在难道就搞不下去了,当然不是Android的妈咪谷歌已经为我们提前准备好了,我们可以到Android源码目录prebuilts或者Android编译生成out目录(这个目录谷歌内置了如果有用的工具)下面去搜索,这里需要根据终端是32位的还是64的具体情况确定使用那个。
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ cd prebuilts/
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code/prebuilts$ find . -name gdbserver
./misc/android-x86/gdbserver
./misc/android-x86/gdbserver/gdbserver
./misc/android-mips/gdbserver
./misc/android-mips/gdbserver/gdbserver
./misc/android-arm/gdbserver
./misc/android-arm/gdbserver/gdbserver
./gcc/linaro-x86/aarch64/gcc-linaro-4.8/gcc-linaro-4.8-2015.06-x86_64_aarch64-linux-gnu/bin/gdbserver
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code/prebuilts$ cd ..
c[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ cd out/
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code/out$ find . -name gdbserver
./target/product/sl8541e_1h10_go/obj/EXECUTABLES/gdbserver_intermediates/gdbserver
./target/product/sl8541e_1h10_go/system/bin/gdbserver
[SPRD] xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code/out$
这里我们选用out目录下面的gbdserver,将其push到终端并修改权限,具体操作步骤如下所示:
xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ adb push ./out/target/product/sl8541e_1h10_go/system/bin/gdbserver /system/bin
782 KB/s (596484 bytes in 0.744s)
xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ adb shell "chmod 777 /system/bin/gdbserver"
xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$ adb shell "ls -l /system/bin/gdbserver"
-rwxrwxrwx 1 root shell 596484 2020-01-17 01:45 /system/bin/gdbserver
xxx@ubuntu:~/hgfs/ssd/xxx/ap/idh.code$
前面做了这么多的准备前戏,都是为了检验最后的姿势是否正式,不,应该是是否能正确调试。好了下面拉开我们的正式调试序幕。这里我们要调试的进程名字是xxx_xxx。
在正式开始调试前,让我们看看gdbserver支持那些命令,在终端下输入gdbserver,可以看出支持如下的命令:
XXX:/ # gdbserver
Usage: gdbserver [OPTIONS] COMM PROG [ARGS ...]
gdbserver [OPTIONS] --attach COMM PID
gdbserver [OPTIONS] --multi COMM
COMM may either be a tty device (for serial debugging),
HOST:PORT to listen for a TCP connection, or '-' or 'stdio' to use
stdin/stdout of gdbserver.
PROG is the executable program. ARGS are arguments passed to inferior.
PID is the process ID to attach to, when --attach is specified.
Operating modes:
--attach Attach to running process PID.
--multi Start server without a specific program, and
only quit when explicitly commanded.
--once Exit after the first connection has closed.
--help Print this message and then exit.
--version Display version information and exit.
Other options:
--wrapper WRAPPER -- Run WRAPPER to start new programs.
--disable-randomization
Run PROG with address space randomization disabled.
--no-disable-randomization
Don't disable address space randomization when
starting PROG.
Debug options:
--debug Enable general debugging output.
--debug-format=opt1[,opt2,...]
Specify extra content in debugging output.
Options:
all
none
timestamp
--remote-debug Enable remote protocol debugging output.
--disable-packet=opt1[,opt2,...]
Disable support for RSP packets or features.
Options:
vCont, Tthread, qC, qfThreadInfo and
threads (disable all threading packets).
For more information, consult the GDB manual (available as on-line
info or a printed manual).
1|XXX:/ #
这里我们使用的是–attach参数来进行,下面来详细介绍一下具体调试应该执行的流程:
XXX:/ # ps -A | grep xxx_xxx
root 4027 1 19960 5096 poll_schedule_timeout a9a0c7d0 S xxx_xxx
XXX:/ #
XXX:/ # gdbserver :5050 --attach 4027
Attached; pid = 4027
Listening on port 5050
参数说明: 在tcp端口5050上监听xxx_xxx程序,其中客户端gdb只要也连上5050端口即可。
结果: 如上图就是attach成功了。
设置调试终端端口转发这里使用的是forward,Android官方解释如下:
adb forward <local> <remote> - forward socket connections
forward specs are one of:
tcp:<port>
localabstract:<unix domain socket name>
localreserved:<unix domain socket name>
localfilesystem:<unix domain socket name>
dev:<character device name>
jdwp:<process pid> (remote only)
adb forward tcp:5050 tcp:5050
参数说明: 表示通过adb映射tcp端口5050,命令中前面的是local的端口,后面的是remote的端口。
注意: 这里的端口必须和前面gdbserver设置的端口一致,否则后果吗就是姿势不对,导致gdb无法和gdbserver服务端通信。
失败的尝试,重要的说三次,三次
网上很多博客说可以使用gdbclient进行调试,不需要进行任何配置,我这边也尝试了一下,但是失败了,应该是Android O上面已经给废弃了,这里一笔带过给说说,具体操作步骤如下:
Android的妈咪谷歌已经为我们提供了gdb调试的客户端,我们可以在目录Android源码目录prebuilts下面进行搜索,如下:
这里我们使用红色框标记的gdb客户端进行调试,下面我们分步骤详细讲解。
可以通过如下命令执行:
arm-eabi-gdb
target remote:5050 //这里填入你实际的端口
这里要监控的二进制文件是xxx_xxx,执行下述命令:
(gdb) file /home/xxx/hgfs/ssd/xxx/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/bin/xxx_xxx
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "/home/xxx/hgfs/ssd/xxx/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/bin/xxx_xxx"? (y or n) y
Reading symbols from /home/xxx/hgfs/ssd/xxx/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/bin/xxx_xxx...done.
(gdb)
执行如下命令:
(gdb) set sysroot /home/xxx/hgfs/ssd/xxx/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols
(gdb)
执行如下命令:
(gdb) set dir /home/xxx/hgfs/ssd/xxx/ap/idh.code
(gdb)
执行如下命令:
(gdb) set solib-absolute-prefix /home/xxx/hgfs/ssd/xxx/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols
(gdb)
执行如下命令:
(gdb) set solib-search-path /home/xxx/hgfs/ssd/xxx/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/lib/
(gdb)
gdb调试支持的命令比较多,这里挑选几个:
执行下述命令:
(gdb) list
206 system("iptables -P OUTPUT ACCEPT");
207 system("iptables -P INPUT ACCEPT");
(gdb) list main.c:110//查看指定行数代码
执行如下命令设置断点:
(gdb) break mian.c:100
No source file named mian.c.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mian.c:100) pending.
执行如下命令:
(gdb) info breakpoints 1
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> mian.c:100
(gdb)
关于gdb命令的使用就介绍到这了,这个不是本文的重点,至于读者想解锁更多姿势那就只能百度gdb常用命令了(说实话我也就知道常用的那么一丢丢而已)。
修行至此,恭喜读者你已经开启了Ubuntu下正确姿势使用GDB调试Android Native进程,行走于ubuntu江湖木有任何问题了。此时的你可以一剑走天下了,为师的必杀器已经倾囊相授了。各位江湖见。但是还有几个点需要注意:
LOCAL_CFLAGS += -g
各位读者看官朋友们,Ubuntu下正确姿势使用GDB调试Android Native进程已经全部完毕,希望能吸引你,激活发你的学习欲望和斗志,我也会在后续篇章中加上gdb命令的更多详细信息。在最后麻烦读者朋友们如果本篇对你有帮助,关注和点赞一下,当然如果有错误和不足的地方也可以拍砖。