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.3"
daniel5151 marked this conversation as resolved.
Show resolved Hide resolved
cfg-if = "0.1.10"
log = "0.4"
managed = { version = "0.8", default-features = false }
Expand Down
4 changes: 3 additions & 1 deletion 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 Expand Up @@ -160,7 +162,7 @@ If you happen to stumble across this crate and end up using it to debug some bar

- When the `paranoid_unsafe` feature is enabled, the following `unsafe` code is _removed_:
- `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.
- `src/protocol/common/hex`: Use an alternate implementation of `decode_hex_buf` which uses unsafe slice indexing.
- `src/protocol/common/hex`: Use an alternate implementation of `decode_hex_buf`/`decode_bin_buf` which uses unsafe slice indexing.

- When the `std` feature is enabled:
- `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.
Expand Down
2 changes: 2 additions & 0 deletions examples/armv4t/emu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Emu {

pub(crate) watchpoints: Vec<u32>,
pub(crate) breakpoints: Vec<u32>,
pub(crate) files: Vec<Option<std::fs::File>>,
}

impl Emu {
Expand Down Expand Up @@ -72,6 +73,7 @@ impl Emu {

watchpoints: Vec::new(),
breakpoints: Vec::new(),
files: Vec::new(),
})
}

Expand Down
228 changes: 228 additions & 0 deletions examples/armv4t/gdb/host_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
use std::io::{Read, Seek, Write};

use gdbstub::target;
use gdbstub::target::ext::host_io::{
FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoOutput, HostIoResult,
HostIoStat, HostIoToken,
};

use crate::emu::Emu;

impl target::ext::host_io::HostIo for Emu {
#[inline(always)]
fn enable_open(&mut self) -> Option<target::ext::host_io::HostIoOpenOps<Self>> {
Some(self)
}

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

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

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

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

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

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

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

impl target::ext::host_io::HostIoOpen for Emu {
fn open(
&mut self,
filename: &[u8],
flags: HostIoOpenFlags,
_mode: HostIoOpenMode,
) -> HostIoResult<u32, Self> {
if filename.starts_with(b"/proc") {
return Err(HostIoError::Errno(HostIoErrno::ENOENT));
}

let path =
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;

let mut read = false;
let mut write = false;
if flags.contains(HostIoOpenFlags::O_RDWR) {
read = true;
write = true;
} else if flags.contains(HostIoOpenFlags::O_WRONLY) {
write = true;
} else {
read = true;
}

let file = std::fs::OpenOptions::new()
.read(read)
.write(write)
.append(flags.contains(HostIoOpenFlags::O_APPEND))
.create(flags.contains(HostIoOpenFlags::O_CREAT))
.truncate(flags.contains(HostIoOpenFlags::O_TRUNC))
.create_new(flags.contains(HostIoOpenFlags::O_EXCL))
.open(path)?;

let n = match self.files.iter_mut().enumerate().find(|(_, f)| f.is_none()) {
Some((n, free_file)) => {
*free_file = Some(file);
n
}
None => {
self.files.push(Some(file));
self.files.len() - 1
}
};

Ok(n as u32)
}
}

impl target::ext::host_io::HostIoClose for Emu {
fn close(&mut self, fd: u32) -> HostIoResult<(), Self> {
let file = match self.files.get_mut(fd as usize) {
Some(file) => file,
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
};

file.take().ok_or(HostIoError::Errno(HostIoErrno::EBADF))?;
while let Some(None) = self.files.last() {
self.files.pop();
}
Ok(())
}
}

impl target::ext::host_io::HostIoPread for Emu {
fn pread<'a>(
&mut self,
fd: u32,
count: u32,
offset: u32,
output: HostIoOutput<'a>,
) -> HostIoResult<HostIoToken<'a>, Self> {
let file = match self.files.get_mut(fd as usize) {
Some(Some(file)) => file,
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
};

let mut buffer = vec![0; count as usize];
file.seek(std::io::SeekFrom::Start(offset as u64))?;
let n = file.read(&mut buffer)?;
Ok(output.write(&buffer[..n]))
}
}

impl target::ext::host_io::HostIoPwrite for Emu {
fn pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult<u32, Self> {
let file = match self.files.get_mut(fd as usize) {
Some(Some(file)) => file,
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
};

file.seek(std::io::SeekFrom::Start(offset as u64))?;
let n = file.write(data)?;
Ok(n as u32)
}
}

impl target::ext::host_io::HostIoFstat for Emu {
fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self> {
let metadata = match self.files.get(fd as usize) {
Some(Some(file)) => file.metadata()?,
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
};

macro_rules! time_to_secs {
($time:expr) => {
$time
.map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))?
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))?
.as_secs() as u32
};
}
let atime = time_to_secs!(metadata.accessed());
let mtime = time_to_secs!(metadata.modified());
let ctime = time_to_secs!(metadata.created());

Ok(HostIoStat {
st_dev: 0,
st_ino: 0,
st_mode: HostIoOpenMode::empty(),
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
st_size: metadata.len(),
st_blksize: 0,
st_blocks: 0,
st_atime: atime,
st_mtime: mtime,
st_ctime: ctime,
})
}
}

impl target::ext::host_io::HostIoUnlink for Emu {
fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self> {
let path =
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;
std::fs::remove_file(path)?;
Ok(())
}
}

impl target::ext::host_io::HostIoReadlink for Emu {
fn readlink<'a>(
&mut self,
filename: &[u8],
output: HostIoOutput<'a>,
) -> HostIoResult<HostIoToken<'a>, Self> {
if filename == b"/proc/1/exe" {
// Support `info proc exe` command
return Ok(output.write(b"/test.elf"));
} else if filename == b"/proc/1/cwd" {
// Support `info proc cwd` command
return Ok(output.write(b"/"));
} else if filename.starts_with(b"/proc") {
return Err(HostIoError::Errno(HostIoErrno::ENOENT));
}

let path =
std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?;
Ok(output.write(
std::fs::read_link(path)?
.to_str()
.ok_or(HostIoError::Errno(HostIoErrno::ENOENT))?
.as_bytes(),
))
}
}

impl target::ext::host_io::HostIoSetfs for Emu {
fn setfs(&mut self, _fs: FsKind) -> HostIoResult<(), Self> {
Ok(())
}
}
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
Loading