Update to latest upstream

This commit is contained in:
pjht 2024-03-29 15:59:15 -05:00
commit 30cff68007
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
28 changed files with 1084 additions and 297 deletions

35
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: CI
on: [push, pull_request]
env:
CARGO_TERM_COLOR: always
jobs:
test:
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
- i686-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- riscv64gc-unknown-linux-gnu
- riscv32gc-unknown-linux-gnu
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Rust
run: |
rustup update nightly
rustup default nightly
- name: Install cross-compilation tools
uses: taiki-e/setup-cross-toolchain-action@v1
with:
target: ${{ matrix.target }}
- name: Build library
run: cargo build --release $BUILD_STD
- name: Run tests
run: cargo test --release $BUILD_STD

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
.vscode/ .vscode/
target target
Cargo.lock

87
Cargo.lock generated Normal file
View File

@ -0,0 +1,87 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "catch_std_exception"
version = "0.1.0"
dependencies = [
"libc",
"unwinding",
]
[[package]]
name = "compiler_builtins"
version = "0.1.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a6d58e9c3408138099a396a98fd0d0e6cfb25d723594d2ae48b5004513fd5b"
[[package]]
name = "gimli"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "rustc-std-workspace-alloc"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff66d57013a5686e1917ed6a025d54dd591fcda71a41fe07edf4d16726aefa86"
[[package]]
name = "rustc-std-workspace-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "std_catch_exception"
version = "0.1.0"
dependencies = [
"libc",
"unwinding",
]
[[package]]
name = "throw_and_catch"
version = "0.1.0"
dependencies = [
"libc",
"unwinding",
]
[[package]]
name = "unwinding"
version = "0.2.1"
dependencies = [
"compiler_builtins",
"gimli",
"libc",
"rustc-std-workspace-core",
"spin",
]
[[package]]
name = "unwinding_dyn"
version = "0.1.0"
dependencies = [
"libc",
"unwinding",
]

View File

@ -1,16 +1,18 @@
[package] [package]
name = "unwinding" name = "unwinding"
version = "0.1.5" version = "0.2.1"
authors = ["Gary Guo <gary@garyguo.net>"] authors = ["Gary Guo <gary@garyguo.net>"]
edition = "2018" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "Unwinding library in Rust and for Rust" description = "Unwinding library in Rust and for Rust"
repository = "https://github.com/nbdd0121/unwinding/" repository = "https://github.com/nbdd0121/unwinding/"
[dependencies] [dependencies]
gimli = { version = "0.26.1", default-features = false, features = ["read-core"] } gimli = { version = "0.28", default-features = false, features = ["read-core"] }
libc = { version = "0.2", optional = true } libc = { version = "0.2", optional = true }
spin = { version = "0.9", optional = true, default-features = false, features = ["mutex", "spin_mutex"] } spin = { version = "0.9.8", optional = true, default-features = false, features = ["mutex", "spin_mutex"] }
core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' }
compiler_builtins = { version = "0.1.2", optional = true }
[features] [features]
alloc = [] alloc = []
@ -21,6 +23,7 @@ fde-phdr-aux = ["fde-phdr"]
fde-registry = ["alloc"] fde-registry = ["alloc"]
fde-static = [] fde-static = []
fde-gnu-eh-frame-hdr = [] fde-gnu-eh-frame-hdr = []
fde-custom = []
dwarf-expr = [] dwarf-expr = []
hide-trace = [] hide-trace = []
personality = [] personality = []
@ -32,6 +35,7 @@ panic-handler = ["print", "panic"]
panic-handler-dummy = [] panic-handler-dummy = []
system-alloc = [] system-alloc = []
default = ["unwinder", "dwarf-expr", "hide-trace", "fde-phdr-dl", "fde-registry"] default = ["unwinder", "dwarf-expr", "hide-trace", "fde-phdr-dl", "fde-registry"]
rustc-dep-of-std = ["core", "gimli/rustc-dep-of-std", "compiler_builtins"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["panic-handler"] features = ["panic-handler"]

View File

@ -9,7 +9,7 @@ This library serves two purposes:
1. Provide a pure Rust alternative to libgcc_eh or libunwind. 1. Provide a pure Rust alternative to libgcc_eh or libunwind.
2. Provide easier unwinding support for `#![no_std]` targets. 2. Provide easier unwinding support for `#![no_std]` targets.
Currently supports x86_64, x86, RV64 and AArch64. Currently supports x86_64, x86, RV64, RV32 and AArch64.
## Unwinder ## Unwinder
@ -23,6 +23,7 @@ The unwinder can be enabled with `unwinder` feature. Here are the feature gates
| fde-registry | Yes | Provide `__register__frame` and others for dynamic registration. Requires either `libc` or `spin` for a mutex implementation. | | fde-registry | Yes | Provide `__register__frame` and others for dynamic registration. Requires either `libc` or `spin` for a mutex implementation. |
| fde-gnu-eh-frame-hdr | No | Use `__executable_start`, `__etext` and `__GNU_EH_FRAME_HDR` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one is provided if GNU LD is used and --eh-frame-hdr option is enabled. | | fde-gnu-eh-frame-hdr | No | Use `__executable_start`, `__etext` and `__GNU_EH_FRAME_HDR` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one is provided if GNU LD is used and --eh-frame-hdr option is enabled. |
| fde-static | No | Use `__executable_start`, `__etext` and `__eh_frame` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one would need to be provided by the user via linker script. | | fde-static | No | Use `__executable_start`, `__etext` and `__eh_frame` to retrieve frame unwind table. The former two symbols are usually provided by the linker, while the last one would need to be provided by the user via linker script. |
| fde-custom | No | Allow the program to provide a custom means of retrieving frame unwind table at runtime via the `set_custom_eh_frame_finder` function. |
| dwarf-expr | Yes | Enable the dwarf expression evaluator. Usually not necessary for Rust | | dwarf-expr | Yes | Enable the dwarf expression evaluator. Usually not necessary for Rust |
| hide-trace | Yes | Hide unwinder frames in back trace | | hide-trace | Yes | Hide unwinder frames in back trace |

2
rust-toolchain Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View File

@ -60,11 +60,11 @@ impl UnwindAction {
pub type UnwindExceptionCleanupFn = unsafe extern "C" fn(UnwindReasonCode, *mut UnwindException); pub type UnwindExceptionCleanupFn = unsafe extern "C" fn(UnwindReasonCode, *mut UnwindException);
pub type UnwindStopFn = extern "C" fn( pub type UnwindStopFn = unsafe extern "C" fn(
c_int, c_int,
UnwindAction, UnwindAction,
u64, u64,
&mut UnwindException, *mut UnwindException,
&mut UnwindContext<'_>, &mut UnwindContext<'_>,
*mut c_void, *mut c_void,
) -> UnwindReasonCode; ) -> UnwindReasonCode;
@ -78,7 +78,7 @@ pub struct UnwindException {
} }
pub type UnwindTraceFn = pub type UnwindTraceFn =
extern "C" fn(ctx: &mut UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode; extern "C" fn(ctx: &UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode;
#[cfg(not(feature = "unwinder"))] #[cfg(not(feature = "unwinder"))]
#[repr(C)] #[repr(C)]
@ -87,31 +87,49 @@ pub struct UnwindContext<'a> {
phantom: core::marker::PhantomData<&'a ()>, phantom: core::marker::PhantomData<&'a ()>,
} }
pub type PersonalityRoutine = extern "C" fn( pub type PersonalityRoutine = unsafe extern "C" fn(
c_int, c_int,
UnwindAction, UnwindAction,
u64, u64,
&mut UnwindException, *mut UnwindException,
&mut UnwindContext<'_>, &mut UnwindContext<'_>,
) -> UnwindReasonCode; ) -> UnwindReasonCode;
#[cfg(not(feature = "unwinder"))] #[cfg(not(feature = "unwinder"))]
macro_rules! binding { macro_rules! binding {
($(extern $abi: literal $([$t:tt])? fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?;)*) => { () => {};
$( (unsafe extern $abi: literal fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?; $($rest: tt)*) => {
#[allow(non_snake_case)] extern $abi {
#[inline] pub fn $name($($arg: $arg_ty),*) $(-> $ret)?;
pub $($t)? fn $name($($arg: $arg_ty),*) $(-> $ret)? { }
extern $abi { binding!($($rest)*);
fn $name($($arg: $arg_ty),*) $(-> $ret)?; };
}
unsafe { $name($($arg),*) } (extern $abi: literal fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?; $($rest: tt)*) => {
#[allow(non_snake_case)]
#[inline]
pub fn $name($($arg: $arg_ty),*) $(-> $ret)? {
extern $abi {
fn $name($($arg: $arg_ty),*) $(-> $ret)?;
} }
)* unsafe { $name($($arg),*) }
}
binding!($($rest)*);
};
}
#[cfg(feature = "unwinder")]
macro_rules! binding {
() => {};
(unsafe extern $abi: literal fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?; $($rest: tt)*) => {
const _: unsafe extern $abi fn($($arg_ty),*) $(-> $ret)? = $name;
};
(extern $abi: literal fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?; $($rest: tt)*) => {
const _: extern $abi fn($($arg_ty),*) $(-> $ret)? = $name;
}; };
} }
#[cfg(not(feature = "unwinder"))]
binding! { binding! {
extern "C" fn _Unwind_GetGR(unwind_ctx: &UnwindContext<'_>, index: c_int) -> usize; extern "C" fn _Unwind_GetGR(unwind_ctx: &UnwindContext<'_>, index: c_int) -> usize;
extern "C" fn _Unwind_GetCFA(unwind_ctx: &UnwindContext<'_>) -> usize; extern "C" fn _Unwind_GetCFA(unwind_ctx: &UnwindContext<'_>) -> usize;
@ -134,19 +152,19 @@ binding! {
extern "C" fn _Unwind_GetTextRelBase(unwind_ctx: &UnwindContext<'_>) -> usize; extern "C" fn _Unwind_GetTextRelBase(unwind_ctx: &UnwindContext<'_>) -> usize;
extern "C" fn _Unwind_GetDataRelBase(unwind_ctx: &UnwindContext<'_>) -> usize; extern "C" fn _Unwind_GetDataRelBase(unwind_ctx: &UnwindContext<'_>) -> usize;
extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void; extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
extern "C-unwind" fn _Unwind_RaiseException( unsafe extern "C-unwind" fn _Unwind_RaiseException(
exception: &mut UnwindException, exception: *mut UnwindException,
) -> UnwindReasonCode; ) -> UnwindReasonCode;
extern "C-unwind" fn _Unwind_ForcedUnwind( unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: &mut UnwindException, exception: *mut UnwindException,
stop: UnwindStopFn, stop: UnwindStopFn,
stop_arg: *mut c_void, stop_arg: *mut c_void,
) -> UnwindReasonCode; ) -> UnwindReasonCode;
extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> !; unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> !;
extern "C-unwind" fn _Unwind_Resume_or_Rethrow( unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: &mut UnwindException, exception: *mut UnwindException,
) -> UnwindReasonCode; ) -> UnwindReasonCode;
extern "C" [unsafe] fn _Unwind_DeleteException(exception: *mut UnwindException); unsafe extern "C" fn _Unwind_DeleteException(exception: *mut UnwindException);
extern "C-unwind" fn _Unwind_Backtrace( extern "C-unwind" fn _Unwind_Backtrace(
trace: UnwindTraceFn, trace: UnwindTraceFn,
trace_argument: *mut c_void, trace_argument: *mut c_void,

View File

@ -1,6 +1,18 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![feature(c_unwind)] #![feature(c_unwind)]
#![feature(naked_functions)] #![feature(naked_functions)]
#![feature(non_exhaustive_omitted_patterns_lint)]
// lang_items is an internal feature. `internal_features` lint is added recently
// so also allow unknown lints to prevent warning in older nightly versions.
#![cfg_attr(
any(
feature = "personality",
feature = "personality-dummy",
feature = "panicking",
feature = "panic-handler-dummy"
),
allow(internal_features)
)]
#![cfg_attr( #![cfg_attr(
any(feature = "personality", feature = "personality-dummy"), any(feature = "personality", feature = "personality-dummy"),
feature(lang_items) feature(lang_items)
@ -20,6 +32,9 @@ extern crate alloc;
#[cfg(feature = "unwinder")] #[cfg(feature = "unwinder")]
mod unwinder; mod unwinder;
#[cfg(all(feature = "unwinder", feature = "fde-custom"))]
pub use unwinder::custom_eh_frame_finder;
pub mod abi; pub mod abi;
mod arch; mod arch;
@ -30,7 +45,7 @@ pub mod print;
#[cfg(feature = "personality")] #[cfg(feature = "personality")]
mod personality; mod personality;
#[cfg(feature = "personality-dummy")] #[cfg(all(not(feature = "personality"), feature = "personality-dummy"))]
mod personality_dummy; mod personality_dummy;
#[cfg(feature = "panic")] #[cfg(feature = "panic")]
@ -40,7 +55,7 @@ pub mod panicking;
#[cfg(feature = "panic-handler")] #[cfg(feature = "panic-handler")]
mod panic_handler; mod panic_handler;
#[cfg(feature = "panic-handler-dummy")] #[cfg(all(not(feature = "panic-handler"), feature = "panic-handler-dummy"))]
mod panic_handler_dummy; mod panic_handler_dummy;
#[cfg(feature = "system-alloc")] #[cfg(feature = "system-alloc")]

View File

@ -7,6 +7,8 @@ use crate::abi::*;
pub use crate::panic_handler::*; pub use crate::panic_handler::*;
use crate::panicking::Exception; use crate::panicking::Exception;
static CANARY: u8 = 0;
#[repr(transparent)] #[repr(transparent)]
struct RustPanic(Box<dyn Any + Send>, DropGuard); struct RustPanic(Box<dyn Any + Send>, DropGuard);
@ -18,13 +20,15 @@ impl Drop for DropGuard {
{ {
drop_panic(); drop_panic();
} }
core::intrinsics::abort(); crate::util::abort();
} }
} }
#[repr(C)] #[repr(C)]
struct ExceptionWithPayload { struct ExceptionWithPayload {
exception: MaybeUninit<UnwindException>, exception: MaybeUninit<UnwindException>,
// See rust/library/panic_unwind/src/gcc.rs for the canary values
canary: *const u8,
payload: RustPanic, payload: RustPanic,
} }
@ -34,12 +38,23 @@ unsafe impl Exception for RustPanic {
fn wrap(this: Self) -> *mut UnwindException { fn wrap(this: Self) -> *mut UnwindException {
Box::into_raw(Box::new(ExceptionWithPayload { Box::into_raw(Box::new(ExceptionWithPayload {
exception: MaybeUninit::uninit(), exception: MaybeUninit::uninit(),
canary: &CANARY,
payload: this, payload: this,
})) as *mut UnwindException })) as *mut UnwindException
} }
unsafe fn unwrap(ex: *mut UnwindException) -> Self { unsafe fn unwrap(ex: *mut UnwindException) -> Self {
let ex = unsafe { Box::from_raw(ex as *mut ExceptionWithPayload) }; let ex = ex as *mut ExceptionWithPayload;
let canary = unsafe { core::ptr::addr_of!((*ex).canary).read() };
if !core::ptr::eq(canary, &CANARY) {
// This is a Rust exception but not generated by us.
#[cfg(feature = "panic-handler")]
{
foreign_exception();
}
crate::util::abort();
}
let ex = unsafe { Box::from_raw(ex) };
ex.payload ex.payload
} }
} }
@ -57,7 +72,7 @@ pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
{ {
foreign_exception(); foreign_exception();
} }
core::intrinsics::abort(); crate::util::abort();
} }
Some(e) => { Some(e) => {
#[cfg(feature = "panic-handler")] #[cfg(feature = "panic-handler")]

View File

@ -59,10 +59,7 @@ fn stack_trace() {
struct CallbackData { struct CallbackData {
counter: usize, counter: usize,
} }
extern "C" fn callback( extern "C" fn callback(unwind_ctx: &UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode {
unwind_ctx: &mut UnwindContext<'_>,
arg: *mut c_void,
) -> UnwindReasonCode {
let data = unsafe { &mut *(arg as *mut CallbackData) }; let data = unsafe { &mut *(arg as *mut CallbackData) };
data.counter += 1; data.counter += 1;
eprintln!( eprintln!(
@ -80,7 +77,7 @@ fn do_panic(msg: Box<dyn Any + Send>) -> ! {
if PANIC_COUNT.get() >= 1 { if PANIC_COUNT.get() >= 1 {
stack_trace(); stack_trace();
eprintln!("thread panicked while processing panic. aborting."); eprintln!("thread panicked while processing panic. aborting.");
core::intrinsics::abort(); crate::util::abort();
} }
PANIC_COUNT.set(1); PANIC_COUNT.set(1);
if check_env() { if check_env() {
@ -88,7 +85,7 @@ fn do_panic(msg: Box<dyn Any + Send>) -> ! {
} }
let code = crate::panic::begin_panic(Box::new(msg)); let code = crate::panic::begin_panic(Box::new(msg));
eprintln!("failed to initiate panic, error {}", code.0); eprintln!("failed to initiate panic, error {}", code.0);
core::intrinsics::abort(); crate::util::abort();
} }
#[panic_handler] #[panic_handler]

View File

@ -2,5 +2,5 @@ use core::panic::PanicInfo;
#[panic_handler] #[panic_handler]
fn panic(_info: &PanicInfo<'_>) -> ! { fn panic(_info: &PanicInfo<'_>) -> ! {
core::intrinsics::abort(); crate::util::abort();
} }

View File

@ -21,7 +21,7 @@ pub fn begin_panic<E: Exception>(exception: E) -> UnwindReasonCode {
unsafe { unsafe {
(*ex).exception_class = u64::from_be_bytes(E::CLASS); (*ex).exception_class = u64::from_be_bytes(E::CLASS);
(*ex).exception_cleanup = Some(exception_cleanup::<E>); (*ex).exception_cleanup = Some(exception_cleanup::<E>);
_Unwind_RaiseException(&mut *ex) _Unwind_RaiseException(ex)
} }
} }
@ -39,7 +39,7 @@ pub fn catch_unwind<E: Exception, R, F: FnOnce() -> R>(f: F) -> Result<R, Option
let data_ptr = &mut data as *mut _ as *mut u8; let data_ptr = &mut data as *mut _ as *mut u8;
unsafe { unsafe {
return if core::intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<E>) == 0 { return if core::intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<E>) == 0 {
Ok(ManuallyDrop::into_inner(data.r)) Ok(ManuallyDrop::into_inner(data.r))
} else { } else {
Err(ManuallyDrop::into_inner(data.p)) Err(ManuallyDrop::into_inner(data.p))

View File

@ -136,11 +136,11 @@ fn find_eh_action(
} }
#[lang = "eh_personality"] #[lang = "eh_personality"]
fn rust_eh_personality( unsafe fn rust_eh_personality(
version: c_int, version: c_int,
actions: UnwindAction, actions: UnwindAction,
_exception_class: u64, _exception_class: u64,
exception: &mut UnwindException, exception: *mut UnwindException,
unwind_ctx: &mut UnwindContext<'_>, unwind_ctx: &mut UnwindContext<'_>,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
if version != 1 { if version != 1 {
@ -170,7 +170,7 @@ fn rust_eh_personality(
_Unwind_SetGR( _Unwind_SetGR(
unwind_ctx, unwind_ctx,
Arch::UNWIND_DATA_REG.0 .0 as _, Arch::UNWIND_DATA_REG.0 .0 as _,
exception as *mut _ as usize, exception as usize,
); );
_Unwind_SetGR(unwind_ctx, Arch::UNWIND_DATA_REG.1 .0 as _, 0); _Unwind_SetGR(unwind_ctx, Arch::UNWIND_DATA_REG.1 .0 as _, 0);
_Unwind_SetIP(unwind_ctx, lpad); _Unwind_SetIP(unwind_ctx, lpad);

View File

@ -2,11 +2,11 @@ use crate::abi::*;
use crate::util::*; use crate::util::*;
#[lang = "eh_personality"] #[lang = "eh_personality"]
extern "C" fn personality( unsafe extern "C" fn personality(
version: c_int, version: c_int,
_actions: UnwindAction, _actions: UnwindAction,
_exception_class: u64, _exception_class: u64,
_exception: &mut UnwindException, _exception: *mut UnwindException,
_ctx: &mut UnwindContext<'_>, _ctx: &mut UnwindContext<'_>,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
if version != 1 { if version != 1 {

View File

@ -49,3 +49,22 @@ macro_rules! eprint {
let _ = core::write!($crate::print::StderrPrinter, $($arg)*); let _ = core::write!($crate::print::StderrPrinter, $($arg)*);
}) })
} }
#[macro_export]
macro_rules! dbg {
() => {
$crate::eprintln!("[{}:{}]", ::core::file!(), ::core::line!())
};
($val:expr $(,)?) => {
match $val {
tmp => {
$crate::eprintln!("[{}:{}] {} = {:#?}",
::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
};
}

View File

@ -59,25 +59,34 @@ impl ops::IndexMut<gimli::Register> for Context {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
unsafe { unsafe {
asm!( asm!(
" "
stp d8, d9, [x8, 0x140] stp x29, x30, [sp, -16]!
stp d10, d11, [x8, 0x150] sub sp, sp, 512
stp d12, d13, [x8, 0x160] mov x8, x0
stp d14, d15, [x8, 0x170]
str x19, [x8, 0x98]
stp x20, x21, [x8, 0xA0]
stp x22, x23, [x8, 0xB0]
stp x24, x25, [x8, 0xC0]
stp x26, x27, [x8, 0xD0]
stp x28, x29, [x8, 0xE0]
mov x0, sp mov x0, sp
stp x30, x0, [x8, 0xF0]
stp d8, d9, [sp, 0x140]
stp d10, d11, [sp, 0x150]
stp d12, d13, [sp, 0x160]
stp d14, d15, [sp, 0x170]
str x19, [sp, 0x98]
stp x20, x21, [sp, 0xA0]
stp x22, x23, [sp, 0xB0]
stp x24, x25, [sp, 0xC0]
stp x26, x27, [sp, 0xD0]
stp x28, x29, [sp, 0xE0]
add x2, sp, 528
stp x30, x2, [sp, 0xF0]
blr x8
add sp, sp, 512
ldp x29, x30, [sp], 16
ret ret
", ",
options(noreturn) options(noreturn)
@ -85,8 +94,7 @@ pub extern "C-unwind" fn save_context() -> Context {
} }
} }
#[naked] pub unsafe fn restore_context(ctx: &Context) -> ! {
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
unsafe { unsafe {
asm!( asm!(
" "
@ -127,6 +135,7 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
ldp x0, x1, [x0, 0x00] ldp x0, x1, [x0, 0x00]
ret ret
", ",
in("x0") ctx,
options(noreturn) options(noreturn)
); );
} }

View File

@ -13,6 +13,11 @@ mod riscv64;
#[cfg(target_arch = "riscv64")] #[cfg(target_arch = "riscv64")]
pub use riscv64::*; pub use riscv64::*;
#[cfg(target_arch = "riscv32")]
mod riscv32;
#[cfg(target_arch = "riscv32")]
pub use riscv32::*;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
mod aarch64; mod aarch64;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
@ -22,6 +27,7 @@ pub use aarch64::*;
target_arch = "x86_64", target_arch = "x86_64",
target_arch = "x86", target_arch = "x86",
target_arch = "riscv64", target_arch = "riscv64",
target_arch = "riscv32",
target_arch = "aarch64" target_arch = "aarch64"
)))] )))]
compile_error!("Current architecture is not supported"); compile_error!("Current architecture is not supported");

View File

@ -0,0 +1,243 @@
use core::arch::asm;
use core::fmt;
use core::ops;
use gimli::{Register, RiscV};
// Match DWARF_FRAME_REGISTERS in libgcc
pub const MAX_REG_RULES: usize = 65;
#[cfg(all(target_feature = "f", not(target_feature = "d")))]
compile_error!("RISC-V with only F extension is not supported");
#[repr(C)]
#[derive(Clone, Default)]
pub struct Context {
pub gp: [usize; 32],
#[cfg(target_feature = "d")]
pub fp: [u64; 32],
}
impl fmt::Debug for Context {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut fmt = fmt.debug_struct("Context");
for i in 0..=31 {
fmt.field(RiscV::register_name(Register(i as _)).unwrap(), &self.gp[i]);
}
#[cfg(target_feature = "d")]
for i in 0..=31 {
fmt.field(
RiscV::register_name(Register((i + 32) as _)).unwrap(),
&self.fp[i],
);
}
fmt.finish()
}
}
impl ops::Index<Register> for Context {
type Output = usize;
fn index(&self, reg: Register) -> &usize {
match reg {
Register(0..=31) => &self.gp[reg.0 as usize],
// We cannot support indexing fp here. It is 64-bit if D extension is implemented,
// and 32-bit if only F extension is implemented.
_ => unimplemented!(),
}
}
}
impl ops::IndexMut<gimli::Register> for Context {
fn index_mut(&mut self, reg: Register) -> &mut usize {
match reg {
Register(0..=31) => &mut self.gp[reg.0 as usize],
// We cannot support indexing fp here. It is 64-bit if D extension is implemented,
// and 32-bit if only F extension is implemented.
_ => unimplemented!(),
}
}
}
macro_rules! code {
(save_gp) => {
"
sw x0, 0x00(sp)
sw ra, 0x04(sp)
sw t0, 0x08(sp)
sw gp, 0x0C(sp)
sw tp, 0x10(sp)
sw s0, 0x20(sp)
sw s1, 0x24(sp)
sw s2, 0x48(sp)
sw s3, 0x4C(sp)
sw s4, 0x50(sp)
sw s5, 0x54(sp)
sw s6, 0x58(sp)
sw s7, 0x5C(sp)
sw s8, 0x60(sp)
sw s9, 0x64(sp)
sw s10, 0x68(sp)
sw s11, 0x6C(sp)
"
};
(save_fp) => {
"
fsd fs0, 0xC0(sp)
fsd fs1, 0xC8(sp)
fsd fs2, 0x110(sp)
fsd fs3, 0x118(sp)
fsd fs4, 0x120(sp)
fsd fs5, 0x128(sp)
fsd fs6, 0x130(sp)
fsd fs7, 0x138(sp)
fsd fs8, 0x140(sp)
fsd fs9, 0x148(sp)
fsd fs10, 0x150(sp)
fsd fs11, 0x158(sp)
"
};
(restore_gp) => {
"
lw ra, 0x04(a0)
lw sp, 0x08(a0)
lw gp, 0x0C(a0)
lw tp, 0x10(a0)
lw t0, 0x14(a0)
lw t1, 0x18(a0)
lw t2, 0x1C(a0)
lw s0, 0x20(a0)
lw s1, 0x24(a0)
lw a1, 0x2C(a0)
lw a2, 0x30(a0)
lw a3, 0x34(a0)
lw a4, 0x38(a0)
lw a5, 0x3C(a0)
lw a6, 0x40(a0)
lw a7, 0x44(a0)
lw s2, 0x48(a0)
lw s3, 0x4C(a0)
lw s4, 0x50(a0)
lw s5, 0x54(a0)
lw s6, 0x58(a0)
lw s7, 0x5C(a0)
lw s8, 0x60(a0)
lw s9, 0x64(a0)
lw s10, 0x68(a0)
lw s11, 0x6C(a0)
lw t3, 0x70(a0)
lw t4, 0x74(a0)
lw t5, 0x78(a0)
lw t6, 0x7C(a0)
"
};
(restore_fp) => {
"
fld ft0, 0x80(a0)
fld ft1, 0x88(a0)
fld ft2, 0x90(a0)
fld ft3, 0x98(a0)
fld ft4, 0xA0(a0)
fld ft5, 0xA8(a0)
fld ft6, 0xB0(a0)
fld ft7, 0xB8(a0)
fld fs0, 0xC0(a0)
fld fs1, 0xC8(a0)
fld fa0, 0xD0(a0)
fld fa1, 0xD8(a0)
fld fa2, 0xE0(a0)
fld fa3, 0xE8(a0)
fld fa4, 0xF0(a0)
fld fa5, 0xF8(a0)
fld fa6, 0x100(a0)
fld fa7, 0x108(a0)
fld fs2, 0x110(a0)
fld fs3, 0x118(a0)
fld fs4, 0x120(a0)
fld fs5, 0x128(a0)
fld fs6, 0x130(a0)
fld fs7, 0x138(a0)
fld fs8, 0x140(a0)
fld fs9, 0x148(a0)
fld fs10, 0x150(a0)
fld fs11, 0x158(a0)
fld ft8, 0x160(a0)
fld ft9, 0x168(a0)
fld ft10, 0x170(a0)
fld ft11, 0x178(a0)
"
};
}
#[naked]
pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here.
#[cfg(target_feature = "d")]
unsafe {
asm!(
"
mv t0, sp
add sp, sp, -0x190
sw ra, 0x180(sp)
",
code!(save_gp),
code!(save_fp),
"
mv t0, a0
mv a0, sp
jalr t0
lw ra, 0x180(sp)
add sp, sp, 0x190
ret
",
options(noreturn)
);
}
#[cfg(not(target_feature = "d"))]
unsafe {
asm!(
"
mv t0, sp
add sp, sp, -0x90
sw ra, 0x80(sp)
",
code!(save_gp),
"
mv t0, a0
mv a0, sp
jalr t0
lw ra, 0x80(sp)
add sp, sp, 0x90
ret
",
options(noreturn)
);
}
}
pub unsafe fn restore_context(ctx: &Context) -> ! {
#[cfg(target_feature = "d")]
unsafe {
asm!(
code!(restore_fp),
code!(restore_gp),
"
lw a0, 0x28(a0)
ret
",
in("a0") ctx,
options(noreturn)
);
}
#[cfg(not(target_feature = "d"))]
unsafe {
asm!(
code!(restore_gp),
"
lw a0, 0x28(a0)
ret
",
in("a0") ctx,
options(noreturn)
);
}
}

View File

@ -6,6 +6,9 @@ use gimli::{Register, RiscV};
// Match DWARF_FRAME_REGISTERS in libgcc // Match DWARF_FRAME_REGISTERS in libgcc
pub const MAX_REG_RULES: usize = 65; pub const MAX_REG_RULES: usize = 65;
#[cfg(all(target_feature = "f", not(target_feature = "d")))]
compile_error!("RISC-V with only F extension is not supported");
#[repr(C)] #[repr(C)]
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Context { pub struct Context {
@ -58,39 +61,39 @@ impl ops::IndexMut<gimli::Register> for Context {
macro_rules! code { macro_rules! code {
(save_gp) => { (save_gp) => {
" "
sd x0, 0x00(a0) sd x0, 0x00(sp)
sd ra, 0x08(a0) sd ra, 0x08(sp)
sd sp, 0x10(a0) sd t0, 0x10(sp)
sd gp, 0x18(a0) sd gp, 0x18(sp)
sd tp, 0x20(a0) sd tp, 0x20(sp)
sd s0, 0x40(a0) sd s0, 0x40(sp)
sd s1, 0x48(a0) sd s1, 0x48(sp)
sd s2, 0x90(a0) sd s2, 0x90(sp)
sd s3, 0x98(a0) sd s3, 0x98(sp)
sd s4, 0xA0(a0) sd s4, 0xA0(sp)
sd s5, 0xA8(a0) sd s5, 0xA8(sp)
sd s6, 0xB0(a0) sd s6, 0xB0(sp)
sd s7, 0xB8(a0) sd s7, 0xB8(sp)
sd s8, 0xC0(a0) sd s8, 0xC0(sp)
sd s9, 0xC8(a0) sd s9, 0xC8(sp)
sd s10, 0xD0(a0) sd s10, 0xD0(sp)
sd s11, 0xD8(a0) sd s11, 0xD8(sp)
" "
}; };
(save_fp) => { (save_fp) => {
" "
fsd fs0, 0x140(a0) fsd fs0, 0x140(sp)
fsd fs1, 0x148(a0) fsd fs1, 0x148(sp)
fsd fs2, 0x190(a0) fsd fs2, 0x190(sp)
fsd fs3, 0x198(a0) fsd fs3, 0x198(sp)
fsd fs4, 0x1A0(a0) fsd fs4, 0x1A0(sp)
fsd fs5, 0x1A8(a0) fsd fs5, 0x1A8(sp)
fsd fs6, 0x1B0(a0) fsd fs6, 0x1B0(sp)
fsd fs7, 0x1B8(a0) fsd fs7, 0x1B8(sp)
fsd fs8, 0x1C0(a0) fsd fs8, 0x1C0(sp)
fsd fs9, 0x1C8(a0) fsd fs9, 0x1C8(sp)
fsd fs10, 0x1D0(a0) fsd fs10, 0x1D0(sp)
fsd fs11, 0x1D8(a0) fsd fs11, 0x1D8(sp)
" "
}; };
(restore_gp) => { (restore_gp) => {
@ -166,34 +169,74 @@ macro_rules! code {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
#[cfg(target_feature = "d")] #[cfg(target_feature = "d")]
unsafe { unsafe {
asm!( asm!(
concat!(code!(save_gp), code!(save_fp), "ret"), "
mv t0, sp
add sp, sp, -0x210
sd ra, 0x200(sp)
",
code!(save_gp),
code!(save_fp),
"
mv t0, a0
mv a0, sp
jalr t0
ld ra, 0x200(sp)
add sp, sp, 0x210
ret
",
options(noreturn) options(noreturn)
); );
} }
#[cfg(not(target_feature = "d"))] #[cfg(not(target_feature = "d"))]
unsafe { unsafe {
asm!(concat!(code!(save_gp), "ret"), options(noreturn)); asm!(
"
mv t0, sp
add sp, sp, -0x110
sd ra, 0x100(sp)
",
code!(save_gp),
"
mv t0, a0
mv a0, sp
jalr t0
ld ra, 0x100(sp)
add sp, sp, 0x110
ret
",
options(noreturn)
);
} }
} }
#[naked] pub unsafe fn restore_context(ctx: &Context) -> ! {
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
#[cfg(target_feature = "d")] #[cfg(target_feature = "d")]
unsafe { unsafe {
asm!( asm!(
concat!(code!(restore_fp), code!(restore_gp), "ld a0, 0x50(a0)\nret"), code!(restore_fp),
code!(restore_gp),
"
ld a0, 0x50(a0)
ret
",
in("a0") ctx,
options(noreturn) options(noreturn)
); );
} }
#[cfg(not(target_feature = "d"))] #[cfg(not(target_feature = "d"))]
unsafe { unsafe {
asm!( asm!(
concat!(code!(restore_gp), "ld a0, 0x50(a0)\nret"), code!(restore_gp),
"
ld a0, 0x50(a0)
ret
",
in("a0") ctx,
options(noreturn) options(noreturn)
); );
} }

View File

@ -56,41 +56,50 @@ impl ops::IndexMut<gimli::Register> for Context {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
unsafe { unsafe {
asm!( asm!(
" "
mov eax, [esp + 4] sub esp, 52
mov [eax + 4], ecx
mov [eax + 8], edx mov [esp + 4], ecx
mov [eax + 12], ebx mov [esp + 8], edx
mov [eax + 20], ebp mov [esp + 12], ebx
mov [eax + 24], esi
mov [eax + 28], edi
/* Adjust the stack to account for the return address */ /* Adjust the stack to account for the return address */
lea edx, [esp + 4] lea eax, [esp + 56]
mov [eax + 16], edx mov [esp + 16], eax
mov edx, [esp] mov [esp + 20], ebp
mov [eax + 32], edx mov [esp + 24], esi
stmxcsr [eax + 36] mov [esp + 28], edi
fnstcw [eax + 40]
ret 4 /* Return address */
mov eax, [esp + 52]
mov [esp + 32], eax
stmxcsr [esp + 36]
fnstcw [esp + 40]
mov eax, [esp + 60]
mov ecx, esp
push eax
push ecx
call [esp + 64]
add esp, 60
ret
", ",
options(noreturn) options(noreturn)
); );
} }
} }
#[naked] pub unsafe fn restore_context(ctx: &Context) -> ! {
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
unsafe { unsafe {
asm!( asm!(
" "
mov edx, [esp + 4]
/* Restore stack */ /* Restore stack */
mov esp, [edx + 16] mov esp, [edx + 16]
@ -118,6 +127,7 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
ret ret
", ",
in("edx") ctx,
options(noreturn) options(noreturn)
); );
} }

View File

@ -58,27 +58,35 @@ impl ops::IndexMut<gimli::Register> for Context {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
unsafe { unsafe {
asm!( asm!(
" "
mov rax, rdi sub rsp, 0x98
mov [rax + 0x18], rbx mov [rsp + 0x18], rbx
mov [rax + 0x30], rbp mov [rsp + 0x30], rbp
/* Adjust the stack to account for the return address */ /* Adjust the stack to account for the return address */
lea rdi, [rsp + 8] lea rax, [rsp + 0xA0]
mov [rax + 0x38], rdi mov [rsp + 0x38], rax
mov [rax + 0x60], r12 mov [rsp + 0x60], r12
mov [rax + 0x68], r13 mov [rsp + 0x68], r13
mov [rax + 0x70], r14 mov [rsp + 0x70], r14
mov [rax + 0x78], r15 mov [rsp + 0x78], r15
mov rdx, [rsp]
mov [rax + 0x80], rdx /* Return address */
/* stmxcsr [rax + 0x88] */ mov rax, [rsp + 0x98]
fnstcw [rax + 0x90] mov [rsp + 0x80], rax
/* stmxcsr [rsp + 0x88] */
fnstcw [rsp + 0x90]
mov rax, rdi
mov rdi, rsp
call rax
add rsp, 0x98
ret ret
", ",
options(noreturn) options(noreturn)
@ -86,8 +94,7 @@ pub extern "C-unwind" fn save_context() -> Context {
} }
} }
#[naked] pub unsafe fn restore_context(ctx: &Context) -> ! {
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
unsafe { unsafe {
asm!( asm!(
" "
@ -121,11 +128,12 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
mov r14, [rdi + 0x70] mov r14, [rdi + 0x70]
mov r15, [rdi + 0x78] mov r15, [rdi + 0x78]
/* RDI resotred last */ /* RDI restored last */
mov rdi, [rdi + 0x28] mov rdi, [rdi + 0x28]
ret ret
", ",
in("rdi") ctx,
options(noreturn) options(noreturn)
); );
} }

View File

@ -0,0 +1,171 @@
use super::{FDEFinder, FDESearchResult};
use crate::util::{deref_pointer, get_unlimited_slice};
use core::sync::atomic::{AtomicU32, Ordering};
use gimli::{BaseAddresses, EhFrame, EhFrameHdr, NativeEndian, UnwindSection};
pub(crate) struct CustomFinder(());
pub(crate) fn get_finder() -> &'static CustomFinder {
&CustomFinder(())
}
impl FDEFinder for CustomFinder {
fn find_fde(&self, pc: usize) -> Option<FDESearchResult> {
get_custom_eh_frame_finder().and_then(|eh_frame_finder| find_fde(eh_frame_finder, pc))
}
}
/// A trait for types whose values can be used as the global EH frame finder set by [`set_custom_eh_frame_finder`].
pub unsafe trait EhFrameFinder {
fn find(&self, pc: usize) -> Option<FrameInfo>;
}
pub struct FrameInfo {
pub text_base: Option<usize>,
pub kind: FrameInfoKind,
}
pub enum FrameInfoKind {
EhFrameHdr(usize),
EhFrame(usize),
}
static mut CUSTOM_EH_FRAME_FINDER: Option<&(dyn EhFrameFinder + Sync)> = None;
static CUSTOM_EH_FRAME_FINDER_STATE: AtomicU32 = AtomicU32::new(UNINITIALIZED);
const UNINITIALIZED: u32 = 0;
const INITIALIZING: u32 = 1;
const INITIALIZED: u32 = 2;
/// The type returned by [`set_custom_eh_frame_finder`] if [`set_custom_eh_frame_finder`] has
/// already been called.
#[derive(Debug)]
pub struct SetCustomEhFrameFinderError(());
/// Sets the global EH frame finder.
///
/// This function should only be called once during the lifetime of the program.
///
/// # Errors
///
/// An error is returned if this function has already been called during the lifetime of the
/// program.
pub fn set_custom_eh_frame_finder(
fde_finder: &'static (dyn EhFrameFinder + Sync),
) -> Result<(), SetCustomEhFrameFinderError> {
match CUSTOM_EH_FRAME_FINDER_STATE.compare_exchange(
UNINITIALIZED,
INITIALIZING,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(UNINITIALIZED) => {
unsafe {
CUSTOM_EH_FRAME_FINDER = Some(fde_finder);
}
CUSTOM_EH_FRAME_FINDER_STATE.store(INITIALIZED, Ordering::SeqCst);
Ok(())
}
Err(INITIALIZING) => {
while CUSTOM_EH_FRAME_FINDER_STATE.load(Ordering::SeqCst) == INITIALIZING {
core::hint::spin_loop();
}
Err(SetCustomEhFrameFinderError(()))
}
Err(INITIALIZED) => Err(SetCustomEhFrameFinderError(())),
_ => {
unreachable!()
}
}
}
fn get_custom_eh_frame_finder() -> Option<&'static dyn EhFrameFinder> {
if CUSTOM_EH_FRAME_FINDER_STATE.load(Ordering::SeqCst) == INITIALIZED {
Some(unsafe { CUSTOM_EH_FRAME_FINDER.unwrap() })
} else {
None
}
}
fn find_fde<T: EhFrameFinder + ?Sized>(eh_frame_finder: &T, pc: usize) -> Option<FDESearchResult> {
let info = eh_frame_finder.find(pc)?;
let text_base = info.text_base;
match info.kind {
FrameInfoKind::EhFrameHdr(eh_frame_hdr) => {
find_fde_with_eh_frame_hdr(pc, text_base, eh_frame_hdr)
}
FrameInfoKind::EhFrame(eh_frame) => find_fde_with_eh_frame(pc, text_base, eh_frame),
}
}
fn find_fde_with_eh_frame_hdr(
pc: usize,
text_base: Option<usize>,
eh_frame_hdr: usize,
) -> Option<FDESearchResult> {
unsafe {
let mut bases = BaseAddresses::default().set_eh_frame_hdr(eh_frame_hdr as _);
if let Some(text_base) = text_base {
bases = bases.set_text(text_base as _);
}
let eh_frame_hdr = EhFrameHdr::new(
get_unlimited_slice(eh_frame_hdr as usize as _),
NativeEndian,
)
.parse(&bases, core::mem::size_of::<usize>() as _)
.ok()?;
let eh_frame = deref_pointer(eh_frame_hdr.eh_frame_ptr());
let bases = bases.set_eh_frame(eh_frame as _);
let eh_frame = EhFrame::new(get_unlimited_slice(eh_frame as _), NativeEndian);
// Use binary search table for address if available.
if let Some(table) = eh_frame_hdr.table() {
if let Ok(fde) =
table.fde_for_address(&eh_frame, &bases, pc as _, EhFrame::cie_from_offset)
{
return Some(FDESearchResult {
fde,
bases,
eh_frame,
});
}
}
// Otherwise do the linear search.
if let Ok(fde) = eh_frame.fde_for_address(&bases, pc as _, EhFrame::cie_from_offset) {
return Some(FDESearchResult {
fde,
bases,
eh_frame,
});
}
None
}
}
fn find_fde_with_eh_frame(
pc: usize,
text_base: Option<usize>,
eh_frame: usize,
) -> Option<FDESearchResult> {
unsafe {
let mut bases = BaseAddresses::default().set_eh_frame(eh_frame as _);
if let Some(text_base) = text_base {
bases = bases.set_text(text_base as _);
}
let eh_frame = EhFrame::new(get_unlimited_slice(eh_frame as _), NativeEndian);
if let Ok(fde) = eh_frame.fde_for_address(&bases, pc as _, EhFrame::cie_from_offset) {
return Some(FDESearchResult {
fde,
bases,
eh_frame,
});
}
None
}
}

View File

@ -1,3 +1,5 @@
#[cfg(feature = "fde-custom")]
mod custom;
#[cfg(feature = "fde-static")] #[cfg(feature = "fde-static")]
mod fixed; mod fixed;
#[cfg(feature = "fde-gnu-eh-frame-hdr")] #[cfg(feature = "fde-gnu-eh-frame-hdr")]
@ -10,6 +12,14 @@ mod registry;
use crate::util::*; use crate::util::*;
use gimli::{BaseAddresses, EhFrame, FrameDescriptionEntry}; use gimli::{BaseAddresses, EhFrame, FrameDescriptionEntry};
#[cfg(feature = "fde-custom")]
pub mod custom_eh_frame_finder {
pub use super::custom::{
set_custom_eh_frame_finder, EhFrameFinder, FrameInfo, FrameInfoKind,
SetCustomEhFrameFinderError,
};
}
#[derive(Debug)] #[derive(Debug)]
pub struct FDESearchResult { pub struct FDESearchResult {
pub fde: FrameDescriptionEntry<StaticSlice>, pub fde: FrameDescriptionEntry<StaticSlice>,
@ -25,6 +35,10 @@ pub struct GlobalFinder(());
impl FDEFinder for GlobalFinder { impl FDEFinder for GlobalFinder {
fn find_fde(&self, pc: usize) -> Option<FDESearchResult> { fn find_fde(&self, pc: usize) -> Option<FDESearchResult> {
#[cfg(feature = "fde-custom")]
if let Some(v) = custom::get_finder().find_fde(pc) {
return Some(v);
}
#[cfg(feature = "fde-registry")] #[cfg(feature = "fde-registry")]
if let Some(v) = registry::get_finder().find_fde(pc) { if let Some(v) = registry::get_finder().find_fde(pc) {
return Some(v); return Some(v);

View File

@ -32,7 +32,7 @@ unsafe fn lock_global_state() -> impl ops::DerefMut<Target = GlobalState> {
#[cfg(feature = "libc")] #[cfg(feature = "libc")]
{ {
static mut MUTEX: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; static mut MUTEX: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
unsafe { libc::pthread_mutex_lock(&mut MUTEX) }; unsafe { libc::pthread_mutex_lock(core::ptr::addr_of_mut!(MUTEX)) };
static mut STATE: GlobalState = GlobalState { static mut STATE: GlobalState = GlobalState {
object: ptr::null_mut(), object: ptr::null_mut(),
@ -41,20 +41,22 @@ unsafe fn lock_global_state() -> impl ops::DerefMut<Target = GlobalState> {
struct LockGuard; struct LockGuard;
impl Drop for LockGuard { impl Drop for LockGuard {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { libc::pthread_mutex_unlock(&mut MUTEX) }; unsafe { libc::pthread_mutex_unlock(core::ptr::addr_of_mut!(MUTEX)) };
} }
} }
impl ops::Deref for LockGuard { impl ops::Deref for LockGuard {
type Target = GlobalState; type Target = GlobalState;
#[allow(static_mut_ref)]
fn deref(&self) -> &GlobalState { fn deref(&self) -> &GlobalState {
unsafe { &STATE } unsafe { &*core::ptr::addr_of!(STATE) }
} }
} }
impl ops::DerefMut for LockGuard { impl ops::DerefMut for LockGuard {
fn deref_mut(&mut self) -> &mut GlobalState { fn deref_mut(&mut self) -> &mut GlobalState {
unsafe { &mut STATE } unsafe { &mut *core::ptr::addr_of_mut!(STATE) }
} }
} }

View File

@ -125,6 +125,11 @@ impl Frame {
Err(gimli::Error::UnsupportedEvaluation) Err(gimli::Error::UnsupportedEvaluation)
} }
pub fn adjust_stack_for_args(&self, ctx: &mut Context) {
let size = self.row.saved_args_size();
ctx[Arch::SP] = ctx[Arch::SP].wrapping_add(size as usize);
}
pub fn unwind(&self, ctx: &Context) -> Result<Context, gimli::Error> { pub fn unwind(&self, ctx: &Context) -> Result<Context, gimli::Error> {
let row = &self.row; let row = &self.row;
let mut new_ctx = ctx.clone(); let mut new_ctx = ctx.clone();
@ -139,6 +144,7 @@ impl Frame {
new_ctx[Arch::SP] = cfa as _; new_ctx[Arch::SP] = cfa as _;
new_ctx[Arch::RA] = 0; new_ctx[Arch::RA] = 0;
#[warn(non_exhaustive_omitted_patterns)]
for (reg, rule) in row.registers() { for (reg, rule) in row.registers() {
let value = match *rule { let value = match *rule {
RegisterRule::Undefined | RegisterRule::SameValue => ctx[*reg], RegisterRule::Undefined | RegisterRule::SameValue => ctx[*reg],
@ -153,6 +159,8 @@ impl Frame {
} }
RegisterRule::ValExpression(expr) => self.evaluate_expression(ctx, expr)?, RegisterRule::ValExpression(expr) => self.evaluate_expression(ctx, expr)?,
RegisterRule::Architectural => unreachable!(), RegisterRule::Architectural => unreachable!(),
RegisterRule::Constant(value) => value as usize,
_ => unreachable!(),
}; };
new_ctx[*reg] = value; new_ctx[*reg] = value;
} }

View File

@ -13,6 +13,36 @@ use arch::*;
use find_fde::FDEFinder; use find_fde::FDEFinder;
use frame::Frame; use frame::Frame;
#[cfg(feature = "fde-custom")]
pub use find_fde::custom_eh_frame_finder;
// Helper function to turn `save_context` which takes function pointer to a closure-taking function.
fn with_context<T, F: FnOnce(&mut Context) -> T>(f: F) -> T {
use core::mem::ManuallyDrop;
union Data<T, F> {
f: ManuallyDrop<F>,
t: ManuallyDrop<T>,
}
extern "C" fn delegate<T, F: FnOnce(&mut Context) -> T>(ctx: &mut Context, ptr: *mut ()) {
// SAFETY: This function is called exactly once; it extracts the function, call it and
// store the return value. This function is `extern "C"` so we don't need to worry about
// unwinding past it.
unsafe {
let data = &mut *ptr.cast::<Data<T, F>>();
let t = ManuallyDrop::take(&mut data.f)(ctx);
data.t = ManuallyDrop::new(t);
}
}
let mut data = Data {
f: ManuallyDrop::new(f),
};
save_context(delegate::<T, F>, ptr::addr_of_mut!(data).cast());
unsafe { ManuallyDrop::into_inner(data.t) }
}
#[repr(C)] #[repr(C)]
pub struct UnwindException { pub struct UnwindException {
pub exception_class: u64, pub exception_class: u64,
@ -119,60 +149,63 @@ macro_rules! try2 {
#[inline(never)] #[inline(never)]
#[no_mangle] #[no_mangle]
pub extern "C-unwind" fn _Unwind_RaiseException( pub unsafe extern "C-unwind" fn _Unwind_RaiseException(
exception: &mut UnwindException, exception: *mut UnwindException,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let saved_ctx = save_context(); with_context(|saved_ctx| {
// Phase 1: Search for handler
let mut ctx = saved_ctx.clone();
let mut signal = false;
loop {
if let Some(frame) = try1!(Frame::from_context(&ctx, signal)) {
if let Some(personality) = frame.personality() {
let result = unsafe {
personality(
1,
UnwindAction::SEARCH_PHASE,
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx: &mut ctx,
signal,
},
)
};
// Phase 1: Search for handler match result {
let mut ctx = saved_ctx.clone(); UnwindReasonCode::CONTINUE_UNWIND => (),
let mut signal = false; UnwindReasonCode::HANDLER_FOUND => {
loop { break;
if let Some(frame) = try1!(Frame::from_context(&ctx, signal)) { }
if let Some(personality) = frame.personality() { _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
let result = personality(
1,
UnwindAction::SEARCH_PHASE,
exception.exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx: &mut ctx,
signal,
},
);
match result {
UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::HANDLER_FOUND => {
break;
} }
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
} }
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
} }
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
} }
}
// Disambiguate normal frame and signal frame. // Disambiguate normal frame and signal frame.
let handler_cfa = ctx[Arch::SP] - signal as usize; let handler_cfa = ctx[Arch::SP] - signal as usize;
exception.private_1 = None; unsafe {
exception.private_2 = handler_cfa; (*exception).private_1 = None;
(*exception).private_2 = handler_cfa;
}
let mut ctx = saved_ctx; let code = raise_exception_phase2(exception, saved_ctx, handler_cfa);
let code = raise_exception_phase2(exception, &mut ctx, handler_cfa); match code {
match code { UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(saved_ctx) },
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) }, _ => code,
_ => code, }
} })
} }
fn raise_exception_phase2( fn raise_exception_phase2(
exception: &mut UnwindException, exception: *mut UnwindException,
ctx: &mut Context, ctx: &mut Context,
handler_cfa: usize, handler_cfa: usize,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
@ -181,26 +214,31 @@ fn raise_exception_phase2(
if let Some(frame) = try2!(Frame::from_context(ctx, signal)) { if let Some(frame) = try2!(Frame::from_context(ctx, signal)) {
let frame_cfa = ctx[Arch::SP] - signal as usize; let frame_cfa = ctx[Arch::SP] - signal as usize;
if let Some(personality) = frame.personality() { if let Some(personality) = frame.personality() {
let code = personality( let code = unsafe {
1, personality(
UnwindAction::CLEANUP_PHASE 1,
| if frame_cfa == handler_cfa { UnwindAction::CLEANUP_PHASE
UnwindAction::HANDLER_FRAME | if frame_cfa == handler_cfa {
} else { UnwindAction::HANDLER_FRAME
UnwindAction::empty() } else {
UnwindAction::empty()
},
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx,
signal,
}, },
exception.exception_class, )
exception, };
&mut UnwindContext {
frame: Some(&frame),
ctx,
signal,
},
);
match code { match code {
UnwindReasonCode::CONTINUE_UNWIND => (), UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::INSTALL_CONTEXT => break, UnwindReasonCode::INSTALL_CONTEXT => {
frame.adjust_stack_for_args(ctx);
return UnwindReasonCode::INSTALL_CONTEXT;
}
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR, _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
} }
} }
@ -211,31 +249,31 @@ fn raise_exception_phase2(
return UnwindReasonCode::FATAL_PHASE2_ERROR; return UnwindReasonCode::FATAL_PHASE2_ERROR;
} }
} }
UnwindReasonCode::INSTALL_CONTEXT
} }
#[inline(never)] #[inline(never)]
#[no_mangle] #[no_mangle]
pub extern "C-unwind" fn _Unwind_ForcedUnwind( pub unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: &mut UnwindException, exception: *mut UnwindException,
stop: UnwindStopFn, stop: UnwindStopFn,
stop_arg: *mut c_void, stop_arg: *mut c_void,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let mut ctx = save_context(); with_context(|ctx| {
unsafe {
(*exception).private_1 = Some(stop);
(*exception).private_2 = stop_arg as _;
}
exception.private_1 = Some(stop); let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
exception.private_2 = stop_arg as _; match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) },
let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg); _ => code,
match code { }
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) }, })
_ => code,
}
} }
fn force_unwind_phase2( fn force_unwind_phase2(
exception: &mut UnwindException, exception: *mut UnwindException,
ctx: &mut Context, ctx: &mut Context,
stop: UnwindStopFn, stop: UnwindStopFn,
stop_arg: *mut c_void, stop_arg: *mut c_void,
@ -244,24 +282,26 @@ fn force_unwind_phase2(
loop { loop {
let frame = try2!(Frame::from_context(ctx, signal)); let frame = try2!(Frame::from_context(ctx, signal));
let code = stop( let code = unsafe {
1, stop(
UnwindAction::FORCE_UNWIND 1,
| UnwindAction::END_OF_STACK UnwindAction::FORCE_UNWIND
| if frame.is_none() { | UnwindAction::END_OF_STACK
UnwindAction::END_OF_STACK | if frame.is_none() {
} else { UnwindAction::END_OF_STACK
UnwindAction::empty() } else {
UnwindAction::empty()
},
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: frame.as_ref(),
ctx,
signal,
}, },
exception.exception_class, stop_arg,
exception, )
&mut UnwindContext { };
frame: frame.as_ref(),
ctx,
signal,
},
stop_arg,
);
match code { match code {
UnwindReasonCode::NO_REASON => (), UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR, _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
@ -269,21 +309,26 @@ fn force_unwind_phase2(
if let Some(frame) = frame { if let Some(frame) = frame {
if let Some(personality) = frame.personality() { if let Some(personality) = frame.personality() {
let code = personality( let code = unsafe {
1, personality(
UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE, 1,
exception.exception_class, UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
exception, (*exception).exception_class,
&mut UnwindContext { exception,
frame: Some(&frame), &mut UnwindContext {
ctx, frame: Some(&frame),
signal, ctx,
}, signal,
); },
)
};
match code { match code {
UnwindReasonCode::CONTINUE_UNWIND => (), UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::INSTALL_CONTEXT => break, UnwindReasonCode::INSTALL_CONTEXT => {
frame.adjust_stack_for_args(ctx);
return UnwindReasonCode::INSTALL_CONTEXT;
}
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR, _ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
} }
} }
@ -294,47 +339,45 @@ fn force_unwind_phase2(
return UnwindReasonCode::END_OF_STACK; return UnwindReasonCode::END_OF_STACK;
} }
} }
UnwindReasonCode::INSTALL_CONTEXT
} }
#[inline(never)] #[inline(never)]
#[no_mangle] #[no_mangle]
pub extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> ! { pub unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> ! {
let mut ctx = save_context(); with_context(|ctx| {
let code = match unsafe { (*exception).private_1 } {
None => {
let handler_cfa = unsafe { (*exception).private_2 };
raise_exception_phase2(exception, ctx, handler_cfa)
}
Some(stop) => {
let stop_arg = unsafe { (*exception).private_2 as _ };
force_unwind_phase2(exception, ctx, stop, stop_arg)
}
};
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
let code = match exception.private_1 { unsafe { restore_context(ctx) }
None => { })
let handler_cfa = exception.private_2;
raise_exception_phase2(exception, &mut ctx, handler_cfa)
}
Some(stop) => {
let stop_arg = exception.private_2 as _;
force_unwind_phase2(exception, &mut ctx, stop, stop_arg)
}
};
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
unsafe { restore_context(&ctx) }
} }
#[inline(never)] #[inline(never)]
#[no_mangle] #[no_mangle]
pub extern "C-unwind" fn _Unwind_Resume_or_Rethrow( pub unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: &mut UnwindException, exception: *mut UnwindException,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let stop = match exception.private_1 { let stop = match unsafe { (*exception).private_1 } {
None => return _Unwind_RaiseException(exception), None => return unsafe { _Unwind_RaiseException(exception) },
Some(v) => v, Some(v) => v,
}; };
let mut ctx = save_context(); with_context(|ctx| {
let stop_arg = unsafe { (*exception).private_2 as _ };
let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
let stop_arg = exception.private_2 as _; unsafe { restore_context(ctx) }
let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg); })
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
unsafe { restore_context(&ctx) }
} }
#[no_mangle] #[no_mangle]
@ -350,36 +393,38 @@ pub extern "C-unwind" fn _Unwind_Backtrace(
trace: UnwindTraceFn, trace: UnwindTraceFn,
trace_argument: *mut c_void, trace_argument: *mut c_void,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let mut ctx = save_context(); with_context(|ctx| {
let mut signal = false; let mut ctx = ctx.clone();
let mut skipping = cfg!(feature = "hide-trace"); let mut signal = false;
let mut skipping = cfg!(feature = "hide-trace");
loop { loop {
let frame = try1!(Frame::from_context(&ctx, signal)); let frame = try1!(Frame::from_context(&ctx, signal));
if !skipping { if !skipping {
let code = trace( let code = trace(
&mut UnwindContext { &UnwindContext {
frame: frame.as_ref(), frame: frame.as_ref(),
ctx: &mut ctx, ctx: &mut ctx,
signal, signal,
}, },
trace_argument, trace_argument,
); );
match code { match code {
UnwindReasonCode::NO_REASON => (), UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR, _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
}
if let Some(frame) = frame {
if skipping {
if frame.initial_address() == _Unwind_Backtrace as usize {
skipping = false;
} }
} }
ctx = try1!(frame.unwind(&ctx)); if let Some(frame) = frame {
signal = frame.is_signal_trampoline(); if skipping {
} else { if frame.initial_address() == _Unwind_Backtrace as usize {
return UnwindReasonCode::END_OF_STACK; skipping = false;
}
}
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
}
} }
} })
} }

View File

@ -23,3 +23,19 @@ pub use libc::c_int;
#[cfg(not(feature = "libc"))] #[cfg(not(feature = "libc"))]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type c_int = i32; pub type c_int = i32;
#[cfg(all(
any(feature = "panic", feature = "panic-handler-dummy"),
feature = "libc"
))]
pub fn abort() -> ! {
unsafe { libc::abort() };
}
#[cfg(all(
any(feature = "panic", feature = "panic-handler-dummy"),
not(feature = "libc")
))]
pub fn abort() -> ! {
core::intrinsics::abort();
}

20
tests/compile_tests.rs Normal file
View File

@ -0,0 +1,20 @@
use std::process::Command;
#[test]
fn main() {
let dir = env!("CARGO_MANIFEST_DIR");
let tests = [
"throw_and_catch",
"catch_std_exception",
"std_catch_exception",
];
for test in tests {
let status = Command::new("./check.sh")
.current_dir(format!("{dir}/test_crates/{test}"))
.status()
.unwrap();
assert!(status.success());
}
}