web-dev-qa-db-ja.com

Linuxでaddr2lineコマンドを使用するには?

Unixでaddr2lineコマンドを使用しようとしていますが、毎回??:0と同じ出力を提供しています。私はaddr2line -e a.out 0x4005BDCとしてコマンドを与えています。 valgrindツールを使用してこのa.out実行可能ファイルを実行しているときにこのアドレスを取得して、メモリリークを見つけました。また、-gオプションを使用してソースコードをコンパイルしました。

44
Prak

Addr2lineの代わりにgdbを使用して、メモリアドレスを調べることもできます。実行可能ファイルをgdbにロードし、アドレスに保存されているシンボルの名前を出力します。 16シンボルテーブルを調べる

(gdb) info symbol 0x4005BDC 
30
ks1322

チェックしてください:

  • バイナリ内のすべての関数が-gでコンパイルされているか、addr2lineサポート関数のみがデバッグ情報を持っているか、つまり-gでコンパイルされているか
  • オフセットが有効なオフセットかどうか。つまり、オフセットは仮想メモリアドレスではなく、.textセクション内のオフセットのみである必要があります。 .textセクションでは、アドレスはバイナリ内の命令を指す必要があることを意味します

addr2lineの使用

以下は、man addr2lineからのメッセージです。

addr2line-アドレスをファイル名と行番号に変換します。

addressesは、実行可能ファイルのアドレスまたは再配置可能オブジェクトのセクションのオフセットにする必要があります。

出力はFILENAME:LINENO、ソースファイル名、ファイル内の行番号のようなものです

例。

例としてhelloworldを取り上げます。

#include <stdio.h>
int main()
{
    printf("hello\n");
    return 0;
}

gcc -g hello.cでコンパイルした後、最初にobjdumpを使用して、生成されたa.outファイルのオフセット情報に関するアイデアを得ることができます。

ダンプされた逆アセンブリの一部を次に示します。

Disassembly of section .text:

0000000000400440 <_start>:
  400440:       31 ed                   xor    %ebp,%ebp
  400442:       49 89 d1                mov    %rdx,%r9
  400445:       5e                      pop    %rsi
  400446:       48 89 e2                mov    %rsp,%rdx
  400449:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40044d:       50                      Push   %rax
  40044e:       54                      Push   %rsp
  40044f:       49 c7 c0 c0 05 40 00    mov    $0x4005c0,%r8
  400456:       48 c7 c1 50 05 40 00    mov    $0x400550,%rcx
  40045d:       48 c7 c7 36 05 40 00    mov    $0x400536,%rdi
  400464:       e8 b7 ff ff ff          callq  400420 <__libc_start_main@plt>
  400469:       f4                      hlt
  40046a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

  ...

   0000000000400536 <main>:

#include <stdio.h>
int main()
{
  400536:       55                      Push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
    printf("hello\n");
  40053a:       bf d4 05 40 00          mov    $0x4005d4,%edi
  40053f:       e8 cc fe ff ff          callq  400410 <puts@plt>
    return 0;
  400544:       b8 00 00 00 00          mov    $0x0,%eax
}
  400549:       5d                      pop    %rbp
  40054a:       c3                      retq
  40054b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

コードの左端の列は、バイナリファイルのオフセットです。 __start関数は標準Cライブラリに由来し、デバッグ情報なしでプリコンパイルされます。 main関数は、-gでファイルをコンパイルするため、デバッグ情報を持つhelloworldコードから取得されます。

addr2lineの出力は次のとおりです。

$ addr2line -e a.out 0x400442 #offset in the `__start` function
??:?
$ addr2line -e a.out 0x400536 #offset in the `main` function
hello.c:21
$ addr2line -e a.out 0x40054b -f #The last instruction of the `main` function
main
??:?

上記の出力からいくつかの結論を出すことができます。

  1. -gフラグで生成されたコードセグメント(セグメントにデバッグ情報があることを意味する)のみが、ファイル名と行番号の情報を正常に生成できました。
  2. -gフラグでコンパイルされた関数本体のすべてのオフセットが、ファイル名と行番号を正常に出力するとは限りません。オフセット0x40054bは、ret関数のmain命令の後の最後の命令ですが、情報を取得できませんでした。
21
Kun Ling

仮想アドレス(VA)ではなく、addr2lineにoffsetを指定する必要があります。おそらく、アドレス空間のランダム化がオフになっている場合、完全なVAを使用できますが、ほとんどの最新のOSでは、新しいプロセスのためにアドレス空間がランダム化されます。

VA 0x4005BDC valgrindにより、メモリ内のプロセスまたはライブラリのベースアドレスを見つけます。これを行うには、/proc/<PID>/mapsファイルはプログラムの実行中です。関心のある行は、プロセスのtextセグメントです。これは、パーミッションr-xpおよびプログラムまたはライブラリの名前。

ベースVAは0x0x4005000。次に、提供されたvalgrind VAとベースVA:0xbdc。次に、それをadd2lineに提供します。

addr2line -e a.out -j .text 0xbdc

そして、それがあなたの行番号を取得するかどうか確認してください。

17
tdenniston

それはまさにあなたがそれを使用する方法です。ただし、お持ちのアドレスがソースコード内の何かに直接対応していない可能性があります。

例えば:

$ cat t.c
#include <stdio.h>
int main()
{
    printf("hello\n");
    return 0;
}
$ gcc -g t.c
$ addr2line -e a.out 0x400534
/tmp/t.c:3
$ addr2line -e a.out 0x400550
??:0

0x400534は私の場合のmainのアドレスです。 0x400408a.outの有効な関数アドレスですが、GCCによって生成/インポートされたコードであり、デバッグ情報はありません。 (この場合、__libc_csu_initreadelf -a your_exeで実行可能ファイルのレイアウトを確認できます。)

addr2lineが失敗する他の場合は、デバッグ情報のないライブラリを含めている場合です。

11
Mat

-fオプションを追加して、関数名を表示してみてください:

addr2line -f -e a.out 0x4005BDC
5
Penghe Geng