实施跨步,矮人

发布于 2024-09-09 04:06:34 字数 197 浏览 4 评论 0原文

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

谢谢

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

z = 1;

Im working on a source level debugger. The debug info available in elf
format. How could be 'step over' implemented?
The problem is at 'Point1', anyway I can wait for the
next source line (reading it from the .debug_line table).

Thanks

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

z = 1;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

绮筵 2024-09-16 04:06:34

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

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

步骤开始时,GDB查找当前PC的源位置。然后它按照机器指令逐步执行,每次查找新PC的源位置,直到源位置发生变化。当源位置发生更改时,步骤即完成。

它还在每一步之后计算帧 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

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

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

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

I'm not sure I understand the question entirely, but I can tell you how GDB implements its step command.

Once control has entered a particular compilation unit, GDB reads that CU's debugging information; in particular, it reads the CU's portion of the .debug_line section and builds a table that maps instruction addresses to source code positions.

When the step begins, GDB looks up the source location for the current PC. Then it steps by machine instruction, looking up the source location of the new PC each time, until the source location changes. When the source location changes, the step is complete.

It also computes the frame ID—the base address of the stack frame, and the start address of the function—after each step, and checks if that has changed. If it has, that means that we've stepped into or returned from a recursive call, and the step is complete.

To see why it's necessary to check the frame ID as well as the source location, consider stepping through a call to the following function:

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

Since this function is defined entirely on the same source line, stepping by instruction until the source line changes would step you through all the recursive calls without stopping. However, when we enter a new call to fact, the stack frame base address will have changed, indicating that we should stop. This gives us the following behavior:

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

GDB's next command combines this general behavior with appropriate logic for recognizing function calls and letting them return to completion. As before, one must use frame IDs in deciding when calls have truly returned to the original frame; and there are other complications.

It's worth thinking a bit about how to treat inlined instances of functions (which DWARF does describe). But that's a bit much for this question.

Not to discourage experimentation, but if I were beginning a debugger project, I would want to look at Apple's work-in-progress debugger, lldb, which is open source.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文