web-dev-qa-db-ja.com

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

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

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

_-rdynamic_を使用してビルドされたプログラムバイナリをstripしても、backtrace_symbols()は引き続き機能します。

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

編集:以下のマットの回答に基づくフォローアップの質問。

あなたが取った同じサンプルコードの場合、これは_-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(Vanilla 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を取得できます。それが_-g_だけで機能する場合、backtrace_symbolsが機能するために_-rdynamic_が必要なのはなぜですか?

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

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

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

30
Manohar

ドキュメントによると:

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

これらはデバッグシンボルではなく、ダイナミックリンカーシンボルです。これらは(ほとんどの場合)実行可能ファイルを破壊するため、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あります。ただし、.dynsymfooについてまったく言及していないことに注意してください。そこには必要不可欠なものがすべて含まれています。これは、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は、実行時にisが必要なため、プロセスイメージに保持されます。したがって、backtrace_symbolsのようなものは、現在のプロセスに関する情報をそれ自体の中から収集することができます。

つまり、要するに:

  • 動的シンボルはstripによって削除されません。これは、実行可能ファイルをロードできなくするためです。
  • backtrace_symbolsは、どのコードがどの関数に属しているかを把握するために動的シンボルを必要とします
  • backtrace_symbolsはデバッグシンボルを使用しません

したがって、あなたが気づいた行動。


あなたの特定の質問のために:

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