Skip to content

Commit ab6e4a9

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.
1 parent 304f0af commit ab6e4a9

10 files changed

+248
-1
lines changed

.gitmodules

+3
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.git

Makefile

+17
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ $(OUT)/emulate.o: CFLAGS += -fno-gcse -fno-crossjumping
5555
endif
5656
endif
5757

58+
MINI_GDBSTUB :=
59+
ENABLE_GDBRSP ?= 1
60+
ifeq ("$(ENABLE_GDBRSP)", "1")
61+
CFLAGS += -D ENABLE_GDBRSP
62+
LDFLAGS += -Wl,-rpath="$(CURDIR)/mini-gdbstub" -L./mini-gdbstub -lgdbstub
63+
MINI_GDBSTUB += ./mini-gdbstub/libgdbstub.so
64+
$(MINI_GDBSTUB):
65+
git submodule update --init mini-gdbstub
66+
$(MAKE) -C mini-gdbstub
67+
$(OUT)/emulate.o: $(MINI_GDBSTUB)
68+
OBJS_EXT += gdbrsp.o
69+
endif
70+
71+
# Clear the .DEFAULT_GOAL special variable, so that the following turns
72+
# to the first target after .DEFAULT_GOAL is not set.
73+
.DEFAULT_GOAL :=
74+
5875
all: $(BIN)
5976

6077
OBJS := \

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,35 @@ 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#Remote-Protocol)
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 --debug <binary>
125+
```
126+
127+
The `<binary>` should be the ELF in riscv32 format. You are also recommended to compile
128+
your program with `-g` option to generate debug information in your ELF.
129+
130+
131+
If the emulator starts correctly without exit, you can then execute the riscv-gdb. Two GDB
132+
commands are required first to hint the supported architecture of the emulator to GDB (also
133+
provide debugging symbol if there's any) and then connect to the emulator.
134+
135+
```
136+
$ riscv32-unknown-elf-gdb
137+
(gdb) file <binary>
138+
(gdb) target remote :1234
139+
```
140+
141+
If there's no error message from the riscv-gdb, congratulate! You can now interact with
142+
the emulator from the GDB command line now!
143+
115144
## External sources
116145

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

emulate.c

+62
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_GDBRSP
17+
#include "gdbrsp.h"
18+
#endif
19+
1620
#include "riscv.h"
1721
#include "riscv_private.h"
1822

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

1679+
#ifdef ENABLE_GDBRSP
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+
"127.0.0.1:1234")) {
1689+
return;
1690+
}
1691+
1692+
if (!gdbstub_run(&rv->gdbstub, (void *) rv)) {
1693+
return;
1694+
}
1695+
gdbstub_close(&rv->gdbstub);
1696+
}
1697+
1698+
bool rv_get_breakpoint(struct riscv_t *rv, riscv_word_t *addr)
1699+
{
1700+
assert(rv && addr);
1701+
if (!rv->bp_is_set)
1702+
return false;
1703+
1704+
*addr = rv->bp_addr;
1705+
return true;
1706+
}
1707+
1708+
bool rv_set_breakpoint(struct riscv_t *rv, riscv_word_t addr)
1709+
{
1710+
assert(rv);
1711+
if (rv->bp_is_set)
1712+
return false;
1713+
1714+
rv->bp_is_set = true;
1715+
rv->bp_addr = addr;
1716+
return true;
1717+
}
1718+
1719+
bool rv_reset_breakpoint(struct riscv_t *rv, riscv_word_t bp_addr)
1720+
{
1721+
assert(rv);
1722+
if (!rv->bp_is_set || bp_addr != rv->bp_addr)
1723+
return false;
1724+
1725+
rv->bp_is_set = false;
1726+
rv->bp_addr = 0;
1727+
return true;
1728+
}
1729+
#endif /* ENABLE_GDBRSP */
1730+
16751731
void rv_step(struct riscv_t *rv, int32_t cycles)
16761732
{
16771733
assert(rv);
@@ -1884,6 +1940,12 @@ riscv_word_t rv_get_reg(struct riscv_t *rv, uint32_t reg)
18841940
return ~0U;
18851941
}
18861942

1943+
uint8_t rv_get_mem(struct riscv_t *rv, uint32_t addr)
1944+
{
1945+
assert(rv);
1946+
return rv->io.mem_read_b(rv, addr);
1947+
}
1948+
18871949
struct riscv_t *rv_create(const struct riscv_io_t *io, riscv_user_t userdata)
18881950
{
18891951
assert(io);

gdbrsp.c

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include <assert.h>
2+
#include "mini-gdbstub/include/gdbstub.h"
3+
#include "riscv.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+
} else if (regno == 32) {
12+
return rv_get_pc(rv);
13+
}
14+
15+
return -1;
16+
}
17+
18+
static void rv_read_mem(void *args, size_t addr, size_t len, void *val)
19+
{
20+
struct riscv_t *rv = (struct riscv_t *) args;
21+
22+
for (size_t i = 0; i < len; i++)
23+
*((uint8_t *) val + i) = rv_get_mem(rv, addr);
24+
}
25+
26+
static gdb_action_t rv_cont(void *args)
27+
{
28+
struct riscv_t *rv = (struct riscv_t *) args;
29+
const uint32_t cycles_per_step = 1;
30+
riscv_word_t bp_addr;
31+
32+
for (; !rv_has_halted(rv);) {
33+
if (rv_get_breakpoint(rv, &bp_addr) && (rv_get_pc(rv) == bp_addr)) {
34+
break;
35+
}
36+
rv_step(rv, cycles_per_step);
37+
}
38+
39+
return ACT_RESUME;
40+
}
41+
42+
static gdb_action_t rv_stepi(void *args)
43+
{
44+
struct riscv_t *rv = (struct riscv_t *) args;
45+
rv_step(rv, 1);
46+
return ACT_RESUME;
47+
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)
54+
return false;
55+
56+
return rv_set_breakpoint(rv, addr);
57+
}
58+
59+
static bool rv_rm_bp(void *args, size_t addr, bp_type_t type)
60+
{
61+
struct riscv_t *rv = (struct riscv_t *) args;
62+
if (type != BP_SOFTWARE)
63+
return false;
64+
// It's fine when there's no matching breakpoint, just doing nothing
65+
rv_reset_breakpoint(rv, addr);
66+
67+
return true;
68+
}
69+
70+
struct target_ops rv_ops = {
71+
.read_reg = rv_read_reg,
72+
.read_mem = rv_read_mem,
73+
.cont = rv_cont,
74+
.stepi = rv_stepi,
75+
.set_bp = rv_set_bp,
76+
.rm_bp = rv_rm_bp,
77+
};

gdbrsp.h

+3
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

+27-1
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_GDBRSP
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;
@@ -60,6 +64,13 @@ static void run_and_trace(struct riscv_t *rv, elf_t *elf)
6064
}
6165
}
6266

67+
#ifdef ENABLE_GDBRSP
68+
static void run_debug(struct riscv_t *rv)
69+
{
70+
rv_debug(rv);
71+
}
72+
#endif
73+
6374
static void run(struct riscv_t *rv)
6475
{
6576
const uint32_t cycles_per_step = 100;
@@ -76,6 +87,9 @@ static void print_usage(const char *filename)
7687
"Usage: %s [options] [filename]\n"
7788
"Options:\n"
7889
" --trace : print executable trace\n"
90+
#ifdef ENABLE_GDBRSP
91+
" --gdbstub: execute emulator in gdbstub mode\n"
92+
#endif
7993
" --arch-test [filename] : dump signature to the given file, "
8094
"required by arch-test test\n",
8195
filename);
@@ -94,6 +108,12 @@ static bool parse_args(int argc, char **args)
94108
opt_trace = true;
95109
continue;
96110
}
111+
#ifdef ENABLE_GDBRSP
112+
if (!strcmp(arg, "--gdbstub")) {
113+
opt_gdbstub = true;
114+
continue;
115+
}
116+
#endif
97117
if (!strcmp(arg, "--arch-test")) {
98118
opt_arch_test = true;
99119
if (i + 1 >= argc) {
@@ -199,7 +219,13 @@ int main(int argc, char **args)
199219
/* run based on the specified mode */
200220
if (opt_trace) {
201221
run_and_trace(rv, elf);
202-
} else {
222+
}
223+
#ifdef ENABLE_GDBRSP
224+
else if (opt_gdbstub) {
225+
run_debug(rv);
226+
}
227+
#endif
228+
else {
203229
run(rv);
204230
}
205231

mini-gdbstub

Submodule mini-gdbstub added at dd39ca8

riscv.h

+17
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,20 @@ 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_GDBRSP
103+
/* Run the RISCV-emulator in debug mode */
104+
void rv_debug(struct riscv_t *rv);
105+
106+
/* Get the breakpoint address by breakpoint's number */
107+
bool rv_get_breakpoint(struct riscv_t *, riscv_word_t *bp_addr);
108+
109+
/* Set the breakpoint address by breakpoint's number */
110+
bool rv_set_breakpoint(struct riscv_t *, riscv_word_t bp_addr);
111+
112+
/* Reset the breakpoint address by breakpoint's number */
113+
bool rv_reset_breakpoint(struct riscv_t *, riscv_word_t bp_addr);
114+
#endif
115+
102116
/* step the RISC-V emulator */
103117
void rv_step(struct riscv_t *, int32_t cycles);
104118

@@ -117,6 +131,9 @@ void rv_set_reg(struct riscv_t *, uint32_t reg, riscv_word_t in);
117131
/* get a register of the RISC-V emulator */
118132
riscv_word_t rv_get_reg(struct riscv_t *, uint32_t reg);
119133

134+
/* get a byte from the memory of the RISC-V emulator */
135+
uint8_t rv_get_mem(struct riscv_t *, uint32_t reg);
136+
120137
/* system call handler */
121138
void syscall_handler(struct riscv_t *rv);
122139

riscv_private.h

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#pragma once
22
#include <stdbool.h>
33

4+
#ifdef ENABLE_GDBRSP
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_GDBRSP
145+
/* gdbstub instance */
146+
gdbstub_t gdbstub;
147+
148+
/* GDB instruction breakpoint */
149+
bool bp_is_set;
150+
riscv_word_t bp_addr;
151+
#endif
152+
141153
#ifdef ENABLE_RV32F
142154
/* float registers */
143155
union {

0 commit comments

Comments
 (0)