exception_x64.rs 8.44 KB
Newer Older
1 2 3 4
use ast::ir::*;
use compiler::machine_code::CompiledFunction;
use compiler::backend::x86_64;
use utils::Address;
qinsoon's avatar
qinsoon committed
5
use utils::Word;
6 7 8 9 10 11
use utils::POINTER_SIZE;
use runtime::thread;

use std::sync::RwLock;
use std::sync::RwLockReadGuard;
use std::collections::HashMap;
12
use std::fmt;
13 14

#[no_mangle]
qinsoon's avatar
qinsoon committed
15
#[allow(unreachable_code)]
qinsoon's avatar
qinsoon committed
16
pub extern fn muentry_throw_exception(exception_obj: Address) {
17 18 19 20 21 22 23 24 25 26
    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()};
qinsoon's avatar
qinsoon committed
27 28
    trace!("current frame RBP: 0x{:x}", rust_frame_rbp);
    inspect_nearby_address(rust_frame_rbp, 5);    
29
    let rust_frame_return_addr = unsafe {rust_frame_rbp.plus(POINTER_SIZE).load::<Address>()};
30
    trace!("return address   : 0x{:x} - throw instruction", rust_frame_return_addr);
31 32 33
    
    // the return address is within throwing frame
    let throw_frame_callsite = rust_frame_return_addr;
34 35
    let (throw_func, throw_fv) = find_func_for_address(&cf_lock, throw_frame_callsite);
    trace!("throwing fucntion: {}", throw_func);
36 37 38 39 40 41 42 43 44
    
    // skip to previous frame
    // this is the frame that throws the exception
    let rbp = unsafe {rust_frame_rbp.load::<Address>()};
    
    // set cursor to throwing frame
    let mut cursor = FrameCursor {
        rbp: rbp,
        return_addr: unsafe {rbp.plus(POINTER_SIZE).load::<Address>()},
45 46
        func_id: throw_func,
        func_ver_id: throw_fv,
47 48
        callee_saved_locs: HashMap::new()
    };
49
    trace!("cursor at first Mu frame: {}", cursor);
50
    
51 52 53
    let mut callsite = rust_frame_return_addr;
    
    trace!("Stack Unwinding starts");
54
    loop {
55
        trace!("frame cursor: {}", cursor);
56 57 58
        // get return address (the slot above RBP slot)
//        let return_addr = unsafe {rbp.plus(POINTER_SIZE).load::<Address>()};
        
59 60 61 62 63 64 65 66 67 68
        // 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()
69
        };
70
        trace!("frame info: {}", frame);
71 72 73 74
        
        // update callee saved register location
        for reg in x86_64::CALLEE_SAVED_GPRs.iter() {
            let reg_id = reg.id();
qinsoon's avatar
qinsoon committed
75
            trace!("update callee saved register {}", reg_id);
76 77 78 79
            if frame.allocated.contains_key(&reg_id) {
                let offset_from_rbp = frame.allocated.get(&reg_id).unwrap().offset;
                let reg_restore_addr = cursor.rbp.offset(offset_from_rbp);
                
80
                trace!("update callee saved register {} with loc 0x{:x}", reg_id, reg_restore_addr);
81
                cursor.callee_saved_locs.insert(reg_id, reg_restore_addr);
qinsoon's avatar
qinsoon committed
82 83 84 85 86 87 88
            } 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");
                }
89 90 91
            }
        }
        
qinsoon's avatar
qinsoon committed
92
        // find exception block - comparing callsite with frame info
93
        trace!("checking catch block: looking for callsite 0x{:x}", callsite);
qinsoon's avatar
qinsoon committed
94 95
        let exception_callsites = frame.get_exception_callsites();
        for &(ref possible_callsite, ref dest) in exception_callsites.iter() {
qinsoon's avatar
qinsoon committed
96
            let possible_callsite_addr = possible_callsite.to_address();
97
            trace!("..check {} at 0x{:x}", possible_callsite, possible_callsite_addr);
qinsoon's avatar
qinsoon committed
98 99
            
            if callsite == possible_callsite_addr {
100
                trace!("found catch block at {}", dest);
qinsoon's avatar
qinsoon committed
101 102 103 104
                // found an exception block
                let dest_addr = dest.to_address();
                
                // restore callee saved register and jump to dest_addr
qinsoon's avatar
qinsoon committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
                
                // 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::<Word>()},
                            None => {
                                warn!("no {} value was saved along unwinding, please check", $reg.name().unwrap());
                                0
                            }
                        }
                    }
                };
                
                let rbx = unpack_callee_saved_from_cursor!(x86_64::RBX);
                let r12 = unpack_callee_saved_from_cursor!(x86_64::R12);
                let r13 = unpack_callee_saved_from_cursor!(x86_64::R13);
                let r14 = unpack_callee_saved_from_cursor!(x86_64::R14);
                let r15 = unpack_callee_saved_from_cursor!(x86_64::R15);
                let rbp = cursor.rbp.as_usize() as Word;
                let array = vec![rbx, rbp, r12, r13, r14, r15];
                
                let rsp = cursor.rbp.offset(frame.cur_offset());
                
                unsafe {thread::exception_restore(dest_addr, array.as_ptr(), rsp)};
                
                unreachable!()
qinsoon's avatar
qinsoon committed
132 133
            }
        }
134
        trace!("didnt find a catch block");
qinsoon's avatar
qinsoon committed
135 136
        
        // keep unwinding
137 138 139
        callsite = cursor.return_addr;
        cursor.to_previous_frame(&cf_lock);
        trace!("cursor unwinds to previous frame: {}", cursor);        
140 141 142
    }
}

qinsoon's avatar
qinsoon committed
143 144 145 146 147 148 149 150 151 152 153 154
fn inspect_nearby_address(base: Address, n: isize) {
    let mut i = n;
    while i >= -n {
        unsafe {
            let addr = base.offset(i * POINTER_SIZE as isize);
            let val  = addr.load::<Word>();
            trace!("addr: 0x{:x} | val: 0x{:x}", addr, val);
        }
        i -= 1;
    }
}

155 156 157 158
struct FrameCursor {
    rbp: Address,
    return_addr: Address,
    func_id: MuID,
159
    func_ver_id: MuID,
160 161 162
    callee_saved_locs: HashMap<MuID, Address>
}

163 164 165 166 167 168 169 170 171 172 173 174
impl fmt::Display for FrameCursor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "FrameCursor{{").unwrap();
        writeln!(f, "  rbp=0x{:x}, return_addr=0x{:x}, func_id={}, func_version_id={}", self.rbp, self.return_addr, self.func_id, self.func_ver_id).unwrap();
        writeln!(f, "  callee_saved:").unwrap();
        for (reg, addr) in self.callee_saved_locs.iter() {
            writeln!(f, "    #{} at 0x{:x}", reg, addr).unwrap()
        }
        writeln!(f, "}}")
    }
}

175 176
impl FrameCursor {
    fn to_previous_frame(&mut self, cf: &RwLockReadGuard<HashMap<MuID, RwLock<CompiledFunction>>>) {
177 178 179 180 181 182
        // check if return_addr is valid
        // FIXME: should use a sentinel value here
        if self.return_addr.is_zero() {
            panic!("cannot go to previous frame (return address is zero)");
        }
        
183 184
        let previous_rbp = unsafe {self.rbp.load::<Address>()};
        let previous_return_addr = unsafe {previous_rbp.plus(POINTER_SIZE).load::<Address>()};
185
        let (previous_func, previous_fv_id) = find_func_for_address(cf, self.return_addr);
186 187 188
        
        self.rbp = previous_rbp;
        self.return_addr = previous_return_addr;
189 190
        self.func_id = previous_func;
        self.func_ver_id = previous_fv_id;
191 192 193
    }
}

194 195
fn find_func_for_address (cf: &RwLockReadGuard<HashMap<MuID, RwLock<CompiledFunction>>>, pc_addr: Address) -> (MuID, MuID) {
    trace!("trying to find FuncVersion for address 0x{:x}", pc_addr);
qinsoon's avatar
qinsoon committed
196
    for (_, func) in cf.iter() {
197 198 199 200 201 202
        let func = func.read().unwrap();
        
        let start = func.start.to_address();
        let end = func.end.to_address();
        trace!("CompiledFunction: func_id={}, fv_id={}, start=0x{:x}, end=0x{:x}", func.func_id, func.func_ver_id, start, end);
        
203 204
        // pc won't be the start of a function, but could be the end
        if pc_addr > start && pc_addr <= end {
205 206 207 208 209
            trace!("Found CompiledFunction: func_id={}, fv_id={}", func.func_id, func.func_ver_id);
            return (func.func_id, func.func_ver_id);
        }
    }
    
210
    panic!("cannot find compiled function for pc 0x{:x}", pc_addr);
211
}