exception_x64.rs 8.43 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>()};
        
qinsoon's avatar
qinsoon committed
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();
qinsoon's avatar
qinsoon committed
97
            trace!("..check {} at 0x{:x}", possible_callsite, possible_callsite_addr);
qinsoon's avatar
qinsoon committed
98
99
            
            if callsite == possible_callsite_addr {
qinsoon's avatar
qinsoon committed
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);
        
qinsoon's avatar
qinsoon committed
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);
        }
    }
    
qinsoon's avatar
qinsoon committed
210
    panic!("cannot find compiled function for pc 0x{:x}", pc_addr);
qinsoon's avatar
qinsoon committed
211
}