ある時点でコールスタックをウォークする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で問題が発生する理由はありますか?それを修正する方法について何か提案はありますか?