【问题标题】:Need help understanding stack frame layout需要帮助了解堆栈框架布局
【发布时间】:2017-01-01 19:39:07
【问题描述】:

在为我正在处理的调试器实现堆栈遍历器时,我达到了将参数提取到函数调用并显示它们的目的。为了简单起见,我从纯 32 位(调试器和调试器)中的 cdecl 约定和一个接受 3 个参数的函数开始。但是,我无法理解为什么堆栈跟踪中的参数与 cdecl 定义的参数相比是无序的(从右到左,寄存器中没有任何内容),尽管现在已经尝试了几天。

这是我试图堆栈跟踪的函数调用的表示:

void Function(unsigned long long a, const void * b, unsigned int c) {
    printf("a=0x%llX, b=%p, c=0x%X\n", a, b, c);
    _asm { int 3 }; /* Because I don't have stepping or dynamic breakpoints implemented yet */
 }
 int main(int argc, char* argv[]) {
     Function(2, (void*)0x7A3FE8, 0x2004);
     return 0;
 }

这是函数(不出所料)打印到控制台的内容:

a=0x2, c=0x7a3fe8, c=0x2004

这是在断点处生成的堆栈跟踪(调试器捕获断点,然后我尝试遍历堆栈):

0x3EF5E0: 0x10004286 /* previous pc */
0x3EF5DC: 0x3EF60C   /* previous fp */
0x3EF5D8: 0x7A3FE8   /* arg b --> Wait... why is b _above_ c here? */
0x3EF5D4: 0x2004     /* arg c */
0x3EF5D0: 0x0        /* arg a, upper 32 bit */
0x3EF5CC: 0x2        /* arg a, lower 32 bit */

负责转储堆栈帧的代码(使用 DIA SDK 实现,但我认为这与我的问题无关)如下所示:

ULONGLONG stackframe_top = 0;
m_frame->get_base(&stackframe_top); /* IDiaStackFrame */

/* dump 30 * 4 bytes */
for (DWORD i = 0; i < 30; i++)
{
    ULONGLONG address = stackframe_top - (i * 4);
    DWORD value;
    SIZE_T read_bytes;
    if (ReadProcessMemory(m_process, reinterpret_cast<LPVOID>(address), &value, sizeof(value), &read_bytes) == TRUE)
    {
        debugprintf(L"0x%llX: 0x%X\n", address, value); /* wrapper around OutputDebugString */
    }
}

我在vs2015 update 3中编译测试程序,没有做任何优化。

通过使用dia2dump 示例应用程序查看pdb,我已经确认我确实将它编译为cdecl。 我不明白是什么导致堆栈看起来像这样,它与我学到的任何东西都不匹配,也不匹配 documentation provided by Microsoft
我还查了很多谷歌(包括 osdev wiki 页面、msdn 博客文章等),并查了我(现在可能已经过时的)关于 32 位 x86 汇编编程的书(在 64 位 CPU 存在之前发布) .

非常感谢您提供任何解释或链接!

【问题讨论】:

  • 如果你明确提出一个问题,而不是仅仅解释你不明白的东西,这可能会有所帮助。您可以通过添加有关您期望堆栈外观的信息来改进您的问题。
  • 抱歉,我认为这是暗示我正在使用 cdecl 调用约定,这就是我做出假设的结果。我会更新问题。
  • 嗯,你看的是本地人,而不是参数。这些可能是 printf 的剩余参数。
  • @RaymondChen 你说的完全正确,谢谢。我现在可以工作了。

标签: windows debugging visual-c++ stack-trace cdecl


【解决方案1】:

正如 Raymond 所指出的,与堆栈帧的基数相比,我不知何故误解了函数调用的参数最终在内存中的位置。这是固定代码sn-p:

ULONGLONG stackframe_top = 0;
m_frame->get_base(&stackframe_top); /* IDiaStackFrame */

/* dump 30 * 4 bytes */
for (DWORD i = 0; i < 30; i++)
{
    ULONGLONG address = stackframe_top + (i * 4); /* <-- Read before the stack frame */
    DWORD value;
    SIZE_T read_bytes;
    if (ReadProcessMemory(m_process, reinterpret_cast<LPVOID>(address), &value, sizeof(value), &read_bytes) == TRUE)
    {
        debugprintf(L"0x%llX: 0x%X\n", address, value); /* wrapper around OutputDebugString */
    }

}

【讨论】:

  • 而不是调用ReadProcessMemory N-time for sizeof(DWORD_PTR)- 只需调用一次ReadProcessMemoryN*sizeof(DWORD_PTR)
猜你喜欢
  • 2021-11-16
  • 1970-01-01
  • 2011-09-19
  • 2019-01-25
  • 1970-01-01
  • 1970-01-01
  • 2017-06-19
  • 2016-05-03
  • 1970-01-01
相关资源
最近更新 更多