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

标签 c linux gcc linker shared-libraries

glibc 提供 backtrace()backtrace_symbols()获取正在运行的程序的堆栈跟踪。但要使其工作,程序必须使用链接器的 -rdynamic 构建。旗帜。
-g有什么区别标志传递给 gcc vs 链接器的 -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(与 vanilla 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。如果仅适用于 -g为什么我们需要 -rdynamic为 backtrace_symbols 工作?
  • 比较添加到 .symtab-g & 添加到 .dynsym-rdynamic它们并不完全相同.. 与另一个相比,其中一个是否提供了更好的调试信息?
    FWIW,产生的输出大小是这样的: with -g > with -rdynamic > 没有选项
  • .dynsym 的具体用法是什么?是这个二进制文件导出的所有符号吗?在这种情况下,为什么 foo 进入 .dynsym ,因为我们没有将代码编译为库。
  • 如果我使用所有静态库链接我的代码,那么 backtrace_symbols 不需要 -rdynamic 工作?
  • 最佳答案

    根据文档:

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



    这些不是调试符号,它们是动态链接器符号。那些不会被 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用于这里:Inside ELF Symbol Tables .需要注意的事情之一是 .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无法将代码地址映射到符号。
  • 关于c - gcc 调试符号(-g 标志)与链接器的 -rdynamic 选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8623884/

    相关文章:

    linux - 根据 Linux 中的名称将文件内容连接到一个文件中

    php - 将字符串与php文本区域内的一行文本进行比较

    c - 共享对象文件的大小很大

    c++ - Clang/GCC 真的支持延迟加载功能吗?

    c - STM32L476 flash 页删除无效

    c - 这个C程序在做什么?整数运算**

    php - WordPress 3.2.1 使用 LAMP 上的所有可用内存

    你能在编译后的二进制文件中看到#defined 项吗?

    c - 如何转换文件。 a 到 .c 或其他扩展名?我需要读取文件.a

    c - 外部函数原型(prototype)和静态定义