forked from M-Labs/nac3
Exception handling: copied eh_artiq from current artiq firmware.
This commit is contained in:
parent
0a35f68562
commit
26ac3194d1
203
src/runtime/src/eh_artiq.rs
Normal file
203
src/runtime/src/eh_artiq.rs
Normal file
@ -0,0 +1,203 @@
|
||||
// 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(private_no_mangle_fns, 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};
|
||||
|
||||
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
|
||||
|
||||
#[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 ip = uw::_Unwind_GetIP(context) - 1;
|
||||
let func_start = uw::_Unwind_GetRegionStart(context);
|
||||
|
||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
||||
let exception = &exception_info.exception.unwrap();
|
||||
|
||||
let eh_action = dwarf::find_eh_action(lsda, func_start, ip, exception.name);
|
||||
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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user