use crate::arch::current::context::TrapFrame;
#[repr(C)]
struct ExceptionTableEntry {
from: i32,
to: i32,
}
impl ExceptionTableEntry {
fn address(field: &i32, table: *const Self) -> usize {
let base = if crate::arch::current::EX_TABLE_RELATIVE_TO_START {
table as usize
} else {
field as *const i32 as usize
};
base.wrapping_add_signed(*field as isize)
}
}
unsafe extern "C" {
static _ex_table_start: [ExceptionTableEntry; 0];
static _ex_table_end: [ExceptionTableEntry; 0];
static _nofault_ex_table_start: [ExceptionTableEntry; 0];
static _nofault_ex_table_end: [ExceptionTableEntry; 0];
}
unsafe fn lookup(
start: *const ExceptionTableEntry,
end: *const ExceptionTableEntry,
pc: usize,
) -> Option<usize> {
let bytes = (end as usize)
.checked_sub(start as usize)
.expect("reversed exception-table linker bounds");
assert!(bytes.is_multiple_of(core::mem::size_of::<ExceptionTableEntry>()));
let entries = unsafe {
core::slice::from_raw_parts(start, bytes / core::mem::size_of::<ExceptionTableEntry>())
};
entries.iter().find_map(|entry| {
(ExceptionTableEntry::address(&entry.from, start) == pc)
.then(|| ExceptionTableEntry::address(&entry.to, start))
})
}
impl TrapFrame {
pub(crate) fn fixup_nofault_exception(&mut self) -> bool {
let recovery = unsafe {
lookup(
_nofault_ex_table_start.as_ptr(),
_nofault_ex_table_end.as_ptr(),
self.ip(),
)
};
if let Some(pc) = recovery {
self.set_ip(pc);
}
recovery.is_some()
}
pub(crate) fn fixup_exception(&mut self) -> bool {
let recovery =
unsafe { lookup(_ex_table_start.as_ptr(), _ex_table_end.as_ptr(), self.ip()) };
if let Some(pc) = recovery {
self.set_ip(pc);
}
recovery.is_some()
}
}