Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic gdb server functionality. #52

Merged
merged 2 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
341 changes: 206 additions & 135 deletions Cargo.lock

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ zmu supports Linux and Windows operating systems.
- DWT
- Cycle counter
- Instruction trace
- GDB Server
- single stepping
- range stepping
- breakpoints

## Missing / Planned features
- Time simulation / sync to real time
Expand All @@ -43,6 +47,10 @@ zmu supports Linux and Windows operating systems.
- System Simulation:
- device profiles, eg stm32 device support
- board profiles, external peripheral simulation
- GDB Server:
- Reading/Writting memory
- Writting Registers
- Pass the port on the command line

## Depedencies

Expand Down Expand Up @@ -178,3 +186,43 @@ Run the emulator:
$zmu run tests/hello_world/hello_world-cm0.elf
hello, world
```

Run the GDB Server:
```
$zmu run --gdb tests/hello_world/hello_world-cm0.elf
Starting GDB Server on port 9001 ...
```

On a separate terminal start the gdb client:
```
$ gdb-multiarch tests/hello_world/hello_world-cm0.elf
GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from tests/hello_world/hello_world-cm0.elf...
(gdb) target remote localhost:9001
Remote debugging using localhost:9001
Reset_Handler ()
at /usr/share/doc/gcc-arm-none-eabi/examples/startup/startup_ARMCM0.S:150
150 ldr r1, =__etext
(gdb) b main
Breakpoint 1 at 0x5c: file main.c, line 6.
(gdb) c
Continuing.

Breakpoint 1, main () at main.c:6
6 printf("hello, world\n");
(gdb)
```
26 changes: 26 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use zmu_cortex_m::Processor;

use zmu_cortex_m::system::simulation::simulate_trace;
use zmu_cortex_m::system::simulation::{simulate, SimulationError};
use zmu_cortex_m::gdb::server::GdbServer;

mod errors {
// Create the Error, ErrorKind, ResultExt, and Result types
Expand All @@ -59,6 +60,7 @@ fn run_bin(
trace: bool,
option_trace_start: Option<u64>,
itm_file: Option<Box<dyn io::Write + 'static>>,
gdb: bool,
) -> Result<u32> {
let res = Object::parse(buffer).unwrap();

Expand Down Expand Up @@ -122,6 +124,22 @@ fn run_bin(
let trace_start = option_trace_start.unwrap_or(0);
let semihost_func = Box::new(get_semihost_func(Instant::now()));

if gdb {
let gdb = GdbServer::new(
&flash_mem,
semihost_func,
if flash_start_address != 0 {
Some(MemoryMapConfig::new(flash_start_address, 0, flash_size))
} else {
None
},
flash_size,
);

let exit_code = gdb?.start().expect("GDB server failed");
return Ok(exit_code);
}

let statistics = if trace {
debug!("Configuring tracing.");

Expand Down Expand Up @@ -234,6 +252,7 @@ fn run(args: &ArgMatches) -> Result<u32> {
run_matches.get_flag("trace"),
trace_start,
itm_output,
run_matches.get_flag("gdb"),
)?
}
Some((_, _)) => unreachable!(),
Expand Down Expand Up @@ -289,6 +308,13 @@ fn main() {
.help("List of free arguments to pass to runtime as parameters")
.index(2)
.action(ArgAction::Append),
)
.arg(
Arg::new("gdb")
.action(ArgAction::SetTrue)
.long("gdb")
.help("Enable the gdb server")
.num_args(0)
),
)
.get_matches();
Expand Down
3 changes: 3 additions & 0 deletions zmu_cortex_m/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ edition = "2021"
[dependencies]
byteorder = "1"
enum-set = "0.0.8"
gdbstub = "0.7"
gdbstub_arch = "0.3"
log = "0.4"


[features]
Expand Down
88 changes: 88 additions & 0 deletions zmu_cortex_m/src/gdb/conn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use gdbstub::conn::{Connection, ConnectionExt};
use std::{
io::Read, net::{Shutdown, TcpListener, TcpStream}, str
};

pub struct TcpConnection {
stream: TcpStream,
}

impl TcpConnection {
pub fn new_localhost(port: u16) -> Result<TcpConnection, &'static str> {
let listener = TcpListener::bind(("127.0.0.1", port)).unwrap();

for stream in listener.incoming() {
let stream = stream.map_err(|_| "Error accepting socket connection")?;
stream.set_read_timeout(Some(std::time::Duration::from_millis(1)))
.map_err(|_| "Error setting timeout")?;
return Ok(TcpConnection { stream });
};

Err("could not accept socket connection")
}
}

impl Drop for TcpConnection {
fn drop(&mut self) {
self.stream.shutdown(Shutdown::Both).expect("shutdown failed");
}
}

impl Connection for TcpConnection {
type Error = &'static str;

fn write(&mut self, b: u8) -> Result<(), &'static str> {
match self.stream.write(b) {
Ok(_) => Ok(()),
Err(_) => Err("socket write failed")
}
}

fn flush(&mut self) -> Result<(), &'static str> {
match self.stream.flush() {
Ok(_) => Ok(()),
Err(_) => Err("socket flush failed")
}
}
}

impl ConnectionExt for TcpConnection {

fn read(&mut self) -> std::result::Result<u8, Self::Error> {
let mut buf: [u8; 1] = [0];
loop {
match self.stream.read_exact(&mut buf)
{
Ok(_) => break,
Err(e) => match e.kind() {
#[cfg(windows)]
std::io::ErrorKind::TimedOut => continue,
#[cfg(unix)]
std::io::ErrorKind::WouldBlock => continue,
_ => return Err("socket read failed")
}
}
}
Ok(buf[0])
}

fn peek(&mut self) -> std::result::Result<Option<u8>, Self::Error> {
let mut buf: [u8; 1] = [0];
loop {
match self.stream.peek(&mut buf)
{
Ok(_) => break,
Err(e) => match e.kind() {
#[cfg(windows)]
std::io::ErrorKind::TimedOut => return Ok(None),
#[cfg(unix)]
std::io::ErrorKind::WouldBlock => return Ok(None),
_ => {
return Err("socket peek failed")
}
}
}
}
Ok(Some(buf[0]))
}
}
8 changes: 8 additions & 0 deletions zmu_cortex_m/src/gdb/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//!
//! Gdb Module
//!

pub mod server;
mod conn;
mod simulation;
mod target;
Loading
Loading