123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- // 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
- }
- }
- }
- }
|