Skip to content

Commit 1712435

Browse files
authored
Implement GDB stub for remote debugging (#45)
With the help of the external mini-gdbstub library, rv32emu offers an experimental way to support a limited number of GDB Remote Serial Protocol (GDBRSP) packets. After applying this patch, we were able to connect rv32emu to GDB and interact with it using basic commands such as "print $pc" to dump the value of current program counter or "continue" to resume program running. It is important to note that this patch only permits the use of one breakpoint. Future upgrades will be made. Close #22
1 parent 304f0af commit 1712435

File tree

12 files changed

+245
-1
lines changed

12 files changed

+245
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ build/gfx.wad
88
# built objects
99
build/rv32emu
1010
build/arch-test
11+
build/mini-gdbstub
1112
*.o
1213
*.o.d

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "riscv-arch-test"]
22
path = tests/riscv-arch-test
33
url = https://github.com/riscv-non-isa/riscv-arch-test
4+
[submodule "mini-gdbstub"]
5+
path = mini-gdbstub
6+
url = https://github.com/RinHizakura/mini-gdbstub

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,27 @@ $(OUT)/emulate.o: CFLAGS += -fno-gcse -fno-crossjumping
5555
endif
5656
endif
5757

58+
ENABLE_GDBSTUB ?= 1
59+
ifeq ("$(ENABLE_GDBSTUB)", "1")
60+
MINI_GDBSTUB_OUT = $(abspath $(OUT)/mini-gdbstub)
61+
GDBSTUB_COMM = 127.0.0.1:1234
62+
LIB_GDBSTUB += $(MINI_GDBSTUB_OUT)/libgdbstub.a
63+
gdbstub-test: $(BIN)
64+
./tests/gdbstub-test/main.sh
65+
66+
$(LIB_GDBSTUB):
67+
git submodule update --init mini-gdbstub
68+
$(MAKE) -C mini-gdbstub O=$(MINI_GDBSTUB_OUT)
69+
$(OUT)/emulate.o: $(LIB_GDBSTUB)
70+
OBJS_EXT += gdbstub.o
71+
CFLAGS += -D ENABLE_GDBSTUB -D'GDBSTUB_COMM="$(GDBSTUB_COMM)"'
72+
LDFLAGS += $(LIB_GDBSTUB)
73+
endif
74+
75+
# Clear the .DEFAULT_GOAL special variable, so that the following turns
76+
# to the first target after .DEFAULT_GOAL is not set.
77+
.DEFAULT_GOAL :=
78+
5879
all: $(BIN)
5980

6081
OBJS := \

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,45 @@ Detail in riscv-arch-test:
112112

113113
Add `-D` to enable and `-U` to disable the specific ISA extensions.
114114

115+
## Debugging mode with GDB remote serial protocal
116+
117+
By supporting a small set of
118+
[GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html)
119+
(GDBRSP), `rv32emu` is allowed to be run as gdbstub experimentally. To enable this feature,
120+
you should first configure the `ENABLE_GDBSTUB` to 1 in the Makefile and build the emulator.
121+
After that, you could run it with the following command.
122+
123+
```
124+
./build/rv32emu --gdbstub <binary>
125+
```
126+
127+
The `<binary>` should be the ELF file in riscv32 format. You are also recommended to compile
128+
your program with `-g` option to generate debug information in your ELF file.
129+
130+
If the emulator starts correctly without exit, you can then execute the riscv-gdb. Two GDB
131+
commands are required first to provide the supported architecture of the emulator to GDB (also
132+
provide debugging symbol if there's any) and then connect to the emulator.
133+
134+
```
135+
$ riscv32-unknown-elf-gdb
136+
(gdb) file <binary>
137+
(gdb) target remote :1234
138+
```
139+
140+
If there's no error message from the riscv-gdb, congratulate! You can now interact with
141+
the emulator from the GDB command line now!
142+
143+
### Limitation
144+
The support of GDB Remote Serial Protocol(GDBRSP) in `rv32emu` is still in the
145+
development phase. There are some known restriction of this functionality due to the
146+
incomplete design now.
147+
* Since the 'G' packet is not supported yet, writing emulator registers with GDB is
148+
not permitted.
149+
* Consequently, the packets for binary download (the "X" packet) and memory writing
150+
(the "M" packet) specified in GDBRSP are not allowed. It is forbidden to use GDB commands
151+
such as "load" that aim to modify the emulator's internal memory.
152+
* Due to the poor design of breakpoints handling, you can only set single breakpoint
153+
during debugging
115154
## External sources
116155

117156
In `rv32emu` repository, there are some prebuilt ELF files for testing purpose.

emulate.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ static inline int isinff(float x)
1313
#endif
1414
#endif
1515

16+
#ifdef ENABLE_GDBSTUB
17+
extern struct target_ops rv_ops;
18+
#endif
19+
1620
#include "riscv.h"
1721
#include "riscv_private.h"
1822

@@ -1672,6 +1676,27 @@ static inline bool op_unimp(struct riscv_t *rv, uint32_t insn UNUSED)
16721676
return false;
16731677
}
16741678

1679+
#ifdef ENABLE_GDBSTUB
1680+
void rv_debug(struct riscv_t *rv)
1681+
{
1682+
if (!gdbstub_init(&rv->gdbstub, &rv_ops,
1683+
(arch_info_t){
1684+
.reg_num = 33,
1685+
.reg_byte = 4,
1686+
.target_desc = TARGET_RV32,
1687+
},
1688+
GDBSTUB_COMM)) {
1689+
return;
1690+
}
1691+
1692+
if (!gdbstub_run(&rv->gdbstub, (void *) rv)) {
1693+
return;
1694+
}
1695+
gdbstub_close(&rv->gdbstub);
1696+
}
1697+
1698+
#endif /* ENABLE_GDBSTUB */
1699+
16751700
void rv_step(struct riscv_t *rv, int32_t cycles)
16761701
{
16771702
assert(rv);

gdbstub.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include <assert.h>
2+
#include "mini-gdbstub/include/gdbstub.h"
3+
#include "riscv_private.h"
4+
5+
static size_t rv_read_reg(void *args, int regno)
6+
{
7+
struct riscv_t *rv = (struct riscv_t *) args;
8+
9+
if (regno < 32) {
10+
return rv_get_reg(rv, regno);
11+
}
12+
13+
if (regno == 32) {
14+
return rv_get_pc(rv);
15+
}
16+
17+
return -1;
18+
}
19+
20+
static void rv_read_mem(void *args, size_t addr, size_t len, void *val)
21+
{
22+
struct riscv_t *rv = (struct riscv_t *) args;
23+
24+
for (size_t i = 0; i < len; i++)
25+
*((uint8_t *) val + i) = rv->io.mem_read_b(rv, addr);
26+
}
27+
28+
static gdb_action_t rv_cont(void *args)
29+
{
30+
struct riscv_t *rv = (struct riscv_t *) args;
31+
const uint32_t cycles_per_step = 1;
32+
33+
for (; !rv_has_halted(rv);) {
34+
if (rv->breakpoint_specified && (rv_get_pc(rv) == rv->breakpoint_addr)) {
35+
break;
36+
}
37+
rv_step(rv, cycles_per_step);
38+
}
39+
40+
return ACT_RESUME;
41+
}
42+
43+
static gdb_action_t rv_stepi(void *args)
44+
{
45+
struct riscv_t *rv = (struct riscv_t *) args;
46+
rv_step(rv, 1);
47+
return ACT_RESUME;
48+
}
49+
50+
static bool rv_set_bp(void *args, size_t addr, bp_type_t type)
51+
{
52+
struct riscv_t *rv = (struct riscv_t *) args;
53+
if (type != BP_SOFTWARE || rv->breakpoint_specified)
54+
return false;
55+
56+
rv->breakpoint_specified = true;
57+
rv->breakpoint_addr = addr;
58+
return true;;
59+
}
60+
61+
static bool rv_del_bp(void *args, size_t addr, bp_type_t type)
62+
{
63+
struct riscv_t *rv = (struct riscv_t *) args;
64+
if (type != BP_SOFTWARE)
65+
return false;
66+
/* When there is no matched breakpoint, no further action is taken */
67+
if (!rv->breakpoint_specified || addr != rv->breakpoint_addr)
68+
return true;
69+
70+
rv->breakpoint_specified = false;
71+
rv->breakpoint_addr = 0;
72+
return true;
73+
}
74+
75+
struct target_ops rv_ops = {
76+
.read_reg = rv_read_reg,
77+
.read_mem = rv_read_mem,
78+
.cont = rv_cont,
79+
.stepi = rv_stepi,
80+
.set_bp = rv_set_bp,
81+
.del_bp = rv_del_bp,
82+
};

main.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
/* enable program trace mode */
99
static bool opt_trace = false;
10+
#ifdef ENABLE_GDBSTUB
11+
/* enable program gdbstub mode */
12+
static bool opt_gdbstub = false;
13+
#endif
1014

1115
/* RISCV arch-test */
1216
static bool opt_arch_test = false;
@@ -76,6 +80,9 @@ static void print_usage(const char *filename)
7680
"Usage: %s [options] [filename]\n"
7781
"Options:\n"
7882
" --trace : print executable trace\n"
83+
#ifdef ENABLE_GDBSTUB
84+
" --gdbstub : allow remote GDB connections (as gdbstub)\n"
85+
#endif
7986
" --arch-test [filename] : dump signature to the given file, "
8087
"required by arch-test test\n",
8188
filename);
@@ -94,6 +101,12 @@ static bool parse_args(int argc, char **args)
94101
opt_trace = true;
95102
continue;
96103
}
104+
#ifdef ENABLE_GDBSTUB
105+
if (!strcmp(arg, "--gdbstub")) {
106+
opt_gdbstub = true;
107+
continue;
108+
}
109+
#endif
97110
if (!strcmp(arg, "--arch-test")) {
98111
opt_arch_test = true;
99112
if (i + 1 >= argc) {
@@ -199,7 +212,13 @@ int main(int argc, char **args)
199212
/* run based on the specified mode */
200213
if (opt_trace) {
201214
run_and_trace(rv, elf);
202-
} else {
215+
}
216+
#ifdef ENABLE_GDBSTUB
217+
else if (opt_gdbstub) {
218+
rv_debug(rv);
219+
}
220+
#endif
221+
else {
203222
run(rv);
204223
}
205224

mini-gdbstub

Submodule mini-gdbstub added at 742bcd4

riscv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ void rv_delete(struct riscv_t *);
9999
/* reset the RISC-V processor */
100100
void rv_reset(struct riscv_t *, riscv_word_t pc);
101101

102+
#ifdef ENABLE_GDBSTUB
103+
/* Run the RISCV-emulator as gdbstub */
104+
void rv_debug(struct riscv_t *rv);
105+
#endif
106+
102107
/* step the RISC-V emulator */
103108
void rv_step(struct riscv_t *, int32_t cycles);
104109

riscv_private.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#pragma once
22
#include <stdbool.h>
33

4+
#ifdef ENABLE_GDBSTUB
5+
#include "mini-gdbstub/include/gdbstub.h"
6+
#endif
47
#include "riscv.h"
58

69
#define RV_NUM_REGS 32
@@ -138,6 +141,15 @@ struct riscv_t {
138141
/* user provided data */
139142
riscv_user_t userdata;
140143

144+
#ifdef ENABLE_GDBSTUB
145+
/* gdbstub instance */
146+
gdbstub_t gdbstub;
147+
148+
/* GDB instruction breakpoint */
149+
bool breakpoint_specified;
150+
riscv_word_t breakpoint_addr;
151+
#endif
152+
141153
#ifdef ENABLE_RV32F
142154
/* float registers */
143155
union {

0 commit comments

Comments
 (0)