GCC调试符号(-g标志)与连接器的-rdynamic选项 [英] gcc debug symbols (-g flag) vs linker's -rdynamic option

查看:3023
本文介绍了GCC调试符号(-g标志)与连接器的-rdynamic选项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

的glibc提供回溯() backtrace_symbols()来得到一个正在运行的程序的堆栈跟踪。但这个工作方案必须与连接器的 -rdynamic 标记之上。

什么是 -g 之间的区别标志传递给gcc的​​VS连接器的 -rdynamic 标志?有关示例code我做readelf到输出进行比较。 -rdynamic 似乎在符号表'显.dynsym来生产更多的信息,但我不太知道什么额外的信息是。

即使我使用内置的程序二进制 -rdynamic backtrace_symbols() 继续工作。

将删除它为什么留下任何被加入该二进制所有符号的 -rdynamic 标志?

编辑:基于下面垫的回应后续问题。

有关同一样品code你拿了这是我看到的差别 -g &安培; -rdynamic

没有任何选项。

 符号表'显.dynsym中包含4个:
       编号:值大小类型绑定可见NDX名称
         0:0 0000000000000000 NoType在本地默认UND
         1:0000000000000000 218 FUNC全局默认UND __libc_start_main@GLIBC_2.2.5(2)
         2:0000000000000000 0 NoType在弱DEFAULT UND _Jv_RegisterClasses
         3:0000000000000000 0 NoType在弱DEFAULT UND __gmon_start__    符号表的.symtab包含70项:
       编号:值大小类型绑定可见NDX名称
         0:0 0000000000000000 NoType在本地默认UND
         1:0000000000400200 0部分本地默认1
         2:0 000000000040021c部分本地默认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    符号表显.dynsym中包含4个:
       编号:值大小类型绑定可见NDX名称
         0:0 0000000000000000 NoType在本地默认UND
         1:0000000000000000 218 FUNC全局默认UND __libc_start_main@GLIBC_2.2.5(2)
         2:0000000000000000 0 NoType在弱DEFAULT UND _Jv_RegisterClasses
         3:0000000000000000 0 NoType在弱DEFAULT UND __gmon_start__    符号表的.symtab包含77项:
       编号:值大小类型绑定可见NDX名称
         0:0 0000000000000000 NoType在本地默认UND
         1:0000000000400200 0部分本地默认1

-rdynamic 没有额外的调试节,的.symtab条目是70(同海湾合作委员会香草调用),但更多的显.dynsym 项..

 符号表'显.dynsym包含19项:
       编号:值大小类型绑定可见NDX名称
         0:0 0000000000000000 NoType在本地默认UND
         1:0000000000000000 218 FUNC全局默认UND __libc_start_main@GLIBC_2.2.5(2)
         2:0 00000000005008e8 OBJECT全局默认ABS _DYNAMIC
         3:0000000000400750 57 FUNC全局默认12 __libc_csu_fini
         4:0 00000000004005e0 FUNC全局默认10 _init
         5:0000000000400620 0 FUNC全局默认12 _start
         6:00000000004006f0 86 FUNC全局默认12 __libc_csu_init
         7:0 0000000000500ab8全球NoType在默认ABS __bss_start
         8:00000000004006de 16 FUNC全局默认12个主要
         9:0 0000000000500aa0弱NoType在默认23 DATA_START
        10:0 00000000004007c8 FUNC全局默认13 _fini
        11:6 00000000004006d8 FUNC全局默认12美孚
        12:0 0000000000500ab8全球NoType在默认ABS _edata
        13:0 0000000000500a80 OBJECT全局默认ABS _GLOBAL_OFFSET_TABLE_
        14:0 0000000000500ac0全球NoType在默认ABS _end
        15:4 00000000004007d8对象全局默认14 _IO_stdin_used
        16:0 0000000000500aa0全球NoType在默认23 __data_start
        17:0000000000000000 0 NoType在弱DEFAULT UND _Jv_RegisterClasses
        18:0000000000000000 0 NoType在弱DEFAULT UND __gmon_start__    符号表的.symtab包含70项:
       编号:值大小类型绑定可见NDX名称
         0:0 0000000000000000 NoType在本地默认UND
         1:0000000000400200 0部分本地默认1
         2:0 000000000040021c部分本地默认2

现在这些都是我的问题。


  1. 在GDB你可以做BT得到bactrace。如果这样的作品只用 -g 为什么我们需要 -rdynamic 为backtrace_symbols工作?


  2. 的补充与比较,以的.symtab -g &安培;补充显.dynsym -rdynamic 他们是不完全一样的..没有任何一个相比,提供更好的调试信息另一个 ?
    FWIW,产生的输出尺寸是这样的:用-g与-rdynamic>既不选项

  3. >
  4. 到底是什么显.dynsym的使用情况如何?它是由该二进制导出的所有符号?在这种情况下,为什么是foo进入显.dynsym因为我们没有编制code作为一个库。


  5. 如果我使用所有静态库则是-rdynamic不需要backtrace_symbols工作联系我code?



解决方案

根据文档:


  

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


这些都不是调试符号,它们是动态连接器的符号。那些不被条删除,因为这将(在大多数情况下)打破可执行文件 - 使用它们运行时链接程序做你的可执行文件的最后链接阶段

例如:

  $猫T.C
无效美孚(){}
INT的main(){富();返回0; }

编译并没有 -rdynamic 链接(没有优化,很明显)

  $ gcc的-O0 -o牛逼T.C
$ readelf -sŤ符号表显.dynsym包含3个条目:
   编号:值大小类型绑定可见NDX名称
     0:0 0000000000000000 NoType在本地默认UND
     1:0 0000000000000000 FUNC全局默认UND __libc_start_main@GLIBC_2.2.5(2)
     2:0000000000000000 0 NoType在弱DEFAULT UND __gmon_start__符号表的.symtab包含50项:
   编号:值大小类型绑定可见NDX名称
     0:0 0000000000000000 NoType在本地默认UND
     1:0000000000400270 0部分本地默认1
....
    27:0000000000000000 0 FILE本地默认ABS T.C
    28:0 0000000000600e14 NoType在本地默认18 __init_array_end
    29:0 0000000000600e40 OBJECT本地默认21 _​​DYNAMIC

所以可执行具有的.symtab 的一切。但是请注意,显.dynsym 并没有提及在所有 - 它拥有最基本的要素在里面。这是没有足够的信息对 backtrace_symbols 工作。它依赖于信息present在该条中,使用功能名称相匹配code类地址。

现在与编译 -rdynamic

  $ gcc的-O0 -o牛逼T.C -rdynamic
$ readelf -sŤ符号表显.dynsym包含17项:
   编号:值大小类型绑定可见NDX名称
     0:0 0000000000000000 NoType在本地默认UND
     1:0 0000000000000000 FUNC全局默认UND __libc_start_main@GLIBC_2.2.5(2)
     2:0000000000000000 0 NoType在弱DEFAULT UND __gmon_start__
     3:0000000000000000 0 NoType在弱DEFAULT UND _Jv_RegisterClasses
     4:0000000000601018 0 NoType在全局默认ABS _edata
     5:0000000000601008 0 NoType在全局默认24 __data_start
     6:0000000000400734 6 FUNC全局默认13美孚
     7:0000000000601028 0 NoType在全局默认ABS _end
     8:0000000000601008 0 NoType在弱DEFAULT 24 DATA_START
     9:0000000000400838 4 OBJECT全局默认15 _IO_stdin_used
    10:0000000000400750 136 FUNC全局默认13 __libc_csu_init
    11:0000000000400650 0 FUNC全局默认13 _start
    12:0000000000601018 0 NoType在全局默认ABS __bss_start
    13:16 000000000040073a FUNC全局默认13个主要
    14:0000000000400618 0 FUNC全局默认11 _init
    15:00000000004007e0 2 FUNC全局默认13 __libc_csu_fini
    16:0000000000400828 0 FUNC全局默认14 _fini符号表的.symtab包含50项:
   编号:值大小类型绑定可见NDX名称
     0:0 0000000000000000 NoType在本地默认UND
     1:0000000000400270 0部分本地默认1
....
    27:0000000000000000 0 FILE本地默认ABS T.C
    28:0 0000000000600e14 NoType在本地默认18 __init_array_end
    29:0 0000000000600e40 OBJECT本地默认21 _​​DYNAMIC

的.symtab 为符号

同样的事情,但现在在动态符号部分符号(和很多其他的符号现在似乎有太多)。这使得 backtrace_symbols 工作 - 它现在有足够的信息(大部分情况下),以code地址与函数名映射

地带是:

  $条--strip-所有T
$ readelf -sŤ符号表显.dynsym包含17项:
   编号:值大小类型绑定可见NDX名称
     0:0 0000000000000000 NoType在本地默认UND
     1:0 0000000000000000 FUNC全局默认UND __libc_start_main@GLIBC_2.2.5(2)
     2:0000000000000000 0 NoType在弱DEFAULT UND __gmon_start__
     3:0000000000000000 0 NoType在弱DEFAULT UND _Jv_RegisterClasses
     4:0000000000601018 0 NoType在全局默认ABS _edata
     5:0000000000601008 0 NoType在全局默认24 __data_start
     6:0000000000400734 6 FUNC全局默认13美孚
     7:0000000000601028 0 NoType在全局默认ABS _end
     8:0000000000601008 0 NoType在弱DEFAULT 24 DATA_START
     9:0000000000400838 4 OBJECT全局默认15 _IO_stdin_used
    10:0000000000400750 136 FUNC全局默认13 __libc_csu_init
    11:0000000000400650 0 FUNC全局默认13 _start
    12:0000000000601018 0 NoType在全局默认ABS __bss_start
    13:16 000000000040073a FUNC全局默认13个主要
    14:0000000000400618 0 FUNC全局默认11 _init
    15:00000000004007e0 2 FUNC全局默认13 __libc_csu_fini
    16:0000000000400828 0 FUNC全局默认14 _fini
$ ./t
$

没有的.symtab 走了,但动态符号表中仍然存在,并运行可执行文件。因此, backtrace_symbols 仍然可以工作。

带钢动态符号表:

  $带-R显.dynsymŤ
$ ./t
./t:搬迁错误:./t:符号,版本GLIBC_2.2.5文件libc.so.6的定义不带链接时参考

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

什么的.symtab 显.dynsym 用于这里是一个有趣的阅读:的Inside ELF符号表的。其中一件事需要注意的是的.symtab 不需要在运行,所以它被装载丢弃。那部分不留在进程的内存。 显.dynsym ,在otherhand,的的在运行时需要的,因此它保持在过程映像中。因此,它是适用于像 backtrace_symbols 事情从自身内部收集有关当前进程的信息。

因此​​,在短期:


  • 动态符号不被剥离条因为这将会使可执行文件能被装载

  • backtrace_symbols 需要动态符号弄清楚code所属功能

  • backtrace_symbols 不使用调试符号

你注意到

因此​​,行为。


有关您的具体问题:


  1. GDB 是一个调试器。它使用的可执行文件和库中的调试信息显示相关信息。它的的比 backtrace_symbols 更复杂,并检查除了直播过程中驱动器上的实际文件。 backtrace_symbols 不,这完全是在过程 - 因此它无法访问未装入可执行映像节。调试节没有加载到运行时的形象,所以它不能使用它们。

  2. 显.dynsym 不是一个调试部分。它是由动态链接程序使用的部分。 .symbtab 不是一个调试部分要么,但它可以通过访问可执行文件(和库)文件,调试器一起使用。 -rdynamic 的生成调试节,只有扩展动态符号表。从 -rdynamic 可执行的增长完全依赖于符号在可执行文件(和对齐/填充的考虑)的数量。它应该比 -g 相当少的。

  3. 除了静态链接的二进制文件,可执行文件需要在加载时解析外部依赖性。像联的printf 和C库的应用程序的启动程序。这些外部符号必须在某个可执行文件中注明:这是用于显.dynsym ,这就是为什么EXE文件有一个显.dynsym ,即使你不指定 -rdynamic 。当你指定它,链接器补充说,是没有必要的过程中工作,但可以通过类似的事情 backtrace_symbols 使用其他符号。

  4. backtrace_symbols 不会解决任何函数名,如果你静态链接。即使您指定 -rdynamic 显.dynsym 部分将不发射到可执行文件。无符号表被加载到可执行映像,所以 backtrace_symbols 不能code不会忽略映射到符号。

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.

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.

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

When strip removes all the symbols from the binary why is it leaving behind whatever was added by the -rdynamic flag ?

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

For the same sample code you took this is the difference I see with -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 -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 

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

  1. 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 ?

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

  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 ?

解决方案

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

Example:

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

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

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.

Now compile with -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 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 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
$

No .symtab is gone, but the dynamic symbol table is still there, and the executable runs. So backtrace_symbols still works too.

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

So in short:

  • 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 function
  • backtrace_symbols does not use debugging symbols

Hence the behavior you noticed.


For your specific questions:

  1. gdb is a debugger. It uses debug information in the executable and libraries to display relevant information. It is much more complex than backtrace_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.
  2. .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.
  3. 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 like backtrace_symbols.
  4. 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, so backtrace_symbols cannot map code adresses to symbols.

这篇关于GCC调试符号(-g标志)与连接器的-rdynamic选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆