use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use core::marker::PhantomData;
use log::*;
use num_traits::ops::saturating::Saturating;
use crate::{
protocol::{Command, Packet, ResponseWriter},
Access, AccessKind, Connection, Error, Target, TargetState,
};
enum ExecState {
Paused,
Running { single_step: bool },
Exit,
}
pub struct GdbStub<T: Target, C: Connection> {
conn: C,
exec_state: ExecState,
swbreak: BTreeSet<T::Usize>,
hwbreak: BTreeSet<T::Usize>,
wwatch: BTreeSet<T::Usize>,
rwatch: BTreeSet<T::Usize>,
awatch: BTreeSet<T::Usize>,
_target: PhantomData<T>,
}
impl<T: Target, C: Connection> GdbStub<T, C> {
pub fn new(conn: C) -> GdbStub<T, C> {
GdbStub {
conn,
swbreak: BTreeSet::new(),
hwbreak: BTreeSet::new(),
wwatch: BTreeSet::new(),
rwatch: BTreeSet::new(),
awatch: BTreeSet::new(),
exec_state: ExecState::Paused,
_target: PhantomData,
}
}
fn handle_command(&mut self, target: &mut T, command: Command) -> Result<(), Error<T, C>> {
self.conn.write(b'+').map_err(Error::ConnectionRead)?;
let mut res = ResponseWriter::new(&mut self.conn);
match command {
Command::qSupported(_features) => {
res.write_str("swbreak+;")?;
res.write_str("hwbreak+;")?;
res.write_str("vContSupported+;")?;
if T::target_description_xml().is_some() {
res.write_str("qXfer:features:read+;")?;
}
}
Command::vContQuestionMark(_) => res.write_str("vCont;c;C;s;S;t")?,
Command::qXferFeaturesRead(cmd) => {
let _annex = cmd.annex;
match T::target_description_xml() {
Some(xml) => {
let xml = xml.trim();
if cmd.offset >= xml.len() {
res.write_str("l")?;
} else if cmd.offset + cmd.len >= xml.len() {
res.write_str("l")?;
res.write_binary(&xml.as_bytes()[cmd.offset..])?
} else {
res.write_str("m")?;
res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])?
}
}
None => unreachable!(),
}
}
Command::QuestionMark(_) => res.write_str("S05")?,
Command::qAttached(_) => res.write_str("1")?,
Command::g(_) => {
let mut err = Ok(());
target.read_registers(|reg| {
if let Err(e) = res.write_hex_buf(reg) {
err = Err(e)
}
});
err?;
}
Command::G(cmd) => {
target.write_registers(cmd.vals.as_slice());
res.write_str("OK")?;
}
Command::m(cmd) => {
let mut err = Ok(());
let start: T::Usize = num_traits::NumCast::from(cmd.addr).unwrap();
let end = start.saturating_add(num_traits::NumCast::from(cmd.len).unwrap());
target.read_addrs(start..end, |val| {
if let Err(e) = res.write_hex(val) {
err = Err(e)
}
});
err?;
}
Command::M(cmd) => {
let addr = cmd.addr;
let mut val = cmd
.val
.into_iter()
.enumerate()
.map(|(i, v)| (addr + i as u64, v))
.map(|(i, v)| (num_traits::NumCast::from(i).unwrap(), v));
target.write_addrs(|| val.next());
}
Command::D(_) => {
res.write_str("OK")?;
self.exec_state = ExecState::Exit
}
Command::Z(cmd) => {
let addr = num_traits::NumCast::from(cmd.addr).unwrap();
let supported = match cmd.type_ {
0 => Some(self.swbreak.insert(addr)),
1 => Some(self.hwbreak.insert(addr)),
2 => Some(self.wwatch.insert(addr)),
3 => Some(self.rwatch.insert(addr)),
4 => Some(self.awatch.insert(addr)),
_ => None,
};
if supported.is_some() {
res.write_str("OK")?;
}
}
Command::z(cmd) => {
let addr = num_traits::NumCast::from(cmd.addr).unwrap();
let supported = match cmd.type_ {
0 => Some(self.swbreak.remove(&addr)),
1 => Some(self.hwbreak.remove(&addr)),
2 => Some(self.wwatch.remove(&addr)),
3 => Some(self.rwatch.remove(&addr)),
4 => Some(self.awatch.remove(&addr)),
_ => None,
};
if supported.is_some() {
res.write_str("OK")?;
}
}
Command::vCont(cmd) => {
use crate::protocol::_vCont::VContKind;
let action = &cmd.actions[0];
self.exec_state = match action.kind {
VContKind::Step => ExecState::Running { single_step: true },
VContKind::Continue => ExecState::Running { single_step: false },
_ => unimplemented!("unsupported vCont action"),
};
return Ok(());
}
Command::c(_) => {
self.exec_state = ExecState::Running { single_step: false };
return Ok(());
}
Command::s(_) => {
self.exec_state = ExecState::Running { single_step: true };
return Ok(());
}
Command::H(_) => res.write_str("OK")?,
Command::qfThreadInfo(_) => res.write_str("m1")?,
Command::qsThreadInfo(_) => res.write_str("l")?,
Command::qC(_) => res.write_str("QC1")?,
Command::Unknown(cmd) => warn!("Unknown command: {}", cmd),
#[allow(unreachable_patterns)]
c => warn!("Unimplemented command: {:?}", c),
}
res.flush().map_err(Error::ConnectionWrite)
}
fn recv_packet<'a, 'b>(
&'a mut self,
packet_buffer: &'b mut Vec<u8>,
) -> Result<Option<Packet<'b>>, Error<T, C>> {
let header_byte = match self.exec_state {
ExecState::Paused => self.conn.read().map(Some),
ExecState::Running { .. } => self.conn.read_nonblocking(),
ExecState::Exit => unreachable!(),
};
match header_byte {
Ok(None) => Ok(None),
Ok(Some(header_byte)) => {
packet_buffer.clear();
packet_buffer.push(header_byte);
if header_byte == b'$' {
loop {
match self.conn.read().map_err(Error::ConnectionRead)? {
b'#' => break,
x => packet_buffer.push(x),
}
}
packet_buffer.push(b'#');
packet_buffer.push(self.conn.read().map_err(Error::ConnectionRead)?);
packet_buffer.push(self.conn.read().map_err(Error::ConnectionRead)?);
}
match Packet::from_buf(packet_buffer) {
Ok(packet) => Ok(Some(packet)),
Err(e) => {
error!("Could not parse packet: {:?}", e);
Err(Error::PacketParse)
}
}
}
Err(e) => Err(Error::ConnectionRead(e)),
}
}
fn send_breakpoint_stop_response(
&mut self,
stop_reason: StopReason<T::Usize>,
) -> Result<(), Error<T, C>> {
let mut res = ResponseWriter::new(&mut self.conn);
res.write_str("T")?;
res.write_hex(5)?;
macro_rules! write_addr {
($addr:expr) => {
let addr: u64 = num_traits::NumCast::from($addr).unwrap();
res.write_hex_buf(&addr.to_ne_bytes())?;
};
}
match stop_reason {
StopReason::WWatch(addr) => {
res.write_str("watch:")?;
write_addr!(addr);
}
StopReason::RWatch(addr) => {
res.write_str("rwatch:")?;
write_addr!(addr);
}
StopReason::AWatch(addr) => {
res.write_str("awatch:")?;
write_addr!(addr);
}
StopReason::SwBreak => res.write_str("swbreak:")?,
StopReason::HwBreak => res.write_str("hwbreak:")?,
};
res.write_str(";")?;
Ok(res.flush()?)
}
pub fn run(&mut self, target: &mut T) -> Result<TargetState, Error<T, C>> {
let mut packet_buffer = Vec::new();
loop {
match self.recv_packet(&mut packet_buffer)? {
None => {}
Some(packet) => match packet {
Packet::Ack => {}
Packet::Nack => unimplemented!(),
Packet::Interrupt => {
self.exec_state = ExecState::Paused;
let mut res = ResponseWriter::new(&mut self.conn);
res.write_str("S05")?;
res.flush()?;
}
Packet::Command(command) => {
self.handle_command(target, command)?;
}
},
};
match self.exec_state {
ExecState::Paused => {}
ExecState::Exit => {
return Ok(TargetState::Running);
}
ExecState::Running { single_step } => {
let mut stop_reason = None;
let on_access = |access: Access<T::Usize>| {
if self.awatch.contains(&access.addr) {
stop_reason = Some(StopReason::AWatch(access.addr));
return;
}
match access.kind {
AccessKind::Read => {
if self.rwatch.contains(&access.addr) {
stop_reason = Some(StopReason::RWatch(access.addr))
}
}
AccessKind::Write => {
if self.wwatch.contains(&access.addr) {
stop_reason = Some(StopReason::WWatch(access.addr))
}
}
}
};
let target_state = target.step(on_access).map_err(Error::TargetError)?;
if target_state == TargetState::Halted {
let mut res = ResponseWriter::new(&mut self.conn);
res.write_str("W00")?;
res.flush()?;
return Ok(TargetState::Halted);
};
let target_pc = target.read_pc();
if target_state == TargetState::SoftwareBreakpoint
|| self.swbreak.contains(&target_pc)
{
stop_reason = Some(StopReason::SwBreak)
} else if self.hwbreak.contains(&target_pc) {
stop_reason = Some(StopReason::HwBreak)
}
if let Some(stop_reason) = stop_reason {
warn!("{:x?}", stop_reason);
self.send_breakpoint_stop_response(stop_reason)?;
self.exec_state = ExecState::Paused;
} else if single_step {
self.exec_state = ExecState::Paused;
let mut res = ResponseWriter::new(&mut self.conn);
res.write_str("S05")?;
res.flush()?;
}
}
}
}
}
}
#[derive(Debug)]
enum StopReason<U> {
WWatch(U),
RWatch(U),
AWatch(U),
SwBreak,
HwBreak,
}