文章目錄
- 一、準備工作
- 二、基本調試流程
- 1. 設置斷點
- 2. 執行程序
- 3. 查看源代碼/匯編
- 三、查看寄存器
- 1. 查看通用寄存器
- 2. 查看特殊寄存器
- 四、查看內存
- 1. 內存查看命令
- 2. 內存修改命令
- 五、調試實戰示例
- 六、高級調試技巧
- 1. 條件斷點
- 2. 自動顯示
- 3. 內存斷點(觀察點)
- 4. 回溯調用棧
- 七、退出調試
- 八、退出QEMU
- 總結
在嵌入式開發中,GDB是調試RISC-V程序的核心工具。以下是使用
gdb-multiarch
調試
add.elf
程序的詳細步驟,包括斷點設置、寄存器查看、內存分析等關鍵操作:
一、準備工作
- 給elf文件增加調試信息
# 編譯匯編代碼(添加-g選項)
riscv64-unknown-elf-as -g -march=rv64g -mabi=lp64 add.s -o add.o# 鏈接(保持-g選項)
riscv64-unknown-elf-ld -g -T link.ld add.o -o add.elf# 檢查文件是否包含調試信息
riscv64-unknown-elf-readelf -S add.elf | grep debug# 輸出示例(應包含多個debug_*節)
.debug_info PROGBITS 0000000000000000 000002e0
.debug_abbrev PROGBITS 0000000000000000 000006e8
.debug_line PROGBITS 0000000000000000 000007e0
- 啟動QEMU并暫停執行
qemu-system-riscv64 \-machine virt \-cpu rv64 \-m 128M \-nographic \-bios none \-kernel add.elf \-s -S # -s開啟GDB服務器,-S暫停執行直到GDB連接
- 啟動GDB并連接到QEMU
如果沒有安裝gdb-multiarch,先用sudo apt install gdb-multiarch進行安裝。
gdb-multiarch add.elf # 加載帶有調試信息的ELF文件# 在GDB交互式界面中執行以下命令
(gdb) set architecture riscv:rv64 # 指定目標架構
(gdb) target remote :1234 # 連接到QEMU的GDB服務器
二、基本調試流程
1. 設置斷點
(gdb) break _start # 在_start標簽處設置斷點
(gdb) break add.s:20 # 在add.s文件第20行設置斷點
(gdb) break print_string # 在print_string函數入口設置斷點
(gdb) info breakpoints # 查看已設置的斷點
2. 執行程序
(gdb) continue # 繼續執行到下一個斷點
(gdb) next # 單步執行(不進入函數)
(gdb) step # 單步執行(進入函數)
(gdb) finish # 執行完當前函數并返回
(gdb) until 25 # 執行到當前文件第25行
3. 查看源代碼/匯編
(gdb) list # 顯示當前位置附近的源代碼
(gdb) list 10,20 # 顯示第10-20行代碼
(gdb) disassemble # 反匯編當前函數
(gdb) x/i $pc # 顯示當前執行的指令
(gdb) layout asm # 切換到匯編視圖
(gdb) layout regs # 顯示寄存器窗口
三、查看寄存器
1. 查看通用寄存器
(gdb) info registers # 顯示所有寄存器
(gdb) print $a0 # 查看a0寄存器的值
(gdb) p/x $sp # 以十六進制形式查看sp寄存器
(gdb) p/a $ra # 以地址形式查看ra寄存器
(gdb) display $t0 # 每次停止時自動顯示t0寄存器
2. 查看特殊寄存器
(gdb) p $mstatus # 查看機器狀態寄存器
(gdb) p $mcause # 查看異常原因寄存器
(gdb) p $mtvec # 查看異常向量表基址
(gdb) set $pc = 0x80000010 # 修改程序計數器
四、查看內存
1. 內存查看命令
(gdb) x/10xw 0x80000000 # 從0x80000000開始,以16進制顯示10個32位字
(gdb) x/20b $sp # 從sp開始,以字節形式顯示20個值
(gdb) x/10i 0x80000000 # 從0x80000000開始,顯示10條指令
(gdb) examine/i $pc # 查看當前執行的指令
2. 內存修改命令
(gdb) set {int}0x80000010 = 42 # 將0x80000010處的32位整數設置為42
(gdb) set *0x80000014 = 0x1234 # 將0x80000014處的16位值設置為0x1234
五、調試實戰示例
假設我們要調試add.s
程序中的加法操作:
- 設置斷點并開始執行
(gdb) break _start
(gdb) continue
- 單步執行到加法操作
(gdb) step # 執行初始化棧指針
(gdb) step # 加載a0 = 10
(gdb) step # 加載a1 = 20
(gdb) step # 執行add a2, a0, a1
- 驗證加法結果
(gdb) p/x $a0 # 應該顯示0xa (10)
(gdb) p/x $a1 # 應該顯示0x14 (20)
(gdb) p/x $a2 # 應該顯示0x1e (30)
- 查看內存中的變量
(gdb) x/i $pc # 顯示當前指令
(gdb) x/10xw 0x80000000 # 查看.text段內容
(gdb) x/10xw $sp # 查看棧內容
- 調試UART輸出
(gdb) break print_string
(gdb) continue
(gdb) step # 進入print_string函數
(gdb) p $a0 # 查看要打印的字符串地址
(gdb) x/s $a0 # 以字符串形式顯示內存內容
六、高級調試技巧
1. 條件斷點
(gdb) break add.s:25 if $a0 > 10 # 當a0>10時觸發斷點
2. 自動顯示
(gdb) display/i $pc # 每次停止時顯示當前指令
(gdb) display/x $a0 # 每次停止時顯示a0寄存器
3. 內存斷點(觀察點)
(gdb) watch *0x80000020 # 當地址0x80000020被修改時觸發
4. 回溯調用棧
(gdb) backtrace # 顯示調用棧
(gdb) frame 1 # 切換到第1層棧幀
七、退出調試
(gdb) disconnect # 斷開與QEMU的連接
(gdb) quit # 退出GDB
八、退出QEMU
退出qemu-system-riscv64
通常可以使用快捷鍵或通過監視器界面來操作,具體方法如下:
- 使用快捷鍵:按下
Ctrl + a
,然后松開這兩個鍵,再按下x
,即可直接終止QEMU進程,回到shell界面。 - 通過監視器界面:首先按下
Ctrl + a
,然后松開,再按下c
,這將退出當前操作系統的shell界面,進入QEMU的監視器界面。接著在監視器界面中,輸入q
并按回車鍵,即可完全退出QEMU。
總結
通過上述命令,你可以全面掌控程序的執行流程,監控寄存器和內存的變化,從而快速定位問題。GDB的強大功能不僅適用于調試簡單程序,也是開發復雜嵌入式系統的必備工具。建議結合具體程序多練習,熟練掌握這些命令將顯著提高開發效率。