断点在 C ++ 代码中是如何工作的?它们是在代码编译时插入一些汇编程序指令之间的特殊指令吗?还是有其他的地方?另外,如何通过代码实现?与断点相同的方式...?
这很大程度上取决于 CPU 和调试器。
例如,x86 CPU 上可能的解决方案之一:
在所需位置插入一个字节的 INT3 指令
等到断点异常命中
将异常地址与断点列表进行比较,以确定哪个
执行断点操作
将 INT3 替换为原始字节,并将调试的进程切换到跟踪模式(逐步执行 CPU 指令)
继续调试进程
立即捕获跟踪异常-指令被执行
将 INT3 放回去
Watchpoints 可以以类似的方式实现,但是不是 INT3,而是将被监视变量所在的内存页面放入只读,或进入无访问模式,并等待分段异常。
逐步通过汇编也可以通过使用跟踪模式来完成。逐步通过源行也可以通过根据调试数据将断点放置到下一条指令上来完成。
还有一些 CPU 有硬件断点支持,当你只是加载地址到一些寄存器。
根据 technochakra.com 上的this blog entry,您是正确的:
软件断点通过在被调试的程序中插入特殊指令来工作。英特尔平台上的这一特殊指令是“int 3”。当执行时,它调用调试器的异常处理程序。
我不确定如何实现进入或超过下一个指令。但是,文章继续添加:
出于实际原因,每当添加或删除断点时要求重新编译是不明智的。调试器更改内存中可执行文件的加载映像,并在运行时插入“int 3”指令。
但是,这仅用于“运行到当前行选项”。
单步执行是在(汇编程序)代码级别而不是在 C ++ 级别实现的。调试器知道如何将 C ++ 代码行映射到代码地址。
有不同的实现,有支持带断点寄存器调试的 CPU,当执行到达断点寄存器中的地址时,CPU 执行断点异常。
一种不同的方法是使用特殊指令在执行时修补代码,最好是一个字节指令。在 x86 系统中,通常是 int 3。
第一种方法允许 ROM 中的断点,第二种方法同时允许更多的断点。
AFAIK 允许无限数量的断点的所有调试器(对于任何编译语言)使用用特殊值替换要断点的指令的变体(如上所述),并保留放置这些值的位置的列表。
当处理器尝试执行这些特殊值之一时,将引发异常,调试器将捕获该异常并检查异常的地址是否在其断点列表中。如果是,则调用调试器,并为用户提供交互的机会。如果不是,则异常是由于从一开始就在程序中的某些内容引起的,调试器允许异常“传递”到可能存在的任何错误处理程序。
还要注意,调试自修改代码可能会失败,因为调试器会暂时修改代码本身。(当然,没有人会写自修改,现在会吗?)
由于这些原因,在终止调试会话之前,给调试器删除它设置的所有断点的机会是很重要的。
本站系公益性非盈利分享网址,本文来自用户投稿,不代表边看边学立场,如若转载,请注明出处
评论列表(13条)