当前位置: 首页 > 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 …...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁

赛门铁克威胁猎手团队最新报告披露&#xff0c;数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据&#xff0c;严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能&#xff0c;但SEMR…...

对象回调初步研究

_OBJECT_TYPE结构分析 在介绍什么是对象回调前&#xff0c;首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例&#xff0c;用_OBJECT_TYPE这个结构来解析它&#xff0c;0x80处就是今天要介绍的回调链表&#xff0c;但是先不着急&#xff0c;先把目光…...

GB/T 43887-2024 核级柔性石墨板材检测

核级柔性石墨板材是指以可膨胀石墨为原料、未经改性和增强、用于核工业的核级柔性石墨板材。 GB/T 43887-2024核级柔性石墨板材检测检测指标&#xff1a; 测试项目 测试标准 外观 GB/T 43887 尺寸偏差 GB/T 43887 化学成分 GB/T 43887 密度偏差 GB/T 43887 拉伸强度…...