1、上篇主要对GDB的使用技巧进行了一个简单总结,其实这里的内容是和上篇总结的内容放在一块的,但是考虑到GDB的实现技术重要性和意义,所以单独拿出来,当前只做皮毛性的记录,留待以后进一步补充内容。
2、GDB基本组成:
GDB由三个部分组成:
(1)用户接口user interface,除支持传统的CLI接口还支持mi接口(ddd等工具使用)
(2)符号处理层symbol handling,当gdb ./debugme后GDB会读取文件的符号信息,之后的原代码,变量/函数/类型的显示都由该部分进行(everything you can do without live process)。
(3)目标系统处理层target system handling。包括执行控制,断点设置,单步执行,堆栈分析等操作都有该部分来进行。
3、GDB各部分的实现:
(1)用户接口层(CLI)的实现很显然要用到readline/history库,而图形界面mi则需要用到:GNU ncurses库。
参考资料:
http://www.gnu.org/software/ncurses/ (2)符号处理层则需要使用到:BFD/Opcodes库,分别用来读取分析ELF/Core文件,反汇编.
参考资料:
http://www.xfocus.net/articles/200109/265.html (3)目标系统控制层:用ptrace系统调用来实现对其他进程的执行控制,检查和改变其核心映像以及寄存器等操作。
4、后端(目标系统控制层)实现:
(1)内核在执行用户请求的系统调用之前回检查当前进程是否处于被“跟踪”状态,如果是的话内核暂停当前进程并将控制权交给调试进程,使跟踪调试进程可以查看甚至修改被调试进程的内存,寄存器等数据。而ptrace函数的作用就是告诉内核在执行子进程的系统调用之前做的动作。所有的动作都可以通过request进行传入。
(2)设置断点原理:通过查找输入的断点和具体代码位置对应起来,并在该位置替换为一条断点指令,并且保存以前的指令,到目标程序运行到该断点处时,产生SIGTRAP信号,该信号被GDB捕获,GDB查找断点列表来确定是否命中断点。继续执行的时候则会把保存的指令重新放回并执行。n/s/ni/si/finish/uitil也会自动设置断点。
(3)内核传递给被调试进程所有的信号,都会先传递给GDB再由gdb采取定义的动作来和被调试进程之间进行相互协调操作。gdb暂停目标程序运行的方法是向其发送SIGSTOP信号,GDB对于随机信号(非GDB产生的)的处理包括,可以通过handle signals命令来预定义
5、 ptrace函数简单介绍:long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);其中第一个参数代表告诉给kernel要做的动作。
PTRACE_ME:设置自己的被跟踪标志,在被调试进程中使用。
PTRACE_PEEKUSER:可以得到系统调用号及参数信息
PTRACE_CONT:使被跟踪进程继续执行
PRACE_GETREGS:一次性得到所有寄存器相关的值,提供输出参数
PTRACE_POKEDATA:可用来改变子进程中变量的值
PTRACE_SINGLESTEP:会使内核在子进程的每一条指令执行前先将其阻塞,然后将控制权交给父进程
PTRACE_ATTACH:向运行着的子进程置上跟踪标志为。
PTRACE_DETACH:和上面的行为相反。
很多工具strace/ltrace/stuss等工具都用到了ptrace,学习ptrace的最好的资料是这些工具的原代码和kernel相关代码。
其他参考资料:
1)Ptrace相关资料:
http://blog.chinaunix.net/u/19651/showart_362901.html //玩转ptrace系列
http://blog.chinaunix.net/u/19651/showart_362921.html 2)GDB的实现说明: 《gdb Internals》
阅读(1769) | 评论(0) | 转发(0) |