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 Host I/O operations #66

Merged
merged 13 commits into from
Aug 20, 2021
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ categories = ["development-tools::debugging", "embedded", "emulators", "network-
exclude = ["examples/**/*.elf", "examples/**/*.o"]

[dependencies]
bitflags = "1.2.1"
cfg-if = "0.1.10"
log = "0.4"
managed = { version = "0.8", default-features = false }
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ Of course, most use-cases will want to support additional debugging features as
- Get section/segment relocation offsets from the target
- Custom `monitor` Commands
- Extend the GDB protocol with custom debug commands using GDB's `monitor` command
- Get target memory map
- Perform Host I/O operations

_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
47 changes: 47 additions & 0 deletions examples/armv4t/gdb/host_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use gdbstub::target;

use crate::emu::Emu;

use gdbstub::common::{HostMode, HostOpenFlags};
use gdbstub::target::ext::host_io::PreadOutput;

impl target::ext::host_io::HostIo for Emu {
fn open(&self, filename: &[u8], _flags: HostOpenFlags, _mode: HostMode) -> i64 {
if filename == b"/proc/1/maps" {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
bet4it marked this conversation as resolved.
Show resolved Hide resolved
1
} else {
-1
}
}

fn pread(
&self,
fd: usize,
count: u32,
offset: u32,
output: &mut PreadOutput<'_>,
) -> Result<(), Self::Error> {
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(())
} else {
Err("pread failed")
}
}

fn close(&self, fd: usize) -> i64 {
if fd == 1 {
0
} else {
-1
}
}

fn setfs(&self, _fd: usize) -> i64 {
0
}
}
6 changes: 6 additions & 0 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::emu::{Emu, Event};
mod breakpoints;
mod catch_syscalls;
mod extended_mode;
mod host_io;
mod memory_map;
mod monitor_cmd;
mod section_offsets;
Expand Down Expand Up @@ -94,6 +95,11 @@ impl Target for Emu {
fn catch_syscalls(&mut self) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<Self>> {
Some(self)
}

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

impl Emu {
Expand Down
52 changes: 52 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,59 @@
//! Common types and definitions.

use bitflags::bitflags;

/// Thread ID
pub type Tid = core::num::NonZeroUsize;

/// Process ID
pub type Pid = core::num::NonZeroUsize;

bitflags! {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
// The read/write flags below may look a little weird, but that is the way
// they are defined in the protocol.
/// Host flags for opening files.
bet4it marked this conversation as resolved.
Show resolved Hide resolved
pub struct HostOpenFlags: u32 {
/// A read-only file.
const O_RDONLY = 0x0;
/// A write-only file.
const O_WRONLY = 0x1;
/// A read-write file.
const O_RDWR = 0x2;
/// Append to an existing file.
const O_APPEND = 0x8;
/// Create a non-existent file.
const O_CREAT = 0x200;
/// Truncate an existing file.
const O_TRUNC = 0x400;
/// Exclusive access.
const O_EXCL = 0x800;
}
}

bitflags! {
/// Host file permissions.
pub struct HostMode: u32 {
/// A regular file.
const S_IFREG = 0o100000;
/// A directory.
const S_IFDIR = 0o40000;
/// User read permissions.
const S_IRUSR = 0o400;
/// User write permissions.
const S_IWUSR = 0o200;
/// User execute permissions.
const S_IXUSR = 0o100;
/// Group read permissions.
const S_IRGRP = 0o40;
/// Group write permissions
const S_IWGRP = 0o20;
/// Group execute permissions.
const S_IXGRP = 0o10;
/// World read permissions.
const S_IROTH = 0o4;
/// World write permissions
const S_IWOTH = 0o2;
/// World execute permissions.
const S_IXOTH = 0o1;
}
}
69 changes: 69 additions & 0 deletions src/gdbstub_impl/ext/host_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use super::prelude::*;
use crate::arch::Arch;
use crate::protocol::commands::ext::HostIo;
use crate::target::ext::host_io::PreadOutput;

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_host_io(
&mut self,
res: &mut ResponseWriter<C>,
target: &mut T,
command: HostIo,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
let ops = match target.host_io() {
Some(ops) => ops,
None => return Ok(HandlerStatus::Handled),
};

crate::__dead_code_marker!("host_io", "impl");

let handler_status = match command {
HostIo::vFileOpen(cmd) => {
let ret = ops.open(cmd.filename, cmd.flags, cmd.mode);
res.write_str("F")?;
res.write_num(ret)?;
HandlerStatus::Handled
}
HostIo::vFileClose(cmd) => {
let ret = ops.close(cmd.fd);
res.write_str("F")?;
res.write_num(ret)?;
HandlerStatus::Handled
}
HostIo::vFilePread(cmd) => {
let count = <T::Arch as Arch>::Usize::from_be_bytes(cmd.count)
.ok_or(Error::TargetMismatch)?;
let offset = <T::Arch as Arch>::Usize::from_be_bytes(cmd.offset)
.ok_or(Error::TargetMismatch)?;
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())?;
bet4it marked this conversation as resolved.
Show resolved Hide resolved
res.write_str(";")?;
res.write_binary(data)?;
Ok(())
})();

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

ops.pread(cmd.fd, count, offset, &mut PreadOutput::new(&mut callback))
.map_err(Error::TargetError)?;
err?;

HandlerStatus::Handled
}
HostIo::vFileSetfs(cmd) => {
let ret = ops.setfs(cmd.fd);
res.write_str("F")?;
res.write_num(ret)?;
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 @@ -15,6 +15,7 @@ mod base;
mod breakpoints;
mod catch_syscalls;
mod extended_mode;
mod host_io;
mod memory_map;
mod monitor_cmd;
mod reverse_exec;
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 @@ -579,6 +579,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
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),
Command::HostIo(cmd) => self.handle_host_io(res, target, cmd),
}
}
}
7 changes: 7 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,13 @@ commands! {
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead,
}

host_io use 'a {
"vFile:open" => _vFile_open::vFileOpen<'a>,
"vFile:close" => _vFile_close::vFileClose,
"vFile:pread" => _vFile_pread::vFilePread<'a>,
"vFile:setfs" => _vFile_setfs::vFileSetfs,
}

catch_syscalls use 'a {
"QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>,
}
Expand Down
23 changes: 23 additions & 0 deletions src/protocol/commands/_vFile_close.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::prelude::*;

#[derive(Debug)]
pub struct vFileClose {
pub fd: usize,
}

impl<'a> ParseCommand<'a> for vFileClose {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() {
return None;
}

match body {
[b':', body @ ..] => {
let fd = decode_hex(body).ok()?;
Some(vFileClose{fd})
},
_ => None,
}
}
}
30 changes: 30 additions & 0 deletions src/protocol/commands/_vFile_open.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use super::prelude::*;

use crate::common::{HostOpenFlags, HostMode};

#[derive(Debug)]
pub struct vFileOpen<'a> {
pub filename: &'a [u8],
pub flags: HostOpenFlags,
pub mode: HostMode,
}

impl<'a> ParseCommand<'a> for vFileOpen<'a> {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() {
return None;
}

match body {
[b':', body @ ..] => {
let mut body = body.splitn_mut_no_panic(3, |b| *b == b',');
let filename = decode_hex_buf(body.next()?).ok()?;
let flags = HostOpenFlags::from_bits_truncate(decode_hex(body.next()?).ok()?);
bet4it marked this conversation as resolved.
Show resolved Hide resolved
let mode = HostMode::from_bits_truncate(decode_hex(body.next()?).ok()?);
Some(vFileOpen{filename, flags, mode})
},
_ => None,
}
}
}
28 changes: 28 additions & 0 deletions src/protocol/commands/_vFile_pread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use super::prelude::*;

#[derive(Debug)]
pub struct vFilePread<'a> {
pub fd: usize,
bet4it marked this conversation as resolved.
Show resolved Hide resolved
pub count: &'a [u8],
pub offset: &'a [u8],
}

impl<'a> ParseCommand<'a> for vFilePread<'a> {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() {
return None;
}

match body {
[b':', body @ ..] => {
let mut body = body.splitn_mut_no_panic(3, |b| *b == b',');
let fd = decode_hex(body.next()?).ok()?;
let count = decode_hex_buf(body.next()?).ok()?;
let offset = decode_hex_buf(body.next()?).ok()?;
Some(vFilePread{fd, count, offset})
},
_ => None,
}
}
}
23 changes: 23 additions & 0 deletions src/protocol/commands/_vFile_setfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::prelude::*;

#[derive(Debug)]
pub struct vFileSetfs {
pub fd: usize,
}

impl<'a> ParseCommand<'a> for vFileSetfs {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() {
return None;
}

match body {
[b':', body @ ..] => {
let fd = decode_hex(body).ok()?;
Some(vFileSetfs{fd})
},
_ => None,
}
}
}
2 changes: 1 addition & 1 deletion src/protocol/console_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl<'a> fmt::Write for ConsoleOutput<'a> {
}

impl<'a> ConsoleOutput<'a> {
pub(crate) fn new(callback: &'a mut dyn FnMut(&[u8])) -> ConsoleOutput<'a> {
pub(crate) fn new(callback: &'a mut dyn FnMut(&[u8])) -> Self {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
ConsoleOutput {
#[cfg(feature = "alloc")]
buf: Vec::new(),
Expand Down
Loading