From 1edd8f12afcd485e280a1e004e43be1fdb769610 Mon Sep 17 00:00:00 2001 From: Jakub Jendryka Date: Sun, 23 Aug 2020 19:33:38 +0200 Subject: [PATCH 1/2] Add package encoding and sending --- src/target.rs | 3 ++ src/target/gdb_remote.rs | 88 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/target/gdb_remote.rs diff --git a/src/target.rs b/src/target.rs index 11acc9a..977ee0d 100644 --- a/src/target.rs +++ b/src/target.rs @@ -18,6 +18,9 @@ mod windows; #[cfg(target_os = "windows")] pub use windows::*; +mod gdb_remote; +pub use gdb_remote::*; + mod thread; #[derive(Debug)] diff --git a/src/target/gdb_remote.rs b/src/target/gdb_remote.rs new file mode 100644 index 0000000..2fc8a65 --- /dev/null +++ b/src/target/gdb_remote.rs @@ -0,0 +1,88 @@ +use std::io::{Read, Write}; + +pub struct GDBRemoteTarget { + stream: S +} + +impl GDBRemoteTarget { + pub fn connect_tcp(addr: A) -> Result> { + let stream = std::net::TcpStream::connect(addr)?; + Ok(Self { stream }) + } +} + +impl GDBRemoteTarget { + + fn send_packet(&mut self, data: &[u8]) -> Result<(), Box> { + let mut buffer = [0u8; 1024]; + let len = encode_packet(data, &mut buffer); + + self.stream.write(&buffer[0..len])?; + self.stream.flush()?; + Ok(()) + } +} + +fn encode_packet(data: &[u8], buffer: &mut [u8]) -> usize { + + // Special character + buffer[0] = '$' as u8; + let mut buffer_index = 1; + + // Escaping characters + for val in data { + if *val == '$' as u8 || *val == '#' as u8 || *val == '}' as u8 { + buffer[buffer_index] = '}' as u8; + buffer[buffer_index + 1] = val ^ 0x20; + buffer_index += 2; + } + else { + buffer[buffer_index] = *val; + buffer_index += 1; + } + } + + // Special character + buffer[buffer_index] = '#' as u8; + buffer_index += 1; + + // Calculating checksum + let remainder: u64 = data.iter().fold(0, |acc, x| { + let res = acc + (*x as u64); + res % 256 + }); + + // Inserting checksum + let checksum = format!("{:x}", remainder); + for ch in checksum.chars(){ + buffer[buffer_index] = ch as u8; + buffer_index += 1; + } + + buffer_index +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encode_packet_test() { + let expected = "$OK#9a"; + let mut buffer = [0u8; 6]; + let data = ['O' as u8, 'K' as u8]; + let len = encode_packet(&data, &mut buffer); + assert_eq!(6, len); + assert_eq!(expected, std::str::from_utf8(&buffer).unwrap()); + } + + #[test] + fn encode_packet_escape_test() { + let expected = "$}]#7d"; + let mut buffer = [0u8; 6]; + let data = ['}' as u8]; + let len = encode_packet(&data, &mut buffer); + assert_eq!(6, len); + assert_eq!(expected, std::str::from_utf8(&buffer).unwrap()); + } +} \ No newline at end of file From 30006ed22f569a6100b8ee70d5352a80d9fcdfe1 Mon Sep 17 00:00:00 2001 From: Jakub Jendryka Date: Mon, 24 Aug 2020 00:56:31 +0200 Subject: [PATCH 2/2] Added waiting for ack --- src/target/gdb_remote.rs | 204 ++++++++++++++++++++++++++++----------- 1 file changed, 149 insertions(+), 55 deletions(-) diff --git a/src/target/gdb_remote.rs b/src/target/gdb_remote.rs index 2fc8a65..ba87ff9 100644 --- a/src/target/gdb_remote.rs +++ b/src/target/gdb_remote.rs @@ -1,65 +1,162 @@ use std::io::{Read, Write}; pub struct GDBRemoteTarget { - stream: S + stream: S, + timeout: Option, } impl GDBRemoteTarget { - pub fn connect_tcp(addr: A) -> Result> { + pub fn connect_tcp( + addr: A, + timeout: std::time::Duration, + ) -> Result> { let stream = std::net::TcpStream::connect(addr)?; - Ok(Self { stream }) + stream.set_read_timeout(Some(timeout))?; + stream.set_write_timeout(Some(timeout))?; + Ok(Self { + stream, + timeout: None, + }) } } +#[derive(Debug, Clone)] +pub enum GDBRemoteTargetError { + InvalidCharacterReceived(char), + AckFailure(), + InvalidNumOfCharsRead(), +} + +impl std::fmt::Display for GDBRemoteTargetError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let string = match self { + GDBRemoteTargetError::InvalidCharacterReceived(ch) => { + format!("Invalid character received: {}", ch) + } + GDBRemoteTargetError::AckFailure() => { + "Could not send data properly or ack was not received".to_string() + } + GDBRemoteTargetError::InvalidNumOfCharsRead() => { + "Read invalid number of chars".to_string() + } + }; + write!(f, "{}", string) + } +} + +impl std::error::Error for GDBRemoteTargetError {} + impl GDBRemoteTarget { + pub fn send(&mut self, mut package: PacketWriter) -> Result<(), Box> { + // FIXME: Move tries_left into struct + let mut tries_left = 5; - fn send_packet(&mut self, data: &[u8]) -> Result<(), Box> { - let mut buffer = [0u8; 1024]; - let len = encode_packet(data, &mut buffer); + while tries_left > 0 { - self.stream.write(&buffer[0..len])?; - self.stream.flush()?; - Ok(()) + package.send(&mut self.stream)?; + + let mut buf = [0u8; 1]; + match self.stream.read(&mut buf) { + Ok(1) => match buf[0] as char { + // Everything ok, received ack + '+' => { + return Ok(()); + } + // Error occured during transmission, resending packet + '-' => { + package.send(&mut self.stream)?; + } + _ => { + return Err(Box::new(GDBRemoteTargetError::InvalidCharacterReceived( + buf[0] as char, + ))); + } + }, + // Received incorrect number of bytes + Ok(_) => { + return Err(Box::new(GDBRemoteTargetError::InvalidNumOfCharsRead())); + } + Err(err) => { + if let std::io::ErrorKind::TimedOut = err.kind() { + } else { + return Err(Box::new(err)); + } + } + } + + // Waiting before trying to read again + if let Some(timeout) = self.timeout { + std::thread::sleep(timeout); + } + tries_left -= 1; + } + + // Tries run out + Err(Box::new(GDBRemoteTargetError::AckFailure())) } } -fn encode_packet(data: &[u8], buffer: &mut [u8]) -> usize { - - // Special character - buffer[0] = '$' as u8; - let mut buffer_index = 1; +pub struct PacketWriter { + buffer: [u8; 1024], + pointer: usize, + checksum: u8, +} - // Escaping characters - for val in data { - if *val == '$' as u8 || *val == '#' as u8 || *val == '}' as u8 { - buffer[buffer_index] = '}' as u8; - buffer[buffer_index + 1] = val ^ 0x20; - buffer_index += 2; +impl PacketWriter { + pub fn new() -> Self { + let mut buffer = [0u8; 1024]; + buffer[0] = '$' as u8; + PacketWriter { + buffer, + pointer: 1, + checksum: 0, } - else { - buffer[buffer_index] = *val; - buffer_index += 1; + } + + fn write_raw(&mut self, byte: u8) { + self.buffer[self.pointer] = byte; + self.pointer += 1; + } + + pub fn write_byte(mut self, byte: u8) -> Self { + if byte == '$' as u8 || byte == '#' as u8 || byte == '}' as u8 { + self.write_raw('}' as u8); + self.write_raw(byte ^ 0x20); + self.checksum = self.checksum.wrapping_add('}' as u8); + self.checksum = self.checksum.wrapping_add(byte ^ 0x20); + } else { + self.write_raw(byte); + self.checksum = self.checksum.wrapping_add(byte); } + self + } + + pub fn write_slice(self, data: &[u8]) -> Self { + data.iter() + .fold(self, |packet_writer, byte| packet_writer.write_byte(*byte)) } - // Special character - buffer[buffer_index] = '#' as u8; - buffer_index += 1; - - // Calculating checksum - let remainder: u64 = data.iter().fold(0, |acc, x| { - let res = acc + (*x as u64); - res % 256 - }); - - // Inserting checksum - let checksum = format!("{:x}", remainder); - for ch in checksum.chars(){ - buffer[buffer_index] = ch as u8; - buffer_index += 1; + pub fn write_str(self, s: &str) -> Self { + self.write_slice(s.as_bytes()) } - buffer_index + fn add_checksum(&mut self) { + self.write_raw('#' as u8); + let checksum_str = format!("{:02x}", self.checksum); + for ch in checksum_str.chars() { + self.write_raw(ch as u8); + }; + } + + pub fn send( + &mut self, + writer: &mut W, + ) -> Result<(), Box> { + self.add_checksum(); + writer.write(&self.buffer[0..self.pointer])?; + writer.flush()?; + Ok(()) + } } #[cfg(test)] @@ -68,21 +165,18 @@ mod tests { #[test] fn encode_packet_test() { - let expected = "$OK#9a"; - let mut buffer = [0u8; 6]; - let data = ['O' as u8, 'K' as u8]; - let len = encode_packet(&data, &mut buffer); - assert_eq!(6, len); - assert_eq!(expected, std::str::from_utf8(&buffer).unwrap()); - } + let mut packet_writer = PacketWriter::new().write_str("OK"); + packet_writer.add_checksum(); + assert_eq!( + "$OK#9a", + std::str::from_utf8(&packet_writer.buffer[0..packet_writer.pointer]).unwrap() + ); - #[test] - fn encode_packet_escape_test() { - let expected = "$}]#7d"; - let mut buffer = [0u8; 6]; - let data = ['}' as u8]; - let len = encode_packet(&data, &mut buffer); - assert_eq!(6, len); - assert_eq!(expected, std::str::from_utf8(&buffer).unwrap()); + let mut packet_writer = PacketWriter::new().write_str("}"); + packet_writer.add_checksum(); + assert_eq!( + "$}]#da", + std::str::from_utf8(&packet_writer.buffer[0..packet_writer.pointer]).unwrap() + ); } -} \ No newline at end of file +}