这种方法适用于想调试android中app所用到的.so库的情况,尤其是载入库后一会就出问题的情况。如果直接起gdb载入动态链接库的符号表,这时符号地址是不对的。本文中以某app为例,因此具体函数名依实例肯定有所不同。
第一步:Dev Tools -> Development Settings ,将要调的程序设为wait for debugger。这时程序一起来就会停住,等待jdb信号。详见(http://www.voidcn.com/article/p-mfrxmgry-brx.html)。
第二步:启动jdb,用ps找出pid(假设为1476)。然后打开~/.jdbrc(没有的话创建一个),断点设在载入动态库的时候:
stop in java.lang.System.loadLibrary
保存退出后执行:
$adb forward tcp:29882 jdwp:1476
$jdb -attach localhost:29882
jdb启动后会自动读./jdbrc设断点,一般情况下会出现类似下面的显示:
jzj@jzj-desktop:~$ jdb -attach localhost:29882
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
> *** Reading commands from /home/zjin/.jdbrc
It will be set after the class is loaded.
> Set breakpoint java.lang.System.loadLibrary
> >
第三步,等到程序因为load library而触发断点。这时可以确认下是不是我们要找的那个library。举例来说,先在jdb中查看局部变量:
<1> main[1] locals
Method arguments:
Local variables:
libName = "bigeLib"
再将程序的apk用dex2jar和jd-gui反汇编出来,找出载入.so对应的一段代码为:
package com.bz.bige;
import com.bz.bige.sound.bzSoundManager;
public class bigeJNI
{
static
{
System.loadLibrary("bigeLib");
}
}
这说明我们整对了,然后在jdb中将当前函数运行完,也就是让动态库载入完:
<1> main[1] step up
第四步,运行gdb 。如被调试程序的pid为1390的话:
在target上运行(没有gdbserver的话先从push进去):
# ./gdbserver :1234 --attach 1390
在host上运行:
$ arm-eabi-gdb -x init.gdb
载入.so的符号表,这时各符号的地址已经是relocate过的了,可以在gdb中用。关于gdb的介绍详见(http://www.voidcn.com/article/p-bazszhfx-brx.html)。
(gdb) shared
觉得有必要的话,可以用下面的命令看下函数列表:
(gdb) info functions
现在可以用函数名设断点了:
(gdb) b bzGame::init()
Breakpoint 1 at 0x4965f9fc
(gdb) b bzGame::init()
Note: breakpoint 1 also set at pc 0x4965f9fc.
Breakpoint 2 at 0x4965f9fc
(gdb) delete 2
(gdb) b bzGame::setState(std::string const&)
Breakpoint 3 at 0x496600ba
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x4965f9fc <:init>
3 breakpoint keep y 0x496600ba <:setstate const>
然后就可以执行continue继续了。程序到时就会停在断点处。