我的日常工具——gdb篇

03 Apr 2014


1.gdb的原理

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

  1. #include <sys/ptrace.h>
  2. long ptrace(enum __ptrace_request request, pid_t pid,void *addr,
  3. 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:

  1. #include <stdio.h>
  2. struct foo{
  3. int i;
  4. char a[];
  5. };
  6. struct fool{
  7. struct foo *henry;
  8. };
  9. int main()
  10. {
  11. struct fool test={};
  12. if(test.henry->a)
  13. printf("%x\n",test.henry->a);
  14. return ;
  15. }

程序2:

  1. #include <stdio.h>
  2. struct foo{
  3. int i;
  4. char *a;
  5. };
  6. struct fool{
  7. struct foo *henry;
  8. };
  9. int main()
  10. {
  11. struct fool test={};
  12. if(test.henry->a)
  13. printf("%x\n",test.henry->a);
  14. return ;
  15. }

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

  1. 题外话:程序2dump会产生core文件,如果没有出现core文件,用ulimit -c unlimited命令产生。
  1. [henry@localhost core]$ gdb -c core.
  2. GNU gdb (GDB) Fedora 7.6.50.20130731-.fc20
  3. Copyright (C) Free Software Foundation, Inc.
  4. License GPLv3+: GNU GPL version or later <http://gnu.org/licenses/gpl.html>
  5. This is free software: you are free to change and redistribute it.
  6. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  7. and "show warranty" for details.
  8. This GDB was configured as "x86_64-redhat-linux-gnu".
  9. Type "show configuration" for configuration details.
  10. For bug reporting instructions, please see:
  11. <http://www.gnu.org/software/gdb/bugs/>.
  12. Find the GDB manual and other documentation resources online at:
  13. <http://www.gnu.org/software/gdb/documentation/>.
  14. For help, type "help".
  15. Type "apropos word" to search for commands related to "word".
  16.  
  17. [New LWP ]
  18. Missing separate debuginfo for the main executable file
  19. Try: yum --enablerepo='*debug*' install /usr/lib/debug/.build-id//a4410588cf88e43ecdfa6825cd15160aa6ddc7
  20. Core was generated by `./struct_dump1'.
  21. Program terminated with signal SIGSEGV, Segmentation fault.
  22. # 0x0000000000400544 in ?? ()
  23. (gdb) file struct_dump1
  24. Reading symbols from /home/henry/code/core/struct_dump1...done.
  25. (gdb) bt
  26. # 0x0000000000400544 in main () at struct_dump1.c:
  27. (gdb) disassemble main
  28. Dump of assembler code for function main:
  29. 0x0000000000400530 <+>: push %rbp
  30. 0x0000000000400531 <+>: mov %rsp,%rbp
  31. 0x0000000000400534 <+>: sub $0x10,%rsp
  32. 0x0000000000400538 <+>: movq $0x0,-0x10(%rbp)
  33. 0x0000000000400540 <+>: mov -0x10(%rbp),%rax
  34. => 0x0000000000400544 <+>: mov 0x8(%rax),%rax
  35. 0x0000000000400548 <+>: test %rax,%rax
  36. 0x000000000040054b <+>: je 0x400567 <main+>
  37. 0x000000000040054d <+>: mov -0x10(%rbp),%rax
  38. 0x0000000000400551 <+>: mov 0x8(%rax),%rax
  39. 0x0000000000400555 <+>: mov %rax,%rsi
  40. 0x0000000000400558 <+>: mov $0x400600,%edi
  41. 0x000000000040055d <+>: mov $0x0,%eax
  42. 0x0000000000400562 <+>: callq 0x400410 <printf@plt>
  43. 0x0000000000400567 <+>: mov $0x0,%eax
  44. 0x000000000040056c <+>: leaveq
  45. 0x000000000040056d <+>: retq
  46. 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

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

  1. #define NULL (void*)0
  2. int main()
  3. {
  4. int *p1 = NULL;
  5. int *p2 = NULL;
  6. int *p3 = NULL;
  7. return ;
  8. }
  9.  
  10. 下面是用gdb测试:
  11.  
  12. [henry@localhost core]$ gcc -g null_point_test.c -o null_point_test
  13. [henry@localhost core]$ gdb null_point_test
  14. GNU gdb (GDB) Fedora 7.6.50.20130731-.fc20
  15. Copyright (C) Free Software Foundation, Inc.
  16. License GPLv3+: GNU GPL version or later <http://gnu.org/licenses/gpl.html>
  17. This is free software: you are free to change and redistribute it.
  18. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  19. and "show warranty" for details.
  20. This GDB was configured as "x86_64-redhat-linux-gnu".
  21. Type "show configuration" for configuration details.
  22. For bug reporting instructions, please see:
  23. <http://www.gnu.org/software/gdb/bugs/>.
  24. Find the GDB manual and other documentation resources online at:
  25. <http://www.gnu.org/software/gdb/documentation/>.
  26. For help, type "help".
  27. Type "apropos word" to search for commands related to "word".
  28. ..
  29. Reading symbols from /home/henry/code/core/null_point_test...done.
  30. (gdb) list
  31. #define NULL (void*)0
  32. int main()
  33. {
  34. int *p1 = NULL;
  35. int *p2 = NULL;
  36. int *p3 = NULL;
  37. return ;
  38. }
  39. (gdb) b
  40. Breakpoint at 0x40050c: file null_point_test.c, line .
  41. (gdb) r
  42. Starting program: /home/henry/code/core/null_point_test
  43.  
  44. Breakpoint , main () at null_point_test.c:
  45. return ;
  46. Missing separate debuginfos, use: debuginfo-install glibc-2.18-.fc20.x86_64
  47. (gdb) p &p1
  48. $ = (int **) 0x7fffffffdf08
  49. (gdb) p &p2
  50. $ = (int **) 0x7fffffffdf00
  51. (gdb) p &p3
  52. $ = (int **) 0x7fffffffdef8
  53. (gdb) p &p1
  54. $ = (int *) 0x0
  55. (gdb) p &p2
  56. $ = (int *) 0x0
  57. (gdb) p &p3
  58. $ = (int *) 0x0
  59. (gdb) bt
  60. # main () at null_point_test.c:
  61. (gdb) p main
  62. $ = {int ()} 0x4004f0 <main>
  63. (gdb)

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

  1. (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结束。

引用:

  1. 指针和数组的差别 

  2. 空指针保护政策 

我的日常工具——gdb篇的更多相关文章

  1. Linux工具参考篇(网摘)

    Linux工具参考篇 原文出处:[Linux Tools Quick Tutorial] 1. gdb 调试利器 2. ldd 查看程序依赖库 3. lsof 一切皆文件 4. ps 进程查看器 5. ...

  2. 一些日常工具集合(C++代码片段)

    一些日常工具集合(C++代码片段) ——工欲善其事,必先利其器 尽管不会松松松,但是至少维持一个比较小的常数还是比较好的 在此之前依然要保证算法的正确性以及代码的可写性 本文依然会持久更新,因为一次写 ...

  3. 打造程序员的高效生产力工具-mac篇

    打造程序员的高效生产力工具-mac篇 1   概述 古语有云:“工欲善其事,必先利其器” [1] ,作为一个程序员,他最重要的生产资源是脑力知识,最重要的生产工具是什么?电脑. 在进行重要的脑力成果输 ...

  4. [转]Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇--开发工具库篇,主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多 ...

  5. Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇——开发工具库篇,**主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容 ...

  6. 第一篇:打造专属开发工具Eclipse篇

    第一篇:打造专属开发工具Eclipse篇 eclipse 优化 1.动画很酷,但如果可以的话,我总是在所有的工具中禁用动画.所以classic或者window classic主题是我最常用的主题 , ...

  7. 开发-日常工具:TFS(Team Foundation Server)

    ylbtech-开发-日常工具:TFS(Team Foundation Server) TFS(Team Foundation Server)是一个高可扩展.高可用.高性能.面向互联网服务的分布式文件 ...

  8. <JVM下篇:性能监控与调优篇>03-JVM监控及诊断工具-GUI篇

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  9. javaSE中级篇2 — 工具类篇 — 更新完毕

    1.工具类(也叫常用类)-- 指的是别人已经写好了的,我们只需要拿来用就行了 官网网址:Overview (Java Platform SE 8 ) (oracle.com) ---- 但是这个是英文 ...

随机推荐

  1. left join查询结果大于原始数据

    left join onon后面一定是一个主键或者是一个值为唯一的字段吗  on后面关联的条件如果是1对1的数量就不变,如果是1对多的数量就会增加 追问: 问题就在这,我1对多了 追答: 通常的做法是 ...

  2. 2、Spring的LocalSessionFactoryBean创建过程源码分析

    spring的LocalSessionFactoryBean生成过程与hibernate的SessionFactory生成过程是高度吻合的. 为了后面源码分析,首先讲解一个接口,一个类的功能:①.接口 ...

  3. Data Flow ->> Slow Changing Dimension

    这里简单讲下SCD 在讲之前贴上两个有用的链接地址.作者的两篇文件讲解了SCD是什么以及应用 http://www.cnblogs.com/biwork/p/3363749.html http://w ...

  4. [转]C:int型指针

    开源中国:http://my.oschina.net/lotte1699/blog/142538 网页快照:http://www.piaocafe.com/295977937/139381567037 ...

  5. poj1988-种类并查集

    题意:有N个立方体(1<=N <=30,000),相应地初始时每个立方体放在一个栈中,有两种操作:1.M X Y:把包含第X个立方体的栈中的所有立方体当做一个整体拿出来压入包含第Y个立方体 ...

  6. php去除数组中重复数据

    <?php /** * 去除数组中重复数据 * by www.jbxue.com **/ $input = array("a" => "green" ...

  7. centos chrome

    在centos6.X和redhat enterprise 中安装chrome,我找了很久都不行,今天终于找到了可以用下脚本那安装: #! /bin/bash # Google Chrome Insta ...

  8. Android HTTPS(1)概念和简单示例

    Security with HTTPS and SSL The Secure Sockets Layer (SSL)—now technically known as Transport Layer ...

  9. ViewPager的监听事件失效

    主要是因为在我项目使用了PageIndicator,所以这个时候监听事件要写在PageIndicator上. mIndicator.setOnPageChangeListener(new OnPage ...

  10. many to one could not resolve property

    今天在做一个功能的时候 遇到了.一个Could not resolve property 的问题. 配置文件如下: <many-to-one name="user"  cla ...