Ubuntu Linux で実行されている x86_64 マシン用に GCC (4.4.3) が生成している実行可能コードを理解しようとしています。特に、コードがスタック フレームを追跡する方法がわかりません。昔の 32 ビット コードでは、ほぼすべての関数でこの「プロローグ」を目にすることに慣れていました。
push %ebp
movl %esp, %ebp
その後、関数の最後に「エピローグ」が表示されます。
sub $xx, %esp # Where xx is a number based on GCC's accounting.
pop %ebp
ret
または単に
leave
ret
同じことを達成します:
- スタック ポインタを現在のフレームの先頭、リターン アドレスのすぐ下に設定します。
- 古いフレーム ポインタの値を復元します。
64 ビット コードでは、objdump の逆アセンブルからわかるように、多くの関数がこの規則に従っていません。%rbp をプッシュせず、%rsp を %rbp に保存しません。GDB のようなデバッガはバックトレースをどのように構築しますか?
ここでの私の本当の目標は、実行がプログラムのさらに先の任意の関数の開始点に到達したときに、ユーザー スタックのトップ (最高アドレス) と見なす妥当なアドレスを見つけようとすることです。たとえば、「トップ」の場合、argv の元のアドレスが理想的ですが、main が呼び出す任意の関数からアクセスすることはできません。最初は、古いバックトレース メソッドを使用できると考えていました。保存されたフレーム ポインター値が 0 になるまで、保存されたフレーム ポインター値を追跡し、その後の次の値が実用的な最高値としてカウントされます。(これは argv のアドレスを取得することと同じではありませんが、たとえば、_start または _start が呼び出すもの [たとえば、__libc_start_main] のスタック ポインターの値を調べることはできます。)
ありがとう。