Linux gcc 调试符号(-g 标志)与链接器的 -rdynamic 选项

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8623884/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:47:59  来源:igfitidea点击:

gcc debug symbols (-g flag) vs linker's -rdynamic option

clinuxgcclinkershared-libraries

提问by Manohar

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 -rdynamicflag.

glibc 提供backtrace()backtrace_symbols()获取正在运行的程序的堆栈跟踪。但是要使其工作,程序必须使用链接器的-rdynamic标志构建。

What is the difference between -gflag passed to gcc vs linker's -rdynamicflag ? For a sample code I did readelf to compare the outputs. -rdynamicseems to produce more info under Symbol table '.dynsym'But I am not quite sure what the additional info is.

-g传递给 gcc 的-rdynamic标志与链接器的标志有什么区别?对于示例代码,我做了 readelf 来比较输出。-rdynamic似乎在下产生更多信息Symbol table '.dynsym'但我不太确定附加信息是什么。

Even if I stripa program binary built using -rdynamic, backtrace_symbols()continue to work.

即使我strip使用构建的程序二进制文件-rdynamic,也可以backtrace_symbols()继续工作。

When stripremoves all the symbols from the binary why is it leaving behind whatever was added by the -rdynamicflag ?

strip从二进制文件中删除所有符号时,为什么它会留下-rdynamic标志添加的任何内容?

Edit: Follow-up questions based on Mat's response below..

编辑:根据 Mat 在下面的回复的后续问题..

For the same sample code you took this is the difference I see with -g& -rdynamic

对于您采用的相同示例代码,这是我看到的不同之处-g&-rdynamic

without any option..

没有任何选择。。

    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 

with -gthere are more sections, more entries in .symtabtable but .dynsymremains the same..

-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 

with -rdynamicno additional debug sections, .symtab entries are 70 (same as vanilla gcc invocation), but more .dynsymentries..

-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 

Now these are the questions I have..

现在这些是我的问题..

  1. In gdb you can do bt to get the bactrace. If that works with just -gwhy do we need -rdynamicfor backtrace_symbols to work ?

  2. Comparing the additions to .symtabwith -g& additions to .dynsymwith -rdynamicthey 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

  3. 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.

  4. If I link my code using all static libraries then -rdynamic is not needed for backtrace_symbols to work ?

  1. 在 gdb 中,您可以执行 bt 来获取 bactrace。如果这适用于-g为什么我们需要-rdynamicbacktrace_symbols 工作?

  2. 将添加.symtab-g和添加进行比较.dynsym-rdynamic它们并不完全相同.. 与另一个相比,其中一个是否提供了更好的调试信息?FWIW,产生的输出大小是这样的: with -g > with -rdynamic > 没有选项

  3. .dynsym 的具体用法是什么?是这个二进制文件导出的所有符号吗?在这种情况下,为什么 foo 进入 .dynsym ,因为我们没有将代码编译为库。

  4. 如果我使用所有静态库链接我的代码,那么 backtrace_symbols 不需要 -rdynamic 工作?

采纳答案by Mat

According to the docs:

根据文档:

This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table.

这会指示链接器将所有符号添加到动态符号表中,而不仅仅是使用过的符号。

Those are not debug symbols, they are dynamic linker symbols. Those are not removed by stripsince it would (in most cases) break the executable - they are used by the runtime linker to do the final link stage of your executable.

这些不是调试符号,它们是动态链接器符号。这些不会被删除,strip因为它会(在大多数情况下)破坏可执行文件 - 运行时链接器使用它们来执行可执行文件的最后链接阶段。

Example:

例子:

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

Compile and link without -rdynamic(and no optimizations, obviously)

没有编译和链接-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

So the executable has a .symtabwith everything. But notice that .dynsymdoesn't mention fooat all - it has the bare essentials in there. This is not enough information for backtrace_symbolsto work. It relies on the information present in that section to match code addresses with function names.

所以可执行文件具有.symtab一切。但是请注意,.dynsym它根本没有提到foo- 它里面有最基本的要素。这不足以提供backtrace_symbols工作所需的信息。它依赖于该部分中存在的信息来将代码地址与函数名称进行匹配。

Now compile with -rdynamic:

现在编译-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

Same thing for symbols in .symtab, but now foohas a symbol in the dynamic symbol section (and a bunch of other symbols appear there now too). This makes backtrace_symbolswork - it now has enough information (in most cases) to map code addresses with function names.

中的符号也是如此.symtab,但现在foo动态符号部分中有一个符号(现在那里也出现了一堆其他符号)。这使backtrace_symbols工作 - 它现在有足够的信息(在大多数情况下)来映射代码地址与函数名称。

Strip that:

剥离:

$ 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
$

Now .symtabis gone, but the dynamic symbol table is still there, and the executable runs. So backtrace_symbolsstill works too.

现在.symtab不见了,但动态符号表仍然存在,可执行文件运行。所以backtrace_symbols仍然有效。

Strip the dynamic symbol table:

剥离动态符号表:

$ 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.

...你会得到一个损坏的可执行文件。

An interesting read for what .symtaband .dynsymare used for is here: Inside ELF Symbol Tables. One of the things to note is that .symtabis 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, isneeded at runtime, so it is kept in the process image. So it is available for things like backtrace_symbolsto gather information about the current process from within itself.

关于什么.symtab和用于什么的有趣读物.dynsym在这里:Inside ELF Symbol Tables。需要注意的一件事是.symtab在运行时不需要它,因此它被加载程序丢弃。该部分不会保留在进程的内存中。.dynsym,在otherhand,需要在运行时,因此它保持在处理图像英寸 因此,它可用于backtrace_symbols从自身内部收集有关当前流程的信息之类的事情。

So in short:

简而言之:

  • dynamic symbols are not stripped by stripsince that would render the executable non-loadable
  • backtrace_symbolsneeds dynamic symbols to figure out what code belongs which function
  • backtrace_symbolsdoes not use debugging symbols
  • 动态符号不会被剥离,strip因为这会使可执行文件不可加载
  • backtrace_symbols需要动态符号来确定哪些代码属于哪个函数
  • backtrace_symbols不使用调试符号

Hence the behavior you noticed.

因此,您注意到的行为。



For your specific questions:

对于您的具体问题:

  1. gdbis a debugger. It uses debug information in the executable and libraries to display relevant information. It is muchmore complex than backtrace_symbols, and inspects the actual files on your drive in addition to the live process. backtrace_symbolsdoes 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.
  2. .dynsymis not a debugging section. It is a section used by the dynamic linker. .symbtabisn't a debugging section either, but it can be used by debugger that have access to the executable (and library) files. -rdynamicdoes notgenerate debug sections, only that extended dynamic symbol table. The executable growth from -rdynamicdepends entirely on the number of symbols in that executable (and alignment/padding considerations). It should be considerably less than -g.
  3. Except for statically linked binaries, executables need external dependencies resolved at load time. Like linking printfand some application startup procedures from the C library. These external symbols must be indicated somewhere in the executable: this is what .dynsymis used for, and this is why the exe has a .dynsymeven 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 like backtrace_symbols.
  4. backtrace_symbolswill not resolve any function names if you statically link. Even if you specify -rdynamic, the .dynsymsection will not be emitted to the executable. No symbol tables gets loaded into the executable image, so backtrace_symbolscannot map code adresses to symbols.
  1. gdb是一个调试器。它使用可执行文件和库中的调试信息来显示相关信息。它复杂得多backtrace_symbols,除了实时过程之外,它还会检查驱动器上的实际文件。backtrace_symbols不是,它完全在进程中 - 因此它无法访问未加载到可执行映像中的部分。调试部分未加载到运行时映像中,因此无法使用它们。
  2. .dynsym不是调试部分。它是动态链接器使用的部分。.symbtab也不是调试部分,但可以由有权访问可执行(和库)文件的调试器使用。-rdynamic生成调试节,只生成扩展动态符号表。可执行文件的增长-rdynamic完全取决于该可执行文件中的符号数量(以及对齐/填充考虑)。它应该大大小于-g
  3. 除了静态链接的二进制文件,可执行文件需要在加载时解析外部依赖项。像链接printf和一些来自 C 库的应用程序启动程序。这些外部符号必须在可执行文件的某处指明:这是.dynsym用于的,这就是为什么.dynsym即使您没有指定-rdynamic. 当您指定它时,链接器会添加其他符号,这些符号对于该进程的工作来说不是必需的,但可以被诸如backtrace_symbols.
  4. backtrace_symbols如果您静态链接,则不会解析任何函数名称。即使您指定-rdynamic,该.dynsym部分也不会发送到可执行文件。没有符号表被加载到可执行映像中,因此backtrace_symbols无法将代码地址映射到符号。