mirror of https://github.com/m-labs/artiq.git
219 lines
8.4 KiB
Rust
219 lines
8.4 KiB
Rust
// Portions of the code in this file are derived from code by:
|
|
//
|
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
// file at http://rust-lang.org/COPYRIGHT.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
#![allow(non_camel_case_types)]
|
|
|
|
use core::{ptr, mem};
|
|
use cslice::CSlice;
|
|
use unwind as uw;
|
|
use libc::{c_int, c_void};
|
|
|
|
use eh::dwarf::{self, EHAction, EHContext};
|
|
|
|
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
|
actions: uw::_Unwind_Action,
|
|
exception_class: uw::_Unwind_Exception_Class,
|
|
exception_object: *mut uw::_Unwind_Exception,
|
|
context: *mut uw::_Unwind_Context,
|
|
stop_parameter: *mut c_void)
|
|
-> uw::_Unwind_Reason_Code;
|
|
extern {
|
|
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
|
|
stop_fn: _Unwind_Stop_Fn,
|
|
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
pub struct Exception<'a> {
|
|
pub name: CSlice<'a, u8>,
|
|
pub file: CSlice<'a, u8>,
|
|
pub line: u32,
|
|
pub column: u32,
|
|
pub function: CSlice<'a, u8>,
|
|
pub message: CSlice<'a, u8>,
|
|
pub param: [i64; 3]
|
|
}
|
|
|
|
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
|
|
|
|
const MAX_BACKTRACE_SIZE: usize = 128;
|
|
|
|
#[repr(C)]
|
|
struct ExceptionInfo {
|
|
uw_exception: uw::_Unwind_Exception,
|
|
exception: Option<Exception<'static>>,
|
|
handled: bool,
|
|
backtrace: [usize; MAX_BACKTRACE_SIZE],
|
|
backtrace_size: usize
|
|
}
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
|
|
|
|
#[cfg(any(target_arch = "or1k"))]
|
|
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
|
|
|
|
#[cfg(any(target_arch = "riscv32"))]
|
|
const UNWIND_DATA_REG: (i32, i32) = (10, 11); // X10, X11
|
|
|
|
#[export_name="__artiq_personality"]
|
|
pub extern fn personality(version: c_int,
|
|
actions: uw::_Unwind_Action,
|
|
uw_exception_class: uw::_Unwind_Exception_Class,
|
|
uw_exception: *mut uw::_Unwind_Exception,
|
|
context: *mut uw::_Unwind_Context)
|
|
-> uw::_Unwind_Reason_Code {
|
|
unsafe {
|
|
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
|
|
return uw::_URC_FATAL_PHASE1_ERROR
|
|
}
|
|
|
|
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
|
let mut ip_before_instr: c_int = 0;
|
|
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
|
|
let eh_context = EHContext {
|
|
// The return address points 1 byte past the call instruction,
|
|
// which could be in the next IP range in LSDA range table.
|
|
ip: if ip_before_instr != 0 { ip } else { ip - 1 },
|
|
func_start: uw::_Unwind_GetRegionStart(context),
|
|
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
|
|
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
|
|
};
|
|
|
|
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
let exception = &exception_info.exception.unwrap();
|
|
|
|
let eh_action = match dwarf::find_eh_action(lsda, &eh_context) {
|
|
Ok(action) => action,
|
|
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
|
|
};
|
|
|
|
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
|
|
match eh_action {
|
|
EHAction::None |
|
|
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
|
|
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
|
|
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
|
|
}
|
|
} else {
|
|
match eh_action {
|
|
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
|
|
EHAction::Cleanup(lpad) |
|
|
EHAction::Catch(lpad) => {
|
|
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
|
|
exception_info.handled = true
|
|
}
|
|
|
|
// Pass a pair of the unwinder exception and ARTIQ exception
|
|
// (which immediately follows).
|
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
|
uw_exception as uw::_Unwind_Word);
|
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
|
|
exception as *const _ as uw::_Unwind_Word);
|
|
uw::_Unwind_SetIP(context, lpad);
|
|
return uw::_URC_INSTALL_CONTEXT;
|
|
}
|
|
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
|
uw_exception: *mut uw::_Unwind_Exception) {
|
|
unsafe {
|
|
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
|
|
exception_info.exception = None;
|
|
}
|
|
}
|
|
|
|
extern fn uncaught_exception(_version: c_int,
|
|
actions: uw::_Unwind_Action,
|
|
_uw_exception_class: uw::_Unwind_Exception_Class,
|
|
uw_exception: *mut uw::_Unwind_Exception,
|
|
context: *mut uw::_Unwind_Context,
|
|
_stop_parameter: *mut c_void)
|
|
-> uw::_Unwind_Reason_Code {
|
|
unsafe {
|
|
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
|
|
|
if exception_info.backtrace_size < exception_info.backtrace.len() {
|
|
let ip = uw::_Unwind_GetIP(context);
|
|
exception_info.backtrace[exception_info.backtrace_size] = ip;
|
|
exception_info.backtrace_size += 1;
|
|
}
|
|
|
|
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
|
|
::terminate(&exception_info.exception.unwrap(),
|
|
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
|
|
} else {
|
|
uw::_URC_NO_REASON
|
|
}
|
|
}
|
|
}
|
|
|
|
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
|
|
// See https://github.com/rust-lang/rust/issues/39498.
|
|
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
|
|
uw_exception: uw::_Unwind_Exception {
|
|
exception_class: EXCEPTION_CLASS,
|
|
exception_cleanup: cleanup,
|
|
private: [0; uw::unwinder_private_data_size],
|
|
},
|
|
exception: None,
|
|
handled: true,
|
|
backtrace: [0; MAX_BACKTRACE_SIZE],
|
|
backtrace_size: 0
|
|
};
|
|
|
|
#[export_name="__artiq_raise"]
|
|
#[unwind(allowed)]
|
|
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
|
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
|
|
// the exception is ever captured. Fortunately, they currently aren't, and we save
|
|
// on the hassle of having to allocate exceptions somewhere except on stack.
|
|
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
|
INFLIGHT.handled = false;
|
|
|
|
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
|
|
assert!(result == uw::_URC_END_OF_STACK);
|
|
|
|
INFLIGHT.backtrace_size = 0;
|
|
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
|
uncaught_exception, ptr::null_mut());
|
|
unreachable!()
|
|
}
|
|
|
|
#[export_name="__artiq_reraise"]
|
|
#[unwind(allowed)]
|
|
pub unsafe extern fn reraise() -> ! {
|
|
use cslice::AsCSlice;
|
|
|
|
if INFLIGHT.handled {
|
|
match INFLIGHT.exception {
|
|
Some(ref exception) => raise(exception),
|
|
None => raise(&Exception {
|
|
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
|
|
file: file!().as_c_slice(),
|
|
line: line!(),
|
|
column: column!(),
|
|
// https://github.com/rust-lang/rfcs/pull/1719
|
|
function: "__artiq_reraise".as_c_slice(),
|
|
message: "No active exception to reraise".as_c_slice(),
|
|
param: [0, 0, 0]
|
|
})
|
|
}
|
|
} else {
|
|
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
|
|
}
|
|
}
|