Skip to content

Commit

Permalink
Support exec-file and Host I/O
Browse files Browse the repository at this point in the history
  • Loading branch information
bet4it committed Jul 30, 2021
1 parent c40145c commit 250568f
Show file tree
Hide file tree
Showing 21 changed files with 328 additions and 2 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ 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
- Get target exec file
- 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
9 changes: 9 additions & 0 deletions examples/armv4t/gdb/exec_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use gdbstub::target;

use crate::emu::Emu;

impl target::ext::exec_file::ExecFile for Emu {
fn get_exec_file(&self, _pid: Option<u64>) -> &[u8] {
b"/test.elf"
}
}
36 changes: 36 additions & 0 deletions examples/armv4t/gdb/host_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use gdbstub::target;

use crate::emu::Emu;

use crate::TEST_PROGRAM_ELF;

impl target::ext::host_io::HostIo for Emu {
fn open(&self, filename: &[u8], _flags: u64, _mode: u64) -> i64 {
if filename == b"/test.elf" {
1
} else {
-1
}
}

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

fn pread(&self, fd: usize, count: usize, offset: usize) -> &[u8] {
if fd == 1 {
let len = TEST_PROGRAM_ELF.len();
&TEST_PROGRAM_ELF[offset.min(len)..(offset + count).min(len)]
} else {
b""
}
}

fn setfs(&self, _fd: usize) -> i64 {
0
}
}
12 changes: 12 additions & 0 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use crate::emu::{Emu, Event};

mod breakpoints;
mod catch_syscalls;
mod exec_file;
mod extended_mode;
mod host_io;
mod memory_map;
mod monitor_cmd;
mod section_offsets;
Expand Down Expand Up @@ -94,6 +96,16 @@ 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)
}

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

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 filename = ops.get_exec_file(cmd.pid);
res.write_binary_range(filename, cmd.offset, cmd.len)?;
HandlerStatus::Handled
}
};

Ok(handler_status)
}
}
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)
}
}
2 changes: 2 additions & 0 deletions src/gdbstub_impl/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ mod prelude {
mod base;
mod breakpoints;
mod catch_syscalls;
mod exec_file;
mod extended_mode;
mod host_io;
mod memory_map;
mod monitor_cmd;
mod reverse_exec;
Expand Down
2 changes: 2 additions & 0 deletions src/gdbstub_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,8 @@ 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),
Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd),
}
}
}
11 changes: 11 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ 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,
"vFile:pread" => _vFile_pread::vFilePread,
"vFile:setfs" => _vFile_setfs::vFileSetfs,
}

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

#[derive(Debug)]
pub struct qXferExecFileRead {
pub pid: Option<u64>,
pub offset: usize,
pub len: usize,
}

impl<'a> ParseCommand<'a> for qXferExecFileRead {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
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::<u64>(body.next()?).ok();

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})
}
}
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,
pub mode: u64,
}

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,
pub count: usize,
pub offset: usize,
}

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,
}
}
}
11 changes: 11 additions & 0 deletions src/target/ext/exec_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Provide exec-file path for the target.
use crate::target::Target;

/// 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<u64>) -> &[u8];
}

define_ext!(ExecFileOps, ExecFile);
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 {
/// 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;
/// 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.
fn pread(&self, fd: usize, count: usize, offset: usize) -> &[u8];
/// Select the filesystem on which vFile operations with filename arguments
/// will operate.
fn setfs(&self, fd: usize) -> i64;
}

define_ext!(HostIoOps, HostIo);
2 changes: 2 additions & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ 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;
pub mod monitor_cmd;
pub mod section_offsets;
Expand Down
Loading

0 comments on commit 250568f

Please sign in to comment.