超級詳細的gdb的入門手冊

1. gdb使用

文章目錄

1.1. Linux Core 文件生成及設置

1.1.1. 永久設置

  • 編輯/root/.bash_profile文件,在其中加入 ulimit -c unlimited,如果沒有這個文件,則創建
  • 重啓系統或者執行:source /root/.bash_profile

1.1.2. 臨時設置

1.1.3. core文件的設置

  • /proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作爲擴展。文件內容爲1,表示添加pid作爲擴展名,生成的core文件格式爲core.xxxx;爲0則表示生成的core文件統一命名爲core,例如:

    echo "1" > /proc/sys/kernel/core_uses_pid
    
  • proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式,可通過以下命令修改此文件,例如:

    echo "/corefile/core-%e-%p-%t-s" >/proc/sys/kernel/core_pattern
    

1.1.4. 參數說明

  • 可以將core文件統一生成到/corefile目錄下,產生的文件名爲core-命令名-pid-時間戳,需要提前創建/corefile文件夾,以下是參數列表:

    1. %p - insert pid into filename #添加pid
    2. %u - insert current uid into filename #添加當前uid
    3. %g - insert current gid into filename #添加當前gid
    4. %s - insert signal that caused the coredump into the filename #添加導致產生core的信號
    5. %t - insert UNIX time that the coredump occurred into filename #添加core文件生成時的unix時間
    6. %h - insert hostname where the coredump happened into filename  #添加主機名
    7. %e - insert coredumping executable name into filename   #添加產生coredump的可執行文件名
    

1.2. GDB調試時的信息顯示

1.2.1. 顯示版本信息

show version

1.2.2. 顯示GDB版權相關信

show copying
show warranty

1.2.3. 啓動時不顯示提示信息

gdb -q                # 可以在~/.bashrc中,爲gdb設置一個別名

1.2.4. gdb退出時不顯示提示信息

set confirm off       # 可以把這個命令加到.gdbinit文件裏面

1.2.5. 輸出信息多時不會暫停輸出

  • 有時當gdb輸出信息較多時,gdb會暫停輸出,並會打印“—Type to continue, or q to quit—”這樣的提示信息,解決方法所示
set pagination off  或者  set height 0

1.3. 函數

1.3.1. info functions

  • 支持正則
info functions        # 列出函數原型以及不帶調試信息的函數
info functions Msg*   # gdb只會列出名字裏包含“Msg”的函數

使用gdb調試遇到函數時,使用step命令(縮寫爲s)可以進入函數(函數必須有調試信息),使用next命令(縮寫爲n)不進入函數,gdb會等函數執行完,再顯示下一行要執行的程序代碼

1.3.2. 進入不帶調試信息的函數

set step-mode on          # 設置該命令,gdb就不會跳過沒有調試信息的函數

1.3.3. 退出正在調試的函數

  • 當單步調試一個函數時,如果不想繼續跟蹤下去了,可以有兩種方式退出
1.finish  當不想再繼續跟蹤函數時,執行完“finish”命令,gdb會打印函數輸出結果,然後停在返回的地方。
2.return  這樣函數不會繼續執行下面的語句,而是直接返回。也可以用“return expression”命令指定函數的返回值

1.3.4. 直接執行函數

  • call 函數名 或者print 函數名
call  func()
print func()

1.3.5. 打印函數堆棧幀信息

info frame

1.3.6. 選擇函數堆棧幀

frame addr addr是堆棧地址

1.3.7. 向下或向上切換函數堆棧幀

up 層級     #  up 2 往外層的堆棧幀移動兩層
down 層級   # down 2 往裏層的堆棧幀移動兩層
或者
"up-silently n"
"down-silently n""up n""down n"命令區別在於,切換堆棧幀後,不會打印信息

1.4. 斷點

1.4.1. 在匿名空間設置斷點

namespace Foo
{
  void foo()
  {
  }
}

namespace
{
  void bar()
  {
  }
}
    在gdb中,如果要對namespace Foo中的foo函數設置斷點,可以使用如下命令:
        (gdb) b Foo::foo
    如果要對匿名空間中的bar函數設置斷點,可以使用如下命令:
        (gdb) b (anonymous namespace)::bar
在程序地址上打斷點:

0000000000400522 <main>:
  400522:       55                      push   %rbp
  400523:       48 89 e5                mov    %rsp,%rbp
  400526:       8b 05 00 1b 00 00       mov    0x1b00(%rip),%eax        # 40202c <he+0xc>
  40052c:       85 c0                   test   %eax,%eax
  40052e:       75 07                   jne    400537 <main+0x15>
  400530:       b8 7c 06 40 00          mov    $0x40067c,%eax
  400535:       eb 05                   jmp    40053c <main+0x1a>
當調試彙編程序,或者沒有調試信息的程序時,經常需要在程序地址上打斷點,方法爲b *address。
例如:
    (gdb) b *0x400522

1.4.2. 在程序入口打斷點

  • 獲取程序入口
1.strip a.out  
2.readelf -h a.out            # 找到Entry point address 就是 程序入口
3.進入gdb,執行info files       # 找到Entry point address 就是 程序入口
  • 打斷點
b *address(這個地方是上個步驟獲取到的程序入口地址)

1.4.3. 在文件行號上打斷點

b file:linenum

1.4.4. 查看斷點信息

info breakpoints

1.4.5. 保存已經設置的斷點

  • 在gdb中,可以使用如下命令將設置的斷點保存下來
(gdb) save breakpoints file-name-to-save
  • 下此調試時,可以使用如下命令批量設置保存的斷點
(gdb) source file-name-to-save

1.4.6. 設置臨時斷點

tbreak     # 簡寫爲tb

1.4.7. 設置條件斷點

  • break … if cond
break 46 if testsize==100
break main if argc > 1
break 180 if string == NULLL && i < 0
break test.c:34 if(s & y) == 1
break myfunc if i % (j + 3) != 0
break if strlen(mystring) == 0

1.4.8. 忽略斷點

  • ignore bunm count
    • 意思是接下來count次編號爲bnum的斷點觸發都不會讓程序中斷,只有第count + 1次斷點觸發纔會讓程序中斷

1.4.9. conditon

  • 與break…if類似,只是condition只能用在已存在的斷點上
  • 用法:condition <break_list> (condition)
  • 示例:
cond 3 i==5 將會在斷點3上附加條件(i==3)

1.4.10. 爲斷點設置命令列表

  • 使用commands命令
  • 用法:commands break_list
  • 示例:
(gdb)commands 1
Type commands for when breakpoint 1 is hit,one per line.
End with a line saying just "end"
>silent
>printf "n = %d\n",n
>continue
>end

1.4.11. 向一個程序的所有函數設置斷點

rbreak .

1.4.12. 清除第n行的斷點

clear 行號

1.4.13. 忽略斷點

  • ignore <break_list> count
ignore 1 100 #表示忽略斷點1的前100次停止

1.5. 觀察點

1.5.1. 設置觀察點

1.watch a    # 當a的值發生變化的時候,程序都會停下來
2.watch *(data_type *)address
  • 如果系統支持硬件觀測的話,當設置觀測點會打印信息
Hardware watchpoint num:expr
  • 如果不想用硬件觀測點的話可設置爲
set can-use-hw-watchpoints

1.5.2. 查看觀察點

info watchpoints
  • watch 設置的觀察點也可以用控制斷點的命令來控制:disable,enable,delete

1.5.3. 設置觀察點只對指定線程有效

watch expr thread thradnum  # 只有編號爲threadnum的線程改變了變量expr的值,程序纔會停下來
  • 這種針對特定線程設置觀察點方式只對硬件觀察點有效

1.5.4. 設置讀觀察點

rwatch  # 當發生讀取變量行爲時,程序就會暫停
  • rwatch只對硬件觀察點才生效

1.6. catchpoint(捕捉點)

1.6.1. 讓catchpoint只觸發一次

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    pid_t pid;
    int i = 0;

    for (i = 0; i < 2; i++)
    {
        pid = fork();
        if (pid < 0)
        {
            exit(1);
        }
        else if (pid == 0)
        {
            exit(0);
        }
    }
    printf("hello world\n");
    return 0;
}
  • tcatch fork ------ tcatch 命令設置catchpoint只觸發一次,程序就暫停
  • catch fork ------ 用catch fork命令爲fork調用設置catchpoint

1.6.2. 爲系統調用設置catchpoint

  • catch syscall [name | number]
catch syscall mmap
catch syscall 9  # 使用系統調用的編號設置catchpoint
  • 系統調用和編號的映射文件路徑:
    • /usr/share/gdb/syscalls/amd64-linux.xml
    • /usr/local/share/gdb/syscalls/amd64-linux.xml

1.6.3. 通過爲ptrace調用設置catchpoint破解anti-debugging的程序

1.7. 打印

1.7.1. 打印ASCII和寬字符字符串

#include <stdio.h>
#include <wchar.h>

int main(void)
{
        char str1[] = "abcd";
        wchar_t str2[] = L"abcd";

        return 0;
}
* x/s  str1           # 打印字符串str1的值
* x/ws str2           # 打印字符串str2的值

1.7.2. 打印STL容器中的內容

#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> vec(10); // 10 zero-initialized elements

    for (int i = 0; i < vec.size(); i++)
    vec[i] = i;

    cout << "vec contains:";
    for (int i = 0; i < vec.size(); i++)
    cout << ' ' << vec[i];
    cout << '\n';

    return 0;
}
  • p vec ----- 缺省的顯示結果可讀性差
  • gcc 7.0之後,可以使用python腳本,改善打印結果,設置方法,將dbinit_stl_views 下載下來,將dbinit_stl_views-1.0.3.txt,執行 cat dbinit_stl_views-1.0.3.txt >> ~/.gdbinit.

1.7.3. 打印大數組中的內容

  • 在GDb中,如果要打印大數組的內容,缺省最多會顯示200個元素,
  • 修改打印的元素個數上限:set print elements number-of-elements
set print elements 0
或  
set print elements unlimited   # 修改打印的元素個數上限爲無限制

1.7.4. 打印數組中任意連續元素值

p array[index]@num   # array:數組名稱,index:數組索引,num:連續打印的元素個數
p *array@10          # 打印從數組開頭連續元素的值

1.7.5. 打印數組的索引下標

set print array-indexs on

1.7.6. 打印函數局部變量的值

  • 當程序在某個斷點停住時
bt full          # 顯示各個函數的局部變量值
bt full n        # 從內向外顯示n個棧幀
info locals      # 打印當前函數局部變量的值

1.7.7. 打印進程內存信息

info proc mappings
info files
info target

1.7.8. 打印靜態變量的值

print filename::static_varname  # filename::static_varname ---->文件名::靜態變量名

1.7.9. 打印變量類型和所在文件

whatis xxxx             # 查看變量類型
ptype xxx               # 查看變量詳細類型
info variables xxx      # 查看xxx變量所在的文件

1.7.10. 打印內存的值

1.7.11. 打印源代碼行

list main         # 指定函數打印
list - , list +   # 向前,向後打印
list 1,10         # 指定打印範圍

1.7.12. 使用"$_“和”$__"變量

1.7.13. 打印程序動態分配內存的信息

1.7.14. 打印調用棧幀中變量的值

1.7.15. 設置打印長度

set print elements 0  # 設置打印 elements 的長度不受限制,默認是打印200個元素
show print elements

1.7.16. 查看局部變量

info local

1.7.17. 打印宏信息

  • 需要在編譯程序的時候加上-g3或者-ggdb3,在gdb內打印宏信息
p xxx
或者
info macro xxx

1.8. core dump 文件

1.8.1. 爲調試進程產生core dump文件

  • gcore 或者 generate-core-file,讓被調試的進程產生core dump文件,記錄現在進程的狀

1.9. 圖形化界面

1.9.1. 進入圖形化界面

  1. gdb -tui
  2. gdbtui
  3. 進入gdb之後使用"Crtl + A + X"組合鍵
  4. 退出界面,也是使用"Crtl + A + X"

1.9.2. 顯示彙編代碼

layout asm

1.9.3. 既顯示彙編又顯示源代碼

layout split

1.9.4. 顯示寄存器窗口

layout regs

1.9.5. 顯示浮點寄存器

tui reg float

1.9.6. 顯示系統寄存器

tui reg system

1.9.7. 顯示通用寄存器

tui reg general

1.9.8. 調整窗口大小

  • winheight <win_name> [+ | -]count
  • win_name 可以是src,cmd,asm,regs
  • 例如:
winheight src -5

1.10. 顯示各項鍊接庫信息

1.10.1. info sharelibrary regex

  • 顯示程序加載的共享鏈接庫信息,其中regex可以是正則表達式,意爲顯示名字符合regex的共享鏈接庫。如果沒有regex,則列出所有的庫

1.11. 信號

  • info signals 或者 info handle 查看gdb如何處理進程收到的信號
Signal        Stop      Print   Pass to program Description
第一項(Signal):標示每個信號。
第二項(Stop):表示被調試的程序有對應的信號發生時,gdb是否會暫停程序。
第三項(Print):表示被調試的程序有對應的信號發生時,gdb是否會打印相關信息。
第四項(Pass to program):gdb是否會把這個信號發給被調試的程序。
第五項(Description):信號的描述信息。

1.12. 啓動

  • gdb -command=command_file exec_file
  • 表示要在可執行文件 exec_file 上運行GDB,首先要從文件command_file中讀取命令

1.13. 線程

1.13.1. 查看當前應用程序中所有的線程

info threads

1.13.2. 指定線程

thread <thread number>

1.13.3. 鎖定調度器來避開gdb根據優先權來改變當前線程

set scheduler-locking on/off

1.13.4. 確定當前模式

show scheduler -locking

1.13.5. 想所有線程應用一個命令

thread apply all <command(比如backtrace)>

1.14. 其他

1.14.1. 設置變量的值

  • print
(gdb)print x=4          # x=4這個表達式是C/C++的語法,意爲把變量x的值修改爲4
(gdb)set var print=4

1.14.2. 查看結構體的定義

ptype struct 類型名

1.14.3. gdb輸出到文件

1.
    set args > output.log
2.
    通過tee在啓動時重定向
    gdb |tee -a file
3.
    set logging file <文件名>
    set logging on
        輸入這個命令後,此後的調試信息將輸出到指定文件
4.
    set logging overwrite [on|off]
        By default, gdb will append to the logfile. Set overwrite if you want set logging on to overwrite the logfile instead. 
5.
    set logging redirect [on|off]
        By default, gdb output will go to both the terminal and the logfile. Set redirect if you want output to go only to the log file.
6.
    show logging
        Show the current values of the logging settings
7.
    set logging off
        Disable logging.

1.14.4. finish

  • 運行程序,直到當前函數完成返回,並打印函數返回時的堆棧地址和返回值及參數值等信息

1.14.5. info program

  • 查看當前進程運行狀態

1.14.6. until

  • 當你厭倦了在一個循環體內單步跟蹤時,這個命令可以運行程序直到退出循環體

1.14.7. call

  • 調用“函數”,並傳遞“參數”,如:call gdb_test(55)

1.14.8. jump

  • jump 行號,行號可以是:1.數字;2.(+/-)偏移;3.文件名:行號
j 136
  • jump 位置,位置可以是:1.函數名;2.文件名:函數名;3.*內存地址

1.15. gdb調試報錯

1.15.1. Cannot get thread event message: debugger service failed

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章