Implement rust personality

This commit is contained in:
Gary Guo 2021-08-26 09:54:50 +01:00
parent e205fb9e64
commit f0e87da0aa
5 changed files with 191 additions and 3 deletions

View File

@ -18,6 +18,7 @@ fde-phdr = ["libc"]
fde-registry = ["alloc"]
dwarf-expr = []
hide-trace = []
personality = []
personality-dummy = []
system-alloc = []
default = ["dwarf-expr", "hide-trace", "fde-phdr", "fde-registry"]

View File

@ -75,6 +75,7 @@ pub struct UnwindException {
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
private_1: Option<UnwindStopFn>,
private_2: usize,
private_unused: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
}
pub type UnwindTraceFn =
@ -128,9 +129,7 @@ pub extern "C" fn _Unwind_SetIP(unwind_ctx: &mut UnwindContext<'_>, value: usize
}
#[no_mangle]
pub extern "C" fn _Unwind_GetLanguageSpecificData(
unwind_ctx: &UnwindContext<'_>,
) -> *mut c_void {
pub extern "C" fn _Unwind_GetLanguageSpecificData(unwind_ctx: &UnwindContext<'_>) -> *mut c_void {
unwind_ctx
.frame
.map(|f| f.lsda() as *mut c_void)

View File

@ -13,9 +13,13 @@ pub struct Context {
pub struct Arch;
#[allow(unused)]
impl Arch {
pub const SP: Register = X86_64::RSP;
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 {

View File

@ -1,6 +1,7 @@
#![feature(c_unwind)]
#![feature(naked_functions)]
#![feature(asm)]
#![cfg_attr(any(feature = "personality", feature = "personality-dummy"), feature(lang_items))]
#![warn(rust_2018_idioms)]
#![warn(unsafe_op_in_unsafe_fn)]
#![no_std]
@ -14,6 +15,8 @@ mod find_fde;
mod frame;
mod util;
#[cfg(feature = "personality")]
mod personality;
#[cfg(feature = "personality-dummy")]
mod personality_dummy;

181
src/personality.rs Normal file
View 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
}
}
}
}