最近安卓项目中碰到一个踩内存导致死机的问题,通过分析Log找到了被踩的内存地址,但无法找到是谁踩掉的。一般踩内存的问题,可以通过下硬件数据断点来找到肇事者。但在这个项目中,踩内存是在安卓开机过程中发生的,来不及上JTAG,另外被踩的内存是动态分配出来的,每次开机都不同(但总是踩在这个被分配的地址的固定字节上),无法预先指定断点地址(如果是全局变量被踩的话,其地址一般是不变的,可以直接在被踩的地址上下数据断点),所以这种情况下只能通过程序来给自己下断点(self-host),程序分配内存后,马上给被踩的内存下个数据断点(已知踩内存肯定发生在这个地址上),当其它程序意外写入该内存时,就会发生异常中断,从而找到踩内存的肇事者。
不幸的是,百度后,几乎没有文章讲这个事情怎么做。翻了GDB的代码,也没看到ARM中如何下硬件断点(所用的GDB版本太旧了)。后来多方搜索,找到一些信息,记录在下:
1. 在ARM的架构文档中(ARM官网可下载),Cortext A8, A9, A15才支持硬件断点(通过协处理器CP14操作调试寄存器DBGWCR和DBGWVR来下数据断点(watchpoint),Processor和JTAG Debuger均可以操作它们)(现在跑安卓的机器一般都是A9以上的架构,所以基本都支持硬件断点)。
2. Linux内核要在2.6.37以后的版本才支持对ARM添加硬件断点。
3. GDB要在7.3以后的版本才支持对ARM添加硬件断点(最新版本是7.8.1,2014年10月)。
如果是自己的ARM系统,可以写汇编,通过MCR协处理器指令操作CP14,从而读写DBGWCR和DBGWVR来下硬件断点。
如果是Linux系统(2.6.37版本以上),就简单了,可以通过ptrace函数来操作DBGWCR和DBGWVR,下硬件断点。
以下是从GDB 7.8.1中截取的下硬件断点的部分代码:
for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++)
if (arm_lwp_info->bpts_changed[i])
{
errno = 0;
if (arm_hwbp_control_is_enabled (bpts[i].control))
if (<span style="color:#ff0000;"><strong>ptrace (PTRACE_SETHBPREGS, pid,
(PTRACE_TYPE_ARG3) ((i << 1) + 1), &bpts[i].address)</strong></span> < 0)
perror_with_name (_("Unexpected error setting breakpoint"));
if (bpts[i].control != 0)
if (<span style="color:#ff0000;"><strong>ptrace (PTRACE_SETHBPREGS, pid,
(PTRACE_TYPE_ARG3) ((i << 1) + 2), &bpts[i].control)</strong></span> < 0)
perror_with_name (_("Unexpected error setting breakpoint"));
arm_lwp_info->bpts_changed[i] = 0;
}
for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++)
if (arm_lwp_info->wpts_changed[i])
{
errno = 0;
if (arm_hwbp_control_is_enabled (wpts[i].control))
if (<span style="color:#ff0000;"><strong>ptrace (PTRACE_SETHBPREGS, pid,
(PTRACE_TYPE_ARG3) -((i << 1) + 1), &wpts[i].address)</strong></span> < 0)
perror_with_name (_("Unexpected error setting watchpoint"));
if (wpts[i].control != 0)
if (<span style="color:#ff0000;"><strong>ptrace (PTRACE_SETHBPREGS, pid,
(PTRACE_TYPE_ARG3) -((i << 1) + 2), &wpts[i].control)</strong></span> < 0)
perror_with_name (_("Unexpected error setting watchpoint"));
arm_lwp_info->wpts_changed[i] = 0;
}