Skip to content

Commit 250568f

Browse files
committed
Support exec-file and Host I/O
1 parent c40145c commit 250568f

21 files changed

+328
-2
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ Of course, most use-cases will want to support additional debugging features as
7575
- Get section/segment relocation offsets from the target
7676
- Custom `monitor` Commands
7777
- Extend the GDB protocol with custom debug commands using GDB's `monitor` command
78+
- Get target memory map
79+
- Get target exec file
80+
- Perform Host I/O operations
7881

7982
_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!
8083

examples/armv4t/gdb/exec_file.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use gdbstub::target;
2+
3+
use crate::emu::Emu;
4+
5+
impl target::ext::exec_file::ExecFile for Emu {
6+
fn get_exec_file(&self, _pid: Option<u64>) -> &[u8] {
7+
b"/test.elf"
8+
}
9+
}

examples/armv4t/gdb/host_io.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use gdbstub::target;
2+
3+
use crate::emu::Emu;
4+
5+
use crate::TEST_PROGRAM_ELF;
6+
7+
impl target::ext::host_io::HostIo for Emu {
8+
fn open(&self, filename: &[u8], _flags: u64, _mode: u64) -> i64 {
9+
if filename == b"/test.elf" {
10+
1
11+
} else {
12+
-1
13+
}
14+
}
15+
16+
fn close(&self, fd: usize) -> i64 {
17+
if fd == 1 {
18+
0
19+
} else {
20+
-1
21+
}
22+
}
23+
24+
fn pread(&self, fd: usize, count: usize, offset: usize) -> &[u8] {
25+
if fd == 1 {
26+
let len = TEST_PROGRAM_ELF.len();
27+
&TEST_PROGRAM_ELF[offset.min(len)..(offset + count).min(len)]
28+
} else {
29+
b""
30+
}
31+
}
32+
33+
fn setfs(&self, _fd: usize) -> i64 {
34+
0
35+
}
36+
}

examples/armv4t/gdb/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use crate::emu::{Emu, Event};
1616

1717
mod breakpoints;
1818
mod catch_syscalls;
19+
mod exec_file;
1920
mod extended_mode;
21+
mod host_io;
2022
mod memory_map;
2123
mod monitor_cmd;
2224
mod section_offsets;
@@ -94,6 +96,16 @@ impl Target for Emu {
9496
fn catch_syscalls(&mut self) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<Self>> {
9597
Some(self)
9698
}
99+
100+
#[inline(always)]
101+
fn host_io(&mut self) -> Option<target::ext::host_io::HostIoOps<Self>> {
102+
Some(self)
103+
}
104+
105+
#[inline(always)]
106+
fn exec_file(&mut self) -> Option<target::ext::exec_file::ExecFileOps<Self>> {
107+
Some(self)
108+
}
97109
}
98110

99111
impl Emu {

examples/armv4t/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use gdbstub::{target::Target, ConnectionExt, DisconnectReason, GdbStub};
99

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

12-
static TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf");
12+
pub static TEST_PROGRAM_ELF: &[u8] = include_bytes!("test_bin/test.elf");
1313

1414
mod emu;
1515
mod gdb;

examples/armv4t/test_bin/.gdbinit

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
file test.elf
21
target extended-remote :9001

src/gdbstub_impl/ext/base.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
115115
res.write_str(";qXfer:memory-map:read+")?;
116116
}
117117

118+
if target.exec_file().is_some() {
119+
res.write_str(";qXfer:exec-file:read+")?;
120+
}
121+
118122
HandlerStatus::Handled
119123
}
120124
Base::QStartNoAckMode(_) => {

src/gdbstub_impl/ext/exec_file.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use super::prelude::*;
2+
use crate::protocol::commands::ext::ExecFile;
3+
4+
impl<T: Target, C: Connection> GdbStubImpl<T, C> {
5+
pub(crate) fn handle_exec_file(
6+
&mut self,
7+
res: &mut ResponseWriter<C>,
8+
target: &mut T,
9+
command: ExecFile,
10+
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
11+
let ops = match target.exec_file() {
12+
Some(ops) => ops,
13+
None => return Ok(HandlerStatus::Handled),
14+
};
15+
16+
crate::__dead_code_marker!("exec_file", "impl");
17+
18+
let handler_status = match command {
19+
ExecFile::qXferExecFileRead(cmd) => {
20+
let filename = ops.get_exec_file(cmd.pid);
21+
res.write_binary_range(filename, cmd.offset, cmd.len)?;
22+
HandlerStatus::Handled
23+
}
24+
};
25+
26+
Ok(handler_status)
27+
}
28+
}

src/gdbstub_impl/ext/host_io.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use super::prelude::*;
2+
use crate::protocol::commands::ext::HostIo;
3+
4+
impl<T: Target, C: Connection> GdbStubImpl<T, C> {
5+
pub(crate) fn handle_host_io(
6+
&mut self,
7+
res: &mut ResponseWriter<C>,
8+
target: &mut T,
9+
command: HostIo,
10+
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
11+
let ops = match target.host_io() {
12+
Some(ops) => ops,
13+
None => return Ok(HandlerStatus::Handled),
14+
};
15+
16+
crate::__dead_code_marker!("host_io", "impl");
17+
18+
let handler_status = match command {
19+
HostIo::vFileOpen(cmd) => {
20+
let ret = ops.open(cmd.filename, cmd.flags, cmd.mode);
21+
res.write_str("F")?;
22+
res.write_num(ret)?;
23+
HandlerStatus::Handled
24+
}
25+
HostIo::vFileClose(cmd) => {
26+
let ret = ops.close(cmd.fd);
27+
res.write_str("F")?;
28+
res.write_num(ret)?;
29+
HandlerStatus::Handled
30+
}
31+
HostIo::vFilePread(cmd) => {
32+
let data = ops.pread(cmd.fd, cmd.count, cmd.offset);
33+
res.write_str("F")?;
34+
res.write_num(data.len())?;
35+
res.write_str(";")?;
36+
res.write_binary(data)?;
37+
HandlerStatus::Handled
38+
}
39+
HostIo::vFileSetfs(cmd) => {
40+
let ret = ops.setfs(cmd.fd);
41+
res.write_str("F")?;
42+
res.write_num(ret)?;
43+
HandlerStatus::Handled
44+
}
45+
};
46+
47+
Ok(handler_status)
48+
}
49+
}

src/gdbstub_impl/ext/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ mod prelude {
1414
mod base;
1515
mod breakpoints;
1616
mod catch_syscalls;
17+
mod exec_file;
1718
mod extended_mode;
19+
mod host_io;
1820
mod memory_map;
1921
mod monitor_cmd;
2022
mod reverse_exec;

src/gdbstub_impl/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,8 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
579579
Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd),
580580
Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd),
581581
Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd),
582+
Command::HostIo(cmd) => self.handle_host_io(res, target, cmd),
583+
Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd),
582584
}
583585
}
584586
}

src/protocol/commands.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,17 @@ commands! {
223223
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead,
224224
}
225225

226+
exec_file {
227+
"qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead,
228+
}
229+
230+
host_io use 'a{
231+
"vFile:open" => _vFile_open::vFileOpen<'a>,
232+
"vFile:close" => _vFile_close::vFileClose,
233+
"vFile:pread" => _vFile_pread::vFilePread,
234+
"vFile:setfs" => _vFile_setfs::vFileSetfs,
235+
}
236+
226237
catch_syscalls use 'a {
227238
"QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>,
228239
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct qXferExecFileRead {
5+
pub pid: Option<u64>,
6+
pub offset: usize,
7+
pub len: usize,
8+
}
9+
10+
impl<'a> ParseCommand<'a> for qXferExecFileRead {
11+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
12+
let body = buf.into_body();
13+
14+
if body.is_empty() {
15+
return None;
16+
}
17+
18+
let mut body = body.split(|b| *b == b':').skip(1);
19+
let pid = decode_hex::<u64>(body.next()?).ok();
20+
21+
let mut body = body.next()?.split(|b| *b == b',');
22+
let offset = decode_hex(body.next()?).ok()?;
23+
let len = decode_hex(body.next()?).ok()?;
24+
25+
Some(qXferExecFileRead {pid, offset, len})
26+
}
27+
}

src/protocol/commands/_vFile_close.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct vFileClose {
5+
pub fd: usize,
6+
}
7+
8+
impl<'a> ParseCommand<'a> for vFileClose {
9+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
10+
let body = buf.into_body();
11+
if body.is_empty() {
12+
return None;
13+
}
14+
15+
match body {
16+
[b':', body @ ..] => {
17+
let fd = decode_hex(body).ok()?;
18+
Some(vFileClose{fd})
19+
},
20+
_ => None,
21+
}
22+
}
23+
}

src/protocol/commands/_vFile_open.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct vFileOpen<'a> {
5+
pub filename: &'a [u8],
6+
pub flags: u64,
7+
pub mode: u64,
8+
}
9+
10+
impl<'a> ParseCommand<'a> for vFileOpen<'a> {
11+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
12+
let body = buf.into_body();
13+
if body.is_empty() {
14+
return None;
15+
}
16+
17+
match body {
18+
[b':', body @ ..] => {
19+
let mut body = body.splitn_mut_no_panic(3, |b| *b == b',');
20+
let filename = decode_hex_buf(body.next()?).ok()?;
21+
let flags= decode_hex(body.next()?).ok()?;
22+
let mode= decode_hex(body.next()?).ok()?;
23+
Some(vFileOpen{filename, flags, mode})
24+
},
25+
_ => None,
26+
}
27+
}
28+
}

src/protocol/commands/_vFile_pread.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct vFilePread {
5+
pub fd: usize,
6+
pub count: usize,
7+
pub offset: usize,
8+
}
9+
10+
impl<'a> ParseCommand<'a> for vFilePread {
11+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
12+
let body = buf.into_body();
13+
if body.is_empty() {
14+
return None;
15+
}
16+
17+
match body {
18+
[b':', body @ ..] => {
19+
let mut body = body.splitn_mut_no_panic(3, |b| *b == b',');
20+
let fd = decode_hex(body.next()?).ok()?;
21+
let count= decode_hex(body.next()?).ok()?;
22+
let offset= decode_hex(body.next()?).ok()?;
23+
Some(vFilePread{fd, count, offset})
24+
},
25+
_ => None,
26+
}
27+
}
28+
}

src/protocol/commands/_vFile_setfs.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use super::prelude::*;
2+
3+
#[derive(Debug)]
4+
pub struct vFileSetfs {
5+
pub fd: usize,
6+
}
7+
8+
impl<'a> ParseCommand<'a> for vFileSetfs {
9+
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
10+
let body = buf.into_body();
11+
if body.is_empty() {
12+
return None;
13+
}
14+
15+
match body {
16+
[b':', body @ ..] => {
17+
let fd = decode_hex(body).ok()?;
18+
Some(vFileSetfs{fd})
19+
},
20+
_ => None,
21+
}
22+
}
23+
}

src/target/ext/exec_file.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//! Provide exec-file path for the target.
2+
use crate::target::Target;
3+
4+
/// Target Extension - Provide current exec-file.
5+
pub trait ExecFile: Target {
6+
/// Return the full absolute name of the file that was executed to create a
7+
/// process running on the remote system.
8+
fn get_exec_file(&self, pid: Option<u64>) -> &[u8];
9+
}
10+
11+
define_ext!(ExecFileOps, ExecFile);

src/target/ext/host_io.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Provide Host I/O operations for the target.
2+
use crate::target::Target;
3+
4+
/// Target Extension - Perform I/O operations on host
5+
pub trait HostIo: Target {
6+
/// Open a file at filename and return a file descriptor for it, or return
7+
/// -1 if an error occurs.
8+
fn open(&self, filename: &[u8], flags: u64, mode: u64) -> i64;
9+
/// Close the open file corresponding to fd and return 0, or -1 if an error
10+
/// occurs.
11+
fn close(&self, fd: usize) -> i64;
12+
/// Read data from the open file corresponding to fd.
13+
fn pread(&self, fd: usize, count: usize, offset: usize) -> &[u8];
14+
/// Select the filesystem on which vFile operations with filename arguments
15+
/// will operate.
16+
fn setfs(&self, fd: usize) -> i64;
17+
}
18+
19+
define_ext!(HostIoOps, HostIo);

src/target/ext/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,9 @@ macro_rules! define_ext {
259259
pub mod base;
260260
pub mod breakpoints;
261261
pub mod catch_syscalls;
262+
pub mod exec_file;
262263
pub mod extended_mode;
264+
pub mod host_io;
263265
pub mod memory_map;
264266
pub mod monitor_cmd;
265267
pub mod section_offsets;

0 commit comments

Comments
 (0)