1 //! Provide Host I/O operations for the target.
2 use crate::arch::Arch;
3 use crate::target::Target;
4 use bitflags::bitflags;
5 
6 /// Host flags for opening files.
7 ///
8 /// Extracted from the GDB documentation at
9 /// [Open Flags](https://sourceware.org/gdb/current/onlinedocs/gdb/Open-Flags.html#Open-Flags),
10 /// and the LLDB source code at
11 /// [`lldb/include/lldb/Host/File.h`](https://github.com/llvm/llvm-project/blob/ec642ceebc1aacc8b16249df7734b8cf90ae2963/lldb/include/lldb/Host/File.h#L47-L66)
12 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
13 #[repr(transparent)]
14 pub struct HostIoOpenFlags(u32);
15 
16 bitflags! {
17     impl HostIoOpenFlags: u32 {
18         /// A read-only file.
19         const O_RDONLY = 0x0;
20         /// A write-only file.
21         const O_WRONLY = 0x1;
22         /// A read-write file.
23         const O_RDWR = 0x2;
24         /// Append to an existing file.
25         const O_APPEND = 0x8;
26         /// Create a non-existent file.
27         const O_CREAT = 0x200;
28         /// Truncate an existing file.
29         const O_TRUNC = 0x400;
30         /// Exclusive access.
31         const O_EXCL = 0x800;
32 
33         /// LLDB extension: Do not block.
34         const O_NONBLOCK = 1 << 28;
35         /// LLDB extension: Do not follow symlinks.
36         const O_DONT_FOLLOW_SYMLINKS = 1 << 29;
37         /// LLDB extension: Close the file when executing a new process.
38         const O_CLOSE_ON_EXEC = 1 << 30;
39         /// LLDB extension: Invalid value.
40         const O_INVALID = 1 << 31;
41     }
42 }
43 
44 /// Host file permissions.
45 ///
46 /// Extracted from the GDB documentation at
47 /// [mode_t Values](https://sourceware.org/gdb/current/onlinedocs/gdb/mode_005ft-Values.html#mode_005ft-Values)
48 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
49 #[repr(transparent)]
50 pub struct HostIoOpenMode(u32);
51 
52 bitflags! {
53     impl HostIoOpenMode: u32 {
54         /// A regular file.
55         const S_IFREG = 0o100000;
56         /// A directory.
57         const S_IFDIR = 0o40000;
58         /// User read permissions.
59         const S_IRUSR = 0o400;
60         /// User write permissions.
61         const S_IWUSR = 0o200;
62         /// User execute permissions.
63         const S_IXUSR = 0o100;
64         /// Group read permissions.
65         const S_IRGRP = 0o40;
66         /// Group write permissions
67         const S_IWGRP = 0o20;
68         /// Group execute permissions.
69         const S_IXGRP = 0o10;
70         /// World read permissions.
71         const S_IROTH = 0o4;
72         /// World write permissions
73         const S_IWOTH = 0o2;
74         /// World execute permissions.
75         const S_IXOTH = 0o1;
76     }
77 }
78 
79 /// Data returned by a host fstat request.
80 ///
81 /// Extracted from the GDB documentation at
82 /// [struct stat](https://sourceware.org/gdb/current/onlinedocs/gdb/struct-stat.html#struct-stat)
83 #[derive(Debug)]
84 pub struct HostIoStat {
85     /// The device.
86     pub st_dev: u32,
87     /// The inode.
88     pub st_ino: u32,
89     /// Protection bits.
90     pub st_mode: HostIoOpenMode,
91     /// The number of hard links.
92     pub st_nlink: u32,
93     /// The user id of the owner.
94     pub st_uid: u32,
95     /// The group id of the owner.
96     pub st_gid: u32,
97     /// The device type, if an inode device.
98     pub st_rdev: u32,
99     /// The size of the file in bytes.
100     pub st_size: u64,
101     /// The blocksize for the filesystem.
102     pub st_blksize: u64,
103     /// The number of blocks allocated.
104     pub st_blocks: u64,
105     /// The last time the file was accessed, in seconds since the epoch.
106     pub st_atime: u32,
107     /// The last time the file was modified, in seconds since the epoch.
108     pub st_mtime: u32,
109     /// The last time the file was changed, in seconds since the epoch.
110     pub st_ctime: u32,
111 }
112 
113 /// Select the filesystem vFile operations will operate on. Used by vFile setfs
114 /// command.
115 #[derive(Debug)]
116 pub enum FsKind {
117     /// Select the filesystem as seen by the remote stub.
118     Stub,
119     /// Select the filesystem as seen by process pid.
120     Pid(crate::common::Pid),
121 }
122 
123 /// Errno values for Host I/O operations.
124 ///
125 /// Extracted from the GDB documentation at
126 /// <https://sourceware.org/gdb/onlinedocs/gdb/Errno-Values.html>
127 #[derive(Debug)]
128 pub enum HostIoErrno {
129     /// Operation not permitted (POSIX.1-2001).
130     EPERM = 1,
131     /// No such file or directory (POSIX.1-2001).
132     ///
133     /// Typically, this error results when a specified pathname does not exist,
134     /// or one of the components in the directory prefix of a pathname does not
135     /// exist, or the specified pathname is a dangling symbolic link.
136     ENOENT = 2,
137     /// Interrupted function call (POSIX.1-2001); see signal(7).
138     EINTR = 4,
139     /// Bad file descriptor (POSIX.1-2001).
140     EBADF = 9,
141     /// Permission denied (POSIX.1-2001).
142     EACCES = 13,
143     /// Bad address (POSIX.1-2001).
144     EFAULT = 14,
145     /// Device or resource busy (POSIX.1-2001).
146     EBUSY = 16,
147     /// File exists (POSIX.1-2001).
148     EEXIST = 17,
149     /// No such device (POSIX.1-2001).
150     ENODEV = 19,
151     /// Not a directory (POSIX.1-2001).
152     ENOTDIR = 20,
153     /// Is a directory (POSIX.1-2001).
154     EISDIR = 21,
155     /// Invalid argument (POSIX.1-2001).
156     EINVAL = 22,
157     /// Too many open files in system (POSIX.1-2001). On Linux, this is probably
158     /// a result of encountering the /proc/sys/fs/file-max limit (see proc(5)).
159     ENFILE = 23,
160     /// Too many open files (POSIX.1-2001). Commonly caused by exceeding the
161     /// RLIMIT_NOFILE resource limit described in getrlimit(2).
162     EMFILE = 24,
163     /// File too large (POSIX.1-2001).
164     EFBIG = 27,
165     /// No space left on device (POSIX.1-2001).
166     ENOSPC = 28,
167     /// Invalid seek (POSIX.1-2001).
168     ESPIPE = 29,
169     /// Read-only filesystem (POSIX.1-2001).
170     EROFS = 30,
171     /// Filename too long (POSIX.1-2001).
172     ENAMETOOLONG = 91,
173     /// Unknown errno - there may not be a GDB mapping for this value
174     EUNKNOWN = 9999,
175 }
176 
177 /// The error type for Host I/O operations.
178 pub enum HostIoError<E> {
179     /// An operation-specific non-fatal error code.
180     ///
181     /// See [`HostIoErrno`] for more details.
182     Errno(HostIoErrno),
183     /// A target-specific fatal error.
184     ///
185     /// **WARNING:** Returning this error will immediately halt the target's
186     /// execution and return a [`GdbStubError`](crate::stub::GdbStubError)!
187     ///
188     /// Note that returning this error will _not_ notify the GDB client that the
189     /// debugging session has been terminated, making it possible to resume
190     /// execution after resolving the error and/or setting up a post-mortem
191     /// debugging environment.
192     Fatal(E),
193 }
194 
195 /// When the `std` feature is enabled, `HostIoError` implements
196 /// `From<std::io::Error>`, mapping [`std::io::ErrorKind`] to the appropriate
197 /// [`HostIoErrno`] when possible, and falling back to [`HostIoErrno::EUNKNOWN`]
198 /// when no mapping exists.
199 #[cfg(feature = "std")]
200 impl<E> From<std::io::Error> for HostIoError<E> {
from(e: std::io::Error) -> HostIoError<E>201     fn from(e: std::io::Error) -> HostIoError<E> {
202         use std::io::ErrorKind::*;
203         let errno = match e.kind() {
204             PermissionDenied => HostIoErrno::EPERM,
205             NotFound => HostIoErrno::ENOENT,
206             Interrupted => HostIoErrno::EINTR,
207             AlreadyExists => HostIoErrno::EEXIST,
208             InvalidInput => HostIoErrno::EINVAL,
209             _ => HostIoErrno::EUNKNOWN,
210         };
211         HostIoError::Errno(errno)
212     }
213 }
214 
215 /// A specialized `Result` type for Host I/O operations. Supports reporting
216 /// non-fatal errors back to the GDB client.
217 ///
218 /// See [`HostIoError`] for more details.
219 pub type HostIoResult<T, Tgt> = Result<T, HostIoError<<Tgt as Target>::Error>>;
220 
221 /// Target Extension - Perform I/O operations on host
222 pub trait HostIo: Target {
223     /// Support `open` operation.
224     #[inline(always)]
support_open(&mut self) -> Option<HostIoOpenOps<'_, Self>>225     fn support_open(&mut self) -> Option<HostIoOpenOps<'_, Self>> {
226         None
227     }
228 
229     /// Support `close` operation.
230     #[inline(always)]
support_close(&mut self) -> Option<HostIoCloseOps<'_, Self>>231     fn support_close(&mut self) -> Option<HostIoCloseOps<'_, Self>> {
232         None
233     }
234 
235     /// Support `pread` operation.
236     #[inline(always)]
support_pread(&mut self) -> Option<HostIoPreadOps<'_, Self>>237     fn support_pread(&mut self) -> Option<HostIoPreadOps<'_, Self>> {
238         None
239     }
240 
241     /// Support `pwrite` operation.
242     #[inline(always)]
support_pwrite(&mut self) -> Option<HostIoPwriteOps<'_, Self>>243     fn support_pwrite(&mut self) -> Option<HostIoPwriteOps<'_, Self>> {
244         None
245     }
246 
247     /// Support `fstat` operation.
248     #[inline(always)]
support_fstat(&mut self) -> Option<HostIoFstatOps<'_, Self>>249     fn support_fstat(&mut self) -> Option<HostIoFstatOps<'_, Self>> {
250         None
251     }
252 
253     /// Support `unlink` operation.
254     #[inline(always)]
support_unlink(&mut self) -> Option<HostIoUnlinkOps<'_, Self>>255     fn support_unlink(&mut self) -> Option<HostIoUnlinkOps<'_, Self>> {
256         None
257     }
258 
259     /// Support `readlink` operation.
260     #[inline(always)]
support_readlink(&mut self) -> Option<HostIoReadlinkOps<'_, Self>>261     fn support_readlink(&mut self) -> Option<HostIoReadlinkOps<'_, Self>> {
262         None
263     }
264 
265     /// Support `setfs` operation.
266     #[inline(always)]
support_setfs(&mut self) -> Option<HostIoSetfsOps<'_, Self>>267     fn support_setfs(&mut self) -> Option<HostIoSetfsOps<'_, Self>> {
268         None
269     }
270 }
271 
272 define_ext!(HostIoOps, HostIo);
273 
274 /// Nested Target Extension - Host I/O open operation.
275 pub trait HostIoOpen: HostIo {
276     /// Open a file at `filename` and return a file descriptor for it, or return
277     /// [`HostIoError::Errno`] if an error occurs.
278     ///
279     /// `flags` are the flags used when opening the file (see
280     /// [`HostIoOpenFlags`]), and `mode` is the mode used if the file is
281     /// created (see [`HostIoOpenMode`]).
open( &mut self, filename: &[u8], flags: HostIoOpenFlags, mode: HostIoOpenMode, ) -> HostIoResult<u32, Self>282     fn open(
283         &mut self,
284         filename: &[u8],
285         flags: HostIoOpenFlags,
286         mode: HostIoOpenMode,
287     ) -> HostIoResult<u32, Self>;
288 }
289 
290 define_ext!(HostIoOpenOps, HostIoOpen);
291 
292 /// Nested Target Extension - Host I/O close operation.
293 pub trait HostIoClose: HostIo {
294     /// Close the open file corresponding to `fd`.
close(&mut self, fd: u32) -> HostIoResult<(), Self>295     fn close(&mut self, fd: u32) -> HostIoResult<(), Self>;
296 }
297 
298 define_ext!(HostIoCloseOps, HostIoClose);
299 
300 /// Nested Target Extension - Host I/O pread operation.
301 pub trait HostIoPread: HostIo {
302     /// Read data from the open file corresponding to `fd`.
303     ///
304     /// Up to `count` bytes will be read from the file, starting at `offset`
305     /// relative to the start of the file.
306     ///
307     /// Return the number of bytes written into `buf` (which may be less than
308     /// `count`).
309     ///
310     /// If `offset` is greater than the length of the underlying data, return
311     /// `Ok(0)`.
pread( &mut self, fd: u32, count: usize, offset: u64, buf: &mut [u8], ) -> HostIoResult<usize, Self>312     fn pread(
313         &mut self,
314         fd: u32,
315         count: usize,
316         offset: u64,
317         buf: &mut [u8],
318     ) -> HostIoResult<usize, Self>;
319 }
320 
321 define_ext!(HostIoPreadOps, HostIoPread);
322 
323 /// Nested Target Extension - Host I/O pwrite operation.
324 pub trait HostIoPwrite: HostIo {
325     /// Write `data` to the open file corresponding to `fd`.
326     ///
327     /// Start the write at `offset` from the start of the file.
328     ///
329     /// Return the number of bytes written, which may be shorter
330     /// than the length of data, or [`HostIoError::Errno`] if an error occurred.
pwrite( &mut self, fd: u32, offset: <Self::Arch as Arch>::Usize, data: &[u8], ) -> HostIoResult<<Self::Arch as Arch>::Usize, Self>331     fn pwrite(
332         &mut self,
333         fd: u32,
334         offset: <Self::Arch as Arch>::Usize,
335         data: &[u8],
336     ) -> HostIoResult<<Self::Arch as Arch>::Usize, Self>;
337 }
338 
339 define_ext!(HostIoPwriteOps, HostIoPwrite);
340 
341 /// Nested Target Extension - Host I/O fstat operation.
342 pub trait HostIoFstat: HostIo {
343     /// Get information about the open file corresponding to `fd`.
344     ///
345     /// On success return a [`HostIoStat`] struct.
346     /// Return [`HostIoError::Errno`] if an error occurs.
fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self>347     fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self>;
348 }
349 
350 define_ext!(HostIoFstatOps, HostIoFstat);
351 
352 /// Nested Target Extension - Host I/O unlink operation.
353 pub trait HostIoUnlink: HostIo {
354     /// Delete the file at `filename` on the target.
unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self>355     fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self>;
356 }
357 
358 define_ext!(HostIoUnlinkOps, HostIoUnlink);
359 
360 /// Nested Target Extension - Host I/O readlink operation.
361 pub trait HostIoReadlink: HostIo {
362     /// Read value of symbolic link `filename` on the target.
363     ///
364     /// Return the number of bytes written into `buf`.
365     ///
366     /// Unlike most other Host IO handlers, if the resolved file path exceeds
367     /// the length of the provided `buf`, the target should NOT return a
368     /// partial response, and MUST return a `Err(HostIoErrno::ENAMETOOLONG)`.
readlink(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self>369     fn readlink(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self>;
370 }
371 
372 define_ext!(HostIoReadlinkOps, HostIoReadlink);
373 
374 /// Nested Target Extension - Host I/O setfs operation.
375 pub trait HostIoSetfs: HostIo {
376     /// Select the filesystem on which vFile operations with filename arguments
377     /// will operate. This is required for GDB to be able to access files on
378     /// remote targets where the remote stub does not share a common filesystem
379     /// with the inferior(s).
380     ///
381     /// See [`FsKind`] for the meaning of `fs`.
382     ///
383     /// If setfs indicates success, the selected filesystem remains selected
384     /// until the next successful setfs operation.
setfs(&mut self, fs: FsKind) -> HostIoResult<(), Self>385     fn setfs(&mut self, fs: FsKind) -> HostIoResult<(), Self>;
386 }
387 
388 define_ext!(HostIoSetfsOps, HostIoSetfs);
389