Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support read exec-file #69

Merged
merged 8 commits into from
Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand Down
22 changes: 22 additions & 0 deletions examples/armv4t/gdb/exec_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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<Pid>,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
let filename = b"/test.elf";
let len = filename.len();
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())
}
}
61 changes: 56 additions & 5 deletions examples/armv4t/gdb/host_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use gdbstub::target::ext::host_io::{
};

use crate::emu::Emu;
use crate::TEST_PROGRAM_ELF;

const FD_RESERVED: u32 = 1;

impl target::ext::host_io::HostIo for Emu {
#[inline(always)]
Expand Down Expand Up @@ -61,6 +64,14 @@ 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" {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
return Ok(0);
}

let path =
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;

Expand Down Expand Up @@ -95,13 +106,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)),
};
Expand All @@ -122,7 +137,18 @@ impl target::ext::host_io::HostIoPread for Emu {
offset: u32,
output: HostIoOutput<'a>,
) -> HostIoResult<HostIoToken<'a>, 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)),
};
Expand All @@ -136,7 +162,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<u32, Self> {
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)),
};
Expand All @@ -149,7 +179,28 @@ impl target::ext::host_io::HostIoPwrite for Emu {

impl target::ext::host_io::HostIoFstat for Emu {
fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self> {
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)),
};
Expand Down
6 changes: 6 additions & 0 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -100,6 +101,11 @@ impl Target for Emu {
fn host_io(&mut self) -> Option<target::ext::host_io::HostIoOps<Self>> {
Some(self)
}

#[inline(always)]
fn exec_file(&mut self) -> Option<target::ext::exec_file::ExecFileOps<Self>> {
Some(self)
}
}

impl Emu {
Expand Down
2 changes: 1 addition & 1 deletion examples/armv4t/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use gdbstub::{target::Target, ConnectionExt, DisconnectReason, GdbStub};

pub type DynResult<T> = Result<T, Box<dyn std::error::Error>>;

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;
Expand Down
1 change: 0 additions & 1 deletion examples/armv4t/test_bin/.gdbinit
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
file test.elf
daniel5151 marked this conversation as resolved.
Show resolved Hide resolved
target extended-remote :9001
4 changes: 4 additions & 0 deletions src/gdbstub_impl/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
res.write_str(";qXfer:memory-map:read+")?;
}

if target.exec_file().is_some() {
res.write_str(";qXfer:exec-file:read+")?;
}

HandlerStatus::Handled
}
Base::QStartNoAckMode(_) => {
Expand Down
36 changes: 36 additions & 0 deletions src/gdbstub_impl/ext/exec_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use super::prelude::*;
use crate::protocol::commands::ext::ExecFile;
daniel5151 marked this conversation as resolved.
Show resolved Hide resolved

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_exec_file(
&mut self,
res: &mut ResponseWriter<C>,
target: &mut T,
command: ExecFile,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
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 ret = ops
.get_exec_file(cmd.pid, cmd.offset, cmd.length, cmd.buf)
.handle_error()?;
if ret == 0 {
res.write_str("l")?;
} else {
res.write_str("m")?;
// TODO: add more specific error variant?
res.write_binary(cmd.buf.get(..ret).ok_or(Error::PacketBufferOverflow)?)?;
}
HandlerStatus::Handled
}
};

Ok(handler_status)
}
}
1 change: 1 addition & 0 deletions src/gdbstub_impl/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/gdbstub_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
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),
}
}
}
4 changes: 4 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ commands! {
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead,
}

exec_file use 'a {
"qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead<'a>,
}

host_io use 'a {
"vFile:open" => _vFile_open::vFileOpen<'a>,
"vFile:close" => _vFile_close::vFileClose,
Expand Down
37 changes: 37 additions & 0 deletions src/protocol/commands/_qXfer_exec_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use super::prelude::*;

use crate::common::Pid;

#[derive(Debug)]
pub struct qXferExecFileRead<'a> {
pub pid: Option<Pid>,
pub offset: u64,
pub length: usize,

pub buf: &'a mut [u8],
}

impl<'a> ParseCommand<'a> for qXferExecFileRead<'a> {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let (buf, body_range) = buf.into_raw_buf();
let body = buf.get_mut(body_range.start..body_range.end)?;

if body.is_empty() {
return None;
}

let mut body = body.split(|b| *b == b':').skip(1);
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()?;
let length = decode_hex(body.next()?).ok()?;

drop(body);

Some(qXferExecFileRead {pid, offset, length, buf})
}
}
2 changes: 1 addition & 1 deletion src/protocol/commands/_vFile_setfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
Expand Down
32 changes: 32 additions & 0 deletions src/target/ext/exec_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Provide exec-file path for the 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 {
/// 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<Pid>,
offset: u64,
length: usize,
buf: &mut [u8],
) -> TargetResult<usize, Self>;
}

define_ext!(ExecFileOps, ExecFile);
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions src/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@ pub trait Target {
fn host_io(&mut self) -> Option<ext::host_io::HostIoOps<Self>> {
None
}

/// Provide exec-file
fn exec_file(&mut self) -> Option<ext::exec_file::ExecFileOps<Self>> {
None
}
}

macro_rules! impl_dyn_target {
Expand Down Expand Up @@ -395,6 +400,11 @@ macro_rules! impl_dyn_target {
(**self).monitor_cmd()
}

#[inline(always)]
fn exec_file(&mut self) -> Option<ext::exec_file::ExecFileOps<Self>> {
(**self).exec_file()
}

#[inline(always)]
fn extended_mode(&mut self) -> Option<ext::extended_mode::ExtendedModeOps<Self>> {
(**self).extended_mode()
Expand Down