3

ある時点でコールスタックをウォークするC++ツールがあります。コードでは、最初にライブCPUレジスタのコピーを(RtlCaptureContext()を介して)取得し、次にいくつかの " #ifdef ..."ブロックを使用してCPU固有のレジスタ名をstackframe.AddrPC.Offset、... AddrStack...、および...に保存しますAddrFrame。 ..; また、Addr上記の3つの...メンバーのそれぞれに対して、...を設定しstackframe.Addrます.Mode = AddrModeFlat。(これは、しばらく前に出くわしたサンプルコードから借用したものです。)

x86バイナリでは、これはうまく機能します。ただし、x64バイナリでは、StackWalk64()は偽のアドレスを返します。 (APIが最初に呼び出されたとき、明らかに偽のアドレス値のみがAddrReturn(== 0xFFFFFFFF'FFFFFFFE-別名StackWalk64()の3番目の引数、GetCurrentThread()によって返される疑似ハンドル)に表示されます。APIが2回呼び出された場合ただし、すべてのAddr...変数は偽のアドレスを受け取ります。) これAddrFrameは、設定方法に関係なく発生します。

  • 推奨されるx64「ベース/フレームポインタ」CPUレジスタのいずれかを使用:rbp(= 0xf)、またはrdi(= 0x0
  • 使用rsp 中(動作することを期待していませんでしたが、とにかく試してみました)
  • 設定AddrPCし、通常はゼロのAddrStackままにします(他のサンプルコードで見られます)AddrFrame
  • すべてのAddr...値をゼロにして、StackWalk64()が渡されたCPUレジスタコンテキストからそれらを入力できるようにします(他のサンプルコードで見られます)

FWIWでは、物理スタックバッファーの内容もx64とx86で異なります(もちろん、ポインター幅とスタックバッファーの場所の違いを考慮した後)。理由に関係なく、StackWalk64()は引き続きコールスタックを正しくウォークできるはずです。つまり、デバッガーは引き続きコールスタックをウォークでき、StackWalk64()自体をバックグラウンドで使用しているように見えます。奇妙なことに、デバッガーによって報告された(正しい)呼び出しスタックには、ベースアドレスとリターンアドレスのポインター値が含まれ、その構成バイトは実際にはスタックバッファー(現在のスタックポインターの下または上)に存在しません。

(FWIW#2:上記のスタックバッファの奇妙さを考慮して、ASLR(/dynamicbase:no)を無効にして違いが生じるかどうかを確認しましたが、バイナリは同じ動作を示しました。)

それで。これがx86で正常に機能するのに、x64で問題が発生する理由はありますか?それを修正する方法について何か提案はありますか?

4

3 に答える 3

2

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;

このコードは、CodeProject の StackWalker プロジェクトから適応された ACE (Adaptive Communications Environment) から取得されます。

于 2008-09-25T23:46:02.973 に答える
2

SymInitialize(process, nullptr, TRUE) must be called (once) before StackWalk64().

于 2020-09-20T09:55:51.933 に答える
1

FWIW、を使用するように切り替えましたがCaptureStackBackTrace()、今では問題なく動作します。

于 2008-10-01T21:29:42.817 に答える