【问题标题】:How do symbols solve walking the stack with FPO in x86 debugging?符号如何解决 x86 调试中使用 FPO 遍历堆栈的问题?
【发布时间】:2018-01-24 16:16:57
【问题描述】:

在这个答案:https://stackoverflow.com/a/8646611/192359 中,解释了在调试 x86 代码时,即使使用 FPO(帧指针省略),符号也允许调试器显示调用堆栈。

给出的解释是:

在 x86 PDB 上包含 FPO 信息,允许调试器可靠地展开调用堆栈。

我的问题是这些信息是什么?据我了解,仅知道函数是否具有 FPO 并不能帮助您找到堆栈指针的原始值,因为这取决于运行时信息。

我在这里错过了什么?

【问题讨论】:

  • IDK Windows 做了什么,但 Linux (System V ABI) 等效堆栈展开信息 (.eh_frame) 记录哪些指令地址推送哪个调用保留寄存器,因此有足够的信息来展开堆栈并恢复寄存器。大概这也是 Windows ABI 使用的那种 FPO 信息。 (您可以使用或不使用将符号映射到 var 名称的其他调试信息。)

标签: windows debugging x86 symbols callstack


【解决方案1】:

从根本上说,总是可以用足够的信息遍历堆栈1,除非堆栈或执行上下文已不可恢复地损坏。

例如,即使rbp 不被用作帧指针,返回地址仍然在堆栈的某个地方,你只需要知道在哪里。对于不修改函数体中的rsp(间接或直接)的函数,它将与rsp 有一个简单的固定偏移量。对于在函数体中修改 rsp 的函数(即具有可变堆栈大小的函数),与 rsp 的偏移量可能取决于函数中的确切位置。

PDB 文件只包含这个“边带”信息,它允许某人确定函数中任何指令的返回地址。 Hans 链接了一个相关的内存结构above - 你可以看到,因为它知道局部变量的大小等等,它可以计算rsp 和帧的基数之间的偏移量,从而得到返回地址。它还知道有多少指令字节是“序言”的一部分,这很重要,因为如果 IP 仍在该区域中,则适用不同的规则(即,尚未调整堆栈以反映此函数中的本地变量)。

在 64 位 Windows 中,确切的函数调用 ABI 已经变得更加具体,所有函数通常必须提供展开信息:不是在 .pdb 中,而是直接在部分中包含在二进制文件中。因此,即使没有.pdb 文件,您也应该能够解开结构正确的 64 位 Windows 程序。它允许将 any 寄存器用作帧指针,并且仍然允许省略帧指针(有一些限制)。详情请start here


1 如果这不是真的,问问自己当前运行的函数怎么会返回?现在,技术上您可以设计一个程序,它以无法返回的方式破坏或忘记堆栈,并且永远不会退出或使用exit()abort() 之类的方法来终止。这是非常不寻常的,不可能在装配之外。

【讨论】:

  • 感谢您的回答。所以看到我理解正确 - 不能有条件堆栈指针更改(if 子句中没有 push/pop/'sub esp')?这意味着对于函数中的每个位置,我都可以知道与原始堆栈指针的当前偏移量?
  • 是的,可以对堆栈进行条件/变量更改(主要怀疑是alloca 和 VLA),但在这种情况下,编译器通常会使用帧指针,因为这是一种快速的方法记住堆栈的底部(从函数返回所需的)。因此,您也可以轻松地遍历这些函数(.pdb 和/或 PROC_FRAME 部分包含有关在这种情况下哪个寄存器用作帧指针的信息)。 @asaf
猜你喜欢
  • 1970-01-01
  • 2011-05-12
  • 2021-07-23
  • 2021-11-30
  • 2016-08-13
  • 1970-01-01
  • 1970-01-01
  • 2020-09-02
  • 2014-06-05
相关资源
最近更新 更多