GDBの破損したスタックフレーム-デバッグ方法


113

次のスタックトレースがあります。これからデバッグに役立つものを作成することは可能ですか?

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) 

を取得しSegmentation fault、スタックトレースがあまり役に立たない場合、どこからコードを調べればよいでしょうか。

注:コードを投稿すると、SOの専門家が答えをくれます。私はSOからガイダンスを受けて自分で答えを見つけたいので、ここではコードを投稿しません。謝罪。


おそらくあなたのプログラムは雑草に飛び込んだ-スタックポインターから何かを回復できますか?
Carl Norum

1
もう1つの考慮事項は、フレームポインターが正しく設定されているかどうかです。最適化せずに構築しています-fno-omit-frame-pointerか?また、メモリの破損についてvalgrindは、それがオプションである場合は、より適切なツールである可能性があります。
FatalError

回答:


155

これらの偽のアドレス(0x00000002など)は実際にはPCの値であり、SPの値ではありません。さて、このようなSEGVを取得し、偽の(非常に小さい)PCアドレスを使用すると、99%の時間は、偽の関数ポインタを介して呼び出されたことが原因です。C ++の仮想呼び出しは関数ポインターを介して実装されるため、仮想呼び出しの問題はすべて同じように現れる可能性があることに注意してください。

間接呼び出し命令は、呼び出し後にスタックにPCをプッシュし、PCをターゲット値(この場合は偽)に設定するだけなので、これ起こった場合、手動でPCをスタックからポップして簡単に元に戻すことができます。 。32ビットx86コードでは、次のようにします。

(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4

64ビットx86コードでは、

(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8

次に、aを実行btして、コードが実際にどこにあるのかを理解できるはずです。

残りの1%の場合、エラーはスタックの上書きによるもので、通常はスタックに格納されている配列がオーバーフローすることによって発生します。この場合、valgrindなどのツールを使用することで、状況をより明確にすることができる場合があります。


5
@George:gdb executable corefile実行可能ファイルとコアファイルを使用してgdbを開きます。この時点で、次のことができますbt(または上記のコマンドの後にbt)...
Chris Dodd

2
@mk .. ARMはスタックを戻りアドレスに使用しません-代わりにリンクレジスタを使用します。したがって、通常はこの問題は発生しません。発生した場合は、通常、他のスタックの破損が原因です。
Chris Dodd、

2
ARMでも、呼び出された関数が実行を開始する前に、すべての汎用レジスタとLRがスタックに格納されていると思います。関数が終了すると、LRの値がPCにポップされ、関数が戻ります。スタックが破損している場合、PCの値が間違っていることがわかりますか?この場合、スタックポインターを調整すると、適切なスタックにつながり、問題のデバッグに役立ちます。どう思いますか?plsは私にあなたの考えを知らせました。ありがとうございました。
mk ..

1
どういう意味ですか?
Danny Lo

5
ARMはx86ではありません-そのスタックポインターはspespまたはrspではなく、呼び出され、その呼び出し命令lrは、スタックではなくレジスターに戻りアドレスを格納します。したがって、ARMの場合、呼び出しを元に戻す必要があるのはだけset $pc = $lrです。$lrが無効な場合は、巻き戻すのがはるかに困難です。
Chris Dodd、2018年

44

状況がかなり単純な場合は、Chris Doddの答えが最適です。NULLポインターをジャンプしたように見えます。

ただし、プログラムがクラッシュする前に、足、膝、首、目を撃った可能性があります。スタックを上書きし、フレームポインターを破壊し、その他の悪事を犯しました。もしそうなら、ハッシュを解いてもジャガイモと肉が表示される可能性は低いです。

より効率的なソリューションは、デバッガーの下でプログラムを実行し、プログラムがクラッシュするまで関数をステップオーバーすることです。クラッシュする関数が特定されたら、再度開始してその関数にステップインし、どの関数がクラッシュの原因であるかを特定します。問題のある1行のコードが見つかるまで繰り返します。75%の確率で、修正は明白になります。

他の25%の状況では、いわゆる問題のあるコード行は赤ニシンです。これは、以前に多くの行を設定した(無効な)条件(おそらく数千行前)に反応します。その場合、選択される最良のコースは、多くの要因に依存します。主に、コードの理解とコードの経験:

  • おそらく、デバッガーのウォッチポイントを設定するかprintf、重要な変数に診断を挿入すると、必要なA haが発生します。
  • 異なる入力でテスト条件を変更すると、デバッグよりも洞察が得られるでしょう。
  • たぶん、もう1組の目があなたにあなたの仮定をチェックするか、見落とされた証拠を集めることを強います。
  • 時々、必要なのは夕食に行き、集められた証拠について考えることだけです。

幸運を!


13
目の2番目のペアが利用できない場合は、代わりにゴム製のアヒルがよく証明されています。
Matt

2
バッファの終わりを消去することもできます。バッファの最後を書き留めてもクラッシュしないかもしれませんが、関数から抜けると死にます。
フィアット2016

役に立つかもしれません:GDB:自動 'Next'ing
user202729

28

スタックポインターが有効であると仮定します...

バックトレースからSEGVが発生する場所を正確に知ることは不可能かもしれません-最初の2つのスタックフレームは完全に上書きされたと思います。0xbffff284は有効なアドレスのように見えますが、次の2つはそうではありません。スタックを詳しく調べるには、次のことを試してください。

gdb $ x / 32ga $ rsp

またはバリアント(32を別の番号に置き換えます)。これは、アドレス(a)としてフォーマットされた、巨大(g)サイズのスタックポインターから始まるいくつかのワード(32)を出力します。形式の詳細については、「help x」と入力してください。

この場合、いくつかの番兵 'printf'でコードを計測することは悪い考えではないかもしれません。


信じられないほど役に立ちました。ありがとうございます。3つのフレームだけ戻ったスタックがあり、「バックトレースが停止しました:このフレームと同じ前のフレーム(破損したスタック?)」を押しました。CPU例外ハンドラーのコードでこれとまったく同じようなことを以前に行ったことがありますがinfo symbol、gdbでこれを行う方法以外は思い出せませんでした。
2013年

22
32ビットARMデバイス上のFWIW:x/256wa $sp =)
2013年

2
@leander X / 256waとは何ですか?64ビットARMに必要です。一般的に、それが何であるかを説明できると役に立ちます。
mk ..

5
答えごとに、「x」=メ​​モリの場所を調べます。これは、いくつかの「w」=ワード(この場合は256)を出力し、「a」=アドレスとして解釈します。詳しくは、sourceware.org / gdb / current / onlinedocs / gdb / Memory.html#MemoryのGDBマニュアルをご覧ください
レアンダー

7

他のレジスタのいくつかを見て、それらの1つにキャッシュされたスタックポインタがあるかどうかを確認します。そこから、スタックを取得できる場合があります。また、これが埋め込まれている場合、スタックは非常に特定のアドレスで定義されることがよくあります。それを使用して、時々まともなスタックを取得することもできます。これはすべて、ハイパースペースにジャンプしたときに、プログラムが途中でメモリ全体を起動しなかったことを前提としています...


3

スタックの上書きの場合、値はプログラムから認識できるものに対応している可能性があります。

たとえば、私はスタックを見ていた

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x000000000000342d in ?? ()
#2  0x0000000000000000 in ?? ()

そして0x342d、私はそれのためにアプリケーションログをgreppedときにノードIDであることが判明13357、です。これにより、スタックの上書きが発生した可能性のある候補サイトをすぐに絞り込むことができました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.