blob: 40d630413065bddbce0d4900dbbf6c4d17c8493e [file] [log] [blame]
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(())
}
}