一、gdb調試器簡介
gdb作爲常用GUN調試器之一,可以幫助我們調試和分析二進制文件(一般就是指可執行程序)。本文和大家一起了解下Linux下如何使用gdb進行程序的簡單調試。
首先我們還是看看gdb調試器的幫助說明:
This is the GNU debugger. Usage:
gdb [options] [executable-file [core-file or process-id]]
gdb [options] --args executable-file [inferior-arguments ...]
Selection of debuggee and its files:
--args Arguments after executable-file are passed to inferior
--core=COREFILE Analyze the core dump COREFILE.
--exec=EXECFILE Use EXECFILE as the executable.
--pid=PID Attach to running process PID.
--directory=DIR Search for source files in DIR.
--se=FILE Use FILE as symbol file and executable file.
--symbols=SYMFILE Read symbols from SYMFILE.
--readnow Fully read symbol files on first access.
--write Set writing into executable and core files.
Initial commands and command files:
--command=FILE, -x Execute GDB commands from FILE.
--init-command=FILE, -ix
Like -x but execute commands before loading inferior.
--eval-command=COMMAND, -ex
Execute a single GDB command.
May be used multiple times and in conjunction
with --command.
--init-eval-command=COMMAND, -iex
Like -ex but before loading inferior.
--nh Do not read ~/.gdbinit.
--nx Do not read any .gdbinit files in any directory.
Output and user interface control:
--fullname Output information used by emacs-GDB interface.
--interpreter=INTERP
Select a specific interpreter / user interface
--tty=TTY Use TTY for input/output by the program being debugged.
-w Use the GUI interface.
--nw Do not use the GUI interface.
--tui Use a terminal user interface.
--dbx DBX compatibility mode.
-q, --quiet, --silent
Do not print version number on startup.
Operating modes:
--batch Exit after processing options.
--batch-silent Like --batch, but suppress all gdb stdout output.
--return-child-result
GDB exit code will be the child's exit code.
--configuration Print details about GDB configuration and then exit.
--help Print this message and then exit.
--version Print version information and then exit.
Remote debugging options:
-b BAUDRATE Set serial port baud rate used for remote debugging.
-l TIMEOUT Set timeout in seconds for remote debugging.
Other options:
--cd=DIR Change current directory to DIR.
--data-directory=DIR, -D
Set GDB's data-directory to DIR.
At startup, GDB reads the following init files and executes their commands:
* user-specific init file: /Users/XXXX/.gdbinit
For more information, type "help" from within GDB, or consult the
GDB manual (available as on-line info or a printed manual).
Report bugs to "<http://www.gnu.org/software/gdb/bugs/>".
基本操作如下:
1. gdb <program> PID # 用gdb調試一個正在運行中的進程,或者使用 gdb <program>
2. 設置斷點:br (br可使用 break或者b 代替)
br filename:line_num # 設置斷點到指定文件制定行
br namespace::classname::func_name # 設置斷點到指定函數
3. n: 單步跳過 s: 單步進入 # n(next) s(step)
4. finish # 執行到函數retun返回
5. list # 列出當前位置之後的10行代碼;
list line_number # 列出line_number之後的十行代碼
6. bt(backtrace) # 列出調用棧
7. info locals # 列出當前函數的局部變量
8. p var # 打印變量值
9. info breakpoints # 列出所有斷點
10. delete breakpoints # 刪除所有斷點;
delete breakpoints id # 刪除編號爲id的斷點;
disable/enable breakpoints id # 禁用/啓用斷點
11. break ... if ... 條件中斷
二、dbg調試示例
首先,我們編寫一個簡單的程序。
// demo: malloc.c
#include <stdio.h>
int main(int argc,char** argv){
int* a;
*a = 5;
return 0;
}
然後,我們使用gcc編譯器編譯malloc.c 。爲了調試程序,有時我們需要使用-g選項,它的作用就是將調試信息加入到最後的二進制可執行文件中。-g 同-o一樣,是分級別的。當不指定級別的時候,其level爲2。如果需要調試宏定義,我們可以使用更高的級別-g3。這裏我們使用默認即可。
root$ gcc -g2 malloc.c
root$ ls
a.out a.out.dSYM malloc.c
看代碼可知,如果直接運行上 a.out程序,會報段錯誤。那麼程序是在哪兒出錯呢(假裝不知道😄),這就需要調試了。我們也是先從簡單的開始,後面和大家一起分享複雜的程序如何進行gdb調試。
下面,我們開始使用gdb調試器去調試生成的a.out 可執行文件。
root$ gdb -q ./a.out # 啓動gdb調試
Reading symbols from ./a.out...Reading symbols from /Desktop/a.out.dSYM/Contents/Resources/DWARF/a.out...done.
done.
(gdb) break main # 在main函數添加斷點
Breakpoint 1 at 0x100000fa4: file malloc.c, line 5.
(gdb) break malloc.c:5 # 在文件第五行添加斷點
Note: breakpoint 1 also set at pc 0x100000fa4.
Breakpoint 2 at 0x100000fa4: file malloc.c, line 5.
(gdb) break malloc.c:6 # 在文件第6行添加斷點
Breakpoint 3 at 0x100000fae: file malloc.c, line 6.
(gdb) info break # 查看所有斷點
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000100000fa4 in main at malloc.c:5
2 breakpoint keep y 0x0000000100000fa4 in main at malloc.c:5
3 breakpoint keep y 0x0000000100000fae in main at malloc.c:6
(gdb) run # 開始運行
Starting program: /Users/Desktop/a.out
[New Thread 0x1903 of process 26422]
warning: unhandled dyld version (15)
Thread 2 hit Breakpoint 1, main (argc=1, argv=0x7ffeefbff770) at malloc.c:5
5 *a = 5; # 在第五行第一個斷點處停下來,實際並沒有執行
(gdb) n # 逐步
Thread 2 received signal SIGSEGV, Segmentation fault.
0x0000000100000fa8 in main (argc=1, argv=0x7ffeefbff770) at malloc.c:5
5 *a = 5; # 因爲設置斷點重複,實際仍停留在當前斷點處
(gdb) p a # 查看指針變量a的值,因爲沒有分配內存或者指向內存空間,默認初始化爲 0x00
$1 = (int *) 0x0
(gdb) p *a # 查看*p 的值,顯然無法進入查看到
Cannot access memory at address 0x0
(gdb) info locals # 查看當前函數的所有局部變量的值
a = 0x0
(gdb) bt # 調用堆棧查看
#0 0x0000000100000fa8 in main (argc=1, argv=0x7ffeefbff770) at malloc.c:5
(gdb) n # 繼續逐步執行,執行完*a = 5,程序異常,報段錯誤
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) list
1 #include <stdio.h>
2
3 int main(int argc,char** argv){
4 int* a /*= (int *)malloc(sizeof(int))*/ ;
5 *a = 5;
6
7
8 return 0;
9 }
(gdb) finish
The program is not being run.
(gdb) q # 退出gdb調試
通過上面執行gdb調試,我們可以定位到錯誤產生的位置。那麼修改完bug,重新編譯執行看看。
// demo: malloc.c
#include <stdio.h>
int main(int argc,char** argv){
int* a = (int *)malloc(sizeof(int)) ;
*a = 5;
free(a);
a = NULL;
return 0;
}
程序正常運行。至此,一個簡單程序的dbg調試完成。下篇,我們一起來試着調試下包含多文件、宏定義以及多線程的可執行程序。