在此答案中:https://stackoverflow.com/a/8646611/192359 ,解释了在调试 x86 代码时,即使使用 FPO(帧指针省略),符号也允许调试器显示调用堆栈。
给出的解释是:
On the x86 PDBs contain FPO information, which allows the debugger to reliably unwind a call stack.
我的问题是这些信息是什么?据我了解,仅知道函数是否具有 FPO 并不能帮助您找到堆栈指针的原始值,因为这取决于运行时信息。
我在这里缺少什么?
最佳答案
从根本上来说,总是可以利用足够的信息来遍历堆栈1,除非堆栈或执行上下文已被不可恢复地损坏。
例如,即使不使用rbp
作为帧指针,返回地址仍然位于堆栈中的某个位置,您只需要知道在哪里即可。对于不在函数体中(间接或直接)修改 rsp 的函数,它将位于距 rsp 的简单固定偏移处。对于在函数体中修改 rsp 的函数(即具有可变堆栈大小的函数),与 rsp 的偏移量可能取决于函数中的确切位置。
PDB 文件仅包含此“边带”信息,该信息允许某人确定函数中任何指令的返回地址。 Hans 链接了相关的内存结构 above - 您可以看到,由于它知道局部变量的大小等,因此它可以计算 rsp 和帧基址之间的偏移量,从而获得返回地址。它还知道有多少指令字节是“序言”的一部分,这很重要,因为如果 IP 仍在该区域,则应用不同的规则(即,堆栈尚未调整以反射(reflect)该函数中的局部变量)。
在 64 位 Windows 中,确切的函数调用 ABI 已经变得更加具体,并且所有函数通常都必须提供展开信息:不在 .pdb
中> 但直接位于二进制文件中包含的部分中。因此,即使没有 .pdb
文件,您也应该能够展开结构正确的 64 位 Windows 程序。它允许任何寄存器用作帧指针,并且仍然允许帧指针省略(有一些限制)。详情start here .
1 如果这不是真的,问问自己当前正在运行的函数如何返回?现在,从技术上讲,您可以设计一个程序,该程序以无法返回的方式破坏或忘记堆栈,并且永远不会退出或使用诸如 exit()
或 abort()
终止。这是非常不寻常的,不可能在汇编之外。
关于windows - x86 调试中符号如何解决使用 FPO 遍历堆栈的问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45714943/