3

我正在研究源代码级调试器。以 elf 格式提供的调试信息。如何实现“跨越”?问题出在“Point1”,无论如何我可以等待下一个源代码行(从 .debug_line 表中读取它)。

谢谢

if (a == 1)
 x = 1; //Point1
else if (a == 2)
 x = 1;

z = 1;
4

1 回答 1

5

我不确定我是否完全理解这个问题,但我可以告诉你 GDB 如何实现它的step命令。

一旦控制进入特定的编译单元,GDB 就会读取那个 CU 的调试信息;特别是,它读取 .debug_line 部分的 CU 部分,并构建一个将指令地址映射到源代码位置的表。

开始时step,GDB 查找当前 PC 的源位置。然后它通过机器指令逐步查找新PC的源位置,直到源位置发生变化。当源位置发生变化时,step就完成了。

它还在每一步之后计算帧 ID(堆栈帧的基地址和函数的起始地址),并检查它是否已更改。如果有,这意味着我们已经进入或从递归调用中返回,并且step完成了。

要了解为什么需要检查帧 ID 以及源位置,请考虑逐步调用以下函数:

int fact(n) { if (n > 0) { return n * fact(n-1); } else return 1; }

由于此函数完全在同一源代码行上定义,因此按指令单步执行直到源代码行更改将引导您完成所有递归调用而不会停止。但是,当我们进入一个新的 fact 调用时,堆栈帧的基地址会发生变化,这表明我们应该停止。这给了我们以下行为:

fact (n=10) at recurse.c:4
(gdb) step
fact (n=9) at recurse.c:4
(gdb) step
fact (n=8) at recurse.c:4

广发银行next命令将这种一般行为与用于识别函数调用并让它们返回完成的适当逻辑相结合。和以前一样,必须使用帧 ID 来确定调用何时真正返回到原始帧;还有其他并发症。

值得考虑一下如何处理函数的内联实例(DWARF 确实描述了这一点)。但这对于这个问题来说有点多。

不是为了阻止实验,但如果我开始一个调试器项目,我想看看 Apple 正在开发的调试器lldb,它是开源的。

于 2010-07-30T21:27:42.647 回答