gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 12:40:55 28 4
gpt4 key购买 nike

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/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com