Skip to content

Commit 9227dfd

Browse files
authored
Support Host I/O operations (#66)
* Support Host I/O operations * Fix argument types * Add missing Host I/O operations * Some fixes * Change return type to HostIoResult * Improve handle_hostio_result macro * Implement real filesystem access in example * Supply example with a complete real filesystem access implementation * Store files in Vec * Simplify code * Allow non-ASCII characters in packet * Optimize away the bounds checks in decode_bin_buf * Fix style
1 parent c40145c commit 9227dfd

23 files changed

+1081
-20
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ categories = ["development-tools::debugging", "embedded", "emulators", "network-
1414
exclude = ["examples/**/*.elf", "examples/**/*.o"]
1515

1616
[dependencies]
17+
bitflags = "1.3"
1718
cfg-if = "0.1.10"
1819
log = "0.4"
1920
managed = { version = "0.8", default-features = false }

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ 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+
- Perform Host I/O operations
7880

7981
_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!
8082

@@ -160,7 +162,7 @@ If you happen to stumble across this crate and end up using it to debug some bar
160162

161163
- When the `paranoid_unsafe` feature is enabled, the following `unsafe` code is _removed_:
162164
- `src/protocol/packet.rs`: Swaps a couple slice-index methods in `PacketBuf` to use `get_unchecked_mut`. The public API of struct ensures that the bounds used to index into the array remain in-bounds.
163-
- `src/protocol/common/hex`: Use an alternate implementation of `decode_hex_buf` which uses unsafe slice indexing.
165+
- `src/protocol/common/hex`: Use an alternate implementation of `decode_hex_buf`/`decode_bin_buf` which uses unsafe slice indexing.
164166

165167
- When the `std` feature is enabled:
166168
- `src/connection/impls/unixstream.rs`: An implementation of `UnixStream::peek` which uses `libc::recv`. This manual implementation will be removed once [rust-lang/rust#76923](https://github.com/rust-lang/rust/issues/76923) is stabilized.

examples/armv4t/emu.rs

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct Emu {
2525

2626
pub(crate) watchpoints: Vec<u32>,
2727
pub(crate) breakpoints: Vec<u32>,
28+
pub(crate) files: Vec<Option<std::fs::File>>,
2829
}
2930

3031
impl Emu {
@@ -72,6 +73,7 @@ impl Emu {
7273

7374
watchpoints: Vec::new(),
7475
breakpoints: Vec::new(),
76+
files: Vec::new(),
7577
})
7678
}
7779

examples/armv4t/gdb/host_io.rs

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
use std::io::{Read, Seek, Write};
2+
3+
use gdbstub::target;
4+
use gdbstub::target::ext::host_io::{
5+
FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoOutput, HostIoResult,
6+
HostIoStat, HostIoToken,
7+
};
8+
9+
use crate::emu::Emu;
10+
11+
impl target::ext::host_io::HostIo for Emu {
12+
#[inline(always)]
13+
fn enable_open(&mut self) -> Option<target::ext::host_io::HostIoOpenOps<Self>> {
14+
Some(self)
15+
}
16+
17+
#[inline(always)]
18+
fn enable_close(&mut self) -> Option<target::ext::host_io::HostIoCloseOps<Self>> {
19+
Some(self)
20+
}
21+
22+
#[inline(always)]
23+
fn enable_pread(&mut self) -> Option<target::ext::host_io::HostIoPreadOps<Self>> {
24+
Some(self)
25+
}
26+
27+
#[inline(always)]
28+
fn enable_pwrite(&mut self) -> Option<target::ext::host_io::HostIoPwriteOps<Self>> {
29+
Some(self)
30+
}
31+
32+
#[inline(always)]
33+
fn enable_fstat(&mut self) -> Option<target::ext::host_io::HostIoFstatOps<Self>> {
34+
Some(self)
35+
}
36+
37+
#[inline(always)]
38+
fn enable_unlink(&mut self) -> Option<target::ext::host_io::HostIoUnlinkOps<Self>> {
39+
Some(self)
40+
}
41+
42+
#[inline(always)]
43+
fn enable_readlink(&mut self) -> Option<target::ext::host_io::HostIoReadlinkOps<Self>> {
44+
Some(self)
45+
}
46+
47+
#[inline(always)]
48+
fn enable_setfs(&mut self) -> Option<target::ext::host_io::HostIoSetfsOps<Self>> {
49+
Some(self)
50+
}
51+
}
52+
53+
impl target::ext::host_io::HostIoOpen for Emu {
54+
fn open(
55+
&mut self,
56+
filename: &[u8],
57+
flags: HostIoOpenFlags,
58+
_mode: HostIoOpenMode,
59+
) -> HostIoResult<u32, Self> {
60+
if filename.starts_with(b"/proc") {
61+
return Err(HostIoError::Errno(HostIoErrno::ENOENT));
62+
}
63+
64+
let path =
65+
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;
66+
67+
let mut read = false;
68+
let mut write = false;
69+
if flags.contains(HostIoOpenFlags::O_RDWR) {
70+
read = true;
71+
write = true;
72+
} else if flags.contains(HostIoOpenFlags::O_WRONLY) {
73+
write = true;
74+
} else {
75+
read = true;
76+
}
77+
78+
let file = std::fs::OpenOptions::new()
79+
.read(read)
80+
.write(write)
81+
.append(flags.contains(HostIoOpenFlags::O_APPEND))
82+
.create(flags.contains(HostIoOpenFlags::O_CREAT))
83+
.truncate(flags.contains(HostIoOpenFlags::O_TRUNC))
84+
.create_new(flags.contains(HostIoOpenFlags::O_EXCL))
85+
.open(path)?;
86+
87+
let n = match self.files.iter_mut().enumerate().find(|(_, f)| f.is_none()) {
88+
Some((n, free_file)) => {
89+
*free_file = Some(file);
90+
n
91+
}
92+
None => {
93+
self.files.push(Some(file));
94+
self.files.len() - 1
95+
}
96+
};
97+
98+
Ok(n as u32)
99+
}
100+
}
101+
102+
impl target::ext::host_io::HostIoClose for Emu {
103+
fn close(&mut self, fd: u32) -> HostIoResult<(), Self> {
104+
let file = match self.files.get_mut(fd as usize) {
105+
Some(file) => file,
106+
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
107+
};
108+
109+
file.take().ok_or(HostIoError::Errno(HostIoErrno::EBADF))?;
110+
while let Some(None) = self.files.last() {
111+
self.files.pop();
112+
}
113+
Ok(())
114+
}
115+
}
116+
117+
impl target::ext::host_io::HostIoPread for Emu {
118+
fn pread<'a>(
119+
&mut self,
120+
fd: u32,
121+
count: u32,
122+
offset: u32,
123+
output: HostIoOutput<'a>,
124+
) -> HostIoResult<HostIoToken<'a>, Self> {
125+
let file = match self.files.get_mut(fd as usize) {
126+
Some(Some(file)) => file,
127+
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
128+
};
129+
130+
let mut buffer = vec![0; count as usize];
131+
file.seek(std::io::SeekFrom::Start(offset as u64))?;
132+
let n = file.read(&mut buffer)?;
133+
Ok(output.write(&buffer[..n]))
134+
}
135+
}
136+
137+
impl target::ext::host_io::HostIoPwrite for Emu {
138+
fn pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult<u32, Self> {
139+
let file = match self.files.get_mut(fd as usize) {
140+
Some(Some(file)) => file,
141+
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
142+
};
143+
144+
file.seek(std::io::SeekFrom::Start(offset as u64))?;
145+
let n = file.write(data)?;
146+
Ok(n as u32)
147+
}
148+
}
149+
150+
impl target::ext::host_io::HostIoFstat for Emu {
151+
fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self> {
152+
let metadata = match self.files.get(fd as usize) {
153+
Some(Some(file)) => file.metadata()?,
154+
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
155+
};
156+
157+
macro_rules! time_to_secs {
158+
($time:expr) => {
159+
$time
160+
.map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))?
161+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
162+
.map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))?
163+
.as_secs() as u32
164+
};
165+
}
166+
let atime = time_to_secs!(metadata.accessed());
167+
let mtime = time_to_secs!(metadata.modified());
168+
let ctime = time_to_secs!(metadata.created());
169+
170+
Ok(HostIoStat {
171+
st_dev: 0,
172+
st_ino: 0,
173+
st_mode: HostIoOpenMode::empty(),
174+
st_nlink: 0,
175+
st_uid: 0,
176+
st_gid: 0,
177+
st_rdev: 0,
178+
st_size: metadata.len(),
179+
st_blksize: 0,
180+
st_blocks: 0,
181+
st_atime: atime,
182+
st_mtime: mtime,
183+
st_ctime: ctime,
184+
})
185+
}
186+
}
187+
188+
impl target::ext::host_io::HostIoUnlink for Emu {
189+
fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self> {
190+
let path =
191+
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;
192+
std::fs::remove_file(path)?;
193+
Ok(())
194+
}
195+
}
196+
197+
impl target::ext::host_io::HostIoReadlink for Emu {
198+
fn readlink<'a>(
199+
&mut self,
200+
filename: &[u8],
201+
output: HostIoOutput<'a>,
202+
) -> HostIoResult<HostIoToken<'a>, Self> {
203+
if filename == b"/proc/1/exe" {
204+
// Support `info proc exe` command
205+
return Ok(output.write(b"/test.elf"));
206+
} else if filename == b"/proc/1/cwd" {
207+
// Support `info proc cwd` command
208+
return Ok(output.write(b"/"));
209+
} else if filename.starts_with(b"/proc") {
210+
return Err(HostIoError::Errno(HostIoErrno::ENOENT));
211+
}
212+
213+
let path =
214+
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;
215+
Ok(output.write(
216+
std::fs::read_link(path)?
217+
.to_str()
218+
.ok_or(HostIoError::Errno(HostIoErrno::ENOENT))?
219+
.as_bytes(),
220+
))
221+
}
222+
}
223+
224+
impl target::ext::host_io::HostIoSetfs for Emu {
225+
fn setfs(&mut self, _fs: FsKind) -> HostIoResult<(), Self> {
226+
Ok(())
227+
}
228+
}

examples/armv4t/gdb/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::emu::{Emu, Event};
1717
mod breakpoints;
1818
mod catch_syscalls;
1919
mod extended_mode;
20+
mod host_io;
2021
mod memory_map;
2122
mod monitor_cmd;
2223
mod section_offsets;
@@ -94,6 +95,11 @@ impl Target for Emu {
9495
fn catch_syscalls(&mut self) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<Self>> {
9596
Some(self)
9697
}
98+
99+
#[inline(always)]
100+
fn host_io(&mut self) -> Option<target::ext::host_io::HostIoOps<Self>> {
101+
Some(self)
102+
}
97103
}
98104

99105
impl Emu {

0 commit comments

Comments
 (0)