implement minimal epoll_create1 shim
This commit is contained in:
parent
8b19af0582
commit
1b51f372eb
12 changed files with 377 additions and 41 deletions
|
|
@ -27,6 +27,7 @@
|
|||
clippy::single_element_loop,
|
||||
clippy::needless_return,
|
||||
clippy::bool_to_int_with_if,
|
||||
clippy::box_default,
|
||||
// We are not implementing queries here so it's fine
|
||||
rustc::potential_query_instability
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ use crate::{
|
|||
*,
|
||||
};
|
||||
|
||||
/// The number of the available real-time signal with the lowest priority.
|
||||
/// Dummy constant related to epoll, must be between 32 and 64.
|
||||
pub const SIGRTMAX: i32 = 42;
|
||||
|
||||
/// Extra data stored with each stack frame
|
||||
pub struct FrameExtra<'tcx> {
|
||||
/// Extra data for Stacked Borrows.
|
||||
|
|
|
|||
|
|
@ -17,20 +17,25 @@ use crate::shims::os_str::bytes_to_os_str;
|
|||
use crate::*;
|
||||
use shims::os_str::os_str_to_bytes;
|
||||
use shims::time::system_time_to_duration;
|
||||
use shims::unix::linux::fd::epoll::Epoll;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FileHandle {
|
||||
pub struct FileHandle {
|
||||
file: File,
|
||||
writable: bool,
|
||||
}
|
||||
|
||||
trait FileDescriptor: std::fmt::Debug {
|
||||
pub trait FileDescriptor: std::fmt::Debug {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
fn as_file_handle<'tcx>(&self) -> InterpResult<'tcx, &FileHandle> {
|
||||
throw_unsup_format!("{} cannot be used as FileHandle", self.name());
|
||||
}
|
||||
|
||||
fn as_epoll_handle<'tcx>(&mut self) -> InterpResult<'tcx, &mut Epoll> {
|
||||
throw_unsup_format!("not an epoll file descriptor");
|
||||
}
|
||||
|
||||
fn read<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
|
|
@ -274,7 +279,7 @@ impl FileDescriptor for NullOutput {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct FileHandler {
|
||||
handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
|
||||
pub handles: BTreeMap<i32, Box<dyn FileDescriptor>>,
|
||||
}
|
||||
|
||||
impl VisitTags for FileHandler {
|
||||
|
|
@ -297,7 +302,7 @@ impl FileHandler {
|
|||
FileHandler { handles }
|
||||
}
|
||||
|
||||
fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
|
||||
pub fn insert_fd(&mut self, file_handle: Box<dyn FileDescriptor>) -> i32 {
|
||||
self.insert_fd_with_min_fd(file_handle, 0)
|
||||
}
|
||||
|
||||
|
|
@ -376,17 +381,6 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
/// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
|
||||
/// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
|
||||
/// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
|
||||
/// types (like `read`, that returns an `i64`).
|
||||
fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
|
||||
let this = self.eval_context_mut();
|
||||
let ebadf = this.eval_libc("EBADF");
|
||||
this.set_last_error(ebadf)?;
|
||||
Ok((-1).into())
|
||||
}
|
||||
|
||||
fn file_type_to_d_type(
|
||||
&mut self,
|
||||
file_type: std::io::Result<FileType>,
|
||||
|
|
@ -726,6 +720,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
))
|
||||
}
|
||||
|
||||
/// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
|
||||
/// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
|
||||
/// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
|
||||
/// types (like `read`, that returns an `i64`).
|
||||
fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
|
||||
let this = self.eval_context_mut();
|
||||
let ebadf = this.eval_libc("EBADF");
|
||||
this.set_last_error(ebadf)?;
|
||||
Ok((-1).into())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
fd: i32,
|
||||
|
|
|
|||
191
src/tools/miri/src/shims/unix/linux/fd.rs
Normal file
191
src/tools/miri/src/shims/unix/linux/fd.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
use rustc_middle::ty::ScalarInt;
|
||||
|
||||
use crate::*;
|
||||
use epoll::{Epoll, EpollEvent};
|
||||
use event::Event;
|
||||
use socketpair::SocketPair;
|
||||
|
||||
use shims::unix::fs::EvalContextExt as _;
|
||||
|
||||
pub mod epoll;
|
||||
pub mod event;
|
||||
pub mod socketpair;
|
||||
|
||||
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()`.
|
||||
///
|
||||
/// <https://linux.die.net/man/2/epoll_create1>
|
||||
fn epoll_create1(
|
||||
&mut self,
|
||||
flags: &OpTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
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.file_handler.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.
|
||||
///
|
||||
/// <https://linux.die.net/man/2/epoll_ctl>
|
||||
fn epoll_ctl(
|
||||
&mut self,
|
||||
epfd: &OpTy<'tcx, Provenance>,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
fd: &OpTy<'tcx, Provenance>,
|
||||
event: &OpTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
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_operand(event)?;
|
||||
|
||||
let events = this.mplace_field(&event, 0)?;
|
||||
let events = this.read_scalar(&events.into())?.to_u32()?;
|
||||
let data = this.mplace_field(&event, 1)?;
|
||||
let data = this.read_scalar(&data.into())?;
|
||||
let event = EpollEvent { events, data };
|
||||
|
||||
if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
|
||||
let epfd = epfd.as_epoll_handle()?;
|
||||
|
||||
epfd.file_descriptors.insert(fd, event);
|
||||
Ok(Scalar::from_i32(0))
|
||||
} else {
|
||||
Ok(Scalar::from_i32(this.handle_not_found()?))
|
||||
}
|
||||
} else if op == epoll_ctl_del {
|
||||
if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) {
|
||||
let epfd = epfd.as_epoll_handle()?;
|
||||
|
||||
epfd.file_descriptors.remove(&fd);
|
||||
Ok(Scalar::from_i32(0))
|
||||
} else {
|
||||
Ok(Scalar::from_i32(this.handle_not_found()?))
|
||||
}
|
||||
} else {
|
||||
let einval = this.eval_libc("EINVAL");
|
||||
this.set_last_error(einval)?;
|
||||
Ok(Scalar::from_i32(-1))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// <https://linux.die.net/man/2/eventfd>
|
||||
fn eventfd(
|
||||
&mut self,
|
||||
val: &OpTy<'tcx, Provenance>,
|
||||
flags: &OpTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
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 fh = &mut this.machine.file_handler;
|
||||
let fd = fh.insert_fd(Box::new(Event { val }));
|
||||
Ok(Scalar::from_i32(fd))
|
||||
}
|
||||
|
||||
/// Currently this function creates new `SocketPair`s without specifying the domain, type, or
|
||||
/// protocol of the new socket and these are stored in the socket values `sv` argument.
|
||||
///
|
||||
/// This function creates an unnamed pair of connected sockets in the specified domain, of the
|
||||
/// specified type, and using the optionally specified protocol.
|
||||
///
|
||||
/// The `domain` argument specified a communication domain; this selects the protocol family
|
||||
/// used for communication. The socket `type` specifies the communication semantics.
|
||||
/// The `protocol` specifies a particular protocol to use with the socket. Normally there's
|
||||
/// only a single protocol supported for a particular socket type within a given protocol
|
||||
/// family, in which case `protocol` can be specified as 0. It is possible that many protocols
|
||||
/// exist and in that case, a particular protocol must be specified.
|
||||
///
|
||||
/// For more information on the arguments see the socket manpage:
|
||||
/// <https://linux.die.net/man/2/socket>
|
||||
///
|
||||
/// <https://linux.die.net/man/2/socketpair>
|
||||
fn socketpair(
|
||||
&mut self,
|
||||
domain: &OpTy<'tcx, Provenance>,
|
||||
type_: &OpTy<'tcx, Provenance>,
|
||||
protocol: &OpTy<'tcx, Provenance>,
|
||||
sv: &OpTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let _domain = this.read_scalar(domain)?.to_i32()?;
|
||||
let _type_ = this.read_scalar(type_)?.to_i32()?;
|
||||
let _protocol = this.read_scalar(protocol)?.to_i32()?;
|
||||
let sv = this.deref_operand(sv)?;
|
||||
|
||||
let fh = &mut this.machine.file_handler;
|
||||
let sv0 = fh.insert_fd(Box::new(SocketPair));
|
||||
let sv0 = ScalarInt::try_from_int(sv0, sv.layout.size).unwrap();
|
||||
let sv1 = fh.insert_fd(Box::new(SocketPair));
|
||||
let sv1 = ScalarInt::try_from_int(sv1, sv.layout.size).unwrap();
|
||||
|
||||
this.write_scalar(sv0, &sv.into())?;
|
||||
this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?.into())?;
|
||||
|
||||
Ok(Scalar::from_i32(0))
|
||||
}
|
||||
}
|
||||
53
src/tools/miri/src/shims/unix/linux/fd/epoll.rs
Normal file
53
src/tools/miri/src/shims/unix/linux/fd/epoll.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use crate::*;
|
||||
|
||||
use crate::shims::unix::fs::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<i32, EpollEvent>,
|
||||
}
|
||||
|
||||
/// 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:
|
||||
///
|
||||
/// <https://man7.org/linux/man-pages/man2/epoll_ctl.2.html>
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EpollEvent {
|
||||
pub events: u32,
|
||||
/// `Scalar<Provenance>` is used to represent the
|
||||
/// `epoll_data` type union.
|
||||
pub data: Scalar<Provenance>,
|
||||
}
|
||||
|
||||
impl FileDescriptor for Epoll {
|
||||
fn name(&self) -> &'static str {
|
||||
"epoll"
|
||||
}
|
||||
|
||||
fn as_epoll_handle<'tcx>(&mut self) -> InterpResult<'tcx, &mut Epoll> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
|
||||
Ok(Box::new(self.clone()))
|
||||
}
|
||||
|
||||
fn is_tty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
Ok(Ok(0))
|
||||
}
|
||||
}
|
||||
38
src/tools/miri/src/shims/unix/linux/fd/event.rs
Normal file
38
src/tools/miri/src/shims/unix/linux/fd/event.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use crate::shims::unix::fs::FileDescriptor;
|
||||
|
||||
use rustc_const_eval::interpret::InterpResult;
|
||||
|
||||
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:
|
||||
///
|
||||
/// <https://man.netbsd.org/eventfd.2>
|
||||
#[derive(Debug)]
|
||||
pub struct Event {
|
||||
pub val: u32,
|
||||
}
|
||||
|
||||
impl FileDescriptor for Event {
|
||||
fn name(&self) -> &'static str {
|
||||
"event"
|
||||
}
|
||||
|
||||
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
|
||||
Ok(Box::new(Event { val: self.val }))
|
||||
}
|
||||
|
||||
fn is_tty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
Ok(Ok(0))
|
||||
}
|
||||
}
|
||||
32
src/tools/miri/src/shims/unix/linux/fd/socketpair.rs
Normal file
32
src/tools/miri/src/shims/unix/linux/fd/socketpair.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use crate::*;
|
||||
|
||||
use crate::shims::unix::fs::FileDescriptor;
|
||||
|
||||
use std::io;
|
||||
|
||||
/// Pair of connected sockets.
|
||||
///
|
||||
/// We currently don't allow sending any data through this pair, so this can be just a dummy.
|
||||
#[derive(Debug)]
|
||||
pub struct SocketPair;
|
||||
|
||||
impl FileDescriptor for SocketPair {
|
||||
fn name(&self) -> &'static str {
|
||||
"socketpair"
|
||||
}
|
||||
|
||||
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
|
||||
Ok(Box::new(SocketPair))
|
||||
}
|
||||
|
||||
fn is_tty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
Ok(Ok(0))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
use rustc_span::Symbol;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::machine::SIGRTMAX;
|
||||
use crate::*;
|
||||
use shims::foreign_items::EmulateByNameResult;
|
||||
use shims::unix::fs::EvalContextExt as _;
|
||||
use shims::unix::linux::fd::EvalContextExt as _;
|
||||
use shims::unix::linux::sync::futex;
|
||||
use shims::unix::sync::EvalContextExt as _;
|
||||
use shims::unix::thread::EvalContextExt as _;
|
||||
|
|
@ -42,6 +44,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let result = this.sync_file_range(fd, offset, nbytes, flags)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"epoll_create1" => {
|
||||
let [flag] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let result = this.epoll_create1(flag)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"epoll_ctl" => {
|
||||
let [epfd, op, fd, event] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let result = this.epoll_ctl(epfd, op, fd, event)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"eventfd" => {
|
||||
let [val, flag] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
let result = this.eventfd(val, flag)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"socketpair" => {
|
||||
let [domain, type_, protocol, sv] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
|
||||
let result = this.socketpair(domain, type_, protocol, sv)?;
|
||||
this.write_scalar(result, dest)?;
|
||||
}
|
||||
"__libc_current_sigrtmax" => {
|
||||
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
|
||||
this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?;
|
||||
}
|
||||
|
||||
// Threading
|
||||
"pthread_condattr_setclock" => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod dlsym;
|
||||
pub mod fd;
|
||||
pub mod foreign_items;
|
||||
pub mod sync;
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
//@compile-flags: -Zmiri-disable-isolation
|
||||
//@error-pattern: can't call foreign function: epoll_create1
|
||||
//@normalize-stderr-test: " = note: inside .*\n" -> ""
|
||||
//@only-target-linux: the errors differ too much between platforms
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
error: unsupported operation: can't call foreign function: epoll_create1
|
||||
--> CARGO_REGISTRY/.../epoll.rs:LL:CC
|
||||
|
|
||||
LL | let res = syscall!(epoll_create1(libc::EPOLL_CLOEXEC));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: epoll_create1
|
||||
|
|
||||
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
|
||||
= note: BACKTRACE:
|
||||
note: inside `main`
|
||||
--> $DIR/tokio_mvp.rs:LL:CC
|
||||
|
|
||||
LL | #[tokio::main]
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: this error originates in the macro `syscall` which comes from the expansion of the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
6
src/tools/miri/tests/pass-dep/tokio_mvp.rs
Normal file
6
src/tools/miri/tests/pass-dep/tokio_mvp.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Need to disable preemption to stay on the supported MVP codepath in mio.
|
||||
//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-preemption-rate=0
|
||||
//@only-target-x86_64-unknown-linux: support for tokio exists only on linux and x86
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue