use super::copy_range_to_buf; use super::copy_to_buf; use crate::emu::Emu; use crate::TEST_PROGRAM_ELF; use gdbstub::target; use gdbstub::target::ext::host_io::FsKind; use gdbstub::target::ext::host_io::HostIoErrno; use gdbstub::target::ext::host_io::HostIoError; use gdbstub::target::ext::host_io::HostIoOpenFlags; use gdbstub::target::ext::host_io::HostIoOpenMode; use gdbstub::target::ext::host_io::HostIoResult; use gdbstub::target::ext::host_io::HostIoStat; use std::io::Read; use std::io::Seek; use std::io::Write; const FD_RESERVED: u32 = 1; impl target::ext::host_io::HostIo for Emu { #[inline(always)] fn support_open(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_close(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_pread(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_pwrite(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_fstat(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_unlink(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_readlink(&mut self) -> Option> { Some(self) } #[inline(always)] fn support_setfs(&mut self) -> Option> { Some(self) } } impl target::ext::host_io::HostIoOpen for Emu { fn open( &mut self, filename: &[u8], flags: HostIoOpenFlags, _mode: HostIoOpenMode, ) -> HostIoResult { 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 { 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 { 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 { 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 { 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(()) } }