From 4a87fef95a51e999ec40277081fdadb8df8cc230 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Fri, 14 May 2021 15:47:06 +0200 Subject: [PATCH 1/4] Add support for memory maps --- src/gdbstub_impl/ext/base.rs | 28 +++++++++++++++++++++ src/protocol/commands.rs | 1 + src/protocol/commands/_qXfer_memory_map.rs | 29 ++++++++++++++++++++++ src/target/ext/memory_map.rs | 14 +++++++++++ src/target/ext/mod.rs | 1 + src/target/mod.rs | 6 +++++ 6 files changed, 79 insertions(+) create mode 100644 src/protocol/commands/_qXfer_memory_map.rs create mode 100644 src/target/ext/memory_map.rs diff --git a/src/gdbstub_impl/ext/base.rs b/src/gdbstub_impl/ext/base.rs index 11987783..047a109b 100644 --- a/src/gdbstub_impl/ext/base.rs +++ b/src/gdbstub_impl/ext/base.rs @@ -107,6 +107,10 @@ impl GdbStubImpl { res.write_str(";qXfer:features:read+")?; } + if target.memory_map().is_some() { + res.write_str(";qXfer:memory-map:read+")?; + } + HandlerStatus::Handled } Base::QStartNoAckMode(_) => { @@ -143,6 +147,30 @@ impl GdbStubImpl { } HandlerStatus::Handled } + Base::qXferMemoryMapRead(cmd) => { + match target.memory_map().map(|ops| ops.memory_map_xml()) { + Some(xml) => { + let xml = xml.trim(); + if cmd.offset >= xml.len() { + // no more data + res.write_str("l")?; + } else if cmd.offset + cmd.len >= xml.len() { + // last little bit of data + res.write_str("l")?; + res.write_binary(&xml.as_bytes()[cmd.offset..])? + } else { + // still more data + res.write_str("m")?; + res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])? + } + } + None => return Err(Error::PacketUnexpected), + } + // If the target hasn't provided a memory map, then the initial response to + // "qSupported" wouldn't have included "qXfer:features:memory-map", and gdb wouldn't + // send this packet unless it was explicitly marked as supported. + HandlerStatus::Handled + } // -------------------- "Core" Functionality -------------------- // // TODO: Improve the '?' response based on last-sent stop reason. diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index 1fd18103..06f2010a 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -177,6 +177,7 @@ commands! { "qsThreadInfo" => _qsThreadInfo::qsThreadInfo, "qSupported" => _qSupported::qSupported<'a>, "qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead, + "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead, "s" => _s::s<'a>, "T" => _t_upcase::T, "vCont" => _vCont::vCont<'a>, diff --git a/src/protocol/commands/_qXfer_memory_map.rs b/src/protocol/commands/_qXfer_memory_map.rs new file mode 100644 index 00000000..e522de09 --- /dev/null +++ b/src/protocol/commands/_qXfer_memory_map.rs @@ -0,0 +1,29 @@ +use super::prelude::*; + +#[derive(Debug)] +pub struct qXferMemoryMapRead { + pub offset: usize, + pub len: usize, +} + +impl<'a> ParseCommand<'a> for qXferMemoryMapRead { + fn from_packet(buf: PacketBuf<'a>) -> Option { + let body = buf.into_body(); + + if body.is_empty() { + return None; + } + + let mut body = body.split(|b| *b == b':').skip(1); + let annex = body.next()?; + if annex != b"" { + return None; + } + + let mut body = body.next()?.split(|b| *b == b','); + let offset = decode_hex(body.next()?).ok()?; + let len = decode_hex(body.next()?).ok()?; + + Some(qXferMemoryMapRead { offset, len }) + } +} diff --git a/src/target/ext/memory_map.rs b/src/target/ext/memory_map.rs new file mode 100644 index 00000000..95547e2e --- /dev/null +++ b/src/target/ext/memory_map.rs @@ -0,0 +1,14 @@ +//! Provide a memory map for the target. +use crate::target::Target; + +/// Target Extension - Provide a target memory map. +pub trait MemoryMap: Target { + /// Return the target memory map XML file. + /// + /// See the [GDB Documentation] for a description of the format. + /// + /// [GDB Documentation]: https://sourceware.org/gdb/onlinedocs/gdb/Memory-Map-Format.html + fn memory_map_xml(&self) -> String; +} + +define_ext!(MemoryMapOps, MemoryMap); diff --git a/src/target/ext/mod.rs b/src/target/ext/mod.rs index 6274f357..cf6b0028 100644 --- a/src/target/ext/mod.rs +++ b/src/target/ext/mod.rs @@ -256,6 +256,7 @@ macro_rules! define_ext { pub mod base; pub mod breakpoints; pub mod extended_mode; +pub mod memory_map; pub mod monitor_cmd; pub mod section_offsets; pub mod target_description_xml_override; diff --git a/src/target/mod.rs b/src/target/mod.rs index 594e80a3..de5956db 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -239,6 +239,12 @@ pub trait Target { ) -> Option> { None } + + /// Prove a target memory map. + #[inline(always)] + fn memory_map(&mut self) -> Option> { + None + } } macro_rules! impl_dyn_target { From c6ccba6ed2bc04bb9fffdf2a1343f87c58b70ce3 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Fri, 14 May 2021 15:51:19 +0200 Subject: [PATCH 2/4] Fix typo --- src/target/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target/mod.rs b/src/target/mod.rs index de5956db..983df9dc 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -240,7 +240,7 @@ pub trait Target { None } - /// Prove a target memory map. + /// Provide a target memory map. #[inline(always)] fn memory_map(&mut self) -> Option> { None From 2e30a462d296c52f190a666bc1e9d7b13299650f Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Fri, 14 May 2021 18:52:39 +0200 Subject: [PATCH 3/4] Separate command group for memory map command --- src/gdbstub_impl/ext/base.rs | 24 ------------------ src/gdbstub_impl/ext/memory_map.rs | 40 ++++++++++++++++++++++++++++++ src/gdbstub_impl/ext/mod.rs | 1 + src/gdbstub_impl/mod.rs | 1 + src/protocol/commands.rs | 5 +++- src/target/ext/memory_map.rs | 2 +- 6 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 src/gdbstub_impl/ext/memory_map.rs diff --git a/src/gdbstub_impl/ext/base.rs b/src/gdbstub_impl/ext/base.rs index 047a109b..8082ce04 100644 --- a/src/gdbstub_impl/ext/base.rs +++ b/src/gdbstub_impl/ext/base.rs @@ -147,30 +147,6 @@ impl GdbStubImpl { } HandlerStatus::Handled } - Base::qXferMemoryMapRead(cmd) => { - match target.memory_map().map(|ops| ops.memory_map_xml()) { - Some(xml) => { - let xml = xml.trim(); - if cmd.offset >= xml.len() { - // no more data - res.write_str("l")?; - } else if cmd.offset + cmd.len >= xml.len() { - // last little bit of data - res.write_str("l")?; - res.write_binary(&xml.as_bytes()[cmd.offset..])? - } else { - // still more data - res.write_str("m")?; - res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])? - } - } - None => return Err(Error::PacketUnexpected), - } - // If the target hasn't provided a memory map, then the initial response to - // "qSupported" wouldn't have included "qXfer:features:memory-map", and gdb wouldn't - // send this packet unless it was explicitly marked as supported. - HandlerStatus::Handled - } // -------------------- "Core" Functionality -------------------- // // TODO: Improve the '?' response based on last-sent stop reason. diff --git a/src/gdbstub_impl/ext/memory_map.rs b/src/gdbstub_impl/ext/memory_map.rs new file mode 100644 index 00000000..ecaf1be7 --- /dev/null +++ b/src/gdbstub_impl/ext/memory_map.rs @@ -0,0 +1,40 @@ +use super::prelude::*; +use crate::protocol::commands::ext::MemoryMap; + +impl GdbStubImpl { + pub(crate) fn handle_memory_map( + &mut self, + res: &mut ResponseWriter, + target: &mut T, + command: MemoryMap, + ) -> Result> { + let ops = match target.memory_map() { + Some(ops) => ops, + None => return Ok(HandlerStatus::Handled), + }; + + crate::__dead_code_marker!("memory_map", "impl"); + + let handler_status = match command { + MemoryMap::qXferMemoryMapRead(cmd) => { + let xml = ops.memory_map_xml().trim(); + if cmd.offset >= xml.len() { + // no more data + res.write_str("l")?; + } else if cmd.offset + cmd.len >= xml.len() { + // last little bit of data + res.write_str("l")?; + res.write_binary(&xml.as_bytes()[cmd.offset..])? + } else { + // still more data + res.write_str("m")?; + res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])? + } + + HandlerStatus::Handled + } + }; + + Ok(handler_status) + } +} diff --git a/src/gdbstub_impl/ext/mod.rs b/src/gdbstub_impl/ext/mod.rs index 58a99df8..5e5db5a2 100644 --- a/src/gdbstub_impl/ext/mod.rs +++ b/src/gdbstub_impl/ext/mod.rs @@ -14,6 +14,7 @@ mod prelude { mod base; mod breakpoints; mod extended_mode; +mod memory_map; mod monitor_cmd; mod reverse_exec; mod section_offsets; diff --git a/src/gdbstub_impl/mod.rs b/src/gdbstub_impl/mod.rs index a8a196e2..de3f0ece 100644 --- a/src/gdbstub_impl/mod.rs +++ b/src/gdbstub_impl/mod.rs @@ -225,6 +225,7 @@ impl GdbStubImpl { Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd), Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd), Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd), + Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd), } } } diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index 06f2010a..782af144 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -177,7 +177,6 @@ commands! { "qsThreadInfo" => _qsThreadInfo::qsThreadInfo, "qSupported" => _qSupported::qSupported<'a>, "qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead, - "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead, "s" => _s::s<'a>, "T" => _t_upcase::T, "vCont" => _vCont::vCont<'a>, @@ -217,4 +216,8 @@ commands! { reverse_step { "bs" => _bs::bs, } + + memory_map { + "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead, + } } diff --git a/src/target/ext/memory_map.rs b/src/target/ext/memory_map.rs index 95547e2e..59952f39 100644 --- a/src/target/ext/memory_map.rs +++ b/src/target/ext/memory_map.rs @@ -8,7 +8,7 @@ pub trait MemoryMap: Target { /// See the [GDB Documentation] for a description of the format. /// /// [GDB Documentation]: https://sourceware.org/gdb/onlinedocs/gdb/Memory-Map-Format.html - fn memory_map_xml(&self) -> String; + fn memory_map_xml(&self) -> &str; } define_ext!(MemoryMapOps, MemoryMap); From eb3e47ab6a4e47394be2d4b5f9f5d784f5f2b78d Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Fri, 14 May 2021 18:53:01 +0200 Subject: [PATCH 4/4] Add memory map implementation to armv4t example --- examples/armv4t/gdb/memory_map.rs | 17 +++++++++++++++++ examples/armv4t/gdb/mod.rs | 6 ++++++ 2 files changed, 23 insertions(+) create mode 100644 examples/armv4t/gdb/memory_map.rs diff --git a/examples/armv4t/gdb/memory_map.rs b/examples/armv4t/gdb/memory_map.rs new file mode 100644 index 00000000..a9e29a9e --- /dev/null +++ b/examples/armv4t/gdb/memory_map.rs @@ -0,0 +1,17 @@ +use gdbstub::target; + +use crate::emu::Emu; + +impl target::ext::memory_map::MemoryMap for Emu { + fn memory_map_xml(&self) -> &str { + // Sample memory map, with RAM coverying the whole + // memory space. + r#" + + + +"# + } +} diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index bb9a0e79..685beb7f 100644 --- a/examples/armv4t/gdb/mod.rs +++ b/examples/armv4t/gdb/mod.rs @@ -16,6 +16,7 @@ use crate::emu::{Emu, Event}; mod breakpoints; mod extended_mode; +mod memory_map; mod monitor_cmd; mod section_offsets; mod target_description_xml_override; @@ -73,6 +74,11 @@ impl Target for Emu { { Some(self) } + + #[inline(always)] + fn memory_map(&mut self) -> Option> { + Some(self) + } } impl Emu {