gdbがbacktraceコマンドを実行するときに行うように、コールスタックを構築する方法を理解したいと思います。これはインタビューで尋ねられ、私はコールスタックとスタックフレームの知識に基づいて答えました。これは、スタックポインター、呼び出し元のリターンアドレス/命令を使用し、実行可能ファイル/アセンブリ命令にマッピングすることで行われると思います。私はそれが実際にどのように行われるか、またはこのスタックウォークの良い説明を探していました。グーグルで見つけたすべての情報は、プログラムでこのウォークへのMicrosoft APIに関連しており、コールスタックの構築にアプローチする方法の一般的な説明を探しています。
2 に答える
グーグル検索は私をここに導きました。
フレームポインタを使用した非常に単純なix86
呼び出し規約について考えてみます。ルーチンを呼び出すたびに、次の命令のアドレスがスタックにプッシュされます。エントリ直後に呼び出されたルーチンは、push %ebp; mov %esp,%ebp
命令を実行します。そうすると、上のページのレイアウトが正確に表示されます。
から呼び出された、から呼び出された、から呼び出された、から呼び出されたルーチンfoo
で停止したとします。bar
baz
main
で指す2つの単語を調べます%ebp
。最初のワードはの前の値です%ebp
(これを呼びましょうprev_ebp
。2番目のワードはリターンアドレスです-内のどこかにある命令ポインタbar
。
ここで、が指す2つの単語を調べますprev_ebp
。最初のものはになりprev_prev_ebp
、2番目はリターンアドレスになります-内のどこかにある命令ポインタbaz
。
mainに到達し、GDBが使用する手順を大まかに実行するまで繰り返します。
フレームポインタを使用しないフレームなど、多くの実用的な複雑さがありますが、それを理解することは期待されていません:-)
gdbでどのように実行されるかはわかりませんが、ここにアイデアがあります。すべてのjmp/call
命令とそのターゲットアドレスをスタックに保持できれば、いつでも完全な呼び出しトレースが得られます。