3

iOS アプリの dSYM ファイルからアドレス、ファイル名、および行番号を解析しました。私は基本的に、アドレスをファイル名と行番号にマップするテーブルを持っています。これはデバッグに非常に役立ちます。

を取得するactual lookup addressには、クラッシュ レポートのスタック トレース アドレスを使用し、https ://stackoverflow.com/a/13576028/2758234 の回答で指定されている式を使用します。だから、このようなもの。

(actual lookup address) 
= (stack trace address) + (virtual memory slide) - (image load address)

私はそのアドレスを使用して、自分のテーブルで調べます。取得したファイル名は正しいですが、行番号は常に、スタック トレースで次の関数を呼び出した実際の行ではなく、呼び出された関数またはメソッドの最後を指しています。

どこかで読んだのですが、フレーム アドレスはシステム ポインター サイズの 2 倍に調整されているため、タグを解除する必要があることを覚えていません。したがって、32 ビット システムの場合、ポインターのサイズは 4 バイトであるため、次のような式を使用して 8 バイトを使用してタグを解除します。

(de-tagged address) = (tagged address) & ~(sizeof(uintptr_t)*2 - 1)

uintptr_t、Objective-C でポインターに使用されるデータ型です。

これを行った後、ルックアップは機能しますが、タグが解除されたアドレス以下である最も近いアドレスを見つけるなどのことをしなければなりません。

質問 #1 :
スタック フレーム アドレスのタグを解除する必要があるのはなぜですか? スタック トレースで、アドレスがまだ正しい場所を指していないのはなぜですか?

質問 #2 :
クラッシュ レポートに、フレームが欠落しているように見えることがあります。たとえば、which がwhich を呼び出すとfunction1()、スタック トレースに次のように表示されます。function2()function3()function4()

0  Exception
1  function4()
2  function3()
4  function1()

また、(フレーム 2、上記) のスタック トレース アドレスfunction3()は、タグを外した後でも、正しい行番号を指していません (ただし、正しいファイルです)。これは、Xcode にクラッシュ レポートをシンボリックさせた場合でも見られます。

なぜこれが起こるのですか?

4

1 に答える 1

4

質問 1 については、iOS クラッシュ レポートのアドレスには、考慮される 3 つのコンポーネントがあります。アプリの元のロード アドレス、アプリの起動時にそのアドレスに追加されたランダム スライド値、およびバイナリ。クラッシュ レポートの最後に、バイナリの実際のロード アドレスを示す行があるはずです。

スライドを計算するには、クラッシュ レポートから実際のロード アドレスを取得し、元のロード アドレスを差し引く必要があります。これは、アプリのこの特定の起動に適用されたランダム スライド値を示します。

テーブルをどのように派生させたのかわかりません - 問題はそこにあるかもしれません。lldb を使用して再確認することをお勧めします。アプリを lldb にロードし、アドレス 0x140000 にロードする必要があることを lldb に伝えることができます (これは、クラッシュ レポートからの実際のロード アドレスになります。スライドや元のロード アドレスについては心配する必要はありません)。

% xcrun lldb
(lldb) target create -d -a armv7 /path/to/myapp.app
(lldb) target modules load -f myapp __TEXT 0x140000

これで、lldb はバイナリをこのクラッシュ レポートの実際のロード アドレスにロードしました。次のような通常のクエリはすべて lldb で実行できます。

(lldb) image lookup -v -a 0x144100

アドレス 0x144100 で詳細なルックアップを実行します (クラッシュ レポートに表示される場合があります)。

を使用して、lldbで気の利いた「内部ラインテーブルをダンプする」コマンドを実行することもできますtarget modules dump line-table。たとえば、hello-world Mac アプリをコンパイルしました。

(lldb) tar mod dump line-table a.c
Line table for /tmp/a.c in `a.out
0x0000000100000f20: /tmp/a.c:3
0x0000000100000f2f: /tmp/a.c:4:5
0x0000000100000f39: /tmp/a.c:5:1
0x0000000100000f44: /tmp/a.c:5:1
(lldb) 

バイナリのロード アドレスを変更して、もう一度行テーブルをダンプしてみることができます。

(lldb) tar mod load -f a.out __TEXT 0x200000000
section '__TEXT' loaded at 0x200000000
(lldb) tar mod dump line-table a.c
Line table for /tmp/a.c in `a.out
0x0000000200000f20: /tmp/a.c:3
0x0000000200000f2f: /tmp/a.c:4:5
0x0000000200000f39: /tmp/a.c:5:1
0x0000000200000f44: /tmp/a.c:5:1    
(lldb) 

アドレスのタグを解除して何をしているのかよくわかりません。コール スタックのアドレスは、呼び出し命令ではなく、これらの関数の戻りアドレスです。したがって、これらは実際のメソッド呼び出し / ディスパッチ ソース行の次の行を指している場合がありますが、通常、ソースを見ていると簡単に理解できます。コード。すべてのルックアップがメソッドの最後を指している場合、ルックアップ スキームに問題がある可能性があります。

質問 2 については、フレーム 0 (現在実行中のフレーム) がスタック フレームを設定していないリーフ関数であるか、設定中の場合、フレーム 1 の巻き戻しが少し難しい場合があります。スタック フレームをアップします。そのような場合、フレーム #1 がスキップされる可能性があります。しかし、特に腕でフレーム #1 を過ぎると、アンワインドでフレームが失われることはありません。

マークされた関数が別の関数を呼び出す場合、非常にエッジケーシーな問題が 1 つありますnoreturn。関数の最後の命令は呼び出しである可能性があります (関数エピローグなし)。これは、二度と制御を取得できないことがわかっているためです。かなり珍しい。しかし、その場合、単純なシンボリック化により、メモリ内の次の関数の最初の命令へのポインターが得られます。デバッガーなどは、この問題を回避するために、シンボル/ソース行の検索を行うときに戻りアドレスから 1 を減算するというトリックを使用しますが、これはカジュアルなシンボリック作成者が通常心配する必要があるものではありません。また、現在実行中の関数 (フレーム 0) で decr-pc トリックを実行しないように注意する必要があります。これは、関数が実行を開始したばかりで、pc を前の関数にバックアップして誤ってシンボリック化したくないためです。 .

于 2013-09-08T13:51:50.873 に答える