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