use ast::ir::*; use compiler::machine_code::CompiledFunction; use compiler::backend::x86_64; use utils::Address; use utils::Word; use utils::POINTER_SIZE; use runtime::thread; use std::sync::RwLock; use std::sync::RwLockReadGuard; use std::collections::HashMap; use std::fmt; #[no_mangle] #[allow(unreachable_code)] pub extern fn muentry_throw_exception(exception_obj: Address) { trace!("throwing exception: {}", exception_obj); let mut cur_thread = thread::MuThread::current_mut(); // set exception object cur_thread.exception_obj = exception_obj; let cf_lock = cur_thread.vm.compiled_funcs().read().unwrap(); // rbp of current frame (mu_throw_exception(), Rust frame) let rust_frame_rbp = unsafe {thread::get_current_frame_rbp()}; trace!("current frame RBP: 0x{:x}", rust_frame_rbp); inspect_nearby_address(rust_frame_rbp, 5); let rust_frame_return_addr = unsafe {rust_frame_rbp.plus(POINTER_SIZE).load::
()}; trace!("return address : 0x{:x} - throw instruction", rust_frame_return_addr); // the return address is within throwing frame let throw_frame_callsite = rust_frame_return_addr; let (throw_func, throw_fv) = find_func_for_address(&cf_lock, throw_frame_callsite); trace!("throwing fucntion: {}", throw_func); // skip to previous frame // this is the frame that throws the exception let rbp = unsafe {rust_frame_rbp.load::()}; // set cursor to throwing frame let mut cursor = FrameCursor { rbp: rbp, return_addr: unsafe {rbp.plus(POINTER_SIZE).load::()}, func_id: throw_func, func_ver_id: throw_fv, callee_saved_locs: HashMap::new() }; trace!("cursor at first Mu frame: {}", cursor); let mut callsite = rust_frame_return_addr; trace!("Stack Unwinding starts"); loop { trace!("frame cursor: {}", cursor); // get return address (the slot above RBP slot) // let return_addr = unsafe {rbp.plus(POINTER_SIZE).load::()}; // release the locks, and keep a clone of the frame // because we may improperly leave this function // FIXME: consider using Rust () -> ! to tell Rust compiler that we may not finish the func let frame = { let rwlock_cf = match cf_lock.get(&cursor.func_ver_id) { Some(ret) => ret, None => panic!("cannot find compiled func with func_id {}, possibly didnt find the right frame for return address", cursor.func_id) }; let rwlock_cf = rwlock_cf.read().unwrap(); rwlock_cf.frame.clone() }; trace!("frame info: {}", frame); // update callee saved register location for reg in x86_64::CALLEE_SAVED_GPRs.iter() { let reg_id = reg.id(); trace!("update callee saved register {}", reg_id); if frame.allocated.contains_key(®_id) { let offset_from_rbp = frame.allocated.get(®_id).unwrap().offset; let reg_restore_addr = cursor.rbp.offset(offset_from_rbp); trace!("update callee saved register {} with loc 0x{:x}", reg_id, reg_restore_addr); cursor.callee_saved_locs.insert(reg_id, reg_restore_addr); } else { // rbp won't find a location if reg_id == x86_64::RBP.id() { } else { warn!("failed to find an entry for it in current frame"); } } } // find exception block - comparing callsite with frame info trace!("checking catch block: looking for callsite 0x{:x}", callsite); let exception_callsites = frame.get_exception_callsites(); for &(ref possible_callsite, ref dest) in exception_callsites.iter() { let possible_callsite_addr = possible_callsite.to_address(); trace!("..check {} at 0x{:x}", possible_callsite, possible_callsite_addr); if callsite == possible_callsite_addr { trace!("found catch block at {}", dest); // found an exception block let dest_addr = dest.to_address(); // restore callee saved register and jump to dest_addr // prepare a plain array [rbx, rbp, r12, r13, r14, r15] macro_rules! unpack_callee_saved_from_cursor { ($reg: expr) => { match cursor.callee_saved_locs.get(&$reg.id()) { Some(addr) => unsafe {addr.load::