13

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] のスタック ポインターの値を調べることはできます。)

ありがとう。

4

4 に答える 4

5

違いは、amd64 ではフレーム ポインターを省略することがより推奨されていることだと思います。abiの16ページの脚注には、

スタック フレームのフレーム ポインタとしての従来の %rbp の使用は、スタック フレームのインデックスに %rsp (スタック ポインタ) を使用することで回避できます。この手法により、プロローグとエピローグで 2 つの命令が節約され、追加の汎用レジスタ (%rbp) が 1 つ使用可能になります。

GDBが何をするのかわかりません。でコンパイルすると-g、オブジェクトには、GDB が必要なものを再構築できるようにする魔法のデバッグ情報があると思います。デバッグ情報なしで 64 ビット マシンで GDB を試したことはないと思います。

于 2011-12-24T15:42:51.450 に答える
3

GDB は、アンワインドに DWARF CFI を使用します。-g でコンパイルされたストリップされていないバイナリの場合、これは .debug_info セクションにあります。ストリップされた x86-64 バイナリの場合、.eh_frame セクションにアンワインド情報があります。これは、x86-64 ABIのセクション 3.7、56 ページで定義されています。DWARF の解析は非常に複雑であるため、この情報を自分で処理するのは非常に困難ですが、libunwindにはそのサポートが含まれていると思います。

于 2012-08-24T11:19:03.810 に答える
1

私がglibcとリンクしていると仮定すると(私はそうしています)、glibcグローバルシンボル__libc_stack_endを使用して、実用的な目的でこの問題を解決できるように見えます。

extern void * __libc_stack_end;

void myfunction(void) {
  /* ... */
  off_t stack_hi = (off_t)__libc_stack_end;
  /* ... */
}
于 2011-12-25T00:55:17.747 に答える
1

argv のアドレスが必要な場合は、それへのポインタを main に保存してみませんか?
スタックを巻き戻そうとすると、たとえそれが機能したとしても、移植性が非常に低くなります。
スタックに戻ることができたとしても、最初の関数のフレーム ポインターが NULL になるかどうかは明らかではありません。スタックの最初の関数は戻りませんが、システム コールを呼び出して終了するため、そのフレーム ポインターは使用されません。NULL に初期化される正当な理由はありません。

于 2011-12-24T15:54:21.037 に答える