gdb篇

转自:http://www.cnblogs.com/ypchenry/p/3668572.html

1.gdb的原理

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

#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,void *addr,
                    void *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>
struct foo{
    int i;
    char a[0];
};
struct fool{
    struct foo *henry;
};
int main()
{
    struct fool test={0};
    if(test.henry->a)
        printf("%x\n",test.henry->a);
    return 0;
}

 

程序2:

#include <stdio.h>
struct foo{
    int i;
    char *a;
};
struct fool{
    struct foo *henry;
};
int main()
{
    struct fool test={0};
    if(test.henry->a)
        printf("%x\n",test.henry->a);
    return 0;
}

 

第一个程序不会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.4340
GNU gdb (GDB) Fedora 7.6.50.20130731-19.fc20
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".

[New LWP 4340]
Missing separate debuginfo for the main executable file
Try: yum --enablerepo='*debug*' install /usr/lib/debug/.build-id/73/a4410588cf88e43ecdfa6825cd15160aa6ddc7
Core was generated by `./struct_dump1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400544 in ?? ()
(gdb) file struct_dump1
Reading symbols from /home/henry/code/core/struct_dump1...done.
(gdb) bt
#0  0x0000000000400544 in main () at struct_dump1.c:12
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400530 <+0>:    push   %rbp
   0x0000000000400531 <+1>:    mov    %rsp,%rbp
   0x0000000000400534 <+4>:    sub    $0x10,%rsp
   0x0000000000400538 <+8>:    movq   $0x0,-0x10(%rbp)
   0x0000000000400540 <+16>:    mov    -0x10(%rbp),%rax
=> 0x0000000000400544 <+20>:    mov    0x8(%rax),%rax
   0x0000000000400548 <+24>:    test   %rax,%rax
   0x000000000040054b <+27>:    je     0x400567 <main+55>
   0x000000000040054d <+29>:    mov    -0x10(%rbp),%rax
   0x0000000000400551 <+33>:    mov    0x8(%rax),%rax
   0x0000000000400555 <+37>:    mov    %rax,%rsi
   0x0000000000400558 <+40>:    mov    $0x400600,%edi
   0x000000000040055d <+45>:    mov    $0x0,%eax
   0x0000000000400562 <+50>:    callq  0x400410 <printf@plt>
   0x0000000000400567 <+55>:    mov    $0x0,%eax
   0x000000000040056c <+60>:    leaveq
   0x000000000040056d <+61>:    retq
End of assembler dump.

 

上面看到程序执行时用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

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

#define NULL (void*)0
int main()
{
  int *p1 = NULL;
  int *p2 = NULL;
  int *p3 = NULL;
  return 0;
}

下面是用gdb测试:

[henry@localhost core]$ gcc -g null_point_test.c -o null_point_test
[henry@localhost core]$ gdb null_point_test
GNU gdb (GDB) Fedora 7.6.50.20130731-19.fc20
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
..
Reading symbols from /home/henry/code/core/null_point_test...done.
(gdb) list
1    #define NULL (void*)0
2    int main()
3    {
4      int *p1 = NULL;
5      int *p2 = NULL;
6      int *p3 = NULL;
7      return 0;
8    }
(gdb) b 7
Breakpoint 1 at 0x40050c: file null_point_test.c, line 7.
(gdb) r
Starting program: /home/henry/code/core/null_point_test 

Breakpoint 1, main () at null_point_test.c:7
7      return 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.18-12.fc20.x86_64
(gdb) p &p1
$1 = (int **) 0x7fffffffdf08
(gdb) p &p2
$2 = (int **) 0x7fffffffdf00
(gdb) p &p3
$3 = (int **) 0x7fffffffdef8
(gdb) p &p1
$4 = (int *) 0x0
(gdb) p &p2
$5 = (int *) 0x0
(gdb) p &p3
$6 = (int *) 0x0
(gdb) bt
#0  main () at null_point_test.c:7
(gdb) p main
$4 = {int ()} 0x4004f0 <main>
(gdb) 

 

 

可以看出gdb测试结果p1 p2 p3的内容即null指针的地址都是

(int *) 0x0

正如上面多说空指针指向段首,并且都指向一个内存单元,null指针只有一个。

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结束。

时间: 2024-08-03 07:20:17

gdb篇的相关文章

每天学点GDB(六)

<一>如何利用gdb对coredump进行分析 本篇主要讲解如何利用gdb对coredump进行分析 gdb ./demo core 查看调用堆栈 gdb)bt 查看更为完整的信息 gdb) bt full 如果是多线程,想看每个线程的调用堆栈 gdb) thread apply all bt 至于如何分析内存变量之类的,在本一系列前面的章节有专门论述,可以参考. 想调试已经在运行的程序 gdb ./demo 假设当前运行着的demo进程为1234,则在gdb中运行attach进行关联 gdb

nginx模块_使用gdb调试nginx源码

工欲善其事必先利其器,如何使用调试工具gdb一步步调试nginx是了解nginx的重要手段. ps:本文的目标人群是像我这样初接触Unix编程的同学,如果有什么地方错误请指正. 熟悉gdb的使用 这里就不说了,谷歌一搜一堆,这里推荐一篇文章:GDB 命令详细解释 请重点看一下step,run,break,list,info,continue命令 下载nginx源码 这里使用nginx-1.0.14 src是源代码,auto文件夹是configure运行时候的各种命令集合 修改config并编译

Xcode的gdb调试

关于GDB 对于大多数Cocoa程序员来说,最常用的debugger莫过于Xcode自带的调试工具了.而实际上,它正是gdb的一个图形化包装.相对于gdb,图形化带来了很多便利,但同时也缺少了一些重要功能.而且在某些情况下,gdb反而更加方便.因此,学习gdb,了解一下幕后的实质,也是有必要的. gdb可以通过终端运行,也可以在Xcode的控制台调用命令.本文将通过终端讲述一些gdb的基本命令和技巧. 首先,我们来看一个例子:     #import <Foundation/Foundation

Linux Debugging(三): C++函数调用的参数传递方法总结(通过gdb+反汇编)

         上一篇文章<Linux Debugging:使用反汇编理解C++程序函数调用栈>没想到能得到那么多人的喜爱,因为那篇文章是以32位的C++普通函数(非类成员函数)为例子写的,因此只是一个特殊的例子.本文将函数调用时的参数传递方法进行一下总结.总结将为C++普通函数.类成员函数:32位和64位进行总结.         建议还是读一下Linux Debugging:使用反汇编理解C++程序函数调用栈,这样本文的结论将非常容易理解,将非常好的为CoreDump分析开一个好头.而且

用gdb配合内核转储文件瞬间定位段错误

前几天在写一个使用Huffman算法的文本压缩程序时被"段错误"折磨了好长时间.因为自己向来对内存的使用保持着"克勤克俭"的作风,所以总是被此类错误折磨的焦头难额.C语言的内存管理本来就是一个繁琐的工作,写代码时略有不慎便会出现诸如"段错误(吐核)"的运行时崩溃. 其实段错误是操作系统的一个内存保护机制,一般情况下某程序尝试访问其许可范围之外的内存空间时便会触发内核的"一般保护性异常",内核便会向程序发送一个SIGSEGV(1

如何写gdb命令脚本

作为UNIX/Linux下使用广泛的调试器,gdb不仅提供了丰富的命令,还引入了对脚本的支持:一种是对已存在的脚本语言支持,比如python,用户可以直接书写python脚本,由gdb调用python解释器执行:另一种是命令脚本(command file),用户可以在脚本中书写gdb已经提供的或者自定义的gdb命令,再由gdb执行.在这篇文章里,我会介绍一下如何写gdb的命令脚本. (一) 自定义命令 gdb支持用户自定义命令,格式是: define commandName statement

搜索引擎和知识图谱那些事 (上).基础篇

        这是一篇基础性文章,主要介绍搜索引擎和知识图谱的一些原理.发展经历和应用等知识.希望文章对你有所帮助~如果有错误或不足之处,还请海涵.(参考资料见后) 一. 搜索引擎 (一).搜索引擎的四个时代         根据张俊林大神的<这就是搜索引擎>这本书中描述(推荐大家阅读),搜索引擎从采取的技术划分为4个时代:         1.史前时代:分类目录的一代         这个时代成为"导航时代",Yahoo和国内hao123是这个时代的代表.通过人工搜集整

调试器的工作原理(一):基础篇

这一篇会讲什么 我将为大家展示 Linux 中调试器的主要构成模块 - ptrace 系统调用.这篇文章所有代码都是基于 32 位 Ubuntu 操作系统.值得注意的是,尽管这些代码是平台相关的,将它们移植到其它平台应该并不困难. 缘由 为了理解我们要做什么,让我们先考虑下调试器为了完成调试都需要什么资源.调试器可以开始一个进程并调试这个进程,又或者将自己同某个已经存在的进程关联起来.调试器能够单步执行代码,设定断点并且将程序执行到断点,检查变量的值并追踪堆栈.许多调试器有着更高级的特性,例如在

一步一步学ROP之linux_x86篇

一步一步学ROP之linux_x86篇 作者:蒸米@阿里聚安全 一.序 ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等).虽然现在大家都在用64位的操作系统,但是想要扎实的学好ROP还是得从基础的x86系统开始,但看官请不要着急,在随后的教程中我们还会带来linux_x64以及android (arm)方面的ROP利用方法,欢迎大家继续学习. 小编备注:文中涉及代