278 lines
8.5 KiB
Rust
278 lines
8.5 KiB
Rust
|
|
use std::io::{Read, Seek, Write};
|
||
|
|
|
||
|
|
use gdbstub::target;
|
||
|
|
use gdbstub::target::ext::host_io::{
|
||
|
|
FsKind, HostIoErrno, HostIoError, HostIoOpenFlags, HostIoOpenMode, HostIoResult, HostIoStat,
|
||
|
|
};
|
||
|
|
|
||
|
|
use super::{copy_range_to_buf, copy_to_buf};
|
||
|
|
use crate::emu::Emu;
|
||
|
|
use crate::TEST_PROGRAM_ELF;
|
||
|
|
|
||
|
|
const FD_RESERVED: u32 = 1;
|
||
|
|
|
||
|
|
impl target::ext::host_io::HostIo for Emu {
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_open(&mut self) -> Option<target::ext::host_io::HostIoOpenOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_close(&mut self) -> Option<target::ext::host_io::HostIoCloseOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_pread(&mut self) -> Option<target::ext::host_io::HostIoPreadOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_pwrite(&mut self) -> Option<target::ext::host_io::HostIoPwriteOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_fstat(&mut self) -> Option<target::ext::host_io::HostIoFstatOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_unlink(&mut self) -> Option<target::ext::host_io::HostIoUnlinkOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_readlink(&mut self) -> Option<target::ext::host_io::HostIoReadlinkOps<'_, Self>> {
|
||
|
|
Some(self)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[inline(always)]
|
||
|
|
fn support_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));
|
||
|
|
}
|
||
|
|
|
||
|
|
// In this example, the test binary is compiled into the binary itself as the
|
||
|
|
// `TEST_PROGRAM_ELF` array using `include_bytes!`. As such, we must "spoof" the
|
||
|
|
// existence of a real file, which will actually be backed by the in-binary
|
||
|
|
// `TEST_PROGRAM_ELF` array.
|
||
|
|
if filename == b"/test.elf" {
|
||
|
|
return Ok(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
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 + FD_RESERVED)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl target::ext::host_io::HostIoClose for Emu {
|
||
|
|
fn close(&mut self, fd: u32) -> HostIoResult<(), Self> {
|
||
|
|
if fd < FD_RESERVED {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
let file = match self.files.get_mut((fd - FD_RESERVED) 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: usize,
|
||
|
|
offset: u64,
|
||
|
|
buf: &mut [u8],
|
||
|
|
) -> HostIoResult<usize, Self> {
|
||
|
|
if fd < FD_RESERVED {
|
||
|
|
if fd == 0 {
|
||
|
|
return Ok(copy_range_to_buf(TEST_PROGRAM_ELF, offset, count, buf));
|
||
|
|
} else {
|
||
|
|
return Err(HostIoError::Errno(HostIoErrno::EBADF));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let file = match self.files.get_mut((fd - FD_RESERVED) as usize) {
|
||
|
|
Some(Some(file)) => file,
|
||
|
|
_ => return Err(HostIoError::Errno(HostIoErrno::EBADF)),
|
||
|
|
};
|
||
|
|
|
||
|
|
file.seek(std::io::SeekFrom::Start(offset))?;
|
||
|
|
let n = file.read(buf)?;
|
||
|
|
Ok(n)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl target::ext::host_io::HostIoPwrite for Emu {
|
||
|
|
fn pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult<u32, Self> {
|
||
|
|
if fd < FD_RESERVED {
|
||
|
|
return Err(HostIoError::Errno(HostIoErrno::EACCES));
|
||
|
|
}
|
||
|
|
|
||
|
|
let file = match self.files.get_mut((fd - FD_RESERVED) 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> {
|
||
|
|
if fd < FD_RESERVED {
|
||
|
|
if fd == 0 {
|
||
|
|
return 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: TEST_PROGRAM_ELF.len() as u64,
|
||
|
|
st_blksize: 0,
|
||
|
|
st_blocks: 0,
|
||
|
|
st_atime: 0,
|
||
|
|
st_mtime: 0,
|
||
|
|
st_ctime: 0,
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
return Err(HostIoError::Errno(HostIoErrno::EBADF));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let metadata = match self.files.get((fd - FD_RESERVED) 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], buf: &mut [u8]) -> HostIoResult<usize, Self> {
|
||
|
|
if filename == b"/proc/1/exe" {
|
||
|
|
// Support `info proc exe` command
|
||
|
|
let exe = b"/test.elf";
|
||
|
|
return Ok(copy_to_buf(exe, buf));
|
||
|
|
} else if filename == b"/proc/1/cwd" {
|
||
|
|
// Support `info proc cwd` command
|
||
|
|
let cwd = b"/";
|
||
|
|
return Ok(copy_to_buf(cwd, buf));
|
||
|
|
} 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))?;
|
||
|
|
let link = std::fs::read_link(path)?;
|
||
|
|
let data = link
|
||
|
|
.to_str()
|
||
|
|
.ok_or(HostIoError::Errno(HostIoErrno::ENOENT))?
|
||
|
|
.as_bytes();
|
||
|
|
if data.len() <= buf.len() {
|
||
|
|
Ok(copy_to_buf(data, buf))
|
||
|
|
} else {
|
||
|
|
Err(HostIoError::Errno(HostIoErrno::ENAMETOOLONG))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl target::ext::host_io::HostIoSetfs for Emu {
|
||
|
|
fn setfs(&mut self, _fs: FsKind) -> HostIoResult<(), Self> {
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|