Debug调试原理(一)

 一. 调试符号:

调试符号可将进程的指令与对应的源代码行数或表达式进行关联,或者从源程序声明的结构化数据对象的角度对一块内存进行描述。通过这些映射,调试器可在源代码层面上执行用户命令来查询和操作进程。

g++ -g cui.cpp cui.o 即可生成带调试符号的.o文件

简单理解为: 调试符号是源程序和程序运行实例的原始内存内容之间的桥梁.

调试符号分类:

  1. 全局函数和变量:跨越编译单元的可见的全局符号和类型、位置信息。全局变量的地址相对于其所属模块的基地址是固定的。因其可见性、位置固定和声明周期长,因此在程序未显示和隐式卸载API时,都可观察数据、修改和设置断点。
  2. 源文件和行信息:函数通常是占据进程连续内存空间的最小可执行代码单元,因此源文件和行号的调试符号会记录每个函数开始和结束的地址,在release版本中,代码往往不是按照文件中声明的顺序来排列的。

  3. 类型信息:  类型调试符号描述了数据类型的组合关系和属性,包括基本类型的数据,或其他数据的聚合。对于复合类型,调试符号包含每个子字段的名字、大小和相对于整个结构体开头的偏移量。  可以尝试在cpp文件中声明一个类,使用命令: -fdump-lang-class 来打印出在编译器视角中的类的信息

  4. 静态函数和局部变量:  静态函数和局部变量只在特定的作用域可见,多数在栈上分配,在未执行到该程序时,位置都是不确定的。

  5. 架构和编译器依赖信息:  某些调试功能与特定的架构和编译器有关。

  DWARF格式:

            生成汇编:一般常用的是树形结构的组织调试符号,为了更好的学习 需要在编译时生成汇编文件。

  编译命令-S :g++ -g -S cui.cpp        

源代码:
int Gloint = 1;

int Glofun(int i)
{
    return i+Gloint;
}

int main()
{return 0;}

生成的汇编代码:

	.file	"foo.cpp"     #文件名
	.text			 #代码段
.Ltext0:			 #标签后续跳转代码用
	.globl	Gloint		 #全局符号
	.data			 #数据段,存放已初始化的全局和静态变量
	.align 4		 #对齐要求为4字节
	.type	Gloint, @object #Gloint是一个对象
	.size	Gloint, 4     #Gloint的大小为4字节
Gloint:
	.long	1		 #定义Gloint并为其分配一个4字节的值1
	.text			 #回到代码段
	.globl	_Z6Glofuni     #全局函数。
	.type	_Z6Glofuni, @function
_Z6Glofuni:			 #函数_Z6Glofuni的起始点
.LFB0:
	.file 1 "foo.cpp"
	.loc 1 4 1		 
	.cfi_startproc
	pushq	%rbp		#栈针后边说
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	%edi, -4(%rbp)  #将传入参数edi中 移动到栈上 -4%rbp位置
	.loc 1 5 13
	movl	Gloint(%rip), %edx #Gloint地址 加载到edx中
	.loc 1 5 14
	movl	-4(%rbp), %eax    #加载传入参数eax
	addl	%edx, %eax	 	#  与edx相加
	.loc 1 6 1
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:					#结束
	.size	_Z6Glofuni, .-_Z6Glofuni
	.globl	main
	.type	main, @function
main:
.LFB1:
	.loc 1 9 1
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	.loc 1 9 9
	movl	$0, %eax
	.loc 1 9 11
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
.Letext0:
	.section	.debug_info,"",@progbits
.Ldebug_info0:
	.long	0x95
	.value	0x4
	.long	.Ldebug_abbrev0
	.byte	0x8
	.uleb128 0x1
	.long	.LASF0
	.byte	0x4
	.long	.LASF1
	.long	.LASF2
	.quad	.Ltext0
	.quad	.Letext0-.Ltext0
	.long	.Ldebug_line0
	.uleb128 0x2
	.long	.LASF3
	.byte	0x1
	.byte	0x1
	.byte	0x5
	.long	0x43
	.uleb128 0x9
	.byte	0x3
	.quad	Gloint
	.uleb128 0x3
	.byte	0x4
	.byte	0x5
	.string	"int"
	.uleb128 0x4
	.long	.LASF4
	.byte	0x1
	.byte	0x8
	.byte	0x5
	.long	0x43
	.quad	.LFB1
	.quad	.LFE1-.LFB1
	.uleb128 0x1
	.byte	0x9c
	.uleb128 0x5
	.long	.LASF5
	.byte	0x1
	.byte	0x3
	.byte	0x5
	.long	.LASF6
	.long	0x43
	.quad	.LFB0
	.quad	.LFE0-.LFB0
	.uleb128 0x1
	.byte	0x9c
	.uleb128 0x6
	.string	"i"
	.byte	0x1
	.byte	0x3
	.byte	0x10
	.long	0x43
	.uleb128 0x2
	.byte	0x91
	.sleb128 -20
	.byte	0
	.byte	0
	.section	.debug_abbrev,"",@progbits
.Ldebug_abbrev0:
	.uleb128 0x1
	.uleb128 0x11
	.byte	0x1
	.uleb128 0x25
	.uleb128 0xe
	.uleb128 0x13
	.uleb128 0xb
	.uleb128 0x3
	.uleb128 0xe
	.uleb128 0x1b
	.uleb128 0xe
	.uleb128 0x11
	.uleb128 0x1
	.uleb128 0x12
	.uleb128 0x7
	.uleb128 0x10
	.uleb128 0x17
	.byte	0
	.byte	0
	.uleb128 0x2
	.uleb128 0x34
	.byte	0
	.uleb128 0x3
	.uleb128 0xe
	.uleb128 0x3a
	.uleb128 0xb
	.uleb128 0x3b
	.uleb128 0xb
	.uleb128 0x39
	.uleb128 0xb
	.uleb128 0x49
	.uleb128 0x13
	.uleb128 0x3f
	.uleb128 0x19
	.uleb128 0x2
	.uleb128 0x18
	.byte	0
	.byte	0
	.uleb128 0x3
	.uleb128 0x24
	.byte	0
	.uleb128 0xb
	.uleb128 0xb
	.uleb128 0x3e
	.uleb128 0xb
	.uleb128 0x3
	.uleb128 0x8
	.byte	0
	.byte	0
	.uleb128 0x4
	.uleb128 0x2e
	.byte	0
	.uleb128 0x3f
	.uleb128 0x19
	.uleb128 0x3
	.uleb128 0xe
	.uleb128 0x3a
	.uleb128 0xb
	.uleb128 0x3b
	.uleb128 0xb
	.uleb128 0x39
	.uleb128 0xb
	.uleb128 0x49
	.uleb128 0x13
	.uleb128 0x11
	.uleb128 0x1
	.uleb128 0x12
	.uleb128 0x7
	.uleb128 0x40
	.uleb128 0x18
	.uleb128 0x2117
	.uleb128 0x19
	.byte	0
	.byte	0
	.uleb128 0x5
	.uleb128 0x2e
	.byte	0x1
	.uleb128 0x3f
	.uleb128 0x19
	.uleb128 0x3
	.uleb128 0xe
	.uleb128 0x3a
	.uleb128 0xb
	.uleb128 0x3b
	.uleb128 0xb
	.uleb128 0x39
	.uleb128 0xb
	.uleb128 0x6e
	.uleb128 0xe
	.uleb128 0x49
	.uleb128 0x13
	.uleb128 0x11
	.uleb128 0x1
	.uleb128 0x12
	.uleb128 0x7
	.uleb128 0x40
	.uleb128 0x18
	.uleb128 0x2117
	.uleb128 0x19
	.byte	0
	.byte	0
	.uleb128 0x6
	.uleb128 0x5
	.byte	0
	.uleb128 0x3
	.uleb128 0x8
	.uleb128 0x3a
	.uleb128 0xb
	.uleb128 0x3b
	.uleb128 0xb
	.uleb128 0x39
	.uleb128 0xb
	.uleb128 0x49
	.uleb128 0x13
	.uleb128 0x2
	.uleb128 0x18
	.byte	0
	.byte	0
	.byte	0
	.section	.debug_aranges,"",@progbits
	.long	0x2c
	.value	0x2
	.long	.Ldebug_info0
	.byte	0x8
	.byte	0
	.value	0
	.value	0
	.quad	.Ltext0
	.quad	.Letext0-.Ltext0
	.quad	0
	.quad	0
	.section	.debug_line,"",@progbits
.Ldebug_line0:
	.section	.debug_str,"MS",@progbits,1
.LASF1:
	.string	"foo.cpp"
.LASF2:
	.string	"/home/Linux/Desktop/TEST"
.LASF6:
	.string	"_Z6Glofuni"
.LASF3:
	.string	"Gloint"
.LASF5:
	.string	"Glofun"
.LASF4:
	.string	"main"
.LASF0:
	.string	"GNU C++14 8.3.0 -mtune=generic -march=x86-64 -g"
	.ident	"GCC: (Uos 8.3.0.3-3+rebuild) 8.3.0"
	.section	.note.GNU-stack,"",@progbits

然后导出调试符号:

       g++ -g foo.cpp -o foo.o

       readelf --debug-dump foo.o

   该命令会输出目标文件foo.o中所以调试符号

  重点看下 .debug_info节就行 上图片:

  参数名称 i ,在第一个文件中 第五行 参数类型 0x43 在内存中偏移量是2

  接下来看下-debug_info中的原始数据 反一下:

          objdump -s --section=.debug_info foo.o

 接下来结合 foo,s文件中找到传入参数i的调试符号

这样就找到了接口位置

然后来说说-debug_line :原理有点复杂没看懂:大概意思是:基于设置初始值的操作码,如初始指令地址,每当行号发生变化时操作码将操作地址移动一个变化量。调试器执行操作码,并在每次状态变化时向状态表中加一行。

总之最终就是调试器基于这些东西构建一个表,表的关键在于将指令映射到源代码行号中。每个状态都包括一个指令地址(以函数开头偏移量表示)及相应的源代码行号和文件名。 表示该接口从和地址开始 到和地址结束,及从哪行到哪行

.debug_frame :栈针和寄存器的分配方式:函数 回滚的重要依据

这部分看看栈帧相关和函数调用即可 不想过多描述

.debug_loc: 包含宏的表达式
.debug_pubnames: 全局变量和函数的查找表
.debug_aranges : 包含一系列地址长度对,用于说明编译单元地址范围

  • 22
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值