当前位置: 首页 > news >正文

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()来创建一个子进程,这个时候子进程做两件事情:

  1. 调用系统函数ptrace(PTRACE_TRACEME,[其他参数]) 让父进程gdb追踪自己;
  2. 进而通过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件事情:

  1. 对第5行源码所对应的第10行汇编代码存储到断点链表中
  2. 在汇编代码的第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个操作:

  1. 把汇编代码中的第10行"INT3"替换为断点链表中原来的代码
  2. 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本地调试的底层调试原理&#xff0c;我们来看一下GDB是通过什么机制来控制被调试程序的执行顺序; 总结部分是断点调试的底层原理&#xff0c;可以直接跳转过去先看看大概…...

LC-1647. 字符频次唯一的最小删除次数(哈希+计数)

1647. 字符频次唯一的最小删除次数 难度中等56 如果字符串 s 中 不存在 两个不同字符 频次 相同的情况&#xff0c;就称 s 是 优质字符串 。 给你一个字符串 s&#xff0c;返回使 s 成为 优质字符串 需要删除的 最小 字符数。 字符串中字符的 频次 是该字符在字符串中的出现…...

HTTP状态码

100: 接受&#xff0c;正在继续处理 200: 请求成功&#xff0c;并返回数据 201: 请求已创建 202: 请求已接受 203: 请求成为&#xff0c;但未授权 204: 请求成功&#xff0c;没有内容 205: 请求成功&#xff0c;重置内容 206: 请求成功&#xff0c;返回部分内容 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决定&#xff08;echo $PATH) which命令格式&#xff1…...

update case when 多字段,多条件, mysql中case when用法

文章目录 前言 sql示例 普通写法&#xff1a; update case when写法 update case when 多字段写法 case when语法 case when 的坑 1、不符合case when条件但是字段被更新为null了 解决方法一&#xff1a;添加where条件 解决方法二&#xff1a;添加else 原样输出 2、同一条数据符…...

mysql隐式转换 “undefined“字符串匹配到mysql int类型0值字段

描述&#xff1a;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 语言开发的开源数据库&#xff08;BSD 许可&#xff09;&#xff0c;与传统数据库不同的是 Redis 的数据是存在内存中的&#xff08;内存数据库&#xff09;&#xff0c;读写速度非常快&#xff0c;被广泛应用于缓存方向。并且&#xff0c…...

InnoDB——详细解释锁的应用,一致性读,自增长与外键

一致性非锁定读 一致性的非锁定读&#xff08;consistent nonlocking read&#xff09;是指InnoDB存储引擎通过行多版本控制的方式读取当前执行时数据库中行的数据。 如果读取的行正在执行 行Delete或Update操作&#xff0c;这时读取操作不会因此去等待行上锁的释放。相反&…...

C++模板基础(四)

函数模板&#xff08;四&#xff09; ● 函数模板的实例化控制 – 显式实例化定义&#xff1a; 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官网下载社区版&#xff0c;这个版本本来就是免费的&#xff0c;而且功能其实已经够了 后续其他设置 首先&#xff0c;第一次启动时&#xff0c;记得在preference->interpreter中设置python环境&a…...

Linux命令·kill·killall

Linux中的kill命令用来终止指定的进程&#xff08;terminate a process&#xff09;的运行&#xff0c;是Linux下进程管理的常用命令。通常&#xff0c;终止一个前台进程可以使用CtrlC键&#xff0c;但是&#xff0c;对于一个后台进程就须用kill命令来终止&#xff0c;我们就需…...

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介绍头文件模块类定义对象构造初始化元素访问容器大小迭代器其他函数测试环境 系统&#xff1a;ubuntu 22.04.2 LTS 64位 gcc版本&#xff1a;11.3.0 编辑器&#xff1a;vsCode 1.76.2 array介绍 array是固定大小的序列式容器&#xff0c;它包含按严格…...

flask教程6:cookie和session

文章目录一、cookie1.1 什么是cookie&#xff1f;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协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、TCP/IP协议五层协议栈&#xff1b; 1.1 应用层协议&#xff1b; 二、传输层协议&#xff1b; 2.1 UDP协议&#xff1b; 2.2 TCP协议&#xff1b; 2.…...

模式识别 —— 第六章 支持向量机(SVM)与核(Kernel)

模式识别 —— 第六章 支持向量机&#xff08;SVM&#xff09;与核&#xff08;Kernel&#xff09; 文章目录模式识别 —— 第六章 支持向量机&#xff08;SVM&#xff09;与核&#xff08;Kernel&#xff09;硬间隔&#xff08;Hard-Margin&#xff09;软间隔&#xff08;Soft…...

总结 synchronized

目录synchronized的特性1. 互斥2. 刷新内存3. 可重入synchronized的使用1. 直接修饰普通方法2. 修饰静态方法3. 修饰代码块synchronized的锁机制基本特点关键锁策略 : 锁升级synchronized的特性 1. 互斥 synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized…...

360周鸿祎又“开炮”:GPT 6-8就将产生自主意识!我们来测算一下对错

‍数据智能产业创新服务媒体——聚焦数智 改变商业近日&#xff0c;360的周鸿祎放言“GPT6到GPT8人工智能将会产生意识&#xff0c;变成新的物种。未来&#xff0c;人工智能大语言模型有可能实现自我进化&#xff0c;自动更新系统和自我升级&#xff0c;或者指数级进化能力&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 …...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...