From 2cfb3a6750b730bc15dac6e0c319e10ec6045636 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Apr 2024 11:31:38 +0200 Subject: shims/linux: move epoll and eventfd into their own files, together with their FD types --- src/tools/miri/src/shims/unix/fd.rs | 2 + src/tools/miri/src/shims/unix/linux/epoll.rs | 197 ++++++++++++++++++++ src/tools/miri/src/shims/unix/linux/eventfd.rs | 122 ++++++++++++ src/tools/miri/src/shims/unix/linux/fd.rs | 204 --------------------- src/tools/miri/src/shims/unix/linux/fd/epoll.rs | 47 ----- src/tools/miri/src/shims/unix/linux/fd/event.rs | 72 -------- .../miri/src/shims/unix/linux/foreign_items.rs | 3 +- src/tools/miri/src/shims/unix/linux/mod.rs | 3 +- src/tools/miri/src/shims/unix/socket.rs | 1 - 9 files changed, 325 insertions(+), 326 deletions(-) create mode 100644 src/tools/miri/src/shims/unix/linux/epoll.rs create mode 100644 src/tools/miri/src/shims/unix/linux/eventfd.rs delete mode 100644 src/tools/miri/src/shims/unix/linux/fd.rs delete mode 100644 src/tools/miri/src/shims/unix/linux/fd/epoll.rs delete mode 100644 src/tools/miri/src/shims/unix/linux/fd/event.rs diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index bc9348ee0e8..a5fe38b902d 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -52,6 +52,8 @@ pub trait FileDescriptor: std::fmt::Debug + Any { fn dup(&mut self) -> io::Result>; fn is_tty(&self, _communicate_allowed: bool) -> bool { + // Most FDs are not tty's and the consequence of a wrong `false` are minor, + // so we use a default impl here. false } } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs new file mode 100644 index 00000000000..82e0bffff77 --- /dev/null +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -0,0 +1,197 @@ +use std::io; + +use rustc_data_structures::fx::FxHashMap; + +use crate::shims::unix::*; +use crate::*; + +/// An `Epoll` file descriptor connects file handles and epoll events +#[derive(Clone, Debug, Default)] +struct Epoll { + /// The file descriptors we are watching, and what we are watching for. + file_descriptors: FxHashMap, +} + +/// Epoll Events associate events with data. +/// These fields are currently unused by miri. +/// This matches the `epoll_event` struct defined +/// by the epoll_ctl man page. For more information +/// see the man page: +/// +/// +#[derive(Clone, Debug)] +struct EpollEvent { + #[allow(dead_code)] + events: u32, + /// `Scalar` is used to represent the + /// `epoll_data` type union. + #[allow(dead_code)] + data: Scalar, +} + +impl FileDescriptor for Epoll { + fn name(&self) -> &'static str { + "epoll" + } + + fn dup(&mut self) -> io::Result> { + Ok(Box::new(self.clone())) + } + + fn close<'tcx>( + self: Box, + _communicate_allowed: bool, + ) -> InterpResult<'tcx, io::Result> { + Ok(Ok(0)) + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// This function returns a file descriptor referring to the new `Epoll` instance. This file + /// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument + /// is 0, then this function is the same as `epoll_create()`. + /// + /// + fn epoll_create1( + &mut self, + flags: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let flags = this.read_scalar(flags)?.to_i32()?; + + let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC"); + if flags == epoll_cloexec { + // Miri does not support exec, so this flag has no effect. + } else if flags != 0 { + throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); + } + + let fd = this.machine.fds.insert_fd(Box::new(Epoll::default())); + Ok(Scalar::from_i32(fd)) + } + + /// This function performs control operations on the `Epoll` instance referred to by the file + /// descriptor `epfd`. It requests that the operation `op` be performed for the target file + /// descriptor, `fd`. + /// + /// Valid values for the op argument are: + /// `EPOLL_CTL_ADD` - Register the target file descriptor `fd` on the `Epoll` instance referred + /// to by the file descriptor `epfd` and associate the event `event` with the internal file + /// linked to `fd`. + /// `EPOLL_CTL_MOD` - Change the event `event` associated with the target file descriptor `fd`. + /// `EPOLL_CTL_DEL` - Deregister the target file descriptor `fd` from the `Epoll` instance + /// referred to by `epfd`. The `event` is ignored and can be null. + /// + /// + fn epoll_ctl( + &mut self, + epfd: &OpTy<'tcx, Provenance>, + op: &OpTy<'tcx, Provenance>, + fd: &OpTy<'tcx, Provenance>, + event: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let epfd = this.read_scalar(epfd)?.to_i32()?; + let op = this.read_scalar(op)?.to_i32()?; + let fd = this.read_scalar(fd)?.to_i32()?; + let _event = this.read_scalar(event)?.to_pointer(this)?; + + let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD"); + let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD"); + let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL"); + + if op == epoll_ctl_add || op == epoll_ctl_mod { + let event = this.deref_pointer_as(event, this.libc_ty_layout("epoll_event"))?; + + let events = this.project_field(&event, 0)?; + let events = this.read_scalar(&events)?.to_u32()?; + let data = this.project_field(&event, 1)?; + let data = this.read_scalar(&data)?; + let event = EpollEvent { events, data }; + + if let Some(epfd) = this.machine.fds.get_mut(epfd) { + let epfd = epfd + .downcast_mut::() + .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; + + epfd.file_descriptors.insert(fd, event); + Ok(Scalar::from_i32(0)) + } else { + Ok(Scalar::from_i32(this.fd_not_found()?)) + } + } else if op == epoll_ctl_del { + if let Some(epfd) = this.machine.fds.get_mut(epfd) { + let epfd = epfd + .downcast_mut::() + .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; + + epfd.file_descriptors.remove(&fd); + Ok(Scalar::from_i32(0)) + } else { + Ok(Scalar::from_i32(this.fd_not_found()?)) + } + } else { + let einval = this.eval_libc("EINVAL"); + this.set_last_error(einval)?; + Ok(Scalar::from_i32(-1)) + } + } + + /// The `epoll_wait()` system call waits for events on the `Epoll` + /// instance referred to by the file descriptor `epfd`. The buffer + /// pointed to by `events` is used to return information from the ready + /// list about file descriptors in the interest list that have some + /// events available. Up to `maxevents` are returned by `epoll_wait()`. + /// The `maxevents` argument must be greater than zero. + + /// The `timeout` argument specifies the number of milliseconds that + /// `epoll_wait()` will block. Time is measured against the + /// CLOCK_MONOTONIC clock. + + /// A call to `epoll_wait()` will block until either: + /// • a file descriptor delivers an event; + /// • the call is interrupted by a signal handler; or + /// • the timeout expires. + + /// Note that the timeout interval will be rounded up to the system + /// clock granularity, and kernel scheduling delays mean that the + /// blocking interval may overrun by a small amount. Specifying a + /// timeout of -1 causes `epoll_wait()` to block indefinitely, while + /// specifying a timeout equal to zero cause `epoll_wait()` to return + /// immediately, even if no events are available. + /// + /// On success, `epoll_wait()` returns the number of file descriptors + /// ready for the requested I/O, or zero if no file descriptor became + /// ready during the requested timeout milliseconds. On failure, + /// `epoll_wait()` returns -1 and errno is set to indicate the error. + /// + /// + fn epoll_wait( + &mut self, + epfd: &OpTy<'tcx, Provenance>, + events: &OpTy<'tcx, Provenance>, + maxevents: &OpTy<'tcx, Provenance>, + timeout: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let epfd = this.read_scalar(epfd)?.to_i32()?; + let _events = this.read_scalar(events)?.to_pointer(this)?; + let _maxevents = this.read_scalar(maxevents)?.to_i32()?; + let _timeout = this.read_scalar(timeout)?.to_i32()?; + + if let Some(epfd) = this.machine.fds.get_mut(epfd) { + let _epfd = epfd + .downcast_mut::() + .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; + + // FIXME return number of events ready when scheme for marking events ready exists + throw_unsup_format!("returning ready events from epoll_wait is not yet implemented"); + } else { + Ok(Scalar::from_i32(this.fd_not_found()?)) + } + } +} diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs new file mode 100644 index 00000000000..4e066493d27 --- /dev/null +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -0,0 +1,122 @@ +//! Linux `eventfd` implementation. +//! Currently just a stub. +use std::cell::Cell; +use std::io; + +use rustc_middle::ty::TyCtxt; +use rustc_target::abi::Endian; + +use crate::shims::unix::*; +use crate::*; + +/// A kind of file descriptor created by `eventfd`. +/// The `Event` type isn't currently written to by `eventfd`. +/// The interface is meant to keep track of objects associated +/// with a file descriptor. For more information see the man +/// page below: +/// +/// +#[derive(Debug)] +struct Event { + /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the + /// kernel. This counter is initialized with the value specified in the argument initval. + val: Cell, +} + +impl FileDescriptor for Event { + fn name(&self) -> &'static str { + "event" + } + + fn dup(&mut self) -> io::Result> { + Ok(Box::new(Event { val: self.val.clone() })) + } + + fn close<'tcx>( + self: Box, + _communicate_allowed: bool, + ) -> InterpResult<'tcx, io::Result> { + Ok(Ok(0)) + } + + /// A write call adds the 8-byte integer value supplied in + /// its buffer (in native endianness) to the counter. The maximum value that may be + /// stored in the counter is the largest unsigned 64-bit value + /// minus 1 (i.e., 0xfffffffffffffffe). If the addition would + /// cause the counter's value to exceed the maximum, then the + /// write either blocks until a read is performed on the + /// file descriptor, or fails with the error EAGAIN if the + /// file descriptor has been made nonblocking. + + /// A write fails with the error EINVAL if the size of the + /// supplied buffer is less than 8 bytes, or if an attempt is + /// made to write the value 0xffffffffffffffff. + fn write<'tcx>( + &self, + _communicate_allowed: bool, + bytes: &[u8], + tcx: TyCtxt<'tcx>, + ) -> InterpResult<'tcx, io::Result> { + let v1 = self.val.get(); + let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size + // Convert from target endianness to host endianness. + let num = match tcx.sess.target.endian { + Endian::Little => u64::from_le_bytes(bytes), + Endian::Big => u64::from_be_bytes(bytes), + }; + // FIXME handle blocking when addition results in exceeding the max u64 value + // or fail with EAGAIN if the file descriptor is nonblocking. + let v2 = v1.checked_add(num).unwrap(); + self.val.set(v2); + assert_eq!(8, bytes.len()); + Ok(Ok(8)) + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// This function creates an `Event` that is used as an event wait/notify mechanism by + /// user-space applications, and by the kernel to notify user-space applications of events. + /// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized + /// with the value specified in the `initval` argument. + /// + /// A new file descriptor referring to the `Event` is returned. The `read`, `write`, `poll`, + /// `select`, and `close` operations can be performed on the file descriptor. For more + /// information on these operations, see the man page linked below. + /// + /// The `flags` are not currently implemented for eventfd. + /// The `flags` may be bitwise ORed to change the behavior of `eventfd`: + /// `EFD_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. + /// `EFD_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the new open file description. + /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics. + /// + /// + #[expect(clippy::needless_if)] + fn eventfd( + &mut self, + val: &OpTy<'tcx, Provenance>, + flags: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let val = this.read_scalar(val)?.to_u32()?; + let flags = this.read_scalar(flags)?.to_i32()?; + + let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC"); + let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK"); + let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE"); + + if flags & (efd_cloexec | efd_nonblock | efd_semaphore) == 0 { + throw_unsup_format!("{flags} is unsupported"); + } + // FIXME handle the cloexec and nonblock flags + if flags & efd_cloexec == efd_cloexec {} + if flags & efd_nonblock == efd_nonblock {} + if flags & efd_semaphore == efd_semaphore { + throw_unsup_format!("EFD_SEMAPHORE is unsupported"); + } + + let fd = this.machine.fds.insert_fd(Box::new(Event { val: Cell::new(val.into()) })); + Ok(Scalar::from_i32(fd)) + } +} diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs deleted file mode 100644 index 7d5177e5c42..00000000000 --- a/src/tools/miri/src/shims/unix/linux/fd.rs +++ /dev/null @@ -1,204 +0,0 @@ -use std::cell::Cell; - -use crate::shims::unix::*; -use crate::*; -use epoll::{Epoll, EpollEvent}; -use event::Event; - -pub mod epoll; -pub mod event; - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// This function returns a file descriptor referring to the new `Epoll` instance. This file - /// descriptor is used for all subsequent calls to the epoll interface. If the `flags` argument - /// is 0, then this function is the same as `epoll_create()`. - /// - /// - fn epoll_create1( - &mut self, - flags: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - - let flags = this.read_scalar(flags)?.to_i32()?; - - let epoll_cloexec = this.eval_libc_i32("EPOLL_CLOEXEC"); - if flags == epoll_cloexec { - // Miri does not support exec, so this flag has no effect. - } else if flags != 0 { - throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); - } - - let fd = this.machine.fds.insert_fd(Box::new(Epoll::default())); - Ok(Scalar::from_i32(fd)) - } - - /// This function performs control operations on the `Epoll` instance referred to by the file - /// descriptor `epfd`. It requests that the operation `op` be performed for the target file - /// descriptor, `fd`. - /// - /// Valid values for the op argument are: - /// `EPOLL_CTL_ADD` - Register the target file descriptor `fd` on the `Epoll` instance referred - /// to by the file descriptor `epfd` and associate the event `event` with the internal file - /// linked to `fd`. - /// `EPOLL_CTL_MOD` - Change the event `event` associated with the target file descriptor `fd`. - /// `EPOLL_CTL_DEL` - Deregister the target file descriptor `fd` from the `Epoll` instance - /// referred to by `epfd`. The `event` is ignored and can be null. - /// - /// - fn epoll_ctl( - &mut self, - epfd: &OpTy<'tcx, Provenance>, - op: &OpTy<'tcx, Provenance>, - fd: &OpTy<'tcx, Provenance>, - event: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - - let epfd = this.read_scalar(epfd)?.to_i32()?; - let op = this.read_scalar(op)?.to_i32()?; - let fd = this.read_scalar(fd)?.to_i32()?; - let _event = this.read_scalar(event)?.to_pointer(this)?; - - let epoll_ctl_add = this.eval_libc_i32("EPOLL_CTL_ADD"); - let epoll_ctl_mod = this.eval_libc_i32("EPOLL_CTL_MOD"); - let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL"); - - if op == epoll_ctl_add || op == epoll_ctl_mod { - let event = this.deref_pointer_as(event, this.libc_ty_layout("epoll_event"))?; - - let events = this.project_field(&event, 0)?; - let events = this.read_scalar(&events)?.to_u32()?; - let data = this.project_field(&event, 1)?; - let data = this.read_scalar(&data)?; - let event = EpollEvent { events, data }; - - if let Some(epfd) = this.machine.fds.get_mut(epfd) { - let epfd = epfd - .downcast_mut::() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; - - epfd.file_descriptors.insert(fd, event); - Ok(Scalar::from_i32(0)) - } else { - Ok(Scalar::from_i32(this.fd_not_found()?)) - } - } else if op == epoll_ctl_del { - if let Some(epfd) = this.machine.fds.get_mut(epfd) { - let epfd = epfd - .downcast_mut::() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; - - epfd.file_descriptors.remove(&fd); - Ok(Scalar::from_i32(0)) - } else { - Ok(Scalar::from_i32(this.fd_not_found()?)) - } - } else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - Ok(Scalar::from_i32(-1)) - } - } - - /// The `epoll_wait()` system call waits for events on the `Epoll` - /// instance referred to by the file descriptor `epfd`. The buffer - /// pointed to by `events` is used to return information from the ready - /// list about file descriptors in the interest list that have some - /// events available. Up to `maxevents` are returned by `epoll_wait()`. - /// The `maxevents` argument must be greater than zero. - - /// The `timeout` argument specifies the number of milliseconds that - /// `epoll_wait()` will block. Time is measured against the - /// CLOCK_MONOTONIC clock. - - /// A call to `epoll_wait()` will block until either: - /// • a file descriptor delivers an event; - /// • the call is interrupted by a signal handler; or - /// • the timeout expires. - - /// Note that the timeout interval will be rounded up to the system - /// clock granularity, and kernel scheduling delays mean that the - /// blocking interval may overrun by a small amount. Specifying a - /// timeout of -1 causes `epoll_wait()` to block indefinitely, while - /// specifying a timeout equal to zero cause `epoll_wait()` to return - /// immediately, even if no events are available. - /// - /// On success, `epoll_wait()` returns the number of file descriptors - /// ready for the requested I/O, or zero if no file descriptor became - /// ready during the requested timeout milliseconds. On failure, - /// `epoll_wait()` returns -1 and errno is set to indicate the error. - /// - /// - fn epoll_wait( - &mut self, - epfd: &OpTy<'tcx, Provenance>, - events: &OpTy<'tcx, Provenance>, - maxevents: &OpTy<'tcx, Provenance>, - timeout: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - - let epfd = this.read_scalar(epfd)?.to_i32()?; - let _events = this.read_scalar(events)?.to_pointer(this)?; - let _maxevents = this.read_scalar(maxevents)?.to_i32()?; - let _timeout = this.read_scalar(timeout)?.to_i32()?; - - if let Some(epfd) = this.machine.fds.get_mut(epfd) { - let _epfd = epfd - .downcast_mut::() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; - - // FIXME return number of events ready when scheme for marking events ready exists - throw_unsup_format!("returning ready events from epoll_wait is not yet implemented"); - } else { - Ok(Scalar::from_i32(this.fd_not_found()?)) - } - } - - /// This function creates an `Event` that is used as an event wait/notify mechanism by - /// user-space applications, and by the kernel to notify user-space applications of events. - /// The `Event` contains an `u64` counter maintained by the kernel. The counter is initialized - /// with the value specified in the `initval` argument. - /// - /// A new file descriptor referring to the `Event` is returned. The `read`, `write`, `poll`, - /// `select`, and `close` operations can be performed on the file descriptor. For more - /// information on these operations, see the man page linked below. - /// - /// The `flags` are not currently implemented for eventfd. - /// The `flags` may be bitwise ORed to change the behavior of `eventfd`: - /// `EFD_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. - /// `EFD_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the new open file description. - /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics. - /// - /// - #[expect(clippy::needless_if)] - fn eventfd( - &mut self, - val: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - - let val = this.read_scalar(val)?.to_u32()?; - let flags = this.read_scalar(flags)?.to_i32()?; - - let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC"); - let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK"); - let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE"); - - if flags & (efd_cloexec | efd_nonblock | efd_semaphore) == 0 { - throw_unsup_format!("{flags} is unsupported"); - } - // FIXME handle the cloexec and nonblock flags - if flags & efd_cloexec == efd_cloexec {} - if flags & efd_nonblock == efd_nonblock {} - if flags & efd_semaphore == efd_semaphore { - throw_unsup_format!("EFD_SEMAPHORE is unsupported"); - } - - let fd = this.machine.fds.insert_fd(Box::new(Event { val: Cell::new(val.into()) })); - Ok(Scalar::from_i32(fd)) - } -} diff --git a/src/tools/miri/src/shims/unix/linux/fd/epoll.rs b/src/tools/miri/src/shims/unix/linux/fd/epoll.rs deleted file mode 100644 index f2da76ca98d..00000000000 --- a/src/tools/miri/src/shims/unix/linux/fd/epoll.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::*; - -use crate::shims::unix::FileDescriptor; - -use rustc_data_structures::fx::FxHashMap; -use std::io; - -/// An `Epoll` file descriptor connects file handles and epoll events -#[derive(Clone, Debug, Default)] -pub struct Epoll { - /// The file descriptors we are watching, and what we are watching for. - pub file_descriptors: FxHashMap, -} - -/// Epoll Events associate events with data. -/// These fields are currently unused by miri. -/// This matches the `epoll_event` struct defined -/// by the epoll_ctl man page. For more information -/// see the man page: -/// -/// -#[derive(Clone, Debug)] -pub struct EpollEvent { - #[allow(dead_code)] - pub events: u32, - /// `Scalar` is used to represent the - /// `epoll_data` type union. - #[allow(dead_code)] - pub data: Scalar, -} - -impl FileDescriptor for Epoll { - fn name(&self) -> &'static str { - "epoll" - } - - fn dup(&mut self) -> io::Result> { - Ok(Box::new(self.clone())) - } - - fn close<'tcx>( - self: Box, - _communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { - Ok(Ok(0)) - } -} diff --git a/src/tools/miri/src/shims/unix/linux/fd/event.rs b/src/tools/miri/src/shims/unix/linux/fd/event.rs deleted file mode 100644 index 0eb4befd52f..00000000000 --- a/src/tools/miri/src/shims/unix/linux/fd/event.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::shims::unix::FileDescriptor; - -use rustc_const_eval::interpret::InterpResult; -use rustc_middle::ty::TyCtxt; -use rustc_target::abi::Endian; - -use std::cell::Cell; -use std::io; - -/// A kind of file descriptor created by `eventfd`. -/// The `Event` type isn't currently written to by `eventfd`. -/// The interface is meant to keep track of objects associated -/// with a file descriptor. For more information see the man -/// page below: -/// -/// -#[derive(Debug)] -pub struct Event { - /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the - /// kernel. This counter is initialized with the value specified in the argument initval. - pub val: Cell, -} - -impl FileDescriptor for Event { - fn name(&self) -> &'static str { - "event" - } - - fn dup(&mut self) -> io::Result> { - Ok(Box::new(Event { val: self.val.clone() })) - } - - fn close<'tcx>( - self: Box, - _communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { - Ok(Ok(0)) - } - - /// A write call adds the 8-byte integer value supplied in - /// its buffer (in native endianness) to the counter. The maximum value that may be - /// stored in the counter is the largest unsigned 64-bit value - /// minus 1 (i.e., 0xfffffffffffffffe). If the addition would - /// cause the counter's value to exceed the maximum, then the - /// write either blocks until a read is performed on the - /// file descriptor, or fails with the error EAGAIN if the - /// file descriptor has been made nonblocking. - - /// A write fails with the error EINVAL if the size of the - /// supplied buffer is less than 8 bytes, or if an attempt is - /// made to write the value 0xffffffffffffffff. - fn write<'tcx>( - &self, - _communicate_allowed: bool, - bytes: &[u8], - tcx: TyCtxt<'tcx>, - ) -> InterpResult<'tcx, io::Result> { - let v1 = self.val.get(); - let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size - // Convert from target endianness to host endianness. - let num = match tcx.sess.target.endian { - Endian::Little => u64::from_le_bytes(bytes), - Endian::Big => u64::from_be_bytes(bytes), - }; - // FIXME handle blocking when addition results in exceeding the max u64 value - // or fail with EAGAIN if the file descriptor is nonblocking. - let v2 = v1.checked_add(num).unwrap(); - self.val.set(v2); - assert_eq!(8, bytes.len()); - Ok(Ok(8)) - } -} diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 7e600f4c54b..2aaec3f5a0b 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -6,7 +6,8 @@ use crate::machine::SIGRTMIN; use crate::shims::unix::*; use crate::*; use shims::foreign_items::EmulateForeignItemResult; -use shims::unix::linux::fd::EvalContextExt as _; +use shims::unix::linux::epoll::EvalContextExt as _; +use shims::unix::linux::eventfd::EvalContextExt as _; use shims::unix::linux::mem::EvalContextExt as _; use shims::unix::linux::sync::futex; diff --git a/src/tools/miri/src/shims/unix/linux/mod.rs b/src/tools/miri/src/shims/unix/linux/mod.rs index fe18f1a32fd..84b604eb9b8 100644 --- a/src/tools/miri/src/shims/unix/linux/mod.rs +++ b/src/tools/miri/src/shims/unix/linux/mod.rs @@ -1,4 +1,5 @@ -pub mod fd; +pub mod epoll; +pub mod eventfd; pub mod foreign_items; pub mod mem; pub mod sync; diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index aa06425ffa1..84ddd746fb5 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -6,7 +6,6 @@ use crate::*; /// Pair of connected sockets. /// /// We currently don't allow sending any data through this pair, so this can be just a dummy. -/// FIXME: show proper errors when trying to send/receive #[derive(Debug)] struct SocketPair; -- cgit 1.4.1-3-g733a5