gdb调试原理及qemu中的gdbserver

(一)gdb调试原理

此部分转自:https://blog.csdn.net/u012658346/article/details/51159971     https://www.cnblogs.com/xsln/p/ptrace.html

gdb调试的原理是基于ptrace系统调用,ptrace()系统调用提供了一个方法,该方法使一个程序(追踪者)可以观察和控制另外一个程序(被追踪者)的执行,并检查和改变被追踪者的内存及寄存器。它主要用于实现断点调试和追踪系统调用。

当被追踪时,被追踪线程在接收信号时会被停止,即使那个信号是被忽略的也是如此(SIGKILL除外)。追踪程序会在一个调用waitpid(或者其他类wait系统调用)时收到通知,该调用会返回一个包含被追踪线程停止的原因的状态值。当被追踪线程停止时,追踪程序可以使用多种ptrace请求来检查和编辑被追踪线程。追踪程序可以让被追踪线程继续运行,有选择地忽略发过来的信号(甚至可以发送一个完全不同的信号给被追踪线程)

利用ptrace系统调用,可在被调试程序和gdb之间建立追踪关系。然后所有发送给被调试程序(被追踪线程)的信号(除SIGKILL)都会被gdb截获,gdb根据截获的信号,查看被调试程序相应的内存地址,并控制被调试的程序继续运行。

ptrace系统调用原型:

long ptrace(enum __ptrace_request request, pid_t pid,void *addr,void *data);

request参数的主要选项:
PTRACE_TRACEME:由子进程调用,表示本进程将被其父进程跟踪,交付给这个进程的所有信号,即使信号是忽略处理的(除SIGKILL之外),都将使其停止,父进程将通过wait()获知这一情况。

PTRACE_ATTACH: attach到一个指定的进程,使其成为当前进程跟踪的子进程,而子进程的行为等同于它进行了一次PTRACE_TRACEME操作。但是,需要注意的是,虽然当前进程成为被跟踪进程的父进程,但是子进程使用getppid()的到的仍将是其原始父进程的pid。当你在gdb中使用attach命令来跟踪一个指定进程/线程的时候,gdb就自动成为改进程的父进程,而被跟踪的进程则使用了一次PTRACE_TRACEME,gdb也就顺理成章的接管了这个进程。

PTRACE_CONT:继续运行之前停止的子进程。可同时向子进程交付指定的信号。

gdb三种调试方式:
1)attach并调试一个已经运行的进程:
确定需要进行调试的进程id,运行gdb,输入attch pid,如:gdb 12345。gdb将对指定进行执行如下操作:ptrace(PTRACE_ATTACH,pid,0,0), 建立自己与进程号为pid的进程间的跟踪关系。即利用PTRACE_ATTACH,使自己变成被调试程序的父进程。用attach建立起来的跟踪关系,可以调用ptrace(PTRACE_DETACH,pid,...)来解除。注意attach进程时的权限问题,如一个非root权限的进程是不能attach到一个root进程上的 。
2)运行并调试一个新的进程,利用fork+execve执行被测试的程序,子进程在执行execve之前调用ptrace(PTRACE_TRACEME),建立了与父进程(debugger)的跟踪关系:
运行gdb,通过命令行参数或file指定目标调试程序,如gdb ./test 
输入run命令,gdb执行下述操作:
通过fork()系统调用创建一个新进程
在新创建的子进程中调用ptrace(PTRACE_TRACEME,0,0,0)
在子进程中通过execv()系统调用加载用户指定的可执行文件
3)远程调试目标主机上新创建的进程
gdb运行在调试机,gdbserver运行在目标机,通过二者之间定义的数据格式进行通信

gdb调试基础--信号

gdb调试的实现都是建立在信号的基础上的,在使用参数为PTRACE_TRACEME或PTRACE_ATTACH的ptrace系统调用建立调试关系后,交付给目标程序的任何信号首先都会被gdb截获。 因此gdb可以先行对信号进行相应处理,并根据信号的属性决定是否要将信号交付给目标程序。

.断点原理:

1)    断点的实现原理,就是在指定的位置插入断点指令,当被调试的程序运行到断点的时候,产生SIGTRAP信号。该信号被gdb捕获并进行断点命中判定,当gdb判断出这次SIGTRAP是断点命中之后就会转入等待用户输入进行下一步处理,否则继续。

2)    断点的设置原理: 在程序中设置断点,就是先将该位置的原来的指令保存,然后向该位置写入int 3。当执行到int 3的时候,发生软中断,内核会给子进程发出SIGTRAP信号,当然这个信号会被转发给父进程。然后用保存的指令替换int3,等待恢复运行。

3)    断点命中判定:gdb把所有的断点位置都存放在一个链表中,命中判定即把被调试程序当前停止的位置和链表中的断点位置进行比较,看是断点产生的信号,还是无关信号。

4)    条件断点的判定:原理同3),只是恢复断点处的指令后,再多加一步条件判断。若表达式为真,则触发断点。由于需要判断一次,因此加入条件断点后,不管有没有触发到条件断点,都会影响性能。在x86平台,某些硬件支持硬件断点,在条件断点处不插入int    3,而是插入一个其他指令,当程序走到这个地址的时候,不发出int 3信号,而是先去比较一下特定寄存器和某个地址的内容,再决定是否发送int 3。因此,当你的断点的位置会被程序频繁地“路过”时,尽量使用硬件断点,会对提高性能有帮助

单步跟踪:

next指令可以实现单步调试,即每次只执行一行语句。一行语句可能对应多条及其指令,当执行next指令时,gdb会计算下一条语句对应的第一条指令的地址,然后控制目标程序走到该位置停止。

(二)qemu中的gdbserver

正常情况下进行远程调试需要被调试端安装有gdbserver程序,而qemu中内置了gdbserver模块,基于此可使用gdb实现对qemu虚拟机的远程调试,GDB/GDBSERVER调试模型的原理如下:

在GDB/GDBSERVER调试模型中,GDBSERVER是一个轻量级的GDB调试器,在调试过程中担任着调试代理的角色。在调试过程中,主机和目标机之间使用串口或者网络作为通信的通道。在主机上GDB通过这条通道使用一种基于ASCII的简单通讯协议RSP与在目标机上运行的GDBSERVER进行通讯。GDB发送指令,如内存、寄存器读写,GDBSERVER则首先与运行被调试程序映像的进程进行绑定,然后等待GDB发来的数据,对包含命令的数据包进行解析之后便进行相关处理,然后将结果返回给主机上的GDB。

RSP协议将GDB/GDBSERVER间通讯的内容更看做是数据包,数据包的内容都使用ASCII字符。每一个数据包都遵循这样的格式:$ <调试信息>#<校验码>.

如上图所示,包的内容会以16进制的形式来编码(enhex),#后面的两位数字是校验码,具体的计算方式是数据包中所有字符求和再用256求余数。而数据包的内容,也就是RSP协议的载体,将会是gdb接收的命令。接受方在收到数据包之后,对数据包进行校验,若正确回应“+”,反之回应“-”。

RSP 协议中定义的主要命令可以分为 3 类:

(1)寄存器/内存读写命令

命令 g: 读所有寄存器的值

命令 G:写所有寄存器的值

命令 P: 写某个寄存器

命令 m: 读某个内存单元

命令 M:写某个内存单元

(2)程序控制命令

命令?: 报告上一次的信号

命令 s: 单步执行

命令 c: 继续执行

命令 k: 终止程序

(3)其它命令

命令 O:控制台输出(Console Output )

命令 E:出错回应(Error response)

当主机使用gdb对运行虚拟机的qemu进程进行调试,qemu中内置的gdbserver与虚拟机进行绑定,使用上述模型的原理对虚拟机进行调试。如gdb调试端发送x/ <n/f/u> <addr>表示读取addr处的内容,命令经RSP协议封装成数据包发送至qemu的gdbserver端,gdbserver收到数据包后对其进行校验,校验成功后进行解析处理并返回至gdb客户端。

开启gdbserver之后,会等待来自gdb的连接请求,接收请求后会调用gdb_handlesig()等待stdin传来的gdb指令,调用gdb_read_byte()解析用户的输入,并对数据包进行校验,若校验正确调用gdb_handle_packet()进行gdb命令的处理。

如下解析字符为m时,表示读取某个内存单元,则调用函数target_memory_rw_debug()进行内存单元的读取,该函数最后调用cpu_memory_rw_debug()读取内存内容

当解析字符为g时,使用gdb_read_register读取寄存器信息,该函数会调用特定CPU类型的回调函数:

x86下调用如下函数,通过qemu为虚拟机维护的CPUX86State结构体得到虚拟机的寄存器信息:

类似地,插入一个断点时:

在kvm_enabled的情况下,调用kvm_insert_breakpoint:

该函数进行断点的插入并最终使用kvm_update_guest_debug向kvm更新客户机的debug状态,该函数调用kvm_invoke_set_guest_debug,进一步调用kvm_vcpu_ioctl(cpu, KVM_SET_GUEST_DEBUG,&dbg_data->dbg)执行ioctl至kvm中设置相关异常向量,BP(breakpoint,int3),DB(int 1)(插一句,可通过设置异常位图中的这两个位对上述指令进行拦截)

原文地址:https://www.cnblogs.com/ccxikka/p/10519518.html

时间: 2024-10-27 06:00:55

gdb调试原理及qemu中的gdbserver的相关文章

GDB调试原理——ptrace系统调用

本文由霸气的菠萝原创,转载请注明出处:http://www.cnblogs.com/xsln/p/ptrace.html 全部关于gdb的文章索引请点这里 引子: gdb基本上大家都在用,你有没有想过它的实现原理是什么?为什么它可以控制程序执行.中断.访问内存甚至直接使程序流程改变? 在使用gdb调试程序时,程序的进程状态是”T”,但又似乎并非接到了SIGSTOP信号,那么这个”T”是什么呢? 追根溯源,我们今天来研究一下Linux下这个强大的系统调用:ptrace() 首先,linux的进程状

Eclispe+qemu+gdb调试linux Kernel

单步调试kernel说明 恩,这个文档的目标是单步调试内核,从每一个工具软件的版本号到每一个命令,都有一个说明 ubuntu1204,32位 http://www.ubuntu.org.cn/download/desktop 用vmware虚拟机安装该系统. 用64位系统时,gdb有bug.报错信息为:xxx太长.所以建议用32位系统 编译kernel 3.5.4 下载内核的地址,北京交通大学的映像地址:http://mirror.bjtu.edu.cn/kernel/linux/kernel/

gdb调试之linux pc和linux arm环境下

Linux PC应用程序gdb调试: 1.查看core文件参数 [email protected]:~/File_unzip/cp_module$ ulimit -a core file size          (blocks, -c) 0        //产生core文件数目,不会产生core文件 data seg size           (kbytes, -d) unlimited scheduling priority             (-e) 0 file size 

用gdb调试游戏服务端

前言 说说gdb的重要性 一般来说,提到gdb,都是用命令来调试."命令",这个对于用户来说几乎等同于繁杂的词语.尽管事实确实如此,但实际的开发调试必须用到gdb.现在,大多数Linux系统是存在于服务器当中.我们想操作这些系统时,一般是通过Terminal来操作.也就是说这些Linux系统不具有图形界面.而调试一般分两部分,开发时调试和运行时调试.当我们的程序部署到Linux上时,那就需要忘记那该死图形调试器了. 说说写这篇文章的目的 昨天公司游戏的其中服务端崩溃了.我在调试时忘记了

使用gdb调试游戏服务器

前言 谈论gdb重要性 一般来说.提gdb,命令用于调试."命令",用户是几乎相同的复杂话.而事实确实如此,实际的开发调试必须用到gdb. 如今.大多数Linux系统是存在于server其中. 我们想操作这些系统时,通常是通过Terminal来操作.也就是说这些Linux系统不具有图形界面.而调试一般分两部分.开发时调试和执行时调试. 当我们的程序部署到Linux上时.那就须要忘记那该死图形调试器了. 说说写这篇文章的目的 昨天公司游戏的当中服务端崩溃了.我在调试时忘记了gdb命令-_

ubuntu: qemu+gdb 调试linux kernel 学习笔记

声明: 本笔记内容并非本人原创,90%来自网络资料的整合.同时,由于自己是刚刚接触qemu & gdbserver remote debug,本文也就算不得教程,仅供有缘人参考而已. ------------------------------------------------------------------------------------------------分割线-----------------------------------------------------------

用qemu与gdb调试linux kernel tcp/ip协议栈

description 用gdb debug linux kernel容易吗?其实要走到这步真的不容易啊,其实也难道是不难,就是要知道的东西太多了.用gdb debug linux kernel 可以有2中方式:UML和qemu方式,这里主要说qemu,从源码编译安装qemu很费劲. 准备环境 linux OS: Debian7.5-i386(当时最新的Wheezy,装在VMware10上,我用的在线安装,安装后以text方式跑起来,我的笔记本配置资源有限!) root fs:Debian-Wh

Linux下交叉编译gdb,gdbserver+gdb的使用以及通过gdb调试core文件

交叉编译gdb和gdbserver 1.下载gdb:下载地址为:http://ftp.gnu.org/gnu/gdb/按照一般的想法,最新版本越好,因此下载7.2这个版本.当然,凡事无绝对.我们以gdb-7.2.tar.bz2 这个文件为例.2.解压缩: $ tar jxvf gdb-7.2.tar.bz2 注:小技巧:Linux下一般压缩文件后缀为.tar.bz2和.tar.gz,它们解压命令有两三个选项是一致的: xf(v),前者再加上j选项,后者再加上z选项. 3.进入该目录 $ cd g

使用 gdb 调试运行中的 Python 进程

本文和大家分享的是使用 gdb 调试运行中的 Python 进程相关内容,一起来看看吧,希望对大家学习python有所帮助. 准备工作 安装 gdb 和 python2.7-dbg: $ sudo apt-get install gdb python2.7-dbg 设置 /proc/sys/kernel/yama/ptrace_scope: $ sudo su# echo 0 > /proc/sys/kernel/yama/ptrace_scope 运行 test.py: $ python te