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

use crate::emu::Emu;

impl target::ext::host_io::HostIo for Emu {
fn open(&self, filename: &[u8], _flags: u64, _mode: u64) -> 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: usize, offset: usize) -> &[u8] {
if fd == 1 {
let maps = b"0x55550000-0x55550078 r-x 0 0 0\n";
let len = maps.len();
&maps[offset.min(len)..(offset + count).min(len)]
} else {
b""
}
}

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
49 changes: 49 additions & 0 deletions src/gdbstub_impl/ext/host_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use super::prelude::*;
use crate::protocol::commands::ext::HostIo;

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 data = ops.pread(cmd.fd, cmd.count, cmd.offset);
res.write_str("F")?;
res.write_num(data.len())?;
res.write_str(";")?;
res.write_binary(data)?;
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{
bet4it marked this conversation as resolved.
Show resolved Hide resolved
"vFile:open" => _vFile_open::vFileOpen<'a>,
"vFile:close" => _vFile_close::vFileClose,
"vFile:pread" => _vFile_pread::vFilePread,
"vFile:setfs" => _vFile_setfs::vFileSetfs,
daniel5151 marked this conversation as resolved.
Show resolved Hide resolved
}

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,
}
}
}
28 changes: 28 additions & 0 deletions src/protocol/commands/_vFile_open.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use super::prelude::*;

#[derive(Debug)]
pub struct vFileOpen<'a> {
pub filename: &'a [u8],
pub flags: u64,
bet4it marked this conversation as resolved.
Show resolved Hide resolved
pub mode: u64,
bet4it marked this conversation as resolved.
Show resolved Hide resolved
}

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= decode_hex(body.next()?).ok()?;
let mode= 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 {
pub fd: usize,
bet4it marked this conversation as resolved.
Show resolved Hide resolved
pub count: usize,
pub offset: usize,
bet4it marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'a> ParseCommand<'a> for vFilePread {
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(body.next()?).ok()?;
let offset= decode_hex(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,
}
}
}
19 changes: 19 additions & 0 deletions src/target/ext/host_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Provide Host I/O operations for the target.
use crate::target::Target;

/// Target Extension - Perform I/O operations on host
pub trait HostIo: Target {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
/// Open a file at filename and return a file descriptor for it, or return
/// -1 if an error occurs.
fn open(&self, filename: &[u8], flags: u64, mode: u64) -> i64;
bet4it marked this conversation as resolved.
Show resolved Hide resolved
/// Close the open file corresponding to fd and return 0, or -1 if an error
/// occurs.
fn close(&self, fd: usize) -> i64;
/// Read data from the open file corresponding to fd.
bet4it marked this conversation as resolved.
Show resolved Hide resolved
fn pread(&self, fd: usize, count: usize, offset: usize) -> &[u8];
bet4it marked this conversation as resolved.
Show resolved Hide resolved
/// Select the filesystem on which vFile operations with filename arguments
/// will operate.
fn setfs(&self, fd: usize) -> i64;
bet4it marked this conversation as resolved.
Show resolved Hide resolved
}

define_ext!(HostIoOps, HostIo);
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ pub mod base;
pub mod breakpoints;
pub mod catch_syscalls;
pub mod extended_mode;
pub mod host_io;
pub mod memory_map;
pub mod monitor_cmd;
pub mod section_offsets;
Expand Down
5 changes: 5 additions & 0 deletions src/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,11 @@ pub trait Target {
fn catch_syscalls(&mut self) -> Option<ext::catch_syscalls::CatchSyscallsOps<Self>> {
None
}

/// Support Host I/O operations.
fn host_io(&mut self) -> Option<ext::host_io::HostIoOps<Self>> {
bet4it marked this conversation as resolved.
Show resolved Hide resolved
None
}
}

macro_rules! impl_dyn_target {
Expand Down