跳过正文

Linux elf 符号表(symtab)

··1492 字
Debug Linux Elf Debug
目录
elf-debug - 这篇文章属于一个选集。
§ 1: 本文

介绍 Linux elf 二进制文件的符号表(symtab)生成和管理机制。

Linux 使用 ELF 格式保存可执行二进制文件、共享库文件和 debuginfo 文件。

ELF 使用两个 Sections 来表示 symbol table:

  • .dynsym:动态符号, dynamic symbols, which can be used by another program;
  • .symtab:局部符号, local symbols used by the binary itself only;

.symtab 保存了程序中 标识符和内存地址的对应关系 ,对于 uprobe/kprobe/stack unwinding 等至关重要,因为只有知道函数名才能追踪(tracing)。

注:symbol table 和 gcc -g 生成的调试符号表不是一回事。

1 生成符号表
#

gcc 编译时默认会生成这两个符号表, file 命令显示 not stripped

  • -g 表示同时生成调试符号表(以 .debug 开头的 Sections, DWARF 格式),file 会显示 with debug_info;
root@lima-ebpf-dev:~# gcc -g test.c -o hello
root@lima-ebpf-dev:~# file hello
hello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a513ea9e31dc7b8ffbf423f56a04fbff6c0e99a9, for GNU/Linux 3.2.0, with debug_info, not stripped

2 查看符号表
#

查看可执行程序/共享库/debuginfo 文件中的符号表:

  • 使用 readelf -s/objdump -t 命令。
root@lima-ebpf-dev:~# readelf -S hello |grep sym
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8
  [35] .symtab           SYMTAB           0000000000000000  000032d0

root@lima-ebpf-dev:~# readelf -s hello|grep Symbo
Symbol table '.dynsym' contains 7 entries:
Symbol table '.symtab' contains 37 entries:

# 符号表中有 hello 函数的地址
# readelf -s hello|grep hello
    52: 000000000040057d    26 FUNC    GLOBAL DEFAULT   13 hello

# readelf -s hello

Symbol table '.dynsym' contains 5 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 65 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000400238     0 SECTION LOCAL  DEFAULT    1
     2: 0000000000400254     0 SECTION LOCAL  DEFAULT    2
     3: 0000000000400274     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000400298     0 SECTION LOCAL  DEFAULT    4
     5: 00000000004002b8     0 SECTION LOCAL  DEFAULT    5
     6: 0000000000400330     0 SECTION LOCAL  DEFAULT    6
     7: 0000000000400374     0 SECTION LOCAL  DEFAULT    7
     8: 0000000000400380     0 SECTION LOCAL  DEFAULT    8
     9: 00000000004003a0     0 SECTION LOCAL  DEFAULT    9
    10: 00000000004003b8     0 SECTION LOCAL  DEFAULT   10
    11: 0000000000400418     0 SECTION LOCAL  DEFAULT   11
    12: 0000000000400440     0 SECTION LOCAL  DEFAULT   12
    13: 0000000000400490     0 SECTION LOCAL  DEFAULT   13
    14: 0000000000400624     0 SECTION LOCAL  DEFAULT   14
    15: 0000000000400630     0 SECTION LOCAL  DEFAULT   15
    16: 0000000000400650     0 SECTION LOCAL  DEFAULT   16
    17: 0000000000400690     0 SECTION LOCAL  DEFAULT   17
    18: 0000000000600e10     0 SECTION LOCAL  DEFAULT   18
    19: 0000000000600e18     0 SECTION LOCAL  DEFAULT   19
    20: 0000000000600e20     0 SECTION LOCAL  DEFAULT   20
    21: 0000000000600e28     0 SECTION LOCAL  DEFAULT   21
    22: 0000000000600ff8     0 SECTION LOCAL  DEFAULT   22
    23: 0000000000601000     0 SECTION LOCAL  DEFAULT   23
    24: 0000000000601038     0 SECTION LOCAL  DEFAULT   24
    25: 000000000060103c     0 SECTION LOCAL  DEFAULT   25
    26: 0000000000000000     0 SECTION LOCAL  DEFAULT   26
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    28: 0000000000600e20     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    29: 00000000004004c0     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    30: 00000000004004f0     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    31: 0000000000400530     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    32: 000000000060103c     1 OBJECT  LOCAL  DEFAULT   25 completed.6355
    33: 0000000000600e18     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    34: 0000000000400550     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    35: 0000000000600e10     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
    37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    38: 00000000004007a0     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    39: 0000000000600e20     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    40: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
    41: 0000000000600e18     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    42: 0000000000600e28     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    43: 0000000000600e10     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    44: 0000000000400650     0 NOTYPE  LOCAL  DEFAULT   16 __GNU_EH_FRAME_HDR
    45: 0000000000601000     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
    46: 0000000000400620     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    47: 0000000000601038     0 NOTYPE  WEAK   DEFAULT   24 data_start
    48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@@GLIBC_2.2.5
    49: 000000000060103c     0 NOTYPE  GLOBAL DEFAULT   24 _edata
    50: 0000000000400624     0 FUNC    GLOBAL DEFAULT   14 _fini
    51: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    52: 000000000040057d    26 FUNC    GLOBAL DEFAULT   13 hello
    53: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
    54: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    55: 0000000000400638     0 OBJECT  GLOBAL HIDDEN    15 __dso_handle
    56: 0000000000400630     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    57: 00000000004005b0   101 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    58: 0000000000601040     0 NOTYPE  GLOBAL DEFAULT   25 _end
    59: 0000000000400490     0 FUNC    GLOBAL DEFAULT   13 _start
    60: 000000000060103c     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
    61: 0000000000400597    16 FUNC    GLOBAL DEFAULT   13 main
    62: 0000000000601040     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__
    63: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@@GLIBC_2.2.5
    64: 0000000000400418     0 FUNC    GLOBAL DEFAULT   11 _init

查看内核符号表:

#grep -E ' (t|T) ' /proc/kallsyms  |head
ffffffffa0000000 T startup_64
ffffffffa0000000 T _stext
ffffffffa0000000 T _text
ffffffffa0000030 T secondary_startup_64
ffffffffa00000e0 T verify_cpu
ffffffffa00001e0 T start_cpu0
ffffffffa00001f0 T __startup_64
ffffffffa0001000 T hypercall_page
ffffffffa0001000 t xen_hypercall_set_trap_table
ffffffffa0001020 t xen_hypercall_mmu_update

3 保存和删除符号表
#

一般情况下, 在生成可执行文件后,会将局部符号表(.symtab) 和调试符号表(.debug_XX) 保存到单独的 debuginfo 文件中,按需安装,从而减少二进制文件大小。

可以使用 objcopy --only-keep-debug hello hello.debug 来创建单独的 debuginfo 文件 hello.debug,该文件 同时包含 .symtab 和各种 .debug_XX Sections:

# du -sh hello
12K     hello

# objcopy --only-keep-debug hello hello.debug

# du -sh hello.debug
8.0K    hello.debug

使用 strip -s/--strip-all 命令同时删除二进制文件中的 .symtab.debug_XX 调试符号表内容,file 命令显示 stripped

  • .dynsym 是二进制给外界的符号表,不能删除;
# strip -s hello

# file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=86296aac1aa4149c55d40ced40c4ca8fbe3b17a9, stripped

# readelf -s hello|grep Symbo
Symbol table '.dynsym' contains 5 entries:

# readelf -s hello|grep hello

删除 .symtab 符号表后, uprobe 将失败:

  • kprobe/uprobe 使用 .symtab 来查找符号名称(变量、函数、类型)和内存地址的关系,不依赖 debug symbol table;
#bpftrace -e 'uprobe:./hello:hello {printf("%s",ustack)}'  -c ./hello
Attaching 1 probe...
hello world

        hello+0
        __libc_start_main+245

#strip -s hello

#bpftrace -e 'uprobe:./hello:hello {printf("%s",ustack)}'  -c ./hello
No probes to attach

4 从 debuginfo 文件合并符号表
#

和 strip 相反的工具是 elfutils 包提供的 eu-unstrip 工具,它可以将 exec binary 和 debuginfo 文件合并到一起,形成一个包含符号表和调试符号表的 exec binary 文件:

root@lima-ebpf-dev:~# eu-unstrip -f /usr/bin/bash /usr/lib/debug/.build-id/33/a5554034feb2af38e8c75872058883b2988bc5.debug -o /tmp/bash
root@lima-ebpf-dev:~#
root@lima-ebpf-dev:~# readelf -S /tmp/bash |grep sym
  [ 6] .dynsym           DYNSYM           0000000000004f68  00004f68
  [37] .symtab           SYMTAB           0000000000000000  002fb538
root@lima-ebpf-dev:~# readelf -S /tmp/bash |grep -E 'sym|\.debug_'
  [ 6] .dynsym           DYNSYM           0000000000004f68  00004f68
  [29] .debug_aranges    PROGBITS         0000000000000000  00154678
  [30] .debug_info       PROGBITS         0000000000000000  00154820
  [31] .debug_abbrev     PROGBITS         0000000000000000  00215af0
  [32] .debug_line       PROGBITS         0000000000000000  0021ad30
  [33] .debug_str        PROGBITS         0000000000000000  0027ad40
  [34] .debug_line_str   PROGBITS         0000000000000000  002840b0
  [35] .debug_loclists   PROGBITS         0000000000000000  002847a0
  [36] .debug_rnglists   PROGBITS         0000000000000000  002ec800
  [37] .symtab           SYMTAB           0000000000000000  002fb538
root@lima-ebpf-dev:~#
elf-debug - 这篇文章属于一个选集。
§ 1: 本文

相关文章

Linux elf 调试符号表(.debug_XX)
··4804 字
Debug Linux Elf Debug

介绍 Linux elf 二进制文件的调试符号表(.debug_XX)生成和管理机制。

objdump
··3921 字
Debug Tools Linux Elf Debug Tools

介绍 objdump 命令的功能用法。

readelf
··2555 字
Debug Tools Linux Elf Debug Tools

介绍 readelf 命令的功能用法。

add package meta to elf file
··1759 字
Elf Tools Linux Elf Tools

介绍向 elf 二进制文件中添加自定义 package meta 信息的方法。