diff --git a/Cargo.lock b/Cargo.lock index 3c80d98..4b3eb56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,6 +397,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1742,6 +1751,7 @@ name = "lemur" version = "0.2.5" dependencies = [ "anyhow", + "atoi", "bitflags 2.6.0", "bytemuck", "cc", diff --git a/Cargo.toml b/Cargo.toml index f7e7434..0e1b3fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" [dependencies] anyhow = "1" +atoi = "2" bitflags = { version = "2", features = ["serde"] } bytemuck = { version = "1", features = ["derive"] } clap = { version = "4", features = ["derive"] } diff --git a/src/gdbserver.rs b/src/gdbserver.rs index 97171e0..ae11619 100644 --- a/src/gdbserver.rs +++ b/src/gdbserver.rs @@ -212,6 +212,33 @@ impl GdbConnection { } else if req.match_str("c") || req.match_str("vCont;c:") { self.client.send_command(EmulatorCommand::Resume); // Don't send a response until we hit a breakpoint or get interrupted + } else if req.match_str("p") { + let Some(register) = req.match_hex::() else { + let res = self.response(); + self.send(res).await?; + continue; + }; + let res = self.response().write_hex(register); + self.send(res).await?; + } else if req.match_str("m") { + let mut read_params = || { + let start = req.match_hex::()?; + if !req.match_str(",") { + return None; + }; + let size = req.match_hex::()?; + Some((start, size)) + }; + let Some((start, size)) = read_params() else { + let res = self.response(); + self.send(res).await?; + continue; + }; + let mut res = self.response(); + for i in 0..size { + res = res.write_hex((start + i) as u8); + } + self.send(res).await?; } else { // unrecognized command let res = self.response(); diff --git a/src/gdbserver/request.rs b/src/gdbserver/request.rs index 63f66e8..6648794 100644 --- a/src/gdbserver/request.rs +++ b/src/gdbserver/request.rs @@ -1,4 +1,5 @@ use anyhow::{bail, Result}; +use atoi::FromRadix16; use tokio::io::{AsyncRead, AsyncReadExt as _}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -6,11 +7,32 @@ pub enum RequestKind { Signal, Command, } +impl RequestKind { + fn name(self) -> &'static str { + match self { + Self::Signal => "Signal", + Self::Command => "Command", + } + } +} -#[derive(Debug)] pub struct Request<'a> { pub kind: RequestKind, - body: &'a str, + buffer: &'a [u8], +} + +impl std::fmt::Debug for Request<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut ds = f.debug_tuple(self.kind.name()); + match self.kind { + RequestKind::Signal => ds.field(&self.buffer), + RequestKind::Command => match std::str::from_utf8(self.buffer) { + Ok(str) => ds.field(&str), + Err(_) => ds.field(&self.buffer), + }, + }; + ds.finish() + } } impl<'a> Request<'a> { @@ -31,10 +53,9 @@ impl<'a> Request<'a> { if char == 0x03 { // This is how the client "cancels an in-flight request" buffer.push(char); - let body = std::str::from_utf8(buffer)?; return Ok(Self { kind: RequestKind::Signal, - body, + buffer, }); } if char != b'$' { @@ -61,24 +82,34 @@ impl<'a> Request<'a> { let mut checksum_bytes = [b'0'; 2]; reader.read_exact(&mut checksum_bytes).await?; - let checksum_str = std::str::from_utf8(&checksum_bytes)?; - let real_checksum = u8::from_str_radix(checksum_str, 16)?; - if checksum != real_checksum { + let (real_checksum, 2) = u8::from_radix_16(&checksum_bytes) else { bail!("invalid checksum"); + }; + if checksum != real_checksum { + bail!("mismatched checksum"); } - let body = std::str::from_utf8(buffer)?; Ok(Self { kind: RequestKind::Command, - body, + buffer, }) } pub fn match_str(&mut self, prefix: &str) -> bool { - if let Some(new_body) = self.body.strip_prefix(prefix) { - self.body = new_body; + if let Some(new_buffer) = self.buffer.strip_prefix(prefix.as_bytes()) { + self.buffer = new_buffer; return true; } false } + + pub fn match_hex(&mut self) -> Option { + match I::from_radix_16(self.buffer) { + (_, 0) => None, + (val, used) => { + self.buffer = self.buffer.split_at(used).1; + Some(val) + } + } + } } diff --git a/src/gdbserver/response.rs b/src/gdbserver/response.rs index fdd9220..b7c684e 100644 --- a/src/gdbserver/response.rs +++ b/src/gdbserver/response.rs @@ -1,3 +1,5 @@ +use num_traits::ToBytes; + pub struct Response { buffer: Vec, checksum: u8, @@ -17,22 +19,24 @@ impl Response { } pub fn write_str(mut self, str: &str) -> Self { - for char in str.as_bytes() { - self.buffer.push(*char); - self.checksum = self.checksum.wrapping_add(*char); + for byte in str.as_bytes() { + self.buffer.push(*byte); + self.checksum = self.checksum.wrapping_add(*byte); } self } - pub fn write_hex_u8(mut self, value: u8) -> Self { - for digit in [(value >> 4), (value & 0xf)] { - let char = if digit > 9 { - b'a' + digit - 10 - } else { - b'0' + digit - }; - self.buffer.push(char); - self.checksum = self.checksum.wrapping_add(char); + pub fn write_hex(mut self, value: T) -> Self { + for byte in value.to_be_bytes().as_ref() { + for digit in [(byte >> 4), (byte & 0xf)] { + let char = if digit > 9 { + b'a' + digit - 10 + } else { + b'0' + digit + }; + self.buffer.push(char); + self.checksum = self.checksum.wrapping_add(char); + } } self } @@ -40,6 +44,6 @@ impl Response { pub fn finish(mut self) -> Vec { let checksum = self.checksum; self.buffer.push(b'#'); - self.write_hex_u8(checksum).buffer + self.write_hex(checksum).buffer } }