windows 如何让 StackWalk64() 在 x64 上成功运行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/136752/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you make StackWalk64() work successfully on x64?
提问by Quasidart
I have a C++ tool that walks the call stack at one point. In the code, it first gets a copy of the live CPU registers (via RtlCaptureContext()), then uses a few "#ifdef ...
" blocks to save the CPU-specific register names into stackframe.AddrPC.Offset
, ...AddrStack
..., and ...AddrFrame
...; also, for each of the 3 Addr
... members above, it sets stackframe.Addr
....Mode = AddrModeFlat
. (This was borrowed from some example code I came across a while back.)
我有一个 C++ 工具,可以一次遍历调用堆栈。在代码中,它首先获取实时 CPU 寄存器的副本(通过 RtlCaptureContext()),然后使用几个 " #ifdef ...
" 块将特定于 CPU 的寄存器名称保存到stackframe.AddrPC.Offset
... AddrStack
... 和 ... 中AddrFrame
。 ..; 此外,对于上述 3 个Addr
... 成员中的每一个,它设置stackframe.Addr
... .Mode = AddrModeFlat
。(这是从我不久前遇到的一些示例代码中借来的。)
With an x86 binary, this works great. With an x64 binary, though, StackWalk64() passes back bogus addresses. (The first time the API is called, the only blatantly bogus address value appears in AddrReturn
( == 0xFFFFFFFF'FFFFFFFE
-- aka StackWalk64()'s 3rd arg, the pseudo-handle returned by GetCurrentThread()). If the API is called a second time, however, all Addr
... variables receive bogus addresses.)This happens regardless of how AddrFrame
is set:
使用 x86 二进制文件,这很好用。但是,对于 x64 二进制文件,StackWalk64() 会传回虚假地址。 (第一次调用 API 时,唯一公然伪造的地址值出现在AddrReturn
( == 0xFFFFFFFF'FFFFFFFE
-- 又名 StackWalk64() 的第三个参数中,即 GetCurrentThread() 返回的伪句柄)。如果第二次调用 API ,但是,所有Addr
... 变量都会收到虚假地址。)无论如何设置,都会发生这种情况AddrFrame
:
- using either of the recommended x64 "base/frame pointer" CPU registers:
rbp
(=0xf
), orrdi
(=0x0
) - using
rsp
(didn't expect it to work, but tried it anyway) - setting
AddrPC
andAddrStack
normally, but leavingAddrFrame
zeroed out (seen in other example code) - zeroing out all
Addr
... values, to let StackWalk64() fill them in from the passed-in CPU-register context (seen in other example code)
- 使用推荐的 x64“基本/帧指针”CPU 寄存器之一:
rbp
(=0xf
) 或rdi
(=0x0
) - 使用
rsp
(没想到它会起作用,但还是尝试了) - 设置
AddrPC
和AddrStack
正常,但保持AddrFrame
归零(见其他示例代码) - 清零所有
Addr
... 值,让 StackWalk64() 从传入的 CPU 寄存器上下文中填充它们(见其他示例代码)
FWIW, the physical stack buffer's contents are also different on x64 vs. x86 (after accounting for different pointer widths & stack buffer locations, of course). Regardless of the reason, StackWalk64() should still be able to walk the call stack correctly -- heck, the debugger is still able to walk the call stack, and it appears to use StackWalk64() itself behind the scenes. The oddity there is that the (correct) call stack reported by the debugger contains base-address & return-address pointer values whose constituent bytes don't actually exist in the stack buffer (below or above the current stack pointer).
FWIW,物理堆栈缓冲区的内容在 x64 和 x86 上也不同(当然,在考虑了不同的指针宽度和堆栈缓冲区位置之后)。不管什么原因,StackWalk64() 应该仍然能够正确地遍历调用堆栈——哎呀,调试器仍然能够遍历调用堆栈,而且它似乎在幕后使用 StackWalk64() 本身。奇怪的是调试器报告的(正确)调用堆栈包含基地址和返回地址指针值,其组成字节实际上不存在于堆栈缓冲区中(低于或高于当前堆栈指针)。
(FWIW #2: Given the stack-buffer strangeness above, I did try disabling ASLR (/dynamicbase:no
) to see if it made a difference, but the binary still exhibited the same behavior.)
(FWIW #2:鉴于上述堆栈缓冲区的奇怪之处,我确实尝试禁用 ASLR ( /dynamicbase:no
) 以查看它是否有所作为,但二进制文件仍然表现出相同的行为。)
So. Any ideas why this would work fine on x86, but have problems on x64? Any suggestions on how to fix it?
所以。任何想法为什么这会在 x86 上正常工作,但在 x64 上有问题?有关如何修复它的任何建议?
回答by Adam Mitz
Given that fs.sf is a STACKFRAME64 structure, you need to initialize it like this before passing it to StackWalk64: (c is a CONTEXT structure)
鉴于 fs.sf 是一个 STACKFRAME64 结构,在传递给 StackWalk64 之前需要像这样初始化它:(c 是一个 CONTEXT 结构)
DWORD machine = IMAGE_FILE_MACHINE_AMD64;
RtlCaptureContext (&c);
fs.sf.AddrPC.Offset = c.Rip;
fs.sf.AddrFrame.Offset = c.Rsp;
fs.sf.AddrStack.Offset = c.Rsp;
fs.sf.AddrPC.Mode = AddrModeFlat;
fs.sf.AddrFrame.Mode = AddrModeFlat;
fs.sf.AddrStack.Mode = AddrModeFlat;
This code is taken from ACE (Adaptive Communications Environment), adapted from the StackWalker project on CodeProject.
此代码取自 ACE(自适应通信环境),改编自 CodeProject 上的 StackWalker 项目。
回答by Quasidart
FWIW, I've switched to using CaptureStackBackTrace()
, and now it works just fine.
FWIW,我已经改用了CaptureStackBackTrace()
,现在它工作得很好。