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