2017-01-01 5 views
0

デバッガ用のスタックウォーカーを実装しているときに、関数呼び出しの引数を抽出して表示するようになっています。シンプルにするために、純粋な32ビット(デバッガとデバッグの両方)のcdecl規則と3つのパラメータを取る関数から始めました。しかし、スタックトレースの引数が何回かcdeclで定義されているもの(右から左、何もレジスタにはない)と比較して、なぜ乱数であるのか理解できません。ここでスタックフレームのレイアウトを理解するのに助けが必要

は私がスタックトレースしようとしている関数呼び出しの表現です:

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; 
} 

この関数は(当然)コンソールに印刷されたものです:

a=0x2, c=0x7a3fe8, c=0x2004 

これは、スタックトレースはブレークポイントで生成されます(デバッガがブレークポイントをキャッチしてスタックを歩いています)。

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 */ 

スタックフレーム投棄の責任だコード(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 */ 
    } 
} 

テストプログラムは、vs2015アップデートでは最適化されていません。

dia2dumpサンプルアプリケーションでpdbを調べることで、実際にcdeclとしてコンパイルしていることを検証しました。 スタックがこのように見える原因を理解できません。学習したものと一致しないか、documentation provided by Microsoftと一致しません。
また、私はosdev wikiページ、msdnブログ記事などを含めてgoogle全体をチェックし、32ビットx86アセンブリプログラミング(64ビットCPUより前にリリースされた)存在した)。

ご説明いただきありがとうございます。

+0

あなたは明示的にあなたが何かを理解していないこと、だけではなく説明のため、質問をしている場合それはおそらく、役立つだろう。スタックがどのように見えるかについての情報を追加することで、質問を改善することができます。 – IInspectable

+0

申し訳ありませんが、私は、私がcdecl呼び出し規約で作業していたことを私に暗示していると思っていました。私は質問を更新します。 – Warepire

+2

um、あなたは地元の人ではなく、パラメータを見ています。これらはおそらくprintfの残りのパラメータです。 –

答えて

0

Raymondが指摘しているように、関数呼び出しの引数がスタックフレームの基底に比べてメモリ内でどこで終わったのか何とか誤解していました。これは、固定されたコードスニペットです:

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 */ 
    } 

}

+0

代わりに 'sizeof(DWORD_PTR)'のために 'ReadProcessMemory'をN回呼び出す - ' N * sizeof(DWORD_PTR) 'に' ReadProcessMemory'を一度だけ呼び出す必要があります – RbMm

関連する問題