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