From 8f0aa74e561352ed98b6dc650dfb82201c441f67 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Sun, 22 Aug 2021 22:57:43 +0800 Subject: [PATCH 1/8] Support read exec-file --- README.md | 1 + examples/armv4t/gdb/exec_file.rs | 10 ++++ examples/armv4t/gdb/host_io.rs | 57 +++++++++++++++++++++-- examples/armv4t/gdb/mod.rs | 6 +++ examples/armv4t/main.rs | 2 +- examples/armv4t/test_bin/.gdbinit | 1 - src/gdbstub_impl/ext/base.rs | 4 ++ src/gdbstub_impl/ext/exec_file.rs | 28 +++++++++++ src/gdbstub_impl/ext/mod.rs | 1 + src/gdbstub_impl/mod.rs | 1 + src/protocol/commands.rs | 4 ++ src/protocol/commands/_qXfer_exec_file.rs | 29 ++++++++++++ src/protocol/commands/_vFile_setfs.rs | 2 +- src/target/ext/exec_file.rs | 13 ++++++ src/target/ext/mod.rs | 1 + src/target/mod.rs | 10 ++++ 16 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 examples/armv4t/gdb/exec_file.rs create mode 100644 src/gdbstub_impl/ext/exec_file.rs create mode 100644 src/protocol/commands/_qXfer_exec_file.rs create mode 100644 src/target/ext/exec_file.rs diff --git a/README.md b/README.md index f3858654..9fcec129 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Of course, most use-cases will want to support additional debugging features as - Extend the GDB protocol with custom debug commands using GDB's `monitor` command - Get target memory map - Perform Host I/O operations +- Get target exec file _Note:_ GDB features are implemented on an as-needed basis by `gdbstub`'s contributors. If there's a missing GDB feature that you'd like `gdbstub` to implement, please file an issue and/or open a PR! diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs new file mode 100644 index 00000000..a21a90c3 --- /dev/null +++ b/examples/armv4t/gdb/exec_file.rs @@ -0,0 +1,10 @@ +use gdbstub::common::Pid; +use gdbstub::target; + +use crate::emu::Emu; + +impl target::ext::exec_file::ExecFile for Emu { + fn get_exec_file(&self, _pid: Option) -> &[u8] { + b"/test.elf" + } +} diff --git a/examples/armv4t/gdb/host_io.rs b/examples/armv4t/gdb/host_io.rs index e4844ab7..8d471914 100644 --- a/examples/armv4t/gdb/host_io.rs +++ b/examples/armv4t/gdb/host_io.rs @@ -1,5 +1,6 @@ use std::io::{Read, Seek, Write}; +use crate::TEST_PROGRAM_ELF; use gdbstub::target; use gdbstub::target::ext::host_io::{ FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoOutput, HostIoResult, @@ -8,6 +9,8 @@ use gdbstub::target::ext::host_io::{ use crate::emu::Emu; +const FD_RESERVED: u32 = 1; + impl target::ext::host_io::HostIo for Emu { #[inline(always)] fn enable_open(&mut self) -> Option> { @@ -61,6 +64,10 @@ impl target::ext::host_io::HostIoOpen for Emu { return Err(HostIoError::Errno(HostIoErrno::ENOENT)); } + if filename == b"/test.elf" { + return Ok(0); + } + let path = std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; @@ -95,13 +102,17 @@ impl target::ext::host_io::HostIoOpen for Emu { } }; - Ok(n as u32) + Ok(n as u32 + FD_RESERVED) } } impl target::ext::host_io::HostIoClose for Emu { fn close(&mut self, fd: u32) -> HostIoResult<(), Self> { - let file = match self.files.get_mut(fd as usize) { + if fd < FD_RESERVED { + return Ok(()); + } + + let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { Some(file) => file, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; @@ -122,7 +133,18 @@ impl target::ext::host_io::HostIoPread for Emu { offset: u32, output: HostIoOutput<'a>, ) -> HostIoResult, Self> { - let file = match self.files.get_mut(fd as usize) { + if fd < FD_RESERVED { + if fd == 0 { + let len = TEST_PROGRAM_ELF.len(); + return Ok(output.write( + &TEST_PROGRAM_ELF[len.min(offset as usize)..len.min((offset + count) as usize)], + )); + } else { + return Err(HostIoError::Errno(HostIoErrno::EBADF)); + } + } + + let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { Some(Some(file)) => file, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; @@ -136,7 +158,11 @@ impl target::ext::host_io::HostIoPread for Emu { impl target::ext::host_io::HostIoPwrite for Emu { fn pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult { - let file = match self.files.get_mut(fd as usize) { + if fd < FD_RESERVED { + return Err(HostIoError::Errno(HostIoErrno::EACCES)); + } + + let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { Some(Some(file)) => file, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; @@ -149,7 +175,28 @@ impl target::ext::host_io::HostIoPwrite for Emu { impl target::ext::host_io::HostIoFstat for Emu { fn fstat(&mut self, fd: u32) -> HostIoResult { - let metadata = match self.files.get(fd as usize) { + if fd < FD_RESERVED { + if fd == 0 { + return Ok(HostIoStat { + st_dev: 0, + st_ino: 0, + st_mode: HostIoOpenMode::empty(), + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_size: TEST_PROGRAM_ELF.len() as u64, + st_blksize: 0, + st_blocks: 0, + st_atime: 0, + st_mtime: 0, + st_ctime: 0, + }); + } else { + return Err(HostIoError::Errno(HostIoErrno::EBADF)); + } + } + let metadata = match self.files.get((fd - FD_RESERVED) as usize) { Some(Some(file)) => file.metadata()?, _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), }; diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index 6b8896a9..89929cc1 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 catch_syscalls; +mod exec_file; mod extended_mode; mod host_io; mod memory_map; @@ -100,6 +101,11 @@ impl Target for Emu { fn host_io(&mut self) -> Option> { Some(self) } + + #[inline(always)] + fn exec_file(&mut self) -> Option> { + Some(self) + } } impl Emu { diff --git a/examples/armv4t/main.rs b/examples/armv4t/main.rs index 1ada5f41..e2e1f242 100644 --- a/examples/armv4t/main.rs +++ b/examples/armv4t/main.rs @@ -9,7 +9,7 @@ use gdbstub::{target::Target, ConnectionExt, DisconnectReason, GdbStub}; pub type DynResult = Result>; -static TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf"); +pub static TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf"); mod emu; mod gdb; diff --git a/examples/armv4t/test_bin/.gdbinit b/examples/armv4t/test_bin/.gdbinit index 6f9d0b88..2d2e812d 100644 --- a/examples/armv4t/test_bin/.gdbinit +++ b/examples/armv4t/test_bin/.gdbinit @@ -1,2 +1 @@ -file test.elf target extended-remote :9001 diff --git a/src/gdbstub_impl/ext/base.rs b/src/gdbstub_impl/ext/base.rs index 58513428..cfbfe678 100644 --- a/src/gdbstub_impl/ext/base.rs +++ b/src/gdbstub_impl/ext/base.rs @@ -115,6 +115,10 @@ impl GdbStubImpl { res.write_str(";qXfer:memory-map:read+")?; } + if target.exec_file().is_some() { + res.write_str(";qXfer:exec-file:read+")?; + } + HandlerStatus::Handled } Base::QStartNoAckMode(_) => { diff --git a/src/gdbstub_impl/ext/exec_file.rs b/src/gdbstub_impl/ext/exec_file.rs new file mode 100644 index 00000000..ff9660e8 --- /dev/null +++ b/src/gdbstub_impl/ext/exec_file.rs @@ -0,0 +1,28 @@ +use super::prelude::*; +use crate::protocol::commands::ext::ExecFile; + +impl GdbStubImpl { + pub(crate) fn handle_exec_file( + &mut self, + res: &mut ResponseWriter, + target: &mut T, + command: ExecFile, + ) -> Result> { + let ops = match target.exec_file() { + Some(ops) => ops, + None => return Ok(HandlerStatus::Handled), + }; + + crate::__dead_code_marker!("exec_file", "impl"); + + let handler_status = match command { + ExecFile::qXferExecFileRead(cmd) => { + let filename = ops.get_exec_file(cmd.pid); + res.write_binary_range(filename, 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 2aae2fdf..88684f85 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 catch_syscalls; +mod exec_file; mod extended_mode; mod host_io; mod memory_map; diff --git a/src/gdbstub_impl/mod.rs b/src/gdbstub_impl/mod.rs index b27390ac..8b7cc6f1 100644 --- a/src/gdbstub_impl/mod.rs +++ b/src/gdbstub_impl/mod.rs @@ -578,6 +578,7 @@ impl GdbStubImpl { Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd), Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd), Command::HostIo(cmd) => self.handle_host_io(res, target, cmd), + Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd), } } } diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index 98538751..146a6ed3 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -225,6 +225,10 @@ commands! { "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead, } + exec_file { + "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead, + } + host_io use 'a { "vFile:open" => _vFile_open::vFileOpen<'a>, "vFile:close" => _vFile_close::vFileClose, diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs new file mode 100644 index 00000000..69044d47 --- /dev/null +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -0,0 +1,29 @@ +use super::prelude::*; + +use crate::common::Pid; + +#[derive(Debug)] +pub struct qXferExecFileRead { + pub pid: Option, + pub offset: usize, + pub len: usize, +} + +impl<'a> ParseCommand<'a> for qXferExecFileRead { + 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 pid = decode_hex(body.next()?).ok().and_then(Pid::new); + + let mut body = body.next()?.split(|b| *b == b','); + let offset = decode_hex(body.next()?).ok()?; + let len = decode_hex(body.next()?).ok()?; + + Some(qXferExecFileRead {pid, offset, len}) + } +} diff --git a/src/protocol/commands/_vFile_setfs.rs b/src/protocol/commands/_vFile_setfs.rs index 331948c5..ff0aab1c 100644 --- a/src/protocol/commands/_vFile_setfs.rs +++ b/src/protocol/commands/_vFile_setfs.rs @@ -16,7 +16,7 @@ impl<'a> ParseCommand<'a> for vFileSetfs { match body { [b':', body @ ..] => { - let fs = match core::num::NonZeroUsize::new(decode_hex(body).ok()?) { + let fs = match crate::common::Pid::new(decode_hex(body).ok()?) { None => FsKind::Stub, Some(pid) => FsKind::Pid(pid), }; diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs new file mode 100644 index 00000000..8f62f7c0 --- /dev/null +++ b/src/target/ext/exec_file.rs @@ -0,0 +1,13 @@ +//! Provide exec-file path for the target. +use crate::target::Target; + +use crate::common::Pid; + +/// Target Extension - Provide current exec-file. +pub trait ExecFile: Target { + /// Return the full absolute name of the file that was executed to create a + /// process running on the remote system. + fn get_exec_file(&self, pid: Option) -> &[u8]; +} + +define_ext!(ExecFileOps, ExecFile); diff --git a/src/target/ext/mod.rs b/src/target/ext/mod.rs index 99f02809..5aae7807 100644 --- a/src/target/ext/mod.rs +++ b/src/target/ext/mod.rs @@ -259,6 +259,7 @@ macro_rules! define_ext { pub mod base; pub mod breakpoints; pub mod catch_syscalls; +pub mod exec_file; pub mod extended_mode; pub mod host_io; pub mod memory_map; diff --git a/src/target/mod.rs b/src/target/mod.rs index 0f262bee..e5b02f61 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -364,6 +364,11 @@ pub trait Target { fn host_io(&mut self) -> Option> { None } + + /// Provide exec-file + fn exec_file(&mut self) -> Option> { + None + } } macro_rules! impl_dyn_target { @@ -395,6 +400,11 @@ macro_rules! impl_dyn_target { (**self).monitor_cmd() } + #[inline(always)] + fn exec_file(&mut self) -> Option> { + (**self).exec_file() + } + #[inline(always)] fn extended_mode(&mut self) -> Option> { (**self).extended_mode() From 01e83cf9b33094532e374362fee70b415df33a52 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Tue, 24 Aug 2021 10:54:47 +0800 Subject: [PATCH 2/8] Some small fixes --- examples/armv4t/gdb/exec_file.rs | 5 +++-- examples/armv4t/gdb/host_io.rs | 6 +++++- src/gdbstub_impl/ext/exec_file.rs | 2 +- src/target/ext/exec_file.rs | 10 ++++++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index a21a90c3..14047276 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -1,10 +1,11 @@ use gdbstub::common::Pid; use gdbstub::target; +use gdbstub::target::TargetResult; use crate::emu::Emu; impl target::ext::exec_file::ExecFile for Emu { - fn get_exec_file(&self, _pid: Option) -> &[u8] { - b"/test.elf" + fn get_exec_file(&self, _pid: Option) -> TargetResult<&[u8], Self> { + Ok(b"/test.elf") } } diff --git a/examples/armv4t/gdb/host_io.rs b/examples/armv4t/gdb/host_io.rs index 8d471914..a3a6e3f1 100644 --- a/examples/armv4t/gdb/host_io.rs +++ b/examples/armv4t/gdb/host_io.rs @@ -1,6 +1,5 @@ use std::io::{Read, Seek, Write}; -use crate::TEST_PROGRAM_ELF; use gdbstub::target; use gdbstub::target::ext::host_io::{ FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoOutput, HostIoResult, @@ -8,6 +7,7 @@ use gdbstub::target::ext::host_io::{ }; use crate::emu::Emu; +use crate::TEST_PROGRAM_ELF; const FD_RESERVED: u32 = 1; @@ -64,6 +64,10 @@ impl target::ext::host_io::HostIoOpen for Emu { return Err(HostIoError::Errno(HostIoErrno::ENOENT)); } + // In this example, the test binary is compiled into the binary itself as the + // `TEST_PROGRAM_ELF` array using `include_bytes!`. As such, we must "spoof" the + // existence of a real file, which will actually be backed by the in-binary + // `TEST_PROGRAM_ELF` array. if filename == b"/test.elf" { return Ok(0); } diff --git a/src/gdbstub_impl/ext/exec_file.rs b/src/gdbstub_impl/ext/exec_file.rs index ff9660e8..f7b35c0f 100644 --- a/src/gdbstub_impl/ext/exec_file.rs +++ b/src/gdbstub_impl/ext/exec_file.rs @@ -17,7 +17,7 @@ impl GdbStubImpl { let handler_status = match command { ExecFile::qXferExecFileRead(cmd) => { - let filename = ops.get_exec_file(cmd.pid); + let filename = ops.get_exec_file(cmd.pid).handle_error()?; res.write_binary_range(filename, cmd.offset, cmd.len)?; HandlerStatus::Handled } diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 8f62f7c0..64c785f2 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -1,13 +1,19 @@ //! Provide exec-file path for the target. -use crate::target::Target; +use crate::target::{Target, TargetResult}; use crate::common::Pid; /// Target Extension - Provide current exec-file. +/// +/// NOTE: this extension is primarily intended to be used alongside the [`Host +/// I/O Extensions`](crate::target::ext::host_io), which enables the GDB client +/// to read the executable file directly from the target pub trait ExecFile: Target { /// Return the full absolute name of the file that was executed to create a /// process running on the remote system. - fn get_exec_file(&self, pid: Option) -> &[u8]; + /// If no `pid` is provided, return the filename corresponding to the + /// currently executing process. + fn get_exec_file(&self, pid: Option) -> TargetResult<&[u8], Self>; } define_ext!(ExecFileOps, ExecFile); From ca15972a5f8bd78c87e6b1bc3afbbeb27ed5f58f Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Thu, 9 Sep 2021 15:52:19 +0800 Subject: [PATCH 3/8] Try to pass buf as an argument of get_exec_file --- examples/armv4t/gdb/exec_file.rs | 16 +++++++++++++-- src/gdbstub_impl/ext/exec_file.rs | 17 +++++++++++++-- src/protocol/commands.rs | 4 ++-- src/protocol/commands/_qXfer_exec_file.rs | 25 ++++++++++++++--------- src/target/ext/exec_file.rs | 9 +++++++- 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index 14047276..05050816 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -5,7 +5,19 @@ use gdbstub::target::TargetResult; use crate::emu::Emu; impl target::ext::exec_file::ExecFile for Emu { - fn get_exec_file(&self, _pid: Option) -> TargetResult<&[u8], Self> { - Ok(b"/test.elf") + fn get_exec_file<'a>( + &self, + _pid: Option, + offset: u32, + length: u32, + buf: &'a mut [u8], + ) -> TargetResult<&'a [u8], Self> { + let filename = b"/test.elf"; + let len = filename.len(); + let data = + &filename[(offset as usize).min(len) as usize..((offset + length) as usize).min(len)]; + let buf = &mut buf[..data.len()]; + buf.copy_from_slice(data); + Ok(buf) } } diff --git a/src/gdbstub_impl/ext/exec_file.rs b/src/gdbstub_impl/ext/exec_file.rs index f7b35c0f..cea75257 100644 --- a/src/gdbstub_impl/ext/exec_file.rs +++ b/src/gdbstub_impl/ext/exec_file.rs @@ -1,6 +1,8 @@ use super::prelude::*; use crate::protocol::commands::ext::ExecFile; +use crate::arch::Arch; + impl GdbStubImpl { pub(crate) fn handle_exec_file( &mut self, @@ -17,8 +19,19 @@ impl GdbStubImpl { let handler_status = match command { ExecFile::qXferExecFileRead(cmd) => { - let filename = ops.get_exec_file(cmd.pid).handle_error()?; - res.write_binary_range(filename, cmd.offset, cmd.len)?; + let offset = ::Usize::from_be_bytes(cmd.offset) + .ok_or(Error::TargetMismatch)?; + let length = ::Usize::from_be_bytes(cmd.length) + .ok_or(Error::TargetMismatch)?; + let ret = ops + .get_exec_file(cmd.pid, offset, length, cmd.buf) + .handle_error()?; + if ret.is_empty() { + res.write_str("l")?; + } else { + res.write_str("m")?; + res.write_binary(ret)?; + } HandlerStatus::Handled } }; diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index 146a6ed3..84eb0735 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -225,8 +225,8 @@ commands! { "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead, } - exec_file { - "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead, + exec_file use 'a{ + "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead<'a>, } host_io use 'a { diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs index 69044d47..742c7443 100644 --- a/src/protocol/commands/_qXfer_exec_file.rs +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -3,27 +3,32 @@ use super::prelude::*; use crate::common::Pid; #[derive(Debug)] -pub struct qXferExecFileRead { +pub struct qXferExecFileRead<'a> { pub pid: Option, - pub offset: usize, - pub len: usize, + pub offset: &'a [u8], + pub length: &'a [u8], + + pub buf: &'a mut [u8], } -impl<'a> ParseCommand<'a> for qXferExecFileRead { +impl<'a> ParseCommand<'a> for qXferExecFileRead<'a> { fn from_packet(buf: PacketBuf<'a>) -> Option { - let body = buf.into_body(); + let (buf, body_range) = buf.into_raw_buf(); + let (body, buf) = buf[body_range.start..].split_at_mut(body_range.end - body_range.start); if body.is_empty() { return None; } - let mut body = body.split(|b| *b == b':').skip(1); + let mut body = body.split_mut_no_panic(|b| *b == b':').skip(1); let pid = decode_hex(body.next()?).ok().and_then(Pid::new); - let mut body = body.next()?.split(|b| *b == b','); - let offset = decode_hex(body.next()?).ok()?; - let len = decode_hex(body.next()?).ok()?; + let mut body = body.next()?.split_mut_no_panic(|b| *b == b','); + let offset = decode_hex_buf(body.next()?).ok()?; + let length = decode_hex_buf(body.next()?).ok()?; + + drop(body); - Some(qXferExecFileRead {pid, offset, len}) + Some(qXferExecFileRead {pid, offset, length, buf}) } } diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 64c785f2..64014f8e 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -1,6 +1,7 @@ //! Provide exec-file path for the target. use crate::target::{Target, TargetResult}; +use crate::arch::Arch; use crate::common::Pid; /// Target Extension - Provide current exec-file. @@ -13,7 +14,13 @@ pub trait ExecFile: Target { /// process running on the remote system. /// If no `pid` is provided, return the filename corresponding to the /// currently executing process. - fn get_exec_file(&self, pid: Option) -> TargetResult<&[u8], Self>; + fn get_exec_file<'a>( + &self, + pid: Option, + offset: ::Usize, + length: ::Usize, + buf: &'a mut [u8], + ) -> TargetResult<&'a [u8], Self>; } define_ext!(ExecFileOps, ExecFile); From 3b37ee9c9cbf92744694d37065943c41d6c369e2 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Fri, 10 Sep 2021 16:39:56 +0800 Subject: [PATCH 4/8] Set type of offset and length to usize --- examples/armv4t/gdb/exec_file.rs | 12 ++++++------ src/gdbstub_impl/ext/exec_file.rs | 12 +++--------- src/protocol/commands/_qXfer_exec_file.rs | 14 +++++++------- src/target/ext/exec_file.rs | 20 ++++++++++---------- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index 05050816..0d8e3454 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -5,19 +5,19 @@ use gdbstub::target::TargetResult; use crate::emu::Emu; impl target::ext::exec_file::ExecFile for Emu { - fn get_exec_file<'a>( + fn get_exec_file( &self, _pid: Option, - offset: u32, - length: u32, - buf: &'a mut [u8], - ) -> TargetResult<&'a [u8], Self> { + offset: usize, + length: usize, + buf: &mut [u8], + ) -> TargetResult { let filename = b"/test.elf"; let len = filename.len(); let data = &filename[(offset as usize).min(len) as usize..((offset + length) as usize).min(len)]; let buf = &mut buf[..data.len()]; buf.copy_from_slice(data); - Ok(buf) + Ok(data.len()) } } diff --git a/src/gdbstub_impl/ext/exec_file.rs b/src/gdbstub_impl/ext/exec_file.rs index cea75257..50a5b029 100644 --- a/src/gdbstub_impl/ext/exec_file.rs +++ b/src/gdbstub_impl/ext/exec_file.rs @@ -1,8 +1,6 @@ use super::prelude::*; use crate::protocol::commands::ext::ExecFile; -use crate::arch::Arch; - impl GdbStubImpl { pub(crate) fn handle_exec_file( &mut self, @@ -19,18 +17,14 @@ impl GdbStubImpl { let handler_status = match command { ExecFile::qXferExecFileRead(cmd) => { - let offset = ::Usize::from_be_bytes(cmd.offset) - .ok_or(Error::TargetMismatch)?; - let length = ::Usize::from_be_bytes(cmd.length) - .ok_or(Error::TargetMismatch)?; let ret = ops - .get_exec_file(cmd.pid, offset, length, cmd.buf) + .get_exec_file(cmd.pid, cmd.offset, cmd.length, cmd.buf) .handle_error()?; - if ret.is_empty() { + if ret == 0 { res.write_str("l")?; } else { res.write_str("m")?; - res.write_binary(ret)?; + res.write_binary(&cmd.buf[..ret])?; } HandlerStatus::Handled } diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs index 742c7443..31277a59 100644 --- a/src/protocol/commands/_qXfer_exec_file.rs +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -5,8 +5,8 @@ use crate::common::Pid; #[derive(Debug)] pub struct qXferExecFileRead<'a> { pub pid: Option, - pub offset: &'a [u8], - pub length: &'a [u8], + pub offset: usize, + pub length: usize, pub buf: &'a mut [u8], } @@ -14,18 +14,18 @@ pub struct qXferExecFileRead<'a> { impl<'a> ParseCommand<'a> for qXferExecFileRead<'a> { fn from_packet(buf: PacketBuf<'a>) -> Option { let (buf, body_range) = buf.into_raw_buf(); - let (body, buf) = buf[body_range.start..].split_at_mut(body_range.end - body_range.start); + let body = buf.get_mut(body_range.start..body_range.end)?; if body.is_empty() { return None; } - let mut body = body.split_mut_no_panic(|b| *b == b':').skip(1); + let mut body = body.split(|b| *b == b':').skip(1); let pid = decode_hex(body.next()?).ok().and_then(Pid::new); - let mut body = body.next()?.split_mut_no_panic(|b| *b == b','); - let offset = decode_hex_buf(body.next()?).ok()?; - let length = decode_hex_buf(body.next()?).ok()?; + let mut body = body.next()?.split(|b| *b == b','); + let offset = decode_hex(body.next()?).ok()?; + let length = decode_hex(body.next()?).ok()?; drop(body); diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 64014f8e..55422cdd 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -1,7 +1,6 @@ //! Provide exec-file path for the target. use crate::target::{Target, TargetResult}; -use crate::arch::Arch; use crate::common::Pid; /// Target Extension - Provide current exec-file. @@ -10,17 +9,18 @@ use crate::common::Pid; /// I/O Extensions`](crate::target::ext::host_io), which enables the GDB client /// to read the executable file directly from the target pub trait ExecFile: Target { - /// Return the full absolute name of the file that was executed to create a - /// process running on the remote system. - /// If no `pid` is provided, return the filename corresponding to the - /// currently executing process. - fn get_exec_file<'a>( + /// Get full absolute name of the file that was executed to create + /// process `pid` running on the remote system, or the filename + /// corresponding to the currently executing process if no `pid` is + /// provided. + /// Store the name into `buf`, and return the length of name. + fn get_exec_file( &self, pid: Option, - offset: ::Usize, - length: ::Usize, - buf: &'a mut [u8], - ) -> TargetResult<&'a [u8], Self>; + offset: usize, + length: usize, + buf: &mut [u8], + ) -> TargetResult; } define_ext!(ExecFileOps, ExecFile); From 3949c68b8b96652cf0fec9501121c478de6825c7 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Sat, 11 Sep 2021 10:05:06 +0800 Subject: [PATCH 5/8] Check return size --- src/gdbstub_impl/ext/exec_file.rs | 3 ++- src/protocol/commands.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gdbstub_impl/ext/exec_file.rs b/src/gdbstub_impl/ext/exec_file.rs index 50a5b029..c7985c6b 100644 --- a/src/gdbstub_impl/ext/exec_file.rs +++ b/src/gdbstub_impl/ext/exec_file.rs @@ -24,7 +24,8 @@ impl GdbStubImpl { res.write_str("l")?; } else { res.write_str("m")?; - res.write_binary(&cmd.buf[..ret])?; + // TODO: add more specific error variant? + res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?; } HandlerStatus::Handled } diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index 84eb0735..b171ea6e 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -225,7 +225,7 @@ commands! { "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead, } - exec_file use 'a{ + exec_file use 'a { "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead<'a>, } From 3a5c666b3681b8cf80d46772b035b1284a9ce882 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Sun, 12 Sep 2021 10:34:07 +0800 Subject: [PATCH 6/8] Apply suggestions from code review Co-authored-by: Daniel Prilik --- examples/armv4t/gdb/exec_file.rs | 2 +- src/protocol/commands/_qXfer_exec_file.rs | 5 ++++- src/target/ext/exec_file.rs | 14 +++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index 0d8e3454..c97e559e 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -15,7 +15,7 @@ impl target::ext::exec_file::ExecFile for Emu { let filename = b"/test.elf"; let len = filename.len(); let data = - &filename[(offset as usize).min(len) as usize..((offset + length) as usize).min(len)]; + &filename[offset.min(len)..(offset + length).min(len)]; let buf = &mut buf[..data.len()]; buf.copy_from_slice(data); Ok(data.len()) diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs index 31277a59..abd9264d 100644 --- a/src/protocol/commands/_qXfer_exec_file.rs +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -21,7 +21,10 @@ impl<'a> ParseCommand<'a> for qXferExecFileRead<'a> { } let mut body = body.split(|b| *b == b':').skip(1); - let pid = decode_hex(body.next()?).ok().and_then(Pid::new); + let pid = match body.next()? { + [] => None, + buf => Some(Pid::new(decode_hex(buf).ok()?)?) + } let mut body = body.next()?.split(|b| *b == b','); let offset = decode_hex(body.next()?).ok()?; diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 55422cdd..9ba3a9d7 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -9,11 +9,15 @@ use crate::common::Pid; /// I/O Extensions`](crate::target::ext::host_io), which enables the GDB client /// to read the executable file directly from the target pub trait ExecFile: Target { - /// Get full absolute name of the file that was executed to create - /// process `pid` running on the remote system, or the filename - /// corresponding to the currently executing process if no `pid` is - /// provided. - /// Store the name into `buf`, and return the length of name. + /// Get full absolute path of the file that was executed to create + /// process `pid` running on the remote system. + /// + /// If `pid` is `None`, return the filename corresponding to the + /// currently executing process. + /// + /// Return the number of bytes written into `buf` (which may be less than `length`). + /// + /// If `offset` is greater than the length of the underlying data, return `Ok(0)`. fn get_exec_file( &self, pid: Option, From 92026bc4fee043f5569c3693e75e194743de1d7c Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Mon, 13 Sep 2021 15:40:27 +0800 Subject: [PATCH 7/8] Lint --- examples/armv4t/gdb/exec_file.rs | 3 +-- src/protocol/commands/_qXfer_exec_file.rs | 2 +- src/target/ext/exec_file.rs | 8 +++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index c97e559e..d34f17cd 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -14,8 +14,7 @@ impl target::ext::exec_file::ExecFile for Emu { ) -> TargetResult { let filename = b"/test.elf"; let len = filename.len(); - let data = - &filename[offset.min(len)..(offset + length).min(len)]; + let data = &filename[len.min(offset)..len.min(offset + length)]; let buf = &mut buf[..data.len()]; buf.copy_from_slice(data); Ok(data.len()) diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs index abd9264d..69a94813 100644 --- a/src/protocol/commands/_qXfer_exec_file.rs +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -24,7 +24,7 @@ impl<'a> ParseCommand<'a> for qXferExecFileRead<'a> { let pid = match body.next()? { [] => None, buf => Some(Pid::new(decode_hex(buf).ok()?)?) - } + }; let mut body = body.next()?.split(|b| *b == b','); let offset = decode_hex(body.next()?).ok()?; diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 9ba3a9d7..5dab9726 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -12,12 +12,14 @@ pub trait ExecFile: Target { /// Get full absolute path of the file that was executed to create /// process `pid` running on the remote system. /// - /// If `pid` is `None`, return the filename corresponding to the + /// If `pid` is `None`, return the filename corresponding to the /// currently executing process. /// - /// Return the number of bytes written into `buf` (which may be less than `length`). + /// Return the number of bytes written into `buf` (which may be less than + /// `length`). /// - /// If `offset` is greater than the length of the underlying data, return `Ok(0)`. + /// If `offset` is greater than the length of the underlying data, return + /// `Ok(0)`. fn get_exec_file( &self, pid: Option, From e9a721ee51275dfce48d9f29f9b6100059b0a7ea Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Wed, 15 Sep 2021 08:24:56 +0800 Subject: [PATCH 8/8] Change type of offset to u64 --- examples/armv4t/gdb/exec_file.rs | 4 ++-- src/protocol/commands/_qXfer_exec_file.rs | 2 +- src/target/ext/exec_file.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/armv4t/gdb/exec_file.rs b/examples/armv4t/gdb/exec_file.rs index d34f17cd..c130a9dc 100644 --- a/examples/armv4t/gdb/exec_file.rs +++ b/examples/armv4t/gdb/exec_file.rs @@ -8,13 +8,13 @@ impl target::ext::exec_file::ExecFile for Emu { fn get_exec_file( &self, _pid: Option, - offset: usize, + offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult { let filename = b"/test.elf"; let len = filename.len(); - let data = &filename[len.min(offset)..len.min(offset + length)]; + let data = &filename[len.min(offset as usize)..len.min(offset as usize + length)]; let buf = &mut buf[..data.len()]; buf.copy_from_slice(data); Ok(data.len()) diff --git a/src/protocol/commands/_qXfer_exec_file.rs b/src/protocol/commands/_qXfer_exec_file.rs index 69a94813..a74b83d5 100644 --- a/src/protocol/commands/_qXfer_exec_file.rs +++ b/src/protocol/commands/_qXfer_exec_file.rs @@ -5,7 +5,7 @@ use crate::common::Pid; #[derive(Debug)] pub struct qXferExecFileRead<'a> { pub pid: Option, - pub offset: usize, + pub offset: u64, pub length: usize, pub buf: &'a mut [u8], diff --git a/src/target/ext/exec_file.rs b/src/target/ext/exec_file.rs index 5dab9726..0db98742 100644 --- a/src/target/ext/exec_file.rs +++ b/src/target/ext/exec_file.rs @@ -23,7 +23,7 @@ pub trait ExecFile: Target { fn get_exec_file( &self, pid: Option, - offset: usize, + offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult;