如何使StackWalk64()在x64上成功工作?

我有一个C ++工具,在一个点上传递调用栈。 在代码中,它首先获取活动CPU寄存器的副本(通过RtlCaptureContext()),然后使用一些“ #ifdef ... ”块将CPU特定的寄存器名称保存到stackframe.AddrPC.Offset中。 AddrStack …和AddrFrame …; 另外,对于上面的3个Addr …成员中的每一个,它都设置stackframe.Addr.Mode = AddrModeFlat 。 (这是从我刚才遇到的一些示例代码中借用的。)

使用x86二进制文件,这个效果很好。 但是,使用x64二进制文件,StackWalk64()将返回伪造地址。 (第一次调用API时,唯一明显的伪地址值出现在AddrReturn (== 0xFFFFFFFF'FFFFFFFE – 又名StackWalk64()的第三个参数,GetCurrentThread()返回的伪句柄)。如果API是然而,所有Addr …variables都会收到伪造的地址。) AddrFrame设置AddrFrame都会发生这种情况:

  • 使用推荐的x64“基本/帧指针”CPU寄存器: rbp (= 0xf )或rdi (= 0x0
  • 使用rsp (没想到它的工作,但试过了)
  • 正常设置AddrPCAddrStack ,但将AddrFrame为零(在其他示例代码中可见)
  • 清零所有Addr …值,让StackWalk64()从传入的CPU寄存器上下文中填充它们(见其他示例代码)

FWIW,物理堆栈缓冲区的内容在x64和x86上也不同(当然,在考虑到不同的指针宽度和堆栈缓冲区的位置之后)。 不pipe原因如何,StackWalk64()应该仍然能够正确地调用堆栈 – debugging器仍然能够遍历调用堆栈,并且在后台似乎使用了StackWalk64()。 奇怪的是,debugging器报告的(正确)调用堆栈包含基本地址和返回地址指针值,其组成字节实际上并不存在于堆栈缓冲区(当前堆栈指针的下方或上方)中。

(FWIW#2:鉴于上面的堆栈缓冲区奇怪,我曾尝试禁用ASLR( /dynamicbase:no )来查看是否/dynamicbase:no ,但二进制文件仍显示相同的行为。

所以。 任何想法,为什么这将在x86上正常工作,但在x64上有问题? 有关如何解决它的任何build议?

鉴于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; 

此代码取自ACE(自适应通信环境),由CodeProject上的StackWalker项目调整。

FWIW,我已经切换到使用CaptureStackBackTrace() ,现在它工作得很好。