diff options
Diffstat (limited to 'vendor/rustix/src/backend/linux_raw/runtime')
-rw-r--r-- | vendor/rustix/src/backend/linux_raw/runtime/mod.rs | 2 | ||||
-rw-r--r-- | vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs | 318 | ||||
-rw-r--r-- | vendor/rustix/src/backend/linux_raw/runtime/tls.rs | 98 |
3 files changed, 418 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/linux_raw/runtime/mod.rs b/vendor/rustix/src/backend/linux_raw/runtime/mod.rs new file mode 100644 index 0000000..0b48649 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/runtime/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod syscalls; +pub(crate) mod tls; diff --git a/vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs b/vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs new file mode 100644 index 0000000..e00acc6 --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/runtime/syscalls.rs @@ -0,0 +1,318 @@ +//! linux_raw syscalls supporting `rustix::runtime`. +//! +//! # Safety +//! +//! See the `rustix::backend` module documentation for details. +#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] + +use crate::backend::c; +#[cfg(target_arch = "x86")] +use crate::backend::conv::by_mut; +#[cfg(target_arch = "x86_64")] +use crate::backend::conv::c_uint; +use crate::backend::conv::{ + by_ref, c_int, ret, ret_c_int, ret_c_int_infallible, ret_error, ret_infallible, ret_void_star, + size_of, zero, +}; +#[cfg(feature = "fs")] +use crate::fd::BorrowedFd; +use crate::ffi::CStr; +#[cfg(feature = "fs")] +use crate::fs::AtFlags; +use crate::io; +use crate::pid::{Pid, RawPid}; +use crate::runtime::{Fork, How, Sigaction, Siginfo, Sigset, Stack}; +use crate::signal::Signal; +use crate::timespec::Timespec; +use crate::utils::option_as_ptr; +use core::ffi::c_void; +use core::mem::MaybeUninit; +#[cfg(target_pointer_width = "32")] +use linux_raw_sys::general::__kernel_old_timespec; +use linux_raw_sys::general::kernel_sigset_t; +#[cfg(target_arch = "x86_64")] +use linux_raw_sys::general::ARCH_SET_FS; + +#[inline] +pub(crate) unsafe fn fork() -> io::Result<Fork> { + let mut child_pid = MaybeUninit::<RawPid>::uninit(); + + // Unix `fork` only returns the child PID in the parent; we'd like it in + // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of + // a memory location to store it to in the child. + // + // Architectures differ on the order of the parameters. + #[cfg(target_arch = "x86_64")] + let pid = ret_c_int(syscall_readonly!( + __NR_clone, + c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID), + zero(), + zero(), + &mut child_pid, + zero() + ))?; + #[cfg(any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "x86" + ))] + let pid = ret_c_int(syscall_readonly!( + __NR_clone, + c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID), + zero(), + zero(), + zero(), + &mut child_pid + ))?; + + Ok(if let Some(pid) = Pid::from_raw(pid) { + Fork::Parent(pid) + } else { + Fork::Child(Pid::from_raw_unchecked(child_pid.assume_init())) + }) +} + +#[cfg(feature = "fs")] +pub(crate) unsafe fn execveat( + dirfd: BorrowedFd<'_>, + path: &CStr, + args: *const *const u8, + env_vars: *const *const u8, + flags: AtFlags, +) -> io::Errno { + ret_error(syscall_readonly!( + __NR_execveat, + dirfd, + path, + args, + env_vars, + flags + )) +} + +pub(crate) unsafe fn execve( + path: &CStr, + args: *const *const u8, + env_vars: *const *const u8, +) -> io::Errno { + ret_error(syscall_readonly!(__NR_execve, path, args, env_vars)) +} + +pub(crate) mod tls { + use super::*; + #[cfg(target_arch = "x86")] + use crate::backend::runtime::tls::UserDesc; + + #[cfg(target_arch = "x86")] + #[inline] + pub(crate) unsafe fn set_thread_area(u_info: &mut UserDesc) -> io::Result<()> { + ret(syscall!(__NR_set_thread_area, by_mut(u_info))) + } + + #[cfg(target_arch = "arm")] + #[inline] + pub(crate) unsafe fn arm_set_tls(data: *mut c::c_void) -> io::Result<()> { + ret(syscall_readonly!(__ARM_NR_set_tls, data)) + } + + #[cfg(target_arch = "x86_64")] + #[inline] + pub(crate) unsafe fn set_fs(data: *mut c::c_void) { + ret_infallible(syscall_readonly!( + __NR_arch_prctl, + c_uint(ARCH_SET_FS), + data, + zero(), + zero(), + zero() + )) + } + + #[inline] + pub(crate) unsafe fn set_tid_address(data: *mut c::c_void) -> Pid { + let tid: i32 = ret_c_int_infallible(syscall_readonly!(__NR_set_tid_address, data)); + Pid::from_raw_unchecked(tid) + } + + #[inline] + pub(crate) fn exit_thread(code: c::c_int) -> ! { + unsafe { syscall_noreturn!(__NR_exit, c_int(code)) } + } +} + +#[inline] +pub(crate) unsafe fn sigaction(signal: Signal, new: Option<Sigaction>) -> io::Result<Sigaction> { + let mut old = MaybeUninit::<Sigaction>::uninit(); + let new = option_as_ptr(new.as_ref()); + ret(syscall!( + __NR_rt_sigaction, + signal, + new, + &mut old, + size_of::<kernel_sigset_t, _>() + ))?; + Ok(old.assume_init()) +} + +#[inline] +pub(crate) unsafe fn sigaltstack(new: Option<Stack>) -> io::Result<Stack> { + let mut old = MaybeUninit::<Stack>::uninit(); + let new = option_as_ptr(new.as_ref()); + ret(syscall!(__NR_sigaltstack, new, &mut old))?; + Ok(old.assume_init()) +} + +#[inline] +pub(crate) unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { + ret(syscall_readonly!(__NR_tkill, tid, sig)) +} + +#[inline] +pub(crate) unsafe fn sigprocmask(how: How, new: Option<&Sigset>) -> io::Result<Sigset> { + let mut old = MaybeUninit::<Sigset>::uninit(); + let new = option_as_ptr(new); + ret(syscall!( + __NR_rt_sigprocmask, + how, + new, + &mut old, + size_of::<kernel_sigset_t, _>() + ))?; + Ok(old.assume_init()) +} + +#[inline] +pub(crate) fn sigpending() -> Sigset { + let mut pending = MaybeUninit::<Sigset>::uninit(); + unsafe { + ret_infallible(syscall!( + __NR_rt_sigpending, + &mut pending, + size_of::<kernel_sigset_t, _>() + )); + pending.assume_init() + } +} + +#[inline] +pub(crate) fn sigsuspend(set: &Sigset) -> io::Result<()> { + unsafe { + ret(syscall_readonly!( + __NR_rt_sigsuspend, + by_ref(set), + size_of::<kernel_sigset_t, _>() + )) + } +} + +#[inline] +pub(crate) fn sigwait(set: &Sigset) -> io::Result<Signal> { + unsafe { + match Signal::from_raw(ret_c_int(syscall_readonly!( + __NR_rt_sigtimedwait, + by_ref(set), + zero(), + zero(), + size_of::<kernel_sigset_t, _>() + ))?) { + Some(signum) => Ok(signum), + None => Err(io::Errno::NOTSUP), + } + } +} + +#[inline] +pub(crate) fn sigwaitinfo(set: &Sigset) -> io::Result<Siginfo> { + let mut info = MaybeUninit::<Siginfo>::uninit(); + unsafe { + let _signum = ret_c_int(syscall!( + __NR_rt_sigtimedwait, + by_ref(set), + &mut info, + zero(), + size_of::<kernel_sigset_t, _>() + ))?; + Ok(info.assume_init()) + } +} + +#[inline] +pub(crate) fn sigtimedwait(set: &Sigset, timeout: Option<Timespec>) -> io::Result<Siginfo> { + let mut info = MaybeUninit::<Siginfo>::uninit(); + let timeout_ptr = option_as_ptr(timeout.as_ref()); + + // `rt_sigtimedwait_time64` was introduced in Linux 5.1. The old + // `rt_sigtimedwait` syscall is not y2038-compatible on 32-bit + // architectures. + #[cfg(target_pointer_width = "32")] + unsafe { + match ret_c_int(syscall!( + __NR_rt_sigtimedwait_time64, + by_ref(set), + &mut info, + timeout_ptr, + size_of::<kernel_sigset_t, _>() + )) { + Ok(_signum) => (), + Err(io::Errno::NOSYS) => sigtimedwait_old(set, timeout, &mut info)?, + Err(err) => return Err(err), + } + Ok(info.assume_init()) + } + + #[cfg(target_pointer_width = "64")] + unsafe { + let _signum = ret_c_int(syscall!( + __NR_rt_sigtimedwait, + by_ref(set), + &mut info, + timeout_ptr, + size_of::<kernel_sigset_t, _>() + ))?; + Ok(info.assume_init()) + } +} + +#[cfg(target_pointer_width = "32")] +unsafe fn sigtimedwait_old( + set: &Sigset, + timeout: Option<Timespec>, + info: &mut MaybeUninit<Siginfo>, +) -> io::Result<()> { + let old_timeout = match timeout { + Some(timeout) => Some(__kernel_old_timespec { + tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?, + tv_nsec: timeout.tv_nsec as _, + }), + None => None, + }; + + let old_timeout_ptr = option_as_ptr(old_timeout.as_ref()); + + let _signum = ret_c_int(syscall!( + __NR_rt_sigtimedwait, + by_ref(set), + info, + old_timeout_ptr, + size_of::<kernel_sigset_t, _>() + ))?; + + Ok(()) +} + +#[inline] +pub(crate) fn exit_group(code: c::c_int) -> ! { + unsafe { syscall_noreturn!(__NR_exit_group, c_int(code)) } +} + +#[inline] +pub(crate) unsafe fn brk(addr: *mut c::c_void) -> io::Result<*mut c_void> { + // This is non-`readonly`, to prevent loads from being reordered past it. + ret_void_star(syscall!(__NR_brk, addr)) +} diff --git a/vendor/rustix/src/backend/linux_raw/runtime/tls.rs b/vendor/rustix/src/backend/linux_raw/runtime/tls.rs new file mode 100644 index 0000000..69bd5ce --- /dev/null +++ b/vendor/rustix/src/backend/linux_raw/runtime/tls.rs @@ -0,0 +1,98 @@ +//! TLS utilities. +//! +//! # Safety +//! +//! This file contains code that reads the raw phdr array pointed to by the +//! kernel-provided AUXV values. +#![allow(unsafe_code)] + +use crate::backend::c; +use crate::backend::param::auxv::exe_phdrs; +use core::arch::global_asm; +use core::ptr::{null, NonNull}; +use linux_raw_sys::elf::*; + +/// For use with [`set_thread_area`]. +/// +/// [`set_thread_area`]: crate::runtime::set_thread_area +#[cfg(target_arch = "x86")] +pub type UserDesc = linux_raw_sys::general::user_desc; + +pub(crate) fn startup_tls_info() -> StartupTlsInfo { + let mut base = null(); + let mut tls_phdr = null(); + let mut stack_size = 0; + + let (first_phdr, phent, phnum) = exe_phdrs(); + let mut current_phdr = first_phdr.cast::<Elf_Phdr>(); + + // The dynamic address of the dynamic section, which we can compare with + // the `PT_DYNAMIC` header's static address, if present. + let dynamic_addr: *const c::c_void = unsafe { &_DYNAMIC }; + + // SAFETY: We assume the phdr array pointer and length the kernel provided + // to the process describe a valid phdr array. + unsafe { + let phdrs_end = current_phdr.cast::<u8>().add(phnum * phent).cast(); + while current_phdr != phdrs_end { + let phdr = &*current_phdr; + current_phdr = current_phdr.cast::<u8>().add(phent).cast(); + + match phdr.p_type { + // Compute the offset from the static virtual addresses + // in the `p_vaddr` fields to the dynamic addresses. We don't + // always get a `PT_PHDR` or `PT_DYNAMIC` header, so use + // whichever one we get. + PT_PHDR => base = first_phdr.cast::<u8>().wrapping_sub(phdr.p_vaddr), + PT_DYNAMIC => base = dynamic_addr.cast::<u8>().wrapping_sub(phdr.p_vaddr), + + PT_TLS => tls_phdr = phdr, + PT_GNU_STACK => stack_size = phdr.p_memsz, + _ => {} + } + } + + if tls_phdr.is_null() { + StartupTlsInfo { + addr: NonNull::dangling().as_ptr(), + mem_size: 0, + file_size: 0, + align: 1, + stack_size: 0, + } + } else { + StartupTlsInfo { + addr: base.cast::<u8>().wrapping_add((*tls_phdr).p_vaddr).cast(), + mem_size: (*tls_phdr).p_memsz, + file_size: (*tls_phdr).p_filesz, + align: (*tls_phdr).p_align, + stack_size, + } + } + } +} + +extern "C" { + /// Declare the `_DYNAMIC` symbol so that we can compare its address with + /// the static address in the `PT_DYNAMIC` header to learn our offset. Use + /// a weak symbol because `_DYNAMIC` is not always present. + static _DYNAMIC: c::c_void; +} +// Rust has `extern_weak` but it isn't stable, so use a `global_asm`. +global_asm!(".weak _DYNAMIC"); + +/// The values returned from [`startup_tls_info`]. +/// +/// [`startup_tls_info`]: crate::runtime::startup_tls_info +pub struct StartupTlsInfo { + /// The base address of the TLS segment. + pub addr: *const c::c_void, + /// The size of the memory region. + pub mem_size: usize, + /// The size beyond which all memory is zero-initialized. + pub file_size: usize, + /// The required alignment for the TLS segment. + pub align: usize, + /// The requested minimum size for stacks. + pub stack_size: usize, +} |