gdbstub/target/ext/
host_io.rs

1//! Provide Host I/O operations for the target.
2use crate::arch::Arch;
3use crate::target::Target;
4use 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)]
14pub struct HostIoOpenFlags(u32);
15
16bitflags! {
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)]
50pub struct HostIoOpenMode(u32);
51
52bitflags! {
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)]
84pub 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)]
116pub 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)]
128pub 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    /// Input/output error (POSIX.1-2001).
140    EIO = 5,
141    /// Bad file descriptor (POSIX.1-2001).
142    EBADF = 9,
143    /// Permission denied (POSIX.1-2001).
144    EACCES = 13,
145    /// Bad address (POSIX.1-2001).
146    EFAULT = 14,
147    /// Device or resource busy (POSIX.1-2001).
148    EBUSY = 16,
149    /// File exists (POSIX.1-2001).
150    EEXIST = 17,
151    /// No such device (POSIX.1-2001).
152    ENODEV = 19,
153    /// Not a directory (POSIX.1-2001).
154    ENOTDIR = 20,
155    /// Is a directory (POSIX.1-2001).
156    EISDIR = 21,
157    /// Invalid argument (POSIX.1-2001).
158    EINVAL = 22,
159    /// Too many open files in system (POSIX.1-2001). On Linux, this is probably
160    /// a result of encountering the /proc/sys/fs/file-max limit (see proc(5)).
161    ENFILE = 23,
162    /// Too many open files (POSIX.1-2001). Commonly caused by exceeding the
163    /// RLIMIT_NOFILE resource limit described in getrlimit(2).
164    EMFILE = 24,
165    /// File too large (POSIX.1-2001).
166    EFBIG = 27,
167    /// No space left on device (POSIX.1-2001).
168    ENOSPC = 28,
169    /// Invalid seek (POSIX.1-2001).
170    ESPIPE = 29,
171    /// Read-only filesystem (POSIX.1-2001).
172    EROFS = 30,
173    /// Function not implemented (POSIX.1-2001).
174    ENOSYS = 88,
175    /// Filename too long (POSIX.1-2001).
176    ENAMETOOLONG = 91,
177    /// Unknown errno - there may not be a GDB mapping for this value
178    EUNKNOWN = 9999,
179}
180
181/// The error type for Host I/O operations.
182pub enum HostIoError<E> {
183    /// An operation-specific non-fatal error code.
184    ///
185    /// See [`HostIoErrno`] for more details.
186    Errno(HostIoErrno),
187    /// A target-specific fatal error.
188    ///
189    /// **WARNING:** Returning this error will immediately halt the target's
190    /// execution and return a [`GdbStubError`](crate::stub::GdbStubError)!
191    ///
192    /// Note that returning this error will _not_ notify the GDB client that the
193    /// debugging session has been terminated, making it possible to resume
194    /// execution after resolving the error and/or setting up a post-mortem
195    /// debugging environment.
196    Fatal(E),
197}
198
199/// When the `std` feature is enabled, `HostIoError` implements
200/// `From<std::io::Error>`, mapping [`std::io::ErrorKind`] to the appropriate
201/// [`HostIoErrno`] when possible, and falling back to [`HostIoErrno::EUNKNOWN`]
202/// when no mapping exists.
203#[cfg(feature = "std")]
204impl<E> From<std::io::Error> for HostIoError<E> {
205    fn from(e: std::io::Error) -> HostIoError<E> {
206        use std::io::ErrorKind::*;
207        let errno = match e.kind() {
208            PermissionDenied => HostIoErrno::EPERM,
209            NotFound => HostIoErrno::ENOENT,
210            Interrupted => HostIoErrno::EINTR,
211            AlreadyExists => HostIoErrno::EEXIST,
212            InvalidInput => HostIoErrno::EINVAL,
213            _ => HostIoErrno::EUNKNOWN,
214        };
215        HostIoError::Errno(errno)
216    }
217}
218
219/// A specialized `Result` type for Host I/O operations. Supports reporting
220/// non-fatal errors back to the GDB client.
221///
222/// See [`HostIoError`] for more details.
223pub type HostIoResult<T, Tgt> = Result<T, HostIoError<<Tgt as Target>::Error>>;
224
225/// Target Extension - Perform I/O operations on host
226pub trait HostIo: Target {
227    /// Support `open` operation.
228    #[inline(always)]
229    fn support_open(&mut self) -> Option<HostIoOpenOps<'_, Self>> {
230        None
231    }
232
233    /// Support `close` operation.
234    #[inline(always)]
235    fn support_close(&mut self) -> Option<HostIoCloseOps<'_, Self>> {
236        None
237    }
238
239    /// Support `pread` operation.
240    #[inline(always)]
241    fn support_pread(&mut self) -> Option<HostIoPreadOps<'_, Self>> {
242        None
243    }
244
245    /// Support `pwrite` operation.
246    #[inline(always)]
247    fn support_pwrite(&mut self) -> Option<HostIoPwriteOps<'_, Self>> {
248        None
249    }
250
251    /// Support `fstat` operation.
252    #[inline(always)]
253    fn support_fstat(&mut self) -> Option<HostIoFstatOps<'_, Self>> {
254        None
255    }
256
257    /// Support `unlink` operation.
258    #[inline(always)]
259    fn support_unlink(&mut self) -> Option<HostIoUnlinkOps<'_, Self>> {
260        None
261    }
262
263    /// Support `readlink` operation.
264    #[inline(always)]
265    fn support_readlink(&mut self) -> Option<HostIoReadlinkOps<'_, Self>> {
266        None
267    }
268
269    /// Support `setfs` operation.
270    #[inline(always)]
271    fn support_setfs(&mut self) -> Option<HostIoSetfsOps<'_, Self>> {
272        None
273    }
274}
275
276define_ext!(HostIoOps, HostIo);
277
278/// Nested Target Extension - Host I/O open operation.
279pub trait HostIoOpen: HostIo {
280    /// Open a file at `filename` and return a file descriptor for it, or return
281    /// [`HostIoError::Errno`] if an error occurs.
282    ///
283    /// `flags` are the flags used when opening the file (see
284    /// [`HostIoOpenFlags`]), and `mode` is the mode used if the file is
285    /// created (see [`HostIoOpenMode`]).
286    fn open(
287        &mut self,
288        filename: &[u8],
289        flags: HostIoOpenFlags,
290        mode: HostIoOpenMode,
291    ) -> HostIoResult<u32, Self>;
292}
293
294define_ext!(HostIoOpenOps, HostIoOpen);
295
296/// Nested Target Extension - Host I/O close operation.
297pub trait HostIoClose: HostIo {
298    /// Close the open file corresponding to `fd`.
299    fn close(&mut self, fd: u32) -> HostIoResult<(), Self>;
300}
301
302define_ext!(HostIoCloseOps, HostIoClose);
303
304/// Nested Target Extension - Host I/O pread operation.
305pub trait HostIoPread: HostIo {
306    /// Read data from the open file corresponding to `fd`.
307    ///
308    /// Up to `count` bytes will be read from the file, starting at `offset`
309    /// relative to the start of the file.
310    ///
311    /// Return the number of bytes written into `buf` (which may be less than
312    /// `count`).
313    ///
314    /// If `offset` is greater than the length of the underlying data, return
315    /// `Ok(0)`.
316    fn pread(
317        &mut self,
318        fd: u32,
319        count: usize,
320        offset: u64,
321        buf: &mut [u8],
322    ) -> HostIoResult<usize, Self>;
323}
324
325define_ext!(HostIoPreadOps, HostIoPread);
326
327/// Nested Target Extension - Host I/O pwrite operation.
328pub trait HostIoPwrite: HostIo {
329    /// Write `data` to the open file corresponding to `fd`.
330    ///
331    /// Start the write at `offset` from the start of the file.
332    ///
333    /// Return the number of bytes written, which may be shorter
334    /// than the length of data, or [`HostIoError::Errno`] if an error occurred.
335    fn pwrite(
336        &mut self,
337        fd: u32,
338        offset: <Self::Arch as Arch>::Usize,
339        data: &[u8],
340    ) -> HostIoResult<<Self::Arch as Arch>::Usize, Self>;
341}
342
343define_ext!(HostIoPwriteOps, HostIoPwrite);
344
345/// Nested Target Extension - Host I/O fstat operation.
346pub trait HostIoFstat: HostIo {
347    /// Get information about the open file corresponding to `fd`.
348    ///
349    /// On success return a [`HostIoStat`] struct.
350    /// Return [`HostIoError::Errno`] if an error occurs.
351    fn fstat(&mut self, fd: u32) -> HostIoResult<HostIoStat, Self>;
352}
353
354define_ext!(HostIoFstatOps, HostIoFstat);
355
356/// Nested Target Extension - Host I/O unlink operation.
357pub trait HostIoUnlink: HostIo {
358    /// Delete the file at `filename` on the target.
359    fn unlink(&mut self, filename: &[u8]) -> HostIoResult<(), Self>;
360}
361
362define_ext!(HostIoUnlinkOps, HostIoUnlink);
363
364/// Nested Target Extension - Host I/O readlink operation.
365pub trait HostIoReadlink: HostIo {
366    /// Read value of symbolic link `filename` on the target.
367    ///
368    /// Return the number of bytes written into `buf`.
369    ///
370    /// Unlike most other Host IO handlers, if the resolved file path exceeds
371    /// the length of the provided `buf`, the target should NOT return a
372    /// partial response, and MUST return a `Err(HostIoErrno::ENAMETOOLONG)`.
373    fn readlink(&mut self, filename: &[u8], buf: &mut [u8]) -> HostIoResult<usize, Self>;
374}
375
376define_ext!(HostIoReadlinkOps, HostIoReadlink);
377
378/// Nested Target Extension - Host I/O setfs operation.
379pub trait HostIoSetfs: HostIo {
380    /// Select the filesystem on which vFile operations with filename arguments
381    /// will operate. This is required for GDB to be able to access files on
382    /// remote targets where the remote stub does not share a common filesystem
383    /// with the inferior(s).
384    ///
385    /// See [`FsKind`] for the meaning of `fs`.
386    ///
387    /// If setfs indicates success, the selected filesystem remains selected
388    /// until the next successful setfs operation.
389    fn setfs(&mut self, fs: FsKind) -> HostIoResult<(), Self>;
390}
391
392define_ext!(HostIoSetfsOps, HostIoSetfs);