符号表是编译期产生的一个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 __cxa_finalize@@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 __libc_start_main@@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 (requested_time=requested_time@entry=0x7fffdf2fb6e0,
---Type <return> to continue, or q <return> to quit---
remaining=remaining@entry=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 (requested_time=requested_time@entry=0x7fffdf2fb6e0,
remaining=remaining@entry=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 来调试运行中的程序

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

  1. C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)

    详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...

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

    最是那一低头的温柔,像一朵水莲花不胜凉风的娇羞,道一声珍重,道一声珍重,那一声珍重里有蜜甜的忧愁 —— 徐志摩 ilocker:关注 Android 安全(新手) QQ: 2597294287 符号表 ...

  3. IDA 与VC 加载符号表

    将Windbg路径下的symsrv.yes 拷贝到ida 的安装目录,重新分析ntoskrnl.exe, 加载本地的符号表 添加环境变量  变量名:_NT_SYMBOL_PATH变量值:SRV*{$P ...

  4. iOS 符号表恢复 & 逆向支付宝

    推荐序 本文介绍了恢复符号表的技巧,并且利用该技巧实现了在 Xcode 中对目标程序下符号断点调试,该技巧可以显著地减少逆向分析时间.在文章的最后,作者以支付宝为例,展示出通过在 UIAlertVie ...

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

    使用objdump objcopy查看与修改符号表动态库Linuxgccfunction    我们在 Linux 下运行一个程序,有时会无法启动,报缺少某某库.这时需要查看可执行程序或者动态库中的符 ...

  6. 符号表(Symbol Tables)

    小时候我们都翻过词典,现在接触过电脑的人大多数都会用文字处理软件(例如微软的word,附带拼写检查).拼写检查本身也是一个词典,只不过容量比较小.现实生活中有许多词典的应用: 拼写检查 数据库管理应用 ...

  7. 符号表实现(Symbol Table Implementations)

    符号表的实现有很多方式,下面介绍其中的几种. 乱序(未排序)数组实现 这种情况,不需要改变数组,操作就在这个数组上执行.在最坏的情况下插入,搜索,删除时间复杂度为O(n). 有序(已排序)数组实现 这 ...

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

    程序减肥,strip,eu-strip 及其符号表 我们要给我们生成的可执行文件和DSO瘦身,因为这样可以节省更多的磁盘空间,所以我们移除了debug信息,移除了符号表信息,同时我们还希望万一出事了, ...

  9. 《Algorithms 4th Edition》读书笔记——3.1 符号表(Elementary Symbol Tables)-Ⅳ

    3.1.4 无序链表中的顺序查找 符号表中使用的数据结构的一个简单选择是链表,每个结点存储一个键值对,如以下代码所示.get()的实现即为遍历链表,用equals()方法比较需被查找的键和每个节点中的 ...

随机推荐

  1. RVA到FOA的转换

    地址空间:这个地址空间指的是PE文件被加载到内存的空间,是一个虚拟的地址空间,之所以不是物理空间是因为数据在内存中的位置经常在变,这样既可以节约内存开支又可以避开错误的内存位置.这个地址空间的大小为4 ...

  2. Python+opencv+pyaudio实现带声音屏幕录制

    原文链接:https://blog.csdn.net/zhaoyun_zzz/article/details/84341801 Python+opencv+pyaudio实现带声音屏幕录制原创luke ...

  3. 第1节 IMPALA:6、yum源制作过程

    impala的安装:第一步:下载5个G的安装包,并且上传linux,解压第二步:安装httpd的服务,并启动,访问httpd就是访问我们linux的 /var/www/html这个路径下面的东西第三步 ...

  4. java文件的上传

    文件的上传和下载在web应用中是非常常用,也是非常有用的功能.  例如:发送电子邮件时可以同过上传附件发送文件,OA系统中可以通过上传文件来提交公文,社交网站通过上传图片来自定义头像等等.  例如:下 ...

  5. R 再也不用愁变量太多跑回归太麻烦!R语言循环常用方法总结

    在高维数据分析过程中,为了筛选出与目标结局相关的变量,通常会用到回归分析,但是因为自变量较多,往往要进行多次回归.这就是统计编程语言发挥作用的时候了 有些大神们认为超过3次的复制粘贴就可以考虑使用循环 ...

  6. django中使用ORM模型修改数据库的表名

    在django中,使用models.py创建好一张表后,如果不指定表的名字,那么表的名字就默认为 model_modelname 例如: class Book(models.Model): id = ...

  7. python集成开发环境Anaconda的安装

    参考博文: anaconda在Linux下的安装 Linux下anaconda3的安装 Anaconda的安装.启用及停用的步骤 Python学习之Anaconda的使用及配置方法 Anaconda ...

  8. 07.swoole学习笔记--tcp客户端

    <?php //创建tcp客户端 $client=new swoole_client(SWOOLE_SOCK_TCP); //连接服务器 $client->connect(,) or di ...

  9. mysql 分组查询入门

  10. 使用linux将一个服务器上的文件或者文件夹复制黏贴到另一个服务器上

    一.复制文件: (1)将本地文件拷贝到远程  scp 文件名 用户名@计算机IP或者计算机名称:远程路径 本地192.168.1.8客户端 scp /root/install.* root@10.12 ...