Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
406
third-party/vendor/psm/src/lib.rs
vendored
Normal file
406
third-party/vendor/psm/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
//! # **P**ortable **S**tack **M**anipulation
|
||||
//! This crate provides portable functions to control the stack pointer and inspect the properties
|
||||
//! of the stack. This crate does not attempt to provide safe abstractions to any operations, the
|
||||
//! only goals are correctness, portability and efficiency (in that exact order). As a consequence
|
||||
//! most functions you will find in this crate are unsafe.
|
||||
//!
|
||||
//! Note, that the stack allocation is left up to the user. Unless you’re writing a safe
|
||||
//! abstraction over stack manipulation, this is unlikely to be the crate you want. Instead
|
||||
//! consider one of the safe abstractions over this crate such as `stacker`. Another good place to
|
||||
//! look at is the crates.io’s reverse dependency list.
|
||||
|
||||
#![allow(unused_macros)]
|
||||
#![no_std]
|
||||
|
||||
macro_rules! extern_item {
|
||||
(unsafe $($toks: tt)+) => {
|
||||
unsafe extern "C" $($toks)+
|
||||
};
|
||||
($($toks: tt)+) => {
|
||||
extern "C" $($toks)+
|
||||
};
|
||||
}
|
||||
|
||||
// Surprising: turns out subsequent macro_rules! override previous definitions, instead of
|
||||
// erroring? Convenient for us in this case, though.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
macro_rules! extern_item {
|
||||
(unsafe $($toks: tt)+) => {
|
||||
unsafe extern "sysv64" $($toks)+
|
||||
};
|
||||
($($toks: tt)+) => {
|
||||
extern "sysv64" $($toks)+
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
macro_rules! extern_item {
|
||||
(unsafe $($toks: tt)+) => {
|
||||
unsafe extern "fastcall" $($toks)+
|
||||
};
|
||||
($($toks: tt)+) => {
|
||||
extern "fastcall" $($toks)+
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
macro_rules! extern_item {
|
||||
(unsafe $($toks: tt)+) => {
|
||||
unsafe extern "aapcs" $($toks)+
|
||||
};
|
||||
($($toks: tt)+) => {
|
||||
extern "aapcs" $($toks)+
|
||||
};
|
||||
}
|
||||
|
||||
// NB: this could be nicer across multiple blocks but we cannot do it because of
|
||||
// https://github.com/rust-lang/rust/issues/65847
|
||||
extern_item! { {
|
||||
#![cfg_attr(asm, link(name="psm_s"))]
|
||||
|
||||
#[cfg(asm)]
|
||||
fn rust_psm_stack_direction() -> u8;
|
||||
#[cfg(asm)]
|
||||
fn rust_psm_stack_pointer() -> *mut u8;
|
||||
|
||||
#[cfg(all(switchable_stack, not(target_os = "windows")))]
|
||||
#[link_name="rust_psm_replace_stack"]
|
||||
fn _rust_psm_replace_stack(
|
||||
data: usize,
|
||||
callback: extern_item!(unsafe fn(usize) -> !),
|
||||
sp: *mut u8
|
||||
) -> !;
|
||||
#[cfg(all(switchable_stack, not(target_os = "windows")))]
|
||||
#[link_name="rust_psm_on_stack"]
|
||||
fn _rust_psm_on_stack(
|
||||
data: usize,
|
||||
return_ptr: usize,
|
||||
callback: extern_item!(unsafe fn(usize, usize)),
|
||||
sp: *mut u8,
|
||||
);
|
||||
#[cfg(all(switchable_stack, target_os = "windows"))]
|
||||
fn rust_psm_replace_stack(
|
||||
data: usize,
|
||||
callback: extern_item!(unsafe fn(usize) -> !),
|
||||
sp: *mut u8,
|
||||
stack_base: *mut u8
|
||||
) -> !;
|
||||
#[cfg(all(switchable_stack, target_os = "windows"))]
|
||||
fn rust_psm_on_stack(
|
||||
data: usize,
|
||||
return_ptr: usize,
|
||||
callback: extern_item!(unsafe fn(usize, usize)),
|
||||
sp: *mut u8,
|
||||
stack_base: *mut u8
|
||||
);
|
||||
} }
|
||||
|
||||
#[cfg(all(switchable_stack, not(target_os = "windows")))]
|
||||
#[inline(always)]
|
||||
unsafe fn rust_psm_replace_stack(
|
||||
data: usize,
|
||||
callback: extern_item!(unsafe fn(usize) -> !),
|
||||
sp: *mut u8,
|
||||
_: *mut u8,
|
||||
) -> ! {
|
||||
_rust_psm_replace_stack(data, callback, sp)
|
||||
}
|
||||
|
||||
#[cfg(all(switchable_stack, not(target_os = "windows")))]
|
||||
#[inline(always)]
|
||||
unsafe fn rust_psm_on_stack(
|
||||
data: usize,
|
||||
return_ptr: usize,
|
||||
callback: extern_item!(unsafe fn(usize, usize)),
|
||||
sp: *mut u8,
|
||||
_: *mut u8,
|
||||
) {
|
||||
_rust_psm_on_stack(data, return_ptr, callback, sp)
|
||||
}
|
||||
|
||||
/// Run the closure on the provided stack.
|
||||
///
|
||||
/// Once the closure completes its execution, the original stack pointer is restored and execution
|
||||
/// returns to the caller.
|
||||
///
|
||||
/// `base` address must be the low address of the stack memory region, regardless of the stack
|
||||
/// growth direction. It is not necessary for the whole region `[base; base + size]` to be usable
|
||||
/// at the time this function called, however it is required that at least the following hold:
|
||||
///
|
||||
/// * Both `base` and `base + size` are aligned up to the target-specific requirements;
|
||||
/// * Depending on `StackDirection` value for the platform, the end of the stack memory region,
|
||||
/// which would end up containing the first frame(s), must have sufficient number of pages
|
||||
/// allocated to execute code until more pages are commited. The other end should contain a guard
|
||||
/// page (not writable, readable or executable) to ensure Rust’s soundness guarantees.
|
||||
///
|
||||
/// Note, that some or all of these considerations are irrelevant to some applications. For
|
||||
/// example, Rust’s soundness story relies on all stacks having a guard-page, however if the user
|
||||
/// is able to guarantee that the memory region used for stack cannot be exceeded, a guard page may
|
||||
/// end up being an expensive unnecessity.
|
||||
///
|
||||
/// The previous stack may not be deallocated. If an ability to deallocate the old stack is desired
|
||||
/// consider `replace_stack` instead.
|
||||
///
|
||||
/// # Guidelines
|
||||
///
|
||||
/// Memory regions that are aligned to a single page (usually 4kB) are an extremely portable choice
|
||||
/// for stacks.
|
||||
///
|
||||
/// Allocate at least 4kB of stack. Some architectures (such as SPARC) consume stack memory
|
||||
/// significantly faster compared to the more usual architectures such as x86 or ARM. Allocating
|
||||
/// less than 4kB of memory may make it impossible to commit more pages without overflowing the
|
||||
/// stack later on.
|
||||
///
|
||||
/// # Unsafety
|
||||
///
|
||||
/// The stack `base` address must be aligned as appropriate for the target.
|
||||
///
|
||||
/// The stack `size` must be a multiple of stack alignment required by target.
|
||||
///
|
||||
/// The `size` must not overflow `isize`.
|
||||
///
|
||||
/// `callback` must not unwind or return control flow by any other means than directly returning.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::alloc;
|
||||
/// const STACK_ALIGN: usize = 4096;
|
||||
/// const STACK_SIZE: usize = 4096;
|
||||
/// unsafe {
|
||||
/// let layout = alloc::Layout::from_size_align(STACK_SIZE, STACK_ALIGN).unwrap();
|
||||
/// let new_stack = alloc::alloc(layout);
|
||||
/// assert!(!new_stack.is_null(), "allocations must succeed!");
|
||||
/// let (stack, result) = psm::on_stack(new_stack, STACK_SIZE, || {
|
||||
/// (psm::stack_pointer(), 4 + 4)
|
||||
/// });
|
||||
/// println!("4 + 4 = {} has been calculated on stack {:p}", result, stack);
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(switchable_stack)]
|
||||
pub unsafe fn on_stack<R, F: FnOnce() -> R>(base: *mut u8, size: usize, callback: F) -> R {
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
extern_item! {
|
||||
unsafe fn with_on_stack<R, F: FnOnce() -> R>(callback_ptr: usize, return_ptr: usize) {
|
||||
let return_ptr = (*(return_ptr as *mut MaybeUninit<R>)).as_mut_ptr();
|
||||
let callback = (*(callback_ptr as *mut MaybeUninit<F>)).as_ptr();
|
||||
// Safe to move out from `F`, because closure in is forgotten in `on_stack` and dropping
|
||||
// only occurs in this callback.
|
||||
return_ptr.write((callback.read())());
|
||||
}
|
||||
}
|
||||
let sp = match StackDirection::new() {
|
||||
StackDirection::Ascending => base,
|
||||
StackDirection::Descending => base.offset(size as isize),
|
||||
};
|
||||
let mut callback: MaybeUninit<F> = MaybeUninit::new(callback);
|
||||
let mut return_value: MaybeUninit<R> = MaybeUninit::uninit();
|
||||
rust_psm_on_stack(
|
||||
&mut callback as *mut MaybeUninit<F> as usize,
|
||||
&mut return_value as *mut MaybeUninit<R> as usize,
|
||||
with_on_stack::<R, F>,
|
||||
sp,
|
||||
base,
|
||||
);
|
||||
return return_value.assume_init();
|
||||
}
|
||||
|
||||
/// Run the provided non-terminating computation on an entirely new stack.
|
||||
///
|
||||
/// `base` address must be the low address of the stack memory region, regardless of the stack
|
||||
/// growth direction. It is not necessary for the whole region `[base; base + size]` to be usable
|
||||
/// at the time this function called, however it is required that at least the following hold:
|
||||
///
|
||||
/// * Both `base` and `base + size` are aligned up to the target-specific requirements;
|
||||
/// * Depending on `StackDirection` value for the platform, the end of the stack memory region,
|
||||
/// which would end up containing the first frame(s), must have sufficient number of pages
|
||||
/// allocated to execute code until more pages are commited. The other end should contain a guard
|
||||
/// page (not writable, readable or executable) to ensure Rust’s soundness guarantees.
|
||||
///
|
||||
/// Note, that some or all of these considerations are irrelevant to some applications. For
|
||||
/// example, Rust’s soundness story relies on all stacks having a guard-page, however if the user
|
||||
/// is able to guarantee that the memory region used for stack cannot be exceeded, a guard page may
|
||||
/// end up being an expensive unnecessity.
|
||||
///
|
||||
/// The previous stack is not deallocated and may not be deallocated unless the data on the old
|
||||
/// stack is not referenced in any way (by e.g. the `callback` closure).
|
||||
///
|
||||
/// On platforms where multiple stack pointers are available, the “current” stack pointer is
|
||||
/// replaced.
|
||||
///
|
||||
/// # Guidelines
|
||||
///
|
||||
/// Memory regions that are aligned to a single page (usually 4kB) are an extremely portable choice
|
||||
/// for stacks.
|
||||
///
|
||||
/// Allocate at least 4kB of stack. Some architectures (such as SPARC) consume stack memory
|
||||
/// significantly faster compared to the more usual architectures such as x86 or ARM. Allocating
|
||||
/// less than 4kB of memory may make it impossible to commit more pages without overflowing the
|
||||
/// stack later on.
|
||||
///
|
||||
/// # Unsafety
|
||||
///
|
||||
/// The stack `base` address must be aligned as appropriate for the target.
|
||||
///
|
||||
/// The stack `size` must be a multiple of stack alignment required by target.
|
||||
///
|
||||
/// The `size` must not overflow `isize`.
|
||||
///
|
||||
/// `callback` must not return (not enforced by typesystem currently because `!` is unstable),
|
||||
/// unwind or otherwise return control flow to any of the previous frames.
|
||||
#[cfg(switchable_stack)]
|
||||
pub unsafe fn replace_stack<F: FnOnce()>(base: *mut u8, size: usize, callback: F) -> ! {
|
||||
extern_item! { unsafe fn with_replaced_stack<F: FnOnce()>(d: usize) -> ! {
|
||||
// Safe to move out, because the closure is essentially forgotten by
|
||||
// this being required to never return...
|
||||
::core::ptr::read(d as *const F)();
|
||||
::core::hint::unreachable_unchecked();
|
||||
} }
|
||||
let sp = match StackDirection::new() {
|
||||
StackDirection::Ascending => base,
|
||||
StackDirection::Descending => base.offset(size as isize),
|
||||
};
|
||||
rust_psm_replace_stack(
|
||||
&callback as *const F as usize,
|
||||
with_replaced_stack::<F>,
|
||||
sp,
|
||||
base,
|
||||
);
|
||||
}
|
||||
|
||||
/// The direction into which stack grows as stack frames are made.
|
||||
///
|
||||
/// This is a target-specific property that can be obtained at runtime by calling
|
||||
/// `StackDirection::new()`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum StackDirection {
|
||||
Ascending = 1,
|
||||
Descending = 2,
|
||||
}
|
||||
|
||||
impl StackDirection {
|
||||
/// Obtain the stack growth direction.
|
||||
#[cfg(asm)]
|
||||
pub fn new() -> StackDirection {
|
||||
const ASC: u8 = StackDirection::Ascending as u8;
|
||||
const DSC: u8 = StackDirection::Descending as u8;
|
||||
unsafe {
|
||||
match rust_psm_stack_direction() {
|
||||
ASC => StackDirection::Ascending,
|
||||
DSC => StackDirection::Descending,
|
||||
_ => ::core::hint::unreachable_unchecked(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns current stack pointer.
|
||||
///
|
||||
/// Note, that the stack pointer returned is from the perspective of the caller. From the
|
||||
/// perspective of `stack_pointer` function the pointer returned is the frame pointer.
|
||||
///
|
||||
/// While it is a goal to minimize the amount of stack used by this function, implementations for
|
||||
/// some targets may be unable to avoid allocating a stack frame. This makes this function
|
||||
/// suitable for stack exhaustion detection only in conjunction with sufficient padding.
|
||||
///
|
||||
/// Using `stack_pointer` to check for stack exhaustion is tricky to get right. It is impossible to
|
||||
/// know the callee’s frame size, therefore such value must be derived some other way. A common
|
||||
/// approach is to use stack padding (reserve enough stack space for any function to be called) and
|
||||
/// check against the padded threshold. If padding is chosen incorrectly, a situation similar to
|
||||
/// one described below may occur:
|
||||
///
|
||||
/// 1. For stack exhaustion check, remaining stack is checked against `stack_pointer` with the
|
||||
/// padding applied;
|
||||
/// 2. Callee allocates more stack than was accounted for with padding, and accesses pages outside
|
||||
/// the stack, invalidating the execution (by e.g. crashing).
|
||||
#[cfg(asm)]
|
||||
pub fn stack_pointer() -> *mut u8 {
|
||||
unsafe { rust_psm_stack_pointer() }
|
||||
}
|
||||
|
||||
/// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use psm::psm_stack_manipulation;
|
||||
/// psm_stack_manipulation! {
|
||||
/// yes {
|
||||
/// /* Functions `on_stack` and `replace_stack` are available here */
|
||||
/// }
|
||||
/// no {
|
||||
/// /* Functions `on_stack` and `replace_stack` are not available here */
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(switchable_stack)]
|
||||
#[macro_export]
|
||||
macro_rules! psm_stack_manipulation {
|
||||
(yes { $($yes: tt)* } no { $($no: tt)* }) => { $($yes)* };
|
||||
}
|
||||
|
||||
/// Macro that outputs its tokens only if `psm::on_stack` and `psm::replace_stack` are available.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use psm::psm_stack_manipulation;
|
||||
/// psm_stack_manipulation! {
|
||||
/// yes {
|
||||
/// /* Functions `on_stack` and `replace_stack` are available here */
|
||||
/// }
|
||||
/// no {
|
||||
/// /* Functions `on_stack` and `replace_stack` are not available here */
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(not(switchable_stack))]
|
||||
#[macro_export]
|
||||
macro_rules! psm_stack_manipulation {
|
||||
(yes { $($yes: tt)* } no { $($no: tt)* }) => { $($no)* };
|
||||
}
|
||||
|
||||
/// Macro that outputs its tokens only if `psm::stack_pointer` and `psm::StackDirection::new` are
|
||||
/// available.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use psm::psm_stack_information;
|
||||
/// psm_stack_information! {
|
||||
/// yes {
|
||||
/// /* `psm::stack_pointer` and `psm::StackDirection::new` are available here */
|
||||
/// }
|
||||
/// no {
|
||||
/// /* `psm::stack_pointer` and `psm::StackDirection::new` are not available here */
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(asm)]
|
||||
#[macro_export]
|
||||
macro_rules! psm_stack_information {
|
||||
(yes { $($yes: tt)* } no { $($no: tt)* }) => { $($yes)* };
|
||||
}
|
||||
|
||||
/// Macro that outputs its tokens only if `psm::stack_pointer` and `psm::StackDirection::new` are
|
||||
/// available.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use psm::psm_stack_information;
|
||||
/// psm_stack_information! {
|
||||
/// yes {
|
||||
/// /* `psm::stack_pointer` and `psm::StackDirection::new` are available here */
|
||||
/// }
|
||||
/// no {
|
||||
/// /* `psm::stack_pointer` and `psm::StackDirection::new` are not available here */
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(not(asm))]
|
||||
#[macro_export]
|
||||
macro_rules! psm_stack_information {
|
||||
(yes { $($yes: tt)* } no { $($no: tt)* }) => { $($no)* };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue