一种基于调试器的运行时非侵入调试方式
在x86系统上,有一条调试指令"int3",在连接GDB的情况下,执行int3指令,被调试的程序便可以和GDB主动连接,之后就可以通过GDB命令观察被调程序的运行环境,如下所示:
代码:
#include <stdio.h>
int main(void)
{
printf("%s line %d.\n", __func__, __LINE__);
asm("int3");
printf("%s line %d.\n", __func__, __LINE__);
return 0;
}
Makefile
all:
gcc -g -O0 main.c -o main
调试情况:
如果不通过gdb运行被调程序,而是直接运行,则会由于无法连接调试器而coredump
之所以可以这样做,是因为Linux内核会检测当前陷入异常的指令是否是调试指令int3,如果是的话,会给被调试进程发送信号,在有GDB启动的情况下,这个调试信号会被发送给父进程,也就是GDB进程进行处理,所以可以和gdb成功建立连接。
但是在直接运行待调程序的情况下,内核产生的调试信号无法发送给调试器,而前者又不知道怎么处理,最后的结果就是被内核杀死。只留下coredump一声哀嚎。
那么,不禁要问,在Linux中,内核作为一个超然的独立,客观,第三方存在,可以检测调试条件,控制调试过程,如果对于RTOS裸机程序呢?所有的内容都编译,链接在一起,没有一个独立观察者存在,这个时候,可以达到这种执行到调试指令便立刻和调试器建立成功连接的效果吗?
答案是肯定的,如下是架构无关的实现流程,通过beak指令触发异常,然后再异常中再次返回break指令,再次触发异常,再次返回。。。。。,通过这样一个指令序列,可以保证CPU循环执行break调试请求调试指令,这样,调试器再任何时刻侵入调试,都可以使CPU进入调试状态:
这个流程和上面X86上的试验有所差异,再上面的实验情况中,内核如果发现待调试的用户进程没有parent父进程处理调试信号,会直接杀死被调试的进程。而不会返回用户程序int3指令处继续执行。这是一点差异(不过应该可以通过修改内核在这个时候让子进程返回到int3指令处,周而复始执行上述过程).
我们以MIPS和ARM 两个架构为例,来分析如何实现上述流程。
MIPS处理器架构方框图的简化版,Sequencer代表流水线Pipeline.
实现此功能需要用到MIPS的"SDBBP"指令,关于这条指令,MIPS官方ISA Spec有详细说明,整个过程可以这样理解,sdbbp之于裸机debugger,就相当于int3之于GDB,ICE仿真器就相当于Linux内核。
MIPS中的实现
ARM中的实现:
arm实现使用的是bkpt指令,关于bkpt 指令,arm ISA 文档花了两页进行介绍
实现:
结束!
相关文章
- openresty lua 调试 (图文死磕)
- chrome developer tool 调试技巧
- eclipse常用设置及调试快捷键
- 基于STM32调试工具STM-STUDIO-STM32的使用
- 打印赛道 - 完成车模调试与比赛
- 基于ESP32智能车竞赛裁判系统第二版硬件调试-6-26
- 基于ESP32的竞赛裁判系统功能调试-与微机通讯
- 前端基础 - 常用调试方式
- Android 反调试技巧之Self-Debuging/proc 文件系统检测、调试断点探测
- Chrome 调试技巧: 调整网速
- Eclipse调试cas server 3.5.2.1
- 基于ADS1299的可穿戴设备调试之接口含义简析
- 利用 filter 机制 给 静态资源 url 加上时间戳,来防止js和css文件的缓存,利于开发调试
- Android:基于Eclipse编译调试系统级应用源代码
- php 安装xdebug进行调试(phpstorm)
- 很好用的php在线调试工具
- 基于云服务的短程低功耗智能用电管控装置调试第二天
- iOS开发UI调试神器----Reveal
- windows环境phpstorm调试环境搭建