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結束。
引用:
指針和數組的差別?
空指針保護政策?
新聞熱點
疑難解答