c++的符号表的肤浅认识

符号表是编译期产生的一个hash列表,随着可执行文件在一起

示例程序

int a = 10;
int b;

void foo(){
    static int c=100;
}
int main(){
    int d=1000;
    int e;

    foo();
}

符号表包括了变量和函数的信息,以及调试信息,可以通过nm 命令查看符号表

$nm -a
0000000000000000 a
0000000000004028 D a
0000000000004034 B b
0000000000004030 b .bss
0000000000004030 B __bss_start
0000000000000000 n .comment
0000000000004030 b completed.7393
0000000000000000 a crtstuff.c
0000000000000000 a crtstuff.c
                 w [email protected]@GLIBC_2.2.5
0000000000004018 d .data
0000000000004018 D __data_start
0000000000004018 W data_start
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 N .debug_str
0000000000001050 t deregister_tm_clones
00000000000010c0 t __do_global_dtors_aux
0000000000003e00 d __do_global_dtors_aux_fini_array_entry
0000000000004020 D __dso_handle
0000000000003e08 d .dynamic
0000000000003e08 d _DYNAMIC
00000000000003b8 r .dynstr
0000000000000328 r .dynsym
0000000000004030 D _edata
0000000000002038 r .eh_frame
0000000000002004 r .eh_frame_hdr
0000000000004038 B _end
00000000000011b8 t .fini
00000000000011b8 T _fini
0000000000003e00 d .fini_array
0000000000001110 t frame_dummy
0000000000003df8 d __frame_dummy_init_array_entry
0000000000002104 r __FRAME_END__
0000000000004000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002004 r __GNU_EH_FRAME_HDR
0000000000000308 r .gnu.hash
000000000000045c r .gnu.version
0000000000000468 r .gnu.version_r
0000000000003fd8 d .got
0000000000004000 d .got.plt
0000000000001000 t .init
0000000000001000 t _init
0000000000003df8 d .init_array
0000000000003e00 d __init_array_end
0000000000003df8 d __init_array_start
0000000000000000 a init.c
00000000000002a8 r .interp
0000000000002000 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
00000000000011b0 T __libc_csu_fini
0000000000001140 T __libc_csu_init
                 U [email protected]@GLIBC_2.2.5
0000000000001120 T main
0000000000000000 a main.cpp
00000000000002e8 r .note.ABI-tag
00000000000002c4 r .note.gnu.build-id
0000000000001080 t register_tm_clones
0000000000000488 r .rela.dyn
0000000000002000 r .rodata
0000000000001020 T _start
0000000000001020 t .text
0000000000004030 D __TMC_END__
0000000000001119 T _Z3foov
000000000000402c d _ZZ3foovE1c

可见这里还包含了位置 , 变量和函数都能看到。 还有debug信息

通过readelf -S a.out 可以查看所有符号表头信息

$readelf -S a.out
There are 32 section headers, starting at offset 0x3c90:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         00000000000002a8  000002a8
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.build-i NOTE             00000000000002c4  000002c4
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .note.ABI-tag     NOTE             00000000000002e8  000002e8
       0000000000000020  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000308  00000308
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           0000000000000328  00000328
       0000000000000090  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           00000000000003b8  000003b8
       00000000000000a4  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000000045c  0000045c
       000000000000000c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000000468  00000468
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000000488  00000488
       00000000000000c0  0000000000000018   A       5     0     8
  [10] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [11] .text             PROGBITS         0000000000001020  00001020
       0000000000000195  0000000000000000  AX       0     0     16
  [12] .fini             PROGBITS         00000000000011b8  000011b8
       000000000000000d  0000000000000000  AX       0     0     4
  [13] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000004  0000000000000004  AM       0     0     4
  [14] .eh_frame_hdr     PROGBITS         0000000000002004  00002004
       0000000000000034  0000000000000000   A       0     0     4
  [15] .eh_frame         PROGBITS         0000000000002038  00002038
       00000000000000d0  0000000000000000   A       0     0     8
  [16] .init_array       INIT_ARRAY       0000000000003df8  00002df8
       0000000000000008  0000000000000008  WA       0     0     8
  [17] .fini_array       FINI_ARRAY       0000000000003e00  00002e00
       0000000000000008  0000000000000008  WA       0     0     8
  [18] .dynamic          DYNAMIC          0000000000003e08  00002e08
       00000000000001d0  0000000000000010  WA       6     0     8
  [19] .got              PROGBITS         0000000000003fd8  00002fd8
       0000000000000028  0000000000000008  WA       0     0     8
  [20] .got.plt          PROGBITS         0000000000004000  00003000
       0000000000000018  0000000000000008  WA       0     0     8
  [21] .data             PROGBITS         0000000000004018  00003018
       0000000000000018  0000000000000000  WA       0     0     8
  [22] .bss              NOBITS           0000000000004030  00003030
       0000000000000008  0000000000000000  WA       0     0     4
  [23] .comment          PROGBITS         0000000000000000  00003030
       000000000000004c  0000000000000001  MS       0     0     1
  [24] .debug_aranges    PROGBITS         0000000000000000  0000307c
       0000000000000030  0000000000000000           0     0     1
  [25] .debug_info       PROGBITS         0000000000000000  000030ac
       00000000000000ca  0000000000000000           0     0     1
  [26] .debug_abbrev     PROGBITS         0000000000000000  00003176
       0000000000000088  0000000000000000           0     0     1
  [27] .debug_line       PROGBITS         0000000000000000  000031fe
       000000000000004b  0000000000000000           0     0     1
  [28] .debug_str        PROGBITS         0000000000000000  00003249
       0000000000000068  0000000000000001  MS       0     0     1
  [29] .symtab           SYMTAB           0000000000000000  000032b8
       0000000000000690  0000000000000018          30    49     8
  [30] .strtab           STRTAB           0000000000000000  00003948
       000000000000020f  0000000000000000           0     0     1
  [31] .shstrtab         STRTAB           0000000000000000  00003b57
       0000000000000139  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

nm 和readelf 还有很多功能, man 真的值得看看

符号表的生成

符号表在编译的词法分析的时候,一直向符号表里填入符号,例如有重复定义的时候会报错,因为符号表已经存在该符号了。

符号表的使用

  1. 链接的时候,链接器会去符号表查找引用的符号是否存在
  2. 对于常量,编译器会向符号表查找const的值,直接替换

符号表中的调试代码

所以说区分debug版本和release 版本的方法就是看符号表里有没有调试符号了

通过objdump -g a.out 可以看到很多调试信息

Contents of the .debug_info section (loaded from a.out):

  Compilation Unit @ offset 0x0:
   Length:        0xe1 (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x34): GNU C++14 9.2.1 20200130 -mtune=generic -march=x86-64 -g
    <10>   DW_AT_language    : 4    (C++)
    <11>   DW_AT_name        : (indirect string, offset: 0x0): main.cpp
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0xe): /home/
    <19>   DW_AT_low_pc      : 0x1119
    <21>   DW_AT_high_pc     : 0x22
    <29>   DW_AT_stmt_list   : 0x0
 <1><2d>: Abbrev Number: 2 (DW_TAG_variable)
    <2e>   DW_AT_name        : a
    <30>   DW_AT_decl_file   : 1
    <31>   DW_AT_decl_line   : 1
    <32>   DW_AT_decl_column : 5
    <33>   DW_AT_type        : <0x41>
    <37>   DW_AT_external    : 1
    <37>   DW_AT_location    : 9 byte block: 3 28 40 0 0 0 0 0 0    (DW_OP_addr: 4028)
 <1><41>: Abbrev Number: 3 (DW_TAG_base_type)
    <42>   DW_AT_byte_size   : 4
    <43>   DW_AT_encoding    : 5    (signed)
    <44>   DW_AT_name        : int
 <1><48>: Abbrev Number: 4 (DW_TAG_const_type)
    <49>   DW_AT_type        : <0x41>
 <1><4d>: Abbrev Number: 2 (DW_TAG_variable)
    <4e>   DW_AT_name        : b
    <50>   DW_AT_decl_file   : 1
    <51>   DW_AT_decl_line   : 2
    <52>   DW_AT_decl_column : 5
    <53>   DW_AT_type        : <0x41>
    <57>   DW_AT_external    : 1
    <57>   DW_AT_location    : 9 byte block: 3 34 40 0 0 0 0 0 0    (DW_OP_addr: 4034)
 <1><61>: Abbrev Number: 5 (DW_TAG_variable)
    <62>   DW_AT_name        : (indirect string, offset: 0x2f): cons
    <66>   DW_AT_decl_file   : 1
    <67>   DW_AT_decl_line   : 3
    <68>   DW_AT_decl_column : 11
    <69>   DW_AT_type        : <0x48>
    <6d>   DW_AT_location    : 9 byte block: 3 4 20 0 0 0 0 0 0     (DW_OP_addr: 2004)
 <1><77>: Abbrev Number: 6 (DW_TAG_subprogram)
    <78>   DW_AT_external    : 1
    <78>   DW_AT_name        : (indirect string, offset: 0x9): main
    <7c>   DW_AT_decl_file   : 1
    <7d>   DW_AT_decl_line   : 7
    <7e>   DW_AT_decl_column : 5
    <7f>   DW_AT_type        : <0x41>
    <83>   DW_AT_low_pc      : 0x1120
    <8b>   DW_AT_high_pc     : 0x1b
    <93>   DW_AT_frame_base  : 1 byte block: 9c     (DW_OP_call_frame_cfa)
    <95>   DW_AT_GNU_all_tail_call_sites: 1
    <95>   DW_AT_sibling     : <0xb1>
 <2><99>: Abbrev Number: 7 (DW_TAG_variable)

分离调试信息

将调试信息保存到a.symbol 中
objcopy --only-keep-debug a.out a.symbol
去除调试信息
objcopy  --strip-debug  a.out a.bin

可以发现去除符号信息的debug 版本少了一下符号(表头)

  [24] .debug_aranges    PROGBITS         0000000000000000  0000307c
       0000000000000030  0000000000000000           0     0     1
  [25] .debug_info       PROGBITS         0000000000000000  000030ac
       00000000000000e5  0000000000000000           0     0     1
  [26] .debug_abbrev     PROGBITS         0000000000000000  00003191
       00000000000000a0  0000000000000000           0     0     1
  [27] .debug_line       PROGBITS         0000000000000000  00003231
       000000000000004b  0000000000000000           0     0     1
  [28] .debug_str        PROGBITS         0000000000000000  0000327c
       000000000000006d  0000000000000001  MS       0     0     1
  [29] .symtab           SYMTAB           0000000000000000  000032f0
       00000000000006a8  0000000000000018          30    50     8
  [30] .strtab           STRTAB           0000000000000000  00003998
       0000000000000218  0000000000000000  

符号表在调试的方法

没有调试信息的符号表是很难调试的,以下是没有调试信息和有调试信息的gdb情况

没有符号表的情况

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007ff3b4f74c60 in __GI___nanosleep (requested_time=0x7ffe32225050, remaining=0x7ffe32225050)
    at ../sysdeps/unix/sysv/linux/nanosleep.c:28
28  ../sysdeps/unix/sysv/linux/nanosleep.c: No such file or directory.
(gdb) bt
#0  0x00007ff3b4f74c60 in __GI___nanosleep (requested_time=0x7ffe32225050, remaining=0x7ffe32225050)
    at ../sysdeps/unix/sysv/linux/nanosleep.c:28
#1  0x0000000000439815 in wait_to_exit(std::shared_ptr<App>&) ()
#2  0x000000000043626a in main ()
(gdb) n
29  in ../sysdeps/unix/sysv/linux/nanosleep.c

有符号表没有源文件的情况

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f8247a4cc60 in __GI___nanosleep ([email protected]=0x7fffdf2fb6e0,
---Type <return> to continue, or q <return> to quit---
    [email protected]=0x7fffdf2fb6e0) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
28  ../sysdeps/unix/sysv/linux/nanosleep.c: No such file or directory.
(gdb) bt
#0  0x00007f8247a4cc60 in __GI___nanosleep ([email protected]=0x7fffdf2fb6e0,
    [email protected]=0x7fffdf2fb6e0) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
#1  0x0000000000439a35 in std::this_thread::sleep_for<long, std::ratio<1l, 1l> > (__rtime=...)
    at /builds/main.cpp:192
#2  wait_to_exit(std::shared_ptr<App>&) () at /builds/main.cpp:192
#3  0x00000000004364b2 in main () at /builds/SkybilityHA/ha-engine/src/ha-sync/main.cpp:345
#4  0x00007f8245ee7b97 in __libc_start_main (main=0x436130 <main>, argc=3, argv=0x7fffdf2fbd88, init=<optimized out>,
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffdf2fbd78) at ../csu/libc-start.c:310
#5  0x00000000004372ad in _start () at ../sysdeps/x86_64/elf/start.S:113
(gdb) n
29  in ../sysdeps/unix/sysv/linux/nanosleep.c
(gdb)
wait_to_exit(std::shared_ptr<App>&) () at /builds/main.cpp:193
193 /builds/main.cpp: No such file or directory.

可见,没有调试信息的堆栈信息是比较少的, 另外有调试信息提示没有源文件,所以如果将文件放到指定位置,就可以逐行调试代码了。

生产上用符号文件调试releas 程序

  1. 我们通常将调试文件放到可执行文件相同的目录,因为gdb会在当前目录查找符号文件。 另外可以通过gdb -s 来指定符号文件的位置。可以加多个符号文件
  2. 我们可以通过attach 加上-s 来调试运行中的程序

原文地址:https://www.cnblogs.com/hustcpp/p/12375416.html

时间: 2024-10-31 07:36:56

c++的符号表的肤浅认识的相关文章

查看程序符号表的几个命令

objdump -t xxx.o [email protected]:~/Documents/encrypchip$ objdump -t main.o main.o: file format elf32-little SYMBOL TABLE: 00000000 l df *ABS* 00000000 main.c 00000000 l d .text 00000000 .text 00000000 l d .data 00000000 .data 00000000 l d .bss 0000

算法-符号表的实现(顺序查找和二分查找)

符号表是一种存储键值对的数据结构,支持两种操作插入和查找,就是将一组新的键值对存入表中然后根据给定的键得到对应的值,在编程语言中常用Dictionary原理类似.符号表是一种典型的抽象数据结构,在生活之中应用的场景也很多,可以根据钥匙开门,域名解析的时候的IP地址查询,字典和图书的简介和页数,key和value是密不可分的,通过key我们可以很快找到我们需要的value. 无序链表的顺序查找 主要通过Node节点存储数据,之前的博客中有关于链表的实现,详情可参考之前的博客,代码有注释就解释太多了

Symbol Table(符号表)

一.定义 符号表是一种存储键值对的数据结构并且支持两种操作:将新的键值对插入符号表中(insert):根据给定的键值查找对应的值(search). 二.API 1.无序符号表 几个设计决策: A.泛型 在设计方法时没有指定处理对象,而是使用了泛型. 并且将键(Key)和值(Value)区分开来. B.重复键的处理 规则: 每个值(Value)都只对应一个键(Key)(即不允许存在重复的键). 当用例代码向表中存入的键值对和表中的已有的键(以及关联的值)冲突时,新的值替代旧的值. C.Null 键

使用objdump objcopy查看与修改符号表

使用objdump objcopy查看与修改符号表动态库Linuxgccfunction    我们在 Linux 下运行一个程序,有时会无法启动,报缺少某某库.这时需要查看可执行程序或者动态库中的符号表,动态库的依赖项, Linux 有现成的工具可用:objdump .    有时我们拿到一个静态库,想调用其中的函数,而某些函数作用域非全局,也可以通过修改符号来达到目的. Linux 有现成的工具可用: objcopy . 下面我们来看看具体怎么使用.    objdump 是 gcc 套件中

X编程语言定义--词汇表、符号表

一直喜欢操作系统,因为这个原因后来才无意中做了程序员,但那个东西实在太大了,退而次之,做个小语言来玩玩. 基本都是来源于C/PASCAL/Oberon/BASIC/C#:用PASCAL的原因是它语法和逻辑清晰易懂,用C/C++/C#中的部分小特性,是因为这此常用也熟悉些. 另外LISP类的函数式语言虽然很先进,但更多的情况下看起来好像显的不是很自然.理想情况下,是需要实现LAMDA表达式的. 涉及到C#则只是最终语言是因为题外原因的干预,可以写程序时模拟出它的委托较简单的实现,不然最后实现事件之

ELF Format 笔记(七)—— 符号表

最是那一低头的温柔,像一朵水莲花不胜凉风的娇羞,道一声珍重,道一声珍重,那一声珍重里有蜜甜的忧愁 —— 徐志摩 符号表 (symbol table) 中保存着符号的定义或者引用信息.对于 android so 文件来说,.dynsym 符号表保存着库文件的导入和导出符号. 用 readelf 看一下 android liblog.so 的 .dynsym 动态符号表: 符号 __cxa_finalize 的 Ndx(st_shndx) 为 UND,表明该符号在本 so 中未定义,需要去 libl

程序减肥,strip,eu-strip 及其符号表

程序减肥,strip,eu-strip 及其符号表 我们要给我们生成的可执行文件和DSO瘦身,因为这样可以节省更多的磁盘空间,所以我们移除了debug信息,移除了符号表信息,同时我们还希望万一出事了,比如coredump了,我们能获取更多的信息,这时候我们又希望有符号表.     我们等不能做到呢.Linux下是怎么解决这个矛盾的呢?先看第一个问题,程序减肥.      1 程序减肥     我写了个简单的代码,main调用了foo,foo调用了bar,其中bar故意访问了非法地址,为了引起co

IDA 与VC 加载符号表

将Windbg路径下的symsrv.yes 拷贝到ida 的安装目录,重新分析ntoskrnl.exe, 加载本地的符号表 添加环境变量  变量名:_NT_SYMBOL_PATH变量值:SRV*{$Path}*http://msdl.microsoft.com/download/symbols/将“{$Path}”替换为要存储pdb符号表文件的路径,比如:C:\PDB,在线的符号下载. 于是用IDA,或者在VC里写程序调试时,都会从网上自动下载符号表……msvcrt.pdb,ole32.pdb,

gdb如何调试没有符号表(未加-g选项的编译)的程序

/*********************************************************************  * Author  : Samson  * Date    : 01/30/2015  * Test platform:  *              3.13.0-24-generic  *              GNU bash, 4.3.11(1)-release  * ************************************