[英]Need help understanding stack frame layout
While implementing a stack walker for a debugger I am working on I reached the point to extract the arguments to a function call and display them. 在为调试器实现堆栈遍历器时,我正在努力将参数提取到函数调用并显示它们。 To make it simple I started with the cdecl convention in pure 32-bit (both debugger and debuggee), and a function that takes 3 parameters. 为简单起见,我从纯32位(调试器和debuggee)开始使用cdecl约定,该函数带有3个参数。 However, I cannot understand why the arguments in the stack trace are out of order compared to what cdecl defines (right-to-left, nothing in registers), despite trying to figure it out for a few days now. 但是,尽管尝试了几天,但我无法理解为什么堆栈跟踪中的参数与cdecl定义的参数相比是乱序的(从右到左,寄存器中没有内容)。
Here is a representation of the function call I am trying to stack trace: 这是我尝试堆栈跟踪的函数调用的表示形式:
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;
}
This is what the function (unsurprisingly) printed to the console: 这是功能(毫不奇怪)打印到控制台的内容:
a=0x2, c=0x7a3fe8, c=0x2004
This is the stack trace generated at the breakpoint (the debugger catches the breakpoint and there I try to walk the stack): 这是在断点处生成的堆栈跟踪(调试器捕获了该断点,然后我尝试遍历堆栈):
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 */
The code that's responsible for dumping the stack frames (implemented using the DIA SDK, though, I don't think that is relevant to my problem) looks like this: 负责转储堆栈帧的代码(虽然使用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 */
}
}
I am compiling the test program without any optimization in vs2015 update 3. 我正在编译测试程序,而没有在vs2015 Update 3中进行任何优化。
I have validated that I am indeed compiling it as cdecl by looking in the pdb with the dia2dump
sample application. 我已经通过使用dia2dump
示例应用程序在pdb中查找来验证是否确实将其编译为cdecl。 I do not understand what is causing the stack to look like this, it doesn't match anything I learned, nor does it match the documentation provided by Microsoft . 我不明白是什么导致堆栈看起来像这样,它与我学到的内容都不匹配,也与Microsoft提供的文档不匹配。
I also checked google a whole lot (including osdev wiki pages, msdn blog posts, and so on), and checked my (by now probably outdated) books on 32-bit x86 assembly programming (that were released before 64-bit CPUs existed). 我还对Google进行了大量检查(包括osdev Wiki页面,msdn博客文章等),并检查了我的(到现在可能已经过时的)有关32位x86汇编编程的书(这些书在64位CPU存在之前就已发布) 。
Thank you very much in advance for any explanations or links! 非常感谢您的任何解释或链接!
I had somehow misunderstood where the arguments to a function call end up in memory compared to the base of the stack frame, as pointed out by Raymond. 正如Raymond所指出的那样,与栈框架的基础相比,我以某种方式误解了函数调用的参数最终出现在内存中的情况。 This is the fixed code snippet: 这是固定的代码段:
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 */
}
} }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.