Vendor dependencies

Let's see how I like this workflow.
This commit is contained in:
John Doty 2022-12-19 08:27:18 -08:00
parent 34d1830413
commit 9c435dc440
7500 changed files with 1665121 additions and 99 deletions

222
vendor/rustix/src/backend/libc/conv.rs vendored Normal file
View file

@ -0,0 +1,222 @@
//! Libc call arguments and return values are often things like `c_int`,
//! `c_uint`, or libc-specific pointer types. This module provides functions
//! for converting between rustix's types and libc types.
#![allow(dead_code)]
use super::c;
use super::fd::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, LibcFd, OwnedFd, RawFd};
#[cfg(not(windows))]
#[cfg(feature = "fs")]
use super::offset::libc_off_t;
#[cfg(not(windows))]
use crate::ffi::CStr;
use crate::io;
#[cfg(windows)]
use core::convert::TryInto;
#[cfg(not(windows))]
#[inline]
pub(super) fn c_str(c: &CStr) -> *const c::c_char {
c.as_ptr()
}
#[cfg(not(windows))]
#[inline]
pub(super) fn no_fd() -> LibcFd {
-1
}
#[inline]
pub(super) fn borrowed_fd(fd: BorrowedFd<'_>) -> LibcFd {
fd.as_raw_fd() as LibcFd
}
#[inline]
pub(super) fn owned_fd(fd: OwnedFd) -> LibcFd {
fd.into_raw_fd() as LibcFd
}
#[inline]
pub(super) fn ret(raw: c::c_int) -> io::Result<()> {
if raw == 0 {
Ok(())
} else {
Err(io::Errno::last_os_error())
}
}
#[inline]
pub(super) fn syscall_ret(raw: c::c_long) -> io::Result<()> {
if raw == 0 {
Ok(())
} else {
Err(io::Errno::last_os_error())
}
}
#[inline]
pub(super) fn nonnegative_ret(raw: c::c_int) -> io::Result<()> {
if raw >= 0 {
Ok(())
} else {
Err(io::Errno::last_os_error())
}
}
#[inline]
pub(super) unsafe fn ret_infallible(raw: c::c_int) {
debug_assert_eq!(raw, 0, "unexpected error: {:?}", io::Errno::last_os_error());
}
#[inline]
pub(super) fn ret_c_int(raw: c::c_int) -> io::Result<c::c_int> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
#[inline]
pub(super) fn ret_u32(raw: c::c_int) -> io::Result<u32> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw as u32)
}
}
#[inline]
pub(super) fn ret_ssize_t(raw: c::ssize_t) -> io::Result<c::ssize_t> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
#[inline]
pub(super) fn syscall_ret_ssize_t(raw: c::c_long) -> io::Result<c::ssize_t> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw as c::ssize_t)
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(super) fn syscall_ret_u32(raw: c::c_long) -> io::Result<u32> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
let r32 = raw as u32;
// Converting `raw` to `u32` should be lossless.
debug_assert_eq!(r32 as c::c_long, raw);
Ok(r32)
}
}
#[cfg(not(windows))]
#[cfg(feature = "fs")]
#[inline]
pub(super) fn ret_off_t(raw: libc_off_t) -> io::Result<libc_off_t> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
#[cfg(not(windows))]
#[inline]
pub(super) fn ret_pid_t(raw: c::pid_t) -> io::Result<c::pid_t> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(raw)
}
}
/// Convert a `c_int` returned from a libc function to an `OwnedFd`, if valid.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a libc function
/// which returns an owned file descriptor.
#[inline]
pub(super) unsafe fn ret_owned_fd(raw: LibcFd) -> io::Result<OwnedFd> {
if raw == !0 {
Err(io::Errno::last_os_error())
} else {
Ok(OwnedFd::from_raw_fd(raw as RawFd))
}
}
#[inline]
pub(super) fn ret_discarded_fd(raw: LibcFd) -> io::Result<()> {
if raw == !0 {
Err(io::Errno::last_os_error())
} else {
Ok(())
}
}
#[inline]
pub(super) fn ret_discarded_char_ptr(raw: *mut c::c_char) -> io::Result<()> {
if raw.is_null() {
Err(io::Errno::last_os_error())
} else {
Ok(())
}
}
/// Convert a `c_long` returned from `syscall` to an `OwnedFd`, if valid.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a `syscall` call
/// which returns an owned file descriptor.
#[cfg(not(windows))]
#[inline]
pub(super) unsafe fn syscall_ret_owned_fd(raw: c::c_long) -> io::Result<OwnedFd> {
if raw == -1 {
Err(io::Errno::last_os_error())
} else {
Ok(OwnedFd::from_raw_fd(raw as RawFd))
}
}
/// Convert the buffer-length argument value of a `send` or `recv` call.
#[cfg(not(windows))]
#[inline]
pub(super) fn send_recv_len(len: usize) -> usize {
len
}
/// Convert the buffer-length argument value of a `send` or `recv` call.
#[cfg(windows)]
#[inline]
pub(super) fn send_recv_len(len: usize) -> i32 {
// On Windows, the length argument has type `i32`; saturate the length,
// since `send` and `recv` are allowed to send and recv less data than
// requested.
len.try_into().unwrap_or(i32::MAX)
}
/// Convert the return value of a `send` or `recv` call.
#[cfg(not(windows))]
#[inline]
pub(super) fn ret_send_recv(len: isize) -> io::Result<c::ssize_t> {
ret_ssize_t(len)
}
/// Convert the return value of a `send` or `recv` call.
#[cfg(windows)]
#[inline]
pub(super) fn ret_send_recv(len: i32) -> io::Result<c::ssize_t> {
ret_ssize_t(len as isize)
}

487
vendor/rustix/src/backend/libc/fs/dir.rs vendored Normal file
View file

@ -0,0 +1,487 @@
use super::super::c;
use super::super::conv::owned_fd;
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
use super::types::FileType;
use crate::fd::{AsFd, BorrowedFd};
use crate::ffi::CStr;
#[cfg(target_os = "wasi")]
use crate::ffi::CString;
use crate::fs::{fcntl_getfl, fstat, openat, Mode, OFlags, Stat};
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
use crate::fs::{fstatfs, StatFs};
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
use crate::fs::{fstatvfs, StatVfs};
use crate::io;
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::process::fchdir;
#[cfg(target_os = "wasi")]
use alloc::borrow::ToOwned;
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
target_os = "openbsd",
)))]
use c::dirent as libc_dirent;
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
)))]
use c::readdir as libc_readdir;
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
))]
use c::{dirent64 as libc_dirent, readdir64 as libc_readdir};
use core::fmt;
use core::mem::zeroed;
use core::ptr::NonNull;
use libc_errno::{errno, set_errno, Errno};
/// `DIR*`
#[repr(transparent)]
pub struct Dir(NonNull<c::DIR>);
impl Dir {
/// Construct a `Dir` that reads entries from the given directory
/// file descriptor.
#[inline]
pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
Self::_read_from(fd.as_fd())
}
#[inline]
fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
// Given an arbitrary `OwnedFd`, it's impossible to know whether the
// user holds a `dup`'d copy which could continue to modify the
// file description state, which would cause Undefined Behavior after
// our call to `fdopendir`. To prevent this, we obtain an independent
// `OwnedFd`.
let flags = fcntl_getfl(fd)?;
let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?;
let raw = owned_fd(fd_for_dir);
unsafe {
let libc_dir = c::fdopendir(raw);
if let Some(libc_dir) = NonNull::new(libc_dir) {
Ok(Self(libc_dir))
} else {
let err = io::Errno::last_os_error();
let _ = c::close(raw);
Err(err)
}
}
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
unsafe { c::rewinddir(self.0.as_ptr()) }
}
/// `readdir(self)`, where `None` means the end of the directory.
pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
set_errno(Errno(0));
let dirent_ptr = unsafe { libc_readdir(self.0.as_ptr()) };
if dirent_ptr.is_null() {
let curr_errno = errno().0;
if curr_errno == 0 {
// We successfully reached the end of the stream.
None
} else {
// `errno` is unknown or non-zero, so an error occurred.
Some(Err(io::Errno(curr_errno)))
}
} else {
// We successfully read an entry.
unsafe {
// We have our own copy of OpenBSD's dirent; check that the
// layout minimally matches libc's.
#[cfg(target_os = "openbsd")]
check_dirent_layout(&*dirent_ptr);
let result = DirEntry {
dirent: read_dirent(&*dirent_ptr.cast()),
#[cfg(target_os = "wasi")]
name: CStr::from_ptr((*dirent_ptr).d_name.as_ptr()).to_owned(),
};
Some(Ok(result))
}
}
}
/// `fstat(self)`
#[inline]
pub fn stat(&self) -> io::Result<Stat> {
fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
}
/// `fstatfs(self)`
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
#[inline]
pub fn statfs(&self) -> io::Result<StatFs> {
fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
}
/// `fstatvfs(self)`
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
#[inline]
pub fn statvfs(&self) -> io::Result<StatVfs> {
fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
}
/// `fchdir(self)`
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
}
}
// A `dirent` pointer returned from `readdir` may not point to a full `dirent`
// struct, as the name is NUL-terminated and memory may not be allocated for
// the full extent of the struct. Copy the fields one at a time.
unsafe fn read_dirent(input: &libc_dirent) -> libc_dirent {
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "solaris"
)))]
let d_type = input.d_type;
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "wasi",
)))]
let d_off = input.d_off;
#[cfg(target_os = "aix")]
let d_offset = input.d_offset;
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
)))]
let d_ino = input.d_ino;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
let d_fileno = input.d_fileno;
#[cfg(not(any(target_os = "dragonfly", target_os = "wasi")))]
let d_reclen = input.d_reclen;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "ios",
target_os = "macos",
))]
let d_namlen = input.d_namlen;
#[cfg(any(target_os = "ios", target_os = "macos"))]
let d_seekoff = input.d_seekoff;
#[cfg(target_os = "haiku")]
let d_dev = input.d_dev;
#[cfg(target_os = "haiku")]
let d_pdev = input.d_pdev;
#[cfg(target_os = "haiku")]
let d_pino = input.d_pino;
// Construct the input. Rust will give us an error if any OS has a input
// with a field that we missed here. And we can avoid blindly copying the
// whole `d_name` field, which may not be entirely allocated.
#[cfg_attr(target_os = "wasi", allow(unused_mut))]
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly")))]
let mut dirent = libc_dirent {
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "solaris"
)))]
d_type,
#[cfg(not(any(
target_os = "aix",
target_os = "freebsd", // Until FreeBSD 12
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "wasi",
)))]
d_off,
#[cfg(target_os = "aix")]
d_offset,
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
d_ino,
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
d_fileno,
#[cfg(not(target_os = "wasi"))]
d_reclen,
#[cfg(any(
target_os = "aix",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
d_namlen,
#[cfg(any(target_os = "ios", target_os = "macos"))]
d_seekoff,
// The `d_name` field is NUL-terminated, and we need to be careful not
// to read bytes past the NUL, even though they're within the nominal
// extent of the `struct dirent`, because they may not be allocated. So
// don't read it from `dirent_ptr`.
//
// In theory this could use `MaybeUninit::uninit().assume_init()`, but
// that [invokes undefined behavior].
//
// [invokes undefined behavior]: https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#initialization-invariant
d_name: zeroed(),
#[cfg(target_os = "openbsd")]
__d_padding: zeroed(),
#[cfg(target_os = "haiku")]
d_dev,
#[cfg(target_os = "haiku")]
d_pdev,
#[cfg(target_os = "haiku")]
d_pino,
};
/*
pub d_ino: ino_t,
pub d_pino: i64,
pub d_reclen: ::c_ushort,
pub d_name: [::c_char; 1024], // Max length is _POSIX_PATH_MAX
// */
// On dragonfly and FreeBSD 12, `dirent` has some non-public padding fields
// so we can't directly initialize it.
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
let mut dirent = {
let mut dirent: libc_dirent = zeroed();
dirent.d_fileno = d_fileno;
dirent.d_namlen = d_namlen;
dirent.d_type = d_type;
#[cfg(target_os = "freebsd")]
{
dirent.d_reclen = d_reclen;
}
dirent
};
// Copy from d_name, reading up to and including the first NUL.
#[cfg(not(target_os = "wasi"))]
{
let name_len = CStr::from_ptr(input.d_name.as_ptr())
.to_bytes_with_nul()
.len();
dirent.d_name[..name_len].copy_from_slice(&input.d_name[..name_len]);
}
dirent
}
/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is
/// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they
/// need `Sync`, which is effectively what'd need to do to implement `Sync`
/// ourselves.
unsafe impl Send for Dir {}
impl Drop for Dir {
#[inline]
fn drop(&mut self) {
unsafe { c::closedir(self.0.as_ptr()) };
}
}
impl Iterator for Dir {
type Item = io::Result<DirEntry>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Self::read(self)
}
}
impl fmt::Debug for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dir")
.field("fd", unsafe { &c::dirfd(self.0.as_ptr()) })
.finish()
}
}
/// `struct dirent`
#[derive(Debug)]
pub struct DirEntry {
dirent: libc_dirent,
#[cfg(target_os = "wasi")]
name: CString,
}
impl DirEntry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
#[cfg(not(target_os = "wasi"))]
unsafe {
CStr::from_ptr(self.dirent.d_name.as_ptr())
}
#[cfg(target_os = "wasi")]
&self.name
}
/// Returns the type of this directory entry.
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "solaris"
)))]
#[inline]
pub fn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.dirent.d_type)
}
/// Return the inode number of this directory entry.
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
)))]
#[inline]
pub fn ino(&self) -> u64 {
self.dirent.d_ino as u64
}
/// Return the inode number of this directory entry.
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
))]
#[inline]
pub fn ino(&self) -> u64 {
#[allow(clippy::useless_conversion)]
self.dirent.d_fileno.into()
}
}
/// libc's OpenBSD `dirent` has a private field so we can't construct it
/// directly, so we declare it ourselves to make all fields accessible.
#[cfg(target_os = "openbsd")]
#[repr(C)]
#[derive(Debug)]
struct libc_dirent {
d_fileno: c::ino_t,
d_off: c::off_t,
d_reclen: u16,
d_type: u8,
d_namlen: u8,
__d_padding: [u8; 4],
d_name: [c::c_char; 256],
}
/// We have our own copy of OpenBSD's dirent; check that the layout
/// minimally matches libc's.
#[cfg(target_os = "openbsd")]
fn check_dirent_layout(dirent: &c::dirent) {
use crate::utils::as_ptr;
use core::mem::{align_of, size_of};
// Check that the basic layouts match.
assert_eq!(size_of::<libc_dirent>(), size_of::<c::dirent>());
assert_eq!(align_of::<libc_dirent>(), align_of::<c::dirent>());
// Check that the field offsets match.
assert_eq!(
{
let z = libc_dirent {
d_fileno: 0_u64,
d_off: 0_i64,
d_reclen: 0_u16,
d_type: 0_u8,
d_namlen: 0_u8,
__d_padding: [0_u8; 4],
d_name: [0 as c::c_char; 256],
};
let base = as_ptr(&z) as usize;
(
(as_ptr(&z.d_fileno) as usize) - base,
(as_ptr(&z.d_off) as usize) - base,
(as_ptr(&z.d_reclen) as usize) - base,
(as_ptr(&z.d_type) as usize) - base,
(as_ptr(&z.d_namlen) as usize) - base,
(as_ptr(&z.d_name) as usize) - base,
)
},
{
let z = dirent;
let base = as_ptr(z) as usize;
(
(as_ptr(&z.d_fileno) as usize) - base,
(as_ptr(&z.d_off) as usize) - base,
(as_ptr(&z.d_reclen) as usize) - base,
(as_ptr(&z.d_type) as usize) - base,
(as_ptr(&z.d_namlen) as usize) - base,
(as_ptr(&z.d_name) as usize) - base,
)
}
);
}

View file

@ -0,0 +1,90 @@
#[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
use super::super::c;
use crate::fs::Dev;
#[cfg(not(any(target_os = "android", target_os = "emscripten")))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
c::makedev(maj, min)
}
#[cfg(all(target_os = "android", not(target_pointer_width = "32")))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// Android's `makedev` oddly has signed argument types.
c::makedev(maj, min)
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit,
// so we do it ourselves.
((u64::from(maj) & 0xffff_f000_u64) << 32)
| ((u64::from(maj) & 0x0000_0fff_u64) << 8)
| ((u64::from(min) & 0xffff_ff00_u64) << 12)
| (u64::from(min) & 0x0000_00ff_u64)
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
// Emscripten's `makedev` has a 32-bit return value.
Dev::from(c::makedev(maj, min))
}
#[cfg(not(any(target_os = "android", target_os = "emscripten")))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
unsafe { c::major(dev) }
}
#[cfg(all(target_os = "android", not(target_pointer_width = "32")))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// Android's `major` oddly has signed return types.
(unsafe { c::major(dev) }) as u32
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit,
// so we do it ourselves.
(((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
// Emscripten's `major` has a 32-bit argument value.
unsafe { c::major(dev as u32) }
}
#[cfg(not(any(target_os = "android", target_os = "emscripten")))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
unsafe { c::minor(dev) }
}
#[cfg(all(target_os = "android", not(target_pointer_width = "32")))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// Android's `minor` oddly has signed return types.
(unsafe { c::minor(dev) }) as u32
}
#[cfg(all(target_os = "android", target_pointer_width = "32"))]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// 32-bit Android's `dev_t` is 32-bit, but its `st_dev` is 64-bit,
// so we do it ourselves.
(((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32
}
#[cfg(target_os = "emscripten")]
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
// Emscripten's `minor` has a 32-bit argument value.
unsafe { c::minor(dev as u32) }
}

View file

@ -0,0 +1,19 @@
#[cfg(not(target_os = "redox"))]
pub(crate) mod dir;
#[cfg(not(any(
target_os = "dragonfly",
target_os = "haiku",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
pub(crate) mod makedev;
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

File diff suppressed because it is too large Load diff

1116
vendor/rustix/src/backend/libc/fs/types.rs vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,573 @@
//! epoll support.
//!
//! This is an experiment, and it isn't yet clear whether epoll is the right
//! level of abstraction at which to introduce safety. But it works fairly well
//! in simple examples 🙂.
//!
//! # Examples
//!
//! ```rust,no_run
//! # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]
//! # #[cfg(feature = "net")]
//! # fn main() -> std::io::Result<()> {
//! use io_lifetimes::AsFd;
//! use rustix::io::epoll::{self, Epoll};
//! use rustix::io::{ioctl_fionbio, read, write};
//! use rustix::net::{
//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, Protocol, SocketAddrV4,
//! SocketType,
//! };
//! use std::os::unix::io::AsRawFd;
//!
//! // Create a socket and listen on it.
//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, Protocol::default())?;
//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?;
//! listen(&listen_sock, 1)?;
//!
//! // Create an epoll object. Using `Owning` here means the epoll object will
//! // take ownership of the file descriptors registered with it.
//! let epoll = Epoll::new(epoll::CreateFlags::CLOEXEC, epoll::Owning::new())?;
//!
//! // Remember the socket raw fd, which we use for comparisons only.
//! let raw_listen_sock = listen_sock.as_fd().as_raw_fd();
//!
//! // Register the socket with the epoll object.
//! epoll.add(listen_sock, epoll::EventFlags::IN)?;
//!
//! // Process events.
//! let mut event_list = epoll::EventVec::with_capacity(4);
//! loop {
//! epoll.wait(&mut event_list, -1)?;
//! for (_event_flags, target) in &event_list {
//! if target.as_raw_fd() == raw_listen_sock {
//! // Accept a new connection, set it to non-blocking, and
//! // register to be notified when it's ready to write to.
//! let conn_sock = accept(&*target)?;
//! ioctl_fionbio(&conn_sock, true)?;
//! epoll.add(conn_sock, epoll::EventFlags::OUT | epoll::EventFlags::ET)?;
//! } else {
//! // Write a message to the stream and then unregister it.
//! write(&*target, b"hello\n")?;
//! let _ = epoll.del(target)?;
//! }
//! }
//! }
//! # }
//! # #[cfg(not(feature = "net"))]
//! # fn main() {}
//! ```
use super::super::c;
use super::super::conv::{ret, ret_owned_fd, ret_u32};
use crate::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(not(feature = "rustc-dep-of-std"))]
use crate::fd::{FromRawFd, IntoRawFd};
use crate::io;
use alloc::vec::Vec;
use bitflags::bitflags;
use core::convert::TryInto;
use core::fmt;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::{null, null_mut};
bitflags! {
/// `EPOLL_*` for use with [`Epoll::new`].
pub struct CreateFlags: c::c_int {
/// `EPOLL_CLOEXEC`
const CLOEXEC = c::EPOLL_CLOEXEC;
}
}
bitflags! {
/// `EPOLL*` for use with [`Epoll::add`].
#[derive(Default)]
pub struct EventFlags: u32 {
/// `EPOLLIN`
const IN = c::EPOLLIN as u32;
/// `EPOLLOUT`
const OUT = c::EPOLLOUT as u32;
/// `EPOLLPRI`
const PRI = c::EPOLLPRI as u32;
/// `EPOLLERR`
const ERR = c::EPOLLERR as u32;
/// `EPOLLHUP`
const HUP = c::EPOLLHUP as u32;
/// `EPOLLET`
const ET = c::EPOLLET as u32;
/// `EPOLLONESHOT`
const ONESHOT = c::EPOLLONESHOT as u32;
/// `EPOLLWAKEUP`
const WAKEUP = c::EPOLLWAKEUP as u32;
/// `EPOLLEXCLUSIVE`
#[cfg(not(target_os = "android"))]
const EXCLUSIVE = c::EPOLLEXCLUSIVE as u32;
}
}
/// A reference to a `T`.
pub struct Ref<'a, T> {
t: T,
_phantom: PhantomData<&'a T>,
}
impl<'a, T> Ref<'a, T> {
#[inline]
fn new(t: T) -> Self {
Self {
t,
_phantom: PhantomData,
}
}
#[inline]
fn consume(self) -> T {
self.t
}
}
impl<'a, T> Deref for Ref<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.t
}
}
impl<'a, T: fmt::Debug> fmt::Debug for Ref<'a, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.t.fmt(fmt)
}
}
/// A trait for data stored within an [`Epoll`] instance.
pub trait Context {
/// The type of an element owned by this context.
type Data;
/// The type of a value used to refer to an element owned by this context.
type Target: AsFd;
/// Assume ownership of `data`, and returning a `Target`.
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target>;
/// Encode `target` as a `u64`. The only requirement on this value is that
/// it be decodable by `decode`.
fn encode(&self, target: Ref<'_, Self::Target>) -> u64;
/// Decode `raw`, which is a value encoded by `encode`, into a `Target`.
///
/// # Safety
///
/// `raw` must be a `u64` value returned from `encode`, from the same
/// context, and within the context's lifetime.
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target>;
/// Release ownership of the value referred to by `target` and return it.
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data;
}
/// A type implementing [`Context`] where the `Data` type is `BorrowedFd<'a>`.
pub struct Borrowing<'a> {
_phantom: PhantomData<BorrowedFd<'a>>,
}
impl<'a> Context for Borrowing<'a> {
type Data = BorrowedFd<'a>;
type Target = BorrowedFd<'a>;
#[inline]
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
Ref::new(data)
}
#[inline]
fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
target.as_raw_fd() as u64
}
#[inline]
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
Ref::new(BorrowedFd::<'a>::borrow_raw(raw as RawFd))
}
#[inline]
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
target.consume()
}
}
/// A type implementing [`Context`] where the `Data` type is `T`, a type
/// implementing `From<OwnedFd>` and `From<T> for OwnedFd`.
///
/// This may be used with [`OwnedFd`], or higher-level types like
/// [`std::fs::File`] or [`std::net::TcpStream`].
#[cfg(not(feature = "rustc-dep-of-std"))]
pub struct Owning<'context, T: Into<OwnedFd> + From<OwnedFd>> {
_phantom: PhantomData<&'context T>,
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: Into<OwnedFd> + From<OwnedFd>> Owning<'context, T> {
/// Creates a new empty `Owning`.
#[allow(clippy::new_without_default)] // This is a specialized type that doesn't need to be generically constructible.
#[inline]
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> Context for Owning<'context, T> {
type Data = T;
type Target = BorrowedFd<'context>;
#[inline]
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
let fd: OwnedFd = data.into();
let raw_fd = fd.into_raw_fd();
// Safety: `epoll` will assign ownership of the file descriptor to the
// kernel epoll object. We use `Into<OwnedFd>`+`IntoRawFd` to consume
// the `Data` and extract the raw file descriptor and then "borrow" it
// with `borrow_raw` knowing that the borrow won't outlive the
// kernel epoll object.
unsafe { Ref::new(BorrowedFd::<'context>::borrow_raw(raw_fd)) }
}
#[inline]
fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
target.as_fd().as_raw_fd() as u64
}
#[inline]
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
Ref::new(BorrowedFd::<'context>::borrow_raw(raw as RawFd))
}
#[inline]
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
// The file descriptor was held by the kernel epoll object and is now
// being released, so we can create a new `OwnedFd` that assumes
// ownership.
let raw_fd = target.consume().as_raw_fd();
unsafe { T::from(OwnedFd::from_raw_fd(raw_fd).into()) }
}
}
/// An "epoll", an interface to an OS object allowing one to repeatedly wait
/// for events from a set of file descriptors efficiently.
pub struct Epoll<Context: self::Context> {
epoll_fd: OwnedFd,
context: Context,
}
impl<Context: self::Context> Epoll<Context> {
/// `epoll_create1(flags)`—Creates a new `Epoll`.
///
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
/// descriptor from being implicitly passed across `exec` boundaries.
#[inline]
#[doc(alias = "epoll_create1")]
pub fn new(flags: CreateFlags, context: Context) -> io::Result<Self> {
// Safety: We're calling `epoll_create1` via FFI and we know how it
// behaves.
unsafe {
Ok(Self {
epoll_fd: ret_owned_fd(c::epoll_create1(flags.bits()))?,
context,
})
}
}
/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an
/// `Epoll`.
///
/// This registers interest in any of the events set in `events` occurring
/// on the file descriptor associated with `data`.
#[doc(alias = "epoll_ctl")]
pub fn add(
&self,
data: Context::Data,
event_flags: EventFlags,
) -> io::Result<Ref<'_, Context::Target>> {
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let target = self.context.acquire(data);
let raw_fd = target.as_fd().as_raw_fd();
let encoded = self.context.encode(target);
ret(c::epoll_ctl(
self.epoll_fd.as_fd().as_raw_fd(),
c::EPOLL_CTL_ADD,
raw_fd,
&mut c::epoll_event {
events: event_flags.bits(),
r#u64: encoded,
},
))?;
Ok(self.context.decode(encoded))
}
}
/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in
/// this `Epoll`.
///
/// This sets the events of interest with `target` to `events`.
#[doc(alias = "epoll_ctl")]
pub fn mod_(
&self,
target: Ref<'_, Context::Target>,
event_flags: EventFlags,
) -> io::Result<()> {
let raw_fd = target.as_fd().as_raw_fd();
let encoded = self.context.encode(target);
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
ret(c::epoll_ctl(
self.epoll_fd.as_fd().as_raw_fd(),
c::EPOLL_CTL_MOD,
raw_fd,
&mut c::epoll_event {
events: event_flags.bits(),
r#u64: encoded,
},
))
}
}
/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in
/// this `Epoll`.
///
/// This also returns the owning `Data`.
#[doc(alias = "epoll_ctl")]
pub fn del(&self, target: Ref<'_, Context::Target>) -> io::Result<Context::Data> {
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let raw_fd = target.as_fd().as_raw_fd();
ret(c::epoll_ctl(
self.epoll_fd.as_fd().as_raw_fd(),
c::EPOLL_CTL_DEL,
raw_fd,
null_mut(),
))?;
}
Ok(self.context.release(target))
}
/// `epoll_wait(self, events, timeout)`—Waits for registered events of
/// interest.
///
/// For each event of interest, an element is written to `events`. On
/// success, this returns the number of written elements.
#[doc(alias = "epoll_wait")]
pub fn wait<'context>(
&'context self,
event_list: &mut EventVec<'context, Context>,
timeout: c::c_int,
) -> io::Result<()> {
// Safety: We're calling `epoll_wait` via FFI and we know how it
// behaves.
unsafe {
event_list.events.set_len(0);
let nfds = ret_u32(c::epoll_wait(
self.epoll_fd.as_fd().as_raw_fd(),
event_list.events.as_mut_ptr().cast::<c::epoll_event>(),
event_list.events.capacity().try_into().unwrap_or(i32::MAX),
timeout,
))?;
event_list.events.set_len(nfds as usize);
event_list.context = &self.context;
}
Ok(())
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> AsRawFd for Epoll<Owning<'context, T>> {
fn as_raw_fd(&self) -> RawFd {
self.epoll_fd.as_raw_fd()
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> IntoRawFd for Epoll<Owning<'context, T>> {
fn into_raw_fd(self) -> RawFd {
self.epoll_fd.into_raw_fd()
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> FromRawFd for Epoll<Owning<'context, T>> {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
epoll_fd: OwnedFd::from_raw_fd(fd),
context: Owning::new(),
}
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> AsFd for Epoll<Owning<'context, T>> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.epoll_fd.as_fd()
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> From<Epoll<Owning<'context, T>>>
for OwnedFd
{
fn from(epoll: Epoll<Owning<'context, T>>) -> Self {
epoll.epoll_fd
}
}
#[cfg(not(feature = "rustc-dep-of-std"))]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> From<OwnedFd>
for Epoll<Owning<'context, T>>
{
fn from(fd: OwnedFd) -> Self {
Self {
epoll_fd: fd,
context: Owning::new(),
}
}
}
/// An iterator over the `Event`s in an `EventVec`.
pub struct Iter<'context, Context: self::Context> {
iter: core::slice::Iter<'context, Event>,
context: *const Context,
_phantom: PhantomData<&'context Context>,
}
impl<'context, Context: self::Context> Iterator for Iter<'context, Context> {
type Item = (EventFlags, Ref<'context, Context::Target>);
fn next(&mut self) -> Option<Self::Item> {
// Safety: `self.context` is guaranteed to be valid because we hold
// `'context` for it. And we know this event is associated with this
// context because `wait` sets both.
self.iter.next().map(|event| {
(event.event_flags, unsafe {
(*self.context).decode(event.encoded)
})
})
}
}
/// A record of an event that occurred.
#[repr(C)]
#[cfg_attr(
any(
all(
target_arch = "x86",
not(target_env = "musl"),
not(target_os = "android"),
),
target_arch = "x86_64",
),
repr(packed)
)]
struct Event {
// Match the layout of `c::epoll_event`. We just use a `u64` instead of
// the full union; `Context` implementations will simply need to deal with
// casting the value into and out of the `u64` themselves.
event_flags: EventFlags,
encoded: u64,
}
/// A vector of `Event`s, plus context for interpreting them.
pub struct EventVec<'context, Context: self::Context> {
events: Vec<Event>,
context: *const Context,
_phantom: PhantomData<&'context Context>,
}
impl<'context, Context: self::Context> EventVec<'context, Context> {
/// Constructs an `EventVec` with memory for `capacity` `Event`s.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
events: Vec::with_capacity(capacity),
context: null(),
_phantom: PhantomData,
}
}
/// Returns the current `Event` capacity of this `EventVec`.
#[inline]
pub fn capacity(&self) -> usize {
self.events.capacity()
}
/// Reserves enough memory for at least `additional` more `Event`s.
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.events.reserve(additional);
}
/// Reserves enough memory for exactly `additional` more `Event`s.
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.events.reserve_exact(additional);
}
/// Clears all the `Events` out of this `EventVec`.
#[inline]
pub fn clear(&mut self) {
self.events.clear();
}
/// Shrinks the capacity of this `EventVec` as much as possible.
#[inline]
pub fn shrink_to_fit(&mut self) {
self.events.shrink_to_fit();
}
/// Returns an iterator over the `Event`s in this `EventVec`.
#[inline]
pub fn iter(&self) -> Iter<'_, Context> {
Iter {
iter: self.events.iter(),
context: self.context,
_phantom: PhantomData,
}
}
/// Returns the number of `Event`s logically contained in this `EventVec`.
#[inline]
pub fn len(&mut self) -> usize {
self.events.len()
}
/// Tests whether this `EventVec` is logically empty.
#[inline]
pub fn is_empty(&mut self) -> bool {
self.events.is_empty()
}
}
impl<'context, Context: self::Context> IntoIterator for &'context EventVec<'context, Context> {
type IntoIter = Iter<'context, Context>;
type Item = (EventFlags, Ref<'context, Context::Target>);
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

1106
vendor/rustix/src/backend/libc/io/errno.rs vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,87 @@
//! The following is derived from Rust's
//! library/std/src/sys/unix/io.rs
//! dca3f1b786efd27be3b325ed1e01e247aa589c3b.
#![allow(missing_docs)]
use super::super::c;
use core::marker::PhantomData;
use core::slice;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct IoSlice<'a> {
vec: c::iovec,
_p: PhantomData<&'a [u8]>,
}
impl<'a> IoSlice<'a> {
#[inline]
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
IoSlice {
vec: c::iovec {
iov_base: buf.as_ptr() as *mut u8 as *mut c::c_void,
iov_len: buf.len(),
},
_p: PhantomData,
}
}
#[inline]
pub fn advance(&mut self, n: usize) {
if self.vec.iov_len < n {
panic!("advancing IoSlice beyond its length");
}
unsafe {
self.vec.iov_len -= n;
self.vec.iov_base = self.vec.iov_base.add(n);
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
}
#[repr(transparent)]
pub struct IoSliceMut<'a> {
vec: c::iovec,
_p: PhantomData<&'a mut [u8]>,
}
impl<'a> IoSliceMut<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
IoSliceMut {
vec: c::iovec {
iov_base: buf.as_mut_ptr() as *mut c::c_void,
iov_len: buf.len(),
},
_p: PhantomData,
}
}
#[inline]
pub fn advance(&mut self, n: usize) {
if self.vec.iov_len < n {
panic!("advancing IoSliceMut beyond its length");
}
unsafe {
self.vec.iov_len -= n;
self.vec.iov_base = self.vec.iov_base.add(n);
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
}

View file

@ -0,0 +1,13 @@
pub(crate) mod errno;
#[cfg(not(windows))]
#[cfg(not(feature = "std"))]
pub(crate) mod io_slice;
pub(crate) mod poll_fd;
#[cfg(not(windows))]
pub(crate) mod types;
#[cfg_attr(windows, path = "windows_syscalls.rs")]
pub(crate) mod syscalls;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub mod epoll;

View file

@ -0,0 +1,136 @@
use super::super::c;
use super::super::conv::borrowed_fd;
#[cfg(windows)]
use super::super::fd::RawFd;
use super::super::fd::{AsFd, AsRawFd, BorrowedFd, LibcFd};
use bitflags::bitflags;
use core::marker::PhantomData;
#[cfg(windows)]
use std::fmt;
bitflags! {
/// `POLL*` flags for use with [`poll`].
///
/// [`poll`]: crate::io::poll
pub struct PollFlags: c::c_short {
/// `POLLIN`
const IN = c::POLLIN;
/// `POLLPRI`
#[cfg(not(target_os = "wasi"))]
const PRI = c::POLLPRI;
/// `POLLOUT`
const OUT = c::POLLOUT;
/// `POLLRDNORM`
#[cfg(not(target_os = "redox"))]
const RDNORM = c::POLLRDNORM;
/// `POLLWRNORM`
#[cfg(not(target_os = "redox"))]
const WRNORM = c::POLLWRNORM;
/// `POLLRDBAND`
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
const RDBAND = c::POLLRDBAND;
/// `POLLWRBAND`
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
const WRBAND = c::POLLWRBAND;
/// `POLLERR`
const ERR = c::POLLERR;
/// `POLLHUP`
const HUP = c::POLLHUP;
/// `POLLNVAL`
const NVAL = c::POLLNVAL;
/// `POLLRDHUP`
#[cfg(all(
any(target_os = "android", target_os = "linux"),
not(any(target_arch = "sparc", target_arch = "sparc64"))),
)]
const RDHUP = c::POLLRDHUP;
}
}
/// `struct pollfd`—File descriptor and flags for use with [`poll`].
///
/// [`poll`]: crate::io::poll
#[doc(alias = "pollfd")]
#[derive(Clone)]
#[cfg_attr(not(windows), derive(Debug))]
#[repr(transparent)]
pub struct PollFd<'fd> {
pollfd: c::pollfd,
_phantom: PhantomData<BorrowedFd<'fd>>,
}
#[cfg(windows)]
impl<'fd> fmt::Debug for PollFd<'fd> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("pollfd")
.field("fd", &self.pollfd.fd)
.field("events", &self.pollfd.events)
.field("revents", &self.pollfd.revents)
.finish()
}
}
impl<'fd> PollFd<'fd> {
/// Constructs a new `PollFd` holding `fd` and `events`.
#[inline]
pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> Self {
Self::from_borrowed_fd(fd.as_fd(), events)
}
/// Sets the contained file descriptor to `fd`.
#[inline]
pub fn set_fd<Fd: AsFd>(&mut self, fd: &'fd Fd) {
self.pollfd.fd = fd.as_fd().as_raw_fd() as LibcFd;
}
/// Clears the ready events.
#[inline]
pub fn clear_revents(&mut self) {
self.pollfd.revents = 0;
}
/// Constructs a new `PollFd` holding `fd` and `events`.
///
/// This is the same as `new`, but can be used to avoid borrowing the
/// `BorrowedFd`, which can be tricky in situations where the `BorrowedFd`
/// is a temporary.
#[inline]
pub fn from_borrowed_fd(fd: BorrowedFd<'fd>, events: PollFlags) -> Self {
Self {
pollfd: c::pollfd {
fd: borrowed_fd(fd),
events: events.bits(),
revents: 0,
},
_phantom: PhantomData,
}
}
/// Returns the ready events.
#[inline]
pub fn revents(&self) -> PollFlags {
// Use `unwrap()` here because in theory we know we know all the bits
// the OS might set here, but OS's have added extensions in the past.
PollFlags::from_bits(self.pollfd.revents).unwrap()
}
}
#[cfg(not(windows))]
impl<'fd> AsFd for PollFd<'fd> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
// Safety: Our constructors and `set_fd` require `pollfd.fd` to be
// valid for the `fd lifetime.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
}
}
#[cfg(windows)]
impl<'fd> io_lifetimes::AsSocket for PollFd<'fd> {
#[inline]
fn as_socket(&self) -> BorrowedFd<'_> {
// Safety: Our constructors and `set_fd` require `pollfd.fd` to be
// valid for the `fd lifetime.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd as RawFd) }
}
}

View file

@ -0,0 +1,533 @@
//! libc syscalls supporting `rustix::io`.
use super::super::c;
#[cfg(any(target_os = "android", target_os = "linux"))]
use super::super::conv::syscall_ret_owned_fd;
use super::super::conv::{
borrowed_fd, ret, ret_c_int, ret_discarded_fd, ret_owned_fd, ret_ssize_t,
};
use super::super::offset::{libc_pread, libc_pwrite};
#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))]
use super::super::offset::{libc_preadv, libc_pwritev};
#[cfg(all(target_os = "linux", target_env = "gnu"))]
use super::super::offset::{libc_preadv2, libc_pwritev2};
use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(not(any(target_os = "aix", target_os = "wasi")))]
use crate::io::DupFlags;
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "wasi"
)))]
use crate::io::PipeFlags;
use crate::io::{self, FdFlags, IoSlice, IoSliceMut, PollFd};
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::io::{EventfdFlags, IoSliceRaw, ReadWriteFlags, SpliceFlags};
use core::cmp::min;
use core::convert::TryInto;
use core::mem::MaybeUninit;
#[cfg(any(target_os = "android", target_os = "linux"))]
use core::ptr;
#[cfg(all(feature = "fs", feature = "net"))]
use libc_errno::errno;
pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> {
let nread = unsafe {
ret_ssize_t(c::read(
borrowed_fd(fd),
buf.as_mut_ptr().cast(),
min(buf.len(), READ_LIMIT),
))?
};
Ok(nread as usize)
}
pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
let nwritten = unsafe {
ret_ssize_t(c::write(
borrowed_fd(fd),
buf.as_ptr().cast(),
min(buf.len(), READ_LIMIT),
))?
};
Ok(nwritten as usize)
}
pub(crate) fn pread(fd: BorrowedFd<'_>, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let len = min(buf.len(), READ_LIMIT);
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
let nread = unsafe {
ret_ssize_t(libc_pread(
borrowed_fd(fd),
buf.as_mut_ptr().cast(),
len,
offset,
))?
};
Ok(nread as usize)
}
pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize> {
let len = min(buf.len(), READ_LIMIT);
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
let nwritten = unsafe {
ret_ssize_t(libc_pwrite(
borrowed_fd(fd),
buf.as_ptr().cast(),
len,
offset,
))?
};
Ok(nwritten as usize)
}
pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
let nread = unsafe {
ret_ssize_t(c::readv(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()) as c::c_int,
))?
};
Ok(nread as usize)
}
pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice]) -> io::Result<usize> {
let nwritten = unsafe {
ret_ssize_t(c::writev(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()) as c::c_int,
))?
};
Ok(nwritten as usize)
}
#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))]
pub(crate) fn preadv(
fd: BorrowedFd<'_>,
bufs: &mut [IoSliceMut],
offset: u64,
) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
let nread = unsafe {
ret_ssize_t(libc_preadv(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()) as c::c_int,
offset,
))?
};
Ok(nread as usize)
}
#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "solaris")))]
pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
let nwritten = unsafe {
ret_ssize_t(libc_pwritev(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()) as c::c_int,
offset,
))?
};
Ok(nwritten as usize)
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(crate) fn preadv2(
fd: BorrowedFd<'_>,
bufs: &mut [IoSliceMut],
offset: u64,
flags: ReadWriteFlags,
) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
let nread = unsafe {
ret_ssize_t(libc_preadv2(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()) as c::c_int,
offset,
flags.bits(),
))?
};
Ok(nread as usize)
}
/// At present, `libc` only has `preadv2` defined for glibc. On other
/// ABIs, `ReadWriteFlags` has no flags defined, and we use plain `preadv`.
#[cfg(any(
target_os = "android",
all(target_os = "linux", not(target_env = "gnu")),
))]
#[inline]
pub(crate) fn preadv2(
fd: BorrowedFd<'_>,
bufs: &mut [IoSliceMut],
offset: u64,
flags: ReadWriteFlags,
) -> io::Result<usize> {
assert!(flags.is_empty());
preadv(fd, bufs, offset)
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(crate) fn pwritev2(
fd: BorrowedFd<'_>,
bufs: &[IoSlice],
offset: u64,
flags: ReadWriteFlags,
) -> io::Result<usize> {
// Silently cast; we'll get `EINVAL` if the value is negative.
let offset = offset as i64;
let nwritten = unsafe {
ret_ssize_t(libc_pwritev2(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()) as c::c_int,
offset,
flags.bits(),
))?
};
Ok(nwritten as usize)
}
/// At present, `libc` only has `pwritev2` defined for glibc. On other
/// ABIs, `ReadWriteFlags` has no flags defined, and we use plain `pwritev`.
#[cfg(any(
target_os = "android",
all(target_os = "linux", not(target_env = "gnu")),
))]
#[inline]
pub(crate) fn pwritev2(
fd: BorrowedFd<'_>,
bufs: &[IoSlice],
offset: u64,
flags: ReadWriteFlags,
) -> io::Result<usize> {
assert!(flags.is_empty());
pwritev(fd, bufs, offset)
}
// These functions are derived from Rust's library/std/src/sys/unix/fd.rs at
// revision a77da2d454e6caa227a85b16410b95f93495e7e0.
// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, with the
// man page quoting that if the count of bytes to read is greater than
// `SSIZE_MAX` the result is "unspecified".
//
// On macOS, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size larger
// than or equal to `INT_MAX`. To handle both of these the read size is capped
// on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c::c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = c::ssize_t::MAX as usize;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
const fn max_iov() -> usize {
c::IOV_MAX as usize
}
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
const fn max_iov() -> usize {
c::UIO_MAXIOV as usize
}
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
const fn max_iov() -> usize {
16 // The minimum value required by POSIX.
}
pub(crate) unsafe fn close(raw_fd: RawFd) {
let _ = c::close(raw_fd as c::c_int);
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn eventfd(initval: u32, flags: EventfdFlags) -> io::Result<OwnedFd> {
unsafe { syscall_ret_owned_fd(c::syscall(c::SYS_eventfd2, initval, flags.bits())) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn ioctl_blksszget(fd: BorrowedFd) -> io::Result<u32> {
let mut result = MaybeUninit::<c::c_uint>::uninit();
unsafe {
ret(c::ioctl(borrowed_fd(fd), c::BLKSSZGET, result.as_mut_ptr()))?;
Ok(result.assume_init() as u32)
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn ioctl_blkpbszget(fd: BorrowedFd) -> io::Result<u32> {
let mut result = MaybeUninit::<c::c_uint>::uninit();
unsafe {
ret(c::ioctl(
borrowed_fd(fd),
c::BLKPBSZGET,
result.as_mut_ptr(),
))?;
Ok(result.assume_init() as u32)
}
}
#[cfg(not(target_os = "redox"))]
pub(crate) fn ioctl_fionread(fd: BorrowedFd<'_>) -> io::Result<u64> {
let mut nread = MaybeUninit::<c::c_int>::uninit();
unsafe {
ret(c::ioctl(borrowed_fd(fd), c::FIONREAD, nread.as_mut_ptr()))?;
// `FIONREAD` returns the number of bytes silently casted to a `c_int`,
// even when this is lossy. The best we can do is convert it back to a
// `u64` without sign-extending it back first.
Ok(u64::from(nread.assume_init() as c::c_uint))
}
}
pub(crate) fn ioctl_fionbio(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
unsafe {
let data = value as c::c_int;
ret(c::ioctl(borrowed_fd(fd), c::FIONBIO, &data))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
#[cfg(all(feature = "fs", feature = "net"))]
pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
let mut not_socket = false;
if read {
// Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
// the read side is shut down; an `EWOULDBLOCK` indicates the read
// side is still open.
match unsafe {
c::recv(
borrowed_fd(fd),
MaybeUninit::<[u8; 1]>::uninit()
.as_mut_ptr()
.cast::<c::c_void>(),
1,
c::MSG_PEEK | c::MSG_DONTWAIT,
)
} {
0 => read = false,
-1 => {
#[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
match errno().0 {
c::EAGAIN | c::EWOULDBLOCK => (),
c::ENOTSOCK => not_socket = true,
err => return Err(io::Errno(err)),
}
}
_ => (),
}
}
if write && !not_socket {
// Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
// the write side is shut down.
if unsafe { c::send(borrowed_fd(fd), [].as_ptr(), 0, c::MSG_DONTWAIT) } == -1 {
#[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
match errno().0 {
c::EAGAIN | c::EWOULDBLOCK => (),
c::ENOTSOCK => (),
c::EPIPE => write = false,
err => return Err(io::Errno(err)),
}
}
}
Ok((read, write))
}
#[cfg(target_os = "wasi")]
#[cfg(all(feature = "fs", feature = "net"))]
pub(crate) fn is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
todo!("Implement is_read_write for WASI in terms of fd_fdstat_get");
}
pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFD)).map(FdFlags::from_bits_truncate) }
}
pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFD, flags.bits())) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD_CLOEXEC, min)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::dup(borrowed_fd(fd))) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
unsafe { ret_discarded_fd(c::dup2(borrowed_fd(fd), borrowed_fd(new.as_fd()))) }
}
#[cfg(not(any(
target_os = "aix",
target_os = "android",
target_os = "dragonfly",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "redox",
target_os = "wasi",
)))]
pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
unsafe {
ret_discarded_fd(c::dup3(
borrowed_fd(fd),
borrowed_fd(new.as_fd()),
flags.bits(),
))
}
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "redox",
))]
pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()> {
// Android 5.0 has `dup3`, but libc doesn't have bindings. Emulate it
// using `dup2`. We don't need to worry about the difference between
// `dup2` and `dup3` when the file descriptors are equal because we
// have an `&mut OwnedFd` which means `fd` doesn't alias it.
dup2(fd, new)
}
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub(crate) fn ioctl_fioclex(fd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::ioctl(borrowed_fd(fd), c::FIOCLEX)) }
}
#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
pub(crate) fn ioctl_tiocexcl(fd: BorrowedFd) -> io::Result<()> {
unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCEXCL as _)) }
}
#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
pub(crate) fn ioctl_tiocnxcl(fd: BorrowedFd) -> io::Result<()> {
unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCNXCL as _)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> {
unsafe {
let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::pipe(result.as_mut_ptr().cast::<i32>()))?;
let [p0, p1] = result.assume_init();
Ok((p0, p1))
}
}
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "wasi"
)))]
pub(crate) fn pipe_with(flags: PipeFlags) -> io::Result<(OwnedFd, OwnedFd)> {
unsafe {
let mut result = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::pipe2(result.as_mut_ptr().cast::<i32>(), flags.bits()))?;
let [p0, p1] = result.assume_init();
Ok((p0, p1))
}
}
#[inline]
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
.map(|nready| nready as usize)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub fn splice(
fd_in: BorrowedFd,
off_in: Option<&mut u64>,
fd_out: BorrowedFd,
off_out: Option<&mut u64>,
len: usize,
flags: SpliceFlags,
) -> io::Result<usize> {
let off_in = off_in
.map(|off| (off as *mut u64).cast())
.unwrap_or(ptr::null_mut());
let off_out = off_out
.map(|off| (off as *mut u64).cast())
.unwrap_or(ptr::null_mut());
ret_ssize_t(unsafe {
c::splice(
borrowed_fd(fd_in),
off_in,
borrowed_fd(fd_out),
off_out,
len,
flags.bits(),
)
})
.map(|spliced| spliced as usize)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub unsafe fn vmsplice(
fd: BorrowedFd,
bufs: &[IoSliceRaw],
flags: SpliceFlags,
) -> io::Result<usize> {
ret_ssize_t(c::vmsplice(
borrowed_fd(fd),
bufs.as_ptr().cast::<c::iovec>(),
min(bufs.len(), max_iov()),
flags.bits(),
))
.map(|spliced| spliced as usize)
}

View file

@ -0,0 +1,164 @@
use super::super::c;
use bitflags::bitflags;
#[cfg(any(target_os = "android", target_os = "linux"))]
use core::marker::PhantomData;
bitflags! {
/// `FD_*` constants for use with [`fcntl_getfd`] and [`fcntl_setfd`].
///
/// [`fcntl_getfd`]: crate::io::fcntl_getfd
/// [`fcntl_setfd`]: crate::io::fcntl_setfd
pub struct FdFlags: c::c_int {
/// `FD_CLOEXEC`
const CLOEXEC = c::FD_CLOEXEC;
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
bitflags! {
/// `RWF_*` constants for use with [`preadv2`] and [`pwritev2`].
///
/// [`preadv2`]: crate::io::preadv2
/// [`pwritev2`]: crate::io::pwritev
pub struct ReadWriteFlags: c::c_int {
/// `RWF_DSYNC` (since Linux 4.7)
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const DSYNC = c::RWF_DSYNC;
/// `RWF_HIPRI` (since Linux 4.6)
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const HIPRI = c::RWF_HIPRI;
/// `RWF_SYNC` (since Linux 4.7)
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const SYNC = c::RWF_SYNC;
/// `RWF_NOWAIT` (since Linux 4.14)
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const NOWAIT = c::RWF_NOWAIT;
/// `RWF_APPEND` (since Linux 4.16)
#[cfg(all(target_os = "linux", target_env = "gnu"))]
const APPEND = c::RWF_APPEND;
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
bitflags! {
/// `SPLICE_F_*` constants for use with [`splice`] and [`vmsplice`].
pub struct SpliceFlags: c::c_uint {
/// `SPLICE_F_MOVE`
const MOVE = c::SPLICE_F_MOVE;
/// `SPLICE_F_NONBLOCK`
const NONBLOCK = c::SPLICE_F_NONBLOCK;
/// `SPLICE_F_MORE`
const MORE = c::SPLICE_F_MORE;
/// `SPLICE_F_GIFT`
const GIFT = c::SPLICE_F_GIFT;
}
}
#[cfg(not(target_os = "wasi"))]
bitflags! {
/// `O_*` constants for use with [`dup2`].
///
/// [`dup2`]: crate::io::dup2
pub struct DupFlags: c::c_int {
/// `O_CLOEXEC`
#[cfg(not(any(
target_os = "android",
target_os = "ios",
target_os = "macos",
target_os = "redox",
)))] // Android 5.0 has dup3, but libc doesn't have bindings
const CLOEXEC = c::O_CLOEXEC;
}
}
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "wasi")))]
bitflags! {
/// `O_*` constants for use with [`pipe_with`].
///
/// [`pipe_with`]: crate::io::pipe_with
pub struct PipeFlags: c::c_int {
/// `O_CLOEXEC`
const CLOEXEC = c::O_CLOEXEC;
/// `O_DIRECT`
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const DIRECT = c::O_DIRECT;
/// `O_NONBLOCK`
const NONBLOCK = c::O_NONBLOCK;
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
bitflags! {
/// `EFD_*` flags for use with [`eventfd`].
///
/// [`eventfd`]: crate::io::eventfd
pub struct EventfdFlags: c::c_int {
/// `EFD_CLOEXEC`
const CLOEXEC = c::EFD_CLOEXEC;
/// `EFD_NONBLOCK`
const NONBLOCK = c::EFD_NONBLOCK;
/// `EFD_SEMAPHORE`
const SEMAPHORE = c::EFD_SEMAPHORE;
}
}
/// `PIPE_BUF`—The maximum size of a write to a pipe guaranteed to be atomic.
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
pub const PIPE_BUF: usize = c::PIPE_BUF;
#[cfg(not(any(windows, target_os = "redox")))]
pub(crate) const AT_FDCWD: c::c_int = c::AT_FDCWD;
#[cfg(not(windows))]
pub(crate) const STDIN_FILENO: c::c_int = c::STDIN_FILENO;
#[cfg(not(windows))]
pub(crate) const STDOUT_FILENO: c::c_int = c::STDOUT_FILENO;
#[cfg(not(windows))]
pub(crate) const STDERR_FILENO: c::c_int = c::STDERR_FILENO;
/// A buffer type used with `vmsplice`.
/// It is guaranteed to be ABI compatible with the iovec type on Unix platforms and WSABUF on Windows.
/// Unlike `IoSlice` and `IoSliceMut` it is semantically like a raw pointer,
/// and therefore can be shared or mutated as needed.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[repr(transparent)]
pub struct IoSliceRaw<'a> {
_buf: c::iovec,
_lifetime: PhantomData<&'a ()>,
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl<'a> IoSliceRaw<'a> {
/// Creates a new IoSlice wrapping a byte slice.
pub fn from_slice(buf: &'a [u8]) -> Self {
IoSliceRaw {
_buf: c::iovec {
iov_base: buf.as_ptr() as *mut u8 as *mut c::c_void,
iov_len: buf.len() as _,
},
_lifetime: PhantomData,
}
}
/// Creates a new IoSlice wrapping a mutable byte slice.
pub fn from_slice_mut(buf: &'a mut [u8]) -> Self {
IoSliceRaw {
_buf: c::iovec {
iov_base: buf.as_mut_ptr() as *mut c::c_void,
iov_len: buf.len() as _,
},
_lifetime: PhantomData,
}
}
}

View file

@ -0,0 +1,39 @@
//! Windows system calls in the `io` module.
use super::super::c;
use super::super::conv::{borrowed_fd, ret, ret_c_int};
use super::super::fd::LibcFd;
use crate::fd::{BorrowedFd, RawFd};
use crate::io;
use crate::io::PollFd;
use core::convert::TryInto;
use core::mem::MaybeUninit;
pub(crate) unsafe fn close(raw_fd: RawFd) {
let _ = c::close(raw_fd as LibcFd);
}
pub(crate) fn ioctl_fionread(fd: BorrowedFd<'_>) -> io::Result<u64> {
let mut nread = MaybeUninit::<c::c_ulong>::uninit();
unsafe {
ret(c::ioctl(borrowed_fd(fd), c::FIONREAD, nread.as_mut_ptr()))?;
Ok(u64::from(nread.assume_init()))
}
}
pub(crate) fn ioctl_fionbio(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
unsafe {
let mut data = value as c::c_uint;
ret(c::ioctl(borrowed_fd(fd), c::FIONBIO, &mut data))
}
}
pub(crate) fn poll(fds: &mut [PollFd<'_>], timeout: c::c_int) -> io::Result<usize> {
let nfds = fds
.len()
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
ret_c_int(unsafe { c::poll(fds.as_mut_ptr().cast(), nfds, timeout) })
.map(|nready| nready as usize)
}

View file

@ -0,0 +1,77 @@
//! `io_lifetimes` types for Windows assuming that Fd is Socket.
//!
//! We can make this assumption since `rustix` supports only `rustix::net` on
//! Windows.
pub use io_lifetimes::{BorrowedSocket as BorrowedFd, OwnedSocket as OwnedFd};
#[cfg(feature = "std")]
pub use std::os::windows::io::RawSocket as RawFd;
pub(crate) use windows_sys::Win32::Networking::WinSock::SOCKET as LibcFd;
// Re-export the `Socket` traits so that users can implement them.
pub use io_lifetimes::AsSocket;
/// A version of [`AsRawFd`] for use with Winsock2 API.
///
/// [`AsRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsRawFd.html
pub trait AsRawFd {
/// A version of [`as_raw_fd`] for use with Winsock2 API.
///
/// [`as_raw_fd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html#tymethod.as_raw_fd
fn as_raw_fd(&self) -> RawFd;
}
#[cfg(feature = "std")]
impl<T: std::os::windows::io::AsRawSocket> AsRawFd for T {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.as_raw_socket()
}
}
/// A version of [`IntoRawFd`] for use with Winsock2 API.
///
/// [`IntoRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.IntoRawFd.html
pub trait IntoRawFd {
/// A version of [`into_raw_fd`] for use with Winsock2 API.
///
/// [`into_raw_fd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html#tymethod.into_raw_fd
fn into_raw_fd(self) -> RawFd;
}
#[cfg(feature = "std")]
impl<T: std::os::windows::io::IntoRawSocket> IntoRawFd for T {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.into_raw_socket()
}
}
/// A version of [`FromRawFd`] for use with Winsock2 API.
///
/// [`FromRawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html
pub trait FromRawFd {
/// A version of [`from_raw_fd`] for use with Winsock2 API.
///
/// [`from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.FromRawFd.html#tymethod.from_raw_fd
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self;
}
#[cfg(feature = "std")]
impl<T: std::os::windows::io::FromRawSocket> FromRawFd for T {
#[inline]
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
Self::from_raw_socket(raw_fd)
}
}
/// A version of [`AsFd`] for use with Winsock2 API.
///
/// [`AsFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/trait.AsFd.html
pub trait AsFd {
/// An `as_fd` function for Winsock2, where a `Fd` is a `Socket`.
fn as_fd(&self) -> BorrowedFd;
}
impl<T: AsSocket> AsFd for T {
#[inline]
fn as_fd(&self) -> BorrowedFd {
self.as_socket()
}
}

View file

@ -0,0 +1 @@
pub(crate) mod syscalls;

View file

@ -0,0 +1,55 @@
//! libc syscalls supporting `rustix::io_uring`.
use super::super::c;
use super::super::conv::{borrowed_fd, syscall_ret, syscall_ret_owned_fd, syscall_ret_u32};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
use crate::io_uring::{io_uring_params, IoringEnterFlags, IoringRegisterOp};
use linux_raw_sys::general::{__NR_io_uring_enter, __NR_io_uring_register, __NR_io_uring_setup};
#[inline]
pub(crate) fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> io::Result<OwnedFd> {
unsafe {
syscall_ret_owned_fd(c::syscall(
__NR_io_uring_setup as _,
entries as usize,
params,
))
}
}
#[inline]
pub(crate) unsafe fn io_uring_register(
fd: BorrowedFd<'_>,
opcode: IoringRegisterOp,
arg: *const c::c_void,
nr_args: u32,
) -> io::Result<()> {
syscall_ret(c::syscall(
__NR_io_uring_register as _,
borrowed_fd(fd),
opcode as u32 as usize,
arg,
nr_args as usize,
))
}
#[inline]
pub(crate) unsafe fn io_uring_enter(
fd: BorrowedFd<'_>,
to_submit: u32,
min_complete: u32,
flags: IoringEnterFlags,
arg: *const c::c_void,
size: usize,
) -> io::Result<u32> {
syscall_ret_u32(c::syscall(
__NR_io_uring_enter as _,
borrowed_fd(fd),
to_submit as usize,
min_complete as usize,
flags.bits() as usize,
arg,
size,
))
}

View file

@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View file

@ -0,0 +1,218 @@
//! libc syscalls supporting `rustix::mm`.
use super::super::c;
#[cfg(any(target_os = "android", target_os = "linux"))]
use super::super::conv::syscall_ret_owned_fd;
use super::super::conv::{borrowed_fd, no_fd, ret};
use super::super::offset::libc_mmap;
#[cfg(not(target_os = "redox"))]
use super::types::Advice;
#[cfg(target_os = "linux")]
use super::types::MremapFlags;
use super::types::{MapFlags, MprotectFlags, MsyncFlags, ProtFlags};
#[cfg(any(target_os = "android", target_os = "linux"))]
use super::types::{MlockFlags, UserfaultfdFlags};
use crate::fd::BorrowedFd;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::fd::OwnedFd;
use crate::io;
#[cfg(not(target_os = "redox"))]
pub(crate) fn madvise(addr: *mut c::c_void, len: usize, advice: Advice) -> io::Result<()> {
// On Linux platforms, `MADV_DONTNEED` has the same value as
// `POSIX_MADV_DONTNEED` but different behavior. We remap it to a different
// value, and check for it here.
#[cfg(target_os = "linux")]
if let Advice::LinuxDontNeed = advice {
return unsafe { ret(c::madvise(addr, len, c::MADV_DONTNEED)) };
}
#[cfg(not(target_os = "android"))]
{
let err = unsafe { c::posix_madvise(addr, len, advice as c::c_int) };
// `posix_madvise` returns its error status rather than using `errno`.
if err == 0 {
Ok(())
} else {
Err(io::Errno(err))
}
}
#[cfg(target_os = "android")]
{
if let Advice::DontNeed = advice {
// Do nothing. Linux's `MADV_DONTNEED` isn't the same as
// `POSIX_MADV_DONTNEED`, so just discard `MADV_DONTNEED`.
Ok(())
} else {
unsafe { ret(c::madvise(addr, len, advice as c::c_int)) }
}
}
}
pub(crate) unsafe fn msync(addr: *mut c::c_void, len: usize, flags: MsyncFlags) -> io::Result<()> {
let err = c::msync(addr, len, flags.bits());
// `msync` returns its error status rather than using `errno`.
if err == 0 {
Ok(())
} else {
Err(io::Errno(err))
}
}
/// # Safety
///
/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working
/// with memory pointed to by raw pointers is unsafe.
pub(crate) unsafe fn mmap(
ptr: *mut c::c_void,
len: usize,
prot: ProtFlags,
flags: MapFlags,
fd: BorrowedFd<'_>,
offset: u64,
) -> io::Result<*mut c::c_void> {
let res = libc_mmap(
ptr,
len,
prot.bits(),
flags.bits(),
borrowed_fd(fd),
offset as i64,
);
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
/// # Safety
///
/// `mmap` is primarily unsafe due to the `addr` parameter, as anything working
/// with memory pointed to by raw pointers is unsafe.
pub(crate) unsafe fn mmap_anonymous(
ptr: *mut c::c_void,
len: usize,
prot: ProtFlags,
flags: MapFlags,
) -> io::Result<*mut c::c_void> {
let res = libc_mmap(
ptr,
len,
prot.bits(),
flags.bits() | c::MAP_ANONYMOUS,
no_fd(),
0,
);
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
pub(crate) unsafe fn mprotect(
ptr: *mut c::c_void,
len: usize,
flags: MprotectFlags,
) -> io::Result<()> {
ret(c::mprotect(ptr, len, flags.bits()))
}
pub(crate) unsafe fn munmap(ptr: *mut c::c_void, len: usize) -> io::Result<()> {
ret(c::munmap(ptr, len))
}
/// # Safety
///
/// `mremap` is primarily unsafe due to the `old_address` parameter, as
/// anything working with memory pointed to by raw pointers is unsafe.
#[cfg(target_os = "linux")]
pub(crate) unsafe fn mremap(
old_address: *mut c::c_void,
old_size: usize,
new_size: usize,
flags: MremapFlags,
) -> io::Result<*mut c::c_void> {
let res = c::mremap(old_address, old_size, new_size, flags.bits());
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
/// # Safety
///
/// `mremap_fixed` is primarily unsafe due to the `old_address` and
/// `new_address` parameters, as anything working with memory pointed to by raw
/// pointers is unsafe.
#[cfg(target_os = "linux")]
pub(crate) unsafe fn mremap_fixed(
old_address: *mut c::c_void,
old_size: usize,
new_size: usize,
flags: MremapFlags,
new_address: *mut c::c_void,
) -> io::Result<*mut c::c_void> {
let res = c::mremap(
old_address,
old_size,
new_size,
flags.bits() | c::MAP_FIXED,
new_address,
);
if res == c::MAP_FAILED {
Err(io::Errno::last_os_error())
} else {
Ok(res)
}
}
/// # Safety
///
/// `mlock` operates on raw pointers and may round out to the nearest page
/// boundaries.
#[inline]
pub(crate) unsafe fn mlock(addr: *mut c::c_void, length: usize) -> io::Result<()> {
ret(c::mlock(addr, length))
}
/// # Safety
///
/// `mlock_with` operates on raw pointers and may round out to the nearest page
/// boundaries.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) unsafe fn mlock_with(
addr: *mut c::c_void,
length: usize,
flags: MlockFlags,
) -> io::Result<()> {
weak_or_syscall! {
fn mlock2(
addr: *const c::c_void,
len: c::size_t,
flags: c::c_int
) via SYS_mlock2 -> c::c_int
}
ret(mlock2(addr, length, flags.bits()))
}
/// # Safety
///
/// `munlock` operates on raw pointers and may round out to the nearest page
/// boundaries.
#[inline]
pub(crate) unsafe fn munlock(addr: *mut c::c_void, length: usize) -> io::Result<()> {
ret(c::munlock(addr, length))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) unsafe fn userfaultfd(flags: UserfaultfdFlags) -> io::Result<OwnedFd> {
syscall_ret_owned_fd(c::syscall(c::SYS_userfaultfd, flags.bits()))
}

View file

@ -0,0 +1,428 @@
use super::super::c;
use bitflags::bitflags;
bitflags! {
/// `PROT_*` flags for use with [`mmap`].
///
/// For `PROT_NONE`, use `ProtFlags::empty()`.
///
/// [`mmap`]: crate::io::mmap
pub struct ProtFlags: c::c_int {
/// `PROT_READ`
const READ = c::PROT_READ;
/// `PROT_WRITE`
const WRITE = c::PROT_WRITE;
/// `PROT_EXEC`
const EXEC = c::PROT_EXEC;
}
}
bitflags! {
/// `PROT_*` flags for use with [`mprotect`].
///
/// For `PROT_NONE`, use `MprotectFlags::empty()`.
///
/// [`mprotect`]: crate::io::mprotect
pub struct MprotectFlags: c::c_int {
/// `PROT_READ`
const READ = c::PROT_READ;
/// `PROT_WRITE`
const WRITE = c::PROT_WRITE;
/// `PROT_EXEC`
const EXEC = c::PROT_EXEC;
/// `PROT_GROWSUP`
#[cfg(any(target_os = "android", target_os = "linux"))]
const GROWSUP = c::PROT_GROWSUP;
/// `PROT_GROWSDOWN`
#[cfg(any(target_os = "android", target_os = "linux"))]
const GROWSDOWN = c::PROT_GROWSDOWN;
}
}
bitflags! {
/// `MAP_*` flags for use with [`mmap`].
///
/// For `MAP_ANONYMOUS` (aka `MAP_ANON`), see [`mmap_anonymous`].
///
/// [`mmap`]: crate::io::mmap
/// [`mmap_anonymous`]: crates::io::mmap_anonymous
pub struct MapFlags: c::c_int {
/// `MAP_SHARED`
const SHARED = c::MAP_SHARED;
/// `MAP_SHARED_VALIDATE`
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const SHARED_VALIDATE = c::MAP_SHARED_VALIDATE;
/// `MAP_PRIVATE`
const PRIVATE = c::MAP_PRIVATE;
/// `MAP_DENYWRITE`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const DENYWRITE = c::MAP_DENYWRITE;
/// `MAP_FIXED`
const FIXED = c::MAP_FIXED;
/// `MAP_FIXED_NOREPLACE`
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const FIXED_NOREPLACE = c::MAP_FIXED_NOREPLACE;
/// `MAP_GROWSDOWN`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const GROWSDOWN = c::MAP_GROWSDOWN;
/// `MAP_HUGETLB`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const HUGETLB = c::MAP_HUGETLB;
/// `MAP_HUGE_2MB`
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const HUGE_2MB = c::MAP_HUGE_2MB;
/// `MAP_HUGE_1GB`
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const HUGE_1GB = c::MAP_HUGE_1GB;
/// `MAP_LOCKED`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const LOCKED = c::MAP_LOCKED;
/// `MAP_NOCORE`
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
const NOCORE = c::MAP_NOCORE;
/// `MAP_NORESERVE`
#[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "redox")))]
const NORESERVE = c::MAP_NORESERVE;
/// `MAP_NOSYNC`
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
const NOSYNC = c::MAP_NOSYNC;
/// `MAP_POPULATE`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
const POPULATE = c::MAP_POPULATE;
/// `MAP_STACK`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "redox",
target_os = "solaris",
)))]
const STACK = c::MAP_STACK;
/// `MAP_PREFAULT_READ`
#[cfg(target_os = "freebsd")]
const PREFAULT_READ = c::MAP_PREFAULT_READ;
/// `MAP_SYNC`
#[cfg(not(any(
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
all(
any(target_os = "android", target_os = "linux"),
any(target_arch = "mips", target_arch = "mips64"),
)
)))]
const SYNC = c::MAP_SYNC;
/// `MAP_UNINITIALIZED`
#[cfg(any())]
const UNINITIALIZED = c::MAP_UNINITIALIZED;
}
}
#[cfg(target_os = "linux")]
bitflags! {
/// `MREMAP_*` flags for use with [`mremap`].
///
/// For `MREMAP_FIXED`, see [`mremap_fixed`].
///
/// [`mremap`]: crate::io::mremap
/// [`mremap_fixed`]: crate::io::mremap_fixed
pub struct MremapFlags: i32 {
/// `MREMAP_MAYMOVE`
const MAYMOVE = c::MREMAP_MAYMOVE;
}
}
bitflags! {
/// `MS_*` flags for use with [`msync`].
///
/// [`msync`]: crate::io::msync
pub struct MsyncFlags: i32 {
/// `MS_SYNC`—Requests an update and waits for it to complete.
const SYNC = c::MS_SYNC;
/// `MS_ASYNC`—Specifies that an update be scheduled, but the call
/// returns immediately.
const ASYNC = c::MS_ASYNC;
/// `MS_INVALIDATE`—Asks to invalidate other mappings of the same
/// file (so that they can be updated with the fresh values just
/// written).
const INVALIDATE = c::MS_INVALIDATE;
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
bitflags! {
/// `MLOCK_*` flags for use with [`mlock_with`].
///
/// [`mlock_with`]: crate::io::mlock_with
pub struct MlockFlags: i32 {
/// `MLOCK_ONFAULT`
const ONFAULT = c::MLOCK_ONFAULT as _;
}
}
/// `POSIX_MADV_*` constants for use with [`madvise`].
///
/// [`madvise`]: crate::mm::madvise
#[cfg(not(target_os = "redox"))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(i32)]
#[non_exhaustive]
pub enum Advice {
/// `POSIX_MADV_NORMAL`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Normal = c::POSIX_MADV_NORMAL,
/// `POSIX_MADV_NORMAL`
#[cfg(any(target_os = "android", target_os = "haiku"))]
Normal = c::MADV_NORMAL,
/// `POSIX_MADV_SEQUENTIAL`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Sequential = c::POSIX_MADV_SEQUENTIAL,
/// `POSIX_MADV_SEQUENTIAL`
#[cfg(any(target_os = "android", target_os = "haiku"))]
Sequential = c::MADV_SEQUENTIAL,
/// `POSIX_MADV_RANDOM`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
Random = c::POSIX_MADV_RANDOM,
/// `POSIX_MADV_RANDOM`
#[cfg(any(target_os = "android", target_os = "haiku"))]
Random = c::MADV_RANDOM,
/// `POSIX_MADV_WILLNEED`
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
WillNeed = c::POSIX_MADV_WILLNEED,
/// `POSIX_MADV_WILLNEED`
#[cfg(any(target_os = "android", target_os = "haiku"))]
WillNeed = c::MADV_WILLNEED,
/// `POSIX_MADV_DONTNEED`
#[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "haiku")))]
DontNeed = c::POSIX_MADV_DONTNEED,
/// `POSIX_MADV_DONTNEED`
#[cfg(any(target_os = "android", target_os = "haiku"))]
DontNeed = i32::MAX - 1,
/// `MADV_DONTNEED`
// `MADV_DONTNEED` has the same value as `POSIX_MADV_DONTNEED`. We don't
// have a separate `posix_madvise` from `madvise`, so we expose a special
// value which we special-case.
#[cfg(target_os = "linux")]
LinuxDontNeed = i32::MAX,
/// `MADV_DONTNEED`
#[cfg(target_os = "android")]
LinuxDontNeed = c::MADV_DONTNEED,
/// `MADV_FREE`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxFree = c::MADV_FREE,
/// `MADV_REMOVE`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxRemove = c::MADV_REMOVE,
/// `MADV_DONTFORK`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxDontFork = c::MADV_DONTFORK,
/// `MADV_DOFORK`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxDoFork = c::MADV_DOFORK,
/// `MADV_HWPOISON`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxHwPoison = c::MADV_HWPOISON,
/// `MADV_SOFT_OFFLINE`
#[cfg(all(
any(target_os = "android", target_os = "linux"),
not(any(target_arch = "mips", target_arch = "mips64")),
))]
LinuxSoftOffline = c::MADV_SOFT_OFFLINE,
/// `MADV_MERGEABLE`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxMergeable = c::MADV_MERGEABLE,
/// `MADV_UNMERGEABLE`
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxUnmergeable = c::MADV_UNMERGEABLE,
/// `MADV_HUGEPAGE` (since Linux 2.6.38)
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxHugepage = c::MADV_HUGEPAGE,
/// `MADV_NOHUGEPAGE` (since Linux 2.6.38)
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxNoHugepage = c::MADV_NOHUGEPAGE,
/// `MADV_DONTDUMP` (since Linux 3.4)
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxDontDump = c::MADV_DONTDUMP,
/// `MADV_DODUMP` (since Linux 3.4)
#[cfg(any(target_os = "android", target_os = "linux"))]
LinuxDoDump = c::MADV_DODUMP,
/// `MADV_WIPEONFORK` (since Linux 4.14)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "mm")]
LinuxWipeOnFork = linux_raw_sys::general::MADV_WIPEONFORK as i32,
/// `MADV_KEEPONFORK` (since Linux 4.14)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "mm")]
LinuxKeepOnFork = linux_raw_sys::general::MADV_KEEPONFORK as i32,
/// `MADV_COLD` (since Linux 5.4)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "mm")]
LinuxCold = linux_raw_sys::general::MADV_COLD as i32,
/// `MADV_PAGEOUT` (since Linux 5.4)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "mm")]
LinuxPageOut = linux_raw_sys::general::MADV_PAGEOUT as i32,
/// `MADV_POPULATE_READ` (since Linux 5.14)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "mm")]
LinuxPopulateRead = linux_raw_sys::general::MADV_POPULATE_READ as i32,
/// `MADV_POPULATE_WRITE` (since Linux 5.14)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "mm")]
LinuxPopulateWrite = linux_raw_sys::general::MADV_POPULATE_WRITE as i32,
}
#[cfg(target_os = "emscripten")]
impl Advice {
/// `POSIX_MADV_DONTNEED`
#[allow(non_upper_case_globals)]
pub const DontNeed: Self = Self::Normal;
}
#[cfg(any(target_os = "android", target_os = "linux"))]
bitflags! {
/// `O_*` flags for use with [`userfaultfd`].
///
/// [`userfaultfd`]: crate::io::userfaultfd
pub struct UserfaultfdFlags: c::c_int {
/// `O_CLOEXEC`
const CLOEXEC = c::O_CLOEXEC;
/// `O_NONBLOCK`
const NONBLOCK = c::O_NONBLOCK;
}
}

110
vendor/rustix/src/backend/libc/mod.rs vendored Normal file
View file

@ -0,0 +1,110 @@
//! The libc backend.
//!
//! On most platforms, this uses the `libc` crate to make system calls. On
//! Windows, this uses the Winsock2 API in `windows-sys`, which can be adapted
//! to have a very `libc`-like interface.
// Every FFI call requires an unsafe block, and there are a lot of FFI
// calls. For now, set this to allow for the libc backend.
#![allow(clippy::undocumented_unsafe_blocks)]
// Lots of libc types vary between platforms, so we often need a `.into()` on
// one platform where it's redundant on another.
#![allow(clippy::useless_conversion)]
#[cfg(not(any(windows, target_os = "wasi")))]
#[macro_use]
mod weak;
mod conv;
mod offset;
#[cfg(windows)]
mod io_lifetimes;
#[cfg(not(windows))]
#[cfg(not(feature = "std"))]
pub(crate) mod fd {
pub(crate) use super::c::c_int as LibcFd;
pub use crate::io::fd::*;
}
#[cfg(windows)]
pub(crate) mod fd {
pub use super::io_lifetimes::*;
}
#[cfg(not(windows))]
#[cfg(feature = "std")]
pub(crate) mod fd {
pub use io_lifetimes::*;
#[cfg(target_os = "wasi")]
#[allow(unused_imports)]
pub(crate) use super::c::c_int as LibcFd;
#[cfg(unix)]
#[allow(unused_imports)]
pub(crate) use std::os::unix::io::RawFd as LibcFd;
#[cfg(unix)]
pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
pub use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
}
// On Windows we emulate selected libc-compatible interfaces. On non-Windows,
// we just use libc here, since this is the libc backend.
#[cfg(windows)]
#[path = "winsock_c.rs"]
pub(crate) mod c;
#[cfg(not(windows))]
pub(crate) use libc as c;
#[cfg(not(windows))]
#[cfg(feature = "fs")]
pub(crate) mod fs;
pub(crate) mod io;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "io_uring")]
pub(crate) mod io_uring;
#[cfg(not(any(windows, target_os = "wasi")))]
#[cfg(feature = "mm")]
pub(crate) mod mm;
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
#[cfg(feature = "net")]
pub(crate) mod net;
#[cfg(not(windows))]
#[cfg(any(
feature = "param",
feature = "runtime",
feature = "time",
target_arch = "x86",
))]
pub(crate) mod param;
#[cfg(not(windows))]
pub(crate) mod process;
#[cfg(not(windows))]
#[cfg(feature = "rand")]
pub(crate) mod rand;
#[cfg(not(windows))]
#[cfg(feature = "termios")]
pub(crate) mod termios;
#[cfg(not(windows))]
#[cfg(feature = "thread")]
pub(crate) mod thread;
#[cfg(not(windows))]
pub(crate) mod time;
/// If the host libc is glibc, return `true` if it is less than version 2.25.
///
/// To restate and clarify, this function returning true does not mean the libc
/// is glibc just that if it is glibc, it is less than version 2.25.
///
/// For now, this function is only available on Linux, but if it ends up being
/// used beyond that, this could be changed to e.g. `#[cfg(unix)]`.
#[cfg(all(unix, target_env = "gnu"))]
pub(crate) fn if_glibc_is_less_than_2_25() -> bool {
// This is also defined inside `weak_or_syscall!` in
// backend/libc/rand/syscalls.rs, but it's not convenient to re-export the weak
// symbol from that macro, so we duplicate it at a small cost here.
weak! { fn getrandom(*mut c::c_void, c::size_t, c::c_uint) -> c::ssize_t }
// glibc 2.25 has `getrandom`, which is how we satisfy the API contract of
// this function. But, there are likely other libc versions which have it.
getrandom.get().is_none()
}

View file

@ -0,0 +1,330 @@
//! IPv4, IPv6, and Socket addresses.
use super::super::c;
#[cfg(unix)]
use crate::ffi::CStr;
#[cfg(unix)]
use crate::io;
#[cfg(unix)]
use crate::path;
#[cfg(not(windows))]
use core::convert::TryInto;
#[cfg(unix)]
use core::fmt;
#[cfg(unix)]
use core::slice;
/// `struct sockaddr_un`
#[cfg(unix)]
#[derive(Clone)]
#[doc(alias = "sockaddr_un")]
pub struct SocketAddrUnix {
pub(crate) unix: c::sockaddr_un,
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
len: c::socklen_t,
}
#[cfg(unix)]
impl SocketAddrUnix {
/// Construct a new Unix-domain address from a filesystem path.
#[inline]
pub fn new<P: path::Arg>(path: P) -> io::Result<Self> {
path.into_with_c_str(Self::_new)
}
#[inline]
fn _new(path: &CStr) -> io::Result<Self> {
let mut unix = Self::init();
let bytes = path.to_bytes_with_nul();
if bytes.len() > unix.sun_path.len() {
return Err(io::Errno::NAMETOOLONG);
}
for (i, b) in bytes.iter().enumerate() {
unix.sun_path[i] = *b as c::c_char;
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
{
unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap();
}
Ok(Self {
unix,
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(),
})
}
/// Construct a new abstract Unix-domain address from a byte slice.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> {
let mut unix = Self::init();
if 1 + name.len() > unix.sun_path.len() {
return Err(io::Errno::NAMETOOLONG);
}
unix.sun_path[0] = b'\0' as c::c_char;
for (i, b) in name.iter().enumerate() {
unix.sun_path[1 + i] = *b as c::c_char;
}
let len = offsetof_sun_path() + 1 + name.len();
let len = len.try_into().unwrap();
Ok(Self {
unix,
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
len,
})
}
fn init() -> c::sockaddr_un {
c::sockaddr_un {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sun_len: 0,
sun_family: c::AF_UNIX as _,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sun_path: [0; 104],
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
sun_path: [0; 108],
#[cfg(target_os = "haiku")]
sun_path: [0; 126],
}
}
/// For a filesystem path address, return the path.
#[inline]
pub fn path(&self) -> Option<&CStr> {
let len = self.len();
if len != 0 && self.unix.sun_path[0] != b'\0' as c::c_char {
let end = len as usize - offsetof_sun_path();
let bytes = &self.unix.sun_path[..end];
// Safety: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. And
// `from_bytes_with_nul_unchecked` since the string is NUL-terminated.
unsafe {
Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
bytes.as_ptr().cast(),
bytes.len(),
)))
}
} else {
None
}
}
/// For an abstract address, return the identifier.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub fn abstract_name(&self) -> Option<&[u8]> {
let len = self.len();
if len != 0 && self.unix.sun_path[0] == b'\0' as c::c_char {
let end = len as usize - offsetof_sun_path();
let bytes = &self.unix.sun_path[1..end];
// Safety: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`.
unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) }
} else {
None
}
}
#[inline]
pub(crate) fn addr_len(&self) -> c::socklen_t {
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
{
self.len
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
{
c::socklen_t::from(self.unix.sun_len)
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.addr_len() as usize
}
}
#[cfg(unix)]
impl PartialEq for SocketAddrUnix {
#[inline]
fn eq(&self, other: &Self) -> bool {
let self_len = self.len() - offsetof_sun_path();
let other_len = other.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len])
}
}
#[cfg(unix)]
impl Eq for SocketAddrUnix {}
#[cfg(unix)]
impl PartialOrd for SocketAddrUnix {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
let self_len = self.len() - offsetof_sun_path();
let other_len = other.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].partial_cmp(&other.unix.sun_path[..other_len])
}
}
#[cfg(unix)]
impl Ord for SocketAddrUnix {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let self_len = self.len() - offsetof_sun_path();
let other_len = other.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len])
}
}
#[cfg(unix)]
impl core::hash::Hash for SocketAddrUnix {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let self_len = self.len() - offsetof_sun_path();
self.unix.sun_path[..self_len].hash(state)
}
}
#[cfg(unix)]
impl fmt::Debug for SocketAddrUnix {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(path) = self.path() {
path.fmt(fmt)
} else {
#[cfg(any(target_os = "android", target_os = "linux"))]
if let Some(name) = self.abstract_name() {
return name.fmt(fmt);
}
"(unnamed)".fmt(fmt)
}
}
}
/// `struct sockaddr_storage` as a raw struct.
pub type SocketAddrStorage = c::sockaddr_storage;
/// Return the offset of the `sun_path` field of `sockaddr_un`.
#[cfg(not(windows))]
#[inline]
pub(crate) fn offsetof_sun_path() -> usize {
let z = c::sockaddr_un {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sun_len: 0_u8,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sun_family: 0_u8,
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
sun_family: 0_u16,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sun_path: [0; 104],
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
sun_path: [0; 108],
#[cfg(target_os = "haiku")]
sun_path: [0; 126],
};
(crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize)
}

View file

@ -0,0 +1,210 @@
use super::super::c;
/// The windows `sockaddr_in6` type is a union with accessor functions which
/// are not `const fn`. Define our own layout-compatible version so that we
/// can transmute in and out of it.
#[cfg(windows)]
#[repr(C)]
struct sockaddr_in6 {
sin6_family: u16,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 {
addr.s_addr
}
#[cfg(not(feature = "std"))]
#[cfg(windows)]
#[inline]
pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 {
// This should be `*addr.S_un.S_addr()`, except that isn't a `const fn`.
unsafe { core::mem::transmute(addr) }
}
// TODO: With Rust 1.55, we can use the above `in_addr_s_addr` definition that
// uses a const-fn transmute.
#[cfg(feature = "std")]
#[cfg(windows)]
#[inline]
pub(crate) fn in_addr_s_addr(addr: c::in_addr) -> u32 {
// This should be `*addr.S_un.S_addr()`, except that isn't a `const fn`.
unsafe { core::mem::transmute(addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr {
c::in_addr { s_addr }
}
#[cfg(not(feature = "std"))]
#[cfg(windows)]
#[inline]
pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr {
unsafe { core::mem::transmute(s_addr) }
}
// TODO: With Rust 1.55, we can use the above `in_addr_new` definition that
// uses a const-fn transmute.
#[cfg(feature = "std")]
#[cfg(windows)]
#[inline]
pub(crate) fn in_addr_new(s_addr: u32) -> c::in_addr {
unsafe { core::mem::transmute(s_addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] {
addr.s6_addr
}
#[cfg(not(feature = "std"))]
#[cfg(windows)]
#[inline]
pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] {
unsafe { core::mem::transmute(addr) }
}
// TODO: With Rust 1.55, we can use the above `in6_addr_s6_addr` definition
// that uses a const-fn transmute.
#[cfg(feature = "std")]
#[cfg(windows)]
#[inline]
pub(crate) fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] {
unsafe { core::mem::transmute(addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr {
c::in6_addr { s6_addr }
}
#[cfg(not(feature = "std"))]
#[cfg(windows)]
#[inline]
pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr {
unsafe { core::mem::transmute(s6_addr) }
}
// TODO: With Rust 1.55, we can use the above `in6_addr_new` definition that
// uses a const-fn transmute.
#[cfg(feature = "std")]
#[cfg(windows)]
#[inline]
pub(crate) fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr {
unsafe { core::mem::transmute(s6_addr) }
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
addr.sin6_scope_id
}
#[cfg(not(feature = "std"))]
#[cfg(windows)]
#[inline]
pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
let addr: sockaddr_in6 = unsafe { core::mem::transmute(addr) };
addr.sin6_scope_id
}
// TODO: With Rust 1.55, we can use the above `sockaddr_in6_sin6_scope_id`
// definition that uses a const-fn transmute.
#[cfg(feature = "std")]
#[cfg(windows)]
#[inline]
pub(crate) fn sockaddr_in6_sin6_scope_id(addr: c::sockaddr_in6) -> u32 {
let addr: sockaddr_in6 = unsafe { core::mem::transmute(addr) };
addr.sin6_scope_id
}
#[cfg(not(windows))]
#[inline]
pub(crate) const fn sockaddr_in6_new(
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sin6_len: u8,
sin6_family: c::sa_family_t,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
) -> c::sockaddr_in6 {
c::sockaddr_in6 {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sin6_len,
sin6_family,
sin6_port,
sin6_flowinfo,
sin6_addr,
sin6_scope_id,
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
__sin6_src_id: 0,
}
}
#[cfg(not(feature = "std"))]
#[cfg(windows)]
#[inline]
pub(crate) const fn sockaddr_in6_new(
sin6_family: u16,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
) -> c::sockaddr_in6 {
let addr = sockaddr_in6 {
sin6_family,
sin6_port,
sin6_flowinfo,
sin6_addr,
sin6_scope_id,
};
unsafe { core::mem::transmute(addr) }
}
// TODO: With Rust 1.55, we can use the above `sockaddr_in6_new` definition
// that uses a const-fn transmute.
#[cfg(feature = "std")]
#[cfg(windows)]
#[inline]
pub(crate) fn sockaddr_in6_new(
sin6_family: u16,
sin6_port: u16,
sin6_flowinfo: u32,
sin6_addr: c::in6_addr,
sin6_scope_id: u32,
) -> c::sockaddr_in6 {
let addr = sockaddr_in6 {
sin6_family,
sin6_port,
sin6_flowinfo,
sin6_addr,
sin6_scope_id,
};
unsafe { core::mem::transmute(addr) }
}

View file

@ -0,0 +1,7 @@
pub(crate) mod addr;
pub(crate) mod ext;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
pub(crate) mod syscalls;
pub(crate) mod types;
pub(crate) mod write_sockaddr;

View file

@ -0,0 +1,255 @@
use super::super::c;
#[cfg(unix)]
use super::addr::SocketAddrUnix;
use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id};
#[cfg(not(windows))]
use crate::ffi::CStr;
use crate::io;
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
#[cfg(not(windows))]
use alloc::vec::Vec;
use core::mem::size_of;
// This must match the header of `sockaddr`.
#[repr(C)]
struct sockaddr_header {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sa_len: u8,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
ss_family: u8,
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
ss_family: u16,
}
#[inline]
unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> u16 {
// Assert that we know the layout of `sockaddr`.
let _ = c::sockaddr {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sa_len: 0_u8,
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sa_family: 0_u8,
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
sa_family: 0_u16,
#[cfg(not(target_os = "haiku"))]
sa_data: [0; 14],
#[cfg(target_os = "haiku")]
sa_data: [0; 30],
};
(*storage.cast::<sockaddr_header>()).ss_family.into()
}
/// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we
/// can test for `AF_UNSPEC` to test whether it was stored to.
pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) {
(*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _;
}
pub(crate) unsafe fn read_sockaddr(
storage: *const c::sockaddr_storage,
len: usize,
) -> io::Result<SocketAddrAny> {
#[cfg(unix)]
let offsetof_sun_path = super::addr::offsetof_sun_path();
if len < size_of::<c::sa_family_t>() {
return Err(io::Errno::INVAL);
}
match read_ss_family(storage).into() {
c::AF_INET => {
if len < size_of::<c::sockaddr_in>() {
return Err(io::Errno::INVAL);
}
let decode = *storage.cast::<c::sockaddr_in>();
Ok(SocketAddrAny::V4(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
u16::from_be(decode.sin_port),
)))
}
c::AF_INET6 => {
if len < size_of::<c::sockaddr_in6>() {
return Err(io::Errno::INVAL);
}
let decode = *storage.cast::<c::sockaddr_in6>();
#[cfg(not(windows))]
let s6_addr = decode.sin6_addr.s6_addr;
#[cfg(windows)]
let s6_addr = decode.sin6_addr.u.Byte;
#[cfg(not(windows))]
let sin6_scope_id = decode.sin6_scope_id;
#[cfg(windows)]
let sin6_scope_id = decode.Anonymous.sin6_scope_id;
Ok(SocketAddrAny::V6(SocketAddrV6::new(
Ipv6Addr::from(s6_addr),
u16::from_be(decode.sin6_port),
u32::from_be(decode.sin6_flowinfo),
sin6_scope_id,
)))
}
#[cfg(unix)]
c::AF_UNIX => {
if len < offsetof_sun_path {
return Err(io::Errno::INVAL);
}
if len == offsetof_sun_path {
Ok(SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap()))
} else {
let decode = *storage.cast::<c::sockaddr_un>();
// Trim off unused bytes from the end of `path_bytes`.
let path_bytes = if cfg!(target_os = "freebsd") {
// FreeBSD sometimes sets the length to longer than the length
// of the NUL-terminated string. Find the NUL and truncate the
// string accordingly.
&decode.sun_path[..decode.sun_path.iter().position(|b| *b == 0).unwrap()]
} else {
// Otherwise, use the provided length.
let provided_len = len - 1 - offsetof_sun_path;
if decode.sun_path[provided_len] != b'\0' as c::c_char {
return Err(io::Errno::INVAL);
}
debug_assert_eq!(
CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(),
provided_len
);
&decode.sun_path[..provided_len]
};
Ok(SocketAddrAny::Unix(
SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>())
.unwrap(),
))
}
}
_ => Err(io::Errno::INVAL),
}
}
pub(crate) unsafe fn maybe_read_sockaddr_os(
storage: *const c::sockaddr_storage,
len: usize,
) -> Option<SocketAddrAny> {
if len == 0 {
return None;
}
assert!(len >= size_of::<c::sa_family_t>());
let family = read_ss_family(storage).into();
if family == c::AF_UNSPEC {
None
} else {
Some(inner_read_sockaddr_os(family, storage, len))
}
}
pub(crate) unsafe fn read_sockaddr_os(
storage: *const c::sockaddr_storage,
len: usize,
) -> SocketAddrAny {
assert!(len >= size_of::<c::sa_family_t>());
let family = read_ss_family(storage).into();
inner_read_sockaddr_os(family, storage, len)
}
unsafe fn inner_read_sockaddr_os(
family: c::c_int,
storage: *const c::sockaddr_storage,
len: usize,
) -> SocketAddrAny {
#[cfg(unix)]
let offsetof_sun_path = super::addr::offsetof_sun_path();
assert!(len >= size_of::<c::sa_family_t>());
match family {
c::AF_INET => {
assert!(len >= size_of::<c::sockaddr_in>());
let decode = *storage.cast::<c::sockaddr_in>();
SocketAddrAny::V4(SocketAddrV4::new(
Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))),
u16::from_be(decode.sin_port),
))
}
c::AF_INET6 => {
assert!(len >= size_of::<c::sockaddr_in6>());
let decode = *storage.cast::<c::sockaddr_in6>();
SocketAddrAny::V6(SocketAddrV6::new(
Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)),
u16::from_be(decode.sin6_port),
u32::from_be(decode.sin6_flowinfo),
sockaddr_in6_sin6_scope_id(decode),
))
}
#[cfg(unix)]
c::AF_UNIX => {
assert!(len >= offsetof_sun_path);
if len == offsetof_sun_path {
SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap())
} else {
let decode = *storage.cast::<c::sockaddr_un>();
assert_eq!(
decode.sun_path[len - 1 - offsetof_sun_path],
b'\0' as c::c_char
);
let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path];
// FreeBSD sometimes sets the length to longer than the length
// of the NUL-terminated string. Find the NUL and truncate the
// string accordingly.
#[cfg(target_os = "freebsd")]
let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()];
SocketAddrAny::Unix(
SocketAddrUnix::new(path_bytes.iter().map(|c| *c as u8).collect::<Vec<u8>>())
.unwrap(),
)
}
}
other => unimplemented!("{:?}", other),
}
}

View file

@ -0,0 +1,83 @@
use super::super::c;
use bitflags::bitflags;
bitflags! {
/// `MSG_*`
pub struct SendFlags: i32 {
/// `MSG_CONFIRM`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
const CONFIRM = c::MSG_CONFIRM;
/// `MSG_DONTROUTE`
const DONTROUTE = c::MSG_DONTROUTE;
/// `MSG_DONTWAIT`
#[cfg(not(windows))]
const DONTWAIT = c::MSG_DONTWAIT;
/// `MSG_EOR`
#[cfg(not(windows))]
const EOT = c::MSG_EOR;
/// `MSG_MORE`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
const MORE = c::MSG_MORE;
#[cfg(not(any(windows, target_os = "ios", target_os = "macos")))]
/// `MSG_NOSIGNAL`
const NOSIGNAL = c::MSG_NOSIGNAL;
/// `MSG_OOB`
const OOB = c::MSG_OOB;
}
}
bitflags! {
/// `MSG_*`
pub struct RecvFlags: i32 {
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "ios", target_os = "macos", target_os = "solaris")))]
/// `MSG_CMSG_CLOEXEC`
const CMSG_CLOEXEC = c::MSG_CMSG_CLOEXEC;
/// `MSG_DONTWAIT`
#[cfg(not(windows))]
const DONTWAIT = c::MSG_DONTWAIT;
/// `MSG_ERRQUEUE`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
const ERRQUEUE = c::MSG_ERRQUEUE;
/// `MSG_OOB`
const OOB = c::MSG_OOB;
/// `MSG_PEEK`
const PEEK = c::MSG_PEEK;
/// `MSG_TRUNC`
const TRUNC = c::MSG_TRUNC as c::c_int;
/// `MSG_WAITALL`
const WAITALL = c::MSG_WAITALL;
}
}

View file

@ -0,0 +1,886 @@
//! libc syscalls supporting `rustix::net`.
use super::super::c;
use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
#[cfg(unix)]
use super::addr::SocketAddrUnix;
use super::ext::{in6_addr_new, in_addr_new};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use super::read_sockaddr::initialize_family_to_unspec;
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use super::read_sockaddr::{maybe_read_sockaddr_os, read_sockaddr_os};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use super::send_recv::{RecvFlags, SendFlags};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use super::types::{AcceptFlags, AddressFamily, Protocol, Shutdown, SocketFlags, SocketType};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
use crate::fd::{BorrowedFd, OwnedFd};
use crate::io;
use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
use crate::utils::as_ptr;
use core::convert::TryInto;
use core::mem::{size_of, MaybeUninit};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use core::ptr::null_mut;
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn recv(fd: BorrowedFd<'_>, buf: &mut [u8], flags: RecvFlags) -> io::Result<usize> {
let nrecv = unsafe {
ret_send_recv(c::recv(
borrowed_fd(fd),
buf.as_mut_ptr().cast(),
send_recv_len(buf.len()),
flags.bits(),
))?
};
Ok(nrecv as usize)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize> {
let nwritten = unsafe {
ret_send_recv(c::send(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
flags.bits(),
))?
};
Ok(nwritten as usize)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn recvfrom(
fd: BorrowedFd<'_>,
buf: &mut [u8],
flags: RecvFlags,
) -> io::Result<(usize, Option<SocketAddrAny>)> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
// `recvfrom` does not write to the storage if the socket is
// connection-oriented sockets, so we initialize the family field to
// `AF_UNSPEC` so that we can detect this case.
initialize_family_to_unspec(storage.as_mut_ptr());
let nread = ret_send_recv(c::recvfrom(
borrowed_fd(fd),
buf.as_mut_ptr().cast(),
send_recv_len(buf.len()),
flags.bits(),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok((
nread as usize,
maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendto_v4(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrV4,
) -> io::Result<usize> {
let nwritten = unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
flags.bits(),
as_ptr(&encode_sockaddr_v4(addr)).cast::<c::sockaddr>(),
size_of::<SocketAddrV4>() as _,
))?
};
Ok(nwritten as usize)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendto_v6(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrV6,
) -> io::Result<usize> {
let nwritten = unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
flags.bits(),
as_ptr(&encode_sockaddr_v6(addr)).cast::<c::sockaddr>(),
size_of::<SocketAddrV6>() as _,
))?
};
Ok(nwritten as usize)
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendto_unix(
fd: BorrowedFd<'_>,
buf: &[u8],
flags: SendFlags,
addr: &SocketAddrUnix,
) -> io::Result<usize> {
let nwritten = unsafe {
ret_send_recv(c::sendto(
borrowed_fd(fd),
buf.as_ptr().cast(),
send_recv_len(buf.len()),
flags.bits(),
as_ptr(&addr.unix).cast(),
addr.addr_len(),
))?
};
Ok(nwritten as usize)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn socket(
domain: AddressFamily,
type_: SocketType,
protocol: Protocol,
) -> io::Result<OwnedFd> {
unsafe {
ret_owned_fd(c::socket(
domain.0 as c::c_int,
type_.0 as c::c_int,
protocol.0,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn socket_with(
domain: AddressFamily,
type_: SocketType,
flags: SocketFlags,
protocol: Protocol,
) -> io::Result<OwnedFd> {
unsafe {
ret_owned_fd(c::socket(
domain.0 as c::c_int,
type_.0 as c::c_int | flags.bits(),
protocol.0,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v4(addr)).cast(),
size_of::<c::sockaddr_in>() as c::socklen_t,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v6(addr)).cast(),
size_of::<c::sockaddr_in6>() as c::socklen_t,
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
unsafe {
ret(c::bind(
borrowed_fd(sockfd),
as_ptr(&addr.unix).cast(),
addr.addr_len(),
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v4(addr)).cast(),
size_of::<c::sockaddr_in>() as c::socklen_t,
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> {
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&encode_sockaddr_v6(addr)).cast(),
size_of::<c::sockaddr_in6>() as c::socklen_t,
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> {
unsafe {
ret(c::connect(
borrowed_fd(sockfd),
as_ptr(&addr.unix).cast(),
addr.addr_len(),
))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> {
unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) }
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
unsafe {
let owned_fd = ret_owned_fd(c::accept(borrowed_fd(sockfd), null_mut(), null_mut()))?;
Ok(owned_fd)
}
}
#[cfg(not(any(
windows,
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "redox",
target_os = "wasi",
)))]
pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, flags: AcceptFlags) -> io::Result<OwnedFd> {
unsafe {
let owned_fd = ret_owned_fd(c::accept4(
borrowed_fd(sockfd),
null_mut(),
null_mut(),
flags.bits(),
))?;
Ok(owned_fd)
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
let owned_fd = ret_owned_fd(c::accept(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok((
owned_fd,
maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
))
}
}
#[cfg(not(any(
windows,
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "redox",
target_os = "wasi",
)))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
flags: AcceptFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
let owned_fd = ret_owned_fd(c::accept4(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
flags.bits(),
))?;
Ok((
owned_fd,
maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()),
))
}
}
/// Darwin lacks `accept4`, but does have `accept`. We define
/// `AcceptFlags` to have no flags, so we can discard it here.
#[cfg(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos"))]
pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: AcceptFlags) -> io::Result<OwnedFd> {
accept(sockfd)
}
/// Darwin lacks `accept4`, but does have `accept`. We define
/// `AcceptFlags` to have no flags, so we can discard it here.
#[cfg(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos"))]
pub(crate) fn acceptfrom_with(
sockfd: BorrowedFd<'_>,
_flags: AcceptFlags,
) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> {
acceptfrom(sockfd)
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) }
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
ret(c::getsockname(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok(read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> {
unsafe {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();
let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t;
ret(c::getpeername(
borrowed_fd(sockfd),
storage.as_mut_ptr().cast(),
&mut len,
))?;
Ok(maybe_read_sockaddr_os(
storage.as_ptr(),
len.try_into().unwrap(),
))
}
}
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn socketpair(
domain: AddressFamily,
type_: SocketType,
flags: SocketFlags,
protocol: Protocol,
) -> io::Result<(OwnedFd, OwnedFd)> {
unsafe {
let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit();
ret(c::socketpair(
c::c_int::from(domain.0),
type_.0 as c::c_int | flags.bits(),
protocol.0,
fds.as_mut_ptr().cast::<c::c_int>(),
))?;
let [fd0, fd1] = fds.assume_init();
Ok((fd0, fd1))
}
}
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
pub(crate) mod sockopt {
use super::{c, in6_addr_new, in_addr_new, BorrowedFd};
use crate::io;
use crate::net::sockopt::Timeout;
use crate::net::{Ipv4Addr, Ipv6Addr, SocketType};
use crate::utils::as_mut_ptr;
use core::convert::TryInto;
use core::time::Duration;
#[cfg(windows)]
use windows_sys::Win32::Foundation::BOOL;
// TODO: With Rust 1.53 we can use `Duration::ZERO` instead.
const DURATION_ZERO: Duration = Duration::from_secs(0);
#[inline]
fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T> {
use super::*;
let mut optlen = core::mem::size_of::<T>().try_into().unwrap();
debug_assert!(
optlen as usize >= core::mem::size_of::<c::c_int>(),
"Socket APIs don't ever use `bool` directly"
);
unsafe {
let mut value = core::mem::zeroed::<T>();
ret(c::getsockopt(
borrowed_fd(fd),
level,
optname,
as_mut_ptr(&mut value).cast(),
&mut optlen,
))?;
// On Windows at least, `getsockopt` has been observed writing 1
// byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though
// Windows' documentation says that should write a 4-byte `BOOL`.
// So, we initialize the memory to zeros above, and just assert
// that `getsockopt` doesn't write too many bytes here.
assert!(
optlen as usize <= size_of::<T>(),
"unexpected getsockopt size"
);
Ok(value)
}
}
#[inline]
fn setsockopt<T: Copy>(
fd: BorrowedFd<'_>,
level: i32,
optname: i32,
value: T,
) -> io::Result<()> {
use super::*;
let optlen = core::mem::size_of::<T>().try_into().unwrap();
debug_assert!(
optlen as usize >= core::mem::size_of::<c::c_int>(),
"Socket APIs don't ever use `bool` directly"
);
unsafe {
ret(c::setsockopt(
borrowed_fd(fd),
level,
optname,
as_ptr(&value).cast(),
optlen,
))
}
}
#[inline]
pub(crate) fn get_socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> {
getsockopt(fd, c::SOL_SOCKET as _, c::SO_TYPE)
}
#[inline]
pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> {
setsockopt(
fd,
c::SOL_SOCKET as _,
c::SO_REUSEADDR,
from_bool(reuseaddr),
)
}
#[inline]
pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> {
setsockopt(
fd,
c::SOL_SOCKET as _,
c::SO_BROADCAST,
from_bool(broadcast),
)
}
#[inline]
pub(crate) fn get_socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::SOL_SOCKET as _, c::SO_BROADCAST).map(to_bool)
}
#[inline]
pub(crate) fn set_socket_linger(
fd: BorrowedFd<'_>,
linger: Option<Duration>,
) -> io::Result<()> {
// Convert `linger` to seconds, rounding up.
let l_linger = if let Some(linger) = linger {
let mut l_linger = linger.as_secs();
if linger.subsec_nanos() != 0 {
l_linger = l_linger.checked_add(1).ok_or(io::Errno::INVAL)?;
}
l_linger.try_into().map_err(|_e| io::Errno::INVAL)?
} else {
0
};
let linger = c::linger {
l_onoff: linger.is_some() as _,
l_linger,
};
setsockopt(fd, c::SOL_SOCKET as _, c::SO_LINGER, linger)
}
#[inline]
pub(crate) fn get_socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> {
let linger: c::linger = getsockopt(fd, c::SOL_SOCKET as _, c::SO_LINGER)?;
// TODO: With Rust 1.50, this could use `.then`.
Ok(if linger.l_onoff != 0 {
Some(Duration::from_secs(linger.l_linger as u64))
} else {
None
})
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> {
setsockopt(fd, c::SOL_SOCKET as _, c::SO_PASSCRED, from_bool(passcred))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn get_socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::SOL_SOCKET as _, c::SO_PASSCRED).map(to_bool)
}
#[inline]
pub(crate) fn set_socket_timeout(
fd: BorrowedFd<'_>,
id: Timeout,
timeout: Option<Duration>,
) -> io::Result<()> {
let optname = match id {
Timeout::Recv => c::SO_RCVTIMEO,
Timeout::Send => c::SO_SNDTIMEO,
};
#[cfg(not(windows))]
let timeout = match timeout {
Some(timeout) => {
if timeout == DURATION_ZERO {
return Err(io::Errno::INVAL);
}
let tv_sec = timeout.as_secs().try_into();
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
let tv_sec = tv_sec.unwrap_or(c::c_long::MAX);
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
let tv_sec = tv_sec.unwrap_or(i64::MAX);
// `subsec_micros` rounds down, so we use `subsec_nanos` and
// manually round up.
let mut timeout = c::timeval {
tv_sec,
tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
};
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
timeout.tv_usec = 1;
}
timeout
}
None => c::timeval {
tv_sec: 0,
tv_usec: 0,
},
};
#[cfg(windows)]
let timeout: u32 = match timeout {
Some(timeout) => {
if timeout == DURATION_ZERO {
return Err(io::Errno::INVAL);
}
// `as_millis` rounds down, so we use `as_nanos` and
// manually round up.
let mut timeout: u32 = ((timeout.as_nanos() + 999_999) / 1_000_000)
.try_into()
.map_err(|_convert_err| io::Errno::INVAL)?;
if timeout == 0 {
timeout = 1;
}
timeout
}
None => 0,
};
setsockopt(fd, c::SOL_SOCKET, optname, timeout)
}
#[inline]
pub(crate) fn get_socket_timeout(
fd: BorrowedFd<'_>,
id: Timeout,
) -> io::Result<Option<Duration>> {
let optname = match id {
Timeout::Recv => c::SO_RCVTIMEO,
Timeout::Send => c::SO_SNDTIMEO,
};
#[cfg(not(windows))]
{
let timeout: c::timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
Ok(None)
} else {
Ok(Some(
Duration::from_secs(timeout.tv_sec as u64)
+ Duration::from_micros(timeout.tv_usec as u64),
))
}
}
#[cfg(windows)]
{
let timeout: u32 = getsockopt(fd, c::SOL_SOCKET, optname)?;
if timeout == 0 {
Ok(None)
} else {
Ok(Some(Duration::from_millis(timeout as u64)))
}
}
}
#[inline]
pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> {
setsockopt(fd, c::IPPROTO_IP as _, c::IP_TTL, ttl)
}
#[inline]
pub(crate) fn get_ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
getsockopt(fd, c::IPPROTO_IP as _, c::IP_TTL)
}
#[inline]
pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> {
setsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_V6ONLY, from_bool(only_v6))
}
#[inline]
pub(crate) fn get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_V6ONLY).map(to_bool)
}
#[inline]
pub(crate) fn set_ip_multicast_loop(
fd: BorrowedFd<'_>,
multicast_loop: bool,
) -> io::Result<()> {
setsockopt(
fd,
c::IPPROTO_IP as _,
c::IP_MULTICAST_LOOP,
from_bool(multicast_loop),
)
}
#[inline]
pub(crate) fn get_ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_LOOP).map(to_bool)
}
#[inline]
pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> {
setsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_TTL, multicast_ttl)
}
#[inline]
pub(crate) fn get_ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> {
getsockopt(fd, c::IPPROTO_IP as _, c::IP_MULTICAST_TTL)
}
#[inline]
pub(crate) fn set_ipv6_multicast_loop(
fd: BorrowedFd<'_>,
multicast_loop: bool,
) -> io::Result<()> {
setsockopt(
fd,
c::IPPROTO_IPV6 as _,
c::IPV6_MULTICAST_LOOP,
from_bool(multicast_loop),
)
}
#[inline]
pub(crate) fn get_ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_IPV6 as _, c::IPV6_MULTICAST_LOOP).map(to_bool)
}
#[inline]
pub(crate) fn set_ipv6_multicast_hops(
fd: BorrowedFd<'_>,
multicast_hops: u32,
) -> io::Result<()> {
setsockopt(
fd,
c::IPPROTO_IP as _,
c::IPV6_MULTICAST_LOOP,
multicast_hops,
)
}
#[inline]
pub(crate) fn get_ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> {
getsockopt(fd, c::IPPROTO_IP as _, c::IPV6_MULTICAST_LOOP)
}
#[inline]
pub(crate) fn set_ip_add_membership(
fd: BorrowedFd<'_>,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr,
) -> io::Result<()> {
let mreq = to_imr(multiaddr, interface);
setsockopt(fd, c::IPPROTO_IP as _, c::IP_ADD_MEMBERSHIP, mreq)
}
#[inline]
pub(crate) fn set_ipv6_add_membership(
fd: BorrowedFd<'_>,
multiaddr: &Ipv6Addr,
interface: u32,
) -> io::Result<()> {
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
use c::IPV6_ADD_MEMBERSHIP;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
))]
use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
let mreq = to_ipv6mr(multiaddr, interface);
setsockopt(fd, c::IPPROTO_IPV6 as _, IPV6_ADD_MEMBERSHIP, mreq)
}
#[inline]
pub(crate) fn set_ip_drop_membership(
fd: BorrowedFd<'_>,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr,
) -> io::Result<()> {
let mreq = to_imr(multiaddr, interface);
setsockopt(fd, c::IPPROTO_IP as _, c::IP_DROP_MEMBERSHIP, mreq)
}
#[inline]
pub(crate) fn set_ipv6_drop_membership(
fd: BorrowedFd<'_>,
multiaddr: &Ipv6Addr,
interface: u32,
) -> io::Result<()> {
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
use c::IPV6_DROP_MEMBERSHIP;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "l4re",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
))]
use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
let mreq = to_ipv6mr(multiaddr, interface);
setsockopt(fd, c::IPPROTO_IPV6 as _, IPV6_DROP_MEMBERSHIP, mreq)
}
#[inline]
pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> {
setsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY, from_bool(nodelay))
}
#[inline]
pub(crate) fn get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> {
getsockopt(fd, c::IPPROTO_TCP as _, c::TCP_NODELAY).map(to_bool)
}
#[inline]
fn to_imr(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
c::ip_mreq {
imr_multiaddr: to_imr_addr(multiaddr),
imr_interface: to_imr_addr(interface),
}
}
#[inline]
fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr {
in_addr_new(u32::from_ne_bytes(addr.octets()))
}
#[inline]
fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq {
c::ipv6_mreq {
ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr),
ipv6mr_interface: to_ipv6mr_interface(interface),
}
}
#[inline]
fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr {
in6_addr_new(multiaddr.octets())
}
#[cfg(target_os = "android")]
#[inline]
fn to_ipv6mr_interface(interface: u32) -> c::c_int {
interface as c::c_int
}
#[cfg(not(target_os = "android"))]
#[inline]
fn to_ipv6mr_interface(interface: u32) -> c::c_uint {
interface as c::c_uint
}
// `getsockopt` and `setsockopt` represent boolean values as integers.
#[cfg(not(windows))]
type RawSocketBool = c::c_int;
#[cfg(windows)]
type RawSocketBool = BOOL;
// Wrap `RawSocketBool` in a newtype to discourage misuse.
#[repr(transparent)]
#[derive(Copy, Clone)]
struct SocketBool(RawSocketBool);
// Convert from a `bool` to a `SocketBool`.
#[inline]
fn from_bool(value: bool) -> SocketBool {
SocketBool(value as _)
}
// Convert from a `SocketBool` to a `bool`.
#[inline]
fn to_bool(value: SocketBool) -> bool {
value.0 != 0
}
}

View file

@ -0,0 +1,687 @@
use super::super::c;
use bitflags::bitflags;
/// A type for holding raw integer socket types.
#[doc(hidden)]
pub type RawSocketType = u32;
/// `SOCK_*` constants for use with [`socket`].
///
/// [`socket`]: crate::net::socket
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct SocketType(pub(crate) RawSocketType);
#[rustfmt::skip]
impl SocketType {
/// `SOCK_STREAM`
pub const STREAM: Self = Self(c::SOCK_STREAM as u32);
/// `SOCK_DGRAM`
pub const DGRAM: Self = Self(c::SOCK_DGRAM as u32);
/// `SOCK_SEQPACKET`
pub const SEQPACKET: Self = Self(c::SOCK_SEQPACKET as u32);
/// `SOCK_RAW`
pub const RAW: Self = Self(c::SOCK_RAW as u32);
/// `SOCK_RDM`
#[cfg(not(target_os = "haiku"))]
pub const RDM: Self = Self(c::SOCK_RDM as u32);
/// Constructs a `SocketType` from a raw integer.
#[inline]
pub const fn from_raw(raw: RawSocketType) -> Self {
Self(raw)
}
/// Returns the raw integer for this `SocketType`.
#[inline]
pub const fn as_raw(self) -> RawSocketType {
self.0
}
}
/// A type for holding raw integer address families.
#[doc(hidden)]
pub type RawAddressFamily = c::sa_family_t;
/// `AF_*` constants.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct AddressFamily(pub(crate) RawAddressFamily);
#[rustfmt::skip]
impl AddressFamily {
/// `AF_UNSPEC`
pub const UNSPEC: Self = Self(c::AF_UNSPEC as _);
/// `AF_INET`
pub const INET: Self = Self(c::AF_INET as _);
/// `AF_INET6`
pub const INET6: Self = Self(c::AF_INET6 as _);
/// `AF_NETLINK`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const NETLINK: Self = Self(c::AF_NETLINK as _);
/// `AF_UNIX`, aka `AF_LOCAL`
#[doc(alias = "LOCAL")]
pub const UNIX: Self = Self(c::AF_UNIX as _);
/// `AF_AX25`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const AX25: Self = Self(c::AF_AX25 as _);
/// `AF_IPX`
pub const IPX: Self = Self(c::AF_IPX as _);
/// `AF_APPLETALK`
pub const APPLETALK: Self = Self(c::AF_APPLETALK as _);
/// `AF_NETROM`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const NETROM: Self = Self(c::AF_NETROM as _);
/// `AF_BRIDGE`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const BRIDGE: Self = Self(c::AF_BRIDGE as _);
/// `AF_ATMPVC`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const ATMPVC: Self = Self(c::AF_ATMPVC as _);
/// `AF_X25`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
pub const X25: Self = Self(c::AF_X25 as _);
/// `AF_ROSE`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const ROSE: Self = Self(c::AF_ROSE as _);
/// `AF_DECnet`
#[allow(non_upper_case_globals)]
#[cfg(not(target_os = "haiku"))]
pub const DECnet: Self = Self(c::AF_DECnet as _);
/// `AF_NETBEUI`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const NETBEUI: Self = Self(c::AF_NETBEUI as _);
/// `AF_SECURITY`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const SECURITY: Self = Self(c::AF_SECURITY as _);
/// `AF_KEY`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
pub const KEY: Self = Self(c::AF_KEY as _);
/// `AF_PACKET`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
pub const PACKET: Self = Self(c::AF_PACKET as _);
/// `AF_ASH`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const ASH: Self = Self(c::AF_ASH as _);
/// `AF_ECONET`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const ECONET: Self = Self(c::AF_ECONET as _);
/// `AF_ATMSVC`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const ATMSVC: Self = Self(c::AF_ATMSVC as _);
/// `AF_RDS`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const RDS: Self = Self(c::AF_RDS as _);
/// `AF_SNA`
#[cfg(not(target_os = "haiku"))]
pub const SNA: Self = Self(c::AF_SNA as _);
/// `AF_IRDA`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const IRDA: Self = Self(c::AF_IRDA as _);
/// `AF_PPPOX`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const PPPOX: Self = Self(c::AF_PPPOX as _);
/// `AF_WANPIPE`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const WANPIPE: Self = Self(c::AF_WANPIPE as _);
/// `AF_LLC`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const LLC: Self = Self(c::AF_LLC as _);
/// `AF_CAN`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const CAN: Self = Self(c::AF_CAN as _);
/// `AF_TIPC`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const TIPC: Self = Self(c::AF_TIPC as _);
/// `AF_BLUETOOTH`
#[cfg(not(any(windows, target_os = "illumos", target_os = "ios", target_os = "macos", target_os = "solaris")))]
pub const BLUETOOTH: Self = Self(c::AF_BLUETOOTH as _);
/// `AF_IUCV`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const IUCV: Self = Self(c::AF_IUCV as _);
/// `AF_RXRPC`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const RXRPC: Self = Self(c::AF_RXRPC as _);
/// `AF_ISDN`
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const ISDN: Self = Self(c::AF_ISDN as _);
/// `AF_PHONET`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const PHONET: Self = Self(c::AF_PHONET as _);
/// `AF_IEEE802154`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const IEEE802154: Self = Self(c::AF_IEEE802154 as _);
/// Constructs a `AddressFamily` from a raw integer.
#[inline]
pub const fn from_raw(raw: RawAddressFamily) -> Self {
Self(raw)
}
/// Returns the raw integer for this `AddressFamily`.
#[inline]
pub const fn as_raw(self) -> RawAddressFamily {
self.0
}
}
/// A type for holding raw integer protocols.
#[doc(hidden)]
pub type RawProtocol = i32;
/// `IPPROTO_*`
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct Protocol(pub(crate) RawProtocol);
#[rustfmt::skip]
impl Protocol {
/// `IPPROTO_IP`
pub const IP: Self = Self(c::IPPROTO_IP as _);
/// `IPPROTO_ICMP`
pub const ICMP: Self = Self(c::IPPROTO_ICMP as _);
/// `IPPROTO_IGMP`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const IGMP: Self = Self(c::IPPROTO_IGMP as _);
/// `IPPROTO_IPIP`
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const IPIP: Self = Self(c::IPPROTO_IPIP as _);
/// `IPPROTO_TCP`
pub const TCP: Self = Self(c::IPPROTO_TCP as _);
/// `IPPROTO_EGP`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const EGP: Self = Self(c::IPPROTO_EGP as _);
/// `IPPROTO_PUP`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const PUP: Self = Self(c::IPPROTO_PUP as _);
/// `IPPROTO_UDP`
pub const UDP: Self = Self(c::IPPROTO_UDP as _);
/// `IPPROTO_IDP`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const IDP: Self = Self(c::IPPROTO_IDP as _);
/// `IPPROTO_TP`
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const TP: Self = Self(c::IPPROTO_TP as _);
/// `IPPROTO_DCCP`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const DCCP: Self = Self(c::IPPROTO_DCCP as _);
/// `IPPROTO_IPV6`
pub const IPV6: Self = Self(c::IPPROTO_IPV6 as _);
/// `IPPROTO_RSVP`
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const RSVP: Self = Self(c::IPPROTO_RSVP as _);
/// `IPPROTO_GRE`
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const GRE: Self = Self(c::IPPROTO_GRE as _);
/// `IPPROTO_ESP`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const ESP: Self = Self(c::IPPROTO_ESP as _);
/// `IPPROTO_AH`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const AH: Self = Self(c::IPPROTO_AH as _);
/// `IPPROTO_MTP`
#[cfg(not(any(
windows,
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const MTP: Self = Self(c::IPPROTO_MTP as _);
/// `IPPROTO_BEETPH`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const BEETPH: Self = Self(c::IPPROTO_BEETPH as _);
/// `IPPROTO_ENCAP`
#[cfg(not(any(windows, target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const ENCAP: Self = Self(c::IPPROTO_ENCAP as _);
/// `IPPROTO_PIM`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const PIM: Self = Self(c::IPPROTO_PIM as _);
/// `IPPROTO_COMP`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const COMP: Self = Self(c::IPPROTO_COMP as _);
/// `IPPROTO_SCTP`
#[cfg(not(any(target_os = "dragonfly", target_os = "haiku", target_os = "illumos", target_os = "openbsd", target_os = "solaris")))]
pub const SCTP: Self = Self(c::IPPROTO_SCTP as _);
/// `IPPROTO_UDPLITE`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const UDPLITE: Self = Self(c::IPPROTO_UDPLITE as _);
/// `IPPROTO_MPLS`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "solaris",
)))]
pub const MPLS: Self = Self(c::IPPROTO_MPLS as _);
/// `IPPROTO_RAW`
pub const RAW: Self = Self(c::IPPROTO_RAW as _);
/// `IPPROTO_MPTCP`
#[cfg(not(any(
windows,
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const MPTCP: Self = Self(c::IPPROTO_MPTCP as _);
/// `IPPROTO_FRAGMENT`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const FRAGMENT: Self = Self(c::IPPROTO_FRAGMENT as _);
/// `IPPROTO_ICMPV6`
pub const ICMPV6: Self = Self(c::IPPROTO_ICMPV6 as _);
/// `IPPROTO_MH`
#[cfg(not(any(
windows,
target_os = "dragonfly",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
pub const MH: Self = Self(c::IPPROTO_MH as _);
/// `IPPROTO_ROUTING`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
pub const ROUTING: Self = Self(c::IPPROTO_ROUTING as _);
/// Constructs a `Protocol` from a raw integer.
#[inline]
pub const fn from_raw(raw: RawProtocol) -> Self {
Self(raw)
}
/// Returns the raw integer for this `Protocol`.
#[inline]
pub const fn as_raw(self) -> RawProtocol {
self.0
}
}
/// `SHUT_*` constants for use with [`shutdown`].
///
/// [`shutdown`]: crate::net::shutdown
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(i32)]
pub enum Shutdown {
/// `SHUT_RD`—Disable further read operations.
Read = c::SHUT_RD,
/// `SHUT_WR`—Disable further write operations.
Write = c::SHUT_WR,
/// `SHUT_RDWR`—Disable further read and write operations.
ReadWrite = c::SHUT_RDWR,
}
bitflags! {
/// `SOCK_*` constants for use with [`accept_with`] and [`acceptfrom_with`].
///
/// [`accept_with`]: crate::net::accept_with
/// [`acceptfrom_with`]: crate::net::acceptfrom_with
pub struct AcceptFlags: c::c_int {
/// `SOCK_NONBLOCK`
#[cfg(not(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos")))]
const NONBLOCK = c::SOCK_NONBLOCK;
/// `SOCK_CLOEXEC`
#[cfg(not(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos")))]
const CLOEXEC = c::SOCK_CLOEXEC;
}
}
bitflags! {
/// `SOCK_*` constants for use with [`socket`].
///
/// [`socket`]: crate::net::socket
pub struct SocketFlags: c::c_int {
/// `SOCK_NONBLOCK`
#[cfg(not(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos")))]
const NONBLOCK = c::SOCK_NONBLOCK;
/// `SOCK_CLOEXEC`
#[cfg(not(any(windows, target_os = "haiku", target_os = "ios", target_os = "macos")))]
const CLOEXEC = c::SOCK_CLOEXEC;
}
}
/// Timeout identifier for use with [`set_socket_timeout`] and
/// [`get_socket_timeout`].
///
/// [`set_socket_timeout`]: crate::net::sockopt::set_socket_timeout.
/// [`get_socket_timeout`]: crate::net::sockopt::get_socket_timeout.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(i32)]
pub enum Timeout {
/// `SO_RCVTIMEO`—Timeout for receiving.
Recv = c::SO_RCVTIMEO,
/// `SO_SNDTIMEO`—Timeout for sending.
Send = c::SO_SNDTIMEO,
}

View file

@ -0,0 +1,102 @@
//! The BSD sockets API requires us to read the `ss_family` field before
//! we can interpret the rest of a `sockaddr` produced by the kernel.
use super::super::c;
use super::addr::SocketAddrStorage;
#[cfg(unix)]
use super::addr::SocketAddrUnix;
use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new};
use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
use core::mem::size_of;
pub(crate) unsafe fn write_sockaddr(
addr: &SocketAddrAny,
storage: *mut SocketAddrStorage,
) -> usize {
match addr {
SocketAddrAny::V4(v4) => write_sockaddr_v4(v4, storage),
SocketAddrAny::V6(v6) => write_sockaddr_v6(v6, storage),
#[cfg(unix)]
SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage),
}
}
pub(crate) unsafe fn encode_sockaddr_v4(v4: &SocketAddrV4) -> c::sockaddr_in {
c::sockaddr_in {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
sin_len: size_of::<c::sockaddr_in>() as _,
sin_family: c::AF_INET as _,
sin_port: u16::to_be(v4.port()),
sin_addr: in_addr_new(u32::from_ne_bytes(v4.ip().octets())),
#[cfg(not(target_os = "haiku"))]
sin_zero: [0; 8_usize],
#[cfg(target_os = "haiku")]
sin_zero: [0; 24_usize],
}
}
unsafe fn write_sockaddr_v4(v4: &SocketAddrV4, storage: *mut SocketAddrStorage) -> usize {
let encoded = encode_sockaddr_v4(v4);
core::ptr::write(storage.cast(), encoded);
size_of::<c::sockaddr_in>()
}
pub(crate) unsafe fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
{
sockaddr_in6_new(
size_of::<c::sockaddr_in6>() as _,
c::AF_INET6 as _,
u16::to_be(v6.port()),
u32::to_be(v6.flowinfo()),
in6_addr_new(v6.ip().octets()),
v6.scope_id(),
)
}
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
{
sockaddr_in6_new(
c::AF_INET6 as _,
u16::to_be(v6.port()),
u32::to_be(v6.flowinfo()),
in6_addr_new(v6.ip().octets()),
v6.scope_id(),
)
}
}
unsafe fn write_sockaddr_v6(v6: &SocketAddrV6, storage: *mut SocketAddrStorage) -> usize {
let encoded = encode_sockaddr_v6(v6);
core::ptr::write(storage.cast(), encoded);
size_of::<c::sockaddr_in6>()
}
#[cfg(unix)]
unsafe fn write_sockaddr_unix(unix: &SocketAddrUnix, storage: *mut SocketAddrStorage) -> usize {
core::ptr::write(storage.cast(), unix.unix);
unix.len()
}

406
vendor/rustix/src/backend/libc/offset.rs vendored Normal file
View file

@ -0,0 +1,406 @@
//! Automatically enable “large file” support features.
#[cfg(not(windows))]
use super::c;
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
)))]
#[cfg(feature = "fs")]
pub(super) use c::{
fstat as libc_fstat, fstatat as libc_fstatat, ftruncate as libc_ftruncate, lseek as libc_lseek,
off_t as libc_off_t,
};
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
))]
#[cfg(feature = "fs")]
pub(super) use c::{
fstat64 as libc_fstat, fstatat64 as libc_fstatat, ftruncate64 as libc_ftruncate,
lseek64 as libc_lseek, off64_t as libc_off_t,
};
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
))]
pub(super) use c::rlimit64 as libc_rlimit;
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
target_os = "wasi",
)))]
#[cfg(feature = "mm")]
pub(super) use c::mmap as libc_mmap;
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "l4re",
target_os = "linux",
target_os = "redox",
target_os = "wasi",
)))]
pub(super) use c::{rlimit as libc_rlimit, RLIM_INFINITY as LIBC_RLIM_INFINITY};
#[cfg(not(any(
windows,
target_os = "android",
target_os = "fuchsia",
target_os = "emscripten",
target_os = "l4re",
target_os = "linux",
target_os = "wasi",
)))]
pub(super) use c::{getrlimit as libc_getrlimit, setrlimit as libc_setrlimit};
// TODO: Add `RLIM64_INFINITY` to upstream libc.
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
))]
pub(super) const LIBC_RLIM_INFINITY: u64 = !0_u64;
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
))]
pub(super) use c::{getrlimit64 as libc_getrlimit, setrlimit64 as libc_setrlimit};
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
))]
#[cfg(feature = "mm")]
pub(super) use c::mmap64 as libc_mmap;
// `prlimit64` wasn't supported in glibc until 2.13.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
weak_or_syscall! {
fn prlimit64(
pid: c::pid_t,
resource: c::__rlimit_resource_t,
new_limit: *const c::rlimit64,
old_limit: *mut c::rlimit64
) via SYS_prlimit64 -> c::c_int
}
#[cfg(all(target_os = "linux", target_env = "musl"))]
weak_or_syscall! {
fn prlimit64(
pid: c::pid_t,
resource: c::c_int,
new_limit: *const c::rlimit64,
old_limit: *mut c::rlimit64
) via SYS_prlimit64 -> c::c_int
}
#[cfg(target_os = "android")]
weak_or_syscall! {
fn prlimit64(
pid: c::pid_t,
resource: c::c_int,
new_limit: *const c::rlimit64,
old_limit: *mut c::rlimit64
) via SYS_prlimit64 -> c::c_int
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) unsafe fn libc_prlimit(
pid: c::pid_t,
resource: c::__rlimit_resource_t,
new_limit: *const c::rlimit64,
old_limit: *mut c::rlimit64,
) -> c::c_int {
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub(super) unsafe fn libc_prlimit(
pid: c::pid_t,
resource: c::c_int,
new_limit: *const c::rlimit64,
old_limit: *mut c::rlimit64,
) -> c::c_int {
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(target_os = "android")]
pub(super) unsafe fn libc_prlimit(
pid: c::pid_t,
resource: c::c_int,
new_limit: *const c::rlimit64,
old_limit: *mut c::rlimit64,
) -> c::c_int {
prlimit64(pid, resource, new_limit, old_limit)
}
#[cfg(not(any(
windows,
target_os = "android",
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
target_os = "redox",
)))]
#[cfg(feature = "fs")]
pub(super) use c::openat as libc_openat;
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
))]
#[cfg(feature = "fs")]
pub(super) use c::openat64 as libc_openat;
#[cfg(target_os = "fuchsia")]
#[cfg(feature = "fs")]
pub(super) use c::fallocate as libc_fallocate;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "fs")]
pub(super) use c::fallocate64 as libc_fallocate;
#[cfg(not(any(
windows,
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "linux",
target_os = "l4re",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
#[cfg(feature = "fs")]
pub(super) use c::posix_fadvise as libc_posix_fadvise;
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "linux",
target_os = "l4re",
))]
#[cfg(feature = "fs")]
pub(super) use c::posix_fadvise64 as libc_posix_fadvise;
#[cfg(all(not(any(
windows,
target_os = "android",
target_os = "linux",
target_os = "emscripten",
))))]
pub(super) use c::{pread as libc_pread, pwrite as libc_pwrite};
#[cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten"))]
pub(super) use c::{pread64 as libc_pread, pwrite64 as libc_pwrite};
#[cfg(any(target_os = "linux", target_os = "emscripten"))]
pub(super) use c::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};
#[cfg(target_os = "android")]
mod readwrite_pv64 {
use super::c;
// 64-bit offsets on 32-bit platforms are passed in endianness-specific
// lo/hi pairs. See src/backend/linux_raw/conv.rs for details.
#[cfg(all(target_endian = "little", target_pointer_width = "32"))]
fn lo(x: u64) -> usize {
(x >> 32) as usize
}
#[cfg(all(target_endian = "little", target_pointer_width = "32"))]
fn hi(x: u64) -> usize {
(x & 0xffff_ffff) as usize
}
#[cfg(all(target_endian = "big", target_pointer_width = "32"))]
fn lo(x: u64) -> usize {
(x & 0xffff_ffff) as usize
}
#[cfg(all(target_endian = "big", target_pointer_width = "32"))]
fn hi(x: u64) -> usize {
(x >> 32) as usize
}
pub(in super::super) unsafe fn preadv64(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off64_t,
) -> c::ssize_t {
// Older Android libc lacks `preadv64`, so use the `weak!` mechanism to
// test for it, and call back to `c::syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64(c::c_int, *const c::iovec, c::c_int, c::off64_t) -> c::ssize_t
}
if let Some(fun) = preadv64.get() {
fun(fd, iov, iovcnt, offset)
} else {
#[cfg(target_pointer_width = "32")]
{
c::syscall(
c::SYS_preadv,
fd,
iov,
iovcnt,
hi(offset as u64),
lo(offset as u64),
) as c::ssize_t
}
#[cfg(target_pointer_width = "64")]
{
c::syscall(c::SYS_preadv, fd, iov, iovcnt, offset) as c::ssize_t
}
}
}
pub(in super::super) unsafe fn pwritev64(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off64_t,
) -> c::ssize_t {
// See the comments in `preadv64`.
weak! {
fn pwritev64(c::c_int, *const c::iovec, c::c_int, c::off64_t) -> c::ssize_t
}
if let Some(fun) = pwritev64.get() {
fun(fd, iov, iovcnt, offset)
} else {
#[cfg(target_pointer_width = "32")]
{
c::syscall(
c::SYS_pwritev,
fd,
iov,
iovcnt,
hi(offset as u64),
lo(offset as u64),
) as c::ssize_t
}
#[cfg(target_pointer_width = "64")]
{
c::syscall(c::SYS_pwritev, fd, iov, iovcnt, offset) as c::ssize_t
}
}
}
}
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "redox",
target_os = "solaris",
)))]
pub(super) use c::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(target_os = "android")]
pub(super) use readwrite_pv64::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};
// macOS added preadv and pwritev in version 11.0
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod readwrite_pv {
use super::c;
weakcall! {
pub(in super::super) fn preadv(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off_t
) -> c::ssize_t
}
weakcall! {
pub(in super::super) fn pwritev(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int, offset: c::off_t
) -> c::ssize_t
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) use c::{preadv64v2 as libc_preadv2, pwritev64v2 as libc_pwritev2};
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub(super) use readwrite_pv::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(not(any(
windows,
target_os = "aix",
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "illumos",
target_os = "ios",
target_os = "linux",
target_os = "l4re",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
#[cfg(feature = "fs")]
pub(super) use c::posix_fallocate as libc_posix_fallocate;
#[cfg(target_os = "l4re")]
#[cfg(feature = "fs")]
pub(super) use c::posix_fallocate64 as libc_posix_fallocate;
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "illumos",
target_os = "linux",
target_os = "l4re",
target_os = "netbsd",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
#[cfg(feature = "fs")]
pub(super) use {c::fstatfs as libc_fstatfs, c::statfs as libc_statfs};
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "illumos",
target_os = "linux",
target_os = "l4re",
target_os = "redox",
target_os = "solaris",
target_os = "wasi",
)))]
#[cfg(feature = "fs")]
pub(super) use {c::fstatvfs as libc_fstatvfs, c::statvfs as libc_statvfs};
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "emscripten",
target_os = "l4re",
))]
#[cfg(feature = "fs")]
pub(super) use {
c::fstatfs64 as libc_fstatfs, c::fstatvfs64 as libc_fstatvfs, c::statfs64 as libc_statfs,
c::statvfs64 as libc_statvfs,
};

View file

@ -0,0 +1,54 @@
use super::super::c;
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
use crate::ffi::CStr;
// `getauxval` wasn't supported in glibc until 2.16.
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
weak!(fn getauxval(c::c_ulong) -> *mut c::c_void);
#[inline]
pub(crate) fn page_size() -> usize {
unsafe { c::sysconf(c::_SC_PAGESIZE) as usize }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn clock_ticks_per_second() -> u64 {
unsafe { c::sysconf(c::_SC_CLK_TCK) as u64 }
}
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
#[inline]
pub(crate) fn linux_hwcap() -> (usize, usize) {
if let Some(libc_getauxval) = getauxval.get() {
unsafe {
let hwcap = libc_getauxval(c::AT_HWCAP) as usize;
let hwcap2 = libc_getauxval(c::AT_HWCAP2) as usize;
(hwcap, hwcap2)
}
} else {
(0, 0)
}
}
#[cfg(any(
all(target_os = "android", target_pointer_width = "64"),
target_os = "linux",
))]
#[inline]
pub(crate) fn linux_execfn() -> &'static CStr {
if let Some(libc_getauxval) = getauxval.get() {
unsafe { CStr::from_ptr(libc_getauxval(c::AT_EXECFN).cast()) }
} else {
cstr!("")
}
}

View file

@ -0,0 +1 @@
pub(crate) mod auxv;

View file

@ -0,0 +1,49 @@
#![allow(non_snake_case)]
use super::super::c;
use super::types::{RawCpuSet, CPU_SETSIZE};
#[inline]
pub(crate) fn CPU_SET(cpu: usize, cpuset: &mut RawCpuSet) {
assert!(
cpu < CPU_SETSIZE,
"cpu out of bounds: the cpu max is {} but the cpu is {}",
CPU_SETSIZE,
cpu
);
unsafe { c::CPU_SET(cpu, cpuset) }
}
#[inline]
pub(crate) fn CPU_ZERO(cpuset: &mut RawCpuSet) {
unsafe { c::CPU_ZERO(cpuset) }
}
#[inline]
pub(crate) fn CPU_CLR(cpu: usize, cpuset: &mut RawCpuSet) {
assert!(
cpu < CPU_SETSIZE,
"cpu out of bounds: the cpu max is {} but the cpu is {}",
CPU_SETSIZE,
cpu
);
unsafe { c::CPU_CLR(cpu, cpuset) }
}
#[inline]
pub(crate) fn CPU_ISSET(cpu: usize, cpuset: &RawCpuSet) -> bool {
assert!(
cpu < CPU_SETSIZE,
"cpu out of bounds: the cpu max is {} but the cpu is {}",
CPU_SETSIZE,
cpu
);
unsafe { c::CPU_ISSET(cpu, cpuset) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn CPU_COUNT(cpuset: &RawCpuSet) -> u32 {
use core::convert::TryInto;
unsafe { c::CPU_COUNT(cpuset).try_into().unwrap() }
}

View file

@ -0,0 +1,12 @@
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "linux",
))]
pub(crate) mod cpu_set;
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;
#[cfg(not(target_os = "wasi"))]
pub(crate) mod wait;

View file

@ -0,0 +1,465 @@
//! libc syscalls supporting `rustix::process`.
use super::super::c;
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
use super::super::conv::borrowed_fd;
#[cfg(not(target_os = "wasi"))]
use super::super::conv::ret_pid_t;
use super::super::conv::{c_str, ret, ret_c_int, ret_discarded_char_ptr};
#[cfg(any(target_os = "android", target_os = "linux"))]
use super::super::conv::{syscall_ret, syscall_ret_u32};
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "linux",
))]
use super::types::RawCpuSet;
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
use crate::fd::BorrowedFd;
use crate::ffi::CStr;
use crate::io;
use core::mem::MaybeUninit;
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
use {
super::super::conv::ret_infallible,
super::super::offset::{libc_getrlimit, libc_rlimit, libc_setrlimit, LIBC_RLIM_INFINITY},
crate::process::{Resource, Rlimit},
core::convert::TryInto,
};
#[cfg(any(target_os = "android", target_os = "linux"))]
use {
super::super::offset::libc_prlimit,
crate::process::{Cpuid, MembarrierCommand, MembarrierQuery},
};
#[cfg(not(target_os = "wasi"))]
use {
super::types::RawUname,
crate::process::{Gid, Pid, RawNonZeroPid, RawPid, Signal, Uid, WaitOptions, WaitStatus},
};
#[cfg(not(target_os = "wasi"))]
pub(crate) fn chdir(path: &CStr) -> io::Result<()> {
unsafe { ret(c::chdir(c_str(path))) }
}
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
pub(crate) fn fchdir(dirfd: BorrowedFd<'_>) -> io::Result<()> {
unsafe { ret(c::fchdir(borrowed_fd(dirfd))) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn getcwd(buf: &mut [u8]) -> io::Result<()> {
unsafe { ret_discarded_char_ptr(c::getcwd(buf.as_mut_ptr().cast(), buf.len())) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn membarrier_query() -> MembarrierQuery {
// GLIBC does not have a wrapper for `membarrier`; [the documentation]
// says to use `syscall`.
//
// [the documentation]: https://man7.org/linux/man-pages/man2/membarrier.2.html#NOTES
const MEMBARRIER_CMD_QUERY: u32 = 0;
unsafe {
match syscall_ret_u32(c::syscall(c::SYS_membarrier, MEMBARRIER_CMD_QUERY, 0)) {
Ok(query) => MembarrierQuery::from_bits_unchecked(query),
Err(_) => MembarrierQuery::empty(),
}
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
unsafe { syscall_ret(c::syscall(c::SYS_membarrier, cmd as u32, 0)) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> {
const MEMBARRIER_CMD_FLAG_CPU: u32 = 1;
unsafe {
syscall_ret(c::syscall(
c::SYS_membarrier,
cmd as u32,
MEMBARRIER_CMD_FLAG_CPU,
cpu.as_raw(),
))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getuid() -> Uid {
unsafe {
let uid = c::getuid();
Uid::from_raw(uid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn geteuid() -> Uid {
unsafe {
let uid = c::geteuid();
Uid::from_raw(uid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getgid() -> Gid {
unsafe {
let gid = c::getgid();
Gid::from_raw(gid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getegid() -> Gid {
unsafe {
let gid = c::getegid();
Gid::from_raw(gid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getpid() -> Pid {
unsafe {
let pid = c::getpid();
debug_assert_ne!(pid, 0);
Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getppid() -> Option<Pid> {
unsafe {
let pid: i32 = c::getppid();
Pid::from_raw(pid)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> {
unsafe {
let pgid = ret_pid_t(c::getpgid(Pid::as_raw(pid) as _))?;
debug_assert_ne!(pgid, 0);
Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid)))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn getpgrp() -> Pid {
unsafe {
let pgid = c::getpgrp();
debug_assert_ne!(pgid, 0);
Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid))
}
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "linux",
))]
#[inline]
pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> {
unsafe {
ret(c::sched_getaffinity(
Pid::as_raw(pid) as _,
core::mem::size_of::<RawCpuSet>(),
cpuset,
))
}
}
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "linux",
))]
#[inline]
pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> {
unsafe {
ret(c::sched_setaffinity(
Pid::as_raw(pid) as _,
core::mem::size_of::<RawCpuSet>(),
cpuset,
))
}
}
#[inline]
pub(crate) fn sched_yield() {
unsafe {
let _ = c::sched_yield();
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn uname() -> RawUname {
let mut uname = MaybeUninit::<RawUname>::uninit();
unsafe {
ret(c::uname(uname.as_mut_ptr())).unwrap();
uname.assume_init()
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
#[inline]
pub(crate) fn nice(inc: i32) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::nice(inc) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::getpriority(c::PRIO_USER, uid.as_raw() as _) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::getpriority(c::PRIO_PGRP, Pid::as_raw(pgid) as _) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> {
libc_errno::set_errno(libc_errno::Errno(0));
let r = unsafe { c::getpriority(c::PRIO_PROCESS, Pid::as_raw(pid) as _) };
if libc_errno::errno().0 != 0 {
ret_c_int(r)
} else {
Ok(r)
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> {
unsafe { ret(c::setpriority(c::PRIO_USER, uid.as_raw() as _, priority)) }
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> {
unsafe {
ret(c::setpriority(
c::PRIO_PGRP,
Pid::as_raw(pgid) as _,
priority,
))
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> {
unsafe {
ret(c::setpriority(
c::PRIO_PROCESS,
Pid::as_raw(pid) as _,
priority,
))
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn getrlimit(limit: Resource) -> Rlimit {
let mut result = MaybeUninit::<libc_rlimit>::uninit();
unsafe {
ret_infallible(libc_getrlimit(limit as _, result.as_mut_ptr()));
rlimit_from_libc(result.assume_init())
}
}
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[inline]
pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
let lim = rlimit_to_libc(new)?;
unsafe { ret(libc_setrlimit(limit as _, &lim)) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> {
let lim = rlimit_to_libc(new)?;
let mut result = MaybeUninit::<libc_rlimit>::uninit();
unsafe {
ret(libc_prlimit(
Pid::as_raw(pid),
limit as _,
&lim,
result.as_mut_ptr(),
))?;
Ok(rlimit_from_libc(result.assume_init()))
}
}
/// Convert a Rust [`Rlimit`] to a C `libc_rlimit`.
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
fn rlimit_from_libc(lim: libc_rlimit) -> Rlimit {
let current = if lim.rlim_cur == LIBC_RLIM_INFINITY {
None
} else {
Some(lim.rlim_cur.try_into().unwrap())
};
let maximum = if lim.rlim_max == LIBC_RLIM_INFINITY {
None
} else {
Some(lim.rlim_max.try_into().unwrap())
};
Rlimit { current, maximum }
}
/// Convert a C `libc_rlimit` to a Rust `Rlimit`.
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
fn rlimit_to_libc(lim: Rlimit) -> io::Result<libc_rlimit> {
let Rlimit { current, maximum } = lim;
let rlim_cur = match current {
Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
None => LIBC_RLIM_INFINITY as _,
};
let rlim_max = match maximum {
Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
None => LIBC_RLIM_INFINITY as _,
};
Ok(libc_rlimit { rlim_cur, rlim_max })
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
_waitpid(!0, waitopts)
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn waitpid(
pid: Option<Pid>,
waitopts: WaitOptions,
) -> io::Result<Option<(Pid, WaitStatus)>> {
_waitpid(Pid::as_raw(pid), waitopts)
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn _waitpid(
pid: RawPid,
waitopts: WaitOptions,
) -> io::Result<Option<(Pid, WaitStatus)>> {
unsafe {
let mut status: c::c_int = 0;
let pid = ret_c_int(c::waitpid(pid as _, &mut status, waitopts.bits() as _))?;
Ok(RawNonZeroPid::new(pid).map(|non_zero| {
(
Pid::from_raw_nonzero(non_zero),
WaitStatus::new(status as _),
)
}))
}
}
#[inline]
pub(crate) fn exit_group(code: c::c_int) -> ! {
// `_exit` and `_Exit` are the same; it's just a matter of which ones
// the libc bindings expose.
#[cfg(any(target_os = "wasi", target_os = "solid"))]
unsafe {
c::_Exit(code)
}
#[cfg(unix)]
unsafe {
c::_exit(code)
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn setsid() -> io::Result<Pid> {
unsafe {
let pid = ret_c_int(c::setsid())?;
debug_assert_ne!(pid, 0);
Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid)))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
unsafe { ret(c::kill(pid.as_raw_nonzero().get(), sig as i32)) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> {
unsafe {
ret(c::kill(
pid.as_raw_nonzero().get().wrapping_neg(),
sig as i32,
))
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> {
unsafe { ret(c::kill(0, sig as i32)) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) unsafe fn prctl(
option: c::c_int,
arg2: *mut c::c_void,
arg3: *mut c::c_void,
arg4: *mut c::c_void,
arg5: *mut c::c_void,
) -> io::Result<c::c_int> {
ret_c_int(c::prctl(option, arg2, arg3, arg4, arg5))
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[inline]
pub(crate) unsafe fn procctl(
idtype: c::idtype_t,
id: c::id_t,
option: c::c_int,
data: *mut c::c_void,
) -> io::Result<()> {
ret(c::procctl(idtype, id, option, data))
}

View file

@ -0,0 +1,414 @@
use super::super::c;
/// A command for use with [`membarrier`] and [`membarrier_cpu`].
///
/// For `MEMBARRIER_CMD_QUERY`, see [`membarrier_query`].
///
/// [`membarrier`]: crate::process::membarrier
/// [`membarrier_cpu`]: crate::process::membarrier_cpu
/// [`membarrier_query`]: crate::process::membarrier_query
// TODO: These are not yet exposed through libc, so we define the
// constants ourselves.
#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(u32)]
pub enum MembarrierCommand {
/// `MEMBARRIER_CMD_GLOBAL`
#[doc(alias = "Shared")]
#[doc(alias = "MEMBARRIER_CMD_SHARED")]
Global = 1,
/// `MEMBARRIER_CMD_GLOBAL_EXPEDITED`
GlobalExpedited = 2,
/// `MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED`
RegisterGlobalExpedited = 4,
/// `MEMBARRIER_CMD_PRIVATE_EXPEDITED`
PrivateExpedited = 8,
/// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED`
RegisterPrivateExpedited = 16,
/// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE`
PrivateExpeditedSyncCore = 32,
/// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE`
RegisterPrivateExpeditedSyncCore = 64,
/// `MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10)
PrivateExpeditedRseq = 128,
/// `MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ` (since Linux 5.10)
RegisterPrivateExpeditedRseq = 256,
}
/// A resource value for use with [`getrlimit`], [`setrlimit`], and
/// [`prlimit`].
///
/// [`getrlimit`]: crate::process::getrlimit
/// [`setrlimit`]: crate::process::setrlimit
/// [`prlimit`]: crate::process::prlimit
#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum Resource {
/// `RLIMIT_CPU`
Cpu = c::RLIMIT_CPU as c::c_int,
/// `RLIMIT_FSIZE`
Fsize = c::RLIMIT_FSIZE as c::c_int,
/// `RLIMIT_DATA`
Data = c::RLIMIT_DATA as c::c_int,
/// `RLIMIT_STACK`
Stack = c::RLIMIT_STACK as c::c_int,
/// `RLIMIT_CORE`
#[cfg(not(target_os = "haiku"))]
Core = c::RLIMIT_CORE as c::c_int,
/// `RLIMIT_RSS`
#[cfg(not(any(
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "solaris",
)))]
Rss = c::RLIMIT_RSS as c::c_int,
/// `RLIMIT_NPROC`
#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))]
Nproc = c::RLIMIT_NPROC as c::c_int,
/// `RLIMIT_NOFILE`
Nofile = c::RLIMIT_NOFILE as c::c_int,
/// `RLIMIT_MEMLOCK`
#[cfg(not(any(
target_os = "aix",
target_os = "haiku",
target_os = "illumos",
target_os = "solaris"
)))]
Memlock = c::RLIMIT_MEMLOCK as c::c_int,
/// `RLIMIT_AS`
#[cfg(not(target_os = "openbsd"))]
As = c::RLIMIT_AS as c::c_int,
/// `RLIMIT_LOCKS`
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
Locks = c::RLIMIT_LOCKS as c::c_int,
/// `RLIMIT_SIGPENDING`
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
Sigpending = c::RLIMIT_SIGPENDING as c::c_int,
/// `RLIMIT_MSGQUEUE`
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
Msgqueue = c::RLIMIT_MSGQUEUE as c::c_int,
/// `RLIMIT_NICE`
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
Nice = c::RLIMIT_NICE as c::c_int,
/// `RLIMIT_RTPRIO`
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
Rtprio = c::RLIMIT_RTPRIO as c::c_int,
/// `RLIMIT_RTTIME`
#[cfg(not(any(
target_os = "aix",
target_os = "android",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
)))]
Rttime = c::RLIMIT_RTTIME as c::c_int,
}
#[cfg(any(target_os = "ios", target_os = "macos"))]
impl Resource {
/// `RLIMIT_RSS`
#[allow(non_upper_case_globals)]
pub const Rss: Self = Self::As;
}
/// A signal number for use with [`kill_process`], [`kill_process_group`],
/// and [`kill_current_process_group`].
///
/// [`kill_process`]: crate::process::kill_process
/// [`kill_process_group`]: crate::process::kill_process_group
/// [`kill_current_process_group`]: crate::process::kill_current_process_group
#[cfg(not(target_os = "wasi"))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum Signal {
/// `SIGHUP`
Hup = c::SIGHUP,
/// `SIGINT`
Int = c::SIGINT,
/// `SIGQUIT`
Quit = c::SIGQUIT,
/// `SIGILL`
Ill = c::SIGILL,
/// `SIGTRAP`
Trap = c::SIGTRAP,
/// `SIGABRT`, aka `SIGIOT`
#[doc(alias = "Iot")]
#[doc(alias = "Abrt")]
Abort = c::SIGABRT,
/// `SIGBUS`
Bus = c::SIGBUS,
/// `SIGFPE`
Fpe = c::SIGFPE,
/// `SIGKILL`
Kill = c::SIGKILL,
/// `SIGUSR1`
Usr1 = c::SIGUSR1,
/// `SIGSEGV`
Segv = c::SIGSEGV,
/// `SIGUSR2`
Usr2 = c::SIGUSR2,
/// `SIGPIPE`
Pipe = c::SIGPIPE,
/// `SIGALRM`
#[doc(alias = "Alrm")]
Alarm = c::SIGALRM,
/// `SIGTERM`
Term = c::SIGTERM,
/// `SIGSTKFLT`
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
all(
any(target_os = "android", target_os = "linux"),
any(
target_arch = "mips",
target_arch = "mips64",
target_arch = "sparc",
target_arch = "sparc64"
),
)
)))]
Stkflt = c::SIGSTKFLT,
/// `SIGCHLD`
#[doc(alias = "Chld")]
Child = c::SIGCHLD,
/// `SIGCONT`
Cont = c::SIGCONT,
/// `SIGSTOP`
Stop = c::SIGSTOP,
/// `SIGTSTP`
Tstp = c::SIGTSTP,
/// `SIGTTIN`
Ttin = c::SIGTTIN,
/// `SIGTTOU`
Ttou = c::SIGTTOU,
/// `SIGURG`
Urg = c::SIGURG,
/// `SIGXCPU`
Xcpu = c::SIGXCPU,
/// `SIGXFSZ`
Xfsz = c::SIGXFSZ,
/// `SIGVTALRM`
#[doc(alias = "Vtalrm")]
Vtalarm = c::SIGVTALRM,
/// `SIGPROF`
Prof = c::SIGPROF,
/// `SIGWINCH`
Winch = c::SIGWINCH,
/// `SIGIO`, aka `SIGPOLL`
#[doc(alias = "Poll")]
#[cfg(not(target_os = "haiku"))]
Io = c::SIGIO,
/// `SIGPWR`
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
#[doc(alias = "Pwr")]
Power = c::SIGPWR,
/// `SIGSYS`, aka `SIGUNUSED`
#[doc(alias = "Unused")]
Sys = c::SIGSYS,
}
#[cfg(not(target_os = "wasi"))]
impl Signal {
/// Convert a raw signal number into a `Signal`, if possible.
pub fn from_raw(sig: i32) -> Option<Self> {
match sig as _ {
c::SIGHUP => Some(Self::Hup),
c::SIGINT => Some(Self::Int),
c::SIGQUIT => Some(Self::Quit),
c::SIGILL => Some(Self::Ill),
c::SIGTRAP => Some(Self::Trap),
c::SIGABRT => Some(Self::Abort),
c::SIGBUS => Some(Self::Bus),
c::SIGFPE => Some(Self::Fpe),
c::SIGKILL => Some(Self::Kill),
c::SIGUSR1 => Some(Self::Usr1),
c::SIGSEGV => Some(Self::Segv),
c::SIGUSR2 => Some(Self::Usr2),
c::SIGPIPE => Some(Self::Pipe),
c::SIGALRM => Some(Self::Alarm),
c::SIGTERM => Some(Self::Term),
#[cfg(not(any(
target_os = "aix",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
all(
any(target_os = "android", target_os = "linux"),
any(
target_arch = "mips",
target_arch = "mips64",
target_arch = "sparc",
target_arch = "sparc64"
),
)
)))]
c::SIGSTKFLT => Some(Self::Stkflt),
c::SIGCHLD => Some(Self::Child),
c::SIGCONT => Some(Self::Cont),
c::SIGSTOP => Some(Self::Stop),
c::SIGTSTP => Some(Self::Tstp),
c::SIGTTIN => Some(Self::Ttin),
c::SIGTTOU => Some(Self::Ttou),
c::SIGURG => Some(Self::Urg),
c::SIGXCPU => Some(Self::Xcpu),
c::SIGXFSZ => Some(Self::Xfsz),
c::SIGVTALRM => Some(Self::Vtalarm),
c::SIGPROF => Some(Self::Prof),
c::SIGWINCH => Some(Self::Winch),
#[cfg(not(target_os = "haiku"))]
c::SIGIO => Some(Self::Io),
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
)))]
c::SIGPWR => Some(Self::Power),
c::SIGSYS => Some(Self::Sys),
_ => None,
}
}
}
pub const EXIT_SUCCESS: c::c_int = c::EXIT_SUCCESS;
pub const EXIT_FAILURE: c::c_int = c::EXIT_FAILURE;
#[cfg(not(target_os = "wasi"))]
pub const EXIT_SIGNALED_SIGABRT: c::c_int = 128 + c::SIGABRT;
/// A process identifier as a raw integer.
#[cfg(not(target_os = "wasi"))]
pub type RawPid = c::pid_t;
/// A non-zero process identifier as a raw non-zero integer.
#[cfg(not(target_os = "wasi"))]
pub type RawNonZeroPid = core::num::NonZeroI32;
/// A group identifier as a raw integer.
#[cfg(not(target_os = "wasi"))]
pub type RawGid = c::gid_t;
/// A user identifier as a raw integer.
#[cfg(not(target_os = "wasi"))]
pub type RawUid = c::uid_t;
/// A CPU identifier as a raw integer.
#[cfg(any(target_os = "android", target_os = "linux"))]
pub type RawCpuid = u32;
#[cfg(target_os = "freebsd")]
pub type RawId = c::id_t;
#[cfg(not(target_os = "wasi"))]
pub(crate) type RawUname = c::utsname;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "linux",
))]
pub(crate) type RawCpuSet = c::cpu_set_t;
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "linux",
))]
#[inline]
pub(crate) fn raw_cpu_set_new() -> RawCpuSet {
let mut set = unsafe { core::mem::zeroed() };
super::cpu_set::CPU_ZERO(&mut set);
set
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
pub(crate) const CPU_SETSIZE: usize = c::CPU_SETSIZE as usize;
#[cfg(target_os = "dragonfly")]
pub(crate) const CPU_SETSIZE: usize = 256;

View file

@ -0,0 +1,6 @@
use super::super::c;
pub(crate) use c::{
WCONTINUED, WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WSTOPSIG,
WTERMSIG, WUNTRACED,
};

View file

@ -0,0 +1,2 @@
pub(crate) mod syscalls;
pub(crate) mod types;

View file

@ -0,0 +1,16 @@
//! libc syscalls supporting `rustix::rand`.
#[cfg(target_os = "linux")]
use {super::super::c, super::super::conv::ret_ssize_t, crate::io, crate::rand::GetRandomFlags};
#[cfg(target_os = "linux")]
pub(crate) fn getrandom(buf: &mut [u8], flags: GetRandomFlags) -> io::Result<usize> {
// `getrandom` wasn't supported in glibc until 2.25.
weak_or_syscall! {
fn getrandom(buf: *mut c::c_void, buflen: c::size_t, flags: c::c_uint) via SYS_getrandom -> c::ssize_t
}
let nread =
unsafe { ret_ssize_t(getrandom(buf.as_mut_ptr().cast(), buf.len(), flags.bits()))? };
Ok(nread as usize)
}

View file

@ -0,0 +1,19 @@
#[cfg(target_os = "linux")]
use super::super::c;
#[cfg(target_os = "linux")]
use bitflags::bitflags;
#[cfg(target_os = "linux")]
bitflags! {
/// `GRND_*` flags for use with [`getrandom`].
///
/// [`getrandom`]: crate::rand::getrandom
pub struct GetRandomFlags: u32 {
/// `GRND_RANDOM`
const RANDOM = c::GRND_RANDOM;
/// `GRND_NONBLOCK`
const NONBLOCK = c::GRND_NONBLOCK;
/// `GRND_INSECURE`
const INSECURE = c::GRND_INSECURE;
}
}

View file

@ -0,0 +1,3 @@
pub(crate) mod syscalls;
#[cfg(not(target_os = "wasi"))]
pub(crate) mod types;

View file

@ -0,0 +1,163 @@
//! libc syscalls supporting `rustix::termios`.
//!
//! # Safety
//!
//! See the `rustix::backend::syscalls` module documentation for details.
use super::super::c;
use super::super::conv::{borrowed_fd, ret, ret_pid_t};
use crate::fd::BorrowedFd;
#[cfg(feature = "procfs")]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
use crate::ffi::CStr;
#[cfg(not(target_os = "wasi"))]
use crate::io;
#[cfg(not(target_os = "wasi"))]
use crate::process::{Pid, RawNonZeroPid};
#[cfg(not(target_os = "wasi"))]
use crate::termios::{Action, OptionalActions, QueueSelector, Speed, Termios, Winsize};
use core::mem::MaybeUninit;
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcgetattr(fd: BorrowedFd<'_>) -> io::Result<Termios> {
let mut result = MaybeUninit::<Termios>::uninit();
unsafe {
ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr()))?;
Ok(result.assume_init())
}
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result<Pid> {
unsafe {
let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?;
debug_assert_ne!(pid, 0);
Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid)))
}
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcsetpgrp(fd: BorrowedFd<'_>, pid: Pid) -> io::Result<()> {
unsafe { ret(c::tcsetpgrp(borrowed_fd(fd), pid.as_raw_nonzero().get())) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcsetattr(
fd: BorrowedFd,
optional_actions: OptionalActions,
termios: &Termios,
) -> io::Result<()> {
unsafe {
ret(c::tcsetattr(
borrowed_fd(fd),
optional_actions as _,
termios,
))
}
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcsendbreak(fd: BorrowedFd) -> io::Result<()> {
unsafe { ret(c::tcsendbreak(borrowed_fd(fd), 0)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcdrain(fd: BorrowedFd) -> io::Result<()> {
unsafe { ret(c::tcdrain(borrowed_fd(fd))) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcflush(fd: BorrowedFd, queue_selector: QueueSelector) -> io::Result<()> {
unsafe { ret(c::tcflush(borrowed_fd(fd), queue_selector as _)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcflow(fd: BorrowedFd, action: Action) -> io::Result<()> {
unsafe { ret(c::tcflow(borrowed_fd(fd), action as _)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcgetsid(fd: BorrowedFd) -> io::Result<Pid> {
unsafe {
let pid = ret_pid_t(c::tcgetsid(borrowed_fd(fd)))?;
debug_assert_ne!(pid, 0);
Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid)))
}
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcsetwinsize(fd: BorrowedFd, winsize: Winsize) -> io::Result<()> {
unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCSWINSZ, &winsize)) }
}
#[cfg(not(target_os = "wasi"))]
pub(crate) fn tcgetwinsize(fd: BorrowedFd) -> io::Result<Winsize> {
unsafe {
let mut buf = MaybeUninit::<Winsize>::uninit();
ret(c::ioctl(
borrowed_fd(fd),
c::TIOCGWINSZ.into(),
buf.as_mut_ptr(),
))?;
Ok(buf.assume_init())
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn cfgetospeed(termios: &Termios) -> Speed {
unsafe { c::cfgetospeed(termios) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn cfgetispeed(termios: &Termios) -> Speed {
unsafe { c::cfgetispeed(termios) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn cfmakeraw(termios: &mut Termios) {
unsafe { c::cfmakeraw(termios) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn cfsetospeed(termios: &mut Termios, speed: Speed) -> io::Result<()> {
unsafe { ret(c::cfsetospeed(termios, speed)) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn cfsetispeed(termios: &mut Termios, speed: Speed) -> io::Result<()> {
unsafe { ret(c::cfsetispeed(termios, speed)) }
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn cfsetspeed(termios: &mut Termios, speed: Speed) -> io::Result<()> {
unsafe { ret(c::cfsetspeed(termios, speed)) }
}
pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool {
// Use the return value of `isatty` alone. We don't check `errno` because
// we return `bool` rather than `io::Result<bool>`, because we assume
// `BorrrowedFd` protects us from `EBADF`, and any other reasonably
// anticipated errno value would end up interpreted as "assume it's not a
// terminal" anyway.
unsafe { c::isatty(borrowed_fd(fd)) != 0 }
}
#[cfg(feature = "procfs")]
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
// `ttyname_r` returns its error status rather than using `errno`.
match c::ttyname_r(borrowed_fd(dirfd), buf.as_mut_ptr().cast(), buf.len()) {
0 => Ok(CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len()),
err => Err(io::Errno::from_raw_os_error(err)),
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;

View file

@ -0,0 +1,301 @@
//! libc syscalls supporting `rustix::thread`.
use super::super::c;
use super::super::conv::ret;
#[cfg(any(target_os = "android", target_os = "linux"))]
use super::super::conv::{borrowed_fd, ret_c_int};
use super::super::time::types::LibcTimespec;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::fd::BorrowedFd;
use crate::io;
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::process::{Pid, RawNonZeroPid};
#[cfg(not(target_os = "redox"))]
use crate::thread::{NanosleepRelativeResult, Timespec};
use core::mem::MaybeUninit;
#[cfg(not(any(
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi",
)))]
use {crate::thread::ClockId, core::ptr::null_mut};
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int);
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int);
#[cfg(not(any(
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi",
)))]
#[inline]
pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
let mut remain = MaybeUninit::<LibcTimespec>::uninit();
let flags = 0;
// 32-bit gnu version: libc has `clock_nanosleep` but it is not y2038 safe by
// default.
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe {
if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
match libc_clock_nanosleep(
id as c::clockid_t,
flags,
&request.clone().into(),
remain.as_mut_ptr(),
) {
0 => NanosleepRelativeResult::Ok,
err if err == io::Errno::INTR.0 => {
NanosleepRelativeResult::Interrupted(remain.assume_init().into())
}
err => NanosleepRelativeResult::Err(io::Errno(err)),
}
} else {
clock_nanosleep_relative_old(id, request)
}
}
// Main version: libc is y2038 safe and has `clock_nanosleep`.
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
unsafe {
match c::clock_nanosleep(id as c::clockid_t, flags, request, remain.as_mut_ptr()) {
0 => NanosleepRelativeResult::Ok,
err if err == io::Errno::INTR.0 => {
NanosleepRelativeResult::Interrupted(remain.assume_init())
}
err => NanosleepRelativeResult::Err(io::Errno(err)),
}
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe fn clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
use core::convert::TryInto;
let tv_sec = match request.tv_sec.try_into() {
Ok(tv_sec) => tv_sec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
};
let tv_nsec = match request.tv_nsec.try_into() {
Ok(tv_nsec) => tv_nsec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
};
let old_request = c::timespec { tv_sec, tv_nsec };
let mut old_remain = MaybeUninit::<c::timespec>::uninit();
let flags = 0;
match c::clock_nanosleep(
id as c::clockid_t,
flags,
&old_request,
old_remain.as_mut_ptr(),
) {
0 => NanosleepRelativeResult::Ok,
err if err == io::Errno::INTR.0 => {
let old_remain = old_remain.assume_init();
let remain = Timespec {
tv_sec: old_remain.tv_sec.into(),
tv_nsec: old_remain.tv_nsec.into(),
};
NanosleepRelativeResult::Interrupted(remain)
}
err => NanosleepRelativeResult::Err(io::Errno(err)),
}
}
#[cfg(not(any(
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
target_os = "haiku",
target_os = "ios",
target_os = "macos",
target_os = "openbsd",
target_os = "redox",
target_os = "wasi",
)))]
#[inline]
pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> {
let flags = c::TIMER_ABSTIME;
// 32-bit gnu version: libc has `clock_nanosleep` but it is not y2038 safe by
// default.
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
{
if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
match unsafe {
libc_clock_nanosleep(
id as c::clockid_t,
flags,
&request.clone().into(),
null_mut(),
)
} {
0 => Ok(()),
err => Err(io::Errno(err)),
}
} else {
clock_nanosleep_absolute_old(id, request)
}
}
// Main version: libc is y2038 safe and has `clock_nanosleep`.
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, request, null_mut()) } {
0 => Ok(()),
err => Err(io::Errno(err)),
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
fn clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()> {
use core::convert::TryInto;
let flags = c::TIMER_ABSTIME;
let old_request = c::timespec {
tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
};
match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, &old_request, null_mut()) } {
0 => Ok(()),
err => Err(io::Errno(err)),
}
}
#[cfg(not(target_os = "redox"))]
#[inline]
pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult {
let mut remain = MaybeUninit::<LibcTimespec>::uninit();
// 32-bit gnu version: libc has `nanosleep` but it is not y2038 safe by
// default.
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe {
if let Some(libc_nanosleep) = __nanosleep64.get() {
match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) {
Ok(()) => NanosleepRelativeResult::Ok,
Err(io::Errno::INTR) => {
NanosleepRelativeResult::Interrupted(remain.assume_init().into())
}
Err(err) => NanosleepRelativeResult::Err(err),
}
} else {
nanosleep_old(request)
}
}
// Main version: libc is y2038 safe and has `nanosleep`.
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
unsafe {
match ret(c::nanosleep(request, remain.as_mut_ptr())) {
Ok(()) => NanosleepRelativeResult::Ok,
Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()),
Err(err) => NanosleepRelativeResult::Err(err),
}
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult {
use core::convert::TryInto;
let tv_sec = match request.tv_sec.try_into() {
Ok(tv_sec) => tv_sec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
};
let tv_nsec = match request.tv_nsec.try_into() {
Ok(tv_nsec) => tv_nsec,
Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
};
let old_request = c::timespec { tv_sec, tv_nsec };
let mut old_remain = MaybeUninit::<c::timespec>::uninit();
match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) {
Ok(()) => NanosleepRelativeResult::Ok,
Err(io::Errno::INTR) => {
let old_remain = old_remain.assume_init();
let remain = Timespec {
tv_sec: old_remain.tv_sec.into(),
tv_nsec: old_remain.tv_nsec.into(),
};
NanosleepRelativeResult::Interrupted(remain)
}
Err(err) => NanosleepRelativeResult::Err(err),
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
#[must_use]
pub(crate) fn gettid() -> Pid {
// `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2,
// so use `syscall`.
// <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62>
weak_or_syscall! {
fn gettid() via SYS_gettid -> c::pid_t
}
unsafe {
let tid = gettid();
debug_assert_ne!(tid, 0);
Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(tid))
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int> {
unsafe { ret_c_int(c::setns(borrowed_fd(fd), nstype)) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
unsafe { ret(c::unshare(flags.bits() as i32)) }
}

View file

@ -0,0 +1,3 @@
#[cfg(not(windows))]
pub(crate) mod syscalls;
pub(crate) mod types;

View file

@ -0,0 +1,414 @@
//! libc syscalls supporting `rustix::time`.
use super::super::c;
use super::super::conv::ret;
#[cfg(feature = "time")]
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
use super::super::time::types::LibcItimerspec;
use super::super::time::types::LibcTimespec;
use super::types::Timespec;
#[cfg(not(target_os = "wasi"))]
use super::types::{ClockId, DynamicClockId};
use crate::io;
use core::mem::MaybeUninit;
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(feature = "time")]
use {
super::super::conv::{borrowed_fd, ret_owned_fd},
crate::fd::{BorrowedFd, OwnedFd},
crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags},
};
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
weak!(fn __clock_gettime64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
weak!(fn __clock_getres64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[cfg(feature = "time")]
weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int);
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[cfg(feature = "time")]
weak!(fn __timerfd_settime64(c::c_int, c::c_int, *const LibcItimerspec, *mut LibcItimerspec) -> c::c_int);
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
#[inline]
#[must_use]
pub(crate) fn clock_getres(id: ClockId) -> Timespec {
let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
// 32-bit gnu version: libc has `clock_getres` but it is not y2038 safe by
// default.
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe {
if let Some(libc_clock_getres) = __clock_getres64.get() {
ret(libc_clock_getres(id as c::clockid_t, timespec.as_mut_ptr())).unwrap();
timespec.assume_init().into()
} else {
clock_getres_old(id)
}
}
// Main version: libc is y2038 safe and has `clock_getres`.
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
unsafe {
let _ = c::clock_getres(id as c::clockid_t, timespec.as_mut_ptr());
timespec.assume_init()
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[must_use]
unsafe fn clock_getres_old(id: ClockId) -> Timespec {
let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
ret(c::clock_getres(
id as c::clockid_t,
old_timespec.as_mut_ptr(),
))
.unwrap();
let old_timespec = old_timespec.assume_init();
Timespec {
tv_sec: old_timespec.tv_sec.into(),
tv_nsec: old_timespec.tv_nsec.into(),
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
#[must_use]
pub(crate) fn clock_gettime(id: ClockId) -> Timespec {
let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe {
if let Some(libc_clock_gettime) = __clock_gettime64.get() {
ret(libc_clock_gettime(
id as c::clockid_t,
timespec.as_mut_ptr(),
))
.unwrap();
timespec.assume_init().into()
} else {
clock_gettime_old(id)
}
}
// Use `unwrap()` here because `clock_getres` can fail if the clock itself
// overflows a number of seconds, but if that happens, the monotonic clocks
// can't maintain their invariants, or the realtime clocks aren't properly
// configured.
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
unsafe {
ret(c::clock_gettime(id as c::clockid_t, timespec.as_mut_ptr())).unwrap();
timespec.assume_init()
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[must_use]
unsafe fn clock_gettime_old(id: ClockId) -> Timespec {
let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
ret(c::clock_gettime(
id as c::clockid_t,
old_timespec.as_mut_ptr(),
))
.unwrap();
let old_timespec = old_timespec.assume_init();
Timespec {
tv_sec: old_timespec.tv_sec.into(),
tv_nsec: old_timespec.tv_nsec.into(),
}
}
#[cfg(not(target_os = "wasi"))]
#[inline]
pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timespec> {
let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
unsafe {
let id: c::clockid_t = match id {
DynamicClockId::Known(id) => id as c::clockid_t,
#[cfg(any(target_os = "android", target_os = "linux"))]
DynamicClockId::Dynamic(fd) => {
use crate::fd::AsRawFd;
const CLOCKFD: i32 = 3;
(!fd.as_raw_fd() << 3) | CLOCKFD
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
DynamicClockId::Dynamic(_fd) => {
// Dynamic clocks are not supported on this platform.
return Err(io::Errno::INVAL);
}
#[cfg(any(target_os = "android", target_os = "linux"))]
DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM,
#[cfg(any(target_os = "android", target_os = "linux"))]
DynamicClockId::Tai => c::CLOCK_TAI,
#[cfg(any(target_os = "android", target_os = "linux"))]
DynamicClockId::Boottime => c::CLOCK_BOOTTIME,
#[cfg(any(target_os = "android", target_os = "linux"))]
DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM,
};
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
{
if let Some(libc_clock_gettime) = __clock_gettime64.get() {
ret(libc_clock_gettime(
id as c::clockid_t,
timespec.as_mut_ptr(),
))?;
Ok(timespec.assume_init().into())
} else {
clock_gettime_dynamic_old(id)
}
}
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
{
ret(c::clock_gettime(id as c::clockid_t, timespec.as_mut_ptr()))?;
Ok(timespec.assume_init())
}
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[inline]
unsafe fn clock_gettime_dynamic_old(id: c::clockid_t) -> io::Result<Timespec> {
let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
ret(c::clock_gettime(
id as c::clockid_t,
old_timespec.as_mut_ptr(),
))?;
let old_timespec = old_timespec.assume_init();
Ok(Timespec {
tv_sec: old_timespec.tv_sec.into(),
tv_nsec: old_timespec.tv_nsec.into(),
})
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(feature = "time")]
pub(crate) fn timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::timerfd_create(id as c::clockid_t, flags.bits())) }
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(feature = "time")]
pub(crate) fn timerfd_settime(
fd: BorrowedFd<'_>,
flags: TimerfdTimerFlags,
new_value: &Itimerspec,
) -> io::Result<Itimerspec> {
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe {
if let Some(libc_timerfd_settime) = __timerfd_settime64.get() {
ret(libc_timerfd_settime(
borrowed_fd(fd),
flags.bits(),
&new_value.clone().into(),
result.as_mut_ptr(),
))?;
Ok(result.assume_init().into())
} else {
timerfd_settime_old(fd, flags, new_value)
}
}
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
unsafe {
ret(c::timerfd_settime(
borrowed_fd(fd),
flags.bits(),
new_value,
result.as_mut_ptr(),
))?;
Ok(result.assume_init())
}
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[cfg(feature = "time")]
unsafe fn timerfd_settime_old(
fd: BorrowedFd<'_>,
flags: TimerfdTimerFlags,
new_value: &Itimerspec,
) -> io::Result<Itimerspec> {
use core::convert::TryInto;
let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
// Convert `new_value` to the old `itimerspec` format.
let old_new_value = c::itimerspec {
it_interval: c::timespec {
tv_sec: new_value
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: new_value
.it_interval
.tv_nsec
.try_into()
.map_err(|_| io::Errno::INVAL)?,
},
it_value: c::timespec {
tv_sec: new_value
.it_value
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: new_value
.it_value
.tv_nsec
.try_into()
.map_err(|_| io::Errno::INVAL)?,
},
};
ret(c::timerfd_settime(
borrowed_fd(fd),
flags.bits(),
&old_new_value,
old_result.as_mut_ptr(),
))?;
let old_result = old_result.assume_init();
Ok(Itimerspec {
it_interval: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
it_value: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
})
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(feature = "time")]
pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
let mut result = MaybeUninit::<LibcItimerspec>::uninit();
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
unsafe {
if let Some(libc_timerfd_gettime) = __timerfd_gettime64.get() {
ret(libc_timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
Ok(result.assume_init().into())
} else {
timerfd_gettime_old(fd)
}
}
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
unsafe {
ret(c::timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
Ok(result.assume_init())
}
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[cfg(feature = "time")]
unsafe fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
use core::convert::TryInto;
let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
ret(c::timerfd_gettime(borrowed_fd(fd), old_result.as_mut_ptr()))?;
let old_result = old_result.assume_init();
Ok(Itimerspec {
it_interval: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
it_value: Timespec {
tv_sec: old_result
.it_interval
.tv_sec
.try_into()
.map_err(|_| io::Errno::OVERFLOW)?,
tv_nsec: old_result.it_interval.tv_nsec as _,
},
})
}

View file

@ -0,0 +1,364 @@
use super::super::c;
#[cfg(not(target_os = "wasi"))]
use crate::fd::BorrowedFd;
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
use bitflags::bitflags;
/// `struct timespec`
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
pub type Timespec = c::timespec;
/// `struct timespec`
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Timespec {
/// Seconds.
pub tv_sec: Secs,
/// Nanoseconds. Must be less than 1_000_000_000.
pub tv_nsec: Nsecs,
}
/// A type for the `tv_sec` field of [`Timespec`].
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
#[allow(deprecated)]
pub type Secs = c::time_t;
/// A type for the `tv_sec` field of [`Timespec`].
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
pub type Secs = i64;
/// A type for the `tv_nsec` field of [`Timespec`].
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
pub type Nsecs = i64;
/// A type for the `tv_nsec` field of [`Timespec`].
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
pub type Nsecs = c::c_long;
/// On most platforms, `LibcTimespec` is just `Timespec`.
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
pub(crate) type LibcTimespec = Timespec;
/// On 32-bit glibc platforms, `timespec` has anonymous padding fields, which
/// Rust doesn't support yet (see `unnamed_fields`), so we define our own
/// struct with explicit padding, with bidirectional `From` impls.
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[repr(C)]
#[derive(Debug, Clone)]
pub(crate) struct LibcTimespec {
pub(crate) tv_sec: Secs,
#[cfg(target_endian = "big")]
padding: core::mem::MaybeUninit<u32>,
pub(crate) tv_nsec: Nsecs,
#[cfg(target_endian = "little")]
padding: core::mem::MaybeUninit<u32>,
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
impl From<LibcTimespec> for Timespec {
#[inline]
fn from(t: LibcTimespec) -> Self {
Self {
tv_sec: t.tv_sec,
tv_nsec: t.tv_nsec,
}
}
}
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
impl From<Timespec> for LibcTimespec {
#[inline]
fn from(t: Timespec) -> Self {
Self {
tv_sec: t.tv_sec,
tv_nsec: t.tv_nsec,
padding: core::mem::MaybeUninit::uninit(),
}
}
}
/// `CLOCK_*` constants for use with [`clock_gettime`].
///
/// These constants are always supported at runtime so `clock_gettime` never
/// has to fail with `INVAL` due to an unsupported clock. See
/// [`DynamicClockId`] for a greater set of clocks, with the caveat that not
/// all of them are always supported.
///
/// [`clock_gettime`]: crate::time::clock_gettime
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "wasi")))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(not(target_os = "dragonfly"), repr(i32))]
#[cfg_attr(target_os = "dragonfly", repr(u64))]
#[non_exhaustive]
pub enum ClockId {
/// `CLOCK_REALTIME`
Realtime = c::CLOCK_REALTIME,
/// `CLOCK_MONOTONIC`
Monotonic = c::CLOCK_MONOTONIC,
/// `CLOCK_PROCESS_CPUTIME_ID`
#[cfg(not(any(
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
ProcessCPUTime = c::CLOCK_PROCESS_CPUTIME_ID,
/// `CLOCK_THREAD_CPUTIME_ID`
#[cfg(not(any(
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
)))]
ThreadCPUTime = c::CLOCK_THREAD_CPUTIME_ID,
/// `CLOCK_REALTIME_COARSE`
#[cfg(any(target_os = "android", target_os = "linux"))]
RealtimeCoarse = c::CLOCK_REALTIME_COARSE,
/// `CLOCK_MONOTONIC_COARSE`
#[cfg(any(target_os = "android", target_os = "linux"))]
MonotonicCoarse = c::CLOCK_MONOTONIC_COARSE,
/// `CLOCK_MONOTONIC_RAW`
#[cfg(any(target_os = "android", target_os = "linux"))]
MonotonicRaw = c::CLOCK_MONOTONIC_RAW,
}
/// `CLOCK_*` constants for use with [`clock_gettime`].
///
/// These constants are always supported at runtime so `clock_gettime` never
/// has to fail with `INVAL` due to an unsupported clock. See
/// [`DynamicClockId`] for a greater set of clocks, with the caveat that not
/// all of them are always supported.
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u32)]
#[non_exhaustive]
pub enum ClockId {
/// `CLOCK_REALTIME`
Realtime = c::CLOCK_REALTIME,
/// `CLOCK_MONOTONIC`
Monotonic = c::CLOCK_MONOTONIC,
/// `CLOCK_PROCESS_CPUTIME_ID`
ProcessCPUTime = c::CLOCK_PROCESS_CPUTIME_ID,
/// `CLOCK_THREAD_CPUTIME_ID`
ThreadCPUTime = c::CLOCK_THREAD_CPUTIME_ID,
}
/// `CLOCK_*` constants for use with [`clock_gettime_dynamic`].
///
/// These constants may be unsupported at runtime, depending on the OS version,
/// and `clock_gettime_dynamic` may fail with `INVAL`. See [`ClockId`] for
/// clocks which are always supported at runtime.
///
/// [`clock_gettime_dynamic`]: crate::time::clock_gettime_dynamic
#[cfg(not(target_os = "wasi"))]
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
pub enum DynamicClockId<'a> {
/// `ClockId` values that are always supported at runtime.
Known(ClockId),
/// Linux dynamic clocks.
Dynamic(BorrowedFd<'a>),
/// `CLOCK_REALTIME_ALARM`, available on Linux >= 3.0
#[cfg(any(target_os = "android", target_os = "linux"))]
RealtimeAlarm,
/// `CLOCK_TAI`, available on Linux >= 3.10
#[cfg(any(target_os = "android", target_os = "linux"))]
Tai,
/// `CLOCK_BOOTTIME`, available on Linux >= 2.6.39
#[cfg(any(target_os = "android", target_os = "linux"))]
Boottime,
/// `CLOCK_BOOTTIME_ALARM`, available on Linux >= 2.6.39
#[cfg(any(target_os = "android", target_os = "linux"))]
BoottimeAlarm,
}
/// `struct itimerspec`
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
pub type Itimerspec = c::itimerspec;
/// `struct itimerspec`
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[allow(missing_docs)]
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Itimerspec {
pub it_interval: Timespec,
pub it_value: Timespec,
}
/// On most platforms, `LibcItimerspec` is just `Itimerspec`.
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(not(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
)))]
pub(crate) type LibcItimerspec = Itimerspec;
/// On 32-bit glibc platforms, `LibcTimespec` differs from `Timespec`, so we
/// define our own struct, with bidirectional `From` impls.
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
#[repr(C)]
#[derive(Debug, Clone)]
pub(crate) struct LibcItimerspec {
pub it_interval: LibcTimespec,
pub it_value: LibcTimespec,
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
impl From<LibcItimerspec> for Itimerspec {
#[inline]
fn from(t: LibcItimerspec) -> Self {
Self {
it_interval: t.it_interval.into(),
it_value: t.it_value.into(),
}
}
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg(all(
any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
target_env = "gnu",
))]
impl From<Itimerspec> for LibcItimerspec {
#[inline]
fn from(t: Itimerspec) -> Self {
Self {
it_interval: t.it_interval.into(),
it_value: t.it_value.into(),
}
}
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
bitflags! {
/// `TFD_*` flags for use with [`timerfd_create`].
pub struct TimerfdFlags: c::c_int {
/// `TFD_NONBLOCK`
const NONBLOCK = c::TFD_NONBLOCK;
/// `TFD_CLOEXEC`
const CLOEXEC = c::TFD_CLOEXEC;
}
}
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
bitflags! {
/// `TFD_TIMER_*` flags for use with [`timerfd_settime`].
pub struct TimerfdTimerFlags: c::c_int {
/// `TFD_TIMER_ABSTIME`
const ABSTIME = c::TFD_TIMER_ABSTIME;
/// `TFD_TIMER_CANCEL_ON_SET`
#[cfg(any(target_os = "android", target_os = "linux"))]
const CANCEL_ON_SET = 2; // TODO: upstream TFD_TIMER_CANCEL_ON_SET
}
}
/// `CLOCK_*` constants for use with [`timerfd_create`].
///
/// [`timerfd_create`]: crate::time::timerfd_create
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum TimerfdClockId {
/// `CLOCK_REALTIME`—A clock that tells the "real" time.
///
/// This is a clock that tells the amount of time elapsed since the
/// Unix epoch, 1970-01-01T00:00:00Z. The clock is externally settable, so
/// it is not monotonic. Successive reads may see decreasing times, so it
/// isn't reliable for measuring durations.
Realtime = c::CLOCK_REALTIME,
/// `CLOCK_MONOTONIC`—A clock that tells an abstract time.
///
/// Unlike `Realtime`, this clock is not based on a fixed known epoch, so
/// individual times aren't meaningful. However, since it isn't settable,
/// it is reliable for measuring durations.
///
/// This clock does not advance while the system is suspended; see
/// `Boottime` for a clock that does.
Monotonic = c::CLOCK_MONOTONIC,
/// `CLOCK_BOOTTIME`—Like `Monotonic`, but advances while suspended.
///
/// This clock is similar to `Monotonic`, but does advance while the system
/// is suspended.
Boottime = c::CLOCK_BOOTTIME,
/// `CLOCK_REALTIME_ALARM`—Like `Realtime`, but wakes a suspended system.
///
/// This clock is like `Realtime`, but can wake up a suspended system.
///
/// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability.
RealtimeAlarm = c::CLOCK_REALTIME_ALARM,
/// `CLOCK_BOOTTIME_ALARM`—Like `Boottime`, but wakes a suspended system.
///
/// This clock is like `Boottime`, but can wake up a suspended system.
///
/// Use of this clock requires the `CAP_WAKE_ALARM` Linux capability.
BoottimeAlarm = c::CLOCK_BOOTTIME_ALARM,
}

226
vendor/rustix/src/backend/libc/weak.rs vendored Normal file
View file

@ -0,0 +1,226 @@
// Implementation derived from `weak` in Rust's
// library/std/src/sys/unix/weak.rs at revision
// fd0cb0cdc21dd9c06025277d772108f8d42cb25f.
//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in libstd require newer versions of OSes but we
//! need to maintain binary compatibility with older releases for now. In order
//! to use the new functionality when available we use this module for
//! detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable on Linux. Hence, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on `GLIBC_PRIVATE` symbols. It assumes
//! that we've been dynamically linked to the library the symbol comes from,
//! but that is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the `__pthread_get_minstack`
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628).
// There are a variety of `#[cfg]`s controlling which targets are involved in
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
// that, we'll just allow that some unix targets don't use this module at all.
#![allow(dead_code, unused_macros)]
#![allow(clippy::doc_markdown)]
use crate::ffi::CStr;
use core::ffi::c_void;
use core::ptr::null_mut;
use core::sync::atomic::{self, AtomicPtr, Ordering};
use core::{marker, mem};
const NULL: *mut c_void = null_mut();
const INVALID: *mut c_void = 1 as *mut c_void;
macro_rules! weak {
($vis:vis fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(non_upper_case_globals)]
$vis static $name: $crate::backend::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
$crate::backend::weak::Weak::new(concat!(stringify!($name), '\0'));
)
}
pub(crate) struct Weak<F> {
name: &'static str,
addr: AtomicPtr<c_void>,
_marker: marker::PhantomData<F>,
}
impl<F> Weak<F> {
pub(crate) const fn new(name: &'static str) -> Self {
Self {
name,
addr: AtomicPtr::new(INVALID),
_marker: marker::PhantomData,
}
}
pub(crate) fn get(&self) -> Option<F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
// Relaxed is fine here because we fence before reading through the
// pointer (see the comment below).
match self.addr.load(Ordering::Relaxed) {
INVALID => self.initialize(),
NULL => None,
addr => {
let func = mem::transmute_copy::<*mut c_void, F>(&addr);
// The caller is presumably going to read through this value
// (by calling the function we've dlsymed). This means we'd
// need to have loaded it with at least C11's consume
// ordering in order to be guaranteed that the data we read
// from the pointer isn't from before the pointer was
// stored. Rust has no equivalent to memory_order_consume,
// so we use an acquire fence (sorry, ARM).
//
// Now, in practice this likely isn't needed even on CPUs
// where relaxed and consume mean different things. The
// symbols we're loading are probably present (or not) at
// init, and even if they aren't the runtime dynamic loader
// is extremely likely have sufficient barriers internally
// (possibly implicitly, for example the ones provided by
// invoking `mprotect`).
//
// That said, none of that's *guaranteed*, and so we fence.
atomic::fence(Ordering::Acquire);
Some(func)
}
}
}
}
// Cold because it should only happen during first-time initialization.
#[cold]
unsafe fn initialize(&self) -> Option<F> {
let val = fetch(self.name);
// This synchronizes with the acquire fence in `get`.
self.addr.store(val, Ordering::Release);
match val {
NULL => None,
addr => Some(mem::transmute_copy::<*mut c_void, F>(&addr)),
}
}
}
unsafe fn fetch(name: &str) -> *mut c_void {
let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
Ok(c_str) => c_str,
Err(..) => return null_mut(),
};
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr().cast())
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) via $_sys_name:ident -> $ret:ty) => (
unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
libc_errno::set_errno(libc_errno::Errno(libc::ENOSYS));
-1
}
}
)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) via $sys_name:ident -> $ret:ty) => (
unsafe fn $name($($arg_name:$t),*) -> $ret {
// This looks like a hack, but concat_idents only accepts idents
// (not paths).
use libc::*;
trait AsSyscallArg {
type SyscallArgType;
fn into_syscall_arg(self) -> Self::SyscallArgType;
}
// Pass pointer types as pointers, to preserve provenance.
impl<T> AsSyscallArg for *mut T {
type SyscallArgType = *mut T;
fn into_syscall_arg(self) -> Self::SyscallArgType { self }
}
impl<T> AsSyscallArg for *const T {
type SyscallArgType = *const T;
fn into_syscall_arg(self) -> Self::SyscallArgType { self }
}
// Pass `BorrowedFd` values as the integer value.
impl AsSyscallArg for $crate::fd::BorrowedFd<'_> {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType {
$crate::fd::AsRawFd::as_raw_fd(&self) as _
}
}
// Coerce integer values into `c_long`.
impl AsSyscallArg for i32 {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType { self as _ }
}
impl AsSyscallArg for u32 {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType { self as _ }
}
impl AsSyscallArg for usize {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType { self as _ }
}
// `concat_idents is unstable, so we take an extra `sys_name`
// parameter and have our users do the concat for us for now.
/*
syscall(
concat_idents!(SYS_, $name),
$($arg_name.into_syscall_arg()),*
) as $ret
*/
syscall($sys_name, $($arg_name.into_syscall_arg()),*) as $ret
}
)
}
macro_rules! weakcall {
($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
$vis unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
// interposition, but if it's not found just fail.
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
libc_errno::set_errno(libc_errno::Errno(libc::ENOSYS));
-1
}
}
)
}
/// A combination of `weakcall` and `syscall`. Use the libc function if it's
/// available, and fall back to `libc::syscall` otherwise.
macro_rules! weak_or_syscall {
($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) via $sys_name:ident -> $ret:ty) => (
$vis unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
// interposition, but if it's not found just fail.
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
syscall! { fn $name($($arg_name: $t),*) via $sys_name -> $ret }
$name($($arg_name),*)
}
}
)
}

View file

@ -0,0 +1,82 @@
//! Adapt the Winsock2 API to resemble a POSIX-style libc API.
#![allow(unused_imports)]
#![allow(non_camel_case_types)]
use windows_sys::Win32::Networking::WinSock;
pub(crate) use libc::{
c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
c_ushort, c_void, ssize_t,
};
pub(crate) type socklen_t = i32;
// windows-sys declares these constants as unsigned. For better compatibility
// with Unix-family APIs, redeclare them as signed. Filed upstream:
// <https://github.com/microsoft/windows-rs/issues/1718>
pub(crate) const AF_INET: i32 = WinSock::AF_INET as _;
pub(crate) const AF_INET6: i32 = WinSock::AF_INET6 as _;
pub(crate) const AF_UNSPEC: i32 = WinSock::AF_UNSPEC as _;
pub(crate) const SO_TYPE: i32 = WinSock::SO_TYPE as _;
pub(crate) const SO_REUSEADDR: i32 = WinSock::SO_REUSEADDR as _;
pub(crate) const SO_BROADCAST: i32 = WinSock::SO_BROADCAST as _;
pub(crate) const SO_LINGER: i32 = WinSock::SO_LINGER as _;
pub(crate) const SOL_SOCKET: i32 = WinSock::SOL_SOCKET as _;
pub(crate) const SO_RCVTIMEO: i32 = WinSock::SO_RCVTIMEO as _;
pub(crate) const SO_SNDTIMEO: i32 = WinSock::SO_SNDTIMEO as _;
pub(crate) const IP_TTL: i32 = WinSock::IP_TTL as _;
pub(crate) const TCP_NODELAY: i32 = WinSock::TCP_NODELAY as _;
pub(crate) const IP_ADD_MEMBERSHIP: i32 = WinSock::IP_ADD_MEMBERSHIP as _;
pub(crate) const IP_DROP_MEMBERSHIP: i32 = WinSock::IP_DROP_MEMBERSHIP as _;
pub(crate) const IP_MULTICAST_TTL: i32 = WinSock::IP_MULTICAST_TTL as _;
pub(crate) const IP_MULTICAST_LOOP: i32 = WinSock::IP_MULTICAST_LOOP as _;
pub(crate) const IPV6_ADD_MEMBERSHIP: i32 = WinSock::IPV6_ADD_MEMBERSHIP as _;
pub(crate) const IPV6_DROP_MEMBERSHIP: i32 = WinSock::IPV6_DROP_MEMBERSHIP as _;
pub(crate) const IPV6_MULTICAST_LOOP: i32 = WinSock::IPV6_MULTICAST_LOOP as _;
pub(crate) const IPV6_V6ONLY: i32 = WinSock::IPV6_V6ONLY as _;
pub(crate) const POLLERR: i16 = WinSock::POLLERR as _;
pub(crate) const POLLIN: i16 = WinSock::POLLIN as _;
pub(crate) const POLLNVAL: i16 = WinSock::POLLNVAL as _;
pub(crate) const POLLHUP: i16 = WinSock::POLLHUP as _;
pub(crate) const POLLPRI: i16 = WinSock::POLLPRI as _;
pub(crate) const POLLOUT: i16 = WinSock::POLLOUT as _;
pub(crate) const POLLRDNORM: i16 = WinSock::POLLRDNORM as _;
pub(crate) const POLLWRNORM: i16 = WinSock::POLLWRNORM as _;
pub(crate) const POLLRDBAND: i16 = WinSock::POLLRDBAND as _;
pub(crate) const POLLWRBAND: i16 = WinSock::POLLWRBAND as _;
// As above, cast the types for better compatibility, and also rename these to
// their Unix names.
pub(crate) const SHUT_RDWR: i32 = WinSock::SD_BOTH as _;
pub(crate) const SHUT_RD: i32 = WinSock::SD_RECEIVE as _;
pub(crate) const SHUT_WR: i32 = WinSock::SD_SEND as _;
// Include the contents of `WinSock`, renaming as needed to match POSIX.
//
// Use `WSA_E_CANCELLED` for `ECANCELED` instead of `WSAECANCELLED`, because
// `WSAECANCELLED` will be removed in the future.
// <https://docs.microsoft.com/en-us/windows/win32/api/ws2spi/nc-ws2spi-lpnsplookupserviceend#remarks>
pub(crate) use WinSock::{
closesocket as close, ioctlsocket as ioctl, WSAPoll as poll, ADDRESS_FAMILY as sa_family_t,
ADDRINFOA as addrinfo, IN6_ADDR as in6_addr, IN_ADDR as in_addr, IPV6_MREQ as ipv6_mreq,
IP_MREQ as ip_mreq, LINGER as linger, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in,
SOCKADDR_IN6 as sockaddr_in6, SOCKADDR_STORAGE as sockaddr_storage, WSAEACCES as EACCES,
WSAEADDRINUSE as EADDRINUSE, WSAEADDRNOTAVAIL as EADDRNOTAVAIL,
WSAEAFNOSUPPORT as EAFNOSUPPORT, WSAEALREADY as EALREADY, WSAEBADF as EBADF,
WSAECONNABORTED as ECONNABORTED, WSAECONNREFUSED as ECONNREFUSED, WSAECONNRESET as ECONNRESET,
WSAEDESTADDRREQ as EDESTADDRREQ, WSAEDISCON as EDISCON, WSAEDQUOT as EDQUOT,
WSAEFAULT as EFAULT, WSAEHOSTDOWN as EHOSTDOWN, WSAEHOSTUNREACH as EHOSTUNREACH,
WSAEINPROGRESS as EINPROGRESS, WSAEINTR as EINTR, WSAEINVAL as EINVAL,
WSAEINVALIDPROCTABLE as EINVALIDPROCTABLE, WSAEINVALIDPROVIDER as EINVALIDPROVIDER,
WSAEISCONN as EISCONN, WSAELOOP as ELOOP, WSAEMFILE as EMFILE, WSAEMSGSIZE as EMSGSIZE,
WSAENAMETOOLONG as ENAMETOOLONG, WSAENETDOWN as ENETDOWN, WSAENETRESET as ENETRESET,
WSAENETUNREACH as ENETUNREACH, WSAENOBUFS as ENOBUFS, WSAENOMORE as ENOMORE,
WSAENOPROTOOPT as ENOPROTOOPT, WSAENOTCONN as ENOTCONN, WSAENOTEMPTY as ENOTEMPTY,
WSAENOTSOCK as ENOTSOCK, WSAEOPNOTSUPP as EOPNOTSUPP, WSAEPFNOSUPPORT as EPFNOSUPPORT,
WSAEPROCLIM as EPROCLIM, WSAEPROTONOSUPPORT as EPROTONOSUPPORT, WSAEPROTOTYPE as EPROTOTYPE,
WSAEPROVIDERFAILEDINIT as EPROVIDERFAILEDINIT, WSAEREFUSED as EREFUSED, WSAEREMOTE as EREMOTE,
WSAESHUTDOWN as ESHUTDOWN, WSAESOCKTNOSUPPORT as ESOCKTNOSUPPORT, WSAESTALE as ESTALE,
WSAETIMEDOUT as ETIMEDOUT, WSAETOOMANYREFS as ETOOMANYREFS, WSAEUSERS as EUSERS,
WSAEWOULDBLOCK as EWOULDBLOCK, WSAEWOULDBLOCK as EAGAIN, WSAPOLLFD as pollfd,
WSA_E_CANCELLED as ECANCELED, *,
};

View file

@ -0,0 +1,268 @@
//! aarch64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[cfg(target_pointer_width = "32")]
compile_error!("arm64-ilp32 is not supported yet");
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
lateout("x0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
in("x8") nr.to_asm(),
in("x0") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("x8") nr.to_asm(),
inlateout("x0") a0.to_asm() => r0,
in("x1") a1.to_asm(),
in("x2") a2.to_asm(),
in("x3") a3.to_asm(),
in("x4") a4.to_asm(),
in("x5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,265 @@
//! arm Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
lateout("r0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"svc 0",
in("r7") nr.to_asm(),
in("r0") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"svc 0",
in("r7") nr.to_asm(),
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,543 @@
//! mipsel Linux system calls.
//!
//! On mipsel, Linux indicates success or failure using `$a3` rather
//! than by returning a negative error code as most other architectures do.
//!
//! Mips-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, A6, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall7_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
".set noat",
"subu $sp, 32",
"sw {}, 16($sp)",
"sw {}, 20($sp)",
"sw {}, 24($sp)",
"syscall",
"addu $sp, 32",
".set at",
in(reg) a4.to_asm(),
in(reg) a5.to_asm(),
in(reg) a6.to_asm(),
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$t0*/) _,
lateout("$9" /*$t1*/) _,
lateout("$10" /*$t2*/) _,
lateout("$11" /*$t3*/) _,
lateout("$12" /*$t4*/) _,
lateout("$13" /*$t5*/) _,
lateout("$14" /*$t6*/) _,
lateout("$15" /*$t7*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View file

@ -0,0 +1,466 @@
//! mips64el Linux system calls.
//!
//! On mips64el, Linux indicates success or failure using `$a3` (`$7`) rather
//! than by returning a negative error code as most other architectures do.
//!
//! Mips-family platforms have a special calling convention for `__NR_pipe`,
//! however we use `__NR_pipe2` instead to avoid having to implement it.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
in("$2" /*$v0*/) nr.to_asm(),
in("$4" /*$a0*/) a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
lateout("$7" /*$a3*/) err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
lateout("$8" /*$a4*/) _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
lateout("$9" /*$a5*/) _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let x0;
let err: usize;
asm!(
"syscall",
inlateout("$2" /*$v0*/) nr.to_asm() => x0,
in("$4" /*$a0*/) a0.to_asm(),
in("$5" /*$a1*/) a1.to_asm(),
in("$6" /*$a2*/) a2.to_asm(),
inlateout("$7" /*$a3*/) a3.to_asm() => err,
inlateout("$8" /*$a4*/) a4.to_asm() => _,
inlateout("$9" /*$a5*/) a5.to_asm() => _,
lateout("$10" /*$a6*/) _,
lateout("$11" /*$a7*/) _,
lateout("$12" /*$t0*/) _,
lateout("$13" /*$t1*/) _,
lateout("$14" /*$t2*/) _,
lateout("$15" /*$t3*/) _,
lateout("$24" /*$t8*/) _,
lateout("$25" /*$t9*/) _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(if err != 0 {
(x0 as usize).wrapping_neg() as *mut _
} else {
x0
})
}

View file

@ -0,0 +1,18 @@
//! Inline asm for making system calls.
//!
//! Compilers should really have intrinsics for making system calls. They're
//! much like regular calls, with custom calling conventions, and calling
//! conventions are otherwise the compiler's job. But for now, use inline asm.
#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
#[cfg_attr(all(target_arch = "arm", not(thumb_mode)), path = "arm.rs")]
#[cfg_attr(all(target_arch = "arm", thumb_mode), path = "thumb.rs")]
#[cfg_attr(target_arch = "mips", path = "mips.rs")]
#[cfg_attr(target_arch = "mips64", path = "mips64.rs")]
#[cfg_attr(target_arch = "powerpc64", path = "powerpc64.rs")]
#[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
#[cfg_attr(target_arch = "x86", path = "x86.rs")]
#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
mod target_arch;
pub(in crate::backend) use self::target_arch::*;

View file

@ -0,0 +1,413 @@
//! powerpc64le Linux system calls.
//!
//! On powerpc64le, Linux indicates success or failure using `cr0.SO` rather
//! than by returning a negative error code as most other architectures do. In
//! theory we could immediately translate this into a `Result`, and it'd save a
//! few branches. And in theory we could have specialized sequences for use
//! with syscalls that are known to never fail. However, those would require
//! more extensive changes in rustix's platform-independent code. For now, we
//! check the flag and negate the error value to make PowerPC64 look like other
//! architectures.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
lateout("r3") r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
lateout("r4") _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"sc",
in("r0") nr.to_asm(),
in("r3") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
lateout("r5") _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
lateout("r6") _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
lateout("r7") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"sc",
"bns 0f",
"neg 3, 3",
"0:",
inlateout("r0") nr.to_asm() => _,
inlateout("r3") a0.to_asm() => r0,
inlateout("r4") a1.to_asm() => _,
inlateout("r5") a2.to_asm() => _,
inlateout("r6") a3.to_asm() => _,
inlateout("r7") a4.to_asm() => _,
inlateout("r8") a5.to_asm() => _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
lateout("r12") _,
lateout("cr0") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,265 @@
//! riscv64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
lateout("a0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"ecall",
in("a7") nr.to_asm(),
in("a0") a0.to_asm(),
options(noreturn)
);
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
in("a5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"ecall",
in("a7") nr.to_asm(),
inlateout("a0") a0.to_asm() => r0,
in("a1") a1.to_asm(),
in("a2") a2.to_asm(),
in("a3") a3.to_asm(),
in("a4") a4.to_asm(),
in("a5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,322 @@
//! arm Linux system calls, using thumb-mode.
//!
//! In thumb-mode, r7 is the frame pointer and is not permitted to be used in
//! an inline asm operand, so we have to use a different register and copy it
//! into r7 inside the inline asm.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
lateout("r0") r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"mov r7, {nr}",
"svc 0",
nr = in(reg) nr.to_asm(),
in("r0") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"mov {tmp}, r7",
"mov r7, {nr}",
"svc 0",
"mov r7, {tmp}",
nr = in(reg) nr.to_asm(),
tmp = out(reg) _,
inlateout("r0") a0.to_asm() => r0,
in("r1") a1.to_asm(),
in("r2") a2.to_asm(),
in("r3") a3.to_asm(),
in("r4") a4.to_asm(),
in("r5") a5.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,494 @@
//! 32-bit x86 Linux system calls.
//!
//! There are two forms; `indirect_*` which take a callee, which allow calling
//! through the vDSO when possible, and plain forms, which use the `int 0x80`
//! instruction.
//!
//! Most `rustix` syscalls use the vsyscall mechanism rather than going using
//! `int 0x80` sequences.
#![allow(dead_code)]
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use crate::backend::vdso_wrappers::SyscallType;
use core::arch::asm;
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall0(
callee: SyscallType,
nr: SyscallNumber<'_>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1_noreturn(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> ! {
asm!(
"call {callee}",
callee = in(reg) callee,
in("eax") nr.to_asm(),
in("ebx") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall2(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall3(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall4(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
// a3 should go in esi, but `asm!` won't let us use it as an operand.
// temporarily swap it into place, and then swap it back afterward.
//
// We hard-code the callee operand to use edi instead of `in(reg)` because
// even though we can't name esi as an operand, the compiler can use esi to
// satisfy `in(reg)`.
asm!(
"xchg esi, {a3}",
"call edi",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
in("edi") callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall5(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// Oof. a3 should go in esi, and `asm!` won't let us use that register as
// an operand. And we can't request stack slots. And there are no other
// registers free. Use eax as a temporary pointer to a slice, since it
// gets clobbered as the return value anyway.
asm!(
"push esi",
"push DWORD PTR [eax + 0]",
"mov esi, DWORD PTR [eax + 4]",
"mov eax, DWORD PTR [eax + 8]",
"call DWORD PTR [esp]",
"pop esi",
"pop esi",
inout("eax") &[callee as _, a3.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall6(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// Oof again. a3 should go in esi, and a5 should go in ebp, and `asm!`
// won't let us use either of those registers as operands. And we can't
// request stack slots. And there are no other registers free. Use eax as a
// temporary pointer to a slice, since it gets clobbered as the return
// value anyway.
//
// This is another reason that syscalls should be compiler intrinsics
// rather than inline asm.
asm!(
"push ebp",
"push esi",
"push DWORD PTR [eax + 0]",
"mov esi, DWORD PTR [eax + 4]",
"mov ebp, DWORD PTR [eax + 8]",
"mov eax, DWORD PTR [eax + 12]",
"call DWORD PTR [esp]",
"pop esi",
"pop esi",
"pop ebp",
inout("eax") &[callee as _, a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"int $$0x80",
in("eax") nr.to_asm(),
in("ebx") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"int $$0x80",
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
// a3 should go in esi, but `asm!` won't let us use it as an operand.
// Temporarily swap it into place, and then swap it back afterward.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// As in `syscall4`, use xchg to handle a3. a4 should go in edi, and we can
// use that register as an operand. Unlike in `indirect_syscall5`, we don't
// have a `callee` operand taking up a register, so we have enough
// registers and don't need to use a slice.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
// See the comments in `syscall5`.
asm!(
"xchg esi, {a3}",
"int $$0x80",
"xchg esi, {a3}",
a3 = in(reg) a3.to_asm(),
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// See the comments in `indirect_syscall6`.
asm!(
"push ebp",
"push esi",
"mov esi, DWORD PTR [eax + 0]",
"mov ebp, DWORD PTR [eax + 4]",
"mov eax, DWORD PTR [eax + 8]",
"int $$0x80",
"pop esi",
"pop ebp",
inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
// See the comments in `indirect_syscall6`.
asm!(
"push ebp",
"push esi",
"mov esi, DWORD PTR [eax + 0]",
"mov ebp, DWORD PTR [eax + 4]",
"mov eax, DWORD PTR [eax + 8]",
"int $$0x80",
"pop esi",
"pop ebp",
inout("eax") &[a3.to_asm(), a5.to_asm(), nr.to_asm()] => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,293 @@
//! x86-64 Linux system calls.
use crate::backend::reg::{
ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0,
};
use core::arch::asm;
#[cfg(target_pointer_width = "32")]
compile_error!("x32 is not yet supported");
#[inline]
pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
in("rax") nr.to_asm(),
in("rdi") a0.to_asm(),
options(noreturn)
)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
in("r9") a5.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags)
);
FromAsm::from_asm(r0)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6_readonly(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
let r0;
asm!(
"syscall",
inlateout("rax") nr.to_asm() => r0,
in("rdi") a0.to_asm(),
in("rsi") a1.to_asm(),
in("rdx") a2.to_asm(),
in("r10") a3.to_asm(),
in("r8") a4.to_asm(),
in("r9") a5.to_asm(),
lateout("rcx") _,
lateout("r11") _,
options(nostack, preserves_flags, readonly)
);
FromAsm::from_asm(r0)
}

View file

@ -0,0 +1,222 @@
//! Architecture-specific syscall code.
//!
//! `rustix` has inline assembly sequences using `asm!`, but that requires
//! nightly Rust, so it also has out-of-line ("outline") assembly sequences
//! in .s files. And 32-bit x86 is special (see comments below).
//!
//! This module also has a `choose` submodule which chooses a scheme and is
//! what most of the `rustix` syscalls use.
//!
//! # Safety
//!
//! This contains the inline `asm` statements performing the syscall
//! instructions and FFI declarations declaring the out-of-line ("outline")
//! syscall instructions.
#![allow(unsafe_code)]
#![cfg_attr(not(feature = "all-apis"), allow(unused_imports))]
// We'll use as many arguments as syscalls need.
#![allow(clippy::too_many_arguments)]
// When inline asm is available, use it. Otherwise, use out-of-line asm. These
// functions always use the machine's syscall instruction, even when it isn't
// the fastest option available.
#[cfg_attr(asm, path = "inline/mod.rs")]
#[cfg_attr(not(asm), path = "outline/mod.rs")]
pub(in crate::backend) mod asm;
// On most architectures, the architecture syscall instruction is fast, so use
// it directly.
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc64",
target_arch = "riscv64",
target_arch = "x86_64",
))]
pub(in crate::backend) use self::asm as choose;
// On 32-bit x86, use vDSO wrappers for all syscalls. We could use the
// architecture syscall instruction (`int 0x80`), but the vDSO kernel_vsyscall
// mechanism is much faster.
#[cfg(target_arch = "x86")]
pub(in crate::backend) use super::vdso_wrappers::x86_via_vdso as choose;
// This would be the code for always using `int 0x80` on 32-bit x86.
//#[cfg(target_arch = "x86")]
//pub(in crate::backend) use self::asm as choose;
// Macros for invoking system calls.
//
// These factor out:
// - Calling `nr` on the syscall number to convert it into `SyscallNumber`.
// - Calling `.into()` on each of the arguments to convert them into `ArgReg`.
// - Qualifying the `syscall*` and `__NR_*` identifiers.
// - Counting the number of arguments.
macro_rules! syscall {
($nr:ident) => {
$crate::backend::arch::choose::syscall0($crate::backend::reg::nr(
linux_raw_sys::general::$nr,
))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::choose::syscall2(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::choose::syscall3(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::choose::syscall4(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::choose::syscall5(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::choose::syscall6(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::choose::syscall7(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
macro_rules! syscall_readonly {
($nr:ident) => {
$crate::backend::arch::choose::syscall0_readonly($crate::backend::reg::nr(
linux_raw_sys::general::$nr,
))
};
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
($nr:ident, $a0:expr, $a1:expr) => {
$crate::backend::arch::choose::syscall2_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr) => {
$crate::backend::arch::choose::syscall3_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::backend::arch::choose::syscall4_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::backend::arch::choose::syscall5_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {
$crate::backend::arch::choose::syscall6_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
)
};
($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {
$crate::backend::arch::choose::syscall7_readonly(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
$a1.into(),
$a2.into(),
$a3.into(),
$a4.into(),
$a5.into(),
$a6.into(),
)
};
}
#[cfg(feature = "runtime")]
macro_rules! syscall_noreturn {
($nr:ident, $a0:expr) => {
$crate::backend::arch::choose::syscall1_noreturn(
$crate::backend::reg::nr(linux_raw_sys::general::$nr),
$a0.into(),
)
};
}

View file

@ -0,0 +1,119 @@
// Assembly code for making aarch64 syscalls.
//
// aarch64 syscall argument register ordering is the same as the aarch64
// userspace argument register ordering except that the syscall number
// (nr) is passed in w8.
//
// outline.rs takes care of reordering the nr argument to the end for us,
// so we only need to move nr into w8.
//
// arm64-ilp32 is not yet supported.
.file "aarch64.s"
.arch armv8-a
.section .text.rustix_syscall0_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, @function
rustix_syscall0_nr_last:
.cfi_startproc
mov w8, w0
svc #0
ret
.cfi_endproc
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, @function
rustix_syscall1_nr_last:
.cfi_startproc
mov w8, w1
svc #0
ret
.cfi_endproc
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, @function
rustix_syscall1_noreturn_nr_last:
.cfi_startproc
mov w8, w1
svc #0
brk #0x1
.cfi_endproc
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, @function
rustix_syscall2_nr_last:
.cfi_startproc
mov w8, w2
svc #0
ret
.cfi_endproc
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, @function
rustix_syscall3_nr_last:
.cfi_startproc
mov w8, w3
svc #0
ret
.cfi_endproc
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, @function
rustix_syscall4_nr_last:
.cfi_startproc
mov w8, w4
svc #0
ret
.cfi_endproc
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, @function
rustix_syscall5_nr_last:
.cfi_startproc
mov w8, w5
svc #0
ret
.cfi_endproc
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, @function
rustix_syscall6_nr_last:
.cfi_startproc
mov w8, w6
svc #0
ret
.cfi_endproc
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,135 @@
// Assembly code for making arm syscalls.
//
// arm syscall argument register ordering is the similar to the arm
// userspace argument register ordering except that the syscall number
// (nr) is passed in r7.
//
// nr_last.rs takes care of reordering the nr argument to the end for us,
// so we only need to move nr into r7 and take care of r4 and r5 if needed.
.file "arm.s"
.arch armv5t
.section .text.rustix_syscall0_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, %function
rustix_syscall0_nr_last:
.fnstart
.cantunwind
push {r7, lr}
mov r7, r0
svc #0
pop {r7, pc}
.fnend
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, %function
rustix_syscall1_nr_last:
.fnstart
.cantunwind
push {r7, lr}
mov r7, r1
svc #0
pop {r7, pc}
.fnend
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, %function
rustix_syscall1_noreturn_nr_last:
.fnstart
.cantunwind
// Don't save r7 and lr; this is noreturn, so we'll never restore them.
mov r7, r1
svc #0
udf #16 // Trap instruction
.fnend
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, %function
rustix_syscall2_nr_last:
.fnstart
.cantunwind
push {r7, lr}
mov r7, r2
svc #0
pop {r7, pc}
.fnend
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, %function
rustix_syscall3_nr_last:
.fnstart
.cantunwind
push {r7, lr}
mov r7, r3
svc #0
pop {r7, pc}
.fnend
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, %function
rustix_syscall4_nr_last:
.fnstart
.cantunwind
push {r7, lr}
ldr r7, [sp, #8]
svc #0
pop {r7, pc}
.fnend
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, %function
rustix_syscall5_nr_last:
.fnstart
.cantunwind
push {r4, r7, r11, lr}
ldr r7, [sp, #20]
ldr r4, [sp, #16]
svc #0
pop {r4, r7, r11, pc}
.fnend
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",%progbits
.p2align 4
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, %function
rustix_syscall6_nr_last:
.fnstart
.cantunwind
push {r4, r5, r7, lr}
add r7, sp, #16
ldm r7, {r4, r5, r7}
svc #0
pop {r4, r5, r7, pc}
.fnend
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",%progbits

View file

@ -0,0 +1,213 @@
# Assembly code for making mips64 syscalls.
#
# mips64 syscall argument register ordering is the same as the mips64
# userspace argument register ordering except that the syscall number
# (nr) is passed in v0.
#
# outline.rs takes care of reordering the nr argument to the end for us,
# so we only need to move nr into v0.
.file "mips.s"
.section .mdebug.abi32
.previous
.abicalls
.section .text.rustix_syscall0_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall0_nr_last
rustix_syscall0_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $4
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall0_nr_last
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall1_nr_last
rustix_syscall1_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $5
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall1_nr_last
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall1_noreturn_nr_last
rustix_syscall1_noreturn_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $5
syscall
teq $zero, $zero
.end rustix_syscall1_noreturn_nr_last
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall2_nr_last
rustix_syscall2_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $6
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall2_nr_last
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall3_nr_last
rustix_syscall3_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $7
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall3_nr_last
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall4_nr_last
rustix_syscall4_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
lw $2, 16($sp)
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall4_nr_last
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall5_nr_last
rustix_syscall5_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
lw $2, 20($sp)
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall5_nr_last
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall6_nr_last
rustix_syscall6_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
lw $2, 24($sp)
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall6_nr_last
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",@progbits
.section .text.rustix_syscall7_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall7_nr_last
.hidden rustix_syscall7_nr_last
.type rustix_syscall7_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall7_nr_last
rustix_syscall7_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
lw $2, 28($sp)
syscall
negu $8, $2
jr $31
movn $2, $8, $7
.end rustix_syscall7_nr_last
.size rustix_syscall7_nr_last, .-rustix_syscall7_nr_last
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,189 @@
# Assembly code for making mips64 syscalls.
#
# mips64 syscall argument register ordering is the same as the mips64
# userspace argument register ordering except that the syscall number
# (nr) is passed in v0.
#
# outline.rs takes care of reordering the nr argument to the end for us,
# so we only need to move nr into v0.
.file "mips.s"
.section .mdebug.abi64
.previous
.abicalls
.section .text.rustix_syscall0_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall0_nr_last
rustix_syscall0_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $4
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall0_nr_last
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall1_nr_last
rustix_syscall1_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $5
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall1_nr_last
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall1_noreturn_nr_last
rustix_syscall1_noreturn_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $5
syscall
teq $0, $0
.end rustix_syscall1_noreturn_nr_last
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall2_nr_last
rustix_syscall2_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $6
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall2_nr_last
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall3_nr_last
rustix_syscall3_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $7
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall3_nr_last
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall4_nr_last
rustix_syscall4_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $8
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall4_nr_last
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall5_nr_last
rustix_syscall5_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $9
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall5_nr_last
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, @function
.set nomips16
.set nomicromips
.ent rustix_syscall6_nr_last
rustix_syscall6_nr_last:
.frame $sp,0,$31
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
move $2, $10
syscall
dnegu $12, $2
jr $31
movn $2, $12, $7
.end rustix_syscall6_nr_last
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,33 @@
//! Declare functions defined in out-of-line ("outline") asm files.
//!
//! Kernel calling conventions differ from userspace calling conventions,
//! so we also define inline function wrappers which reorder the arguments
//! so that they match with the kernel convention as closely as possible,
//! to minimize the amount of out-of-line code we need.
#[cfg(target_arch = "x86")]
mod x86;
// For these architectures, pass the `nr` argument last.
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc64",
target_arch = "riscv64",
target_arch = "x86_64",
))]
mod nr_last;
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "powerpc64",
target_arch = "riscv64",
target_arch = "x86_64",
))]
pub(in crate::backend) use nr_last::*;
#[cfg(target_arch = "x86")]
pub(in crate::backend) use x86::*;

View file

@ -0,0 +1,166 @@
//! Syscall wrappers for platforms which pass the syscall number specially.
//!
//! Rustix aims to minimize the amount of assembly code it needs. To that end,
//! this code reorders syscall arguments as close as feasible to the actual
//! syscall convention before calling the assembly functions.
//!
//! Many architectures use a convention where the syscall number is passed in a
//! special register, with the regular syscall arguments passed in either the
//! same or similar registers as the platform C convention. This code
//! approximates that order by passing the regular syscall arguments first, and
//! the syscall number last. That way, the outline assembly code typically just
//! needs to move the syscall number to its special register, and leave the
//! other arguments mostly as they are.
#[cfg(target_arch = "mips")]
use crate::backend::reg::A6;
use crate::backend::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0};
// First we declare the actual assembly routines with `*_nr_last` names and
// reordered arguments. If the signatures or calling conventions are ever
// changed, the symbol names should also be updated accordingly, to avoid
// collisions with other versions of this crate.
//
// We don't define `_readonly` versions of these because we have no way to tell
// Rust that calls to our outline assembly are readonly.
extern "C" {
fn rustix_syscall0_nr_last(nr: SyscallNumber<'_>) -> RetReg<R0>;
fn rustix_syscall1_nr_last(a0: ArgReg<'_, A0>, nr: SyscallNumber<'_>) -> RetReg<R0>;
fn rustix_syscall1_noreturn_nr_last(a0: ArgReg<'_, A0>, nr: SyscallNumber<'_>) -> !;
fn rustix_syscall2_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall3_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall4_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall5_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall6_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
#[cfg(target_arch = "mips")]
fn rustix_syscall7_nr_last(
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
}
// Then we define inline wrapper functions that do the reordering.
#[inline]
pub(in crate::backend) unsafe fn syscall0(nr: SyscallNumber<'_>) -> RetReg<R0> {
rustix_syscall0_nr_last(nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
rustix_syscall1_nr_last(a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
rustix_syscall1_noreturn_nr_last(a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
rustix_syscall2_nr_last(a0, a1, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
rustix_syscall3_nr_last(a0, a1, a2, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
rustix_syscall4_nr_last(a0, a1, a2, a3, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
rustix_syscall5_nr_last(a0, a1, a2, a3, a4, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
rustix_syscall6_nr_last(a0, a1, a2, a3, a4, a5, nr)
}
#[cfg(target_arch = "mips")]
#[inline]
pub(in crate::backend) unsafe fn syscall7(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
a6: ArgReg<'_, A6>,
) -> RetReg<R0> {
rustix_syscall7_nr_last(a0, a1, a2, a3, a4, a5, a6, nr)
}
// Then we define the `_readonly` versions of the wrappers. We don't have
// separate `_readonly` implementations, so these can just be aliases to
// their non-`_readonly` counterparts.
#[cfg(target_arch = "mips")]
pub(in crate::backend) use syscall7 as syscall7_readonly;
pub(in crate::backend) use {
syscall0 as syscall0_readonly, syscall1 as syscall1_readonly, syscall2 as syscall2_readonly,
syscall3 as syscall3_readonly, syscall4 as syscall4_readonly, syscall5 as syscall5_readonly,
syscall6 as syscall6_readonly,
};

View file

@ -0,0 +1,132 @@
# Assembly code for making powerpc64le syscalls.
#
# powerpc64le syscall argument register ordering is the same as the
# powerpc64le userspace argument register ordering except that the syscall
# number (nr) is passed in r0.
#
# outline.rs takes care of reordering the nr argument to the end for us,
# so we only need to move nr into r0.
.file "powerpc64le.s"
.machine power8
.abiversion 2
.section .text.rustix_syscall0_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, @function
rustix_syscall0_nr_last:
.cfi_startproc
mr 0, 3
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, @function
rustix_syscall1_nr_last:
.cfi_startproc
mr 0, 4
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, @function
rustix_syscall1_noreturn_nr_last:
.cfi_startproc
mr 0, 4
sc
trap
.cfi_endproc
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, @function
rustix_syscall2_nr_last:
.cfi_startproc
mr 0, 5
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, @function
rustix_syscall3_nr_last:
.cfi_startproc
mr 0, 6
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, @function
rustix_syscall4_nr_last:
.cfi_startproc
mr 0, 7
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, @function
rustix_syscall5_nr_last:
.cfi_startproc
mr 0, 8
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",@progbits
.p2align 2
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, @function
rustix_syscall6_nr_last:
.cfi_startproc
mr 0, 9
sc
bnslr
neg 3, 3
blr
.cfi_endproc
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,116 @@
# Assembly code for making riscv64 syscalls.
#
# riscv64 syscall argument register ordering is the same as the riscv64
# userspace argument register ordering except that the syscall number
# (nr) is passed in a7.
#
# nr_last.rs takes care of reordering the nr argument to the end for us,
# so we only need to move nr into a7.
.file "riscv64.s"
.section .text.rustix_syscall0_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, @function
rustix_syscall0_nr_last:
.cfi_startproc
mv a7, a0
ecall
ret
.cfi_endproc
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, @function
rustix_syscall1_nr_last:
.cfi_startproc
mv a7, a1
ecall
ret
.cfi_endproc
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, @function
rustix_syscall1_noreturn_nr_last:
.cfi_startproc
mv a7, a1
ecall
unimp
.cfi_endproc
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, @function
rustix_syscall2_nr_last:
.cfi_startproc
mv a7, a2
ecall
ret
.cfi_endproc
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, @function
rustix_syscall3_nr_last:
.cfi_startproc
mv a7, a3
ecall
ret
.cfi_endproc
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, @function
rustix_syscall4_nr_last:
.cfi_startproc
mv a7, a4
ecall
ret
.cfi_endproc
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, @function
rustix_syscall5_nr_last:
.cfi_startproc
mv a7, a5
ecall
ret
.cfi_endproc
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, @function
rustix_syscall6_nr_last:
.cfi_startproc
mv a7, a6
ecall
ret
.cfi_endproc
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,285 @@
//! Syscall wrappers for 32-bit x86.
//!
//! This module is similar to the `nr_last` module, except specialized for
//! 32-bit x86.
//!
//! The syscall convention passes all arguments in registers. The closest we
//! can easily get to that from Rust is to use the fastcall convention which
//! passes the first two arguments in `ecx` and `edx`, which are the second
//! and third Linux syscall arguments. To line them up, this function passes
//! the second and third syscall argument as the first and second argument to
//! the outline assembly, followed by the first syscall argument, and then the
//! rest of the syscall arguments. The assembly code still has to do some work,
//! but at least we can get up to two arguments into the right place for it.
#![allow(dead_code, unused_imports)]
use crate::backend::reg::{ArgReg, RetReg, SyscallNumber, A0, A1, A2, A3, A4, A5, R0};
use crate::backend::vdso_wrappers::SyscallType;
// First we declare the actual assembly routines with `*_nr_last_fastcall`
// names and reordered arguments. If the signatures or calling conventions are
// ever changed, the symbol names should also be updated accordingly, to avoid
// collisions with other versions of this crate.
//
// We don't define `_readonly` versions of these because we have no way to tell
// Rust that calls to our outline assembly are readonly.
extern "fastcall" {
fn rustix_syscall0_nr_last_fastcall(nr: SyscallNumber<'_>) -> RetReg<R0>;
fn rustix_syscall1_nr_last_fastcall(a0: ArgReg<'_, A0>, nr: SyscallNumber<'_>) -> RetReg<R0>;
fn rustix_syscall1_noreturn_nr_last_fastcall(a0: ArgReg<'_, A0>, nr: SyscallNumber<'_>) -> !;
fn rustix_syscall2_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a0: ArgReg<'_, A0>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall3_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall4_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
a3: ArgReg<'_, A3>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall5_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
fn rustix_syscall6_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
nr: SyscallNumber<'_>,
) -> RetReg<R0>;
}
// Then we define inline wrapper functions that do the reordering.
#[inline]
pub(in crate::backend) unsafe fn syscall0(nr: SyscallNumber<'_>) -> RetReg<R0> {
rustix_syscall0_nr_last_fastcall(nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg<R0> {
rustix_syscall1_nr_last_fastcall(a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
rustix_syscall1_noreturn_nr_last_fastcall(a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall2(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
rustix_syscall2_nr_last_fastcall(a1, a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall3(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
rustix_syscall3_nr_last_fastcall(a1, a2, a0, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall4(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
rustix_syscall4_nr_last_fastcall(a1, a2, a0, a3, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall5(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
rustix_syscall5_nr_last_fastcall(a1, a2, a0, a3, a4, nr)
}
#[inline]
pub(in crate::backend) unsafe fn syscall6(
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
rustix_syscall6_nr_last_fastcall(a1, a2, a0, a3, a4, a5, nr)
}
// Then we define the `_readonly` versions of the wrappers. We don't have
// separate `_readonly` implementations, so these can just be aliases to
// their non-`_readonly` counterparts.
pub(in crate::backend) use {
syscall0 as syscall0_readonly, syscall1 as syscall1_readonly, syscall2 as syscall2_readonly,
syscall3 as syscall3_readonly, syscall4 as syscall4_readonly, syscall5 as syscall5_readonly,
syscall6 as syscall6_readonly,
};
// x86 prefers to route all syscalls through the vDSO, though this isn't
// always possible, so it also has a special form for doing the dispatch.
//
// First we declare the actual assembly routines with `*_nr_last_fastcall`
// names and reordered arguments. If the signatures or calling conventions are
// ever changed, the symbol names should also be updated accordingly, to avoid
// collisions with other versions of this crate.
extern "fastcall" {
fn rustix_indirect_syscall0_nr_last_fastcall(
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
fn rustix_indirect_syscall1_nr_last_fastcall(
a0: ArgReg<'_, A0>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
fn rustix_indirect_syscall1_noreturn_nr_last_fastcall(
a0: ArgReg<'_, A0>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> !;
fn rustix_indirect_syscall2_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a0: ArgReg<'_, A0>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
fn rustix_indirect_syscall3_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
fn rustix_indirect_syscall4_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
a3: ArgReg<'_, A3>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
fn rustix_indirect_syscall5_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
fn rustix_indirect_syscall6_nr_last_fastcall(
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a0: ArgReg<'_, A0>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
nr: SyscallNumber<'_>,
callee: SyscallType,
) -> RetReg<R0>;
}
// Then we define inline wrapper functions that do the reordering.
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall0(
callee: SyscallType,
nr: SyscallNumber<'_>,
) -> RetReg<R0> {
rustix_indirect_syscall0_nr_last_fastcall(nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> RetReg<R0> {
rustix_indirect_syscall1_nr_last_fastcall(a0, nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall1_noreturn(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
) -> ! {
rustix_indirect_syscall1_noreturn_nr_last_fastcall(a0, nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall2(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
) -> RetReg<R0> {
rustix_indirect_syscall2_nr_last_fastcall(a1, a0, nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall3(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
) -> RetReg<R0> {
rustix_indirect_syscall3_nr_last_fastcall(a1, a2, a0, nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall4(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
) -> RetReg<R0> {
rustix_indirect_syscall4_nr_last_fastcall(a1, a2, a0, a3, nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall5(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
) -> RetReg<R0> {
rustix_indirect_syscall5_nr_last_fastcall(a1, a2, a0, a3, a4, nr, callee)
}
#[inline]
pub(in crate::backend) unsafe fn indirect_syscall6(
callee: SyscallType,
nr: SyscallNumber<'_>,
a0: ArgReg<'_, A0>,
a1: ArgReg<'_, A1>,
a2: ArgReg<'_, A2>,
a3: ArgReg<'_, A3>,
a4: ArgReg<'_, A4>,
a5: ArgReg<'_, A5>,
) -> RetReg<R0> {
rustix_indirect_syscall6_nr_last_fastcall(a1, a2, a0, a3, a4, a5, nr, callee)
}

View file

@ -0,0 +1,381 @@
// Assembly code for making x86 syscalls.
//
// On x86 we use the "fastcall" convention which passes the first two
// arguments in ecx and edx. Outline.rs reorders the arguments to put
// a1 and a2 in those registers so they we don't have to move them to
// set up the kernel convention.
//
// "fastcall" expects callee to pop argument stack space, so we use
// `ret imm` instructions to clean up the stack. We don't need callee
// cleanup per se, it just comes along with using "fastcall".
.file "x86.s"
.intel_syntax noprefix
.section .text.rustix_indirect_syscall0_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall0_nr_last_fastcall
.hidden rustix_indirect_syscall0_nr_last_fastcall
.type rustix_indirect_syscall0_nr_last_fastcall, @function
rustix_indirect_syscall0_nr_last_fastcall:
.cfi_startproc
mov eax,ecx
call edx
ret
.cfi_endproc
.size rustix_indirect_syscall0_nr_last_fastcall, .-rustix_indirect_syscall0_nr_last_fastcall
.section .text.rustix_indirect_syscall1_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall1_nr_last_fastcall
.hidden rustix_indirect_syscall1_nr_last_fastcall
.type rustix_indirect_syscall1_nr_last_fastcall, @function
rustix_indirect_syscall1_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
.cfi_offset ebx, -8
mov ebx,ecx
mov eax,edx
call DWORD PTR [esp+0x8]
pop ebx
.cfi_def_cfa_offset 4
ret 0x4
.cfi_endproc
.size rustix_indirect_syscall1_nr_last_fastcall, .-rustix_indirect_syscall1_nr_last_fastcall
.section .text.rustix_indirect_syscall1_noreturn_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall1_noreturn_nr_last_fastcall
.hidden rustix_indirect_syscall1_noreturn_nr_last_fastcall
.type rustix_indirect_syscall1_noreturn_nr_last_fastcall, @function
rustix_indirect_syscall1_noreturn_nr_last_fastcall:
.cfi_startproc
mov ebx,ecx
mov eax,edx
call DWORD PTR [esp+0x4]
ud2
.cfi_endproc
.size rustix_indirect_syscall1_noreturn_nr_last_fastcall, .-rustix_indirect_syscall1_noreturn_nr_last_fastcall
.section .text.rustix_indirect_syscall2_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall2_nr_last_fastcall
.hidden rustix_indirect_syscall2_nr_last_fastcall
.type rustix_indirect_syscall2_nr_last_fastcall, @function
rustix_indirect_syscall2_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
.cfi_offset ebx, -8
mov ebx,edx
mov eax,DWORD PTR [esp+0x8]
call DWORD PTR [esp+0xc]
pop ebx
.cfi_def_cfa_offset 4
ret 0x8
.cfi_endproc
.size rustix_indirect_syscall2_nr_last_fastcall, .-rustix_indirect_syscall2_nr_last_fastcall
.section .text.rustix_indirect_syscall3_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall3_nr_last_fastcall
.hidden rustix_indirect_syscall3_nr_last_fastcall
.type rustix_indirect_syscall3_nr_last_fastcall, @function
rustix_indirect_syscall3_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0x8]
mov eax,DWORD PTR [esp+0xc]
call DWORD PTR [esp+0x10]
pop ebx
.cfi_def_cfa_offset 4
ret 0xc
.cfi_endproc
.size rustix_indirect_syscall3_nr_last_fastcall, .-rustix_indirect_syscall3_nr_last_fastcall
.section .text.rustix_indirect_syscall4_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall4_nr_last_fastcall
.hidden rustix_indirect_syscall4_nr_last_fastcall
.type rustix_indirect_syscall4_nr_last_fastcall, @function
rustix_indirect_syscall4_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
push esi
.cfi_def_cfa_offset 12
.cfi_offset esi, -12
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0xc]
mov esi,DWORD PTR [esp+0x10]
mov eax,DWORD PTR [esp+0x14]
call DWORD PTR [esp+0x18]
pop esi
.cfi_def_cfa_offset 8
pop ebx
.cfi_def_cfa_offset 4
ret 0x10
.cfi_endproc
.size rustix_indirect_syscall4_nr_last_fastcall, .-rustix_indirect_syscall4_nr_last_fastcall
.section .text.rustix_indirect_syscall5_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall5_nr_last_fastcall
.hidden rustix_indirect_syscall5_nr_last_fastcall
.type rustix_indirect_syscall5_nr_last_fastcall, @function
rustix_indirect_syscall5_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
push esi
.cfi_def_cfa_offset 12
push edi
.cfi_def_cfa_offset 16
.cfi_offset edi, -16
.cfi_offset esi, -12
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0x10]
mov esi,DWORD PTR [esp+0x14]
mov edi,DWORD PTR [esp+0x18]
mov eax,DWORD PTR [esp+0x1c]
call DWORD PTR [esp+0x20]
pop edi
.cfi_def_cfa_offset 12
pop esi
.cfi_def_cfa_offset 8
pop ebx
.cfi_def_cfa_offset 4
ret 0x14
.cfi_endproc
.size rustix_indirect_syscall5_nr_last_fastcall, .-rustix_indirect_syscall5_nr_last_fastcall
.section .text.rustix_indirect_syscall6_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_indirect_syscall6_nr_last_fastcall
.hidden rustix_indirect_syscall6_nr_last_fastcall
.type rustix_indirect_syscall6_nr_last_fastcall, @function
rustix_indirect_syscall6_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
push esi
.cfi_def_cfa_offset 12
push edi
.cfi_def_cfa_offset 16
push ebp
.cfi_def_cfa_offset 20
.cfi_offset ebp, -20
.cfi_offset edi, -16
.cfi_offset esi, -12
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0x14]
mov esi,DWORD PTR [esp+0x18]
mov edi,DWORD PTR [esp+0x1c]
mov ebp,DWORD PTR [esp+0x20]
mov eax,DWORD PTR [esp+0x24]
call DWORD PTR [esp+0x28]
pop ebp
.cfi_def_cfa_offset 16
pop edi
.cfi_def_cfa_offset 12
pop esi
.cfi_def_cfa_offset 8
pop ebx
.cfi_def_cfa_offset 4
ret 0x18
.cfi_endproc
.size rustix_indirect_syscall6_nr_last_fastcall, .-rustix_indirect_syscall6_nr_last_fastcall
.section .text.rustix_syscall0_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall0_nr_last_fastcall
.hidden rustix_syscall0_nr_last_fastcall
.type rustix_syscall0_nr_last_fastcall, @function
rustix_syscall0_nr_last_fastcall:
.cfi_startproc
mov eax,ecx
int 0x80
ret
.cfi_endproc
.size rustix_syscall0_nr_last_fastcall, .-rustix_syscall0_nr_last_fastcall
.section .text.rustix_syscall1_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall1_nr_last_fastcall
.hidden rustix_syscall1_nr_last_fastcall
.type rustix_syscall1_nr_last_fastcall, @function
rustix_syscall1_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
.cfi_offset ebx, -8
mov eax,edx
mov ebx,ecx
int 0x80
pop ebx
.cfi_def_cfa_offset 4
ret
.cfi_endproc
.size rustix_syscall1_nr_last_fastcall, .-rustix_syscall1_nr_last_fastcall
.section .text.rustix_syscall1_noreturn_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall1_noreturn_nr_last_fastcall
.hidden rustix_syscall1_noreturn_nr_last_fastcall
.type rustix_syscall1_noreturn_nr_last_fastcall, @function
rustix_syscall1_noreturn_nr_last_fastcall:
.cfi_startproc
mov eax,edx
mov ebx,ecx
int 0x80
ud2
.cfi_endproc
.size rustix_syscall1_noreturn_nr_last_fastcall, .-rustix_syscall1_noreturn_nr_last_fastcall
.section .text.rustix_syscall2_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall2_nr_last_fastcall
.hidden rustix_syscall2_nr_last_fastcall
.type rustix_syscall2_nr_last_fastcall, @function
rustix_syscall2_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
.cfi_offset ebx, -8
mov ebx,edx
mov eax,DWORD PTR [esp+0x8]
int 0x80
pop ebx
.cfi_def_cfa_offset 4
ret 0x4
.cfi_endproc
.size rustix_syscall2_nr_last_fastcall, .-rustix_syscall2_nr_last_fastcall
.section .text.rustix_syscall3_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall3_nr_last_fastcall
.hidden rustix_syscall3_nr_last_fastcall
.type rustix_syscall3_nr_last_fastcall, @function
rustix_syscall3_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0x8]
mov eax,DWORD PTR [esp+0xc]
int 0x80
pop ebx
.cfi_def_cfa_offset 4
ret 0x8
.cfi_endproc
.size rustix_syscall3_nr_last_fastcall, .-rustix_syscall3_nr_last_fastcall
.section .text.rustix_syscall4_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall4_nr_last_fastcall
.hidden rustix_syscall4_nr_last_fastcall
.type rustix_syscall4_nr_last_fastcall, @function
rustix_syscall4_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
push esi
.cfi_def_cfa_offset 12
.cfi_offset esi, -12
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0xc]
mov esi,DWORD PTR [esp+0x10]
mov eax,DWORD PTR [esp+0x14]
int 0x80
pop esi
.cfi_def_cfa_offset 8
pop ebx
.cfi_def_cfa_offset 4
ret 0xc
.cfi_endproc
.size rustix_syscall4_nr_last_fastcall, .-rustix_syscall4_nr_last_fastcall
.section .text.rustix_syscall5_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall5_nr_last_fastcall
.hidden rustix_syscall5_nr_last_fastcall
.type rustix_syscall5_nr_last_fastcall, @function
rustix_syscall5_nr_last_fastcall:
.cfi_startproc
push ebx
.cfi_def_cfa_offset 8
push edi
.cfi_def_cfa_offset 12
push esi
.cfi_def_cfa_offset 16
.cfi_offset esi, -16
.cfi_offset edi, -12
.cfi_offset ebx, -8
mov ebx,DWORD PTR [esp+0x10]
mov esi,DWORD PTR [esp+0x14]
mov edi,DWORD PTR [esp+0x18]
mov eax,DWORD PTR [esp+0x1c]
int 0x80
pop esi
.cfi_def_cfa_offset 12
pop edi
.cfi_def_cfa_offset 8
pop ebx
.cfi_def_cfa_offset 4
ret 0x10
.cfi_endproc
.size rustix_syscall5_nr_last_fastcall, .-rustix_syscall5_nr_last_fastcall
.section .text.rustix_syscall6_nr_last_fastcall,"ax",@progbits
.p2align 4
.weak rustix_syscall6_nr_last_fastcall
.hidden rustix_syscall6_nr_last_fastcall
.type rustix_syscall6_nr_last_fastcall, @function
rustix_syscall6_nr_last_fastcall:
.cfi_startproc
push ebp
.cfi_def_cfa_offset 8
push ebx
.cfi_def_cfa_offset 12
push edi
.cfi_def_cfa_offset 16
push esi
.cfi_def_cfa_offset 20
.cfi_offset esi, -20
.cfi_offset edi, -16
.cfi_offset ebx, -12
.cfi_offset ebp, -8
mov ebx,DWORD PTR [esp+0x14]
mov esi,DWORD PTR [esp+0x18]
mov edi,DWORD PTR [esp+0x1c]
mov ebp,DWORD PTR [esp+0x20]
mov eax,DWORD PTR [esp+0x24]
int 0x80
pop esi
.cfi_def_cfa_offset 16
pop edi
.cfi_def_cfa_offset 12
pop ebx
.cfi_def_cfa_offset 8
pop ebp
.cfi_def_cfa_offset 4
ret 0x14
.cfi_endproc
.size rustix_syscall6_nr_last_fastcall, .-rustix_syscall6_nr_last_fastcall
.section .text.rustix_int_0x80,"ax",@progbits
.p2align 4
.weak rustix_int_0x80
.hidden rustix_int_0x80
.type rustix_int_0x80, @function
rustix_int_0x80:
.cfi_startproc
int 0x80
ret
.cfi_endproc
.size rustix_int_0x80, .-rustix_int_0x80
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,122 @@
// Assembly code for making x86-64 syscalls.
//
// x86-64 syscall argument register ordering is the same as the x86-64
// userspace argument register ordering except that a3 is passed in r10
// instead of rcx, and the syscall number (nr) is passed in eax.
//
// outline.rs takes care of reordering the nr argument to the end for us,
// so we only need to move nr into eax and move rcx into r10 as needed.
//
// x32 is not yet supported.
.file "x86_64.s"
.intel_syntax noprefix
.section .text.rustix_syscall0_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall0_nr_last
.hidden rustix_syscall0_nr_last
.type rustix_syscall0_nr_last, @function
rustix_syscall0_nr_last:
.cfi_startproc
mov eax,edi
syscall
ret
.cfi_endproc
.size rustix_syscall0_nr_last, .-rustix_syscall0_nr_last
.section .text.rustix_syscall1_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall1_nr_last
.hidden rustix_syscall1_nr_last
.type rustix_syscall1_nr_last, @function
rustix_syscall1_nr_last:
.cfi_startproc
mov eax,esi
syscall
ret
.cfi_endproc
.size rustix_syscall1_nr_last, .-rustix_syscall1_nr_last
.section .text.rustix_syscall1_noreturn_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall1_noreturn_nr_last
.hidden rustix_syscall1_noreturn_nr_last
.type rustix_syscall1_noreturn_nr_last, @function
rustix_syscall1_noreturn_nr_last:
.cfi_startproc
mov eax,esi
syscall
ud2
.cfi_endproc
.size rustix_syscall1_noreturn_nr_last, .-rustix_syscall1_noreturn_nr_last
.section .text.rustix_syscall2_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall2_nr_last
.hidden rustix_syscall2_nr_last
.type rustix_syscall2_nr_last, @function
rustix_syscall2_nr_last:
.cfi_startproc
mov eax,edx
syscall
ret
.cfi_endproc
.size rustix_syscall2_nr_last, .-rustix_syscall2_nr_last
.section .text.rustix_syscall3_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall3_nr_last
.hidden rustix_syscall3_nr_last
.type rustix_syscall3_nr_last, @function
rustix_syscall3_nr_last:
.cfi_startproc
mov eax,ecx
syscall
ret
.cfi_endproc
.size rustix_syscall3_nr_last, .-rustix_syscall3_nr_last
.section .text.rustix_syscall4_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall4_nr_last
.hidden rustix_syscall4_nr_last
.type rustix_syscall4_nr_last, @function
rustix_syscall4_nr_last:
.cfi_startproc
mov eax,r8d
mov r10,rcx
syscall
ret
.cfi_endproc
.size rustix_syscall4_nr_last, .-rustix_syscall4_nr_last
.section .text.rustix_syscall5_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall5_nr_last
.hidden rustix_syscall5_nr_last
.type rustix_syscall5_nr_last, @function
rustix_syscall5_nr_last:
.cfi_startproc
mov eax,r9d
mov r10,rcx
syscall
ret
.cfi_endproc
.size rustix_syscall5_nr_last, .-rustix_syscall5_nr_last
.section .text.rustix_syscall6_nr_last,"ax",@progbits
.p2align 4
.weak rustix_syscall6_nr_last
.hidden rustix_syscall6_nr_last
.type rustix_syscall6_nr_last, @function
rustix_syscall6_nr_last:
.cfi_startproc
mov eax,DWORD PTR [rsp+0x8]
mov r10,rcx
syscall
ret
.cfi_endproc
.size rustix_syscall6_nr_last, .-rustix_syscall6_nr_last
.section .note.GNU-stack,"",@progbits

View file

@ -0,0 +1,29 @@
//! Adapt the Linux API to resemble a POSIX-style libc API.
//!
//! The linux_raw backend doesn't use actual libc; this just defines certain
//! types that are convenient to have defined.
#![allow(unused_imports)]
pub(crate) use linux_raw_sys::ctypes::*;
pub(crate) use linux_raw_sys::errno::EINVAL;
pub(crate) use linux_raw_sys::general::{
AF_DECnet, __kernel_sa_family_t as sa_family_t, __kernel_sockaddr_storage as sockaddr_storage,
in6_addr, in_addr, iovec, ip_mreq, ipv6_mreq, linger, sockaddr, sockaddr_in, sockaddr_in6,
sockaddr_un, socklen_t, AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH,
AF_BRIDGE, AF_CAN, AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN,
AF_IUCV, AF_KEY, AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX,
AF_RDS, AF_ROSE, AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE,
AF_X25, IPPROTO_AH, IPPROTO_BEETPH, IPPROTO_COMP, IPPROTO_DCCP, IPPROTO_EGP, IPPROTO_ENCAP,
IPPROTO_ESP, IPPROTO_ETHERNET, IPPROTO_FRAGMENT, IPPROTO_GRE, IPPROTO_ICMP, IPPROTO_ICMPV6,
IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IPIP, IPPROTO_IPV6, IPPROTO_MH, IPPROTO_MPLS,
IPPROTO_MPTCP, IPPROTO_MTP, IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW, IPPROTO_ROUTING,
IPPROTO_RSVP, IPPROTO_SCTP, IPPROTO_TCP, IPPROTO_TP, IPPROTO_UDP, IPPROTO_UDPLITE,
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_CMSG_CLOEXEC, MSG_CONFIRM,
MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK,
MSG_TRUNC, MSG_WAITALL, O_CLOEXEC, O_NONBLOCK, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM,
SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, SO_LINGER,
SO_PASSCRED, SO_RCVTIMEO_NEW, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD,
SO_TYPE, TCP_NODELAY,
};

View file

@ -0,0 +1,790 @@
//! Convert values to [`ArgReg`] and from [`RetReg`].
//!
//! System call arguments and return values are all communicated with inline
//! asm and FFI as `*mut Opaque`. To protect these raw pointers from escaping
//! or being accidentally misused as they travel through the code, we wrap
//! them in [`ArgReg`] and [`RetReg`] structs. This file provides `From`
//! implementations and explicit conversion functions for converting values
//! into and out of these wrapper structs.
//!
//! # Safety
//!
//! Some of this code is `unsafe` in order to work with raw file descriptors,
//! and some is `unsafe` to interpret the values in a `RetReg`.
#![allow(unsafe_code)]
use super::c;
use super::fd::{AsRawFd, BorrowedFd, FromRawFd, RawFd};
#[cfg(not(debug_assertions))]
use super::io::errno::decode_usize_infallible;
#[cfg(feature = "runtime")]
use super::io::errno::try_decode_error;
#[cfg(target_pointer_width = "64")]
use super::io::errno::try_decode_u64;
use super::io::errno::{
try_decode_c_int, try_decode_c_uint, try_decode_raw_fd, try_decode_usize, try_decode_void,
try_decode_void_star,
};
use super::reg::{raw_arg, ArgNumber, ArgReg, RetReg, R0};
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
use super::time::types::ClockId;
#[cfg(feature = "time")]
use super::time::types::TimerfdClockId;
use crate::fd::OwnedFd;
use crate::ffi::CStr;
#[cfg(feature = "fs")]
use crate::fs::{FileType, Mode, OFlags};
use crate::io;
use crate::process::{Pid, Resource, Signal};
use crate::utils::{as_mut_ptr, as_ptr};
use core::mem::MaybeUninit;
use core::ptr::null_mut;
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
use linux_raw_sys::general::__kernel_clockid_t;
#[cfg(target_pointer_width = "64")]
use linux_raw_sys::general::__kernel_loff_t;
#[cfg(feature = "net")]
use linux_raw_sys::general::socklen_t;
#[cfg(target_pointer_width = "32")]
#[cfg(feature = "fs")]
use linux_raw_sys::general::O_LARGEFILE;
/// Convert `SYS_*` constants for socketcall.
#[cfg(target_arch = "x86")]
#[inline]
pub(super) fn x86_sys<'a, Num: ArgNumber>(sys: u32) -> ArgReg<'a, Num> {
pass_usize(sys as usize)
}
/// Pass the "low" half of the endian-specific memory encoding of a `u64`, for
/// 32-bit architectures.
#[cfg(target_pointer_width = "32")]
#[inline]
pub(super) fn lo<'a, Num: ArgNumber>(x: u64) -> ArgReg<'a, Num> {
#[cfg(target_endian = "little")]
let x = x >> 32;
#[cfg(target_endian = "big")]
let x = x & 0xffff_ffff;
pass_usize(x as usize)
}
/// Pass the "high" half of the endian-specific memory encoding of a `u64`, for
/// 32-bit architectures.
#[cfg(target_pointer_width = "32")]
#[inline]
pub(super) fn hi<'a, Num: ArgNumber>(x: u64) -> ArgReg<'a, Num> {
#[cfg(target_endian = "little")]
let x = x & 0xffff_ffff;
#[cfg(target_endian = "big")]
let x = x >> 32;
pass_usize(x as usize)
}
/// Pass a zero, or null, argument.
#[inline]
pub(super) fn zero<'a, Num: ArgNumber>() -> ArgReg<'a, Num> {
raw_arg(null_mut())
}
/// Pass the `mem::size_of` of a type.
#[inline]
pub(super) fn size_of<'a, T: Sized, Num: ArgNumber>() -> ArgReg<'a, Num> {
pass_usize(core::mem::size_of::<T>())
}
/// Pass an arbitrary `usize` value.
///
/// For passing pointers, use `void_star` or other functions which take a raw
/// pointer instead of casting to `usize`, so that provenance is preserved.
#[inline]
pub(super) fn pass_usize<'a, Num: ArgNumber>(t: usize) -> ArgReg<'a, Num> {
raw_arg(t as *mut _)
}
impl<'a, Num: ArgNumber, T> From<*mut T> for ArgReg<'a, Num> {
#[inline]
fn from(c: *mut T) -> ArgReg<'a, Num> {
raw_arg(c.cast())
}
}
impl<'a, Num: ArgNumber, T> From<*const T> for ArgReg<'a, Num> {
#[inline]
fn from(c: *const T) -> ArgReg<'a, Num> {
let mut_ptr = c as *mut T;
raw_arg(mut_ptr.cast())
}
}
impl<'a, Num: ArgNumber> From<&'a CStr> for ArgReg<'a, Num> {
#[inline]
fn from(c: &'a CStr) -> Self {
let mut_ptr = c.as_ptr() as *mut u8;
raw_arg(mut_ptr.cast())
}
}
impl<'a, Num: ArgNumber> From<Option<&'a CStr>> for ArgReg<'a, Num> {
#[inline]
fn from(t: Option<&'a CStr>) -> Self {
raw_arg(match t {
Some(s) => {
let mut_ptr = s.as_ptr() as *mut u8;
mut_ptr.cast()
}
None => null_mut(),
})
}
}
/// Pass a borrowed file-descriptor argument.
impl<'a, Num: ArgNumber> From<BorrowedFd<'a>> for ArgReg<'a, Num> {
#[inline]
fn from(fd: BorrowedFd<'a>) -> Self {
// Safety: `BorrowedFd` ensures that the file descriptor is valid, and the
// lifetime parameter on the resulting `ArgReg` ensures that the result is
// bounded by the `BorrowedFd`'s lifetime.
unsafe { raw_fd(fd.as_raw_fd()) }
}
}
/// Pass a raw file-descriptor argument. Most users should use [`ArgReg::from`]
/// instead, to preserve I/O safety as long as possible.
///
/// # Safety
///
/// `fd` must be a valid open file descriptor.
#[inline]
pub(super) unsafe fn raw_fd<'a, Num: ArgNumber>(fd: RawFd) -> ArgReg<'a, Num> {
// Use `no_fd` when passing `-1` is intended.
#[cfg(feature = "fs")]
debug_assert!(fd == crate::fs::cwd().as_raw_fd() || fd >= 0);
// Don't pass the `io_uring_register_files_skip` sentry value this way.
#[cfg(feature = "io_uring")]
debug_assert_ne!(
fd,
crate::io_uring::io_uring_register_files_skip().as_raw_fd()
);
// Linux doesn't look at the high bits beyond the `c_int`, so use
// zero-extension rather than sign-extension because it's a smaller
// instruction.
let fd: c::c_int = fd;
pass_usize(fd as c::c_uint as usize)
}
/// Deliberately pass `-1` to a file-descriptor argument, for system calls
/// like `mmap` where this indicates the argument is omitted.
#[inline]
pub(super) fn no_fd<'a, Num: ArgNumber>() -> ArgReg<'a, Num> {
pass_usize(!0_usize)
}
#[inline]
pub(super) fn slice_just_addr<T: Sized, Num: ArgNumber>(v: &[T]) -> ArgReg<Num> {
let mut_ptr = v.as_ptr() as *mut T;
raw_arg(mut_ptr.cast())
}
#[inline]
pub(super) fn slice<T: Sized, Num0: ArgNumber, Num1: ArgNumber>(
v: &[T],
) -> (ArgReg<Num0>, ArgReg<Num1>) {
(slice_just_addr(v), pass_usize(v.len()))
}
#[inline]
pub(super) fn slice_mut<T: Sized, Num0: ArgNumber, Num1: ArgNumber>(
v: &mut [T],
) -> (ArgReg<Num0>, ArgReg<Num1>) {
(raw_arg(v.as_mut_ptr().cast()), pass_usize(v.len()))
}
#[inline]
pub(super) fn by_ref<T: Sized, Num: ArgNumber>(t: &T) -> ArgReg<Num> {
let mut_ptr = as_ptr(t) as *mut T;
raw_arg(mut_ptr.cast())
}
#[inline]
pub(super) fn by_mut<T: Sized, Num: ArgNumber>(t: &mut T) -> ArgReg<Num> {
raw_arg(as_mut_ptr(t).cast())
}
/// Convert an optional mutable reference into a `usize` for passing to a
/// syscall.
#[inline]
pub(super) fn opt_mut<T: Sized, Num: ArgNumber>(t: Option<&mut T>) -> ArgReg<Num> {
// This optimizes into the equivalent of `transmute(t)`, and has the
// advantage of not requiring `unsafe`.
match t {
Some(t) => by_mut(t),
None => raw_arg(null_mut()),
}
}
/// Convert an optional immutable reference into a `usize` for passing to a
/// syscall.
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
#[inline]
pub(super) fn opt_ref<T: Sized, Num: ArgNumber>(t: Option<&T>) -> ArgReg<Num> {
// This optimizes into the equivalent of `transmute(t)`, and has the
// advantage of not requiring `unsafe`.
match t {
Some(t) => by_ref(t),
None => raw_arg(null_mut()),
}
}
/// Convert a `c_int` into an `ArgReg`.
///
/// Be sure to use `raw_fd` to pass `RawFd` values.
#[inline]
pub(super) fn c_int<'a, Num: ArgNumber>(i: c::c_int) -> ArgReg<'a, Num> {
pass_usize(i as usize)
}
/// Convert a `c_uint` into an `ArgReg`.
#[inline]
pub(super) fn c_uint<'a, Num: ArgNumber>(i: c::c_uint) -> ArgReg<'a, Num> {
pass_usize(i as usize)
}
#[cfg(target_pointer_width = "64")]
#[inline]
pub(super) fn loff_t<'a, Num: ArgNumber>(i: __kernel_loff_t) -> ArgReg<'a, Num> {
pass_usize(i as usize)
}
#[cfg(target_pointer_width = "64")]
#[inline]
pub(super) fn loff_t_from_u64<'a, Num: ArgNumber>(i: u64) -> ArgReg<'a, Num> {
// `loff_t` is signed, but syscalls which expect `loff_t` return `EINVAL`
// if it's outside the signed `i64` range, so we can silently cast.
pass_usize(i as usize)
}
#[cfg(any(feature = "thread", feature = "time", target_arch = "x86"))]
impl<'a, Num: ArgNumber> From<ClockId> for ArgReg<'a, Num> {
#[inline]
fn from(i: ClockId) -> Self {
pass_usize(i as __kernel_clockid_t as usize)
}
}
#[cfg(feature = "time")]
impl<'a, Num: ArgNumber> From<TimerfdClockId> for ArgReg<'a, Num> {
#[inline]
fn from(i: TimerfdClockId) -> Self {
pass_usize(i as __kernel_clockid_t as usize)
}
}
#[cfg(feature = "net")]
#[inline]
pub(super) fn socklen_t<'a, Num: ArgNumber>(i: socklen_t) -> ArgReg<'a, Num> {
pass_usize(i as usize)
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<Mode> for ArgReg<'a, Num> {
#[inline]
fn from(mode: Mode) -> Self {
pass_usize(mode.bits() as usize)
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<(Mode, FileType)> for ArgReg<'a, Num> {
#[inline]
fn from(pair: (Mode, FileType)) -> Self {
pass_usize(pair.0.as_raw_mode() as usize | pair.1.as_raw_mode() as usize)
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::AtFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::fs::AtFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::MemfdFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::fs::MemfdFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::RenameFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::fs::RenameFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::StatxFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::fs::StatxFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::io::FdFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io::FdFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::io::PipeFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io::PipeFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::io::DupFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io::DupFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::io::ReadWriteFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io::ReadWriteFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::io::EventfdFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io::EventfdFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::io::epoll::CreateFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io::epoll::CreateFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::ProtFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::ProtFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MsyncFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::MsyncFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MremapFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::MremapFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MlockFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::MlockFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MapFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::MapFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::MprotectFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::MprotectFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "mm")]
impl<'a, Num: ArgNumber> From<crate::backend::mm::types::UserfaultfdFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::backend::mm::types::UserfaultfdFlags) -> Self {
c_uint(flags.bits())
}
}
impl<'a, Num: ArgNumber> From<crate::backend::process::types::MembarrierCommand>
for ArgReg<'a, Num>
{
#[inline]
fn from(cmd: crate::backend::process::types::MembarrierCommand) -> Self {
c_uint(cmd as u32)
}
}
impl<'a, Num: ArgNumber> From<crate::process::Cpuid> for ArgReg<'a, Num> {
#[inline]
fn from(cpuid: crate::process::Cpuid) -> Self {
c_uint(cpuid.as_raw())
}
}
#[cfg(target_pointer_width = "64")]
#[inline]
pub(super) fn dev_t<'a, Num: ArgNumber>(dev: u64) -> ArgReg<'a, Num> {
pass_usize(dev as usize)
}
#[cfg(target_pointer_width = "32")]
#[inline]
pub(super) fn dev_t<'a, Num: ArgNumber>(dev: u64) -> io::Result<ArgReg<'a, Num>> {
use core::convert::TryInto;
Ok(pass_usize(dev.try_into().map_err(|_err| io::Errno::INVAL)?))
}
#[cfg(target_pointer_width = "32")]
#[cfg(feature = "fs")]
#[inline]
fn oflags_bits(oflags: OFlags) -> c::c_uint {
let mut bits = oflags.bits();
// Add `O_LARGEFILE`, unless `O_PATH` is set, as Linux returns `EINVAL`
// when both are set.
if !oflags.contains(OFlags::PATH) {
bits |= O_LARGEFILE;
}
bits
}
#[cfg(target_pointer_width = "64")]
#[cfg(feature = "fs")]
#[inline]
const fn oflags_bits(oflags: OFlags) -> c::c_uint {
oflags.bits()
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<OFlags> for ArgReg<'a, Num> {
#[inline]
fn from(oflags: OFlags) -> Self {
pass_usize(oflags_bits(oflags) as usize)
}
}
/// Convert an `OFlags` into a `u64` for use in the `open_how` struct.
#[cfg(feature = "fs")]
#[inline]
pub(super) fn oflags_for_open_how(oflags: OFlags) -> u64 {
u64::from(oflags_bits(oflags))
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::FallocateFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::fs::FallocateFlags) -> Self {
c_uint(flags.bits())
}
}
/// Convert a `Resource` into a syscall argument.
impl<'a, Num: ArgNumber> From<Resource> for ArgReg<'a, Num> {
#[inline]
fn from(resource: Resource) -> Self {
c_uint(resource as c::c_uint)
}
}
impl<'a, Num: ArgNumber> From<Pid> for ArgReg<'a, Num> {
#[inline]
fn from(pid: Pid) -> Self {
pass_usize(pid.as_raw_nonzero().get() as usize)
}
}
#[inline]
pub(super) fn negative_pid<'a, Num: ArgNumber>(pid: Pid) -> ArgReg<'a, Num> {
pass_usize(pid.as_raw_nonzero().get().wrapping_neg() as usize)
}
impl<'a, Num: ArgNumber> From<Signal> for ArgReg<'a, Num> {
#[inline]
fn from(sig: Signal) -> Self {
pass_usize(sig as usize)
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::Advice> for ArgReg<'a, Num> {
#[inline]
fn from(advice: crate::fs::Advice) -> Self {
c_uint(advice as c::c_uint)
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::SealFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::fs::SealFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "io_uring")]
impl<'a, Num: ArgNumber> From<crate::io_uring::IoringEnterFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::io_uring::IoringEnterFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "time")]
impl<'a, Num: ArgNumber> From<crate::time::TimerfdFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::time::TimerfdFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "time")]
impl<'a, Num: ArgNumber> From<crate::time::TimerfdTimerFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::time::TimerfdTimerFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "rand")]
impl<'a, Num: ArgNumber> From<crate::rand::GetRandomFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::rand::GetRandomFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<crate::net::RecvFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::net::RecvFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<crate::net::SendFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::net::SendFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<crate::net::AcceptFlags> for ArgReg<'a, Num> {
#[inline]
fn from(flags: crate::net::AcceptFlags) -> Self {
c_uint(flags.bits())
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<crate::net::AddressFamily> for ArgReg<'a, Num> {
#[inline]
fn from(family: crate::net::AddressFamily) -> Self {
c_uint(family.0.into())
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)>
for ArgReg<'a, Num>
{
#[inline]
fn from(pair: (crate::net::SocketType, crate::net::SocketFlags)) -> Self {
c_uint(pair.0 .0 | pair.1.bits())
}
}
#[cfg(feature = "thread")]
impl<'a, Num: ArgNumber> From<(crate::thread::FutexOperation, crate::thread::FutexFlags)>
for ArgReg<'a, Num>
{
#[inline]
fn from(pair: (crate::thread::FutexOperation, crate::thread::FutexFlags)) -> Self {
c_uint(pair.0 as u32 | pair.1.bits())
}
}
#[cfg(feature = "fs")]
impl<'a, Num: ArgNumber> From<crate::fs::Access> for ArgReg<'a, Num> {
#[inline]
fn from(access: crate::fs::Access) -> Self {
c_uint(access.bits())
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<crate::net::SocketType> for ArgReg<'a, Num> {
#[inline]
fn from(type_: crate::net::SocketType) -> Self {
c_uint(type_.0)
}
}
#[cfg(feature = "net")]
impl<'a, Num: ArgNumber> From<crate::net::Protocol> for ArgReg<'a, Num> {
#[inline]
fn from(protocol: crate::net::Protocol) -> Self {
c_uint(protocol.0)
}
}
impl<'a, Num: ArgNumber, T> From<&'a mut MaybeUninit<T>> for ArgReg<'a, Num> {
#[inline]
fn from(t: &'a mut MaybeUninit<T>) -> Self {
raw_arg(t.as_mut_ptr().cast())
}
}
/// Convert a `usize` returned from a syscall that effectively returns `()` on
/// success.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a syscall which
/// just returns 0 on success.
#[inline]
pub(super) unsafe fn ret(raw: RetReg<R0>) -> io::Result<()> {
try_decode_void(raw)
}
/// Convert a `usize` returned from a syscall that doesn't return on success.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a syscall which
/// doesn't return on success.
#[cfg(feature = "runtime")]
#[inline]
pub(super) unsafe fn ret_error(raw: RetReg<R0>) -> io::Errno {
try_decode_error(raw)
}
/// Convert a `usize` returned from a syscall that effectively always returns
/// `()`.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a syscall which
/// always returns `()`.
#[inline]
pub(super) unsafe fn ret_infallible(raw: RetReg<R0>) {
#[cfg(debug_assertions)]
{
try_decode_void(raw).unwrap()
}
#[cfg(not(debug_assertions))]
drop(raw);
}
/// Convert a `usize` returned from a syscall that effectively returns a
/// `c_int` on success.
#[inline]
pub(super) fn ret_c_int(raw: RetReg<R0>) -> io::Result<c::c_int> {
try_decode_c_int(raw)
}
/// Convert a `usize` returned from a syscall that effectively returns a
/// `c_uint` on success.
#[inline]
pub(super) fn ret_c_uint(raw: RetReg<R0>) -> io::Result<c::c_uint> {
try_decode_c_uint(raw)
}
/// Convert a `usize` returned from a syscall that effectively returns a `u64`
/// on success.
#[cfg(target_pointer_width = "64")]
#[inline]
pub(super) fn ret_u64(raw: RetReg<R0>) -> io::Result<u64> {
try_decode_u64(raw)
}
/// Convert a `usize` returned from a syscall that effectively returns a
/// `usize` on success.
#[inline]
pub(super) fn ret_usize(raw: RetReg<R0>) -> io::Result<usize> {
try_decode_usize(raw)
}
/// Convert a `usize` returned from a syscall that effectively always
/// returns a `usize`.
///
/// # Safety
///
/// This function must only be used with return values from infallible
/// syscalls.
#[inline]
pub(super) unsafe fn ret_usize_infallible(raw: RetReg<R0>) -> usize {
#[cfg(debug_assertions)]
{
try_decode_usize(raw).unwrap()
}
#[cfg(not(debug_assertions))]
{
decode_usize_infallible(raw)
}
}
/// Convert a `usize` returned from a syscall that effectively returns an
/// `OwnedFd` on success.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a syscall which
/// returns an owned file descriptor.
#[inline]
pub(super) unsafe fn ret_owned_fd(raw: RetReg<R0>) -> io::Result<OwnedFd> {
let raw_fd = try_decode_raw_fd(raw)?;
Ok(crate::backend::fd::OwnedFd::from_raw_fd(raw_fd))
}
/// Convert the return value of `dup2` and `dup3`.
///
/// When these functions succeed, they return the same value as their second
/// argument, so we don't construct a new `OwnedFd`.
///
/// # Safety
///
/// The caller must ensure that this is the return value of a syscall which
/// returns a file descriptor.
#[inline]
pub(super) unsafe fn ret_discarded_fd(raw: RetReg<R0>) -> io::Result<()> {
let _raw_fd = try_decode_raw_fd(raw)?;
Ok(())
}
/// Convert a `usize` returned from a syscall that effectively returns a
/// `*mut c_void` on success.
#[inline]
pub(super) fn ret_void_star(raw: RetReg<R0>) -> io::Result<*mut c::c_void> {
try_decode_void_star(raw)
}

View file

@ -0,0 +1,176 @@
//! The ELF ABI.
#![allow(non_snake_case)]
#![cfg_attr(
all(not(target_vendor = "mustang"), feature = "use-libc-auxv"),
allow(dead_code)
)]
pub(super) const SELFMAG: usize = 4;
pub(super) const ELFMAG: [u8; SELFMAG] = [0x7f, b'E', b'L', b'F'];
pub(super) const EI_CLASS: usize = 4;
pub(super) const EI_DATA: usize = 5;
pub(super) const EI_VERSION: usize = 6;
pub(super) const EI_OSABI: usize = 7;
pub(super) const EI_ABIVERSION: usize = 8;
pub(super) const EV_CURRENT: u8 = 1;
#[cfg(target_pointer_width = "32")]
pub(super) const ELFCLASS: u8 = 1; // ELFCLASS32
#[cfg(target_pointer_width = "64")]
pub(super) const ELFCLASS: u8 = 2; // ELFCLASS64
#[cfg(target_endian = "little")]
pub(super) const ELFDATA: u8 = 1; // ELFDATA2LSB
#[cfg(target_endian = "big")]
pub(super) const ELFDATA: u8 = 2; // ELFDATA2MSB
pub(super) const ELFOSABI_SYSV: u8 = 0;
pub(super) const ELFOSABI_LINUX: u8 = 3;
// At present all of our supported platforms use 0.
pub(super) const ELFABIVERSION: u8 = 0;
pub(super) const ET_DYN: u16 = 3;
pub(super) const EI_NIDENT: usize = 16;
pub(super) const SHN_UNDEF: u16 = 0;
pub(super) const SHN_ABS: u16 = 0xfff1;
pub(super) const PN_XNUM: u16 = 0xffff;
pub(super) const PT_LOAD: u32 = 1;
pub(super) const PT_DYNAMIC: u32 = 2;
pub(super) const PT_INTERP: u32 = 3;
pub(super) const PT_PHDR: u32 = 6;
pub(super) const PT_TLS: u32 = 7;
pub(super) const PT_GNU_STACK: u32 = 0x6474_e551;
pub(super) const PT_GNU_RELRO: u32 = 0x6474_e552;
pub(super) const PF_X: u32 = 1;
pub(super) const PF_W: u32 = 2;
pub(super) const PF_R: u32 = 4;
pub(super) const DT_NULL: i32 = 0;
pub(super) const DT_HASH: i32 = 4;
pub(super) const DT_STRTAB: i32 = 5;
pub(super) const DT_SYMTAB: i32 = 6;
pub(super) const DT_SYMENT: i32 = 11;
pub(super) const DT_VERSYM: i32 = 0x6fff_fff0;
pub(super) const DT_VERDEF: i32 = 0x6fff_fffc;
pub(super) const STB_WEAK: u8 = 2;
pub(super) const STB_GLOBAL: u8 = 1;
pub(super) const STT_NOTYPE: u8 = 0;
pub(super) const STT_FUNC: u8 = 2;
pub(super) const STN_UNDEF: u32 = 0;
pub(super) const VER_FLG_BASE: u16 = 0x1;
pub(super) const VER_DEF_CURRENT: u16 = 1;
pub(super) const STV_DEFAULT: u8 = 0;
#[cfg(target_arch = "arm")]
pub(super) const EM_CURRENT: u16 = 40; // EM_ARM
#[cfg(target_arch = "x86")]
pub(super) const EM_CURRENT: u16 = 3; // EM_386
#[cfg(target_arch = "powerpc64")]
pub(super) const EM_CURRENT: u16 = 21; // EM_PPC64
#[cfg(any(target_arch = "mips", target_arch = "mips64"))]
pub(super) const EM_CURRENT: u16 = 8; // EM_MIPS
#[cfg(target_arch = "x86_64")]
pub(super) const EM_CURRENT: u16 = 62; // EM_X86_64
#[cfg(target_arch = "aarch64")]
pub(super) const EM_CURRENT: u16 = 183; // EM_AARCH64
#[cfg(target_arch = "riscv64")]
pub(super) const EM_CURRENT: u16 = 243; // EM_RISCV
#[inline]
pub(super) const fn ELF_ST_VISIBILITY(o: u8) -> u8 {
o & 0x03
}
#[inline]
pub(super) const fn ELF_ST_BIND(val: u8) -> u8 {
val >> 4
}
#[inline]
pub(super) const fn ELF_ST_TYPE(val: u8) -> u8 {
val & 0xf
}
#[repr(C)]
pub(super) struct Elf_Ehdr {
pub(super) e_ident: [u8; EI_NIDENT],
pub(super) e_type: u16,
pub(super) e_machine: u16,
pub(super) e_version: u32,
pub(super) e_entry: usize,
pub(super) e_phoff: usize,
pub(super) e_shoff: usize,
pub(super) e_flags: u32,
pub(super) e_ehsize: u16,
pub(super) e_phentsize: u16,
pub(super) e_phnum: u16,
pub(super) e_shentsize: u16,
pub(super) e_shnum: u16,
pub(super) e_shstrndx: u16,
}
#[cfg(target_pointer_width = "32")]
#[repr(C)]
pub(super) struct Elf_Phdr {
pub(super) p_type: u32,
pub(super) p_offset: usize,
pub(super) p_vaddr: usize,
pub(super) p_paddr: usize,
pub(super) p_filesz: usize,
pub(super) p_memsz: usize,
pub(super) p_flags: u32,
pub(super) p_align: usize,
}
#[cfg(target_pointer_width = "64")]
#[repr(C)]
pub(super) struct Elf_Phdr {
pub(super) p_type: u32,
pub(super) p_flags: u32,
pub(super) p_offset: usize,
pub(super) p_vaddr: usize,
pub(super) p_paddr: usize,
pub(super) p_filesz: usize,
pub(super) p_memsz: usize,
pub(super) p_align: usize,
}
#[cfg(target_pointer_width = "32")]
#[repr(C)]
pub(super) struct Elf_Sym {
pub(super) st_name: u32,
pub(super) st_value: usize,
pub(super) st_size: usize,
pub(super) st_info: u8,
pub(super) st_other: u8,
pub(super) st_shndx: u16,
}
#[cfg(target_pointer_width = "64")]
#[repr(C)]
pub(super) struct Elf_Sym {
pub(super) st_name: u32,
pub(super) st_info: u8,
pub(super) st_other: u8,
pub(super) st_shndx: u16,
pub(super) st_value: usize,
pub(super) st_size: usize,
}
#[repr(C)]
pub(super) struct Elf_Dyn {
pub(super) d_tag: i32,
pub(super) d_val: usize,
}
#[repr(C)]
pub(super) struct Elf_Verdef {
pub(super) vd_version: u16,
pub(super) vd_flags: u16,
pub(super) vd_ndx: u16,
pub(super) vd_cnt: u16,
pub(super) vd_hash: u32,
pub(super) vd_aux: u32,
pub(super) vd_next: u32,
}
#[repr(C)]
pub(super) struct Elf_Verdaux {
pub(super) vda_name: u32,
pub(super) _vda_next: u32,
}

View file

@ -0,0 +1,225 @@
use crate::fd::{AsFd, BorrowedFd, OwnedFd};
use crate::ffi::{CStr, CString};
use crate::fs::{
fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs,
};
use crate::io;
use crate::process::fchdir;
use crate::utils::as_ptr;
use alloc::borrow::ToOwned;
use alloc::vec::Vec;
use core::fmt;
use core::mem::size_of;
use linux_raw_sys::general::{linux_dirent64, SEEK_SET};
/// `DIR*`
pub struct Dir {
/// The `OwnedFd` that we read directory entries from.
fd: OwnedFd,
buf: Vec<u8>,
pos: usize,
next: Option<u64>,
}
impl Dir {
/// Construct a `Dir` that reads entries from the given directory
/// file descriptor.
#[inline]
pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
Self::_read_from(fd.as_fd())
}
#[inline]
fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
let flags = fcntl_getfl(fd)?;
let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?;
Ok(Self {
fd: fd_for_dir,
buf: Vec::new(),
pos: 0,
next: None,
})
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
self.pos = self.buf.len();
self.next = Some(0);
}
/// `readdir(self)`, where `None` means the end of the directory.
pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
if let Some(next) = self.next.take() {
match crate::backend::fs::syscalls::_seek(self.fd.as_fd(), next as i64, SEEK_SET) {
Ok(_) => (),
Err(err) => return Some(Err(err)),
}
}
// Compute linux_dirent64 field offsets.
let z = linux_dirent64 {
d_ino: 0_u64,
d_off: 0_i64,
d_type: 0_u8,
d_reclen: 0_u16,
d_name: Default::default(),
};
let base = as_ptr(&z) as usize;
let offsetof_d_reclen = (as_ptr(&z.d_reclen) as usize) - base;
let offsetof_d_name = (as_ptr(&z.d_name) as usize) - base;
let offsetof_d_ino = (as_ptr(&z.d_ino) as usize) - base;
let offsetof_d_type = (as_ptr(&z.d_type) as usize) - base;
// Test if we need more entries, and if so, read more.
if self.buf.len() - self.pos < size_of::<linux_dirent64>() {
match self.read_more()? {
Ok(()) => (),
Err(e) => return Some(Err(e)),
}
}
// We successfully read an entry. Extract the fields.
let pos = self.pos;
// Do an unaligned u16 load.
let d_reclen = u16::from_ne_bytes([
self.buf[pos + offsetof_d_reclen],
self.buf[pos + offsetof_d_reclen + 1],
]);
assert!(self.buf.len() - pos >= d_reclen as usize);
self.pos += d_reclen as usize;
// Read the NUL-terminated name from the `d_name` field. Without
// `unsafe`, we need to scan for the NUL twice: once to obtain a size
// for the slice, and then once within `CStr::from_bytes_with_nul`.
let name_start = pos + offsetof_d_name;
let name_len = self.buf[name_start..]
.iter()
.position(|x| *x == b'\0')
.unwrap();
let name =
CStr::from_bytes_with_nul(&self.buf[name_start..name_start + name_len + 1]).unwrap();
let name = name.to_owned();
assert!(name.as_bytes().len() <= self.buf.len() - name_start);
// Do an unaligned u64 load.
let d_ino = u64::from_ne_bytes([
self.buf[pos + offsetof_d_ino],
self.buf[pos + offsetof_d_ino + 1],
self.buf[pos + offsetof_d_ino + 2],
self.buf[pos + offsetof_d_ino + 3],
self.buf[pos + offsetof_d_ino + 4],
self.buf[pos + offsetof_d_ino + 5],
self.buf[pos + offsetof_d_ino + 6],
self.buf[pos + offsetof_d_ino + 7],
]);
let d_type = self.buf[pos + offsetof_d_type];
// Check that our types correspond to the `linux_dirent64` types.
let _ = linux_dirent64 {
d_ino,
d_off: 0,
d_type,
d_reclen,
d_name: Default::default(),
};
Some(Ok(DirEntry {
d_ino,
d_type,
name,
}))
}
fn read_more(&mut self) -> Option<io::Result<()>> {
let og_len = self.buf.len();
// Capacity increment currently chosen by wild guess.
self.buf
.resize(self.buf.capacity() + 32 * size_of::<linux_dirent64>(), 0);
let nread = match crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf) {
Ok(nread) => nread,
Err(err) => {
self.buf.resize(og_len, 0);
return Some(Err(err));
}
};
self.buf.resize(nread, 0);
self.pos = 0;
if nread == 0 {
None
} else {
Some(Ok(()))
}
}
/// `fstat(self)`
#[inline]
pub fn stat(&self) -> io::Result<Stat> {
fstat(&self.fd)
}
/// `fstatfs(self)`
#[inline]
pub fn statfs(&self) -> io::Result<StatFs> {
fstatfs(&self.fd)
}
/// `fstatvfs(self)`
#[inline]
pub fn statvfs(&self) -> io::Result<StatVfs> {
fstatvfs(&self.fd)
}
/// `fchdir(self)`
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(&self.fd)
}
}
impl Iterator for Dir {
type Item = io::Result<DirEntry>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Self::read(self)
}
}
impl fmt::Debug for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dir").field("fd", &self.fd).finish()
}
}
/// `struct dirent`
#[derive(Debug)]
pub struct DirEntry {
d_ino: u64,
d_type: u8,
name: CString,
}
impl DirEntry {
/// Returns the file name of this directory entry.
#[inline]
pub fn file_name(&self) -> &CStr {
&self.name
}
/// Returns the type of this directory entry.
#[inline]
pub fn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.d_type)
}
/// Return the inode number of this directory entry.
#[inline]
pub fn ino(&self) -> u64 {
self.d_ino
}
}

View file

@ -0,0 +1,19 @@
use crate::fs::Dev;
#[inline]
pub(crate) fn makedev(maj: u32, min: u32) -> Dev {
((u64::from(maj) & 0xffff_f000_u64) << 32)
| ((u64::from(maj) & 0x0000_0fff_u64) << 8)
| ((u64::from(min) & 0xffff_ff00_u64) << 12)
| (u64::from(min) & 0x0000_00ff_u64)
}
#[inline]
pub(crate) fn major(dev: Dev) -> u32 {
(((dev >> 31 >> 1) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as u32
}
#[inline]
pub(crate) fn minor(dev: Dev) -> u32 {
(((dev >> 12) & 0xffff_ff00) | (dev & 0x0000_00ff)) as u32
}

View file

@ -0,0 +1,4 @@
pub(crate) mod dir;
pub(crate) mod makedev;
pub(crate) mod syscalls;
pub(crate) mod types;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,646 @@
use super::super::c;
use bitflags::bitflags;
bitflags! {
/// `*_OK` constants for use with [`accessat`].
///
/// [`accessat`]: fn.accessat.html
pub struct Access: c::c_uint {
/// `R_OK`
const READ_OK = linux_raw_sys::general::R_OK;
/// `W_OK`
const WRITE_OK = linux_raw_sys::general::W_OK;
/// `X_OK`
const EXEC_OK = linux_raw_sys::general::X_OK;
/// `F_OK`
const EXISTS = linux_raw_sys::general::F_OK;
}
}
bitflags! {
/// `AT_*` constants for use with [`openat`], [`statat`], and other `*at`
/// functions.
///
/// [`openat`]: crate::fs::openat
/// [`statat`]: crate::fs::statat
pub struct AtFlags: c::c_uint {
/// `AT_REMOVEDIR`
const REMOVEDIR = linux_raw_sys::general::AT_REMOVEDIR;
/// `AT_SYMLINK_FOLLOW`
const SYMLINK_FOLLOW = linux_raw_sys::general::AT_SYMLINK_FOLLOW;
/// `AT_SYMLINK_NOFOLLOW`
const SYMLINK_NOFOLLOW = linux_raw_sys::general::AT_SYMLINK_NOFOLLOW;
/// `AT_EMPTY_PATH`
const EMPTY_PATH = linux_raw_sys::general::AT_EMPTY_PATH;
/// `AT_EACCESS`
const EACCESS = linux_raw_sys::general::AT_EACCESS;
/// `AT_STATX_SYNC_AS_STAT`
const STATX_SYNC_AS_STAT = linux_raw_sys::general::AT_STATX_SYNC_AS_STAT;
/// `AT_STATX_FORCE_SYNC`
const STATX_FORCE_SYNC = linux_raw_sys::general::AT_STATX_FORCE_SYNC;
/// `AT_STATX_DONT_SYNC`
const STATX_DONT_SYNC = linux_raw_sys::general::AT_STATX_DONT_SYNC;
}
}
bitflags! {
/// `S_I*` constants for use with [`openat`], [`chmodat`], and [`fchmod`].
///
/// [`openat`]: crate::fs::openat
/// [`chmodat`]: crate::fs::chmodat
/// [`fchmod`]: crate::fs::fchmod
pub struct Mode: RawMode {
/// `S_IRWXU`
const RWXU = linux_raw_sys::general::S_IRWXU;
/// `S_IRUSR`
const RUSR = linux_raw_sys::general::S_IRUSR;
/// `S_IWUSR`
const WUSR = linux_raw_sys::general::S_IWUSR;
/// `S_IXUSR`
const XUSR = linux_raw_sys::general::S_IXUSR;
/// `S_IRWXG`
const RWXG = linux_raw_sys::general::S_IRWXG;
/// `S_IRGRP`
const RGRP = linux_raw_sys::general::S_IRGRP;
/// `S_IWGRP`
const WGRP = linux_raw_sys::general::S_IWGRP;
/// `S_IXGRP`
const XGRP = linux_raw_sys::general::S_IXGRP;
/// `S_IRWXO`
const RWXO = linux_raw_sys::general::S_IRWXO;
/// `S_IROTH`
const ROTH = linux_raw_sys::general::S_IROTH;
/// `S_IWOTH`
const WOTH = linux_raw_sys::general::S_IWOTH;
/// `S_IXOTH`
const XOTH = linux_raw_sys::general::S_IXOTH;
/// `S_ISUID`
const SUID = linux_raw_sys::general::S_ISUID;
/// `S_ISGID`
const SGID = linux_raw_sys::general::S_ISGID;
/// `S_ISVTX`
const SVTX = linux_raw_sys::general::S_ISVTX;
}
}
impl Mode {
/// Construct a `Mode` from the mode bits of the `st_mode` field of a
/// `Stat`.
#[inline]
pub const fn from_raw_mode(st_mode: RawMode) -> Self {
Self::from_bits_truncate(st_mode)
}
/// Construct an `st_mode` value from `Stat`.
#[inline]
pub const fn as_raw_mode(self) -> RawMode {
self.bits()
}
}
bitflags! {
/// `O_*` constants for use with [`openat`].
///
/// [`openat`]: crate::fs::openat
pub struct OFlags: c::c_uint {
/// `O_ACCMODE`
const ACCMODE = linux_raw_sys::general::O_ACCMODE;
/// Similar to `ACCMODE`, but just includes the read/write flags, and
/// no other flags.
///
/// Some implementations include `O_PATH` in `O_ACCMODE`, when
/// sometimes we really just want the read/write bits. Caution is
/// indicated, as the presence of `O_PATH` may mean that the read/write
/// bits don't have their usual meaning.
const RWMODE = linux_raw_sys::general::O_RDONLY |
linux_raw_sys::general::O_WRONLY |
linux_raw_sys::general::O_RDWR;
/// `O_APPEND`
const APPEND = linux_raw_sys::general::O_APPEND;
/// `O_CREAT`
#[doc(alias = "CREAT")]
const CREATE = linux_raw_sys::general::O_CREAT;
/// `O_DIRECTORY`
const DIRECTORY = linux_raw_sys::general::O_DIRECTORY;
/// `O_DSYNC`. Linux 2.6.32 only supports `O_SYNC`.
const DSYNC = linux_raw_sys::general::O_SYNC;
/// `O_EXCL`
const EXCL = linux_raw_sys::general::O_EXCL;
/// `O_FSYNC`. Linux 2.6.32 only supports `O_SYNC`.
const FSYNC = linux_raw_sys::general::O_SYNC;
/// `O_NOFOLLOW`
const NOFOLLOW = linux_raw_sys::general::O_NOFOLLOW;
/// `O_NONBLOCK`
const NONBLOCK = linux_raw_sys::general::O_NONBLOCK;
/// `O_RDONLY`
const RDONLY = linux_raw_sys::general::O_RDONLY;
/// `O_WRONLY`
const WRONLY = linux_raw_sys::general::O_WRONLY;
/// `O_RDWR`
const RDWR = linux_raw_sys::general::O_RDWR;
/// `O_NOCTTY`
const NOCTTY = linux_raw_sys::general::O_NOCTTY;
/// `O_RSYNC`. Linux 2.6.32 only supports `O_SYNC`.
const RSYNC = linux_raw_sys::general::O_SYNC;
/// `O_SYNC`
const SYNC = linux_raw_sys::general::O_SYNC;
/// `O_TRUNC`
const TRUNC = linux_raw_sys::general::O_TRUNC;
/// `O_PATH`
const PATH = linux_raw_sys::general::O_PATH;
/// `O_CLOEXEC`
const CLOEXEC = linux_raw_sys::general::O_CLOEXEC;
/// `O_TMPFILE`
const TMPFILE = linux_raw_sys::general::O_TMPFILE;
/// `O_NOATIME`
const NOATIME = linux_raw_sys::general::O_NOATIME;
}
}
bitflags! {
/// `RESOLVE_*` constants for use with [`openat2`].
///
/// [`openat2`]: crate::fs::openat2
#[derive(Default)]
pub struct ResolveFlags: u64 {
/// `RESOLVE_NO_XDEV`
const NO_XDEV = linux_raw_sys::general::RESOLVE_NO_XDEV as u64;
/// `RESOLVE_NO_MAGICLINKS`
const NO_MAGICLINKS = linux_raw_sys::general::RESOLVE_NO_MAGICLINKS as u64;
/// `RESOLVE_NO_SYMLINKS`
const NO_SYMLINKS = linux_raw_sys::general::RESOLVE_NO_SYMLINKS as u64;
/// `RESOLVE_BENEATH`
const BENEATH = linux_raw_sys::general::RESOLVE_BENEATH as u64;
/// `RESOLVE_IN_ROOT`
const IN_ROOT = linux_raw_sys::general::RESOLVE_IN_ROOT as u64;
/// `RESOLVE_CACHED` (since Linux 5.12)
const CACHED = linux_raw_sys::general::RESOLVE_CACHED as u64;
}
}
bitflags! {
/// `RENAME_*` constants for use with [`renameat_with`].
///
/// [`renameat_with`]: crate::fs::renameat_with
pub struct RenameFlags: c::c_uint {
/// `RENAME_EXCHANGE`
const EXCHANGE = linux_raw_sys::general::RENAME_EXCHANGE;
/// `RENAME_NOREPLACE`
const NOREPLACE = linux_raw_sys::general::RENAME_NOREPLACE;
/// `RENAME_WHITEOUT`
const WHITEOUT = linux_raw_sys::general::RENAME_WHITEOUT;
}
}
/// `S_IF*` constants for use with [`mknodat`] and [`Stat`]'s `st_mode` field.
///
/// [`mknodat`]: crate::fs::mknodat
/// [`Stat`]: crate::fs::Stat
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FileType {
/// `S_IFREG`
RegularFile = linux_raw_sys::general::S_IFREG as isize,
/// `S_IFDIR`
Directory = linux_raw_sys::general::S_IFDIR as isize,
/// `S_IFLNK`
Symlink = linux_raw_sys::general::S_IFLNK as isize,
/// `S_IFIFO`
Fifo = linux_raw_sys::general::S_IFIFO as isize,
/// `S_IFSOCK`
Socket = linux_raw_sys::general::S_IFSOCK as isize,
/// `S_IFCHR`
CharacterDevice = linux_raw_sys::general::S_IFCHR as isize,
/// `S_IFBLK`
BlockDevice = linux_raw_sys::general::S_IFBLK as isize,
/// An unknown filesystem object.
Unknown,
}
impl FileType {
/// Construct a `FileType` from the `S_IFMT` bits of the `st_mode` field of
/// a `Stat`.
#[inline]
pub const fn from_raw_mode(st_mode: RawMode) -> Self {
match st_mode & linux_raw_sys::general::S_IFMT {
linux_raw_sys::general::S_IFREG => Self::RegularFile,
linux_raw_sys::general::S_IFDIR => Self::Directory,
linux_raw_sys::general::S_IFLNK => Self::Symlink,
linux_raw_sys::general::S_IFIFO => Self::Fifo,
linux_raw_sys::general::S_IFSOCK => Self::Socket,
linux_raw_sys::general::S_IFCHR => Self::CharacterDevice,
linux_raw_sys::general::S_IFBLK => Self::BlockDevice,
_ => Self::Unknown,
}
}
/// Construct an `st_mode` value from `Stat`.
#[inline]
pub const fn as_raw_mode(self) -> RawMode {
match self {
Self::RegularFile => linux_raw_sys::general::S_IFREG,
Self::Directory => linux_raw_sys::general::S_IFDIR,
Self::Symlink => linux_raw_sys::general::S_IFLNK,
Self::Fifo => linux_raw_sys::general::S_IFIFO,
Self::Socket => linux_raw_sys::general::S_IFSOCK,
Self::CharacterDevice => linux_raw_sys::general::S_IFCHR,
Self::BlockDevice => linux_raw_sys::general::S_IFBLK,
Self::Unknown => linux_raw_sys::general::S_IFMT,
}
}
/// Construct a `FileType` from the `d_type` field of a `dirent`.
#[inline]
pub(crate) const fn from_dirent_d_type(d_type: u8) -> Self {
match d_type as u32 {
linux_raw_sys::general::DT_REG => Self::RegularFile,
linux_raw_sys::general::DT_DIR => Self::Directory,
linux_raw_sys::general::DT_LNK => Self::Symlink,
linux_raw_sys::general::DT_SOCK => Self::Socket,
linux_raw_sys::general::DT_FIFO => Self::Fifo,
linux_raw_sys::general::DT_CHR => Self::CharacterDevice,
linux_raw_sys::general::DT_BLK => Self::BlockDevice,
// linux_raw_sys::general::DT_UNKNOWN |
_ => Self::Unknown,
}
}
}
/// `POSIX_FADV_*` constants for use with [`fadvise`].
///
/// [`fadvise`]: crate::fs::fadvise
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u32)]
pub enum Advice {
/// `POSIX_FADV_NORMAL`
Normal = linux_raw_sys::general::POSIX_FADV_NORMAL,
/// `POSIX_FADV_SEQUENTIAL`
Sequential = linux_raw_sys::general::POSIX_FADV_SEQUENTIAL,
/// `POSIX_FADV_RANDOM`
Random = linux_raw_sys::general::POSIX_FADV_RANDOM,
/// `POSIX_FADV_NOREUSE`
NoReuse = linux_raw_sys::general::POSIX_FADV_NOREUSE,
/// `POSIX_FADV_WILLNEED`
WillNeed = linux_raw_sys::general::POSIX_FADV_WILLNEED,
/// `POSIX_FADV_DONTNEED`
DontNeed = linux_raw_sys::general::POSIX_FADV_DONTNEED,
}
bitflags! {
/// `MFD_*` constants for use with [`memfd_create`].
///
/// [`memfd_create`]: crate::fs::memfd_create
pub struct MemfdFlags: c::c_uint {
/// `MFD_CLOEXEC`
const CLOEXEC = linux_raw_sys::general::MFD_CLOEXEC;
/// `MFD_ALLOW_SEALING`
const ALLOW_SEALING = linux_raw_sys::general::MFD_ALLOW_SEALING;
/// `MFD_HUGETLB` (since Linux 4.14)
const HUGETLB = linux_raw_sys::general::MFD_HUGETLB;
/// `MFD_HUGE_64KB`
const HUGE_64KB = linux_raw_sys::general::MFD_HUGE_64KB;
/// `MFD_HUGE_512JB`
const HUGE_512KB = linux_raw_sys::general::MFD_HUGE_512KB;
/// `MFD_HUGE_1MB`
const HUGE_1MB = linux_raw_sys::general::MFD_HUGE_1MB;
/// `MFD_HUGE_2MB`
const HUGE_2MB = linux_raw_sys::general::MFD_HUGE_2MB;
/// `MFD_HUGE_8MB`
const HUGE_8MB = linux_raw_sys::general::MFD_HUGE_8MB;
/// `MFD_HUGE_16MB`
const HUGE_16MB = linux_raw_sys::general::MFD_HUGE_16MB;
/// `MFD_HUGE_32MB`
const HUGE_32MB = linux_raw_sys::general::MFD_HUGE_32MB;
/// `MFD_HUGE_256MB`
const HUGE_256MB = linux_raw_sys::general::MFD_HUGE_256MB;
/// `MFD_HUGE_512MB`
const HUGE_512MB = linux_raw_sys::general::MFD_HUGE_512MB;
/// `MFD_HUGE_1GB`
const HUGE_1GB = linux_raw_sys::general::MFD_HUGE_1GB;
/// `MFD_HUGE_2GB`
const HUGE_2GB = linux_raw_sys::general::MFD_HUGE_2GB;
/// `MFD_HUGE_16GB`
const HUGE_16GB = linux_raw_sys::general::MFD_HUGE_16GB;
}
}
bitflags! {
/// `F_SEAL_*` constants for use with [`fcntl_add_seals`] and
/// [`fcntl_get_seals`].
///
/// [`fcntl_add_seals`]: crate::fs::fcntl_add_seals
/// [`fcntl_get_seals`]: crate::fs::fcntl_get_seals
pub struct SealFlags: u32 {
/// `F_SEAL_SEAL`.
const SEAL = linux_raw_sys::general::F_SEAL_SEAL;
/// `F_SEAL_SHRINK`.
const SHRINK = linux_raw_sys::general::F_SEAL_SHRINK;
/// `F_SEAL_GROW`.
const GROW = linux_raw_sys::general::F_SEAL_GROW;
/// `F_SEAL_WRITE`.
const WRITE = linux_raw_sys::general::F_SEAL_WRITE;
/// `F_SEAL_FUTURE_WRITE` (since Linux 5.1)
const FUTURE_WRITE = linux_raw_sys::general::F_SEAL_FUTURE_WRITE;
}
}
bitflags! {
/// `STATX_*` constants for use with [`statx`].
///
/// [`statx`]: crate::fs::statx
pub struct StatxFlags: u32 {
/// `STATX_TYPE`
const TYPE = linux_raw_sys::general::STATX_TYPE;
/// `STATX_MODE`
const MODE = linux_raw_sys::general::STATX_MODE;
/// `STATX_NLINK`
const NLINK = linux_raw_sys::general::STATX_NLINK;
/// `STATX_UID`
const UID = linux_raw_sys::general::STATX_UID;
/// `STATX_GID`
const GID = linux_raw_sys::general::STATX_GID;
/// `STATX_ATIME`
const ATIME = linux_raw_sys::general::STATX_ATIME;
/// `STATX_MTIME`
const MTIME = linux_raw_sys::general::STATX_MTIME;
/// `STATX_CTIME`
const CTIME = linux_raw_sys::general::STATX_CTIME;
/// `STATX_INO`
const INO = linux_raw_sys::general::STATX_INO;
/// `STATX_SIZE`
const SIZE = linux_raw_sys::general::STATX_SIZE;
/// `STATX_BLOCKS`
const BLOCKS = linux_raw_sys::general::STATX_BLOCKS;
/// `STATX_BASIC_STATS`
const BASIC_STATS = linux_raw_sys::general::STATX_BASIC_STATS;
/// `STATX_BTIME`
const BTIME = linux_raw_sys::general::STATX_BTIME;
/// `STATX_MNT_ID` (since Linux 5.8)
const MNT_ID = linux_raw_sys::general::STATX_MNT_ID;
/// `STATX_ALL`
const ALL = linux_raw_sys::general::STATX_ALL;
}
}
bitflags! {
/// `FALLOC_FL_*` constants for use with [`fallocate`].
///
/// [`fallocate`]: crate::fs::fallocate
pub struct FallocateFlags: u32 {
/// `FALLOC_FL_KEEP_SIZE`
const KEEP_SIZE = linux_raw_sys::general::FALLOC_FL_KEEP_SIZE;
/// `FALLOC_FL_PUNCH_HOLE`
const PUNCH_HOLE = linux_raw_sys::general::FALLOC_FL_PUNCH_HOLE;
/// `FALLOC_FL_NO_HIDE_STALE`
const NO_HIDE_STALE = linux_raw_sys::general::FALLOC_FL_NO_HIDE_STALE;
/// `FALLOC_FL_COLLAPSE_RANGE`
const COLLAPSE_RANGE = linux_raw_sys::general::FALLOC_FL_COLLAPSE_RANGE;
/// `FALLOC_FL_ZERO_RANGE`
const ZERO_RANGE = linux_raw_sys::general::FALLOC_FL_ZERO_RANGE;
/// `FALLOC_FL_INSERT_RANGE`
const INSERT_RANGE = linux_raw_sys::general::FALLOC_FL_INSERT_RANGE;
/// `FALLOC_FL_UNSHARE_RANGE`
const UNSHARE_RANGE = linux_raw_sys::general::FALLOC_FL_UNSHARE_RANGE;
}
}
bitflags! {
/// `ST_*` constants for use with [`StatVfs`].
pub struct StatVfsMountFlags: u64 {
/// `ST_MANDLOCK`
const MANDLOCK = linux_raw_sys::general::MS_MANDLOCK as u64;
/// `ST_NOATIME`
const NOATIME = linux_raw_sys::general::MS_NOATIME as u64;
/// `ST_NODEV`
const NODEV = linux_raw_sys::general::MS_NODEV as u64;
/// `ST_NODIRATIME`
const NODIRATIME = linux_raw_sys::general::MS_NODIRATIME as u64;
/// `ST_NOEXEC`
const NOEXEC = linux_raw_sys::general::MS_NOEXEC as u64;
/// `ST_NOSUID`
const NOSUID = linux_raw_sys::general::MS_NOSUID as u64;
/// `ST_RDONLY`
const RDONLY = linux_raw_sys::general::MS_RDONLY as u64;
/// `ST_RELATIME`
const RELATIME = linux_raw_sys::general::MS_RELATIME as u64;
/// `ST_SYNCHRONOUS`
const SYNCHRONOUS = linux_raw_sys::general::MS_SYNCHRONOUS as u64;
}
}
/// `LOCK_*` constants for use with [`flock`]
///
/// [`flock`]: crate::fs::flock
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum FlockOperation {
/// `LOCK_SH`
LockShared = linux_raw_sys::general::LOCK_SH,
/// `LOCK_EX`
LockExclusive = linux_raw_sys::general::LOCK_EX,
/// `LOCK_UN`
Unlock = linux_raw_sys::general::LOCK_UN,
/// `LOCK_SH | LOCK_NB`
NonBlockingLockShared = linux_raw_sys::general::LOCK_SH | linux_raw_sys::general::LOCK_NB,
/// `LOCK_EX | LOCK_NB`
NonBlockingLockExclusive = linux_raw_sys::general::LOCK_EX | linux_raw_sys::general::LOCK_NB,
/// `LOCK_UN | LOCK_NB`
NonBlockingUnlock = linux_raw_sys::general::LOCK_UN | linux_raw_sys::general::LOCK_NB,
}
/// `struct stat` for use with [`statat`] and [`fstat`].
///
/// [`statat`]: crate::fs::statat
/// [`fstat`]: crate::fs::fstat
// On 32-bit, and mips64, Linux's `struct stat64` has a 32-bit `st_mtime` and
// friends, so we use our own struct, populated from `statx` where possible, to
// avoid the y2038 bug.
#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub struct Stat {
pub st_dev: u64,
pub st_mode: u32,
pub st_nlink: u32,
pub st_uid: u32,
pub st_gid: u32,
pub st_rdev: u64,
pub st_size: i64,
pub st_blksize: u32,
pub st_blocks: u64,
pub st_atime: u64,
pub st_atime_nsec: u32,
pub st_mtime: u64,
pub st_mtime_nsec: u32,
pub st_ctime: u64,
pub st_ctime_nsec: u32,
pub st_ino: u64,
}
/// `struct stat` for use with [`statat`] and [`fstat`].
///
/// [`statat`]: crate::fs::statat
/// [`fstat`]: crate::fs::fstat
#[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
pub type Stat = linux_raw_sys::general::stat;
/// `struct statfs` for use with [`statfs`] and [`fstatfs`].
///
/// [`statfs`]: crate::fs::statfs
/// [`fstatfs`]: crate::fs::fstatfs
#[allow(clippy::module_name_repetitions)]
pub type StatFs = linux_raw_sys::general::statfs64;
/// `struct statvfs` for use with [`statvfs`] and [`fstatvfs`].
///
/// [`statvfs`]: crate::fs::statvfs
/// [`fstatvfs`]: crate::fs::fstatvfs
#[allow(missing_docs)]
pub struct StatVfs {
pub f_bsize: u64,
pub f_frsize: u64,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_bavail: u64,
pub f_files: u64,
pub f_ffree: u64,
pub f_favail: u64,
pub f_fsid: u64,
pub f_flag: StatVfsMountFlags,
pub f_namemax: u64,
}
/// `struct statx` for use with [`statx`].
///
/// [`statx`]: crate::fs::statx
pub type Statx = linux_raw_sys::general::statx;
/// `struct statx_timestamp` for use with [`Statx`].
pub type StatxTimestamp = linux_raw_sys::general::statx_timestamp;
/// `mode_t`
#[cfg(not(any(
target_arch = "x86",
target_arch = "sparc",
target_arch = "avr",
target_arch = "arm",
)))]
pub type RawMode = linux_raw_sys::general::__kernel_mode_t;
/// `mode_t
#[cfg(any(
target_arch = "x86",
target_arch = "sparc",
target_arch = "avr",
target_arch = "arm",
))]
// Don't use `__kernel_mode_t` since it's `u16` which differs from `st_size`.
pub type RawMode = c::c_uint;
/// `dev_t`
// Within the kernel the dev_t is 32-bit, but userspace uses a 64-bit field.
pub type Dev = u64;
/// `__fsword_t`
#[cfg(not(target_arch = "mips64"))]
pub type FsWord = linux_raw_sys::general::__fsword_t;
/// `__fsword_t`
#[cfg(target_arch = "mips64")]
pub type FsWord = i64;
pub use linux_raw_sys::general::{UTIME_NOW, UTIME_OMIT};
/// `PROC_SUPER_MAGIC`—The magic number for the procfs filesystem.
pub const PROC_SUPER_MAGIC: FsWord = linux_raw_sys::general::PROC_SUPER_MAGIC as FsWord;
/// `NFS_SUPER_MAGIC`—The magic number for the NFS filesystem.
pub const NFS_SUPER_MAGIC: FsWord = linux_raw_sys::general::NFS_SUPER_MAGIC as FsWord;

View file

@ -0,0 +1,555 @@
//! epoll support.
//!
//! This is an experiment, and it isn't yet clear whether epoll is the right
//! level of abstraction at which to introduce safety. But it works fairly well
//! in simple examples 🙂.
//!
//! # Examples
//!
//! ```rust,no_run
//! # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))]
//! # #[cfg(feature = "net")]
//! # fn main() -> std::io::Result<()> {
//! use io_lifetimes::AsFd;
//! use rustix::io::epoll::{self, Epoll};
//! use rustix::io::{ioctl_fionbio, read, write};
//! use rustix::net::{
//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, Protocol, SocketAddrV4,
//! SocketType,
//! };
//! use std::os::unix::io::AsRawFd;
//!
//! // Create a socket and listen on it.
//! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, Protocol::default())?;
//! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?;
//! listen(&listen_sock, 1)?;
//!
//! // Create an epoll object. Using `Owning` here means the epoll object will
//! // take ownership of the file descriptors registered with it.
//! let epoll = Epoll::new(epoll::CreateFlags::CLOEXEC, epoll::Owning::new())?;
//!
//! // Remember the socket raw fd, which we use for comparisons only.
//! let raw_listen_sock = listen_sock.as_fd().as_raw_fd();
//!
//! // Register the socket with the epoll object.
//! epoll.add(listen_sock, epoll::EventFlags::IN)?;
//!
//! // Process events.
//! let mut event_list = epoll::EventVec::with_capacity(4);
//! loop {
//! epoll.wait(&mut event_list, -1)?;
//! for (_event_flags, target) in &event_list {
//! if target.as_raw_fd() == raw_listen_sock {
//! // Accept a new connection, set it to non-blocking, and
//! // register to be notified when it's ready to write to.
//! let conn_sock = accept(&*target)?;
//! ioctl_fionbio(&conn_sock, true)?;
//! epoll.add(conn_sock, epoll::EventFlags::OUT | epoll::EventFlags::ET)?;
//! } else {
//! // Write a message to the stream and then unregister it.
//! write(&*target, b"hello\n")?;
//! let _ = epoll.del(target)?;
//! }
//! }
//! }
//! # }
//! # #[cfg(not(feature = "net"))]
//! # fn main() {}
//! ```
#![allow(unsafe_code)]
use super::super::c;
use crate::backend::io::syscalls::{epoll_add, epoll_create, epoll_del, epoll_mod, epoll_wait};
use crate::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(feature = "std")]
use crate::fd::{FromRawFd, IntoRawFd};
use crate::io;
use alloc::vec::Vec;
use bitflags::bitflags;
use core::fmt;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::null;
bitflags! {
/// `EPOLL_*` for use with [`Epoll::new`].
pub struct CreateFlags: c::c_uint {
/// `EPOLL_CLOEXEC`
const CLOEXEC = linux_raw_sys::general::EPOLL_CLOEXEC;
}
}
bitflags! {
/// `EPOLL*` for use with [`Epoll::add`].
#[derive(Default)]
pub struct EventFlags: u32 {
/// `EPOLLIN`
const IN = linux_raw_sys::general::EPOLLIN as u32;
/// `EPOLLOUT`
const OUT = linux_raw_sys::general::EPOLLOUT as u32;
/// `EPOLLPRI`
const PRI = linux_raw_sys::general::EPOLLPRI as u32;
/// `EPOLLERR`
const ERR = linux_raw_sys::general::EPOLLERR as u32;
/// `EPOLLHUP`
const HUP = linux_raw_sys::general::EPOLLHUP as u32;
/// `EPOLLET`
const ET = linux_raw_sys::general::EPOLLET as u32;
/// `EPOLLONESHOT`
const ONESHOT = linux_raw_sys::general::EPOLLONESHOT as u32;
/// `EPOLLWAKEUP`
const WAKEUP = linux_raw_sys::general::EPOLLWAKEUP as u32;
/// `EPOLLEXCLUSIVE`
const EXCLUSIVE = linux_raw_sys::general::EPOLLEXCLUSIVE as u32;
}
}
/// A reference to a `T`.
pub struct Ref<'a, T> {
t: T,
_phantom: PhantomData<&'a T>,
}
impl<'a, T> Ref<'a, T> {
#[inline]
fn new(t: T) -> Self {
Self {
t,
_phantom: PhantomData,
}
}
#[inline]
fn consume(self) -> T {
self.t
}
}
impl<'a, T> Deref for Ref<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.t
}
}
impl<'a, T: fmt::Debug> fmt::Debug for Ref<'a, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.t.fmt(fmt)
}
}
/// A trait for data stored within an [`Epoll`] instance.
pub trait Context {
/// The type of an element owned by this context.
type Data;
/// The type of a value used to refer to an element owned by this context.
type Target: AsFd;
/// Assume ownership of `data`, and returning a `Target`.
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target>;
/// Encode `target` as a `u64`. The only requirement on this value is that
/// it be decodable by `decode`.
fn encode(&self, target: Ref<'_, Self::Target>) -> u64;
/// Decode `raw`, which is a value encoded by `encode`, into a `Target`.
///
/// # Safety
///
/// `raw` must be a `u64` value returned from `encode`, from the same
/// context, and within the context's lifetime.
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target>;
/// Release ownership of the value referred to by `target` and return it.
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data;
}
/// A type implementing [`Context`] where the `Data` type is `BorrowedFd<'a>`.
pub struct Borrowing<'a> {
_phantom: PhantomData<BorrowedFd<'a>>,
}
impl<'a> Context for Borrowing<'a> {
type Data = BorrowedFd<'a>;
type Target = BorrowedFd<'a>;
#[inline]
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
Ref::new(data)
}
#[inline]
fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
target.as_raw_fd() as u64
}
#[inline]
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
Ref::new(BorrowedFd::<'a>::borrow_raw(raw as RawFd))
}
#[inline]
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
target.consume()
}
}
/// A type implementing [`Context`] where the `Data` type is `T`, a type
/// implementing `From<OwnedFd>` and `From<T> of OwnedFd`.
///
/// This may be used with [`OwnedFd`], or higher-level types like
/// [`std::fs::File`] or [`std::net::TcpStream`].
#[cfg(feature = "std")]
pub struct Owning<'context, T: Into<OwnedFd> + From<OwnedFd>> {
_phantom: PhantomData<&'context T>,
}
#[cfg(feature = "std")]
impl<'context, T: Into<OwnedFd> + From<OwnedFd>> Owning<'context, T> {
/// Creates a new empty `Owning`.
#[allow(clippy::new_without_default)] // This is a specialized type that doesn't need to be generically constructible.
#[inline]
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> Context for Owning<'context, T> {
type Data = T;
type Target = BorrowedFd<'context>;
#[inline]
fn acquire<'call>(&self, data: Self::Data) -> Ref<'call, Self::Target> {
let raw_fd = data.into().into_raw_fd();
// Safety: `epoll` will assign ownership of the file descriptor to the
// kernel epoll object. We use `Into<OwnedFd>`+`IntoRawFd` to consume
// the `Data` and extract the raw file descriptor and then "borrow" it
// with `borrow_raw` knowing that the borrow won't outlive the
// kernel epoll object.
unsafe { Ref::new(BorrowedFd::<'context>::borrow_raw(raw_fd)) }
}
#[inline]
fn encode(&self, target: Ref<'_, Self::Target>) -> u64 {
target.as_fd().as_raw_fd() as u64
}
#[inline]
unsafe fn decode<'call>(&self, raw: u64) -> Ref<'call, Self::Target> {
Ref::new(BorrowedFd::<'context>::borrow_raw(raw as RawFd))
}
#[inline]
fn release(&self, target: Ref<'_, Self::Target>) -> Self::Data {
let raw_fd = target.consume().as_raw_fd();
// Safety: The file descriptor was held by the kernel epoll object and
// is now being released, so we can create a new `OwnedFd` that assumes
// ownership.
unsafe { T::from(io_lifetimes::OwnedFd::from_raw_fd(raw_fd)) }
}
}
/// An "epoll", an interface to an OS object allowing one to repeatedly wait
/// for events from a set of file descriptors efficiently.
pub struct Epoll<Context: self::Context> {
epoll_fd: OwnedFd,
context: Context,
}
impl<Context: self::Context> Epoll<Context> {
/// `epoll_create1(flags)`—Creates a new `Epoll`.
///
/// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file
/// descriptor from being implicitly passed across `exec` boundaries.
#[inline]
#[doc(alias = "epoll_create1")]
pub fn new(flags: CreateFlags, context: Context) -> io::Result<Self> {
// Safety: We're calling `epoll_create1` via FFI and we know how it
// behaves.
Ok(Self {
epoll_fd: epoll_create(flags)?,
context,
})
}
/// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an
/// `Epoll`.
///
/// This registers interest in any of the events set in `events` occurring
/// on the file descriptor associated with `data`.
#[doc(alias = "epoll_ctl")]
pub fn add(
&self,
data: Context::Data,
event_flags: EventFlags,
) -> io::Result<Ref<'_, Context::Target>> {
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let target = self.context.acquire(data);
let raw_fd = target.as_fd().as_raw_fd();
let encoded = self.context.encode(target);
epoll_add(
self.epoll_fd.as_fd(),
raw_fd,
&linux_raw_sys::general::epoll_event {
events: event_flags.bits(),
data: encoded,
},
)?;
Ok(self.context.decode(encoded))
}
}
/// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in
/// this `Epoll`.
///
/// This sets the events of interest with `target` to `events`.
#[doc(alias = "epoll_ctl")]
pub fn mod_(
&self,
target: Ref<'_, Context::Target>,
event_flags: EventFlags,
) -> io::Result<()> {
let raw_fd = target.as_fd().as_raw_fd();
let encoded = self.context.encode(target);
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
epoll_mod(
self.epoll_fd.as_fd(),
raw_fd,
&linux_raw_sys::general::epoll_event {
events: event_flags.bits(),
data: encoded,
},
)
}
}
/// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in
/// this `Epoll`.
///
/// This also returns the owning `Data`.
#[doc(alias = "epoll_ctl")]
pub fn del(&self, target: Ref<'_, Context::Target>) -> io::Result<Context::Data> {
// Safety: We're calling `epoll_ctl` via FFI and we know how it
// behaves.
unsafe {
let raw_fd = target.as_fd().as_raw_fd();
epoll_del(self.epoll_fd.as_fd(), raw_fd)?;
}
Ok(self.context.release(target))
}
/// `epoll_wait(self, events, timeout)`—Waits for registered events of
/// interest.
///
/// For each event of interest, an element is written to `events`. On
/// success, this returns the number of written elements.
#[doc(alias = "epoll_wait")]
pub fn wait<'context>(
&'context self,
event_list: &mut EventVec<'context, Context>,
timeout: c::c_int,
) -> io::Result<()> {
// Safety: We're calling `epoll_wait` via FFI and we know how it
// behaves.
unsafe {
event_list.events.set_len(0);
let nfds = epoll_wait(
self.epoll_fd.as_fd(),
event_list.events[..].as_mut_ptr().cast(),
event_list.events.capacity(),
timeout,
)?;
event_list.events.set_len(nfds);
event_list.context = &self.context;
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> AsRawFd for Epoll<Owning<'context, T>> {
fn as_raw_fd(&self) -> RawFd {
self.epoll_fd.as_raw_fd()
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> IntoRawFd for Epoll<Owning<'context, T>> {
fn into_raw_fd(self) -> RawFd {
self.epoll_fd.into_raw_fd()
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> FromRawFd for Epoll<Owning<'context, T>> {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
epoll_fd: OwnedFd::from_raw_fd(fd),
context: Owning::new(),
}
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> AsFd for Epoll<Owning<'context, T>> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.epoll_fd.as_fd()
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> From<Epoll<Owning<'context, T>>>
for OwnedFd
{
fn from(epoll: Epoll<Owning<'context, T>>) -> Self {
epoll.epoll_fd
}
}
#[cfg(feature = "std")]
impl<'context, T: AsFd + Into<OwnedFd> + From<OwnedFd>> From<OwnedFd>
for Epoll<Owning<'context, T>>
{
fn from(fd: OwnedFd) -> Self {
Self {
epoll_fd: fd,
context: Owning::new(),
}
}
}
/// An iterator over the `Event`s in an `EventVec`.
pub struct Iter<'context, Context: self::Context> {
iter: core::slice::Iter<'context, Event>,
context: *const Context,
_phantom: PhantomData<&'context Context>,
}
impl<'context, Context: self::Context> Iterator for Iter<'context, Context> {
type Item = (EventFlags, Ref<'context, Context::Target>);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|event| {
// Safety: `self.context` is guaranteed to be valid because we hold
// `'context` for it. And we know this event is associated with this
// context because `wait` sets both.
let decoded = unsafe { (*self.context).decode(event.encoded) };
(event.event_flags, decoded)
})
}
}
/// A record of an event that occurred.
#[repr(C)]
#[cfg_attr(target_arch = "x86_64", repr(packed))]
struct Event {
// Match the layout of `linux_raw_sys::general::epoll_event`. We just use a
// `u64` instead of the full union; `Context` implementations will simply
// need to deal with casting the value into and out of the `u64`
// themselves.
event_flags: EventFlags,
encoded: u64,
}
/// A vector of `Event`s, plus context for interpreting them.
pub struct EventVec<'context, Context: self::Context> {
events: Vec<Event>,
context: *const Context,
_phantom: PhantomData<&'context Context>,
}
impl<'context, Context: self::Context> EventVec<'context, Context> {
/// Constructs an `EventVec` with memory for `capacity` `Event`s.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
events: Vec::with_capacity(capacity),
context: null(),
_phantom: PhantomData,
}
}
/// Returns the current `Event` capacity of this `EventVec`.
#[inline]
pub fn capacity(&self) -> usize {
self.events.capacity()
}
/// Reserves enough memory for at least `additional` more `Event`s.
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.events.reserve(additional);
}
/// Reserves enough memory for exactly `additional` more `Event`s.
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.events.reserve_exact(additional);
}
/// Clears all the `Events` out of this `EventVec`.
#[inline]
pub fn clear(&mut self) {
self.events.clear();
}
/// Shrinks the capacity of this `EventVec` as much as possible.
#[inline]
pub fn shrink_to_fit(&mut self) {
self.events.shrink_to_fit();
}
/// Returns an iterator over the `Event`s in this `EventVec`.
#[inline]
pub fn iter(&self) -> Iter<'_, Context> {
Iter {
iter: self.events.iter(),
context: self.context,
_phantom: PhantomData,
}
}
/// Returns the number of `Event`s logically contained in this `EventVec`.
#[inline]
pub fn len(&mut self) -> usize {
self.events.len()
}
/// Tests whether this `EventVec` is logically empty.
#[inline]
pub fn is_empty(&mut self) -> bool {
self.events.is_empty()
}
}
impl<'context, Context: self::Context> IntoIterator for &'context EventVec<'context, Context> {
type IntoIter = Iter<'context, Context>;
type Item = (EventFlags, Ref<'context, Context::Target>);
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

View file

@ -0,0 +1,517 @@
//! The `rustix` `Errno` type.
//!
//! This type holds an OS error code, which conceptually corresponds to an
//! `errno` value.
//!
//! # Safety
//!
//! Linux uses error codes in `-4095..0`; we use rustc attributes to describe
//! this restricted range of values.
#![allow(unsafe_code)]
#![cfg_attr(not(rustc_attrs), allow(unused_unsafe))]
use super::super::c;
use crate::backend::fd::RawFd;
use crate::backend::reg::{RetNumber, RetReg};
use crate::io;
use linux_raw_sys::errno;
/// The error type for `rustix` APIs.
///
/// This is similar to `std::io::Error`, but only holds an OS error code,
/// and no extra error value.
#[repr(transparent)]
#[doc(alias = "errno")]
#[derive(Eq, PartialEq, Hash, Copy, Clone)]
// Linux returns negated error codes, and we leave them in negated form, so
// error codes are in `-4095..0`.
#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0xf001))]
#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xffff))]
pub struct Errno(u16);
impl Errno {
/// Extract an `Errno` value from a `std::io::Error`.
///
/// This isn't a `From` conversion because it's expected to be relatively
/// uncommon.
#[cfg(feature = "std")]
#[inline]
pub fn from_io_error(io_err: &std::io::Error) -> Option<Self> {
io_err.raw_os_error().and_then(|raw| {
// `std::io::Error` could theoretically have arbitrary "OS error"
// values, so check that they're in Linux's range.
if (1..4096).contains(&raw) {
Some(Self::from_errno(raw as u32))
} else {
None
}
})
}
/// Extract the raw OS error number from this error.
#[inline]
pub const fn raw_os_error(self) -> i32 {
(self.0 as i16 as i32).wrapping_neg()
}
/// Construct an `Errno` from a raw OS error number.
#[inline]
pub const fn from_raw_os_error(raw: i32) -> Self {
Self::from_errno(raw as u32)
}
/// Convert from a C errno value (which is positive) to an `Errno`.
const fn from_errno(raw: u32) -> Self {
// We store error values in negated form, so that we don't have to negate
// them after every syscall.
let encoded = raw.wrapping_neg() as u16;
// TODO: Use Range::contains, once that's `const`.
const_assert!(encoded >= 0xf001);
// Safety: Linux syscalls return negated error values in the range
// `-4095..0`, which we just asserted.
unsafe { Self(encoded) }
}
}
/// Check for an error from the result of a syscall which encodes a
/// `c::c_int` on success.
#[inline]
pub(in crate::backend) fn try_decode_c_int<Num: RetNumber>(
raw: RetReg<Num>,
) -> io::Result<c::c_int> {
if raw.is_in_range(-4095..0) {
// Safety: `raw` must be in `-4095..0`, and we just checked that raw is
// in that range.
return Err(unsafe { Errno(raw.decode_error_code()) });
}
Ok(raw.decode_c_int())
}
/// Check for an error from the result of a syscall which encodes a
/// `c::c_uint` on success.
#[inline]
pub(in crate::backend) fn try_decode_c_uint<Num: RetNumber>(
raw: RetReg<Num>,
) -> io::Result<c::c_uint> {
if raw.is_in_range(-4095..0) {
// Safety: `raw` must be in `-4095..0`, and we just checked that raw is
// in that range.
return Err(unsafe { Errno(raw.decode_error_code()) });
}
Ok(raw.decode_c_uint())
}
/// Check for an error from the result of a syscall which encodes a `usize` on
/// success.
#[inline]
pub(in crate::backend) fn try_decode_usize<Num: RetNumber>(raw: RetReg<Num>) -> io::Result<usize> {
if raw.is_in_range(-4095..0) {
// Safety: `raw` must be in `-4095..0`, and we just checked that raw is
// in that range.
return Err(unsafe { Errno(raw.decode_error_code()) });
}
Ok(raw.decode_usize())
}
/// Check for an error from the result of a syscall which encodes a
/// `*mut c_void` on success.
#[inline]
pub(in crate::backend) fn try_decode_void_star<Num: RetNumber>(
raw: RetReg<Num>,
) -> io::Result<*mut c::c_void> {
if raw.is_in_range(-4095..0) {
// Safety: `raw` must be in `-4095..0`, and we just checked that raw is
// in that range.
return Err(unsafe { Errno(raw.decode_error_code()) });
}
Ok(raw.decode_void_star())
}
/// Check for an error from the result of a syscall which encodes a
/// `u64` on success.
#[cfg(target_pointer_width = "64")]
#[inline]
pub(in crate::backend) fn try_decode_u64<Num: RetNumber>(raw: RetReg<Num>) -> io::Result<u64> {
if raw.is_in_range(-4095..0) {
// Safety: `raw` must be in `-4095..0`, and we just checked that raw is
// in that range.
return Err(unsafe { Errno(raw.decode_error_code()) });
}
Ok(raw.decode_u64())
}
/// Check for an error from the result of a syscall which encodes a file
/// descriptor on success.
///
/// # Safety
///
/// This must only be used with syscalls which return file descriptors on
/// success.
#[inline]
pub(in crate::backend) unsafe fn try_decode_raw_fd<Num: RetNumber>(
raw: RetReg<Num>,
) -> io::Result<RawFd> {
// Instead of using `check_result` here, we just check for negative, since
// this function is only used for system calls which return file
// descriptors, and this produces smaller code.
if raw.is_negative() {
debug_assert!(raw.is_in_range(-4095..0));
// Tell the optimizer that we know the value is in the error range.
// This helps it avoid unnecessary integer conversions.
#[cfg(core_intrinsics)]
{
core::intrinsics::assume(raw.is_in_range(-4095..0));
}
return Err(Errno(raw.decode_error_code()));
}
Ok(raw.decode_raw_fd())
}
/// Check for an error from the result of a syscall which encodes no value on
/// success. On success, return the unconsumed `raw` value.
///
/// # Safety
///
/// This must only be used with syscalls which return no value on success.
#[inline]
pub(in crate::backend) unsafe fn try_decode_void<Num: RetNumber>(
raw: RetReg<Num>,
) -> io::Result<()> {
// Instead of using `check_result` here, we just check for zero, since this
// function is only used for system calls which have no other return value,
// and this produces smaller code.
if raw.is_nonzero() {
debug_assert!(raw.is_in_range(-4095..0));
// Tell the optimizer that we know the value is in the error range.
// This helps it avoid unnecessary integer conversions.
#[cfg(core_intrinsics)]
{
core::intrinsics::assume(raw.is_in_range(-4095..0));
}
return Err(Errno(raw.decode_error_code()));
}
raw.decode_void();
Ok(())
}
/// Check for an error from the result of a syscall which does not return on
/// success. On success, return the unconsumed `raw` value.
///
/// # Safety
///
/// This must only be used with syscalls which do not return on success.
#[cfg(feature = "runtime")]
#[inline]
pub(in crate::backend) unsafe fn try_decode_error<Num: RetNumber>(raw: RetReg<Num>) -> io::Errno {
debug_assert!(raw.is_in_range(-4095..0));
// Tell the optimizer that we know the value is in the error range.
// This helps it avoid unnecessary integer conversions.
#[cfg(core_intrinsics)]
{
core::intrinsics::assume(raw.is_in_range(-4095..0));
}
Errno(raw.decode_error_code())
}
/// Return the contained `usize` value.
#[cfg(not(debug_assertions))]
#[inline]
pub(in crate::backend) fn decode_usize_infallible<Num: RetNumber>(raw: RetReg<Num>) -> usize {
raw.decode_usize()
}
impl Errno {
/// `EACCES`
#[doc(alias = "ACCES")]
pub const ACCESS: Self = Self::from_errno(errno::EACCES);
/// `EADDRINUSE`
pub const ADDRINUSE: Self = Self::from_errno(errno::EADDRINUSE);
/// `EADDRNOTAVAIL`
pub const ADDRNOTAVAIL: Self = Self::from_errno(errno::EADDRNOTAVAIL);
/// `EADV`
pub const ADV: Self = Self::from_errno(errno::EADV);
/// `EAFNOSUPPORT`
pub const AFNOSUPPORT: Self = Self::from_errno(errno::EAFNOSUPPORT);
/// `EAGAIN`
pub const AGAIN: Self = Self::from_errno(errno::EAGAIN);
/// `EALREADY`
pub const ALREADY: Self = Self::from_errno(errno::EALREADY);
/// `EBADE`
pub const BADE: Self = Self::from_errno(errno::EBADE);
/// `EBADF`
pub const BADF: Self = Self::from_errno(errno::EBADF);
/// `EBADFD`
pub const BADFD: Self = Self::from_errno(errno::EBADFD);
/// `EBADMSG`
pub const BADMSG: Self = Self::from_errno(errno::EBADMSG);
/// `EBADR`
pub const BADR: Self = Self::from_errno(errno::EBADR);
/// `EBADRQC`
pub const BADRQC: Self = Self::from_errno(errno::EBADRQC);
/// `EBADSLT`
pub const BADSLT: Self = Self::from_errno(errno::EBADSLT);
/// `EBFONT`
pub const BFONT: Self = Self::from_errno(errno::EBFONT);
/// `EBUSY`
pub const BUSY: Self = Self::from_errno(errno::EBUSY);
/// `ECANCELED`
pub const CANCELED: Self = Self::from_errno(errno::ECANCELED);
/// `ECHILD`
pub const CHILD: Self = Self::from_errno(errno::ECHILD);
/// `ECHRNG`
pub const CHRNG: Self = Self::from_errno(errno::ECHRNG);
/// `ECOMM`
pub const COMM: Self = Self::from_errno(errno::ECOMM);
/// `ECONNABORTED`
pub const CONNABORTED: Self = Self::from_errno(errno::ECONNABORTED);
/// `ECONNREFUSED`
pub const CONNREFUSED: Self = Self::from_errno(errno::ECONNREFUSED);
/// `ECONNRESET`
pub const CONNRESET: Self = Self::from_errno(errno::ECONNRESET);
/// `EDEADLK`
pub const DEADLK: Self = Self::from_errno(errno::EDEADLK);
/// `EDEADLOCK`
pub const DEADLOCK: Self = Self::from_errno(errno::EDEADLOCK);
/// `EDESTADDRREQ`
pub const DESTADDRREQ: Self = Self::from_errno(errno::EDESTADDRREQ);
/// `EDOM`
pub const DOM: Self = Self::from_errno(errno::EDOM);
/// `EDOTDOT`
pub const DOTDOT: Self = Self::from_errno(errno::EDOTDOT);
/// `EDQUOT`
pub const DQUOT: Self = Self::from_errno(errno::EDQUOT);
/// `EEXIST`
pub const EXIST: Self = Self::from_errno(errno::EEXIST);
/// `EFAULT`
pub const FAULT: Self = Self::from_errno(errno::EFAULT);
/// `EFBIG`
pub const FBIG: Self = Self::from_errno(errno::EFBIG);
/// `EHOSTDOWN`
pub const HOSTDOWN: Self = Self::from_errno(errno::EHOSTDOWN);
/// `EHOSTUNREACH`
pub const HOSTUNREACH: Self = Self::from_errno(errno::EHOSTUNREACH);
/// `EHWPOISON`
pub const HWPOISON: Self = Self::from_errno(errno::EHWPOISON);
/// `EIDRM`
pub const IDRM: Self = Self::from_errno(errno::EIDRM);
/// `EILSEQ`
pub const ILSEQ: Self = Self::from_errno(errno::EILSEQ);
/// `EINPROGRESS`
pub const INPROGRESS: Self = Self::from_errno(errno::EINPROGRESS);
/// `EINTR`.
///
/// For a convenient way to retry system calls that exit with `INTR`, use
/// [`retry_on_intr`].
///
/// [`retry_on_intr`]: io::retry_on_intr
pub const INTR: Self = Self::from_errno(errno::EINTR);
/// `EINVAL`
pub const INVAL: Self = Self::from_errno(errno::EINVAL);
/// `EIO`
pub const IO: Self = Self::from_errno(errno::EIO);
/// `EISCONN`
pub const ISCONN: Self = Self::from_errno(errno::EISCONN);
/// `EISDIR`
pub const ISDIR: Self = Self::from_errno(errno::EISDIR);
/// `EISNAM`
pub const ISNAM: Self = Self::from_errno(errno::EISNAM);
/// `EKEYEXPIRED`
pub const KEYEXPIRED: Self = Self::from_errno(errno::EKEYEXPIRED);
/// `EKEYREJECTED`
pub const KEYREJECTED: Self = Self::from_errno(errno::EKEYREJECTED);
/// `EKEYREVOKED`
pub const KEYREVOKED: Self = Self::from_errno(errno::EKEYREVOKED);
/// `EL2HLT`
pub const L2HLT: Self = Self::from_errno(errno::EL2HLT);
/// `EL2NSYNC`
pub const L2NSYNC: Self = Self::from_errno(errno::EL2NSYNC);
/// `EL3HLT`
pub const L3HLT: Self = Self::from_errno(errno::EL3HLT);
/// `EL3RST`
pub const L3RST: Self = Self::from_errno(errno::EL3RST);
/// `ELIBACC`
pub const LIBACC: Self = Self::from_errno(errno::ELIBACC);
/// `ELIBBAD`
pub const LIBBAD: Self = Self::from_errno(errno::ELIBBAD);
/// `ELIBEXEC`
pub const LIBEXEC: Self = Self::from_errno(errno::ELIBEXEC);
/// `ELIBMAX`
pub const LIBMAX: Self = Self::from_errno(errno::ELIBMAX);
/// `ELIBSCN`
pub const LIBSCN: Self = Self::from_errno(errno::ELIBSCN);
/// `ELNRNG`
pub const LNRNG: Self = Self::from_errno(errno::ELNRNG);
/// `ELOOP`
pub const LOOP: Self = Self::from_errno(errno::ELOOP);
/// `EMEDIUMTYPE`
pub const MEDIUMTYPE: Self = Self::from_errno(errno::EMEDIUMTYPE);
/// `EMFILE`
pub const MFILE: Self = Self::from_errno(errno::EMFILE);
/// `EMLINK`
pub const MLINK: Self = Self::from_errno(errno::EMLINK);
/// `EMSGSIZE`
pub const MSGSIZE: Self = Self::from_errno(errno::EMSGSIZE);
/// `EMULTIHOP`
pub const MULTIHOP: Self = Self::from_errno(errno::EMULTIHOP);
/// `ENAMETOOLONG`
pub const NAMETOOLONG: Self = Self::from_errno(errno::ENAMETOOLONG);
/// `ENAVAIL`
pub const NAVAIL: Self = Self::from_errno(errno::ENAVAIL);
/// `ENETDOWN`
pub const NETDOWN: Self = Self::from_errno(errno::ENETDOWN);
/// `ENETRESET`
pub const NETRESET: Self = Self::from_errno(errno::ENETRESET);
/// `ENETUNREACH`
pub const NETUNREACH: Self = Self::from_errno(errno::ENETUNREACH);
/// `ENFILE`
pub const NFILE: Self = Self::from_errno(errno::ENFILE);
/// `ENOANO`
pub const NOANO: Self = Self::from_errno(errno::ENOANO);
/// `ENOBUFS`
pub const NOBUFS: Self = Self::from_errno(errno::ENOBUFS);
/// `ENOCSI`
pub const NOCSI: Self = Self::from_errno(errno::ENOCSI);
/// `ENODATA`
#[doc(alias = "NOATTR")]
pub const NODATA: Self = Self::from_errno(errno::ENODATA);
/// `ENODEV`
pub const NODEV: Self = Self::from_errno(errno::ENODEV);
/// `ENOENT`
pub const NOENT: Self = Self::from_errno(errno::ENOENT);
/// `ENOEXEC`
pub const NOEXEC: Self = Self::from_errno(errno::ENOEXEC);
/// `ENOKEY`
pub const NOKEY: Self = Self::from_errno(errno::ENOKEY);
/// `ENOLCK`
pub const NOLCK: Self = Self::from_errno(errno::ENOLCK);
/// `ENOLINK`
pub const NOLINK: Self = Self::from_errno(errno::ENOLINK);
/// `ENOMEDIUM`
pub const NOMEDIUM: Self = Self::from_errno(errno::ENOMEDIUM);
/// `ENOMEM`
pub const NOMEM: Self = Self::from_errno(errno::ENOMEM);
/// `ENOMSG`
pub const NOMSG: Self = Self::from_errno(errno::ENOMSG);
/// `ENONET`
pub const NONET: Self = Self::from_errno(errno::ENONET);
/// `ENOPKG`
pub const NOPKG: Self = Self::from_errno(errno::ENOPKG);
/// `ENOPROTOOPT`
pub const NOPROTOOPT: Self = Self::from_errno(errno::ENOPROTOOPT);
/// `ENOSPC`
pub const NOSPC: Self = Self::from_errno(errno::ENOSPC);
/// `ENOSR`
pub const NOSR: Self = Self::from_errno(errno::ENOSR);
/// `ENOSTR`
pub const NOSTR: Self = Self::from_errno(errno::ENOSTR);
/// `ENOSYS`
pub const NOSYS: Self = Self::from_errno(errno::ENOSYS);
/// `ENOTBLK`
pub const NOTBLK: Self = Self::from_errno(errno::ENOTBLK);
/// `ENOTCONN`
pub const NOTCONN: Self = Self::from_errno(errno::ENOTCONN);
/// `ENOTDIR`
pub const NOTDIR: Self = Self::from_errno(errno::ENOTDIR);
/// `ENOTEMPTY`
pub const NOTEMPTY: Self = Self::from_errno(errno::ENOTEMPTY);
/// `ENOTNAM`
pub const NOTNAM: Self = Self::from_errno(errno::ENOTNAM);
/// `ENOTRECOVERABLE`
pub const NOTRECOVERABLE: Self = Self::from_errno(errno::ENOTRECOVERABLE);
/// `ENOTSOCK`
pub const NOTSOCK: Self = Self::from_errno(errno::ENOTSOCK);
/// `ENOTSUP`
// On Linux, `ENOTSUP` has the same value as `EOPNOTSUPP`.
pub const NOTSUP: Self = Self::from_errno(errno::EOPNOTSUPP);
/// `ENOTTY`
pub const NOTTY: Self = Self::from_errno(errno::ENOTTY);
/// `ENOTUNIQ`
pub const NOTUNIQ: Self = Self::from_errno(errno::ENOTUNIQ);
/// `ENXIO`
pub const NXIO: Self = Self::from_errno(errno::ENXIO);
/// `EOPNOTSUPP`
pub const OPNOTSUPP: Self = Self::from_errno(errno::EOPNOTSUPP);
/// `EOVERFLOW`
pub const OVERFLOW: Self = Self::from_errno(errno::EOVERFLOW);
/// `EOWNERDEAD`
pub const OWNERDEAD: Self = Self::from_errno(errno::EOWNERDEAD);
/// `EPERM`
pub const PERM: Self = Self::from_errno(errno::EPERM);
/// `EPFNOSUPPORT`
pub const PFNOSUPPORT: Self = Self::from_errno(errno::EPFNOSUPPORT);
/// `EPIPE`
pub const PIPE: Self = Self::from_errno(errno::EPIPE);
/// `EPROTO`
pub const PROTO: Self = Self::from_errno(errno::EPROTO);
/// `EPROTONOSUPPORT`
pub const PROTONOSUPPORT: Self = Self::from_errno(errno::EPROTONOSUPPORT);
/// `EPROTOTYPE`
pub const PROTOTYPE: Self = Self::from_errno(errno::EPROTOTYPE);
/// `ERANGE`
pub const RANGE: Self = Self::from_errno(errno::ERANGE);
/// `EREMCHG`
pub const REMCHG: Self = Self::from_errno(errno::EREMCHG);
/// `EREMOTE`
pub const REMOTE: Self = Self::from_errno(errno::EREMOTE);
/// `EREMOTEIO`
pub const REMOTEIO: Self = Self::from_errno(errno::EREMOTEIO);
/// `ERESTART`
pub const RESTART: Self = Self::from_errno(errno::ERESTART);
/// `ERFKILL`
pub const RFKILL: Self = Self::from_errno(errno::ERFKILL);
/// `EROFS`
pub const ROFS: Self = Self::from_errno(errno::EROFS);
/// `ESHUTDOWN`
pub const SHUTDOWN: Self = Self::from_errno(errno::ESHUTDOWN);
/// `ESOCKTNOSUPPORT`
pub const SOCKTNOSUPPORT: Self = Self::from_errno(errno::ESOCKTNOSUPPORT);
/// `ESPIPE`
pub const SPIPE: Self = Self::from_errno(errno::ESPIPE);
/// `ESRCH`
pub const SRCH: Self = Self::from_errno(errno::ESRCH);
/// `ESRMNT`
pub const SRMNT: Self = Self::from_errno(errno::ESRMNT);
/// `ESTALE`
pub const STALE: Self = Self::from_errno(errno::ESTALE);
/// `ESTRPIPE`
pub const STRPIPE: Self = Self::from_errno(errno::ESTRPIPE);
/// `ETIME`
pub const TIME: Self = Self::from_errno(errno::ETIME);
/// `ETIMEDOUT`
pub const TIMEDOUT: Self = Self::from_errno(errno::ETIMEDOUT);
/// `E2BIG`
#[doc(alias = "2BIG")]
pub const TOOBIG: Self = Self::from_errno(errno::E2BIG);
/// `ETOOMANYREFS`
pub const TOOMANYREFS: Self = Self::from_errno(errno::ETOOMANYREFS);
/// `ETXTBSY`
pub const TXTBSY: Self = Self::from_errno(errno::ETXTBSY);
/// `EUCLEAN`
pub const UCLEAN: Self = Self::from_errno(errno::EUCLEAN);
/// `EUNATCH`
pub const UNATCH: Self = Self::from_errno(errno::EUNATCH);
/// `EUSERS`
pub const USERS: Self = Self::from_errno(errno::EUSERS);
/// `EWOULDBLOCK`
pub const WOULDBLOCK: Self = Self::from_errno(errno::EWOULDBLOCK);
/// `EXDEV`
pub const XDEV: Self = Self::from_errno(errno::EXDEV);
/// `EXFULL`
pub const XFULL: Self = Self::from_errno(errno::EXFULL);
}

View file

@ -0,0 +1,98 @@
//! The following is derived from Rust's
//! library/std/src/sys/unix/io.rs
//! dca3f1b786efd27be3b325ed1e01e247aa589c3b.
#![allow(unsafe_code)]
use super::super::c;
use core::marker::PhantomData;
use core::slice;
use linux_raw_sys::general::__kernel_size_t;
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSlice.html>
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct IoSlice<'a> {
vec: c::iovec,
_p: PhantomData<&'a [u8]>,
}
impl<'a> IoSlice<'a> {
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSlice.html#method.new>
#[inline]
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
IoSlice {
vec: c::iovec {
iov_base: buf.as_ptr() as *mut u8 as *mut c::c_void,
iov_len: buf.len() as _,
},
_p: PhantomData,
}
}
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSlice.html#method.advance>
#[inline]
pub fn advance(&mut self, n: usize) {
if self.vec.iov_len < n as _ {
panic!("advancing IoSlice beyond its length");
}
unsafe {
self.vec.iov_len -= n as __kernel_size_t;
self.vec.iov_base = self.vec.iov_base.add(n);
}
}
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSlice.html#method.as_slice>
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len as usize) }
}
}
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSliceMut.html>
#[repr(transparent)]
pub struct IoSliceMut<'a> {
vec: c::iovec,
_p: PhantomData<&'a mut [u8]>,
}
impl<'a> IoSliceMut<'a> {
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSliceMut.html#method.new>
#[inline]
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
IoSliceMut {
vec: c::iovec {
iov_base: buf.as_mut_ptr() as *mut c::c_void,
iov_len: buf.len() as _,
},
_p: PhantomData,
}
}
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSliceMut.html#method.advance>
#[inline]
pub fn advance(&mut self, n: usize) {
if self.vec.iov_len < n as _ {
panic!("advancing IoSliceMut beyond its length");
}
unsafe {
self.vec.iov_len -= n as __kernel_size_t;
self.vec.iov_base = self.vec.iov_base.add(n);
}
}
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSliceMut.html#method.as_slice>
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len as usize) }
}
/// <https://doc.rust-lang.org/nightly/std/io/struct.IoSliceMut.html#method.as_slice_mut>
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len as usize)
}
}
}

View file

@ -0,0 +1,7 @@
pub mod epoll;
pub(crate) mod errno;
#[cfg(not(feature = "std"))]
pub(crate) mod io_slice;
pub(crate) mod poll_fd;
pub(crate) mod syscalls;
pub(crate) mod types;

Some files were not shown because too many files have changed in this diff Show more