03 APR 2014
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.fc20Copyright (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 fileTry: yum --enablerepo='*debug*' install /usr/lib/debug/.build-id/73/a4410588cf88e43ecdfa6825cd15160aa6ddc7Core was generated by `./struct_dump1'.Program terminated with signal SIGSEGV, Segmentation fault.#0 0x0000000000400544 in ?? ()(gdb) file struct_dump1Reading symbols from /home/henry/code/core/struct_dump1...done.(gdb) bt#0 0x0000000000400544 in main () at struct_dump1.c:12(gdb) disassemble mainDump 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
ni
和si
是单步执行汇编命令,和next
与step
一样,n表示在当前函数一步步执行,s代表跟踪函数,可以从当前函数跳到另一个函数。display
可以显示一些寄存器内容,如display /x $pc
显示程序计数器。info reg
显示所有寄存器内容。
tips——关于NULL指针:
如果程序里有NULL指针,NULL指针会指向系统为程序分配的段地址的开始,系统为段开头64k做苛刻的规定。程序中(低访问权限)访问要求高访问权限的这64K内存被视作是不容许的,会引发access Volitation 错误。64K内存是一块保留内存(即不能被程序动态内存分配器分配,不能被访问,也不能被使用),就是简单的保留,不作任何使用。2
下面的代码是对空指针的测试:
#define NULL (void*)0int 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.fc20Copyright (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) list1 #define NULL (void*)02 int main()3 {4 int *p1 = NULL;5 int *p2 = NULL;6 int *p3 = NULL;7 return 0;8 }(gdb) b 7Breakpoint 1 at 0x40050c: file null_point_test.c, line 7.(gdb) rStarting program: /home/henry/code/core/null_point_test Breakpoint 1, main () at null_point_test.c:77 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结束。
引用:
指针和数组的差别?
空指针保护政策?
新闻热点
疑难解答