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, HostIoResult, HostIoStat, 6 }; 7 8 use super::{copy_range_to_buf, copy_to_buf}; 9 use crate::emu::Emu; 10 use crate::TEST_PROGRAM_ELF; 11 12 const FD_RESERVED: u32 = 1; 13 14 impl target::ext::host_io::HostIo for Emu { 15 #[inline(always)] support_open(&mut self) -> Option<target::ext::host_io::HostIoOpenOps<'_, Self>>16 fn support_open(&mut self) -> Option<target::ext::host_io::HostIoOpenOps<'_, Self>> { 17 Some(self) 18 } 19 20 #[inline(always)] support_close(&mut self) -> Option<target::ext::host_io::HostIoCloseOps<'_, Self>>21 fn support_close(&mut self) -> Option<target::ext::host_io::HostIoCloseOps<'_, Self>> { 22 Some(self) 23 } 24 25 #[inline(always)] support_pread(&mut self) -> Option<target::ext::host_io::HostIoPreadOps<'_, Self>>26 fn support_pread(&mut self) -> Option<target::ext::host_io::HostIoPreadOps<'_, Self>> { 27 Some(self) 28 } 29 30 #[inline(always)] support_pwrite(&mut self) -> Option<target::ext::host_io::HostIoPwriteOps<'_, Self>>31 fn support_pwrite(&mut self) -> Option<target::ext::host_io::HostIoPwriteOps<'_, Self>> { 32 Some(self) 33 } 34 35 #[inline(always)] support_fstat(&mut self) -> Option<target::ext::host_io::HostIoFstatOps<'_, Self>>36 fn support_fstat(&mut self) -> Option<target::ext::host_io::HostIoFstatOps<'_, Self>> { 37 Some(self) 38 } 39 40 #[inline(always)] support_unlink(&mut self) -> Option<target::ext::host_io::HostIoUnlinkOps<'_, Self>>41 fn support_unlink(&mut self) -> Option<target::ext::host_io::HostIoUnlinkOps<'_, Self>> { 42 Some(self) 43 } 44 45 #[inline(always)] support_readlink(&mut self) -> Option<target::ext::host_io::HostIoReadlinkOps<'_, Self>>46 fn support_readlink(&mut self) -> Option<target::ext::host_io::HostIoReadlinkOps<'_, Self>> { 47 Some(self) 48 } 49 50 #[inline(always)] support_setfs(&mut self) -> Option<target::ext::host_io::HostIoSetfsOps<'_, Self>>51 fn support_setfs(&mut self) -> Option<target::ext::host_io::HostIoSetfsOps<'_, Self>> { 52 Some(self) 53 } 54 } 55 56 impl target::ext::host_io::HostIoOpen for Emu { open( &mut self, filename: &[u8], flags: HostIoOpenFlags, _mode: HostIoOpenMode, ) -> HostIoResult<u32, Self>57 fn open( 58 &mut self, 59 filename: &[u8], 60 flags: HostIoOpenFlags, 61 _mode: HostIoOpenMode, 62 ) -> HostIoResult<u32, Self> { 63 if filename.starts_with(b"/proc") { 64 return Err(HostIoError::Errno(HostIoErrno::ENOENT)); 65 } 66 67 // In this example, the test binary is compiled into the binary itself as the 68 // `TEST_PROGRAM_ELF` array using `include_bytes!`. As such, we must "spoof" the 69 // existence of a real file, which will actually be backed by the in-binary 70 // `TEST_PROGRAM_ELF` array. 71 if filename == b"/test.elf" { 72 return Ok(0); 73 } 74 75 let path = 76 std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; 77 78 let mut read = false; 79 let mut write = false; 80 if flags.contains(HostIoOpenFlags::O_RDWR) { 81 read = true; 82 write = true; 83 } else if flags.contains(HostIoOpenFlags::O_WRONLY) { 84 write = true; 85 } else { 86 read = true; 87 } 88 89 let file = std::fs::OpenOptions::new() 90 .read(read) 91 .write(write) 92 .append(flags.contains(HostIoOpenFlags::O_APPEND)) 93 .create(flags.contains(HostIoOpenFlags::O_CREAT)) 94 .truncate(flags.contains(HostIoOpenFlags::O_TRUNC)) 95 .create_new(flags.contains(HostIoOpenFlags::O_EXCL)) 96 .open(path)?; 97 98 let n = match self.files.iter_mut().enumerate().find(|(_, f)| f.is_none()) { 99 Some((n, free_file)) => { 100 *free_file = Some(file); 101 n 102 } 103 None => { 104 self.files.push(Some(file)); 105 self.files.len() - 1 106 } 107 }; 108 109 Ok(n as u32 + FD_RESERVED) 110 } 111 } 112 113 impl target::ext::host_io::HostIoClose for Emu { close(&mut self, fd: u32) -> HostIoResult<(), Self>114 fn close(&mut self, fd: u32) -> HostIoResult<(), Self> { 115 if fd < FD_RESERVED { 116 return Ok(()); 117 } 118 119 let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { 120 Some(file) => file, 121 _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), 122 }; 123 124 file.take().ok_or(HostIoError::Errno(HostIoErrno::EBADF))?; 125 while let Some(None) = self.files.last() { 126 self.files.pop(); 127 } 128 Ok(()) 129 } 130 } 131 132 impl target::ext::host_io::HostIoPread for Emu { pread<'a>( &mut self, fd: u32, count: usize, offset: u64, buf: &mut [u8], ) -> HostIoResult<usize, Self>133 fn pread<'a>( 134 &mut self, 135 fd: u32, 136 count: usize, 137 offset: u64, 138 buf: &mut [u8], 139 ) -> HostIoResult<usize, Self> { 140 if fd < FD_RESERVED { 141 if fd == 0 { 142 return Ok(copy_range_to_buf(TEST_PROGRAM_ELF, offset, count, buf)); 143 } else { 144 return Err(HostIoError::Errno(HostIoErrno::EBADF)); 145 } 146 } 147 148 let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { 149 Some(Some(file)) => file, 150 _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), 151 }; 152 153 file.seek(std::io::SeekFrom::Start(offset))?; 154 let n = file.read(buf)?; 155 Ok(n) 156 } 157 } 158 159 impl target::ext::host_io::HostIoPwrite for Emu { pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult<u32, Self>160 fn pwrite(&mut self, fd: u32, offset: u32, data: &[u8]) -> HostIoResult<u32, Self> { 161 if fd < FD_RESERVED { 162 return Err(HostIoError::Errno(HostIoErrno::EACCES)); 163 } 164 165 let file = match self.files.get_mut((fd - FD_RESERVED) as usize) { 166 Some(Some(file)) => file, 167 _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), 168 }; 169 170 file.seek(std::io::SeekFrom::Start(offset as u64))?; 171 let n = file.write(data)?; 172 Ok(n as u32) 173 } 174 } 175 176 impl target::ext::host_io::HostIoFstat for Emu { fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self>177 fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self> { 178 if fd < FD_RESERVED { 179 if fd == 0 { 180 return Ok(HostIoStat { 181 st_dev: 0, 182 st_ino: 0, 183 st_mode: HostIoOpenMode::empty(), 184 st_nlink: 0, 185 st_uid: 0, 186 st_gid: 0, 187 st_rdev: 0, 188 st_size: TEST_PROGRAM_ELF.len() as u64, 189 st_blksize: 0, 190 st_blocks: 0, 191 st_atime: 0, 192 st_mtime: 0, 193 st_ctime: 0, 194 }); 195 } else { 196 return Err(HostIoError::Errno(HostIoErrno::EBADF)); 197 } 198 } 199 let metadata = match self.files.get((fd - FD_RESERVED) as usize) { 200 Some(Some(file)) => file.metadata()?, 201 _ => return Err(HostIoError::Errno(HostIoErrno::EBADF)), 202 }; 203 204 macro_rules! time_to_secs { 205 ($time:expr) => { 206 $time 207 .map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))? 208 .duration_since(std::time::SystemTime::UNIX_EPOCH) 209 .map_err(|_| HostIoError::Errno(HostIoErrno::EACCES))? 210 .as_secs() as u32 211 }; 212 } 213 let atime = time_to_secs!(metadata.accessed()); 214 let mtime = time_to_secs!(metadata.modified()); 215 let ctime = time_to_secs!(metadata.created()); 216 217 Ok(HostIoStat { 218 st_dev: 0, 219 st_ino: 0, 220 st_mode: HostIoOpenMode::empty(), 221 st_nlink: 0, 222 st_uid: 0, 223 st_gid: 0, 224 st_rdev: 0, 225 st_size: metadata.len(), 226 st_blksize: 0, 227 st_blocks: 0, 228 st_atime: atime, 229 st_mtime: mtime, 230 st_ctime: ctime, 231 }) 232 } 233 } 234 235 impl target::ext::host_io::HostIoUnlink for Emu { unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self>236 fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self> { 237 let path = 238 std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; 239 std::fs::remove_file(path)?; 240 Ok(()) 241 } 242 } 243 244 impl target::ext::host_io::HostIoReadlink for Emu { readlink<'a>(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self>245 fn readlink<'a>(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self> { 246 if filename == b"/proc/1/exe" { 247 // Support `info proc exe` command 248 let exe = b"/test.elf"; 249 return Ok(copy_to_buf(exe, buf)); 250 } else if filename == b"/proc/1/cwd" { 251 // Support `info proc cwd` command 252 let cwd = b"/"; 253 return Ok(copy_to_buf(cwd, buf)); 254 } else if filename.starts_with(b"/proc") { 255 return Err(HostIoError::Errno(HostIoErrno::ENOENT)); 256 } 257 258 let path = 259 std::str::from_utf8(filename).map_err(|_| HostIoError::Errno(HostIoErrno::ENOENT))?; 260 let link = std::fs::read_link(path)?; 261 let data = link 262 .to_str() 263 .ok_or(HostIoError::Errno(HostIoErrno::ENOENT))? 264 .as_bytes(); 265 if data.len() <= buf.len() { 266 Ok(copy_to_buf(data, buf)) 267 } else { 268 Err(HostIoError::Errno(HostIoErrno::ENAMETOOLONG)) 269 } 270 } 271 } 272 273 impl target::ext::host_io::HostIoSetfs for Emu { setfs(&mut self, _fs: FsKind) -> HostIoResult<(), Self>274 fn setfs(&mut self, _fs: FsKind) -> HostIoResult<(), Self> { 275 Ok(()) 276 } 277 } 278