use std::env;
use std::net::Ipv4Addr;
use std::net::SocketAddr;
use std::net::TcpListener;
use std::net::TcpStream;
use std::num::NonZeroI32;
use embive::interpreter::memory::{Memory, MemoryType, SliceMemory};
use embive::interpreter::Debugger;
use embive::interpreter::Error;
use embive::interpreter::Interpreter;
use embive::interpreter::SYSCALL_ARGS;
use embive::transpiler::transpile_elf;
use gdbstub::conn::{Connection, ConnectionExt};
use gdbstub::stub::{DisconnectReason, GdbStub};
struct TcpConnection {
stream: TcpStream,
}
impl TcpConnection {
fn new(stream: TcpStream) -> Self {
Self { stream }
}
}
impl Connection for TcpConnection {
type Error = std::io::Error;
fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
use std::io::Write;
Write::write_all(&mut self.stream, &[byte])
}
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
use std::io::Write;
Write::write_all(&mut self.stream, buf)
}
fn flush(&mut self) -> Result<(), Self::Error> {
use std::io::Write;
Write::flush(&mut self.stream)
}
fn on_session_start(&mut self) -> Result<(), Self::Error> {
self.stream.set_nodelay(true)
}
}
impl ConnectionExt for TcpConnection {
fn read(&mut self) -> Result<u8, Self::Error> {
use std::io::Read;
self.stream.set_nonblocking(false)?;
let mut buf = [0u8];
match Read::read_exact(&mut self.stream, &mut buf) {
Ok(_) => Ok(buf[0]),
Err(e) => Err(e),
}
}
fn peek(&mut self) -> Result<Option<u8>, Self::Error> {
self.stream.set_nonblocking(true)?;
let mut buf = [0u8];
match self.stream.peek(&mut buf) {
Ok(_) => Ok(Some(buf[0])),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
Err(e) => Err(e),
}
}
}
fn syscall<M: Memory>(
nr: i32,
args: &[i32; SYSCALL_ARGS],
memory: &mut M,
) -> Result<Result<i32, NonZeroI32>, Error> {
Ok(match nr {
1 => Ok(args[0] + args[1]),
2 => match i32::load(memory, args[0] as u32) {
Ok(val) => Ok(val),
Err(_) => Err(1.try_into().unwrap()), },
_ => Err(2.try_into().unwrap()), })
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <binary.elf>", args[0]);
return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput).into());
}
println!("Reading ELF: {}", args[1]);
let elf = std::fs::read(&args[1])?;
let mut code = [0; 256 * 1024];
println!("Transpiling ELF...");
transpile_elf(&elf, &mut code)?;
println!("ELF transpiled!");
let mut ram = [0; 64 * 1024];
let mut memory = SliceMemory::new(code.as_slice(), &mut ram);
let mut debugger: Debugger<'_, _, TcpConnection, _> = Debugger::new(&mut memory, syscall);
println!("Waiting for GDB client to connect (localhost:9001)...");
let sock = TcpListener::bind(SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 9001))?;
let (stream, addr) = sock.accept()?;
let conn = TcpConnection::new(stream);
println!("Connected to: {}", addr);
let mut buffer = [0; 4096];
let gdb = GdbStub::builder(conn)
.with_packet_buffer(&mut buffer)
.build()
.map_err(|e| std::io::Error::other(e.to_string()))?;
match gdb.run_blocking::<Debugger<'_, _, TcpConnection, _>>(&mut debugger) {
Ok(disconnect_reason) => match disconnect_reason {
DisconnectReason::Disconnect => {
println!("GDB client has disconnected.");
}
DisconnectReason::TargetExited(code) => {
println!("Target exited with code {}!", code)
}
DisconnectReason::TargetTerminated(sig) => {
println!("Target terminated with signal {}!", sig)
}
DisconnectReason::Kill => println!("GDB sent a kill command!"),
},
Err(e) => {
if e.is_target_error() {
println!(
"target encountered a fatal error: {}",
e.into_target_error().unwrap()
)
} else if e.is_connection_error() {
let (e, kind) = e.into_connection_error().unwrap();
println!("connection error: {:?} - {}", kind, e,)
} else {
println!("gdbstub encountered a fatal error: {}", e)
}
}
}
let mut interpreter: Interpreter<'_, SliceMemory> = debugger.into();
println!("");
println!("Interpreter State:");
println!("Registers: {:?}", interpreter.registers.cpu);
println!("Program Counter: {:#08x}", interpreter.program_counter);
println!("Instruction: {:?}", interpreter.fetch());
return Ok(());
}