gdb-stub 和 Target Description

在尝试给NEMU移值linux的陷入的调试的黑洞,遂尝试nemu接入gdb

GDB Stub

在嵌入式系统中,由于目标设备可能没有足够的资源运行完整的 GDB,或者无法直接与开发者的主机连接,因此需要一个简化的代理程序来完成以下任务:

  • 接收 GDB 的调试命令。
  • 在目标设备上执行这些命令(例如设置断点、读取寄存器、修改内存等)。
  • 将执行结果返回给 GDB

找到了一个非常简易的实现方式 https://github.com/RinHizakura/mini-gdbstub

使用

target remote localhost:1234 就可以让gdb连接上

结合tmux和Makefile , 可以让编译运行的时候自动分屏

1
tmux split-window -h -p 65 "riscv64-unknown-linux-gnu-gdb -ex \"target remote localhost:1234\" $(ELF)"

Target Description

Target Description 是 GDB 用来描述目标设备(Target)硬件架构和特性的一个机制。它定义了目标设备的寄存器、内存布局、指令集等信息,从而使 GDB 能够正确地与目标设备通信并执行调试操作。

我的目标是在gdb上watch/read 我自己实现的CSR寄存器

参考了一下qemu-system-riscv64的配制,自己写了宏来展开

Imp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#define GenCSR(name, paddr) \
"<reg name=\"" #name "\" bitsize=\"32\" type=\"int\" regnum=\"" #paddr "\" />\n"

#define NEMU_CSR_TAGS \
"<feature name=\"org.gnu.gdb.riscv.csr\">\n" \
CSR_LIST \
"</feature>\n"

#define NEMU_REG_TAGS \
"<feature name=\"org.gnu.gdb.riscv.cpu\">" \
"<reg name=\"zero\" bitsize=\"32\" type=\"int\" regnum=\"0\"/>" \
"<reg name=\"RA\" bitsize=\"32\" type=\"code_ptr\" regnum=\"1\"/>" \
"<reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\" regnum=\"2\"/>" \
"<reg name=\"gp\" bitsize=\"32\" type=\"data_ptr\" regnum=\"3\"/>" \
"<reg name=\"tp\" bitsize=\"32\" type=\"data_ptr\" regnum=\"4\"/>" \
"<reg name=\"t0\" bitsize=\"32\" type=\"int\" regnum=\"5\"/>" \
"<reg name=\"t1\" bitsize=\"32\" type=\"int\" regnum=\"6\"/>" \
"<reg name=\"t2\" bitsize=\"32\" type=\"int\" regnum=\"7\"/>" \
"<reg name=\"fp\" bitsize=\"32\" type=\"data_ptr\" regnum=\"8\"/>" \
"<reg name=\"s1\" bitsize=\"32\" type=\"int\" regnum=\"9\"/>" \
"<reg name=\"a0\" bitsize=\"32\" type=\"int\" regnum=\"10\"/>" \
"<reg name=\"a1\" bitsize=\"32\" type=\"int\" regnum=\"11\"/>" \
"<reg name=\"a2\" bitsize=\"32\" type=\"int\" regnum=\"12\"/>" \
"<reg name=\"a3\" bitsize=\"32\" type=\"int\" regnum=\"13\"/>" \
"<reg name=\"a4\" bitsize=\"32\" type=\"int\" regnum=\"14\"/>" \
"<reg name=\"a5\" bitsize=\"32\" type=\"int\" regnum=\"15\"/>" \
"<reg name=\"a6\" bitsize=\"32\" type=\"int\" regnum=\"16\"/>" \
"<reg name=\"a7\" bitsize=\"32\" type=\"int\" regnum=\"17\"/>" \
"<reg name=\"s2\" bitsize=\"32\" type=\"int\" regnum=\"18\"/>" \
"<reg name=\"s3\" bitsize=\"32\" type=\"int\" regnum=\"19\"/>" \
"<reg name=\"s4\" bitsize=\"32\" type=\"int\" regnum=\"20\"/>" \
"<reg name=\"s5\" bitsize=\"32\" type=\"int\" regnum=\"21\"/>" \
"<reg name=\"s6\" bitsize=\"32\" type=\"int\" regnum=\"22\"/>" \
"<reg name=\"s7\" bitsize=\"32\" type=\"int\" regnum=\"23\"/>" \
"<reg name=\"s8\" bitsize=\"32\" type=\"int\" regnum=\"24\"/>" \
"<reg name=\"s9\" bitsize=\"32\" type=\"int\" regnum=\"25\"/>" \
"<reg name=\"s10\" bitsize=\"32\" type=\"int\" regnum=\"26\"/>" \
"<reg name=\"s11\" bitsize=\"32\" type=\"int\" regnum=\"27\"/>" \
"<reg name=\"t3\" bitsize=\"32\" type=\"int\" regnum=\"28\"/>" \
"<reg name=\"t4\" bitsize=\"32\" type=\"int\" regnum=\"29\"/>" \
"<reg name=\"t5\" bitsize=\"32\" type=\"int\" regnum=\"30\"/>" \
"<reg name=\"t6\" bitsize=\"32\" type=\"int\" regnum=\"31\"/>" \
"<reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\" regnum=\"32\"/>" \
"</feature>"

#define NEMU_OTHER_TAGS \
"<feature name=\"org.gnu.gdb.riscv.virtual\">" \
"<reg name=\"PRIV\" bitsize=\"32\" type=\"int\" regnum=\"65\"/>" \
"</feature>"

#define NEMU_FEATURES \
"<target version=\"1.0\"><architecture>riscv:rv32</architecture>" \
NEMU_REG_TAGS \
NEMU_OTHER_TAGS \
NEMU_CSR_TAGS \
"</target>"


其中CSR_LIST的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 需要和spike做diff的寄存器
#define CSR_DIFF_LIST \
GenCSR(MHARTID, 0xf14) \
GenCSR(MSTATUS, 0x300) \
GenCSR(MISA, 0x301) \
GenCSR(MEDELEG, 0x302) \
GenCSR(MIDELEG, 0x303) \
GenCSR(MIE, 0x304) \
GenCSR(MTVEC, 0x305) \
GenCSR(MSTATUSH, 0x310) \
GenCSR(MSCRATCH, 0x340) \
GenCSR(MEPC, 0x341) \
GenCSR(MCAUSE, 0x342) \
GenCSR(MTVAL, 0x343) \
GenCSR(MIP, 0x344) \
\
GenCSR(SATP, 0x180) \
GenCSR(SCAUSE, 0x142) \
GenCSR(SEPC, 0x141) \
GenCSR(SIE, 0x104) \
GenCSR(SIP, 0x144) \
GenCSR(SSCRATCH, 0x140) \
GenCSR(SSTATUS, 0x100) \
GenCSR(STVAL, 0x143) \
GenCSR(STVEC, 0x105) \
\
GenCSR(PMPCFG0, 0x3A0) \
GenCSR(PMPCFG1, 0x3A1) \
GenCSR(PMPCFG2, 0x3A2) \
GenCSR(PMPCFG3, 0x3A3) \
GenCSR(PMPCFG4, 0x3A4) \
GenCSR(PMPCFG5, 0x3A5) \
GenCSR(PMPCFG6, 0x3A6) \
GenCSR(PMPCFG7, 0x3A7) \
GenCSR(PMPCFG8, 0x3A8) \
GenCSR(PMPCFG9, 0x3A9) \
GenCSR(PMPCFG10, 0x3AA) \
GenCSR(PMPCFG11, 0x3AB) \
GenCSR(PMPCFG12, 0x3AC) \
GenCSR(PMPCFG13, 0x3AD) \
GenCSR(PMPCFG14, 0x3AE) \
GenCSR(PMPCFG15, 0x3AF) \
GenCSR(PMPADDR0, 0x3B0) \
GenCSR(PMPADDR1, 0x3B1) \
GenCSR(PMPADDR2, 0x3B2) \
GenCSR(PMPADDR3, 0x3B3) \
GenCSR(PMPADDR4, 0x3B4) \
GenCSR(PMPADDR5, 0x3B5) \
GenCSR(PMPADDR6, 0x3B6) \
GenCSR(PMPADDR7, 0x3B7) \
GenCSR(PMPADDR8, 0x3B8) \
GenCSR(PMPADDR9, 0x3B9) \
GenCSR(PMPADDR10, 0x3BA) \
GenCSR(PMPADDR11, 0x3BB) \
GenCSR(PMPADDR12, 0x3BC) \
GenCSR(PMPADDR13, 0x3BD) \
GenCSR(PMPADDR14, 0x3BE) \
GenCSR(PMPADDR15, 0x3BF) \

// 全部csr寄存器
#define CSR_LIST \
GenCSR(MVENDROID, 0xF11) \
GenCSR(MARCHID, 0xF12) \
CSR_DIFF_LIST
//GenCSR(MEDELEGH, 0x312)

// 生成静态常量定义
#define GenCSR(name, paddr) \
static const uint32_t NEMU_CSR_V_##name = paddr; \
static const uint32_t NEMU_CSR_##name = paddr;
CSR_LIST
#undef GenCSR

这里我使用define/undef来实现多种展开方式

分片传输

这部分内容已经pr(#8)了

在mini-gdbstub里面把一块数据的大小设置成了0x400, 如果要传我们自己的东西是肯定不够的如果强行设大的话gdb会报错Received too much data from the target.

所以得给数据分片传输

简单上网查询了规范

1
$(l/m)-------#hh

$->开始
(l/m)->是否是最后一个
#->结束
hh->校验


gdb-stub 和 Target Description
https://20040702.xyz/2025/02/05/gdb-stub/
作者
Seeker
发布于
2025年2月5日
许可协议