Skip to content

Commit ec4f468

Browse files
committed
Add debug mode using mini-gdbstub submodules
This patch provide an experimental solution that lets rv32emu support a small set of GDB Remote Serial Protocol packet with the mini-gdbstub library. After this patch, we could connect our emulator to GDB and interact with some simple commands like printing current program counter(`print $pc`) or unpausing the execution(`continue`). It's worth mentioning that we only enable to use single breakpoint on rv32emu under this patch. It will be improved in the future. Close #22
1 parent 304f0af commit ec4f468

File tree

11 files changed

+208
-1
lines changed

11 files changed

+208
-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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ $(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+
CONFIG_GDBSTUB_COMM = 127.0.0.1:1234
62+
LIB_GDBSTUB += $(MINI_GDBSTUB_OUT)/libgdbstub.a
63+
$(LIB_GDBSTUB):
64+
git submodule update --init mini-gdbstub
65+
$(MAKE) -C mini-gdbstub O=$(MINI_GDBSTUB_OUT)
66+
$(OUT)/emulate.o: $(LIB_GDBSTUB)
67+
OBJS_EXT += gdbstub.o
68+
CFLAGS += -D ENABLE_GDBSTUB -D'GDBSTUB_COMM="$(CONFIG_GDBSTUB_COMM)"'
69+
LDFLAGS += $(LIB_GDBSTUB)
70+
endif
71+
72+
# Clear the .DEFAULT_GOAL special variable, so that the following turns
73+
# to the first target after .DEFAULT_GOAL is not set.
74+
.DEFAULT_GOAL :=
75+
5876
all: $(BIN)
5977

6078
OBJS := \

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,44 @@ 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+
, `rv32emu` is allowed to be run as gdbstub experimentally. To enable this feature, you should
120+
first configure the `ENABLE_GDBRSP` to 1 in the Makefile and build the emulator. After that,
121+
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 hint 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 in `rv32emu` is still in the development phase.
145+
There are some known restriction of this functionality due to the incomplete design now.
146+
* It is not allowed to write registers of emulator with GDB since the 'G' packet is not
147+
supported now
148+
* Since the packet for binary download (the 'X' packet) and memory write (the 'M' packet)
149+
of GDBRSP are not supported now. The GDB commands like `load` which will attempt to modify
150+
the emulator's memory is not allowed
151+
* Due to the poor design of breakpoints handling, you can only set single breakpoint
152+
during debugging
115153
## External sources
116154

117155
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+
#include "gdbstub.h"
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_rm_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+
// It's fine when there's no matched breakpoint, just doing nothing
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+
.rm_bp = rv_rm_bp,
82+
};

gdbstub.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
extern struct target_ops rv_ops;

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 d66994a

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 in debug mode */
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

0 commit comments

Comments
 (0)