知っておくべき最も重要な情報は、現在のプログラムの状態がどのようにしてそこに到達したかということである場合があります。 backtrace コマンドがあり、プログラムの現在の関数呼び出しチェーンを取得できます。この記事では、x86_64 でスタックの巻き戻しを実装してこのようなトレースバックを生成する方法を説明します。 |
環境の準備
問題は、それを x86_64 にどのように実装するかです。最も堅牢なアプローチは、ELF ファイルの .eh_frame 部分を解析し、そこからスタックを巻き戻す方法を見つけることですが、それは面倒です。 libunwind などを使用して実行することもできますが、それは退屈です。代わりに、コンパイラが何らかの方法でスタックを設定していると想定し、それを手動で走査します。これを行うには、まずスタックのレイアウトを理解する必要があります。
リーリー
ご覧のとおり、最後のスタック フレームのフレーム ポインタは現在のスタック フレームの先頭に格納され、ポインタのリンク リストが作成されます。スタックはこのリンクされたリストに基づいてアンワインドされます。 DWARF メッセージ内の戻りアドレスを検索することで、リスト内の次のフレームの関数を見つけることができます。一部のコンパイラは、EBP のフレーム ベース アドレスの追跡を無視します。これは、これが ESP からのオフセットとして表現され、追加のレジスタが解放されるためです。最適化が有効になっている場合でも、-fno-omit-frame-pointer を GCC または Clang に渡すと、依存する規則に強制的に従うことになります。print_backtrace 関数ですべての作業を行います:
リーリー
最初に決定することは、フレーム情報を出力するためにどの形式を使用するかです。このメソッドを展開するにはラムダを使用しました:リーリー
印刷される最初のフレームは、現在実行中のフレームです。 DWARF で現在のプログラム カウンターを検索することで、このフレームに関する情報を取得できます:リーリー
次に、フレーム ポインターと現在の関数の戻りアドレスを取得する必要があります。フレーム ポインタは rbp レジスタに格納され、リターン アドレスはフレーム ポインタからスタックされた 8 バイトです。リーリー
これで、スタックを拡張するために必要な情報がすべて揃いました。デバッガーが main に到達するまで巻き戻しを続けますが、フレーム ポインターが 0x0 のときに停止することも選択できます。これは、main 関数を呼び出す前に呼び出す関数です。各フレームからフレーム ポインターと戻りアドレスを取得し、情報を出力します。リーリー ###それでおしまい!関数全体は次のとおりです:
リーリーコマンドを追加
もちろん、このコマンドをユーザーに公開する必要があります。
リーリー ###テスト###この機能をテストする 1 つの方法は、相互に呼び出しを行う多数の小さな関数を含むテスト プログラムを作成することです。いくつかのブレークポイントを設定し、コードの近くにジャンプして、トレースバックが正確であることを確認します。
私たちは、他のプログラムを生成して接続することしかできないプログラムから長い道のりを歩んできました。このシリーズの最後から 2 番目の記事では、変数の読み取りと書き込みをサポートすることでデバッガーの実装を完了します。それまでは、この投稿のコードをここで見つけることができます。以上がLinux デバッガ スタックの拡張!の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。