gcc デバッグシンボル (-g フラグ) とリンカーの -rdynamic オプションの比較 質問する

gcc デバッグシンボル (-g フラグ) とリンカーの -rdynamic オプションの比較 質問する

glibc は、実行中のプログラムのスタック トレースを取得するためにbacktrace()と を提供します。ただし、これを機能させるには、プログラムをリンカーのフラグ付きでビルドする必要があります。backtrace_symbols()-rdynamic

-ggcc に渡されるフラグとリンカーのフラグの違いは何ですか-rdynamic? サンプル コードでは、出力を比較するために readelf を実行しました。-rdynamicの下にさらに情報が生成されるようですSymbol table '.dynsym'が、追加情報が何であるかはよくわかりません。

stripを使用してビルドされたプログラムバイナリであっても-rdynamicbacktrace_symbols()動作を続けます。

stripバイナリからすべてのシンボルを削除すると、-rdynamicフラグによって追加されたものが残されるのはなぜですか?

編集: 以下の Mat の回答に基づいた追加の質問です。

-gあなたが取得した同じサンプルコードの場合、これが&の違いです。-rdynamic

オプションなしで..

    Symbol table '.dynsym' contains 4 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000   218 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
         2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
         3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    Symbol table '.symtab' contains 70 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
         2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 

セクションが増え-g.symtabテーブル内のエントリも増えますが、.dynsym同じままです。

      [26] .debug_aranges    PROGBITS         0000000000000000  0000095c
           0000000000000030  0000000000000000           0     0     1
      [27] .debug_pubnames   PROGBITS         0000000000000000  0000098c
           0000000000000023  0000000000000000           0     0     1
      [28] .debug_info       PROGBITS         0000000000000000  000009af
           00000000000000a9  0000000000000000           0     0     1
      [29] .debug_abbrev     PROGBITS         0000000000000000  00000a58
           0000000000000047  0000000000000000           0     0     1
      [30] .debug_line       PROGBITS         0000000000000000  00000a9f
           0000000000000038  0000000000000000           0     0     1
      [31] .debug_frame      PROGBITS         0000000000000000  00000ad8
           0000000000000058  0000000000000000           0     0     8
      [32] .debug_loc        PROGBITS         0000000000000000  00000b30
           0000000000000098  0000000000000000           0     0     1

    Symbol table '.dynsym' contains 4 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000   218 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
         2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
         3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    Symbol table '.symtab' contains 77 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 

追加のデバッグ セクションがない場合-rdynamic、.symtab エントリは 70 個 (通常の gcc 呼び出しと同じ) ですが、.dynsymエントリ数はさらに多くなります。

    Symbol table '.dynsym' contains 19 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000   218 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
         2: 00000000005008e8     0 OBJECT  GLOBAL DEFAULT  ABS _DYNAMIC
         3: 0000000000400750    57 FUNC    GLOBAL DEFAULT   12 __libc_csu_fini   
         4: 00000000004005e0     0 FUNC    GLOBAL DEFAULT   10 _init
         5: 0000000000400620     0 FUNC    GLOBAL DEFAULT   12 _start
         6: 00000000004006f0    86 FUNC    GLOBAL DEFAULT   12 __libc_csu_init   
         7: 0000000000500ab8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
         8: 00000000004006de    16 FUNC    GLOBAL DEFAULT   12 main
         9: 0000000000500aa0     0 NOTYPE  WEAK   DEFAULT   23 data_start
        10: 00000000004007c8     0 FUNC    GLOBAL DEFAULT   13 _fini
        11: 00000000004006d8     6 FUNC    GLOBAL DEFAULT   12 foo
        12: 0000000000500ab8     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
        13: 0000000000500a80     0 OBJECT  GLOBAL DEFAULT  ABS _GLOBAL_OFFSET_TABLE_
        14: 0000000000500ac0     0 NOTYPE  GLOBAL DEFAULT  ABS _end
        15: 00000000004007d8     4 OBJECT  GLOBAL DEFAULT   14 _IO_stdin_used
        16: 0000000000500aa0     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
        17: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
        18: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__    

    Symbol table '.symtab' contains 70 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
         2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 

さて、私が抱いている疑問は次の通りです。

  1. gdb では、bt を実行して bactrace を取得できます。これが機能するのであれば、backtrace_symbols を機能させる-g必要があるのはなぜですか?-rdynamic

  2. .symtabへの追加と への追加を比較すると、まったく-g同じではありません。どちらかが他方と比較して優れたデバッグ情報を提供しますか? ちなみに、生成される出力のサイズは次のようになります: -g あり > -rdynamic あり > どちらのオプションもなし.dynsym-rdynamic

  3. .dynsym の正確な使用法は何ですか? このバイナリによってエクスポートされるすべてのシンボルですか? その場合、コードをライブラリとしてコンパイルしていないのに、なぜ foo が .dynsym に入るのでしょうか。

  4. すべての静的ライブラリを使用してコードをリンクする場合、backtrace_symbols が機能するために -rdynamic は必要ありませんか?

ベストアンサー1

ドキュメントによると:

これは、使用されているシンボルだけでなく、すべてのシンボルを動的シンボル テーブルに追加するようにリンカーに指示します。

これらはデバッグ シンボルではなく、動的リンカー シンボルです。これらは、strip(ほとんどの場合) 実行可能ファイルを壊してしまうため削除されません。これらは、実行可能ファイルの最終リンク ステージを実行するためにランタイム リンカーによって使用されます。

例:

$ cat t.c
void foo() {}
int main() { foo(); return 0; }

コンパイルとリンクは-rdynamic(もちろん最適化もせずに)

$ gcc -O0 -o t t.c
$ readelf -s t

Symbol table '.dynsym' contains 3 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

Symbol table '.symtab' contains 50 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400270     0 SECTION LOCAL  DEFAULT    1 
....
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS t.c
    28: 0000000000600e14     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    29: 0000000000600e40     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

したがって、実行可能ファイルには.symtab、すべてが含まれています。ただし、 についてはまったく.dynsym触れられていないことに注意してくださいfoo。そこには、必要最低限​​のものだけが含まれています。これは、 が機能するには十分な情報ではありませんbacktrace_symbols。コード アドレスと関数名を一致させるには、そのセクションにある情報に依存します。

次のようにコンパイルします-rdynamic:

$ gcc -O0 -o t t.c -rdynamic
$ readelf -s t

Symbol table '.dynsym' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     5: 0000000000601008     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
     6: 0000000000400734     6 FUNC    GLOBAL DEFAULT   13 foo
     7: 0000000000601028     0 NOTYPE  GLOBAL DEFAULT  ABS _end
     8: 0000000000601008     0 NOTYPE  WEAK   DEFAULT   24 data_start
     9: 0000000000400838     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    10: 0000000000400750   136 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    11: 0000000000400650     0 FUNC    GLOBAL DEFAULT   13 _start
    12: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    13: 000000000040073a    16 FUNC    GLOBAL DEFAULT   13 main
    14: 0000000000400618     0 FUNC    GLOBAL DEFAULT   11 _init
    15: 00000000004007e0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    16: 0000000000400828     0 FUNC    GLOBAL DEFAULT   14 _fini

Symbol table '.symtab' contains 50 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400270     0 SECTION LOCAL  DEFAULT    1 
....
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS t.c
    28: 0000000000600e14     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    29: 0000000000600e40     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

のシンボルについても同様です.symtabが、foo動的シンボル セクションにシンボルが含まれるようになりました (他の多くのシンボルもそこに表示されます)。これによりbacktrace_symbols、コード アドレスを関数名にマップするのに十分な情報 (ほとんどの場合) が含まれるようになりました。

それを剥ぎ取る:

$ strip --strip-all t
$ readelf -s t

Symbol table '.dynsym' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     5: 0000000000601008     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
     6: 0000000000400734     6 FUNC    GLOBAL DEFAULT   13 foo
     7: 0000000000601028     0 NOTYPE  GLOBAL DEFAULT  ABS _end
     8: 0000000000601008     0 NOTYPE  WEAK   DEFAULT   24 data_start
     9: 0000000000400838     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    10: 0000000000400750   136 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    11: 0000000000400650     0 FUNC    GLOBAL DEFAULT   13 _start
    12: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    13: 000000000040073a    16 FUNC    GLOBAL DEFAULT   13 main
    14: 0000000000400618     0 FUNC    GLOBAL DEFAULT   11 _init
    15: 00000000004007e0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    16: 0000000000400828     0 FUNC    GLOBAL DEFAULT   14 _fini
$ ./t
$

今は.symtabなくなってしまいましたが、動的シンボル テーブルはまだ存在し、実行可能ファイルは実行されます。したがって、backtrace_symbolsまだ動作します。

動的シンボル テーブルを削除します。

$ strip -R .dynsym t
$ ./t
./t: relocation error: ./t: symbol , version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

... 実行ファイルが壊れてしまいます。

.symtabおよびが何の目的で使用されているかについては、次の記事.dynsymが興味深いです。ELF シンボルテーブルの内部注目すべき点の1つは、.symtab実行時に必要ないため、ローダーによって破棄されることです。そのセクションはプロセスのメモリに残りません。.dynsym一方、実行時に必要となるため、プロセス イメージ内に保持されます。そのため、プロセスのbacktrace_symbols内部から現在のプロセスに関する情報を収集するなどの用途に使用できます。

つまり、要約すると:

  • 動的シンボルは削除されない。削除stripすると実行ファイルがロード不可能になるからである。
  • backtrace_symbolsどのコードがどの関数に属しているかを判断するために動的なシンボルが必要
  • backtrace_symbolsデバッグシンボルを使用しない

したがって、あなたが気づいた動作です。


具体的なご質問については:

  1. gdbデバッガーです。実行ファイルとライブラリ内のデバッグ情報を使用して、関連情報を表示します。多くのよりも複雑でbacktrace_symbols、ライブ プロセスに加えてドライブ上の実際のファイルを検査します。backtrace_symbolsは完全にインプロセスであるため、実行可能イメージにロードされていないセクションにアクセスできません。デバッグ セクションはランタイム イメージにロードされないため、使用できません。
  2. .dynsymはデバッグ セクションではありません。これは、ダイナミック リンカーによって使用されるセクションです。.symbtabもデバッグ セクションではありませんが、実行可能ファイル (およびライブラリ ファイル) にアクセスできるデバッガーによって使用できます。-rdynamic ではないデバッグ セクションを生成するのではなく、拡張された動的シンボル テーブルのみを生成します。 からの実行可能ファイルの増加は、-rdynamicその実行可能ファイル内のシンボルの数 (およびアライメント/パディングの考慮事項) に完全に依存します。 よりもかなり小さくなるはずです-g
  3. 静的にリンクされたバイナリを除き、実行可能ファイルはロード時に解決された外部依存関係を必要とします。リンクprintfや C ライブラリからの一部のアプリケーション起動手順などです。これらの外部シンボルは実行可能ファイルのどこかで示される必要があります。これは の用途であり、を指定しない場合でも.dynsymexe に があるのはそのためです。 を指定すると、リンカーはプロセスの動作には必要ではないが などで使用できる他のシンボルを追加します。.dynsym-rdynamicbacktrace_symbols
  4. backtrace_symbols静的にリンクすると、関数名が解決されません。 を指定しても-rdynamic.dynsymセクションは実行可能ファイルに出力されません。実行可能イメージにシンボル テーブルがロードされないため、backtrace_symbolsコード アドレスをシンボルにマップできません。

おすすめ記事