Ubuntu下正确姿势使用GDB调试Android Native进程
前言
对于Android Native进程大家是既爱又恨啊,爱的是它能为我们的Android世界带来别样的精彩,狠的是当它耍脾气奔溃或者是创造它的人不留神造歪了那后果可是天崩地裂而且还不能让人好好调试。除了常规手段debuggerd -b PID分析Native进程的traces信息或者待Crash后分析trace.txt日志外,也没有比较好的完善的调试手段。不,难道我们伟大的android世界就这么羸弱吗!当然不是它还有一个必杀技,可以通过gdb+gdbserver的经典组合来进行Android Native进程的调试。那么本篇的重任来了,我将和大伙一起探讨正确姿势使用GDB调试Android Native进程(主要介绍怎么开启GDB调试)。开干!
注意:本篇是以Android 8版本为基础进行讲解的。
一.前期知识储备
在正式开干前,让我们先磨磨刀,磨刀不误砍柴工吗!先把基础打牢固了,还怕啥牛鬼蛇神的给解决不了。
1.1 啥是GDB
这里不是经济频道说的不是GDP是GDB,GDB是GNU Project Debugger 的缩写,它也是很多开源软件的调试利器。它主要提供了如下几个功能点:
- 启动程序,可以按照自定义的要求随心所欲的运行程序
- 可让被调试的程序在所指定的断点处停住(断点可以是条件表达式)
- 当程序被停住时,可以检查此时程序中所发生的事
- 动态的改变程序的执行环境
最后盗用一张网上的GDB调试原理图,如下:
二.调试环境准备
前期知识已经储备OK了,那么得准备好调试幻境了。不然环境不好,就不能正确的使用姿势调试了。让我们一一准备。
2.1 准备好Ubuntu开发环境
- 当前首先你得安装了Ubuntu的操作系统,这个至于是用虚拟机安装或是其它方式,就不是本篇的讨论重点了。我这边的Ubuntu版本的信息如下,可以看到我当期的ubuntu版本信息是14.04,这里不做过多的讲解。
tangkw@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
tangkw@ubuntu:~/.android$
-
安装好Ubuntu操作系统后,必须取得Root权限,这个也不是本文的重点。
-
调试的终端可以在Ubuntu下环境通过adb进行真机调试,如果读者有不清楚怎么连接的,可以参考如下博客Ubuntu下正确姿势使用adb调试真机保证教会为止。
2.2 Android源码和编译环境准备
这个Android源码不管读者是偷的也好,抢的也好反正只要能搞到就行。譬如下面就是我的Android 8的源码环境,这里我是使用SSH将远程服务器挂载到我的虚拟机里里面的,至于怎么通过SSH挂载远程服务器目录可以参见篇章Ubuntu下使用SSH挂载远程服务器目录,其中hgfs就是我将远程服务器挂载的目录,如下所示:
[SPRD] tangkw@ubuntu:~/hgfs$ pwd
/home/tangkw/hgfs
[SPRD] tangkw@ubuntu:~/hgfs$ ls
A910 Code sections for review-A920Pro_20190708T1.docx ntfs.txt repo ssd vfat.txt
Code sections for review-A77_20190708T1.docx ~$de sections for review-A77_20190708T1.docx PaxSPManager.rar sourceisight Tools
[SPRD] tangkw@ubuntu:~/hgfs$
[SPRD] tangkw@ubuntu:~/hgfs$ cd ssd/
[SPRD] tangkw@ubuntu:~/hgfs/ssd$ cd A50/ap/idh.code/
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ ls
Android.bp bootstrap.bash compatibility device frameworks libcore packages prebuilts stack toolchain
art build cts docs hardware libnativehelper paxdroid 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 Sprd_Version_information.txt test vendor
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/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=
PAX_PRODUCT_BUILD=SPRD
============================================
2.3 获取终端调试权限
对于被调试的终端一定要获取如下的权限,否则就上天无门了,谁也救不了,具体需要的权限如下:
- 对于被调试的终端一定要能被root,然后被remount,这个其中的道道就不介绍了,不是本文的重点,对于从事终端开发的一般都可以获取到。
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ adb root
adbd is already running as root
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ adb remount
remount succeeded
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ adb shell
XXX:/ #
- 临时关闭SELinux,否则后续执行相关操作会报avc 错误。
A77:/ # setenforce 0
A77:/ # getenforce
Permissive
XXX:/ #
2.4 设置终端调试环境
通过adb 查看终端当前是否有gdbserver存在,一般情况下是不存在。这个也是终端厂商为了安全方便考虑故意裁剪的。
A77:/system/bin # ls | gerp gdbserver
/system/bin/sh: gerp: not found
127|A77:/system/bin #
不存在难道就搞不下去了,当然不是Android的妈咪谷歌已经为我们提前准备好了,我们可以到Android源码目录prebuilts或者Android编译生成out目录(这个目录谷歌内置了如果有用的工具)下面去搜索,这里需要根据终端是32位的还是64的具体情况确定使用那个。
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ cd prebuilts/
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/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] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code/prebuilts$ cd ..
c[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ cd out/
[SPRD] tangkw@ubuntu:~/hgfs/ssd/A50/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] tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code/out$
这里我们选用out目录下面的gbdserver,将其push到终端并修改权限,具体操作步骤如下所示:
tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ adb push ./out/target/product/sl8541e_1h10_go/system/bin/gdbserver /system/bin
782 KB/s (596484 bytes in 0.744s)
tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ adb shell "chmod 777 /system/bin/gdbserver"
tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$ adb shell "ls -l /system/bin/gdbserver"
-rwxrwxrwx 1 root shell 596484 2020-01-17 01:45 /system/bin/gdbserver
tangkw@ubuntu:~/hgfs/ssd/A50/ap/idh.code$
三.正式开始调试
前面做了这么多的准备前戏,都是为了检验最后的姿势是否正式,不,应该是是否能正确调试。好了下面拉开我们的正式调试序幕。这里我们要调试的进程名字是auth_bpadownload。
3.1 启动gdbserver并attach到想要调试的进程
在正式开始调试前,让我们看看gdbserver支持那些命令,在终端下输入gdbserver,可以看出支持如下的命令:
A77:/ # 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|A77:/ #
这里我们使用的是–attach参数来进行,下面来详细介绍一下具体调试应该执行的流程:
- 查找需要被调试Native进程的PID号,可以使用如下命令查找。
A77:/ # ps -A | grep auth_bpadownload
root 4027 1 19960 5096 poll_schedule_timeout a9a0c7d0 S auth_bpadownload
A77:/ #
- 通过gdbserver attach到我们需要调试的Native进程
A77:/ # gdbserver :5050 --attach 4027
Attached; pid = 4027
Listening on port 5050
参数说明: 在tcp端口5050上监听auth_bpadownload程序,其中客户端gdb只要也连上5050端口即可。
结果: 如上图就是attach成功了。
3.2 设置tcp转发端口
设置调试终端端口转发这里使用的是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服务端通信。
3.3 在终端上启动gdb调试
失败的尝试,重要的说三次,三次
网上很多博客说可以使用gdbclient进行调试,不需要进行任何配置,我这边也尝试了一下,但是失败了,应该是Android O上面已经给废弃了,这里一笔带过给说说,具体操作步骤如下:
- 构建Android编译环境,当然是source然后lunch
- 然后执行下述命令,其中auth_bpadownload是我们要调试的Native进程。
Android的妈咪谷歌已经为我们提供了gdb调试的客户端,我们可以在目录Android源码目录prebuilts下面进行搜索,如下:
这里我们使用红色框标记的gdb客户端进行调试,下面我们分步骤详细讲解。
3.3.1 启动gdb并连接到gbdserver
可以通过如下命令执行:
arm-eabi-gdb
target remote:5050 //这里填入你实际的端口
连接成功后,服务端提示如下:
3.3.2 设置监控的二进制文件
这里要监控的二进制文件是auth_bpadownload,执行下述命令:
(gdb) file /home/tangkw/hgfs/ssd/A50/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/bin/auth_bpadownload
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/tangkw/hgfs/ssd/A50/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/bin/auth_bpadownload"? (y or n) y
Reading symbols from /home/tangkw/hgfs/ssd/A50/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/bin/auth_bpadownload...done.
(gdb)
3.3.3 Set sysroot路径
执行如下命令:
(gdb) set sysroot /home/tangkw/hgfs/ssd/A50/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols
(gdb)
3.3.4 设置Android源码目录
执行如下命令:
(gdb) set dir /home/tangkw/hgfs/ssd/A50/ap/idh.code
(gdb)
3.3.4 设置gdb带符号表的so路径
执行如下命令:
(gdb) set solib-absolute-prefix /home/tangkw/hgfs/ssd/A50/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols
(gdb)
3.3.4 设置gdb的so搜索路径
执行如下命令:
(gdb) set solib-search-path /home/tangkw/hgfs/ssd/A50/ap/idh.code/out/target/product/sl8541e_1h10_go/symbols/system/lib/
(gdb)
3.4 开启终极调试
gdb调试支持的命令比较多,这里挑选几个:
3.4.1 gdb list查看源码
执行下述命令:
(gdb) list
206 system("iptables -P OUTPUT ACCEPT");
207 system("iptables -P INPUT ACCEPT");
(gdb) list main.c:110//查看指定行数代码
3.4.2 gdb break设置断点
执行如下命令设置断点:
(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.
3.4.3 gdb breakpoints显示断点信息
执行如下命令:
(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江湖木有任何问题了。此时的你可以一剑走天下了,为师的必杀器已经倾囊相授了。各位江湖见。但是还有几个点需要注意:
- 编译时请禁止编译优化选项,即添加-O0编译选项,否则会导致代码在调试时出现跳来跳去的可能,并且在打印变量值是出现
- 在编译程序时需要添加-g编译选项以为程序添加gdb调试功能,否则你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址
LOCAL_CFLAGS += -g
写在最后
各位读者看官朋友们,Ubuntu下正确姿势使用GDB调试Android Native进程已经全部完毕,希望能吸引你,激活发你的学习欲望和斗志,我也会在后续篇章中加上gdb命令的更多详细信息。在最后麻烦读者朋友们如果本篇对你有帮助,关注和点赞一下,当然如果有错误和不足的地方也可以拍砖。