1use std::env;
11use std::net::Ipv4Addr;
12use std::net::SocketAddr;
13use std::net::TcpListener;
14use std::net::TcpStream;
15use std::num::NonZeroI32;
16
17use embive::interpreter::memory::{Memory, MemoryType, SliceMemory};
18use embive::interpreter::Debugger;
19use embive::interpreter::Error;
20use embive::interpreter::Interpreter;
21use embive::interpreter::SYSCALL_ARGS;
22use embive::transpiler::transpile_elf;
23use gdbstub::conn::{Connection, ConnectionExt};
24use gdbstub::stub::{DisconnectReason, GdbStub};
25
26struct TcpConnection {
28 stream: TcpStream,
29}
30
31impl TcpConnection {
32 fn new(stream: TcpStream) -> Self {
33 Self { stream }
34 }
35}
36
37impl Connection for TcpConnection {
38 type Error = std::io::Error;
39
40 fn write(&mut self, byte: u8) -> Result<(), Self::Error> {
41 use std::io::Write;
42
43 Write::write_all(&mut self.stream, &[byte])
44 }
45
46 fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
47 use std::io::Write;
48
49 Write::write_all(&mut self.stream, buf)
50 }
51
52 fn flush(&mut self) -> Result<(), Self::Error> {
53 use std::io::Write;
54
55 Write::flush(&mut self.stream)
56 }
57
58 fn on_session_start(&mut self) -> Result<(), Self::Error> {
59 self.stream.set_nodelay(true)
61 }
62}
63
64impl ConnectionExt for TcpConnection {
65 fn read(&mut self) -> Result<u8, Self::Error> {
66 use std::io::Read;
67
68 self.stream.set_nonblocking(false)?;
69
70 let mut buf = [0u8];
71 match Read::read_exact(&mut self.stream, &mut buf) {
72 Ok(_) => Ok(buf[0]),
73 Err(e) => Err(e),
74 }
75 }
76
77 fn peek(&mut self) -> Result<Option<u8>, Self::Error> {
78 self.stream.set_nonblocking(true)?;
79
80 let mut buf = [0u8];
81 match self.stream.peek(&mut buf) {
82 Ok(_) => Ok(Some(buf[0])),
83 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
84 Err(e) => Err(e),
85 }
86 }
87}
88
89fn syscall<M: Memory>(
91 nr: i32,
92 args: &[i32; SYSCALL_ARGS],
93 memory: &mut M,
94) -> Result<Result<i32, NonZeroI32>, Error> {
95 Ok(match nr {
97 1 => Ok(args[0] + args[1]),
99 2 => match i32::load(memory, args[0] as u32) {
101 Ok(val) => Ok(val),
102 Err(_) => Err(1.try_into().unwrap()), },
104 _ => Err(2.try_into().unwrap()), })
106}
107
108fn main() -> Result<(), Box<dyn std::error::Error>> {
109 let args: Vec<String> = env::args().collect();
111 if args.len() != 2 {
112 eprintln!("Usage: {} <binary.elf>", args[0]);
113 return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput).into());
114 }
115
116 println!("Reading ELF: {}", args[1]);
118 let elf = std::fs::read(&args[1])?;
119
120 let mut code = [0; 256 * 1024];
122 println!("Transpiling ELF...");
123 transpile_elf(&elf, &mut code)?;
124 println!("ELF transpiled!");
125
126 let mut ram = [0; 64 * 1024];
128 let mut memory = SliceMemory::new(code.as_slice(), &mut ram);
129 let mut debugger: Debugger<'_, _, TcpConnection, _> = Debugger::new(&mut memory, syscall);
130
131 println!("Waiting for GDB client to connect (localhost:9001)...");
133 let sock = TcpListener::bind(SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 9001))?;
134 let (stream, addr) = sock.accept()?;
135 let conn = TcpConnection::new(stream);
136 println!("Connected to: {}", addr);
137
138 let mut buffer = [0; 4096];
140 let gdb = GdbStub::builder(conn)
141 .with_packet_buffer(&mut buffer)
142 .build()
143 .map_err(|e| std::io::Error::other(e.to_string()))?;
144
145 match gdb.run_blocking::<Debugger<'_, _, TcpConnection, _>>(&mut debugger) {
147 Ok(disconnect_reason) => match disconnect_reason {
148 DisconnectReason::Disconnect => {
149 println!("GDB client has disconnected.");
150 }
151 DisconnectReason::TargetExited(code) => {
152 println!("Target exited with code {}!", code)
153 }
154 DisconnectReason::TargetTerminated(sig) => {
155 println!("Target terminated with signal {}!", sig)
156 }
157 DisconnectReason::Kill => println!("GDB sent a kill command!"),
158 },
159 Err(e) => {
160 if e.is_target_error() {
161 println!(
162 "target encountered a fatal error: {}",
163 e.into_target_error().unwrap()
164 )
165 } else if e.is_connection_error() {
166 let (e, kind) = e.into_connection_error().unwrap();
167 println!("connection error: {:?} - {}", kind, e,)
168 } else {
169 println!("gdbstub encountered a fatal error: {}", e)
170 }
171 }
172 }
173
174 let mut interpreter: Interpreter<'_, SliceMemory> = debugger.into();
176 println!("");
177 println!("Interpreter State:");
178 println!("Registers: {:?}", interpreter.registers.cpu);
179 println!("Program Counter: {:#08x}", interpreter.program_counter);
180 println!("Instruction: {:?}", interpreter.fetch());
181
182 return Ok(());
183}