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/
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]
name = "unwinding"
version = "0.1.5"
version = "0.2.1"
authors = ["Gary Guo <gary@garyguo.net>"]
edition = "2018"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Unwinding library in Rust and for Rust"
repository = "https://github.com/nbdd0121/unwinding/"
[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 }
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]
alloc = []
@ -21,6 +23,7 @@ fde-phdr-aux = ["fde-phdr"]
fde-registry = ["alloc"]
fde-static = []
fde-gnu-eh-frame-hdr = []
fde-custom = []
dwarf-expr = []
hide-trace = []
personality = []
@ -32,6 +35,7 @@ panic-handler = ["print", "panic"]
panic-handler-dummy = []
system-alloc = []
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]
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.
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
@ -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-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-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 |
| 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 UnwindStopFn = extern "C" fn(
pub type UnwindStopFn = unsafe extern "C" fn(
c_int,
UnwindAction,
u64,
&mut UnwindException,
*mut UnwindException,
&mut UnwindContext<'_>,
*mut c_void,
) -> UnwindReasonCode;
@ -78,7 +78,7 @@ pub struct UnwindException {
}
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"))]
#[repr(C)]
@ -87,31 +87,49 @@ pub struct UnwindContext<'a> {
phantom: core::marker::PhantomData<&'a ()>,
}
pub type PersonalityRoutine = extern "C" fn(
pub type PersonalityRoutine = unsafe extern "C" fn(
c_int,
UnwindAction,
u64,
&mut UnwindException,
*mut UnwindException,
&mut UnwindContext<'_>,
) -> UnwindReasonCode;
#[cfg(not(feature = "unwinder"))]
macro_rules! binding {
($(extern $abi: literal $([$t:tt])? fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?;)*) => {
$(
#[allow(non_snake_case)]
#[inline]
pub $($t)? fn $name($($arg: $arg_ty),*) $(-> $ret)? {
extern $abi {
fn $name($($arg: $arg_ty),*) $(-> $ret)?;
}
unsafe { $name($($arg),*) }
() => {};
(unsafe extern $abi: literal fn $name: ident ($($arg: ident : $arg_ty: ty),*$(,)?) $(-> $ret: ty)?; $($rest: tt)*) => {
extern $abi {
pub fn $name($($arg: $arg_ty),*) $(-> $ret)?;
}
binding!($($rest)*);
};
(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! {
extern "C" fn _Unwind_GetGR(unwind_ctx: &UnwindContext<'_>, index: c_int) -> 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_GetDataRelBase(unwind_ctx: &UnwindContext<'_>) -> usize;
extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
extern "C-unwind" fn _Unwind_RaiseException(
exception: &mut UnwindException,
unsafe extern "C-unwind" fn _Unwind_RaiseException(
exception: *mut UnwindException,
) -> UnwindReasonCode;
extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: &mut UnwindException,
unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: *mut UnwindException,
stop: UnwindStopFn,
stop_arg: *mut c_void,
) -> UnwindReasonCode;
extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> !;
extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: &mut UnwindException,
unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> !;
unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: *mut UnwindException,
) -> 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(
trace: UnwindTraceFn,
trace_argument: *mut c_void,

View File

@ -1,6 +1,18 @@
#![doc = include_str!("../README.md")]
#![feature(c_unwind)]
#![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(
any(feature = "personality", feature = "personality-dummy"),
feature(lang_items)
@ -20,6 +32,9 @@ extern crate alloc;
#[cfg(feature = "unwinder")]
mod unwinder;
#[cfg(all(feature = "unwinder", feature = "fde-custom"))]
pub use unwinder::custom_eh_frame_finder;
pub mod abi;
mod arch;
@ -30,7 +45,7 @@ pub mod print;
#[cfg(feature = "personality")]
mod personality;
#[cfg(feature = "personality-dummy")]
#[cfg(all(not(feature = "personality"), feature = "personality-dummy"))]
mod personality_dummy;
#[cfg(feature = "panic")]
@ -40,7 +55,7 @@ pub mod panicking;
#[cfg(feature = "panic-handler")]
mod panic_handler;
#[cfg(feature = "panic-handler-dummy")]
#[cfg(all(not(feature = "panic-handler"), feature = "panic-handler-dummy"))]
mod panic_handler_dummy;
#[cfg(feature = "system-alloc")]

View File

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

View File

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

View File

@ -2,5 +2,5 @@ use core::panic::PanicInfo;
#[panic_handler]
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 {
(*ex).exception_class = u64::from_be_bytes(E::CLASS);
(*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;
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))
} else {
Err(ManuallyDrop::into_inner(data.p))

View File

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

View File

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

View File

@ -49,3 +49,22 @@ macro_rules! eprint {
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]
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.
unsafe {
asm!(
"
stp d8, d9, [x8, 0x140]
stp d10, d11, [x8, 0x150]
stp d12, d13, [x8, 0x160]
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]
stp x29, x30, [sp, -16]!
sub sp, sp, 512
mov x8, x0
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
",
options(noreturn)
@ -85,8 +94,7 @@ pub extern "C-unwind" fn save_context() -> Context {
}
}
#[naked]
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
pub unsafe fn restore_context(ctx: &Context) -> ! {
unsafe {
asm!(
"
@ -127,6 +135,7 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
ldp x0, x1, [x0, 0x00]
ret
",
in("x0") ctx,
options(noreturn)
);
}

View File

@ -13,6 +13,11 @@ mod riscv64;
#[cfg(target_arch = "riscv64")]
pub use riscv64::*;
#[cfg(target_arch = "riscv32")]
mod riscv32;
#[cfg(target_arch = "riscv32")]
pub use riscv32::*;
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "aarch64")]
@ -22,6 +27,7 @@ pub use aarch64::*;
target_arch = "x86_64",
target_arch = "x86",
target_arch = "riscv64",
target_arch = "riscv32",
target_arch = "aarch64"
)))]
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
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 {
@ -58,39 +61,39 @@ impl ops::IndexMut<gimli::Register> for Context {
macro_rules! code {
(save_gp) => {
"
sd x0, 0x00(a0)
sd ra, 0x08(a0)
sd sp, 0x10(a0)
sd gp, 0x18(a0)
sd tp, 0x20(a0)
sd s0, 0x40(a0)
sd s1, 0x48(a0)
sd s2, 0x90(a0)
sd s3, 0x98(a0)
sd s4, 0xA0(a0)
sd s5, 0xA8(a0)
sd s6, 0xB0(a0)
sd s7, 0xB8(a0)
sd s8, 0xC0(a0)
sd s9, 0xC8(a0)
sd s10, 0xD0(a0)
sd s11, 0xD8(a0)
sd x0, 0x00(sp)
sd ra, 0x08(sp)
sd t0, 0x10(sp)
sd gp, 0x18(sp)
sd tp, 0x20(sp)
sd s0, 0x40(sp)
sd s1, 0x48(sp)
sd s2, 0x90(sp)
sd s3, 0x98(sp)
sd s4, 0xA0(sp)
sd s5, 0xA8(sp)
sd s6, 0xB0(sp)
sd s7, 0xB8(sp)
sd s8, 0xC0(sp)
sd s9, 0xC8(sp)
sd s10, 0xD0(sp)
sd s11, 0xD8(sp)
"
};
(save_fp) => {
"
fsd fs0, 0x140(a0)
fsd fs1, 0x148(a0)
fsd fs2, 0x190(a0)
fsd fs3, 0x198(a0)
fsd fs4, 0x1A0(a0)
fsd fs5, 0x1A8(a0)
fsd fs6, 0x1B0(a0)
fsd fs7, 0x1B8(a0)
fsd fs8, 0x1C0(a0)
fsd fs9, 0x1C8(a0)
fsd fs10, 0x1D0(a0)
fsd fs11, 0x1D8(a0)
fsd fs0, 0x140(sp)
fsd fs1, 0x148(sp)
fsd fs2, 0x190(sp)
fsd fs3, 0x198(sp)
fsd fs4, 0x1A0(sp)
fsd fs5, 0x1A8(sp)
fsd fs6, 0x1B0(sp)
fsd fs7, 0x1B8(sp)
fsd fs8, 0x1C0(sp)
fsd fs9, 0x1C8(sp)
fsd fs10, 0x1D0(sp)
fsd fs11, 0x1D8(sp)
"
};
(restore_gp) => {
@ -166,34 +169,74 @@ macro_rules! code {
}
#[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.
#[cfg(target_feature = "d")]
unsafe {
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)
);
}
#[cfg(not(target_feature = "d"))]
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 extern "C" fn restore_context(ctx: &Context) -> ! {
pub unsafe fn restore_context(ctx: &Context) -> ! {
#[cfg(target_feature = "d")]
unsafe {
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)
);
}
#[cfg(not(target_feature = "d"))]
unsafe {
asm!(
concat!(code!(restore_gp), "ld a0, 0x50(a0)\nret"),
code!(restore_gp),
"
ld a0, 0x50(a0)
ret
",
in("a0") ctx,
options(noreturn)
);
}

View File

@ -56,41 +56,50 @@ impl ops::IndexMut<gimli::Register> for Context {
}
#[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.
unsafe {
asm!(
"
mov eax, [esp + 4]
mov [eax + 4], ecx
mov [eax + 8], edx
mov [eax + 12], ebx
mov [eax + 20], ebp
mov [eax + 24], esi
mov [eax + 28], edi
sub esp, 52
mov [esp + 4], ecx
mov [esp + 8], edx
mov [esp + 12], ebx
/* Adjust the stack to account for the return address */
lea edx, [esp + 4]
mov [eax + 16], edx
lea eax, [esp + 56]
mov [esp + 16], eax
mov edx, [esp]
mov [eax + 32], edx
stmxcsr [eax + 36]
fnstcw [eax + 40]
ret 4
mov [esp + 20], ebp
mov [esp + 24], esi
mov [esp + 28], edi
/* 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)
);
}
}
#[naked]
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
pub unsafe fn restore_context(ctx: &Context) -> ! {
unsafe {
asm!(
"
mov edx, [esp + 4]
/* Restore stack */
mov esp, [edx + 16]
@ -118,6 +127,7 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
ret
",
in("edx") ctx,
options(noreturn)
);
}

View File

@ -58,27 +58,35 @@ impl ops::IndexMut<gimli::Register> for Context {
}
#[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.
unsafe {
asm!(
"
mov rax, rdi
mov [rax + 0x18], rbx
mov [rax + 0x30], rbp
sub rsp, 0x98
mov [rsp + 0x18], rbx
mov [rsp + 0x30], rbp
/* Adjust the stack to account for the return address */
lea rdi, [rsp + 8]
mov [rax + 0x38], rdi
lea rax, [rsp + 0xA0]
mov [rsp + 0x38], rax
mov [rax + 0x60], r12
mov [rax + 0x68], r13
mov [rax + 0x70], r14
mov [rax + 0x78], r15
mov rdx, [rsp]
mov [rax + 0x80], rdx
/* stmxcsr [rax + 0x88] */
fnstcw [rax + 0x90]
mov [rsp + 0x60], r12
mov [rsp + 0x68], r13
mov [rsp + 0x70], r14
mov [rsp + 0x78], r15
/* Return address */
mov rax, [rsp + 0x98]
mov [rsp + 0x80], rax
/* stmxcsr [rsp + 0x88] */
fnstcw [rsp + 0x90]
mov rax, rdi
mov rdi, rsp
call rax
add rsp, 0x98
ret
",
options(noreturn)
@ -86,8 +94,7 @@ pub extern "C-unwind" fn save_context() -> Context {
}
}
#[naked]
pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
pub unsafe fn restore_context(ctx: &Context) -> ! {
unsafe {
asm!(
"
@ -121,11 +128,12 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
mov r14, [rdi + 0x70]
mov r15, [rdi + 0x78]
/* RDI resotred last */
/* RDI restored last */
mov rdi, [rdi + 0x28]
ret
",
in("rdi") ctx,
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")]
mod fixed;
#[cfg(feature = "fde-gnu-eh-frame-hdr")]
@ -10,6 +12,14 @@ mod registry;
use crate::util::*;
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)]
pub struct FDESearchResult {
pub fde: FrameDescriptionEntry<StaticSlice>,
@ -25,6 +35,10 @@ pub struct GlobalFinder(());
impl FDEFinder for GlobalFinder {
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")]
if let Some(v) = registry::get_finder().find_fde(pc) {
return Some(v);

View File

@ -32,7 +32,7 @@ unsafe fn lock_global_state() -> impl ops::DerefMut<Target = GlobalState> {
#[cfg(feature = "libc")]
{
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 {
object: ptr::null_mut(),
@ -41,20 +41,22 @@ unsafe fn lock_global_state() -> impl ops::DerefMut<Target = GlobalState> {
struct LockGuard;
impl Drop for LockGuard {
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 {
type Target = GlobalState;
#[allow(static_mut_ref)]
fn deref(&self) -> &GlobalState {
unsafe { &STATE }
unsafe { &*core::ptr::addr_of!(STATE) }
}
}
impl ops::DerefMut for LockGuard {
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)
}
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> {
let row = &self.row;
let mut new_ctx = ctx.clone();
@ -139,6 +144,7 @@ impl Frame {
new_ctx[Arch::SP] = cfa as _;
new_ctx[Arch::RA] = 0;
#[warn(non_exhaustive_omitted_patterns)]
for (reg, rule) in row.registers() {
let value = match *rule {
RegisterRule::Undefined | RegisterRule::SameValue => ctx[*reg],
@ -153,6 +159,8 @@ impl Frame {
}
RegisterRule::ValExpression(expr) => self.evaluate_expression(ctx, expr)?,
RegisterRule::Architectural => unreachable!(),
RegisterRule::Constant(value) => value as usize,
_ => unreachable!(),
};
new_ctx[*reg] = value;
}

View File

@ -13,6 +13,36 @@ use arch::*;
use find_fde::FDEFinder;
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)]
pub struct UnwindException {
pub exception_class: u64,
@ -119,60 +149,63 @@ macro_rules! try2 {
#[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn _Unwind_RaiseException(
exception: &mut UnwindException,
pub unsafe extern "C-unwind" fn _Unwind_RaiseException(
exception: *mut UnwindException,
) -> 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
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 = 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;
match result {
UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::HANDLER_FOUND => {
break;
}
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
_ => 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.
let handler_cfa = ctx[Arch::SP] - signal as usize;
exception.private_1 = None;
exception.private_2 = handler_cfa;
// Disambiguate normal frame and signal frame.
let handler_cfa = ctx[Arch::SP] - signal as usize;
unsafe {
(*exception).private_1 = None;
(*exception).private_2 = handler_cfa;
}
let mut ctx = saved_ctx;
let code = raise_exception_phase2(exception, &mut ctx, handler_cfa);
match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) },
_ => code,
}
let code = raise_exception_phase2(exception, saved_ctx, handler_cfa);
match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(saved_ctx) },
_ => code,
}
})
}
fn raise_exception_phase2(
exception: &mut UnwindException,
exception: *mut UnwindException,
ctx: &mut Context,
handler_cfa: usize,
) -> UnwindReasonCode {
@ -181,26 +214,31 @@ fn raise_exception_phase2(
if let Some(frame) = try2!(Frame::from_context(ctx, signal)) {
let frame_cfa = ctx[Arch::SP] - signal as usize;
if let Some(personality) = frame.personality() {
let code = personality(
1,
UnwindAction::CLEANUP_PHASE
| if frame_cfa == handler_cfa {
UnwindAction::HANDLER_FRAME
} else {
UnwindAction::empty()
let code = unsafe {
personality(
1,
UnwindAction::CLEANUP_PHASE
| if frame_cfa == handler_cfa {
UnwindAction::HANDLER_FRAME
} 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 {
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,
}
}
@ -211,31 +249,31 @@ fn raise_exception_phase2(
return UnwindReasonCode::FATAL_PHASE2_ERROR;
}
}
UnwindReasonCode::INSTALL_CONTEXT
}
#[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: &mut UnwindException,
pub unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: *mut UnwindException,
stop: UnwindStopFn,
stop_arg: *mut c_void,
) -> 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);
exception.private_2 = stop_arg as _;
let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg);
match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) },
_ => code,
}
let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) },
_ => code,
}
})
}
fn force_unwind_phase2(
exception: &mut UnwindException,
exception: *mut UnwindException,
ctx: &mut Context,
stop: UnwindStopFn,
stop_arg: *mut c_void,
@ -244,24 +282,26 @@ fn force_unwind_phase2(
loop {
let frame = try2!(Frame::from_context(ctx, signal));
let code = stop(
1,
UnwindAction::FORCE_UNWIND
| UnwindAction::END_OF_STACK
| if frame.is_none() {
UnwindAction::END_OF_STACK
} else {
UnwindAction::empty()
let code = unsafe {
stop(
1,
UnwindAction::FORCE_UNWIND
| UnwindAction::END_OF_STACK
| if frame.is_none() {
UnwindAction::END_OF_STACK
} else {
UnwindAction::empty()
},
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: frame.as_ref(),
ctx,
signal,
},
exception.exception_class,
exception,
&mut UnwindContext {
frame: frame.as_ref(),
ctx,
signal,
},
stop_arg,
);
stop_arg,
)
};
match code {
UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
@ -269,21 +309,26 @@ fn force_unwind_phase2(
if let Some(frame) = frame {
if let Some(personality) = frame.personality() {
let code = personality(
1,
UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
exception.exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx,
signal,
},
);
let code = unsafe {
personality(
1,
UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx,
signal,
},
)
};
match code {
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,
}
}
@ -294,47 +339,45 @@ fn force_unwind_phase2(
return UnwindReasonCode::END_OF_STACK;
}
}
UnwindReasonCode::INSTALL_CONTEXT
}
#[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> ! {
let mut ctx = save_context();
pub unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> ! {
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 {
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) }
unsafe { restore_context(ctx) }
})
}
#[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: &mut UnwindException,
pub unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: *mut UnwindException,
) -> UnwindReasonCode {
let stop = match exception.private_1 {
None => return _Unwind_RaiseException(exception),
let stop = match unsafe { (*exception).private_1 } {
None => return unsafe { _Unwind_RaiseException(exception) },
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 _;
let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg);
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
unsafe { restore_context(&ctx) }
unsafe { restore_context(ctx) }
})
}
#[no_mangle]
@ -350,36 +393,38 @@ pub extern "C-unwind" fn _Unwind_Backtrace(
trace: UnwindTraceFn,
trace_argument: *mut c_void,
) -> UnwindReasonCode {
let mut ctx = save_context();
let mut signal = false;
let mut skipping = cfg!(feature = "hide-trace");
with_context(|ctx| {
let mut ctx = ctx.clone();
let mut signal = false;
let mut skipping = cfg!(feature = "hide-trace");
loop {
let frame = try1!(Frame::from_context(&ctx, signal));
if !skipping {
let code = trace(
&mut UnwindContext {
frame: frame.as_ref(),
ctx: &mut ctx,
signal,
},
trace_argument,
);
match code {
UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
}
if let Some(frame) = frame {
if skipping {
if frame.initial_address() == _Unwind_Backtrace as usize {
skipping = false;
loop {
let frame = try1!(Frame::from_context(&ctx, signal));
if !skipping {
let code = trace(
&UnwindContext {
frame: frame.as_ref(),
ctx: &mut ctx,
signal,
},
trace_argument,
);
match code {
UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
}
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
if let Some(frame) = frame {
if skipping {
if frame.initial_address() == _Unwind_Backtrace as usize {
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"))]
#[allow(non_camel_case_types)]
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());
}
}