gcc 调试符号(-g 标志)与链接器的 -rdynamic 选项 [英] gcc debug symbols (-g flag) vs linker's -rdynamic option
问题描述
glibc 提供 backtrace()
和 backtrace_symbols()
来获取正在运行的程序的堆栈跟踪.但要使其正常工作,必须使用链接器的 -rdynamic
标志构建程序.
glibc provides backtrace()
and backtrace_symbols()
to get the stack trace of a running program. But for this to work the program has to be built with linker's -rdynamic
flag.
传递给 gcc 的 -g
标志与链接器的 -rdynamic
标志有什么区别?对于示例代码,我做了 readelf 来比较输出.-rdynamic
似乎在 Symbol table '.dynsym'
下产生了更多信息,但我不太确定附加信息是什么.
What is the difference between -g
flag passed to gcc vs linker's -rdynamic
flag ? For a sample code I did readelf to compare the outputs. -rdynamic
seems to produce more info under Symbol table '.dynsym'
But I am not quite sure what the additional info is.
即使我 strip
使用 -rdynamic
构建的程序二进制文件,backtrace_symbols()
也会继续工作.
Even if I strip
a program binary built using -rdynamic
, backtrace_symbols()
continue to work.
当 strip
从二进制文件中删除所有符号时,为什么它会留下 -rdynamic
标志添加的任何内容?
When strip
removes all the symbols from the binary why is it leaving behind whatever was added by the -rdynamic
flag ?
基于下面 Mat 的回答的后续问题..
Follow-up questions based on Mat's response below..
对于您使用的相同示例代码,这是我在 -g
& 中看到的差异-rdynamic
For the same sample code you took this is the difference I see with -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
保持不变..
with -g
there are more sections, more entries in .symtab
table but .dynsym
remains the same..
[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
条目..
with -rdynamic
no additional debug sections, .symtab entries are 70 (same as vanilla gcc invocation), but more .dynsym
entries..
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
现在这些是我的问题..
Now these are the questions I have..
在 gdb 中,您可以通过 bt 获取 bactrace.如果这仅适用于
-g
为什么我们需要-rdynamic
才能使 backtrace_symbols 工作?
In gdb you can do bt to get the bactrace. If that works with just
-g
why do we need-rdynamic
for backtrace_symbols to work ?
比较 .symtab
与 -g
的新增内容 &.dynsym
与 -rdynamic
的补充它们并不完全相同.与另一个相比,它们是否提供更好的调试信息?FWIW,产生的输出大小是这样的:with -g > with -rdynamic > with no option
Comparing the additions to .symtab
with -g
& additions to .dynsym
with -rdynamic
they are not exactly the same.. Does either one provide better debugging info compared to the other ?
FWIW, size of the output produced is like this: with -g > with -rdynamic > with neither option
.dynsym 的具体用途是什么?是这个二进制文件导出的所有符号吗?在那种情况下,为什么 foo 进入 .dynsym 因为我们没有将代码编译为库.
What exactly is the usage of .dynsym ? Is it all the symbols exported by this binary ? In that case why is foo going into .dynsym because we are not compiling the code as a library.
如果我使用所有静态库链接我的代码,那么 backtrace_symbols 就不需要 -rdynamic 了吗?
If I link my code using all static libraries then -rdynamic is not needed for backtrace_symbols to work ?
推荐答案
根据文档:
这指示链接器将所有符号(不仅是使用的符号)添加到动态符号表中.
This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table.
那些不是调试符号,它们是动态链接器符号.strip
不会删除这些,因为它会(在大多数情况下)破坏可执行文件 - 运行时链接器使用它们来执行可执行文件的最终链接阶段.
Those are not debug symbols, they are dynamic linker symbols. Those are not removed by strip
since it would (in most cases) break the executable - they are used by the runtime linker to do the final link stage of your executable.
例子:
$ cat t.c
void foo() {}
int main() { foo(); return 0; }
在没有 -rdynamic
的情况下编译和链接(显然没有优化)
Compile and link without -rdynamic
(and no optimizations, obviously)
$ 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
工作.它依赖于该部分中的信息来匹配代码地址和函数名称.
So the executable has a .symtab
with everything. But notice that .dynsym
doesn't mention foo
at all - it has the bare essentials in there. This is not enough information for backtrace_symbols
to work. It relies on the information present in that section to match code addresses with function names.
现在用-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
工作 - 它现在有足够的信息(在大多数情况下)来映射代码地址和函数名称.
Same thing for symbols in .symtab
, but now foo
has a symbol in the dynamic symbol section (and a bunch of other symbols appear there now too). This makes backtrace_symbols
work - it now has enough information (in most cases) to map code addresses with function names.
剥离:
$ 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
仍然有效.
Now .symtab
is gone, but the dynamic symbol table is still there, and the executable runs. So backtrace_symbols
still works too.
剥离动态符号表:
$ 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
...你得到一个损坏的可执行文件.
... and you get a broken executable.
关于 .symtab
和 .dynsym
用途的有趣读物在这里:内部 ELF 符号表.需要注意的一件事是 .symtab
在运行时不需要,因此它被加载程序丢弃.该部分不会保留在进程的内存中..dynsym
,另一方面,在运行时需要,所以它保存在进程映像中.因此,它可用于 backtrace_symbols
之类的东西,以从自身内部收集有关当前进程的信息.
An interesting read for what .symtab
and .dynsym
are used for is here: Inside ELF Symbol Tables. One of the things to note is that .symtab
is not needed at runtime, so it is discarded by the loader. That section does not remain in the process's memory. .dynsym
, on the otherhand, is needed at runtime, so it is kept in the process image. So it is available for things like backtrace_symbols
to gather information about the current process from within itself.
简而言之:
- 动态符号不会被
strip
剥离,因为这会使可执行文件不可加载 backtrace_symbols
需要动态符号来判断什么代码属于哪个函数backtrace_symbols
不使用调试符号
- dynamic symbols are not stripped by
strip
since that would render the executable non-loadable backtrace_symbols
needs dynamic symbols to figure out what code belongs which functionbacktrace_symbols
does not use debugging symbols
因此你注意到了这种行为.
Hence the behavior you noticed.
对于您的具体问题:
gdb
是一个调试器.它使用可执行文件和库中的调试信息来显示相关信息.它比backtrace_symbols
复杂得多,除了实时进程之外,它还会检查驱动器上的实际文件.backtrace_symbols
没有,它完全在进程中 - 因此它无法访问未加载到可执行映像中的部分.调试部分未加载到运行时映像中,因此无法使用它们..dynsym
不是调试部分.它是动态链接器使用的部分..symbtab
也不是调试部分,但可以由有权访问可执行(和库)文件的调试器使用.-rdynamic
不生成调试部分,只生成扩展的动态符号表.-rdynamic
的可执行文件增长完全取决于该可执行文件中的符号数量(以及对齐/填充注意事项).它应该比-g
小很多.- 除了静态链接的二进制文件外,可执行文件需要在加载时解析外部依赖项.比如链接
printf
和 C 库中的一些应用程序启动过程.这些外部符号必须在可执行文件的某处指明:这就是.dynsym
的用途,这就是为什么即使你没有,exe 也有.dynsym
指定-rdynamic
.当您指定它时,链接器会添加其他符号,这些符号对于进程工作来说不是必需的,但可以被backtrace_symbols
之类的东西使用. 如果您静态链接, backtrace_symbols
将不会解析任何函数名称.即使您指定-rdynamic
,.dynsym
部分也不会发送到可执行文件.没有符号表被加载到可执行映像中,因此backtrace_symbols
无法将代码地址映射到符号.
gdb
is a debugger. It uses debug information in the executable and libraries to display relevant information. It is much more complex thanbacktrace_symbols
, and inspects the actual files on your drive in addition to the live process.backtrace_symbols
does not, it is entirely in-process - so it cannot access sections that are not loaded into the executable image. Debug sections are not loaded into the runtime image, so it can't use them..dynsym
is not a debugging section. It is a section used by the dynamic linker..symbtab
isn't a debugging section either, but it can be used by debugger that have access to the executable (and library) files.-rdynamic
does not generate debug sections, only that extended dynamic symbol table. The executable growth from-rdynamic
depends entirely on the number of symbols in that executable (and alignment/padding considerations). It should be considerably less than-g
.- Except for statically linked binaries, executables need external dependencies resolved at load time. Like linking
printf
and some application startup procedures from the C library. These external symbols must be indicated somewhere in the executable: this is what.dynsym
is used for, and this is why the exe has a.dynsym
even if you don't specify-rdynamic
. When you do specify it, the linker adds other symbols that are not necessary for the process to work, but can be used by things likebacktrace_symbols
. backtrace_symbols
will not resolve any function names if you statically link. Even if you specify-rdynamic
, the.dynsym
section will not be emitted to the executable. No symbol tables gets loaded into the executable image, sobacktrace_symbols
cannot map code adresses to symbols.
这篇关于gcc 调试符号(-g 标志)与链接器的 -rdynamic 选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!