|
@@ -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
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|