129 lines
5.8 KiB
Markdown
129 lines
5.8 KiB
Markdown
# mini-gdbstub
|
|
|
|
`mini-gdbstub` is an implementation of the
|
|
[GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html)
|
|
that gives your emulators debugging capabilities.
|
|
|
|
## Usage
|
|
|
|
The very first thing you should do is to build the statically-linked library `libgdbstub.a`.
|
|
```
|
|
make
|
|
```
|
|
|
|
To use the library in your project, you should include the file `gdbstub.h` first.
|
|
Then, you have to initialize the pre-allocated structure `gdbstub_t` with `gdbstub_init`.
|
|
|
|
```c
|
|
bool gdbstub_init(gdbstub_t *gdbstub, struct target_ops *ops, arch_info_t arch, char *s);
|
|
```
|
|
|
|
The parameters `s` is the easiest one to understand. It is a string of the socket
|
|
which your emulator would like to bind as gdb server.
|
|
|
|
The `struct target_ops` is made up of function pointers. Each member function represents an
|
|
abstraction of your emulator's operation. The following lists the requirement
|
|
that should be provided for each method:
|
|
|
|
Method | Description
|
|
---------------|------------------
|
|
`cont` | Run the emulator until hitting breakpoint or exit.
|
|
`stepi` | Do one step on the emulator. You may define your own step for the emulator. For example, the common design is executing one instruction.
|
|
`get_reg_bytes` | Get the register size in bytes for the register specified by `regno` as a return value.
|
|
`read_reg` | Read the value of the register specified by `regno` to `*value`. Return zero if the operation success, otherwise return an errno for the corresponding error.
|
|
`write_reg` | Write value `value` to the register specified by `regno`. Return zero if the operation success, otherwise return an errno for the corresponding error.
|
|
`read_mem` | Read the memory according to the address specified by `addr` with size `len` to the buffer `*val`. Return zero if the operation success, otherwise return an errno for the corresponding error.
|
|
`write_mem` | Write data in the buffer `val` with size `len` to the memory which address is specified by `addr`. Return zero if the operation success, otherwise return an errno for the corresponding error.
|
|
`set_bp` | Set type `type` breakpoint on the address specified by `addr`. Return true if we set the breakpoint successfully, otherwise return false.
|
|
`del_bp` | Delete type `type` breakpoint on the address specified by `addr`. Return true if we delete the breakpoint successfully, otherwise return false.
|
|
`on_interrupt` | Do something when receiving interrupt from GDB client. This method will run concurrently with `cont`, so you should be careful if there're shared data between them. You will need a lock or something similar to avoid data race.
|
|
`set_cpu` | Set the debug target CPU to `cpuid`.
|
|
`get_cpu` | Get the current debug target CPU `cpuid` as return value.
|
|
|
|
```c
|
|
struct target_ops {
|
|
gdb_action_t (*cont)(void *args);
|
|
gdb_action_t (*stepi)(void *args);
|
|
size_t (*get_reg_bytes)(int regno);
|
|
int (*read_reg)(void *args, int regno, void *value);
|
|
int (*write_reg)(void *args, int regno, void* value);
|
|
int (*read_mem)(void *args, size_t addr, size_t len, void *val);
|
|
int (*write_mem)(void *args, size_t addr, size_t len, void *val);
|
|
bool (*set_bp)(void *args, size_t addr, bp_type_t type);
|
|
bool (*del_bp)(void *args, size_t addr, bp_type_t type);
|
|
void (*on_interrupt)(void *args);
|
|
|
|
void (*set_cpu)(void *args, int cpuid);
|
|
int (*get_cpu)(void *args);
|
|
};
|
|
```
|
|
|
|
For `cont` and `stepi` which are used to process the execution of emulator, their return type
|
|
should be `gdb_action_t`. After performing the relevant operation, you should return `ACT_RESUME`
|
|
to continue debugging; otherwise, return `ACT_SHUTDOWN` to finish debugging. The library
|
|
typically uses `ACT_NONE` to take no action.
|
|
|
|
```c
|
|
typedef enum {
|
|
ACT_NONE,
|
|
ACT_RESUME,
|
|
ACT_SHUTDOWN,
|
|
} gdb_action_t;
|
|
```
|
|
|
|
For `set_bp` and `del_bp`, the type of breakpoint which should be set or deleted is described
|
|
in the type `bp_type_t`. In fact, its value will always be `BP_SOFTWARE` currently.
|
|
|
|
```c
|
|
typedef enum {
|
|
BP_SOFTWARE = 0,
|
|
} bp_type_t;
|
|
```
|
|
|
|
Another structure you have to declare is `arch_info_t`. You must explicitly specify about the
|
|
following field within `arch_info_t` while integrating into your emulator:
|
|
* `smp`: Number of target's CPU
|
|
* `reg_num`: Number of target's registers
|
|
|
|
The `target_desc` is an optional member which could be
|
|
`TARGET_RV32`, `TARGET_RV64` if the emulator is RISC-V 32-bit or 64-bit instruction
|
|
set architecture or `TARGET_X86_64` if the emulator is x86_64 instruction set architecture. Alternatively, it can be a custom target description document
|
|
string used by gdb. If none of these apply, simply set it to NULL.
|
|
|
|
* Although the value of `reg_num` may be determined by `target_desc`, those
|
|
members are still required to be filled correctly.
|
|
|
|
```c
|
|
typedef struct {
|
|
char *target_desc;
|
|
int smp;
|
|
int reg_num;
|
|
} arch_info_t;
|
|
```
|
|
|
|
After startup, we can use `gdbstub_run` to run the emulator as gdbstub. The `args`
|
|
can be used to pass the argument to any function in `struct target_ops`.
|
|
|
|
```c
|
|
bool gdbstub_run(gdbstub_t *gdbstub, void *args);
|
|
```
|
|
|
|
When exiting from `gdbstub_run`, `gdbstub_close` should be called to recycle the resource on
|
|
the initialization.
|
|
|
|
```c
|
|
void gdbstub_close(gdbstub_t *gdbstub);
|
|
```
|
|
|
|
Finally, you can build you project with the statically-linked library `libgdbstub.a` now!
|
|
Additionally, it is advised that you check the reference emulator in the directory `emu,` which
|
|
demonstrates how to integrate `mini-gdbstub` into your project.
|
|
|
|
## Reference
|
|
### Project
|
|
* [bet4it/gdbserver](https://github.com/bet4it/gdbserver)
|
|
* [devbored/minigdbstub](https://github.com/devbored/minigdbstub)
|
|
### Article
|
|
* [Howto: GDB Remote Serial Protocol](https://www.embecosm.com/appnotes/ean4/embecosm-howto-rsp-server-ean4-issue-2.html)
|
|
* [TLMBoy: Implementing the GDB Remote Serial Protocol](https://www.chciken.com/tlmboy/2022/04/03/gdb-z80.html)
|