Linux gdb调试底层原理
@TOC
前言
linux下gdb调试程序操作过程参考本人文章:gdb调试操作; 这里不再叙述;
本文主要内容是介绍GDB本地调试的底层调试原理,我们来看一下GDB是通过什么机制来控制被调试程序的执行顺序;
总结部分是断点调试的底层原理,可以直接跳转过去先看看大概框架!
本地调试
本地调试:调试程序和被调试程序运行在同一台电脑中。
远程调试
远程调试:调试程序运行在一台电脑中,被调试程序运行在另一台电脑中。
关于可视化调试程序并不是重点,它只是一个用来封装GDB的外壳而已,我们通过它和gdb调试程序交互。
我们既可以用黑乎乎的终端窗口来手动输入调试命令;也可以选择集成开发环境(IDE),这个IDE中已经嵌入了器调试,
GDB调试指令
随便贴几个指令,等会可以介绍到;
每一个调试指令都有很多的命令选项,例如断点相关的就包括:设置断点、删除断点、条件断点、临时停用启用等等。这篇文章的重点是理解gdb底层的调试机制,所以应用层的这些指令的使用方法就不再列出了,网络上的资源很多。
GDB与被调试程序之间的关系
为了方便描述,先写一个最最简单的C程序:
#include <stdio.h>int main(int argc, char *argv[])
{int a = 1;int b = 2;int c = a + b;printf("c = %d \n", c);return 0;
}
编译命令:
$ gcc -g test.c -o test //记得-g选项,生成debug版的可执行程序
我们对可执行程序 test 进行调试,输入命令:
$ gdb ./test
输出如下:
在最后一行可以看到光标在闪烁,这是gdb程序在等着我们给它下达调试命令呢。
当上面这个黑乎乎的终端窗口在执行gdb ./test的时候,在操作系统里发生了很多复杂的事情:
GDB调试程序时执行的两个动作
系统首先会启动gdb调试进程,这个进程会调用系统函数fork()来创建一个子进程,这个时候子进程做两件事情:
- 调用系统函数ptrace(PTRACE_TRACEME,[其他参数]) 让父进程gdb追踪自己;
- 进而通过exec来加载、执行可执行程序test,那么test程序就在这个子exec加载的子进程中开始执行了。
ptrace系统调用
铺垫了半天,终于轮到主角登场了,那就是系统调用函数ptrace(其中的参数后面会解释),正是在它的帮助下,gdb才拥有了强大的调试能力。函数原型是:
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
ptrace系统函数是Linux内核提供的一个用于进程跟踪的系统调用,通过它,一个进程(gdb)可以读写另外一个进程(test)的指令空间、数据空间、堆栈和寄存器的值。而且gdb进程接管了test进程的所有信号,也就是说系统向test进程发送的所有信号,都被gdb进程接收到,这样一来,test进程的执行就被gdb控制了,从而达到输入各种gdb指令,调试控制程序的目的。
注意,ptrace是子进程调用的,让父进程gdb追踪自己,然后exec替换目标调试程序;
也就是说,如果没有gdb调试,操作系统与目标进程之间是直接交互的;
如果使用gdb来调试程序,那么操作系统发送给目标进程的信号就会被gdb截获,gdb根据信号的属性来决定:在继续运行目标程序时是否把当前截获的信号转交给目标程序,如此一来,目标程序就在gdb发来的信号指挥下进行相应的动作。
剖析GDB如何实现断点指令(调试的底层原理)
曾经面试被问到,调试的程序为何在断点处停下?这个经常使用但是不知道底层的点,让我苦恼了很久,今天配合gdb进程和源进程的关系,可以好好分析一下断点的底层原理!
以下代码实例,gdb使用break(b)命令来研究下底层调试原理:
#include <stdio.h>int main(int argc, char *argv[])
{int a = 1;int b = 2;int c = a + b;printf("c = %d \n", c);return 0;
}
- gcc -S test.c; cat test.S 查看反汇编代码
、
上面说到,在执行gdb ./test之后,gdb就会fork出一个子进程,这个子进程首先调用ptrace然后exec执行test程序,这样就准备好调试环境了。
在调试窗口输入设置断点指令“break 5”,此时gdb做2件事情:
- 对第5行源码所对应的第10行汇编代码存储到断点链表中。
- 在汇编代码的第10行,插入中断指令INT3,也就是说:汇编代码中的第10行被替换为INT3。
INT 3指令的操作码为0xcc ,是一种专门用来调试的软中断指令,也称断点指令,cpu执行它之后,OS会发送一个SIGTRAP 3号信号给源程序; gdb捕获到之后,检测到断点会将调试的程序“挂起”暂停,进入TASK_TRACED 的T状态,等待gdb程序进一步调试;下面会详细介绍;
然后,在调试窗口继续输入执行指令“run”指令(一直执行,直到遇到断点就暂停),汇编代码中PC指针执行到第10行时,发现是INT3(中断陷阱)指令,于是操作系统就发送一个SIGTRAP信号给test进程。
此刻,第10行汇编代码被执行过了,PC指针就指向第11行了
上面已经说过,操作系统发给test的任何信号,都被gdb接管了,也就是说gdb会首先接收到这SIGTRAP个信号,gdb发现当前汇编代码执行的是第10行,于是到断点链表中查找,发现断点链表中存储了第10行的代码,说明第10行被设置了断点。于是gdb将调试的test进程挂起(进入TASK_TRACED ; T状态)! 然后又做了2个操作:
- 把汇编代码中的第10行"INT3"替换为断点链表中原来的代码。
- 把 PC 指针回退一步,也即是设置为指向第10 行刚才替换进来的代码。
然后,gdb继续等待用户的调试指令… 此时test进程处于挂起状态
此刻,就相当于下一条执行的指令是汇编代码中的第10行,源程序第5行; 从我们调试者角度看,就是被调试程序在源程序第5行断点处暂停了下来,此时我们可以继续输入其他调试指令来debug,比如:查看变量值、info… 查看堆栈信息、bt… 修改局部变量的值等等。也正是因为发现中断之后的恢复替换代码,PC指针回退的操作,保证了我们中断这一行之前替换掉的代码能被正常恢复和执行!
进一步剖析next指令的调试
还是以刚才的源代码和汇编代码为例,假设此时程序停止在源码的第6行,即汇编代码的第11行:
在调试窗口输入单步执行指令next,我们的目的是执行一行代码,也就是把源码中第6行代码执行完,然后停止在第7行。
gdb在接收到next执行时,会计算出第7行源码(执行完第六行应该停在第七行源码的位置),应该对应到汇编代码的第14行,于是gdb就控制汇编代码中的PC指针一直执行,直到第13行执行结束,也就是PC指向第14行时,就停止下来,然后继续等待用户输入调试指令进一步调试。
通过break和next这2个调试指令还有gdb调试进程和源程序的关系,以及OS系统的参与,我们已经明白了gdb中是如何处理调试指令。当然,gdb中的调试指令还有很多,包括更复杂的获取堆栈信息、修改变量的值等等,有兴趣的小伙伴可以继续深入跟踪。
如何对正在运行的进程调试?
上面说的都是对未运行的进程调试,那么怎么对正在运行的服务调试呢?:
如果想对一个已经执行的进程B进行调试,那么就要在gdb这个父进程中调用ptrace(PTRACE_ATTACH,[其他参数])(调试未运行的是子进程调用这个ptrace的,这点区分下);
此时,gdb进程会attach(绑定)到已经执行的进程B,gdb把进程B收养成为自己的子进程,对于子进程B来说等同于它自己进行了一次 PTRACE_TRACEME操作。
此时gdb进程会发送SIGTRAP信号给子进程B,子进程B接收到SIGTRAP信号后,就会暂停执行进入TASK_TRACED状态,表示自己准备好被调试了。等待下一步调试程序gdb的操作
总结
假设我们想在第五行打断点停下,会发生以下事情:
启动gdb+调试程序,gdb调用fork创建子进程,这个子进程调用ptrace系统调用让gdb父进程追踪自己,紧接着进行exec程序替换将调试程序test加载进来,实现了gdb程序对test的控制权;
接着输入 b 5 命令之后 : 1.记录第5行的汇编代码到断点链表(用于确认断点和恢复代码); 2.汇编代码中INT3中断指令替换(一种软中断,中断陷阱);
接着输入 r 指令之后:调试程序运行到了INT3位置以后,OS识别到中断INT3,发送SIGTRAP信号,被调试程序gdb先拿到! 在刚才的断点列表检索,发现存在这个设置的断点,直接挂起程序,然后 1.PC–,2.第五行的汇编原始代码换回来,接着等到gdb下一步操作(比如:查看变量值、查看堆栈信息、修改局部变量的值等等。)
相关文章:
Linux gdb调试底层原理
TOC 前言 linux下gdb调试程序操作过程参考本人文章:gdb调试操作; 这里不再叙述; 本文主要内容是介绍GDB本地调试的底层调试原理,我们来看一下GDB是通过什么机制来控制被调试程序的执行顺序; 总结部分是断点调试的底层原理,可以直接跳转过去先看看大概…...
LC-1647. 字符频次唯一的最小删除次数(哈希+计数)
1647. 字符频次唯一的最小删除次数 难度中等56 如果字符串 s 中 不存在 两个不同字符 频次 相同的情况,就称 s 是 优质字符串 。 给你一个字符串 s,返回使 s 成为 优质字符串 需要删除的 最小 字符数。 字符串中字符的 频次 是该字符在字符串中的出现…...
HTTP状态码
100: 接受,正在继续处理 200: 请求成功,并返回数据 201: 请求已创建 202: 请求已接受 203: 请求成为,但未授权 204: 请求成功,没有内容 205: 请求成功,重置内容 206: 请求成功,返回部分内容 301: 永久性重定…...
【Linux】初见“which命令”,“find命令”以及linux执行命令优先级
文章目录1.which命令1.1 whereis命令1.2 locate命令1.3 搜索文件命令总结2.find命令2.1 find之exec用法2.2 管道符之xargs用法3 Linux常用命令4.命令执行优先级1.which命令 查找命令文件存放目录 搜索范围由环境变量PATH决定(echo $PATH) which命令格式࿱…...
update case when 多字段,多条件, mysql中case when用法
文章目录 前言 sql示例 普通写法: update case when写法 update case when 多字段写法 case when语法 case when 的坑 1、不符合case when条件但是字段被更新为null了 解决方法一:添加where条件 解决方法二:添加else 原样输出 2、同一条数据符…...
mysql隐式转换 “undefined“字符串匹配到mysql int类型0值字段
描述:mysql 用字符串搜索 能搜到int类型查询结果 mysql int类型条件用字符串查询 table: CREATE TABLE all_participate_records (id bigint unsigned NOT NULL AUTO_INCREMENT,created_at datetime(3) DEFAULT NULL,updated_at datetime(3) DEFAULT NULL,deleted…...
Redis八股文
1.Redis是什么? Redis 是一个基于 C 语言开发的开源数据库(BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的(内存数据库),读写速度非常快,被广泛应用于缓存方向。并且,…...
InnoDB——详细解释锁的应用,一致性读,自增长与外键
一致性非锁定读 一致性的非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制的方式读取当前执行时数据库中行的数据。 如果读取的行正在执行 行Delete或Update操作,这时读取操作不会因此去等待行上锁的释放。相反&…...
C++模板基础(四)
函数模板(四) ● 函数模板的实例化控制 – 显式实例化定义: template void fun(int) / template void fun(int) //header.h template<typename T> void fun(T x) {std::cout << x << std::endl; }//main.cpp #include&quo…...
pycharm使用记录
文章目录下载安装后续其他设置编辑器设置关于debug下载安装 直接去pycharm官网下载社区版,这个版本本来就是免费的,而且功能其实已经够了 后续其他设置 首先,第一次启动时,记得在preference->interpreter中设置python环境&a…...
Linux命令·kill·killall
Linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令。通常,终止一个前台进程可以使用CtrlC键,但是,对于一个后台进程就须用kill命令来终止,我们就需…...
Linux /proc/version 文件解析
/proc/version文件里面的内容: Linux version 4.14.180-perf (oe-user@oe-host) (clang version 10.0.5 for Android NDK, GNU ld (GNU Binutils) 2.29.1.20180115) #1 SMP PREEMPT Wed Mar 29 18:55:02 CST 2023 /proc/version文件里面记录了如下内容: 1、Linux kernel的…...
【Django 网页Web开发】15. 实战项目:管理员增删改查,md5密码和密码重置(08)(保姆级图文)
目录1. model编写数据表2. 管理员列表2.1 admin.py视图文件2.2 admin_list.html2.3 url.py2.4 最终效果3. 管理员添加3.0 md5包的书写3.1 form.py表单组件3.2 admin.py视图文件3.3 引入公共的添加数据html3.4 url.py3.5 最终效果4. 管理员编辑4.0 form表单组件4.1 admin.py视图…...
STL容器之<array>
文章目录测试环境array介绍头文件模块类定义对象构造初始化元素访问容器大小迭代器其他函数测试环境 系统:ubuntu 22.04.2 LTS 64位 gcc版本:11.3.0 编辑器:vsCode 1.76.2 array介绍 array是固定大小的序列式容器,它包含按严格…...
flask教程6:cookie和session
文章目录一、cookie1.1 什么是cookie?1.2 使用cookie1.2.1 设置cookie1.2.2设置cookie的有效期1.2.3在Flask中查询cookie1.2.4删除cookie二、session2.1实现session的两种思路2.1.1 第一种2.1.2 第二种2.2使用session2.2 .1设置session2.2.2 设置有效期2.2.3 获取se…...
【JavaEE初阶】第六节.网络原理TCP/IP协议
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、TCP/IP协议五层协议栈; 1.1 应用层协议; 二、传输层协议; 2.1 UDP协议; 2.2 TCP协议; 2.…...
模式识别 —— 第六章 支持向量机(SVM)与核(Kernel)
模式识别 —— 第六章 支持向量机(SVM)与核(Kernel) 文章目录模式识别 —— 第六章 支持向量机(SVM)与核(Kernel)硬间隔(Hard-Margin)软间隔(Soft…...
总结 synchronized
目录synchronized的特性1. 互斥2. 刷新内存3. 可重入synchronized的使用1. 直接修饰普通方法2. 修饰静态方法3. 修饰代码块synchronized的锁机制基本特点关键锁策略 : 锁升级synchronized的特性 1. 互斥 synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized…...
360周鸿祎又“开炮”:GPT 6-8就将产生自主意识!我们来测算一下对错
数据智能产业创新服务媒体——聚焦数智 改变商业近日,360的周鸿祎放言“GPT6到GPT8人工智能将会产生意识,变成新的物种。未来,人工智能大语言模型有可能实现自我进化,自动更新系统和自我升级,或者指数级进化能力&am…...
python——飞机大战小游戏
目录 1、导入模块 2、窗口操作 3、事件操作 4、长按事件 5、添加游戏背景 6、添加英雄飞机 7、获取飞机的图片矩形 8、基本游戏窗口 9、添加游戏窗口图片 10、英雄飞机登场 11、英雄飞机装备子弹并发射 1、enemy_plane 2、game_main 3、game_map 4、game_score …...
数组(完全二叉树)向下建堆法与堆排序O(N*logN)
TIPS AdjustUp & AdjustDown向上调整AdjustUp与向下调整AdjustDown的参数是一个数组(完全二叉树)需要进行调整操作的数值的下标/一个数组(完全二叉树)堆元素个数需要调整操作的数值的下标。实际上就是对完全二叉树当中的某一点…...
Lua require 函数使用
从 Lua 的用户文档中我们知道 require("modName") 函数是用来加载模块的,而如果这个modName已经用require 加载过的,再调用require时,将直接返回模块的值。因为函数首先查找 package.loaded 表, 检测 modName 是否被加载…...
【面试】如何定位线上问题?
这个面试题我在两年社招的时候遇到过,前几天面试也遇到了。我觉得我每一次都答得中规中矩,今天来梳理复盘下,下次又被问到的时候希望可以答得更好。 下一次我应该会按照这个思路去答: 1、如果线上出现了问题,我们更多…...
字节二面,原来我对自动化测试的理解太浅了
如果你入职一家新的公司,领导让你开展自动化测试,作为一个新人,你肯定会手忙脚乱,你会如何落地自动化测试呢? 01 什么是自动化 有很多人做了很长时间的自动化但却连自动化的概念都不清楚,这样的人也是很悲…...
Android11.0 应用升级成功后立即断电重启,版本恢复
问题:客户反馈内置的应用升级成功后立刻断电重启,应用的版本被恢复。 使用adb命令升级客户应用,查看版本显示已更新,/data/system目录下packages.xml和packages.xml中应用版本信息均已更新 C:\Users\dell>adb shell dumpsys …...
关于python常用软件用法:Pycharm 常用功能
人生苦短,我用python 一.Pycharm的基本使用 1.在Pycharm下为你的Python项目配置Python解释器 (1).Setting>Project Interpreter>源码资料电子书:点击此处跳转文末名片获取 二.在Pycharm下创建Python文件、Python模块 1.File>New&g…...
SOLIDWORKS你不知道的小技巧
◉ SOLIDWORKS圆弧长度标注点智能标注,再选中该圆弧,然后分别点圆弧的两个端点,点击左键可以标注圆弧长度。◉ SOLIDWORKS强力裁剪剪裁实体中的强劲剪裁,除了可以裁剪实体外,还可以任意延伸实体。◉ SOLIDWORKS转折线转…...
有了HTTP,为啥还要用RPC
既然有 HTTP 请求,为什么还要用 RPC 调用? 一直以来都没有深究过RPC和HTTP的区别,不都是写一个服务然后在客户端调用么? HTTP和RPC最本质的区别,就是 RPC 主要是基于 TCP/IP 协议的,而 HTTP 服务主要是基…...
[leetcode] 动态规划
背包 先啃懂 背包九讲 01背包,即物品有限。 for 物品for 容量(倒序)P1048 [NOIP2005 普及组] 采药 [ 原题 | 题解 ] P1049 [NOIP2001 普及组] 装箱问题 [ 原题 | 题解 ] P1507 NASA的食物计划 [ 原题 | 题解 ] P1510 精卫填海 [ 原题 | 题…...
科大奥瑞物理实验——热电偶特性及其应用研究
实验名称:热电偶特性及其应用研究 1. 实验目的: 掌握电位差计的工作原理和结构特点;了解温差电偶测温的原理和方法;学会电位差计的使用及注意事项。 2. 实验器材: 电位差计 标准电池 光电检流计 稳压电源 温差电偶…...
郑州即将迎来全面解封/抖音关键词优化
一、概述。 上一篇博客讲述了用注解的形式实现AOP如今讲述第二种AOP实现的方式利用XML来实现AOP。二、代码演示。 准备工作參照上一篇博客《菜鸟学习Spring——60s使用annotation实现简单AOP》 文件夹结构: 事实上比起上一篇博客中用annotation来实现AOP的方式我们仅…...
内蒙古建设厅门户网站/关键字排名优化工具
Aware XXXAware在Spring里表示对XXX可以感知,通俗点解释就是:如果在某个类里边想要使用spring的一些东西,就可以通过实现XXXAware接口告诉Spring, Spring看到后就会给你送过来,而接收的方式是通过实现接口唯一的方法se…...
wordpress添加中文语言/电商平台发展现状与趋势
摆脱这些坏习惯可以带来意想不到的效果:编写出更优质精简的代码。以下为译文:我们都有过这样的经历:在妈妈不注意的时候偷吃一块饼干;聚餐时过量饮酒;把车停在超时停车的停车位上——我们甚至在死亡边缘疯狂试探。是的…...
wordpress菜单保存不/网站开发教程
1.作用域 作用域是根据名称找变量的一套规则。 变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。 引擎在查找变量时执行怎样的…...
做营销网站策划有什么前景/百度推广怎么才能效果好
第四章 多自由度系统的振动 1. 主阻尼矩阵 主阻尼矩阵Cp一般不是对角矩阵 方1 只保留其对角元素,Cpi为第i阶主振型的阻尼系数,第i阶模态阻尼 Cpi/Mpi 2*ξi*wi,ξi为第i阶振型阻尼比,第i阶模态阻尼比 方2 比例阻尼 Ca*Mb*K …...
怎么建论坛网站/目前病毒的最新情况
某少年宫引进了一批机器人小车。可以接受预先输入的指令,按指令行动。小车的基本动作很简单,只有3种:左转(记为L),右转(记为R),向前走若干厘米(直接记数字&am…...