exception_x64.rs 8.44 KB
Newer Older
qinsoon's avatar
qinsoon committed
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;
qinsoon's avatar
qinsoon committed
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;
qinsoon's avatar
qinsoon committed
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) {
qinsoon's avatar
qinsoon committed
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);    
qinsoon's avatar
qinsoon committed
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);
qinsoon's avatar
qinsoon committed
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);
qinsoon's avatar
qinsoon committed
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,
qinsoon's avatar
qinsoon committed
47 48
        callee_saved_locs: HashMap::new()
    };
49
    trace!("cursor at first Mu frame: {}", cursor);
qinsoon's avatar
qinsoon committed
50
    
51 52 53
    let mut callsite = rust_frame_return_addr;
    
    trace!("Stack Unwinding starts");
qinsoon's avatar
qinsoon committed
54
    loop {
55
        trace!("frame cursor: {}", cursor);
qinsoon's avatar
qinsoon committed
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()
qinsoon's avatar
qinsoon committed
69
        };
70
        trace!("frame info: {}", frame);
qinsoon's avatar
qinsoon committed
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);
qinsoon's avatar
qinsoon committed
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);
qinsoon's avatar
qinsoon committed
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");
                }
qinsoon's avatar
qinsoon committed
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);        
qinsoon's avatar
qinsoon committed
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;
    }
}

qinsoon's avatar
qinsoon committed
155 156 157 158
struct FrameCursor {
    rbp: Address,
    return_addr: Address,
    func_id: MuID,
159
    func_ver_id: MuID,
qinsoon's avatar
qinsoon committed
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, "}}")
    }
}

qinsoon's avatar
qinsoon committed
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)");
        }
        
qinsoon's avatar
qinsoon committed
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);
qinsoon's avatar
qinsoon committed
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;
qinsoon's avatar
qinsoon committed
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
}