Implement rust personality
This commit is contained in:
parent
e205fb9e64
commit
f0e87da0aa
@ -18,6 +18,7 @@ fde-phdr = ["libc"]
|
|||||||
fde-registry = ["alloc"]
|
fde-registry = ["alloc"]
|
||||||
dwarf-expr = []
|
dwarf-expr = []
|
||||||
hide-trace = []
|
hide-trace = []
|
||||||
|
personality = []
|
||||||
personality-dummy = []
|
personality-dummy = []
|
||||||
system-alloc = []
|
system-alloc = []
|
||||||
default = ["dwarf-expr", "hide-trace", "fde-phdr", "fde-registry"]
|
default = ["dwarf-expr", "hide-trace", "fde-phdr", "fde-registry"]
|
||||||
|
@ -75,6 +75,7 @@ pub struct UnwindException {
|
|||||||
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
|
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
|
||||||
private_1: Option<UnwindStopFn>,
|
private_1: Option<UnwindStopFn>,
|
||||||
private_2: usize,
|
private_2: usize,
|
||||||
|
private_unused: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UnwindTraceFn =
|
pub type UnwindTraceFn =
|
||||||
@ -128,9 +129,7 @@ pub extern "C" fn _Unwind_SetIP(unwind_ctx: &mut UnwindContext<'_>, value: usize
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn _Unwind_GetLanguageSpecificData(
|
pub extern "C" fn _Unwind_GetLanguageSpecificData(unwind_ctx: &UnwindContext<'_>) -> *mut c_void {
|
||||||
unwind_ctx: &UnwindContext<'_>,
|
|
||||||
) -> *mut c_void {
|
|
||||||
unwind_ctx
|
unwind_ctx
|
||||||
.frame
|
.frame
|
||||||
.map(|f| f.lsda() as *mut c_void)
|
.map(|f| f.lsda() as *mut c_void)
|
||||||
|
@ -13,9 +13,13 @@ pub struct Context {
|
|||||||
|
|
||||||
pub struct Arch;
|
pub struct Arch;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
impl Arch {
|
impl Arch {
|
||||||
pub const SP: Register = X86_64::RSP;
|
pub const SP: Register = X86_64::RSP;
|
||||||
pub const RA: Register = X86_64::RA;
|
pub const RA: Register = X86_64::RA;
|
||||||
|
|
||||||
|
pub const UNWIND_DATA_REG: (Register, Register) = (X86_64::RAX, X86_64::RDX);
|
||||||
|
pub const UNWIND_PRIVATE_DATA_SIZE: usize = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Context {
|
impl fmt::Debug for Context {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![feature(c_unwind)]
|
#![feature(c_unwind)]
|
||||||
#![feature(naked_functions)]
|
#![feature(naked_functions)]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
|
#![cfg_attr(any(feature = "personality", feature = "personality-dummy"), feature(lang_items))]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![warn(unsafe_op_in_unsafe_fn)]
|
#![warn(unsafe_op_in_unsafe_fn)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
@ -14,6 +15,8 @@ mod find_fde;
|
|||||||
mod frame;
|
mod frame;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
#[cfg(feature = "personality")]
|
||||||
|
mod personality;
|
||||||
#[cfg(feature = "personality-dummy")]
|
#[cfg(feature = "personality-dummy")]
|
||||||
mod personality_dummy;
|
mod personality_dummy;
|
||||||
|
|
||||||
|
181
src/personality.rs
Normal file
181
src/personality.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
// References:
|
||||||
|
// https://github.com/rust-lang/rust/blob/c4be230b4a30eb74e3a3908455731ebc2f731d3d/library/panic_unwind/src/gcc.rs
|
||||||
|
// https://github.com/rust-lang/rust/blob/c4be230b4a30eb74e3a3908455731ebc2f731d3d/library/panic_unwind/src/dwarf/eh.rs
|
||||||
|
// https://docs.rs/gimli/0.25.0/src/gimli/read/cfi.rs.html
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
use gimli::{constants, NativeEndian};
|
||||||
|
use gimli::{EndianSlice, Error, Pointer, Reader};
|
||||||
|
|
||||||
|
use crate::abi::*;
|
||||||
|
use crate::arch::*;
|
||||||
|
use crate::util::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum EHAction {
|
||||||
|
None,
|
||||||
|
Cleanup(usize),
|
||||||
|
Catch(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_pointer_encoding(input: &mut StaticSlice) -> gimli::Result<constants::DwEhPe> {
|
||||||
|
let eh_pe = input.read_u8()?;
|
||||||
|
let eh_pe = constants::DwEhPe(eh_pe);
|
||||||
|
|
||||||
|
if eh_pe.is_valid_encoding() {
|
||||||
|
Ok(eh_pe)
|
||||||
|
} else {
|
||||||
|
Err(gimli::Error::UnknownPointerEncoding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_encoded_pointer(
|
||||||
|
encoding: constants::DwEhPe,
|
||||||
|
unwind_ctx: &UnwindContext<'_>,
|
||||||
|
input: &mut StaticSlice,
|
||||||
|
) -> gimli::Result<Pointer> {
|
||||||
|
if encoding == constants::DW_EH_PE_omit {
|
||||||
|
return Err(Error::CannotParseOmitPointerEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = match encoding.application() {
|
||||||
|
constants::DW_EH_PE_absptr => 0,
|
||||||
|
constants::DW_EH_PE_pcrel => input.slice().as_ptr() as u64,
|
||||||
|
constants::DW_EH_PE_textrel => _Unwind_GetTextRelBase(unwind_ctx) as u64,
|
||||||
|
constants::DW_EH_PE_datarel => _Unwind_GetDataRelBase(unwind_ctx) as u64,
|
||||||
|
constants::DW_EH_PE_funcrel => _Unwind_GetRegionStart(unwind_ctx) as u64,
|
||||||
|
constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = match encoding.format() {
|
||||||
|
constants::DW_EH_PE_absptr => input.read_address(mem::size_of::<usize>() as _),
|
||||||
|
constants::DW_EH_PE_uleb128 => input.read_uleb128(),
|
||||||
|
constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from),
|
||||||
|
constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from),
|
||||||
|
constants::DW_EH_PE_udata8 => input.read_u64(),
|
||||||
|
constants::DW_EH_PE_sleb128 => input.read_sleb128().map(|a| a as u64),
|
||||||
|
constants::DW_EH_PE_sdata2 => input.read_i16().map(|a| a as u64),
|
||||||
|
constants::DW_EH_PE_sdata4 => input.read_i32().map(|a| a as u64),
|
||||||
|
constants::DW_EH_PE_sdata8 => input.read_i64().map(|a| a as u64),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let address = base.wrapping_add(offset);
|
||||||
|
Ok(if encoding.is_indirect() {
|
||||||
|
Pointer::Indirect(address)
|
||||||
|
} else {
|
||||||
|
Pointer::Direct(address)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_eh_action(
|
||||||
|
reader: &mut StaticSlice,
|
||||||
|
unwind_ctx: &UnwindContext<'_>,
|
||||||
|
) -> gimli::Result<EHAction> {
|
||||||
|
let func_start = _Unwind_GetRegionStart(unwind_ctx);
|
||||||
|
let mut ip_before_instr = 0;
|
||||||
|
let ip = _Unwind_GetIPInfo(unwind_ctx, &mut ip_before_instr);
|
||||||
|
let ip = if ip_before_instr != 0 { ip } else { ip - 1 };
|
||||||
|
|
||||||
|
let start_encoding = parse_pointer_encoding(reader)?;
|
||||||
|
let lpad_base = if !start_encoding.is_absent() {
|
||||||
|
unsafe { deref_pointer(parse_encoded_pointer(start_encoding, unwind_ctx, reader)?) }
|
||||||
|
} else {
|
||||||
|
func_start
|
||||||
|
};
|
||||||
|
|
||||||
|
let ttype_encoding = parse_pointer_encoding(reader)?;
|
||||||
|
if !ttype_encoding.is_absent() {
|
||||||
|
reader.read_uleb128()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let call_site_encoding = parse_pointer_encoding(reader)?;
|
||||||
|
let call_site_table_length = reader.read_uleb128()?;
|
||||||
|
reader.truncate(call_site_table_length as _)?;
|
||||||
|
|
||||||
|
while !reader.is_empty() {
|
||||||
|
let cs_start = unsafe {
|
||||||
|
deref_pointer(parse_encoded_pointer(
|
||||||
|
call_site_encoding,
|
||||||
|
unwind_ctx,
|
||||||
|
reader,
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
let cs_len = unsafe {
|
||||||
|
deref_pointer(parse_encoded_pointer(
|
||||||
|
call_site_encoding,
|
||||||
|
unwind_ctx,
|
||||||
|
reader,
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
let cs_lpad = unsafe {
|
||||||
|
deref_pointer(parse_encoded_pointer(
|
||||||
|
call_site_encoding,
|
||||||
|
unwind_ctx,
|
||||||
|
reader,
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
let cs_action = reader.read_uleb128()?;
|
||||||
|
if ip < func_start + cs_start {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ip < func_start + cs_start + cs_len {
|
||||||
|
if cs_lpad == 0 {
|
||||||
|
return Ok(EHAction::None);
|
||||||
|
} else {
|
||||||
|
let lpad = lpad_base + cs_lpad;
|
||||||
|
return Ok(match cs_action {
|
||||||
|
0 => EHAction::Cleanup(lpad),
|
||||||
|
_ => EHAction::Catch(lpad),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(EHAction::None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "eh_personality"]
|
||||||
|
fn rust_eh_personality(
|
||||||
|
version: c_int,
|
||||||
|
actions: UnwindAction,
|
||||||
|
_exception_class: u64,
|
||||||
|
exception: &mut UnwindException,
|
||||||
|
unwind_ctx: &mut UnwindContext<'_>,
|
||||||
|
) -> UnwindReasonCode {
|
||||||
|
if version != 1 {
|
||||||
|
return UnwindReasonCode::FATAL_PHASE1_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lsda = _Unwind_GetLanguageSpecificData(unwind_ctx);
|
||||||
|
if lsda.is_null() {
|
||||||
|
return UnwindReasonCode::CONTINUE_UNWIND;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut lsda = EndianSlice::new(unsafe { get_unlimited_slice(lsda as _) }, NativeEndian);
|
||||||
|
let eh_action = match find_eh_action(&mut lsda, unwind_ctx) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return UnwindReasonCode::FATAL_PHASE1_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
if actions.contains(UnwindAction::SEARCH_PHASE) {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None | EHAction::Cleanup(_) => UnwindReasonCode::CONTINUE_UNWIND,
|
||||||
|
EHAction::Catch(_) => UnwindReasonCode::HANDLER_FOUND,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None => UnwindReasonCode::CONTINUE_UNWIND,
|
||||||
|
EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
|
||||||
|
_Unwind_SetGR(
|
||||||
|
unwind_ctx,
|
||||||
|
Arch::UNWIND_DATA_REG.0 .0 as _,
|
||||||
|
exception as *mut _ as usize,
|
||||||
|
);
|
||||||
|
_Unwind_SetGR(unwind_ctx, Arch::UNWIND_DATA_REG.1 .0 as _, 0);
|
||||||
|
_Unwind_SetIP(unwind_ctx, lpad);
|
||||||
|
UnwindReasonCode::INSTALL_CONTEXT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user