我的日常工具——gdb篇


我的日常工具——gdb篇

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.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結束。

 

引用:

  1. 指針和數組的差別 

  2. 空指針保護政策 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM