• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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