关于调试,可以分为两方面,有硬件调试器(st-link,jlink,jtag等),无硬件调试器(Linux内核对其上运行的应用程序的调试)
有硬件调试器
比如stm32f4,连接了st-link调试器,打断点,就是在那个地址对应的内存写入一条BKPT指令,cpu运行到这儿就停住了,调试器可以让它进入函数,下一行等执行,调试器完全拿捏住了cpu,因此可以控制它。其它嵌入式开发的单片机调试也是同样的。
这种会让cpu完全停在断点处,对于裸机程序(相当于操作系统单线程),没什么问题,但是对于操作系统多任务来说,你这样会造成系统中的所有任务停止运行,内核也停止,这个是不合理的。
无硬件调试器
有OS的情况:
这个纯粹是软件技巧了,比如OS内核对应用程序调试,会在那个应用程序代码打一个断点,就是替换一条int3指令,应用程序执行到这儿,陷入异常处理函数,判断是否这里之前打了个断点的原因,然后进行查看寄存器,变量等处理,内核说继续执行,就是把这里指令又替换为原来指令,还得在下一个位置(这个又可以分为硬单步和软单步了)再注入一个断点(目的是这个断点下一次还能生效),pc指针减一下,让pc回退到那条指令,就可以了。
这其中有两个问题,int3就是一条软中断指令,有些单片机没有软中断的话,那就只好自己制作一条跳转指令替换在这儿了。第二个问题,硬单步和软单步:
硬单步:
比如x86的cpu,就支持这个,就是执行一条指令,自己会产生异常,供我们进行相关处理(探秘INT3指令_积累点滴,保持自我-CSDN博客_int3汇编,https://www.orcode.com/question/906448_ke3038.html,[求助]INT3之后到底执行哪条指令?-经典问答-看雪论坛-安全社区|安全招聘|bbs.pediy.com,)。而arm等,是不支持的,只能通过软单步。KGDB 与 指令级单步调试 – linux kgdb gdb debug
软单步:
又称为伪单步,靠GDB等进行下一条待指令预测实现,这个会比较复杂了。
没有OS:
倒是也可以,就是自己让自己单步往下走,比如cortex-m单片机,配置相应的触发寄存器,就支持替换为BKPT指令,执行到这儿会进入异常处理函数,我们就可以进行相关处理啦。Step-through debugging with no debugger on Cortex-M | Interrupt
对了再说一点,为什么int3等断点注入指令是一个字节的,因为,如果是多个字节,别的应用程序刚好想跳转到这个地址,就会产生跳转到这个地址的一半的现象,那么这个就是一个未定义指令,会让那个应用程序跑飞的。如果注入的断点指令只是一个字节,最多就是那个应用程序在这儿停住了,而不会跑飞。
对于开发一个嵌入式实时OS来说,还是有必要支持对应用程序的调试的(1.不能让cpu完全停止,因为不能影响内核和别的应用程序运行才行啊(比如st-link等调试器就会这样),2. 而且不能依赖于外部硬件调试器,不然说明这个OS不好用),那么只能通过软件注入指令实现调试了,而且arm又不支持有硬件单步,所以还需要我们自己实现一个GDB等类似的下一条指令预测功能在本机或者上位机(最好是在这上面,因为性能强大)。
参考文章
用图文带你彻底弄懂GDB调试原理_Peter的专栏-CSDN博客
int3断点指令的原理和示例
arm的软断点和硬断点_xiaoyaofeidao的博客-CSDN博客_arm硬件断点
|