GDB提供了很多原生的命令,可將這些命令按照預設序列做為單位執(zhí)行,可以極大的擴展其功能。GDB提供了兩種方式來實現上述預設序列: user-defined commands 用戶定義命令和 command files命令文件。下面就來介紹下這兩種方式。
參考https://sourceware.org/gdb/current/onlinedocs/gdb#Sequences
所謂的用戶定義命令即將一系列 GDB 命令作為一個整體分配一個用戶定義的命令名字來調用。
其語法如下
define commandname命令序列end
其中commandname是自定義命令名字,可以是多級命令比如
define target my-target
示例
define adderprint $arg0 + $arg1 + $arg2end
調用方式
adder 1 2 3
其中adder為命令名字, 后續(xù)無需定義參數原型, 這有別函數定義。
GDB中要理解一句話GDB中一切皆命令,一切皆字符串。
類似于我們實現SHELL命令一樣,這里adder 1 2 3就是一個字符串命令, 解析時自動將命令后的字符解析為參數。
用戶命令可以接受任意數量的參數,參數之間用空格隔開,例如adder 1 2 3。
使用$arg數字, 即$arg0, $arg1... 的方式引用這些參數,例如print $arg0 + $arg1 + $arg2。
這些參數是文本替換,因此它們可能引用變量、使用復雜表達式,甚至執(zhí)行函數調用。
由于是文本替換所以無法傳遞包含空格的參數,因為命令是字符串,空格代表參數間隔。
雖然可以使用像 "$arg1" 這樣的表達式將參數轉換為字符串,但如果參數包含引號,就沒辦法了。這種更復雜需求可以使用Python腳本后續(xù)再分享。
$argc 可以引用參數的個數,例如
define adderif $argc == 2print $arg0 + $arg1endif $argc == 3print $arg0 + $arg1 + $arg2end
使用eval 結合$argc可以實現可變數量參數的處理
例如
define adderset $i = 0set $sum = 0while $i < $argceval "set $sum = $sum + $arg%d", $iset $i = $i + 1endprint $sumend
使用
eval "set $sum = $sum + $arg%d", $i實現類似于printf的格式化替換
$arg%d中%d替換為$i的值, 這樣就可以引用到任意的變量,而無需寫死。
define commandname文本end
例如
document adder計算加法end
輸入help adder就會打印
計算加法

show usershow user commandname
可以define 一類特殊名字的命令
其commandname為
hook-foo 或者
hookpost-foo
其中foo是已經存在的命令。
這類自定義命令即鉤子命令,可以自動在某個命令foo之前和之后執(zhí)行,這在某個命令前后需要插入某個動作時非常有用。
hook-foo表示在執(zhí)行foo前調用hook-foo命令
hookpost-foo表示在執(zhí)行foo后調用hookpost-foo命令。
例如
define hook-echoecho <<<---enddefine hookpost-echoecho --->>>\nend
則在echo前后自動打印一些信息
(gdb) echo Hello World<<<---Hello World--->>>(gdb)
GDB 的命令文件是由 GDB 命令行組成的文本文件。
以# 開頭的行為注釋。
命令文件中的空行不起作用。
其中的命令在命令文件加載時按序列執(zhí)行。
自定義命令可以在GDB命令行中輸入,也可放入命令文件中,此時自定義命令可以手動調用,或者命令文件中調用。
執(zhí)行命令文件
source [-s] [-v] filename
-s指定文件路徑
-v 指定打印命令
filename為命令文件名
也可以在gdb啟動時通過”-x 命令文件名”選項加載。
gdb -x command_file_name
如果有提示重復定義命令,例如
Redefine command "add"? (y or n) [answered Y; input not from terminal]
可以在文件最開始添加一行set confirm off
使用變量可以大大的提高自定義命令實現的靈活性。
以$name表示變量,name為任意名字,但是
有些特殊的不能使用
比如$寄存器名 例如$pc
比如$$或$$數字 表示歷史值
還有一些_開頭的名字有特定含義
變量賦值使用set命令
比如set $foo = *0x1000 表示將0x1000處的值(默認為unsigned int)賦值給foo,相當于讀該地址處的值。
更一般的set $foo = *object_ptr, object_ptr可以是一個指向任意對象的指針(結構體指針等)。
還可以使用條件初始賦值,類似C的static變量,如果變量未初始化則賦該初始值,已經初始化了則不再重復賦值。
init-if-undefined $variable = expression
- 變量是全局的
- 可以隨時重新set修改變量的值。
- 變量沒有固定類型,可以是任意類型,數組結構等,甚至可以隨時set修改為新的類型的值。
- 變量可以作為表達式的一部分。
- show convenience簡寫show conv查看當前所有的變量。
示例
可以使用類似C的自增
set $i = 0print bar[$i++]->contents
可以使用流控命令編寫復雜命令
if 表達式命令end或者if 表達式命令else命令end
while 表達式命令loop_breakloop_continue命令end
其中loop_break結束整個while循環(huán),loop_continue結束本次循環(huán)從while繼續(xù)。
和c語言的break和continue類似。
以下演示一個cache操作的實例。
主要涉及到了變量的使用,參數的使用,流控的使用,基本上使用這些就可以滿足大部分使用需求了。類似的有寄存器讀寫配置,模塊初始化等需求可以參考此修改。
主要實現cache的flush和invalid操作
涉及ICACHE和DCACHE,寄存器基地址分別為0x01480000, 0x01440000
flush和invalid的范圍由偏移0x80和0x84處的開始結束地址配置。
然后偏移0x00的控制寄存器的bit0=1表示執(zhí)行flush或者invalid操作,bit21=0表示flush,為0表示invalid。Bit4=1表示操作完成。
實現了cache_flush和cache_invalid兩個命令
接收3個參數分別是CACHE這個IP寄存器的基地址,flush或者invalid操作的開始地址和長度。這樣就可以調用這個命令進一步實現操作指定范圍,操作全部范圍的命令,只需要傳入不同參數即可。
cache.gdb
內容如下
set confirm offset $REG_SADDR_OFFSET = 0x80set $REG_EADDR_OFFSET = 0x84set $REG_CTRL_OFFSET = 0x00set $DCACHE_REG_BASE = 0x01480000set $ICACHE_REG_BASE = 0x01440000set $DCACHE_ADDR=0x60000000set $DCACHE_LEN=0x280000set $ICACHE_ADDR=0x60000000set $ICACHE_LEN=0x280000define cache_flush# $arg0 reg_base# $arg1 addr 必須32字節(jié)對齊# $arg2 len 必須32字節(jié)對齊set $REG_SADDR = $REG_SADDR_OFFSET + $arg0set $REG_EADDR = $REG_EADDR_OFFSET + $arg0set $REG_CTRL = $REG_CTRL_OFFSET + $arg0set *($REG_SADDR) = $arg1 - $DCACHE_ADDRset *($REG_EADDR) = $arg1 -$DCACHE_ADDR + $arg2 -1set $tmp = *($REG_CTRL)#icache_flush_done=0set $tmp = $tmp & (~(1u<<4))#icache_flush_invalid_mode=0set $tmp = $tmp & (~(1u<<21))#icache_flush_ena=1set $tmp = $tmp | (1u<<0)set *($REG_CTRL) = $tmpwhile ((*($REG_CTRL) & (1u<<4)) == 0)endenddefine cache_invalid# $arg0 reg_base# $arg1 addr 必須32字節(jié)對齊# $arg2 len 必須32字節(jié)對齊set $REG_SADDR = $REG_SADDR_OFFSET + $arg0set $REG_EADDR = $REG_EADDR_OFFSET + $arg0set $REG_CTRL = $REG_CTRL_OFFSET + $arg0set *($REG_SADDR) = $arg1 - $DCACHE_ADDRset *($REG_EADDR) = $arg1 - $DCACHE_ADDR + $arg2 -1set $tmp = *($REG_CTRL)#icache_flush_done=0set $tmp = $tmp & (~(1u<<4))#icache_flush_invalid_mode=1set $tmp = $tmp | (1u<<21)#icache_flush_ena=1set $tmp = $tmp | (1u<<0)set *($REG_CTRL) = $tmpwhile ((*($REG_CTRL) & (1u<<4)) == 0)endenddefine cache_clr# $arg0 reg_baseset $REG_CTRL = $REG_CTRL_OFFSET + $arg0set $tmp = *($REG_CTRL)#icache_clear_done=0set $tmp = $tmp & (~(1u<<6))#icache_clear_ena=1set $tmp = $tmp | (1u<<2)set *($REG_CTRL) = $tmpwhile ((*($REG_CTRL) & (1u<<6)) == 0)endenddefine dcache_flush_allset $base = $DCACHE_ADDRset $len = $DCACHE_LENcache_flush $DCACHE_REG_BASE $base $lenenddefine icache_flush_allset $base = $DCACHE_ADDRset $len = $DCACHE_LENcache_flush $ICACHE_REG_BASE $base $lenenddefine dcache_flush_rangeset $base = $arg0set $len = $arg1cache_flush $DCACHE_REG_BASE $base $lenenddefine icache_flush_rangeset $base = $arg0set $len = $arg1cache_flush $ICACHE_REG_BASE $base $lenenddefine dcache_invalid_allset $base = $DCACHE_ADDRset $len = $DCACHE_LENcache_invalid $DCACHE_REG_BASE $base $lenenddefine icache_invalid_allset $base = $DCACHE_ADDRset $len = $DCACHE_LENcache_invalid $ICACHE_REG_BASE $base $lenenddefine dcache_invalid_rangeset $base = $arg0set $len = $arg1cache_invalid $DCACHE_REG_BASE $base $lenenddefine icache_invalid_rangeset $base = $arg0set $len = $arg1cache_invalid $ICACHE_REG_BASE $base $lenenddefine dcache_clear_allcache_clear $DCACHE_REG_BASEenddefine icache_clear_allcache_clear $ICACHE_REG_BASEend
加載
source cache.gdb
執(zhí)行命令
dcache_flush_all
以上介紹了GDB的自定義命令和命令文件,分享了實例。基本上使用變量,參數,流控命令幾點就可以寫出滿足大部分需求的自定義命令。比如DDR初始化,時鐘初始化,某些模塊初始化等需求,因為這些配置無非就是寫寄存器,讀寄存器,判斷狀態(tài)。分享了上述實例,有類似需求可以對照修改即可。