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

Man*_*har 30 c linux linker gcc shared-libraries

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 
Run Code Online (Sandbox Code Playgroud)

-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 
Run Code Online (Sandbox Code Playgroud)

-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 
Run Code Online (Sandbox Code Playgroud)

现在这些是我的问题..

  1. 在gdb中你可以做bt来获得bactrace.如果这只是-g为什么我们需要-rdynamicbacktrace_symbols才能工作?

  2. 在增加的比较.symtab-g和补充,.dynsym-rdynamic他们是不完全一样的..没有比其他任何一个提供更好的调试信息?FWIW,产生的输出大小如下:使用-g> with -rdynamic>且没有选项

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

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

Mat*_*Mat 40

根据文件:

这指示链接器将所有符号(不仅是已使用的符号)添加到动态符号表中.

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

例:

$ cat t.c
void foo() {}
int main() { foo(); return 0; }
Run Code Online (Sandbox Code Playgroud)

没有编译和链接-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
Run Code Online (Sandbox Code Playgroud)

所以可执行文件.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
Run Code Online (Sandbox Code Playgroud)

符号中的符号相同.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
$
Run Code Online (Sandbox Code Playgroud)

没有.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
Run Code Online (Sandbox Code Playgroud)

......你得到一个破碎的可执行文件.

有关什么.symtab.dynsym用于的有趣读物在这里:内部ELF符号表.需要注意的一点.symtab是在运行时不需要,因此加载器会将其丢弃.该部分不会留在进程的内存中..dynsym,在otherhand,需要在运行时,因此它保持在处理图像英寸 因此,它可用于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无法将代码映射映射到符号.