1 use std::net::{TcpListener, TcpStream};
2 
3 #[cfg(unix)]
4 use std::os::unix::net::{UnixListener, UnixStream};
5 
6 use gdbstub::{Connection, DisconnectReason, GdbStub};
7 
8 pub type DynResult<T> = Result<T, Box<dyn std::error::Error>>;
9 
10 static TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf");
11 
12 mod emu;
13 mod gdb;
14 mod mem_sniffer;
15 
wait_for_tcp(port: u16) -> DynResult<TcpStream>16 fn wait_for_tcp(port: u16) -> DynResult<TcpStream> {
17     let sockaddr = format!("127.0.0.1:{}", port);
18     eprintln!("Waiting for a GDB connection on {:?}...", sockaddr);
19 
20     let sock = TcpListener::bind(sockaddr)?;
21     let (stream, addr) = sock.accept()?;
22     eprintln!("Debugger connected from {}", addr);
23 
24     Ok(stream)
25 }
26 
27 #[cfg(unix)]
wait_for_uds(path: &str) -> DynResult<UnixStream>28 fn wait_for_uds(path: &str) -> DynResult<UnixStream> {
29     match std::fs::remove_file(path) {
30         Ok(_) => {}
31         Err(e) => match e.kind() {
32             std::io::ErrorKind::NotFound => {}
33             _ => return Err(e.into()),
34         },
35     }
36 
37     eprintln!("Waiting for a GDB connection on {}...", path);
38 
39     let sock = UnixListener::bind(path)?;
40     let (stream, addr) = sock.accept()?;
41     eprintln!("Debugger connected from {:?}", addr);
42 
43     Ok(stream)
44 }
45 
main() -> DynResult<()>46 fn main() -> DynResult<()> {
47     pretty_env_logger::init();
48 
49     let mut emu = emu::Emu::new(TEST_PROGRAM_ELF)?;
50 
51     let connection: Box<dyn Connection<Error = std::io::Error>> = {
52         if std::env::args().nth(1) == Some("--uds".to_string()) {
53             #[cfg(not(unix))]
54             {
55                 return Err("Unix Domain Sockets can only be used on Unix".into());
56             }
57             #[cfg(unix)]
58             {
59                 Box::new(wait_for_uds("/tmp/armv4t_gdb")?)
60             }
61         } else {
62             Box::new(wait_for_tcp(9001)?)
63         }
64     };
65 
66     // hook-up debugger
67     let mut debugger = GdbStub::new(connection);
68 
69     match debugger.run(&mut emu)? {
70         DisconnectReason::Disconnect => {
71             // run to completion
72             while emu.step() != Some((emu::Event::Halted, emu::CpuId::Cpu)) {}
73         }
74         DisconnectReason::TargetHalted => println!("Target halted!"),
75         DisconnectReason::Kill => {
76             println!("GDB sent a kill command!");
77             return Ok(());
78         }
79     }
80 
81     let ret = emu.cpu.reg_get(armv4t_emu::Mode::User, 0);
82     println!("Program completed. Return value: {}", ret);
83 
84     Ok(())
85 }
86