Skip to content
12 changes: 6 additions & 6 deletions examples/armv4t/gdb/host_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use gdbstub::target;

use crate::emu::Emu;

use gdbstub::target::ext::host_io::{HostIoOutput, HostMode, HostOpenFlags};
use gdbstub::target::ext::host_io::{HostMode, HostOpenFlags, PreadOutput, PreadToken};
use gdbstub::target::TargetResult;

impl target::ext::host_io::HostIo for Emu {
Expand All @@ -29,6 +29,7 @@ impl target::ext::host_io::HostIoOpen for Emu {
_flags: HostOpenFlags,
_mode: HostMode,
) -> TargetResult<i32, Self> {
// Support `info proc mappings` command
if filename == b"/proc/1/maps" {
Ok(1)
} else {
Expand All @@ -38,20 +39,19 @@ impl target::ext::host_io::HostIoOpen for Emu {
}

impl target::ext::host_io::HostIoPread for Emu {
fn pread(
fn pread<'a>(
&mut self,
fd: i32,
count: u32,
offset: u32,
output: HostIoOutput<'_>,
) -> TargetResult<(), Self> {
output: PreadOutput<'a>,
) -> TargetResult<PreadToken<'a>, Self> {
if fd == 1 {
let maps = b"0x55550000-0x55550078 r-x 0 0 0\n";
let len = maps.len();
let count: usize = count as usize;
let offset: usize = offset as usize;
output.write(&maps[offset.min(len)..(offset + count).min(len)]);
Ok(())
Ok(output.write(&maps[offset.min(len)..(offset + count).min(len)]))
} else {
Err(().into())
}
Expand Down
33 changes: 11 additions & 22 deletions src/gdbstub_impl/ext/host_io.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::prelude::*;
use crate::arch::Arch;
use crate::protocol::commands::ext::HostIo;
use crate::target::ext::host_io::HostIoOutput;
use crate::target::ext::host_io::{HostStat, PreadOutput};

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_host_io(
Expand Down Expand Up @@ -53,7 +53,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
};

let ops = ops.enable_pread().unwrap();
ops.pread(cmd.fd, count, offset, HostIoOutput::new(&mut callback))
ops.pread(cmd.fd, count, offset, PreadOutput::new(&mut callback))
.handle_error()?;
err?;

Expand All @@ -69,26 +69,15 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
HandlerStatus::Handled
}
HostIo::vFileFstat(cmd) if ops.enable_fstat().is_some() => {
let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
let mut callback = |data: &[u8]| {
let e = (|| {
res.write_str("F")?;
res.write_num(data.len())?;
res.write_str(";")?;
res.write_binary(data)?;
Ok(())
})();

if let Err(e) = e {
err = Err(e)
}
};

let ops = ops.enable_fstat().unwrap();
ops.fstat(cmd.fd, HostIoOutput::new(&mut callback))
.handle_error()?;
err?;

let stat = ops.fstat(cmd.fd).handle_error()?;
let size = core::mem::size_of_val(&stat);
let p: *const HostStat = &stat;
let p: *const u8 = p as *const u8;
res.write_str("F")?;
res.write_num(size)?;
res.write_str(";")?;
res.write_binary(unsafe { core::slice::from_raw_parts(p, size) })?;
HandlerStatus::Handled
}
HostIo::vFileUnlink(cmd) if ops.enable_unlink().is_some() => {
Expand All @@ -107,7 +96,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
HostIo::vFileSetfs(cmd) if ops.enable_setfs().is_some() => {
let ops = ops.enable_setfs().unwrap();
ops.setfs(cmd.pid).handle_error()?;
ops.setfs(cmd.fs).handle_error()?;
HandlerStatus::Handled
}
_ => HandlerStatus::Handled,
Expand Down
11 changes: 8 additions & 3 deletions src/protocol/commands/_vFile_setfs.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::prelude::*;
use crate::target::ext::host_io::FsKind;
use core::num::NonZeroUsize;

#[derive(Debug)]
pub struct vFileSetfs {
pub pid: usize,
pub fs: FsKind,
}

impl<'a> ParseCommand<'a> for vFileSetfs {
Expand All @@ -14,8 +16,11 @@ impl<'a> ParseCommand<'a> for vFileSetfs {

match body {
[b':', body @ ..] => {
let pid = decode_hex(body).ok()?;
Some(vFileSetfs{pid})
let fs = match decode_hex(body).ok()? {
0 => FsKind::Stub,
pid => FsKind::Pid(NonZeroUsize::new(pid).unwrap()),
};
Some(vFileSetfs{fs})
},
_ => None,
}
Expand Down
153 changes: 112 additions & 41 deletions src/target/ext/host_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use bitflags::bitflags;

bitflags! {
/// Host flags for opening files.
/// [Open Flags]: https://sourceware.org/gdb/onlinedocs/gdb/Open-Flags.html
///
/// Extracted from the GDB documentation at
/// [Open Flags](https://sourceware.org/gdb/current/onlinedocs/gdb/Open-Flags.html#Open-Flags)
pub struct HostOpenFlags: u32 {
/// A read-only file.
const O_RDONLY = 0x0;
Expand All @@ -26,7 +28,9 @@ bitflags! {

bitflags! {
/// Host file permissions.
/// [mode_t Values]: https://sourceware.org/gdb/onlinedocs/gdb/mode_005ft-Values.html
///
/// Extracted from the GDB documentation at
/// [mode_t Values](https://sourceware.org/gdb/current/onlinedocs/gdb/mode_005ft-Values.html#mode_005ft-Values)
pub struct HostMode: u32 {
/// A regular file.
const S_IFREG = 0o100000;
Expand All @@ -53,19 +57,71 @@ bitflags! {
}
}

/// Data returned by a host fstat request.
///
/// Extracted from the GDB documentation at
/// [struct stat](https://sourceware.org/gdb/current/onlinedocs/gdb/struct-stat.html#struct-stat)
#[derive(Debug)]
pub struct HostStat {
/// The device.
pub st_dev: u32,
/// The inode.
pub st_ino: u32,
/// Protection bits.
pub st_mode: HostMode,
/// The number of hard links.
pub st_nlink: u32,
/// The user id of the owner.
pub st_uid: u32,
/// The group id of the owner.
pub st_gid: u32,
/// The device type, if an inode device.
pub st_rdev: u32,
/// The size of the file in bytes.
pub st_size: u64,
/// The blocksize for the filesystem.
pub st_blksize: u64,
/// The number of blocks allocated.
pub st_blocks: u64,
/// The last time the file was accessed, in seconds since the epoch.
pub st_atime: u32,
/// The last time the file was modified, in seconds since the epoch.
pub st_mtime: u32,
/// The last time the file was changed, in seconds since the epoch.
pub st_ctime: u32,
}

/// Select the filesystem vFile operations will operate on. Used by vFile setfs
/// command.
#[derive(Debug)]
pub enum FsKind {
/// select the filesystem as seen by the remote stub
Stub,
/// select the filesystem as seen by process pid
Pid(crate::common::Pid),
}

/// Token to ensure PreadOutput is used
pub struct PreadToken<'a>(core::marker::PhantomData<&'a *mut ()>);

/// An interface to send pread data back to the GDB client.
pub struct HostIoOutput<'a> {
pub struct PreadOutput<'a> {
cb: &'a mut dyn FnMut(&[u8]),
token: PreadToken<'a>,
}

impl<'a> HostIoOutput<'a> {
impl<'a> PreadOutput<'a> {
pub(crate) fn new(cb: &'a mut dyn FnMut(&[u8])) -> Self {
Self { cb }
Self {
cb,
token: PreadToken(core::marker::PhantomData),
}
}

/// Write out raw file bytes to the GDB debugger.
pub fn write(self, buf: &[u8]) {
(self.cb)(buf)
pub fn write(self, buf: &[u8]) -> PreadToken<'a> {
(self.cb)(buf);
self.token
}
}

Expand Down Expand Up @@ -117,8 +173,12 @@ define_ext!(HostIoOps, HostIo);

/// Nested Target Extension - Host I/O open operation.
pub trait HostIoOpen: HostIo {
/// Close the open file corresponding to fd and return 0, or -1 if an error
/// occurs.
/// Open a file at filename and return a file descriptor for it, or return
/// -1 if an error occurs.
///
/// The filename is a string, flags is an integer indicating a mask of open
/// flags (see [`HostOpenFlags`]), and mode is an integer indicating a mask
/// of mode bits to use if the file is created (see [`HostMode`]).
fn open(
&mut self,
filename: &[u8],
Expand All @@ -140,31 +200,38 @@ define_ext!(HostIoCloseOps, HostIoClose);

/// Nested Target Extension - Host I/O pread operation.
pub trait HostIoPread: HostIo {
/// Read data from the open file corresponding to fd. Up to count bytes will
/// be read from the file, starting at offset relative to the start of the
/// file. The target may read fewer bytes; common reasons include packet
/// size limits and an end-of-file condition. The number of bytes read is
/// returned. Zero should only be returned for a successful read at the end
/// of the file, or if count was zero.
fn pread(
/// Read data from the open file corresponding to fd.
///
/// Up to count bytes will be read from the file, starting at offset
/// relative to the start of the file.
///
/// The target may read fewer bytes; common reasons include packet size
/// limits and an end-of-file condition.
///
/// The number of bytes read is returned. Zero should only be returned for a
/// successful read at the end of the file, or if count was zero.
fn pread<'a>(
&mut self,
fd: i32,
count: <Self::Arch as Arch>::Usize,
offset: <Self::Arch as Arch>::Usize,
output: HostIoOutput<'_>,
) -> TargetResult<(), Self>;
output: PreadOutput<'a>,
) -> TargetResult<PreadToken<'a>, Self>;
}

define_ext!(HostIoPreadOps, HostIoPread);

/// Nested Target Extension - Host I/O pwrite operation.
pub trait HostIoPwrite: HostIo {
/// Write data (a binary buffer) to the open file corresponding to fd. Start
/// the write at offset from the start of the file. Unlike many write system
/// calls, there is no separate count argument; the length of data in the
/// packet is used. ‘vFile:pwrite’ returns the number of bytes written,
/// which may be shorter than the length of data, or -1 if an error
/// occurred.
/// Write data (a binary buffer) to the open file corresponding to fd.
///
/// Start the write at offset from the start of the file.
///
/// Unlike many write system calls, there is no separate count argument; the
/// length of data in the packet is used.
///
/// ‘vFile:pwrite’ returns the number of bytes written, which may be shorter
/// than the length of data, or -1 if an error occurred.
fn pwrite(
&mut self,
fd: i32,
Expand All @@ -177,19 +244,21 @@ define_ext!(HostIoPwriteOps, HostIoPwrite);

/// Nested Target Extension - Host I/O fstat operation.
pub trait HostIoFstat: HostIo {
/// Get information about the open file corresponding to fd. On success the
/// information is returned as a binary attachment and the return value is
/// the size of this attachment in bytes. If an error occurs the return
/// value is -1.
fn fstat(&mut self, fd: i32, output: HostIoOutput<'_>) -> TargetResult<i32, Self>;
/// Get information about the open file corresponding to fd.
///
/// On success the information is returned as a binary attachment and the
/// return value is the size of this attachment in bytes. If an error occurs
/// the return value is -1.
fn fstat(&mut self, fd: i32) -> TargetResult<HostStat, Self>;
}

define_ext!(HostIoFstatOps, HostIoFstat);

/// Nested Target Extension - Host I/O unlink operation.
pub trait HostIoUnlink: HostIo {
/// Delete the file at filename on the target. Return 0, or -1 if an error
/// occurs. The filename is a string.
/// Delete the file at filename on the target.
///
/// Return 0, or -1 if an error occurs.
fn unlink(&mut self, filename: &[u8]) -> TargetResult<i32, Self>;
}

Expand All @@ -199,12 +268,13 @@ define_ext!(HostIoUnlinkOps, HostIoUnlink);
pub trait HostIoReadlink: HostIo {
/// Read value of symbolic link filename on the target. Return the number of
/// bytes read, or -1 if an error occurs.

///
/// The data read should be returned as a binary attachment on success. If
/// zero bytes were read, the response should include an empty binary
/// attachment (i.e. a trailing semicolon). The return value is the number
/// of target bytes read; the binary attachment may be longer if some
/// characters were escaped.
/// attachment (i.e. a trailing semicolon).
///
/// The return value is the number of target bytes read; the binary
/// attachment may be longer if some characters were escaped.
fn readlink(&mut self, filename: &[u8]) -> TargetResult<i32, Self>;
}

Expand All @@ -216,13 +286,14 @@ pub trait HostIoSetfs: HostIo {
/// will operate. This is required for GDB to be able to access files on
/// remote targets where the remote stub does not share a common filesystem
/// with the inferior(s).

///
/// If pid is nonzero, select the filesystem as seen by process pid. If pid
/// is zero, select the filesystem as seen by the remote stub. Return 0 on
/// success, or -1 if an error occurs. If vFile:setfs: indicates success,
/// the selected filesystem remains selected until the next successful
/// vFile:setfs: operation.
fn setfs(&mut self, pid: usize) -> TargetResult<i32, Self>;
/// is zero, select the filesystem as seen by the remote stub.
///
/// Return 0 on success, or -1 if an error occurs. If vFile:setfs: indicates
/// success, the selected filesystem remains selected until the next
/// successful vFile:setfs: operation.
fn setfs(&mut self, fs: FsKind) -> TargetResult<i32, Self>;
}

define_ext!(HostIoSetfsOps, HostIoSetfs);
1 change: 1 addition & 0 deletions src/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ pub trait Target {
}

/// Support Host I/O operations.
#[inline(always)]
fn host_io(&mut self) -> Option<ext::host_io::HostIoOps<Self>> {
None
}
Expand Down