Debugging 为什么在X86上使用单步指令?

Debugging 为什么在X86上使用单步指令?,debugging,gdb,x86,Debugging,Gdb,X86,所以有一个“int3”,它是一个中断指令,用于调试器中的断点 但是还有一个“int1”,用于单步执行。但为什么需要这样做呢?我已经读到,在EFLAGS寄存器中设置陷阱标志(TF)将启用单步执行,并将每个指令陷阱到操作系统中。那么为什么需要单独的中断类型呢 谢谢 将INT和INT 3指令与中断向量混淆,如果调用该指令,这些指令将通过中断向量进行调用。没有单步指令 INT 3(或“断点指令”)将在调试器存在时调用调试器(或者,调试器将钩住INT 3向量,以便在发生INT 3时调用调试器) 如果调试器

所以有一个“int3”,它是一个中断指令,用于调试器中的断点

但是还有一个“int1”,用于单步执行。但为什么需要这样做呢?我已经读到,在EFLAGS寄存器中设置陷阱标志(TF)将启用单步执行,并将每个指令陷阱到操作系统中。那么为什么需要单独的中断类型呢


谢谢

将INT和INT 3指令与中断向量混淆,如果调用该指令,这些指令将通过中断向量进行调用。没有单步指令

INT 3(或“断点指令”)将在调试器存在时调用调试器(或者,调试器将钩住INT 3向量,以便在发生INT 3时调用调试器)


如果调试器设置TF(跟踪标志),则每条指令都会导致#1中断发生。这将导致调用中断向量中的任何地址。希望这将是调试器的单步例程。最终,调试器将清除TF,导致单步中断停止。

int3
是一种特殊的1字节中断。如果存在调试器,调用它将进入调试器,否则应用程序通常会崩溃


当调试器设置陷阱标志时,这会导致处理器在每条指令后自动执行
int 1
中断。这允许调试器单步执行指令,而无需插入
int 3
指令。您不必显式调用此中断。

其他人已经解释了中断向量1和int 3指令之间的区别


现在,如果你想知道为什么在处理调试中断时会涉及到多个中断向量,我认为这只是因为最初的8086/8088电路设计得相对简单,并执行相对简单的软件。它只有很少的特殊中断向量,int向量1仅用于单步陷阱,根据中断向量数,将其与断点陷阱区分开来是微不足道的,也就是说,只需为向量1和向量3提供不同的处理程序就足够了。该设计被转移到随后的x86 CPU上。较新的CPU实质上“快速”地将特殊中断向量集扩展到大约20个,以处理新的异常,并扩展了调试功能,在原始单步陷阱(例如,指令提取、内存/端口I/O、任务切换等)的基础上添加了几个其他有用的中断向量1触发器。将它们大多数放在同一中断向量下是合乎逻辑的,因为它们是相关的,不会消耗更多的向量。

int 3用于设置断点,以便代码可以自由执行,直到达到特定点(断点)。这会加快调试过程,因此无需通过已知良好的代码进行陷阱捕获

int 1用于在每条指令后无条件停止。当执行条件分支指令且状态标志的条件未知时,这非常方便。否则,需要在分支地址和分支后的指令地址处设置断点


当电路板硬件和启动都是新的且未经测试时,int 1也可用于电路板启动。

以下是《英特尔软件开发人员手册》第3B卷第17章的部分引用:

英特尔64和IA-32体系结构专用于两种中断 处理调试异常的向量:向量1(debug异常, #DB)和向量3(断点异常,#BP)

对于调试异常:

调试异常处理程序通常是调试器程序或程序的一部分 更大的软件系统。处理器将为生成调试异常 几种情况之一。调试器检查DR6和DR7寄存器中的标志,以确定导致异常的条件 以及可能适用的其他条件

对于断点
异常:

断点异常(中断3)是由执行 INT3指令。调试器使用断点异常作为一个 暂停程序执行的机制以检查寄存器和 内存位置

使用Intel386和更高版本的IA-32处理器,您可以更方便地 使用断点地址寄存器设置断点(DR0到 DR3)。但是,断点异常对于 断点调试程序,因为断点异常可以调用 单独的异常处理程序。断点异常也很有用 当需要设置的断点多于调试时 寄存器,或者在源代码中放置断点时 项目正在开发中

所以我们可以看到,断点异常使您可以暂停程序执行,而调试异常会检查几个条件,并以不同的方式处理它们

仅在调试异常情况下,您将无法在您想要的位置中断。只有在某个位置中断后,才能然后为调试异常消耗的单步或其他内容配置处理器

INT3是一个单字节操作码。因此,它可以重写任何具有可控副作用的现有指令,从而中断当前程序流的执行。没有它,你怎么可能有机会在适当的时间在EFLAGS中设置单步标志而没有副作用

因此,两步中断然后调试机制是必要的

整个流程是:

首先,将调试器作为处理程序连接到bothint1(#DB