#![allow(non_upper_case_globals)]
#![allow(clippy::transmutes_expressible_as_ptr_casts)]
#![allow(clippy::comparison_chain)]
#![allow(unused)]
use core::{mem, ptr};
use gimli::DwEhPe;
use super::DwarfReader;
#[derive(Copy, Clone)]
pub struct EHContext<'a> {
pub ip: *const u8, pub func_start: *const u8, pub get_text_start: &'a dyn Fn() -> *const u8, pub get_data_start: &'a dyn Fn() -> *const u8, }
type LPad = *const u8;
#[derive(Debug, Clone)]
pub enum EHAction {
None,
CatchAll { lpad: LPad },
CatchSpecific { lpad: LPad, tags: Vec<u32> },
CatchSpecificOrAll { lpad: LPad, tags: Vec<u32> },
Terminate,
}
pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(
target_vendor = "apple",
not(target_os = "watchos"),
target_arch = "arm"
));
macro_rules! log {
($e: expr) => {
if false {
eprintln!($e)
}
};
($($e: expr),*) => {
if false {
eprintln!($($e),*)
}
};
}
pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
if lsda.is_null() {
log!("(pers) LSDA is null for IP {:?}", context.ip);
return Ok(EHAction::None);
}
log!("(pers) Analysing LSDA at {lsda:?}");
let func_start = context.func_start;
let mut reader = DwarfReader::new(lsda);
let lpad_start_encoding = unsafe { DwEhPe(reader.read::<u8>()) };
log!("(pers) Read LP start encoding {lpad_start_encoding:?}");
let lpad_base = unsafe {
if lpad_start_encoding != gimli::DW_EH_PE_omit {
read_encoded_pointer(&mut reader, context, lpad_start_encoding)?
} else {
log!("(pers) (is omit)");
func_start
}
};
log!("(pers) read landingpad base: {lpad_base:?}");
let types_table_encoding = unsafe { DwEhPe(reader.read::<u8>()) };
log!("(pers) read ttype encoding: {types_table_encoding:?}");
if types_table_encoding == gimli::DW_EH_PE_omit {
log!("(pers) ttype is omit, returning None");
return Ok(EHAction::None);
}
let types_table_base_offset = unsafe { reader.read_uleb128() };
let types_table_base = unsafe {
log!("(pers) read class_info offset {types_table_base_offset:?}");
reader.ptr.wrapping_add(types_table_base_offset as _)
};
log!("(pers) read types_table_base sits at offset {types_table_base:?}");
let call_site_table_encoding = unsafe { DwEhPe(reader.read::<u8>()) };
log!("(pers) read call_site_table_encoding is {call_site_table_encoding:?}");
let call_site_table_size = unsafe { reader.read_uleb128() };
let action_table = unsafe {
log!("(pers) read call_site has length {call_site_table_size:?}");
reader.ptr.wrapping_add(call_site_table_size as usize)
};
log!("(pers) action table sits at offset {action_table:?}");
let ip = context.ip;
if !USING_SJLJ_EXCEPTIONS {
while reader.ptr < action_table {
let call_site_record_reader = &mut reader;
unsafe {
let call_site_start =
read_encoded_offset(call_site_record_reader, call_site_table_encoding)?;
let call_site_length =
read_encoded_offset(call_site_record_reader, call_site_table_encoding)?;
let call_site_lpad =
read_encoded_offset(call_site_record_reader, call_site_table_encoding)?;
let call_site_action_entry = call_site_record_reader.read_uleb128();
log!("(pers) read cs_start is {call_site_start:?}");
log!("(pers) read cs_len is {call_site_length:?}");
log!("(pers) read cs_lpad is {call_site_lpad:?}");
log!("(pers) read cs_ae is {call_site_action_entry:?}");
if ip < func_start.wrapping_add(call_site_start) {
break;
}
if ip < func_start.wrapping_add(call_site_start + call_site_length) {
log!(
"(pers) found a matching call site: {func_start:?} <= {ip:?} <= {:?}",
func_start.wrapping_add(call_site_start + call_site_length)
);
if call_site_lpad == 0 {
return Ok(EHAction::None);
} else {
let lpad = lpad_base.wrapping_add(call_site_lpad);
let mut catches = vec![];
log!("(pers) lpad sits at {lpad:?}");
if call_site_action_entry == 0 {
return Ok(EHAction::Terminate);
}
log!("(pers) read cs_action_entry: {call_site_action_entry}");
log!("(pers) action_table: {action_table:?}");
let mut action_record: *const u8 =
action_table.wrapping_add((call_site_action_entry - 1) as usize);
log!("(pers) first action at: {action_record:?}");
loop {
let mut action_record_reader = DwarfReader::new(action_record);
let type_filter = action_record_reader.read_sleb128();
log!(
"(pers) type_filter for action #{call_site_action_entry}: {type_filter:?}"
);
if type_filter > 0 {
let types_table_index = type_filter;
if types_table_base.is_null() {
panic!();
}
let tag_ptr = {
let new_types_table_index =
match DwEhPe(types_table_encoding.0 & 0x0f) {
gimli::DW_EH_PE_absptr => {
type_filter * (size_of::<*const u8>() as i64)
}
gimli::DW_EH_PE_sdata2 | gimli::DW_EH_PE_udata2 => {
type_filter * 2
}
gimli::DW_EH_PE_sdata4 | gimli::DW_EH_PE_udata4 => {
type_filter * 4
}
gimli::DW_EH_PE_sdata8 | gimli::DW_EH_PE_udata8 => {
type_filter * 8
}
_ => panic!(),
};
log!(
"(pers) new_types_table_index for action #{call_site_action_entry}: {new_types_table_index:?}"
);
let typeinfo = types_table_base
.wrapping_sub(new_types_table_index as usize);
log!("(pers) reading ttype info from {typeinfo:?}");
read_encoded_pointer(
&mut DwarfReader::new(typeinfo),
context,
types_table_encoding,
)
};
let tag_ptr = tag_ptr.unwrap();
if tag_ptr.is_null() {
if catches.is_empty() {
return Ok(EHAction::CatchAll { lpad });
} else {
return Ok(EHAction::CatchSpecificOrAll {
lpad,
tags: catches,
});
}
}
let tag = std::mem::transmute::<*const u8, *const u32>(tag_ptr)
.read_unaligned();
log!("(pers) read tag {tag:?}");
catches.push(tag);
} else if type_filter == 0 {
return Ok(EHAction::Terminate);
}
let next_action_record = action_record_reader.clone().read_sleb128();
if next_action_record == 0 {
return Ok(if catches.is_empty() {
EHAction::None
} else {
EHAction::CatchSpecific {
lpad,
tags: catches,
}
});
}
action_record = action_record_reader
.ptr
.wrapping_add(next_action_record as usize);
}
}
}
}
}
Ok(EHAction::Terminate)
} else {
todo!()
}
}
#[inline]
fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
if align.is_power_of_two() {
Ok(unrounded.next_multiple_of(align))
} else {
Err(())
}
}
unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: DwEhPe) -> Result<usize, ()> {
if encoding == gimli::DW_EH_PE_omit || encoding.0 & 0xF0 != 0 {
return Err(());
}
let result = unsafe {
match DwEhPe(encoding.0 & 0x0F) {
gimli::DW_EH_PE_absptr => reader.read::<usize>(),
gimli::DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
gimli::DW_EH_PE_udata2 => reader.read::<u16>() as usize,
gimli::DW_EH_PE_udata4 => reader.read::<u32>() as usize,
gimli::DW_EH_PE_udata8 => reader.read::<u64>() as usize,
gimli::DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
gimli::DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
gimli::DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
gimli::DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
_ => return Err(()),
}
};
Ok(result)
}
unsafe fn read_encoded_pointer(
reader: &mut DwarfReader,
context: &EHContext<'_>,
encoding: DwEhPe,
) -> Result<*const u8, ()> {
if encoding == gimli::DW_EH_PE_omit {
return Err(());
}
log!("(pers) About to read encoded pointer at {:?}", reader.ptr);
let base_ptr = match DwEhPe(encoding.0 & 0x70) {
gimli::DW_EH_PE_absptr => {
log!("(pers) encoding is: DW_EH_PE_absptr");
core::ptr::null()
}
gimli::DW_EH_PE_pcrel => {
log!("(pers) encoding is: DW_EH_PE_pcrel");
reader.ptr
}
gimli::DW_EH_PE_funcrel => {
log!("(pers) encoding is: DW_EH_PE_funcrel");
if context.func_start.is_null() {
return Err(());
}
context.func_start
}
gimli::DW_EH_PE_textrel => {
log!("(pers) encoding is: DW_EH_PE_textrel");
(*context.get_text_start)()
}
gimli::DW_EH_PE_datarel => {
log!("(pers) encoding is: DW_EH_PE_datarel");
(*context.get_data_start)()
}
gimli::DW_EH_PE_aligned => {
log!("(pers) encoding is: DW_EH_PE_aligned");
reader.ptr = {
let this = reader.ptr;
let addr = round_up(
{
let this = reader.ptr;
unsafe { mem::transmute::<*const (), usize>(this.cast::<()>()) }
},
mem::size_of::<*const u8>(),
)?;
let self_addr = unsafe { mem::transmute::<*const (), isize>(this.cast::<()>()) };
let dest_addr = addr as isize;
let offset = dest_addr.wrapping_sub(self_addr);
this.wrapping_byte_offset(offset)
};
core::ptr::null()
}
_ => return Err(()),
};
let mut ptr = if base_ptr.is_null() {
if DwEhPe(encoding.0 & 0x0f) != gimli::DW_EH_PE_absptr {
return Err(());
}
unsafe { reader.read::<*const u8>() }
} else {
log!("(pers) since base_ptr is not null, we must an offset");
let offset = unsafe { read_encoded_offset(reader, DwEhPe(encoding.0 & 0x0f))? };
log!("(pers) read offset is {offset:x?}");
if offset == 0 {
core::ptr::null()
} else {
base_ptr.wrapping_add(offset)
}
};
log!("(pers) about to read from {ptr:?}");
if !ptr.is_null() && encoding.0 & gimli::DW_EH_PE_indirect.0 != 0 {
ptr = unsafe { ptr.cast::<*const u8>().read_unaligned() };
}
log!("(pers) returning ptr value {ptr:?}");
Ok(ptr)
}