glibc は、実行中のプログラムのスタック トレースを取得するためにbacktrace()
と を提供します。ただし、これを機能させるには、プログラムをリンカーのフラグ付きでビルドする必要があります。backtrace_symbols()
-rdynamic
-g
gcc に渡されるフラグとリンカーのフラグの違いは何ですか-rdynamic
? サンプル コードでは、出力を比較するために readelf を実行しました。-rdynamic
の下にさらに情報が生成されるようですSymbol table '.dynsym'
が、追加情報が何であるかはよくわかりません。
strip
を使用してビルドされたプログラムバイナリであっても-rdynamic
、backtrace_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
さて、私が抱いている疑問は次の通りです。
gdb では、bt を実行して bactrace を取得できます。これが機能するのであれば、backtrace_symbols を機能させる
-g
必要があるのはなぜですか?-rdynamic
.symtab
への追加と への追加を比較すると、まったく-g
同じではありません。どちらかが他方と比較して優れたデバッグ情報を提供しますか? ちなみに、生成される出力のサイズは次のようになります: -g あり > -rdynamic あり > どちらのオプションもなし.dynsym
-rdynamic
.dynsym の正確な使用法は何ですか? このバイナリによってエクスポートされるすべてのシンボルですか? その場合、コードをライブラリとしてコンパイルしていないのに、なぜ foo が .dynsym に入るのでしょうか。
すべての静的ライブラリを使用してコードをリンクする場合、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
デバッグシンボルを使用しない
したがって、あなたが気づいた動作です。
具体的なご質問については:
gdb
デバッガーです。実行ファイルとライブラリ内のデバッグ情報を使用して、関連情報を表示します。多くのよりも複雑でbacktrace_symbols
、ライブ プロセスに加えてドライブ上の実際のファイルを検査します。backtrace_symbols
は完全にインプロセスであるため、実行可能イメージにロードされていないセクションにアクセスできません。デバッグ セクションはランタイム イメージにロードされないため、使用できません。.dynsym
はデバッグ セクションではありません。これは、ダイナミック リンカーによって使用されるセクションです。.symbtab
もデバッグ セクションではありませんが、実行可能ファイル (およびライブラリ ファイル) にアクセスできるデバッガーによって使用できます。-rdynamic
ではないデバッグ セクションを生成するのではなく、拡張された動的シンボル テーブルのみを生成します。 からの実行可能ファイルの増加は、-rdynamic
その実行可能ファイル内のシンボルの数 (およびアライメント/パディングの考慮事項) に完全に依存します。 よりもかなり小さくなるはずです-g
。- 静的にリンクされたバイナリを除き、実行可能ファイルはロード時に解決された外部依存関係を必要とします。リンク
printf
や C ライブラリからの一部のアプリケーション起動手順などです。これらの外部シンボルは実行可能ファイルのどこかで示される必要があります。これは の用途であり、を指定しない場合でも.dynsym
exe に があるのはそのためです。 を指定すると、リンカーはプロセスの動作には必要ではないが などで使用できる他のシンボルを追加します。.dynsym
-rdynamic
backtrace_symbols
backtrace_symbols
静的にリンクすると、関数名が解決されません。 を指定しても-rdynamic
、.dynsym
セクションは実行可能ファイルに出力されません。実行可能イメージにシンボル テーブルがロードされないため、backtrace_symbols
コード アドレスをシンボルにマップできません。