我的日常工具——gdb篇


我的日常工具——gdb篇

03 Apr 2014


1.gdb的原理

熟悉linux的同学面试官会问你用过gdb么?那好用过,知道gdb是怎么工作的么?然后直接傻眼。。。 gdb是怎么接管一个进程?并且能获取这个进程的变量、堆栈、寄存器、内存映像等信息的呢?还可以打断点执行?这些都是gdb一些基本的功能。 很简单,ptrace,好来看看manual上这个系统调用的定义。

#include <sys/ptrace.h>
 ptrace( __ptrace_request request, pid_t pid, * *data);

 

简单描述: ptrace系统调用提供一种方法使某一父进程(叫做"tracer")可以观察并控制另外一个进程(叫做"tracee")的执行,而且还可以检查并改变执行tracee进程时的内存映像和寄存器。这个系统调用主要用来实现断点调试和函数调用跟踪( It is primarily used to implement breakpoint debugging and system call tracing)。

2.gdb将高级语言转成汇编

对于c、c++这样的语言,如果不注意内存释放经常会出现“野指针”、“空指针”等,程序dump掉的时候要找清楚那地方crash了,汇编指令显的非常重要。 比如:

程序1:

#include <stdio.h>
 a[ foo * fool test={(test.henry->,test.henry-> 

 

程序2:

#include <stdio.h>
 * foo * fool test={(test.henry->,test.henry-> 

 

第一个程序不会core dump,而第二个程序core dump掉了。原因在第12行程序1访问的a是数组的地址,而程序2访问的时指针a的内容,a为NULL指针,访问其内容当然时非法的。你可能要问了,你为什么知道程序1访问的是地址而程序2访问的是内容呢? 那就需要汇编指令帮忙了。

题外话:程序2dump会产生core文件,如果没有出现core文件,用ulimit -c unlimited命令产生。
[henry@localhost core]$ gdb -c core..-+: GNU GPL version  or later <http:
This is  software: you are   <http:
<http:
For help, type  to search  commands related to  the main executable  --enablerepo=  /usr/lib/debug/.build-///struct_dump1
    ??/home/henry/code/core/struct_dump1...    main () at struct_dump1.c:  <+>:    push   % <+>:    mov    %rsp,% <+>:    sub    $,% <+>:    movq   $,-(% <+>:    mov    -(%rbp),%=>  <+>:    mov    (%rax),% <+>:    test   %rax,% <+>:    je      <main+>
    <+>:    mov    -(%rbp),% <+>:    mov    (%rax),% <+>:    mov    %rax,% <+>:    mov    $,% <+>:    mov    $,% <+>:    callq   <printf@plt>
    <+>:    mov    $,% <+> <+>

 

上面看到程序执行时用bt提示程序在12行dump掉了,然后转换成汇编代码可以看到12行执行的时mov指令。

  • 对于char a[0]来说,汇编代码用了lea指令,lea 0×8(%rax), %rax
  • 对于char *a来说,汇编代码用了mov指令,mov 0×8(%rax), %rax

lea指令是把地址放进去,而mov是把内容放进去,而NULL指针的内容是不能访问的。这就是前面提到的*a 和a[0]的不同。1

nisi是单步执行汇编命令,和nextstep一样,n表示在当前函数一步步执行,s代表跟踪函数,可以从当前函数跳到另一个函数。 display可以显示一些寄存器内容,如display /x $pc显示程序计数器。 info reg显示所有寄存器内容。 tips——关于NULL指针:

如果程序里有NULL指针,NULL指针会指向系统为程序分配的段地址的开始,系统为段开头64k做苛刻的规定。程序中(低访问权限)访问要求高访问权限的这64K内存被视作是不容许的,会引发Access Volitation 错误。64K内存是一块保留内存(即不能被程序动态内存分配器分配,不能被访问,也不能被使用),就是简单的保留,不作任何使用。2(这段话正确与否有待考究)

下面的代码是对空指针的测试:

 NULL (void*)0
 *p1 = *p2 = *p3 = 

 

下面是用gdb测试:

[henry@localhost core]$  -g null_point_test.c -.-+: GNU GPL version  or later <http:
This is  software: you are   <http:
<http:
For help, type  to search  commands related to /home/henry/code/core/null_point_test...     NULL (void*)0
           *p1 =       *p2 =       *p3 =      return  at :  null_point_test.c, line /home/henry/code/core/, main () at null_point_test.c:
      return - glibc--& = ( **) & = ( **) & = ( **)   main () at null_point_test.c: = { ()}  <main>

 

可以看出gdb测试结果并不是按照上面所说空指针指向64k的段首。(这个问题先放一放,有哪位牛人知道可以告诉我)

3.gdb调试core文件

gdb -c core文件命令调试core文件,调试过程种可能会总是一堆问号的问题,用symbol-file core文件对应的bin文件命令添加字符集即可。

4.gdb条件断点

已经有了断点break_num将其转化成条件断点:condition break_num(断点编号) cond(条件),当满足条件cond时,GDB才会在断点break_num处暂停程序的执行。

 break break_num if cond(条件)定义一个断点并使之成为条件断点。

 tbreak break_num临时断点,断点执行一次后此段点无效。

 commands breakpoint_number可以设置执行断点breakpoint_number时执行一段程序,有点批量执行的意思,以end结束。

 

引用:

相关内容