GDB 使用手册(详细版)
面向嵌入式固件调试(RISC-V / 裸机 / core dump 分析)的 GDB 实战文档。
所有标 ✅实测 的结论都在本机
riscv64-unknown-elf-gdb上跑过,附真实输出;标 📖手册 的是 GDB 通用行为,未在本机复现(多数需要 live target)。
| 项 | 值 |
|---|---|
| 文档版本 | v1.0 |
| 编写日期 | 2026-09-28 |
| 基线 GDB | GNU gdb (GDB) 16.2.90.20250210-git(Nuclei RISC-V 工具链自带) |
| 路径 | E:/software/nuclei_riscv_gcc/bin/riscv64-unknown-elf-gdb.exe |
| 测试夹具 | tools/mk_minicore.py → demo.core(本目录内,可复现) |
0. 本机基线:先认清手里这把 GDB
调试工具的行为高度依赖编译配置 。同一句 set logging enabled on、
同一个 info unwinder,在不同 GDB 上命运完全不同。所以先看基线。
bash
$ /e/software/nuclei_riscv_gcc/bin/riscv64-unknown-elf-gdb.exe --version
GNU gdb (GDB) 16.2.90.20250210-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
show configuration 的关键几行(去掉无关项):
configure --host=x86_64-w64-mingw32 --target=riscv64-unknown-elf
--with-expat <- 支持 XML target description(远程目标自带寄存器布局)
--without-python <- ★ 没有 Python!脚本化/pretty-printer/unwinder 全没了
--without-python-libdir
--without-debuginfod <- 没有 debuginfod,符号必须自己喂
--with-curses
--enable-tui <- TUI 可用
--disable-source-highlight
--disable-threading
0.1 能力矩阵(全部实跑验证)
| 能力 | 结果 | 说明 |
|---|---|---|
| 架构选择 | ✅ | 合法参数仅 4 个:riscv / riscv:rv64 / riscv:rv32 / auto |
| TUI(分屏源码/寄存器) | ✅ | --enable-tui |
| core 文件加载 | ✅ | 见第 12 章,需要 core 自身格式合法 |
target remote / extended-remote |
✅ | 走 GDB RSP 协议 |
target sim |
✅ | 编译内置模拟器(riscv64-unknown-elf-run 同一套) |
record full(反向调试) |
✅ | 命令存在;裸机远程目标上实际可用性另说 |
maint packet / set debug remote |
✅ | 本机最有价值的两个命令,见 §11.4 |
compare-sections |
✅ | 校验已经烧进芯片的镜像和本地 ELF 是否一致 |
dprintf |
✅ | 动态 printf 断点 |
frame apply |
✅ | 批量对多帧执行命令 |
| Python 脚本 | ❌ | Python scripting is not supported in this copy of GDB. |
info unwinder |
❌ | Undefined info command: "unwinders" |
disable unwinder |
❌ | 被解析成 disable <断点号>:Bad breakpoint number 'unwinder' |
checkpoint |
❌ | Linux fork 快照机制,本工具链没有 |
info proc mappings |
⚠️ | 裸机 core 上静默无输出 ,改用 info files(§12.5) |
bash
# 实测原始输出(这三条务必记住)
$ gdb -q -batch -ex "python print('PY_OK')"
Python scripting is not supported in this copy of GDB.
$ gdb -q -batch -ex "info unwinders"
Undefined info command: "unwinders". Try "help info".
$ gdb -q -batch -ex "disable unwinder global foo"
Bad breakpoint number 'unwinder'
⚠️ 这条影响你现有的
gdb_core_btfix项目 :项目 README 里的
(gdb) info unwinder和(gdb) disable unwinder global trap-frame两行在这把 GDB 上是无效命令 。BTW:
disable的子命令只有
breakpoints/display/mem/probes四个,没有unwinder。也就是说------装了也没法列出来、也没法关掉 ,只能靠
bt结果自己判断是否生效。
结论先行 :这台机器上调试 RISC-V,
能用的只有「命令脚本 + MI + maint packet」这条线,Python 那条线是断的。
所有依赖 Python 的方案(pretty-printer、Python unwinder、btfix.py)在本机不可执行,
只能拿去做「没 Python 时的备选路线」。
1. GDB 到底在调试什么:四个部件
搞清这台 GDB 的边界之后,先建立结构认知。GDB 不是一个程序,是四个部件的组合:
┌──────────────────────────────────────────────────────────────┐
│ GDB 前端(你敲命令的地方) │
│ - CLI 命令行 / TUI 分屏 / MI 机器接口(给 IDE 用) │
├──────────────────────────────────────────────────────────────┤
│ 符号层:把地址翻译成 函数名/变量名/行号/类型 │
│ - 来源:ELF 的 .symtab/.debug_*(DWARF) │
│ - 没有它 → 全是 `??`,能看寄存器但看不懂意思 │
├──────────────────────────────────────────────────────────────┤
│ target 层:抽象出的「被调试对象」 │
│ - target exec 只加载 ELF,不执行 │
│ - target core 离线快照,★只读★ │
│ - target remote 通过 RSP 协议连真人(JTAG/OpenOCD/gdbserver)│
│ - target sim 内置模拟器 │
├──────────────────────────────────────────────────────────────┤
│ 通信层:GDB ↔ 目标 的字节流 │
│ - 串口 / TCP / 管道;协议是 GDB RSP(一堆 $...#xx 包) │
└──────────────────────────────────────────────────────────────┘
三条推论,直接决定你踩不踩坑:
- 符号和运行状态是两回事。
file fw.elf只加载符号不加载内存;
core-file fw.core只加载内存快照不加载符号映射。两个都要喂,bt才有意义。 - core 是只读 target。 所有
set/call/断点类操作都会失败,因为底层
target_store_registers()在 core 的 target 实现里是空的。这是后面第 12 章的根因。 - 远程调试的一切问题,本质上都是 RSP 包的问题。 所以
set debug remote 1
和maint packet是嵌入式场景的终极武器。
2. 该用哪个 GDB
| 场景 | 用哪个 | 理由 |
|---|---|---|
| 调试 RISC-V 裸机 / ELF | riscv64-unknown-elf-gdb(本机已有) |
target 编进了 riscv |
| 同上,但没有交叉 GDB | 任意 GDB + set architecture riscv:rv32 |
本机验证过:无 target 支持时该命令报 Invalid architecture |
| 调试 Linux 用户态进程 | 发行版 gdb / gdb-multiarch |
需要 ptrace、线程、共享库支持 |
| 调试嵌入式 Linux | gdb-multiarch + gdbserver |
双架构(host ARM/MIPS + target) |
| 调试 x86 Windows 程序 | MinGW-w64 的 gdb.exe |
原生 host,能用 /c/... 之外的 Windows 路径 |
本机情况:只有一次 GDB(RISC-V 交叉版),宿主 GDB 不存在。
bash
$ which gdb gdb.exe
# (无输出)
$ gdb --version
bash: gdb: command not found
所以:本机所有 GDB 操作都必须写全路径或先做别名。
bash
# Git Bash 里建议这样(写进 ~/.bashrc)
alias gdb='/e/software/nuclei_riscv_gcc/bin/riscv64-unknown-elf-gdb.exe'
3. 启动方式(6 种)
bash
# ① 只加载可执行文件(不运行)
gdb fw.elf
# ② ELF + core,最常用的离线分析组合
gdb fw.elf fw.core
# ③ 附加到正在跑的进程(Linux)
gdb -p 1234
# ④ 批量模式:执行完就退,适合写脚本/CI
gdb -q -batch -ex "bt" -ex "info registers" fw.elf fw.core
# ⑤ 用命令脚本(-x 可叠加多个)
gdb -q fw.elf fw.core -x btfix.gdb -x dump_all.gdb
# ⑥ 带程序参数启动
gdb --args ./a.out arg1 arg2
常用命令行开关:
| 开关 | 作用 |
|---|---|
-q / --quiet |
不打印版权横幅(脚本里必加) |
-batch |
无人值守;出错则退出码非 0 |
-ex CMD |
执行一条命令,可重复 |
-x FILE |
执行脚本文件,可重复 |
-p PID |
attach 到进程 |
-c / --core FILE |
指定 core |
--args |
之后全当被调程序的参数 |
--interpreter=mi2 |
切 MI 机器接口(给 IDE/自动化用) |
-nx |
不读 ~/.gdbinit(排障用:先排除配置污染) |
排障第一招 :行为诡异时先加
-nx再跑一遍。80% 的"GDB 抽风"是你的
.gdbinit或某个-x脚本留下的后遗症。
3.1 路径的坑(✅实测,Windows 专属)
GDB 是原生 Windows 程序,它不认识 Git Bash 的 POSIX 路径:
bash
# ❌ 实测失败:GDB 把 /c/... 当成不存在的文件
$ gdb -q -batch -ex "core-file /c/Users/gongyi/x.core"
... No such file or directory.
# ✅ 正确:用 Windows 路径(正斜杠即可,不必转义反斜杠)
$ gdb -q -batch -ex "core-file C:/Users/gongyi/x.core"
同样受影响的还有 :dump binary memory 的输出路径(写 /tmp/x.bin 会
静默写到你没想到的地方,ls /tmp/x.bin 报 No such file ------ 实测)。
规则:凡是喂给 GDB 的路径,一律写 C:/...;只有喂给 bash 的才用 /c/...。
4. 命令速查表
4.1 加载与切换被调试对象
| 命令 | 说明 |
|---|---|
file F / exec-file F |
加载可执行文件(exec 只加载,不含符号) |
symbol-file F |
只加载符号表 |
add-symbol-file F ADDR |
给动态加载的模块/固件补符号,ADDR 是它的加载地址 |
core-file F |
加载 core(等价于 target core F) |
target exec F / target core F |
以 target 语法加载 |
target remote HOST:PORT |
连接远程 stub(§11) |
target extended-remote HOST:PORT |
可反复 detach/attach、可跑多进程的远程 |
target sim |
内置模拟器 |
info files |
看当前加载了哪些、内存覆盖范围 |
info target |
同上,格式更详细 |
4.2 运行控制
| 命令 | 缩写 | 说明 |
|---|---|---|
run |
r |
从头开始跑(set args 可改参数) |
start |
--- | 在 main 设临时断点并停住 |
continue |
c |
继续 |
next |
n |
单步,跨过函数调用 |
step |
s |
单步,进入函数 |
nexti / stepi |
ni / si |
汇编级(不跨/跨) |
finish |
fin |
跑到当前函数返回 |
until |
u |
跑到某行/某地址(用于跳出循环) |
advance LOC |
adv |
临时断点 + 继续,一步到位 |
jump LOC |
j |
★直接改 PC 跳转,不改栈,危险 |
return [EXPR] |
--- | 从当前函数返回,可指定返回值 |
call FN(args) |
--- | 调用目标里的函数 |
signal SIG |
--- | 继续并投递信号(如 signal 0 表示忽略) |
kill |
k |
结束被调试程序 |
4.3 断点
| 命令 | 说明 |
|---|---|
break LOC |
普通断点;LOC 可为 file:line、func、*ADDR |
tbreak LOC |
一次性断点,命中即删 |
hbreak LOC |
硬件断点(★Flash/ROM 代码必用) |
rbreak REGEX |
按正则给一批函数下断(含"未限定的全局函数") |
thbreak LOC |
一次性硬件断点 |
break LOC if COND |
条件断点 |
watch EXPR |
写监视点(值变化时停) |
rwatch EXPR / awatch EXPR |
读 / 读写监视点 |
watch -l EXPR |
监视地址而非表达式值(防优化把变量优化掉) |
catch syscall NAME |
系统调用断点(Linux) |
catch throw / catch catch |
C++ 异常 |
catch signal SIG |
信号断点 |
catch exec / catch fork / catch vfork |
进程事件 |
dprintf LOC, "fmt", args |
动态 printf,不停机 |
ignore N COUNT |
忽略断点 N 的前 COUNT 次命中 |
commands N ... end |
给断点挂命令序列(§5.4) |
condition N COND |
事后给断点加条件 |
disable N / enable N |
关/开 |
enable once N / enable delete N |
开一次后自动关/自动删 |
delete N / clear LOC |
删断点(clear 按位置删) |
info breakpoints |
列出所有断点 |
4.4 栈与帧
| 命令 | 说明 |
|---|---|
backtrace / bt |
调用栈 |
bt 10 / bt -10 |
只看内侧 10 帧 / 最外侧 10 帧 |
bt full |
附带每帧局部变量(输出极长,重定向到文件) |
frame N / f N |
切到第 N 帧 |
up / down |
上/下一帧(up 3) |
info frame |
当前帧详情(含"是否保存了 PC",★关键) |
info args / info locals |
参数 / 局部变量 |
info frame 的补充:info registers |
该帧的寄存器视图 |
frame apply all -s bt |
对每帧执行命令(GDB 9+) |
select-frame / up-silently |
不打印的切换,用于脚本 |
4.5 数据与内存
| 命令 | 说明 |
|---|---|
print EXPR / p |
求值打印 |
p/x p/d p/t p/c p/s p/a |
十六进制/十进制/二进制/字符/字符串/地址 |
p *ptr@LEN |
打印数组前 LEN 个元素(人造数组) |
p $sp, p $pc, p $x8/s0 |
伪寄存器 |
ptype TYPE |
看类型定义 |
ptype /o STRUCT |
看结构体布局与洞(padding),GDB 8.2+ |
info registers |
整数寄存器 |
info all-registers |
全部(含浮点/CSR) |
info registers $pc |
单个 |
x/FMT ADDR |
按地址看内存(§4.6) |
set var X = V |
改变量 |
set {int}0x80000000 = 0x1234 |
按类型改变量(也能写寄存器映射) |
set $myvar = ... |
自定义便捷变量($ 开头) |
display EXPR |
每次停机自动打印 |
undisplay N / disable display N |
取消 |
whatis EXPR / info address SYM |
类型 / 符号地址 |
info symbol ADDR |
反向:地址 → 符号(★core 分析常用) |
find START, END, VAL |
搜内存 |
dump binary memory FILE START END |
把内存导出成文件 |
restore FILE binary ADDR |
反向:把文件写进目标内存 |
compare-sections |
比对目标内存与 ELF 内容 |
set max-value-size unlimited |
解除大对象打印限制 |
4.6 x 命令的完整语法(最容易被写错的一个)
格式:x/[数量][格式][单位] 地址
| 符号 | 格式 | 符号 | 单位 |
|---|---|---|---|
x |
十六进制 | b |
字节 (8bit) |
d |
十进制 | h |
半字 (16bit) |
u |
无符号十进制 | w |
字 (32bit) |
o |
八进制 | g |
巨字 (64bit) |
t |
二进制 | ||
a |
地址 | ||
c |
字符 | ||
s |
字符串 | ||
i |
汇编(★反汇编用这个) |
gdb
(gdb) x/4xw 0x80000000 # 4 个 32 位字,十六进制
(gdb) x/10i $pc # 从 PC 反汇编 10 条指令
(gdb) x/s 0x80001000 # 当字符串打印
(gdb) x/16bx &buf # 16 个字节
本机实测(core 上的内存读取):
gdb
(gdb) x/4xw 0x80000000
0x80000000: 0x564f4841 0x726b645d 0x8e878079 0xaaa39c95
4.7 源码 / 汇编 / TUI
| 命令 | 说明 |
|---|---|
list / l |
看源码 |
list FUNC / list *ADDR |
定位 |
directory DIR / dir |
加源码搜索路径(编译机路径对不上时必用) |
set substitute-path FROM TO |
重写源码路径前缀(/home/build/... → E:/src/...) |
info line LOC |
行号 ↔ 地址 |
info source |
当前源码文件信息 |
disassemble / disassemble FN |
反汇编 |
disassemble /r FN |
带机器码字节(对比 objdump 时有用) |
disassemble /s FN |
带源码交错(GDB 8.1+) |
set disassemble-next-line on |
每次停机自动反汇编下一条 |
set listsize 30 |
list 显示行数 |
tui enable / `layout src |
asm |
| `focus cmd | src |
winheight src +5 |
调窗口高度 |
update |
刷新 TUI |
TUI 快捷键 (tui enable 后):
| 按键 | 作用 |
|---|---|
Ctrl-x a |
开/关 TUI |
Ctrl-l |
重绘(花屏时救命) |
Ctrl-x o |
切换焦点窗口 |
Ctrl-x 1 |
单窗口 / Ctrl-x 2 双窗口 |
Ctrl-x s |
切 single-key 模式(之后 n/s/c 不用回车) |
RISC-V 没有
set disassembly-flavor(那是 x86 的 AT&T/Intel 开关)。本机
set architecture只接受riscv/riscv:rv64/riscv:rv32/auto。
4.8 多线程 / 多进程 / 多 inferior
| 命令 | 说明 |
|---|---|
info threads |
线程列表(* 表示当前) |
thread N |
切线程 |
thread apply all bt |
★所有线程打栈(死锁/挂死分析起手式) |
thread apply all bt full |
带上局部变量(输出巨大) |
thread apply 1-4 bt |
指定范围 |
| `set scheduler-locking on | step |
set non-stop on |
非全停模式(调试多核时有用) |
| `set follow-fork-mode parent | child` |
| `set detach-on-fork on | off` |
| `set follow-exec-mode same | new` |
add-inferior / clone-inferior |
增/克隆 inferior |
inferior N |
切 inferior |
set print thread-events off |
关掉线程创建/退出噪音(嵌入式常关) |
4.9 环境与信息
| 命令 | 说明 |
|---|---|
set args A B C / show args |
程序参数 |
set environment VAR=VAL |
环境变量 |
show version / show configuration |
版本与编译配置(★排障第一步) |
show architecture |
当前架构 |
set architecture riscv:rv32 |
强制架构 |
info all-registers |
全寄存器 |
maintenance info sections |
★目标的内存段布局(core 上也有效) |
info sharedlibrary |
共享库加载情况 |
set sysroot PATH |
交叉调试找库的根 |
set solib-search-path PATH |
共享库搜索路径 |
set debug-file-directory PATH |
分离调试符号的目录 |
apropos WORD |
★按关键字搜命令(不记得命令名时用) |
help CMD / help CMD SUB |
帮助 |
set pagination off |
关分页(脚本必加) |
set confirm off |
关确认(脚本必加) |
set height 0 / set width 0 |
不限终端尺寸 |
set logging enabled on / set logging file F |
把会话记进文件 |
set $var = ... |
便捷变量 |
5. 断点体系(深入)
5.1 五种"断"
| 类型 | 机制 | 限制 |
|---|---|---|
| 软件断点 | 把指令替换成 trap 指令 | 不能在 Flash/ROM 上用;需要能写内存 |
硬件断点 hbreak |
用 CPU 的断点寄存器 | 数量有限(RISC-V 通常 2~4 个) |
监视点 watch |
同样占用硬件资源 | 数量同样有限;-l 版盯地址更可靠 |
事件断点 catch |
GDB 在事件上停 | 依赖 target 支持(syscall 需 Linux) |
动态打印 dprintf |
不停机打日志 | 需要 live target |
裸机/Flash 场景第一条纪律 :代码在 Flash 里时,
用
hbreak,而不是break。软断点会写坏 Flash 内容(或直接写不进去)。
5.2 位置表达式的写法
gdb
break main # 函数名
break func.c:42 # 文件:行
break *0x80001234 # 地址(前面必须带 *)
break *main+16 # 函数+偏移
break "func.c":42 # 文件名带特殊字符时加引号
break kernel_init if ctx->state==3 # 条件
break -qualified ns::Cls::fn # 限定名
5.3 条件与命中计数
gdb
(gdb) break process_cmd if lba == 0x1234 # 条件断点
(gdb) condition 2 lba > 0x1000 # 事后加条件
(gdb) condition 2 # 清掉条件
(gdb) ignore 3 100 # 前 100 次命中不停
⚠️ 条件断点的性能陷阱 :条件是在每次命中时由 GDB 求值的 ------意味着
每次命中都要停下、读内存、求值、继续。循环里的条件断点会让程序慢几百倍。
替代方案:
- 用
dprintf打印(不停机); - 用
ignore先跳过前 N 次; - 在目标代码里自己加判断(改代码比改调试条件快)。
5.4 断点命令序列(自动化的核心)
gdb
(gdb) break ftl_write_page
(gdb) commands
> silent # 不打印 "Breakpoint 1, ..."
> printf "lba=%#x len=%u\n", lba, len
> if len > 64
> bt 5
> end
> continue
> end
要点:
silent放在第一条,否则每次命中都先刷一大段提示;printf里的%#x是十六进制带0x前缀(GDB 自己的 printf 方言);- 以
end结尾(脚本文件里也一样)。
5.5 保存/复用断点
gdb
(gdb) save breakpoints my.bp # 存盘
(gdb) source my.bp # 下次一键恢复
6. 反向调试(时间倒流)
gdb
(gdb) record full # 开始记录(性能大降,内存涨)
(gdb) continue
(gdb) reverse-continue # 倒着跑
(gdb) reverse-step # 倒着单步
(gdb) reverse-stepi # 倒着单步(汇编)
(gdb) record stop # 停止记录,释放
(gdb) info record # 看记录了多少条
| 要点 | 说明 |
|---|---|
| 只对 live target 有效 | core 是快照,没有时间轴 |
record full 会显著变慢 |
别在长跑场景一直开着 |
| 内存有限 | 默认记录上限可调 set record full insn-number-max |
record btrace |
需要 Intel PT / ARM CoreSight,RISC-V 一般没有 |
真实价值场景:"这个变量什么时候被改坏的?"
→ watch var + record full + 命中后 reverse-continue
→ 直接落到上一次修改点的前一条指令。
7. 调用栈与帧(含"栈断了"的处置)
7.1 基本功
gdb
(gdb) bt
#0 ftl_write_page (lba=291, len=8) at ftl_write.c:412
#1 0x80005678 in ufs_write_handler () at ufs_cmd.c:88
#2 0x80001234 in main () at main.c:31
(gdb) frame 1 # 切到第 1 帧
(gdb) info args # 看参数
(gdb) info locals # 看局部变量
(gdb) info frame # 看这一帧的元信息
7.2 info frame 输出怎么读(★这是诊断栈的关键)
本机实测(core,无符号):
gdb
(gdb) info frame
Stack level 0, frame at 0x0:
pc = 0x80001234; saved pc = <not saved>
Outermost frame: outermost
Arglist at unknown address.
Locals at unknown address, Previous frame's sp in sp
逐行解读:
| 行 | 含义 | 异常信号 |
|---|---|---|
frame at 0x0 |
该帧的栈基址 | 0x0 = GDB 找不到有效栈帧 |
pc = ...; saved pc = <not saved> |
本帧 PC / 上一帧的返回地址 | <not saved> = 没法往上回 ← 栈断的根因 |
Outermost frame: outermost |
GDB 认为这是最外层 | 明明在函数中间却说是最外层 = 明显错误 |
Arglist at unknown address |
参数地址未知 | 缺 DWARF / 缺 CFI |
Locals at unknown address |
局部变量地址未知 | 同上 |
推论:bt 只能走到这一帧,是因为 GDB 不知道"上一帧的 PC 保存在哪"。
这就是 Backtrace stopped: frame did not save the PC 的含义。
7.3 栈断了的三种修法
| 方案 | 做法 | 优点 | 缺点 | 本机可用 |
|---|---|---|---|---|
| A. 编译期根治 | 加 CFI / 保留帧指针 | 一次修好,永久有效 | 涨 FLASH | ✅ |
| B. GDB Python unwinder | 注册规则告诉 GDB 帧信息在哪 | 不改固件、不改 core | 要 GDB 有 Python | ❌ 本机没 Python |
| C. 离线改 core | 改 core 里 NT_PRSTATUS 的寄存器块 | 不改固件、不需要 Python | 只挪了起点,治标 | ✅ |
A 的具体做法(按模块开,注意体积代价):
makefile
CFLAGS += -funwind-tables # 生成 .eh_frame(GDB 才能做 CFI unwinding)
CFLAGS += -fno-omit-frame-pointer # 保留 s0 帧指针链(最省事的兜底)
链接脚本必须保留:
KEEP(*(.eh_frame))
KEEP(*(.eh_frame_hdr))
⚠️ 编译时把 ELF 和 core 都喂给 GDB :
gdb fw.elf fw.core。因为 CFI unwinder 要靠 prologue 扫描读
.text指令,不给 ELF,GDB 读不到指令,
.eh_frame也白给。
7.4 core 上为什么不能 set $pc(★根因 + 实测)
gdb
(gdb) set $pc = 0x80009999
You can't do that without a process to debug.
(gdb) info registers pc
pc 0x80001234 0x80001234 <- 没改成功
根因 :set $reg 最终走 target_store_registers(),而 core 的 target 是只读的。
结论 :"改寄存器挪 bt 起点"只在 live 调试下可用
(JTAG / OpenOCD / qemu -s / 模拟器),core 场景必须换路走(上表 B 或 C)。
7.5 方案 C 的实现要点(离线改 core 寄存器)
RISC-V 的 core 里,寄存器排布遵循 GDB 约定:
reg[0]=pc reg[1]=ra reg[2]=sp reg[3]=gp reg[4]=tp
reg[5..7] = t0..t2
reg[8] = s0/fp reg[9] = s1
reg[10..17]= a0..a7
reg[18..27]= s2..s11
reg[28..31]= t3..t6
- 共 32 个字 ,
x0不占位置 (所以reg[1]就是ra,不是x1的错位); - 32 位
elf_prstatus里pr_reg的偏移 = 72; - 结构体总长 = 72 + 32×4 + 4(
pr_fpvalid) = 204。
这段结论已用本机 GDB 反向验证 :tools/mk_minicore.py 按上述偏移写入
pc/ra/sp/s0 后,GDB 读出完全一致(§12.6 有完整记录)。
7.6 RISC-V 特有的栈回溯难点
| 难点 | 说明 |
|---|---|
| 无帧指针约定 | RISC-V ABI 不强制 s0 作 FP,编译器默认可能省掉 |
ra 会被覆盖 |
非叶函数压栈保存 ra,但没有 CFI 时 GDB 不知道压在哪 |
| 尾调用优化 | -O2 会把 call 变成 j,栈上少一层,回溯结果"少帧"是对的 |
| 中断/trap | trap entry 手工保存现场,编译器 CFI 完全对不上 → 栈必断 |
多处 -mcmodel / 链接脚本 |
.eh_frame 未 KEEP 时被 GC 掉,DWARF 在也救不了 |
8. 变量、表达式与内存
8.1 打印的讲究
gdb
(gdb) p/x reg # 十六进制
(gdb) p *(struct ftl_ctx *)0x80002000 # 按结构体解引用
(gdb) p *lun_ptr@8 # 当数组看 8 个元素
(gdb) p pa(ce, plane, block, page) # 调用宏?不行,宏在编译期就没了
(gdb) p/d 0b1010 # 二进制字面量 GDB 认
(gdb) set $i = 0 # 便捷变量
(gdb) p $i++ # 每次打印自增 1
(gdb) p $1 # 值历史:上一次、上上次的 print 结果
(gdb) p $_ # x 命令最后一次看的地址
宏不能打印 (
p SOME_MACRO会报No symbol "SOME_MACRO")。变通:编译时加
-g3,GCC 会把宏定义放进 DWARF,GDB 就能info macro/macro expand。
8.2 打印相关设置(输出又长又乱时的救命设置)
gdb
set print pretty on # 结构体缩进多行(默认 off,实测可开)
set print elements 0 # 数组/字符串不限长度(0 = unlimited)
set print array on # 数组每个元素一行
set print null-stop on # 遇 \0 停(字符串)
set print object on # C++ 按动态类型打印
set print demangle on # C++ 名字反修饰
set print repeats 0 # 不折叠重复元素
set max-value-size unlimited # 解除大对象 64KB 限制
set pagination off # 关分页
8.3 内存读写
gdb
(gdb) x/8xw 0x80000000 # 读
(gdb) set {unsigned int}0x80000000 = 0xdeadbeef # 写(live target)
(gdb) set {char}0x80000000 = 1
(gdb) find 0x80000000, 0x8000ffff, 0x1a2b3c4d # 搜索
(gdb) dump binary memory ram.bin 0x80000000 0x80001000 # 导出
(gdb) restore ram.bin binary 0x80000000 # 写回
⚠️ 写内存的前提 :set mem inaccessible-by-default off。
默认情况下 GDB 拒绝访问"未知区域",裸机上几乎所有地址都是"未知区域",
所以这句在嵌入式调试里几乎是必配。
8.4 寄存器
gdb
(gdb) info registers # 整数寄存器
(gdb) info all-registers # 全部(含 CSR / 浮点)
(gdb) info registers pc ra sp s0 # 指定几个(可混用 ABI 名与 x 名)
(gdb) p $pc # 当值用
(gdb) set $sp = 0x8000ff00 # live 才行;core 上必失败
(gdb) info registers $mstatus # CSR(需要 target description 支持)
本机实测(core 加载后):
gdb
(gdb) info registers pc ra sp s0
pc 0x80001234 0x80001234
ra 0x80005678 0x80005678
sp 0x8000ff00 0x8000ff00
s0 0x8000ff00 0x8000ff00
9. 远程 / 嵌入式调试(JTAG、OpenOCD、gdbserver)
9.1 三条通路对比
| 通路 | 连接方式 | 典型场景 |
|---|---|---|
| OpenOCD + JTAG | target extended-remote localhost:3333 |
真实芯片,可下 Flash、可复位 |
| gdbserver | target remote TARGET_IP:2345 |
嵌入式 Linux 用户态 |
| QEMU / 模拟器 | target remote :1234 / target sim |
无硬件时的功能验证 |
9.2 OpenOCD 标准起手式
bash
# 终端 1:起 OpenOCD(以 RISC-V 为例,配置文件按你的调试器改)
openocd -f interface/ftdi/xxx.cfg -f target/riscv.cfg
gdb
# 终端 2:
(gdb) file fw.elf
(gdb) set mem inaccessible-by-default off
(gdb) target extended-remote localhost:3333
(gdb) monitor reset halt # ★monitor = 给 OpenOCD 发命令
(gdb) monitor flash write_image erase fw.bin 0x80000000
(gdb) load # 或用 GDB 自己的 load
(gdb) compare-sections # ★校验烧进去的和本地 ELF 是否一致
(gdb) hbreak main # Flash 里必须用硬件断点
(gdb) continue
9.3 monitor 与 maint packet 的区别
| 命令 | 发给谁 | 用途 |
|---|---|---|
monitor CMD |
gdbserver / OpenOCD(GDB 之外的远端代理) | 复位、烧 Flash、查目标状态 |
maint packet HEX |
直接构造 RSP 包 | ★绕过 GDB 逻辑,直接问 stub"你会吗" |
gdb
(gdb) monitor help # 先看远端支持什么(实测命令存在)
(gdb) maint packet qSupported # 问 stub 支持哪些包
(gdb) maint packet qXfer:features:read:target.xml:0,fff # 取 target description
9.4 调试远程协议本身(嵌入式排障王牌)
gdb
(gdb) set debug remote 1 # 打印每一次 RSP 包收发
(gdb) set debug remote 0 # 关掉
连接失败时它能直接告出"哪个包没被支持":
Sending packet: $qSupported:multiprocess+;...:...#xx...Ack
Packet received: PacketSize=1000;qXfer:features:read+;...
典型症状 → 结论对照:
| 现象 | 看包得到的结论 |
|---|---|
连上了但 info registers 全是 0 |
stub 没实现 g 包,或不认识 arch |
| 断点设不上 | 没实现 Z0(软件断点)/ Z1(硬件断点) |
| 单步卡死 | s 包被 stub 当成了 continue |
| 读 Flash 失败 | m 包返回 EXX,通常是 stub 不允许读该区域 |
9.5 远程相关的重要设置
gdb
set remotetimeout 20 # 远端慢/Flash 编程时调大(默认 2 秒易超时)
set remote hardware-breakpoint-limit 4 # 告诉 GDB 硬件断点上限
set remote memory-read-packet-size 0x400
set remote memory-write-packet-size 0x400
set sysroot /path/to/target-root # 交叉环境找库
set solib-search-path /path/to/libs
9.6 裸机 / 无 OS 场景的必配清单
gdb
set confirm off
set pagination off
set height 0
set mem inaccessible-by-default off # ★最关键
set print thread-events off
set remotetimeout 20
set architecture riscv:rv32 # 若 GDB 认不出目标架构
set $sp = 0x8000ff00 # 手工设栈(有时 stub 不给)
10. 脚本化:命令脚本、MI、以及"没有 Python 怎么办"
10.1 命令脚本(本机唯一可用的自动化通道)
dump.gdb:
gdb
set pagination off
set confirm off
set print pretty on
set logging file gdb_session.log
set logging enabled on
echo \n===== REGISTERS =====\n
info registers
echo \n===== BACKTRACE =====\n
bt full
echo \n===== SECTIONS =====\n
maintenance info sections
echo \n===== STACK MEM (256B) =====\n
x/64xw $sp
set logging enabled off
quit
bash
gdb -q -batch fw.elf fw.core -x dump.gdb
10.2 自定义命令(define)
gdb
define ctxdump
silent
printf "--- ctx @ %p ---\n", $arg0
set $c = (struct ftl_ctx *)$arg0
printf "state=%d lba=%#x err=%d\n", $c->state, $c->lba, $c->err
printf "pc=%#x sp=%#x\n", $pc, $sp
end
document ctxdump
用法: ctxdump ADDR 打印 FTL 上下文
end
define walk_list
set $p = $arg0
set $n = 0
while $p != 0 && $n < 32
printf "[%2d] %p\n", $n, $p
set $p = ((struct node *)$p)->next
set $n = $n + 1
end
printf "total %d nodes\n", $n
end
要点:
| 语法 | 说明 |
|---|---|
$arg0 $arg1 ... |
位置参数(最多 $arg9,再多用 $argc/$argv 不便,改用脚本文件) |
$argc |
参数个数 |
silent |
不打印 define 调用回显 |
while ... end / if ... else ... end |
控制流 |
document NAME ... end |
写 help NAME 时显示的说明(强烈建议写) |
define 放在 .gdbinit 里 |
每次启动自动可用 |
hook-stop |
★每次停机自动执行的钩子 |
hook-stop 实战(每次停机自动打印关键状态):
gdb
define hook-stop
printf ">> pc=%#010x ra=%#010x sp=%#010x\n", $pc, $ra, $sp
x/2i $pc
end
10.3 .gdbinit 自动加载与安全路径
GDB 只会在"安全路径"内自动加载 .gdbinit:
gdb
(gdb) show auto-load safe-path
(gdb) set auto-load safe-path / # 打开所有(本地开发方便,生产慎用)
set auto-load local-gdbinit on
10.4 用 -batch + 退出码做 CI/回归
bash
gdb -q -batch fw.elf fw.core \
-ex "bt" \
-ex "info registers" \
-ex "quit" > out.txt 2>&1
echo "退出码=$?" # 命令失败时非 0,可直接卡 CI
10.5 MI 接口(给 IDE / 自动化用)
bash
gdb -q --interpreter=mi2 fw.elf
=thread-group-added,id="i1"
~"Reading symbols from fw.elf...\n"
(gdb)
-break-insert main
^done,bkpt={number="1",type="breakpoint",...}
-exec-run
*stopped,reason="breakpoint-hit",bkptno="1",frame={func="main",args=[],"file":"main.c",line":31}
特点:^done / *stopped / =notify / ~"文本" / &"日志"。
VS Code 的 cppdbg、CLion、Eclipse 走的都是这条线。
"GDB 没有 Python"时,用 MI + 自己写解析器,一样能做自动化。
10.6 没有 Python 时的替代方案(本机适用)
| 本该用 Python 做的事 | 没 Python 的替代 |
|---|---|
| pretty-printer | 手写 define + printf 拼输出 |
| Python unwinder 修栈 | 离线改 core 寄存器块(§7.5) |
| 遍历复杂数据结构 | define 里的 while 循环 + $ 便捷变量 |
| 批量处理多个 core | bash 循环 + -batch -x script.gdb |
| 复杂格式化输出 | printf + echo,或输出到 log 后用外部脚本处理 |
架构建议 :把「GDB 只负责取原始数据」+「外部脚本负责分析」分开。
用
dump binary memory把关键内存导出来,用 Python 在外面解析------这条路不依赖 GDB 的 Python,且可复现、可版本管理。
11. 推荐 .gdbinit
gdb
# ---- 基础体验 ----
set pagination off
set confirm off
set print pretty on
set print elements 0
set print array on
set print demangle on
set print thread-events off
# ---- 裸机必备 ----
set mem inaccessible-by-default off
set remotetimeout 20
# ---- 源码路径重写(按自己环境改)----
# set substitute-path /home/build/fw E:/code/ufs2.2
# directory E:/code/ufs2.2/src
# directory E:/code/ufs2.2/include
# ---- 常用快捷命令 ----
define regs
printf "pc=%#010x ra=%#010x sp=%#010x s0=%#010x\n", $pc, $ra, $sp, $s0
printf "a0=%#010x a1=%#010x\n", $a0, $a1
end
define ihere
x/12i $pc
end
define stack32
x/32xw $sp
end
define xx
x/$arg0
end
# ---- 每次停机自动显示 ----
# define hook-stop
# regs
# end
12. core dump 分析(重点章节)
12.1 先明确:core 是"只读快照"
| 能做的事 | 不能做的事 |
|---|---|
| 看寄存器、看内存、看栈 | set $reg / set var |
bt / frame / info locals |
下断点 / continue / next |
| 反汇编(需喂 ELF) | call 函数 |
| 导出内存、搜索 | record 反向调试 |
12.2 生成 core 的三种途径
| 途径 | 做法 |
|---|---|
| Linux 内核 | ulimit -c unlimited;/proc/sys/kernel/core_pattern 决定路径 |
| gdbserver | (gdb) generate-core-file fw.core |
自研(你的 panic_to_core_file.py) |
从 panic 现场内存手工拼 ELF core |
12.3 GDB 认不认你的 core,就看 ELF 头三项
这是最容易踩的坑 :core 文件本质是 ELF,如果头部几项不对,
GDB 直接拒绝,报 is not a core dump: file format not recognized。
| 字段 | 必须是 | 实测对比 |
|---|---|---|
e_type |
ET_CORE (4) |
旧合成 core 写成 NONE(0) → GDB 拒绝加载 |
e_machine |
EM_RISCV (243) |
写成 None 同样被拒 |
PT_NOTE |
含 NT_PRSTATUS,name = "CORE\0" |
没有它 → 读不到寄存器 |
bash
$ riscv64-unknown-elf-readelf -h demo.core | head -6
Class: ELF32
Data: 2's complement, little endian
OS/ABI: UNIX - System V
Type: CORE (Core file) <- ★
Machine: RISC-V <- ★
bash
$ riscv64-unknown-elf-readelf -n demo.core
Displaying notes found at file offset 0x00000074 with length 0x000000e0:
Owner Data size Description
CORE 0x000000cc NT_PRSTATUS (prstatus structure)
12.4 加载与第一轮勘察
bash
gdb -q -batch fw.elf fw.core \
-ex "info files" \
-ex "info registers" \
-ex "bt" \
-ex "x/16xw \$sp" \
-ex "maintenance info sections"
⚠️ 本机 rv32 core 若架构识别不对,必须先
set architecture riscv:rv32,否则
info registers只回一句The program has no registers now.(实测):
gdb(gdb) info registers The program has no registers now. <- 没设架构时的典型症状 (gdb) set architecture riscv:rv32 The target architecture is set to "riscv:rv32". (gdb) info registers pc 0x80001234 0x80001234 <- 正常了
12.5 怎么知道 core 覆盖了哪些内存?(★命令选择有讲究)
| 命令 | 裸机 core 上的实测结果 |
|---|---|
info proc mappings |
❌ 静默无输出 (那是 Linux /proc 的概念) |
info files |
✅ 正确:0x80000000 - 0x80001000 is load1 |
maintenance info sections |
✅ 最详细,能看到 note0 / .reg / load1 全貌 |
gdb
(gdb) info files
Local core dump file:
`C:\...\demo.core', file type elf32-littleriscv.
0x80000000 - 0x80001000 is load1
gdb
(gdb) maintenance info sections
Core file: `C:\...\demo.core', file type elf32-littleriscv.
[0] 0x0000->0x00e0 at 0x00000074: note0 READONLY HAS_CONTENTS
[1] 0x0000->0x0080 at 0x000000d0: .reg/1 HAS_CONTENTS
[2] 0x0000->0x0080 at 0x000000d0: .reg HAS_CONTENTS
[3] 0x80000000->0x80001000 at 0x00000154: load1 ALLOC LOAD READONLY HAS_CONTENTS
读法:
note0= 寄存器/进程信息所在的 NOTE 段;.reg/.reg/1= GDB 从 NOTE 里抽出来的寄存器区;load1= 唯一真正被 dump 的内存范围 ;
凡是x落到这个范围外,都会报Cannot access memory。
12.6 一个完整可复现的验证(本机实跑记录)
下面这段是真实执行结果 ,可用本目录的 tools/mk_minicore.py 自己复现:
bash
# 造 core:pc=0x80001234 ra=0x80005678 sp=0x8000ff00
python tools/mk_minicore.py demo.core
gdb
$ riscv64-unknown-elf-gdb -q -batch \
-ex "set architecture riscv:rv32" -ex "core-file demo.core" \
-ex "info registers pc ra sp s0" -ex "x/4xw 0x80000000" \
-ex "set \$pc = 0x80009999" -ex "info registers pc" -ex "bt"
The target architecture is set to "riscv:rv32".
[New process 1]
Program terminated with signal ?, Unknown signal.
#0 0x80001234 in ?? ()
pc 0x80001234 0x80001234
ra 0x80005678 0x80005678
sp 0x8000ff00 0x8000ff00
s0 0x8000ff00 0x8000ff00
0x80000000: 0x564f4841 0x726b645d 0x8e878079 0xaaa39c95
You can't do that without a process to debug.
pc 0x80001234 0x80001234
#0 0x80001234 in ?? ()
这段输出一次验证了 4 件事:
pr_reg偏移 = 72、RISC-V 寄存器顺序(reg[0]=pc, reg[1]=ra, reg[2]=sp, reg[8]=s0)正确 ------
写进去的 4 个值 GDB 一字不差地读出来了;- core 的内存映射生效(
x/4xw 0x80000000能读到内容); set $pc在 core 上必然失败 :You can't do that without a process to debug.,
且pc保持原值;- 无符号时
bt只能给出第 0 帧#0 0x80001234 in ?? ()。
signal ?, Unknown signal是 newlib 版 GDB 的信号名映射问题,不影响寄存器与内存分析。
12.7 core 分析的推荐工作流
1. 确认 core 合法性 readelf -h / -n → Type=CORE? Machine=RISC-V? NT_PRSTATUS?
2. 确认架构 set architecture riscv:rv32
3. 加载符号 gdb fw.elf fw.core
4. 看覆盖范围 info files / maintenance info sections
5. 看现场 info registers / info frame
6. 打栈 bt / bt full;断栈则走 §7.3
7. 定位崩溃点 info symbol $pc / x/10i $pc
8. 看内存 x/... / dump binary memory 导出
9. 归档 set logging file/enabled on
12.8 Backtrace stopped 的完整处置决策树
bt 停在 "frame did not save the PC"
│
├─ 有 GDB Python?
│ ├─ 有 → 写 Python unwinder,读 trap frame 里保存的 pc/ra/sp
│ │ (不改文件;但本机不可用)
│ └─ 无 ─┐
│ │
└───────────┴─ 没 Python(★本机情况)
├─ 方案 C:离线改 core 的 NT_PRSTATUS 寄存器块(§7.5)
│ 只解决 frame0 → frame1 这一跳
└─ 方案 A:重编固件,加 -funwind-tables / -fno-omit-frame-pointer
链接脚本 KEEP(.eh_frame) ← 根治,但涨 FLASH
13. 常见报错与坑(全部带真实输出)
13.1 路径类
| 报错 | 原因 | 解法 |
|---|---|---|
No such file or directory. |
用 /c/... 这种 POSIX 路径 |
换 C:/...(✅实测) |
dump 导出后 ls 找不到文件 |
同上,/tmp/x.bin 被当成相对路径 |
用 Windows 绝对路径 |
No symbol table is loaded |
只加了 core,没加 ELF | gdb fw.elf fw.core 两个都喂 |
| 源码找不到 / 路径不对 | 编译机路径 ≠ 本机路径 | set substitute-path + directory |
13.2 core / 格式类
| 报错 | 原因 | 解法 |
|---|---|---|
is not a core dump: file format not recognized |
e_type 不是 ET_CORE(✅实测:旧合成 core 是 NONE) |
生成端把 ELF 头改对 |
The program has no registers now. |
架构未指定(✅实测) | set architecture riscv:rv32 |
You can't do that without a process to debug. |
core 是只读 target(✅实测) | 换 §7.3 的 B/C 方案 |
Cannot access memory at address 0x... |
地址在 core 覆盖范围外(看 info files) |
只能换范围,或改 core 生成端多 dump 一点 |
info proc mappings 没输出 |
裸机 core 没有 /proc 概念(✅实测) |
用 info files |
Program terminated with signal ?, Unknown signal |
newlib GDB 信号名映射(✅实测) | 无害,忽略 |
无符号时 bt 只有 #0 ... in ?? () |
没加载 ELF | 加载 ELF + -funwind-tables |
13.3 功能缺失类(★本机专属)
| 报错 | 说明 | 对策 |
|---|---|---|
Python scripting is not supported in this copy of GDB. |
编译时 --without-python(✅实测) |
走命令脚本 / MI / 外部脚本 |
Undefined info command: "unwinders" |
本 build 没有 unwinder 管理命令(✅实测) | 只能靠 bt 结果判断 |
Bad breakpoint number 'unwinder' |
disable unwinder 不存在(✅实测) |
--- |
Undefined command: "checkpoint" |
非 Linux 平台没有该命令(✅实测) | 用外部快照/多次 run |
13.4 远程调试类
| 现象 | 原因 | 解法 |
|---|---|---|
| 连接超时 | 默认 2 秒太短 | set remotetimeout 20 |
| 读内存报错 | GDB 认为该区域不可访问 | set mem inaccessible-by-default off |
| Flash 里断点不生效 | 软断点写不进 Flash | hbreak |
| 断点设多了失效 | 硬件断点数量有限 | 删掉不用的;set remote hardware-breakpoint-limit |
| 单步行为怪 | stub 实现有 bug | set debug remote 1 看包 |
| 复位后错乱 | 目标被 reset,GDB 状态过期 | monitor reset halt 后重新 load |
13.5 断栈类
| 报错 | 原因 | 解法 |
|---|---|---|
Backtrace stopped: frame did not save the PC |
缺 CFI / 缺帧指针 | §12.8 决策树 |
Backtrace stopped: previous frame inner to this frame |
栈已损坏 | 手工用 x 扫描栈找返回地址 |
| 回溯少帧/多帧 | -O2 尾调用优化 |
属正常,需交叉验证 objdump |
13.6 其它高频
| 报错 | 原因 | 解法 |
|---|---|---|
No symbol "XXX" in current context |
打印的是宏 | -g3 后用 macro expand |
<optimized out> |
变量被优化掉 | -O0 重编;或算偏移手工读内存 |
| 行为空前古怪 | .gdbinit 污染 |
加 -nx 复跑 |
info registers 看不到 CSR |
目标没给 XML target description | maint packet qXfer:features:read:target.xml:0,fff 查 |
14. 附录 A:一页速查卡
gdb
### 加载
file fw.elf 加载符号
core-file fw.core 加载快照
target extended-remote localhost:3333
set architecture riscv:rv32
set mem inaccessible-by-default off
### 跑
r start c n s ni si fin u adv j return signal
### 断
b file:42 b *0x80001234 hbreak main tbreak fn
watch var watch -l var rwatch var awatch var
catch syscall read catch throw
ignore N 100 condition N x>3
commands N ... end dprintf fn, "v=%d\n", v
info breakpoints delete N clear fn
### 栈
bt bt full bt 10 bt -10 frame N up down
info frame info args info locals
thread apply all bt
### 看
p/x v x/16xw 0x80000000 x/10i $pc x/s 0x80001000
info registers info all-registers info symbol $pc
ptype /o struct ftl_ctx
find 0x80000000, 0x8000ffff, 0x1234
dump binary memory ram.bin 0x80000000 0x80001000
### 汇编 / 源码
disassemble /r fn disassemble /s fn info line fn
list fn dir PATH set substitute-path FROM TO
tui enable layout asm focus regs update
Ctrl-x a 开/关TUI Ctrl-l 重绘 Ctrl-x o 切窗口
### 信息 / 排障
info files ★core 覆盖范围
maintenance info sections ★段布局
show configuration ★编译配置(有没有 Python)
set debug remote 1 ★看 RSP 包
maint packet qSupported ★直接问 stub
monitor help 远端代理命令
compare-sections 校验烧录镜像
apropos KEYWORD 搜命令
set logging enabled on
### 脚本
gdb -q -batch -x s.gdb fw.elf fw.core
define NAME ... end / document NAME ... end
while ... end if ... end
-hook-stop 每次停机自动执行
### 反向
record full reverse-continue reverse-step reverse-stepi
15. 附录 B:本目录文件说明
| 文件 | 作用 |
|---|---|
GDB_使用手册.md |
本文档 |
tools/mk_minicore.py |
生成 GDB 可加载的最小 RV32 core 测试夹具 |
tools/demo.core |
由上面脚本生成的样例 core(pc=0x80001234 / ra=0x80005678 / sp=0x8000ff00) |
复现验证:
bash
cd tools
python mk_minicore.py demo.core
/e/software/nuclei_riscv_gcc/bin/riscv64-unknown-elf-gdb.exe -q -batch \
-ex "set architecture riscv:rv32" -ex "core-file demo.core" \
-ex "info registers pc ra sp s0" -ex "bt"
预期输出见 §12.6。
16. 待确认 / 未覆盖
| 项 | 状态 |
|---|---|
| live target(JTAG/OpenOCD/QEMU)实机验证 | 未做,本机无硬件、无 qemu |
record full 在 RISC-V 裸机上的实际可用性 |
命令存在,未实测 |
64 位 core(EI_CLASS==2)的 note 对齐 |
未验证;32 位实测 4 字节对齐 |
info proc mappings 在 Linux core(含 NT_FILE)上的行为 |
未测,裸机 core 上无输出 |
| GDB Python 相关章节(pretty-printer / unwinder / MI+Python) | 本机无 Python,仅作说明性收录 |