WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.6% of users enabled 2FA.

exception.rs 7.03 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright 2017 The Australian National University
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

15 16 17
use compiler::backend::*;
use utils::Address;
use utils::POINTER_SIZE;
18 19 20
use std::collections::HashMap;
use std::ops::Deref;
use compiler::machine_code::CompiledCallsite;
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
use runtime::*;

// muentry_throw_exception should call this function,
// With the first argument being the address of the exception object,
// And the second argument should be point to the base of the call frame of muentry_throw_exception,
// which saves every callee saved register (note this frame will be modified by this function).
// e.g. on aarch64 (where the values are the value of the registers immediatly before the first instruction in muentry_throw_exception is executed):
//                  Return Address              (value of X30)
// frame_cursor --> Frame Pointer               (value of X29)
//                  First Callee Saved Register (value of X19)
//                  .........
//                  Last Callee Saved Register  (value of D15)
// The actual offsets of the callee saved registers is determined by get_callee_saved_offset (relative to frame_cursor)
// The location of Frame Pointer and Return address is architecture dependent
// (and are accesed by get/set_return_address and get/set_previous_frame and may be passed real frame pointers or the frame cursor)

#[no_mangle]
pub extern fn throw_exception_internal(exception_obj: Address, frame_cursor: Address) -> !  {
    trace!("throwing exception: {}", exception_obj);

    if cfg!(debug_assertions) {
        trace!("Initial Frame: ");
        print_frame(frame_cursor);
    }

    let ref mut cur_thread = thread::MuThread::current_mut();
    // set exception object
    cur_thread.exception_obj = exception_obj;
    let ref vm = cur_thread.vm;

    let mut current_frame_pointer = frame_cursor; // this will be 16 bytes bellow the bottom of the previous frame
    let mut callsite = get_return_address(current_frame_pointer);
    let mut previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer); // thrower::fp, the starting point of the previous frame

55
    // acquire lock for exception table
56
    let compiled_callsite_table = vm.compiled_callsite_table.read().unwrap();
57

58 59 60 61 62
    loop {
        // Lookup the table for the callsite
        trace!("Callsite: 0x{:x}", callsite);
        trace!("\tprevious_frame_pointer: 0x{:x}", previous_frame_pointer);
        trace!("\tcurrent_frame_pointer: 0x{:x}", current_frame_pointer);
63 64 65
        //CompiledCallsite
        let callsite_info = {
            let table_entry = compiled_callsite_table.get(&callsite);
66

67 68
            if table_entry.is_none() {
                error!("Cannot find Mu callsite (i.e. we have reached a native frame), either there isn't a catch block to catch the exception or your catch block is above a native function call");
69 70
                print_backtrace(frame_cursor, compiled_callsite_table.deref()); // This function may segfault
                panic!("Uncaught Mu Exception");
71
            }
72

73 74
            table_entry.unwrap()
        };
75 76

        // Check for a catch block at this callsite (there won't be one on the first iteration of this loop)
77 78
        if callsite_info.exceptional_destination.is_some() {
            let catch_address = callsite_info.exceptional_destination.unwrap();
79
            trace!("Found catch block: 0x{:x}", catch_address);
80
            let sp = get_previous_stack_pointer(current_frame_pointer, callsite_info.stack_args_size);
81 82 83 84 85 86 87 88
            trace!("\tRestoring SP to: 0x{:x}", sp);

            if cfg!(debug_assertions) {
                trace!("Restoring frame: ");
                print_frame(frame_cursor);
            }

            // Found a catch block, branch to it
89
            // drop(compiled_callsite_table); // TODO: Work out how to make the borrow checker let me do this
90 91 92 93 94
            unsafe { thread::exception_restore(catch_address, frame_cursor.to_ptr(), sp); }
        }

        // Restore callee saved registers
        unsafe {
95
            for (target_offset, source_offset) in callsite_info.callee_saved_registers.iter() {
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
                // *(frame_cursor + target_offset) = *(frame_pointer + source_offset)
                let val = previous_frame_pointer.offset(*source_offset).load::<Address>();
                frame_cursor.offset(*target_offset).store::<Address>(val);
            }
        }

        // Move up to the previous frame
        current_frame_pointer = previous_frame_pointer;
        previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer);

        // Restore the callsite
        callsite = get_return_address(current_frame_pointer);
        set_return_address(frame_cursor, callsite);
        set_previous_frame_pointer(frame_cursor, previous_frame_pointer);
    }
}

fn print_frame(base: Address) {
    let top = 2;
    let bottom = -(CALLEE_SAVED_COUNT as isize);
    for i in (bottom .. top).rev() {
        unsafe {
            let addr = base.offset(i * POINTER_SIZE as isize);
            let val  = addr.load::<Word>();
            trace!("\taddr: 0x{:x} | val: 0x{:x} {}", addr, val, {if addr == base {"<- base"} else {""}});
        }

    }
}

126
// This function may segfault when it reaches the bottom of the stack
127
// (TODO: Determine where the bottom is without segfaulting)
128
fn print_backtrace(base: Address, compiled_callsite_table: &HashMap<Address, CompiledCallsite>) {
129 130 131 132
    error!("BACKTRACE: ");

    let cur_thread = thread::MuThread::current();
    let ref vm = cur_thread.vm;
133 134
    // compiled_funcs: RwLock<HashMap<MuID, RwLock<CompiledFunction>>>;
    let compiled_funcs = vm.compiled_funcs().read().unwrap();
135 136 137 138 139 140
    let mut frame_pointer = base;
    let mut frame_count = 0;

    loop {
        let callsite = get_return_address(frame_pointer);

141 142 143
        if compiled_callsite_table.contains_key(&callsite) {
            let function_version = compiled_callsite_table.get(&callsite).unwrap().function_version;
            let compiled_func = compiled_funcs.get(&function_version).unwrap().read().unwrap();
144

145 146 147
            error!("\tframe {:2}: 0x{:x} - {} (fid: #{}, fvid: #{}) at 0x{:x}", frame_count,
            compiled_func.start.to_address(), vm.name_of(compiled_func.func_id),
            compiled_func.func_id, compiled_func.func_ver_id, callsite);
148 149 150 151 152 153 154
        } else {
            let (func_name, func_start) = get_function_info(callsite);
            error!("\tframe {:2}: 0x{:x} - {} at 0x{:x}", frame_count, func_start, func_name, callsite);
        }

        frame_pointer = get_previous_frame_pointer(frame_pointer);
        if frame_pointer.is_zero() {
155
            return;
156 157 158 159
        }
        frame_count += 1;
    }
}