use std::io::{Read, Write};
use std::net::TcpStream;
use lager::LagerBox;
fn main() -> lager::Result<()> {
let net = std::env::args()
.nth(1)
.unwrap_or_else(|| "debug1".to_string());
let lager_box = LagerBox::from_env()?;
let debug = lager_box.debug(&net);
let connection = debug.connect()?;
let port = connection
.gdb_server
.and_then(|server| server.gdb_port)
.unwrap_or(2331) as u16;
println!("GDB server for {net} on port {port}");
let mut stream = lager_box.debug_tunnel(port)?;
let result = session(&mut stream);
drop(stream);
debug.disconnect(false)?;
result.map_err(|e| lager::Error::Stream(format!("GDB remote protocol: {e}")))
}
fn session(stream: &mut TcpStream) -> std::io::Result<()> {
let features = exchange(stream, "qSupported:multiprocess+;swbreak+;hwbreak+")?;
println!("qSupported -> {features}");
let memory = exchange(stream, "m20000000,20")?;
println!("m20000000,20 -> {memory}");
if let Some(text) = hex_to_text(&memory) {
println!(" as text: {text:?}");
}
println!("D -> {}", exchange(stream, "D")?);
Ok(())
}
fn exchange(stream: &mut TcpStream, payload: &str) -> std::io::Result<String> {
let checksum = payload.bytes().fold(0u8, |acc, b| acc.wrapping_add(b));
stream.write_all(format!("${payload}#{checksum:02x}").as_bytes())?;
let reply = read_packet(stream)?;
stream.write_all(b"+")?;
Ok(reply)
}
fn read_packet(stream: &mut TcpStream) -> std::io::Result<String> {
let mut byte = [0u8; 1];
loop {
stream.read_exact(&mut byte)?;
if byte[0] == b'$' {
break;
}
}
let mut payload = Vec::new();
loop {
stream.read_exact(&mut byte)?;
if byte[0] == b'#' {
break;
}
payload.push(byte[0]);
}
let mut checksum = [0u8; 2];
stream.read_exact(&mut checksum)?;
Ok(String::from_utf8_lossy(&payload).into_owned())
}
fn hex_to_text(hex: &str) -> Option<String> {
let bytes: Option<Vec<u8>> = (0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(hex.get(i..i + 2)?, 16).ok())
.collect();
Some(String::from_utf8_lossy(&bytes?).into_owned())
}