forked from M-Labs/nac3
Exception handling: patched exception handling for ARTIQ.
This commit is contained in:
parent
26ac3194d1
commit
eb750eb1f0
|
@ -20,6 +20,8 @@
|
||||||
"max-atomic-width": 32,
|
"max-atomic-width": 32,
|
||||||
"os": "none",
|
"os": "none",
|
||||||
"panic-strategy": "abort",
|
"panic-strategy": "abort",
|
||||||
|
"requires-uwtable": true,
|
||||||
|
"force-unwind-tables": "yes",
|
||||||
"relocation-model": "static",
|
"relocation-model": "static",
|
||||||
"target-c-int-width": "32",
|
"target-c-int-width": "32",
|
||||||
"target-endian": "little",
|
"target-endian": "little",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// From Current artiq firmware ksupport implementation.
|
||||||
|
// Modified to suit the case of artiq-zynq port, for ARM EHABI.
|
||||||
// Portions of the code in this file are derived from code by:
|
// Portions of the code in this file are derived from code by:
|
||||||
//
|
//
|
||||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
@ -8,27 +10,21 @@
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
#![allow(private_no_mangle_fns, non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use core::{ptr, mem};
|
use core::mem;
|
||||||
use cslice::CSlice;
|
use cslice::CSlice;
|
||||||
use unwind as uw;
|
use unwind as uw;
|
||||||
use libc::{c_int, c_void};
|
use libc::{c_int, uintptr_t};
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
use eh::dwarf::{self, EHAction};
|
use dwarf::eh::{self, EHAction, EHContext};
|
||||||
|
|
||||||
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
|
|
||||||
actions: uw::_Unwind_Action,
|
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
|
||||||
exception_class: uw::_Unwind_Exception_Class,
|
|
||||||
exception_object: *mut uw::_Unwind_Exception,
|
#[cfg(target_arch = "arm")]
|
||||||
context: *mut uw::_Unwind_Context,
|
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
|
||||||
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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -42,8 +38,6 @@ pub struct Exception<'a> {
|
||||||
pub param: [i64; 3]
|
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;
|
const MAX_BACKTRACE_SIZE: usize = 128;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -55,61 +49,113 @@ struct ExceptionInfo {
|
||||||
backtrace_size: usize
|
backtrace_size: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
unsafe fn find_eh_action(
|
||||||
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
|
context: *mut uw::_Unwind_Context,
|
||||||
|
foreign_exception: bool,
|
||||||
|
) -> Result<EHAction, ()> {
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
eh::find_eh_action(lsda, &eh_context, foreign_exception)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(target_arch = "or1k"))]
|
pub unsafe fn artiq_personality(state: uw::_Unwind_State,
|
||||||
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
|
exception_object: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context)
|
||||||
#[export_name="__artiq_personality"]
|
-> uw::_Unwind_Reason_Code {
|
||||||
pub extern fn personality(version: c_int,
|
let state = state as c_int;
|
||||||
actions: uw::_Unwind_Action,
|
let action = state & uw::_US_ACTION_MASK as c_int;
|
||||||
uw_exception_class: uw::_Unwind_Exception_Class,
|
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
|
||||||
uw_exception: *mut uw::_Unwind_Exception,
|
// Backtraces on ARM will call the personality routine with
|
||||||
context: *mut uw::_Unwind_Context)
|
// state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
|
||||||
-> uw::_Unwind_Reason_Code {
|
// we want to continue unwinding the stack, otherwise all our backtraces
|
||||||
unsafe {
|
// would end at __rust_try
|
||||||
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
|
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
|
||||||
return uw::_URC_FATAL_PHASE1_ERROR
|
return continue_unwind(exception_object, context);
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
|
||||||
|
false
|
||||||
|
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
|
||||||
|
return continue_unwind(exception_object, context);
|
||||||
|
} else {
|
||||||
|
return uw::_URC_FAILURE;
|
||||||
|
};
|
||||||
|
|
||||||
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
// The DWARF unwinder assumes that _Unwind_Context holds things like the function
|
||||||
let ip = uw::_Unwind_GetIP(context) - 1;
|
// and LSDA pointers, however ARM EHABI places them into the exception object.
|
||||||
let func_start = uw::_Unwind_GetRegionStart(context);
|
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
|
||||||
|
// take only the context pointer, GCC personality routines stash a pointer to
|
||||||
|
// exception_object in the context, using location reserved for ARM's
|
||||||
|
// "scratch register" (r12).
|
||||||
|
uw::_Unwind_SetGR(context,
|
||||||
|
uw::UNWIND_POINTER_REG,
|
||||||
|
exception_object as uw::_Unwind_Ptr);
|
||||||
|
// ...A more principled approach would be to provide the full definition of ARM's
|
||||||
|
// _Unwind_Context in our libunwind bindings and fetch the required data from there
|
||||||
|
// directly, bypassing DWARF compatibility functions.
|
||||||
|
|
||||||
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
|
let exception_class = (*exception_object).exception_class;
|
||||||
let exception = &exception_info.exception.unwrap();
|
let foreign_exception = exception_class != EXCEPTION_CLASS;
|
||||||
|
let eh_action = match find_eh_action(context, foreign_exception) {
|
||||||
let eh_action = dwarf::find_eh_action(lsda, func_start, ip, exception.name);
|
Ok(action) => action,
|
||||||
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
|
Err(_) => return uw::_URC_FAILURE,
|
||||||
match eh_action {
|
};
|
||||||
EHAction::None |
|
let exception_info = &mut *(exception_object as *mut ExceptionInfo);
|
||||||
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
|
let exception = &exception_info.exception.unwrap();
|
||||||
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
|
if search_phase {
|
||||||
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
|
match eh_action {
|
||||||
|
EHAction::None |
|
||||||
|
EHAction::Cleanup(_) => return continue_unwind(exception_object, context),
|
||||||
|
EHAction::Catch(_) => {
|
||||||
|
// EHABI requires the personality routine to update the
|
||||||
|
// SP value in the barrier cache of the exception object.
|
||||||
|
(*exception_object).private[5] =
|
||||||
|
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
|
||||||
|
return uw::_URC_HANDLER_FOUND;
|
||||||
}
|
}
|
||||||
|
EHAction::Terminate => return uw::_URC_FAILURE,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match eh_action {
|
||||||
|
EHAction::None => return continue_unwind(exception_object, context),
|
||||||
|
EHAction::Cleanup(lpad) |
|
||||||
|
EHAction::Catch(lpad) => {
|
||||||
|
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
|
||||||
|
exception_object as uintptr_t);
|
||||||
|
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_FAILURE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On ARM EHABI the personality routine is responsible for actually
|
||||||
|
// unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
|
||||||
|
unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context)
|
||||||
|
-> uw::_Unwind_Reason_Code {
|
||||||
|
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
|
||||||
|
uw::_URC_CONTINUE_UNWIND
|
||||||
} else {
|
} else {
|
||||||
match eh_action {
|
uw::_URC_FAILURE
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// defined in libgcc
|
||||||
|
extern "C" {
|
||||||
|
fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception,
|
||||||
|
context: *mut uw::_Unwind_Context)
|
||||||
|
-> uw::_Unwind_Reason_Code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
||||||
|
@ -121,33 +167,6 @@ extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
|
||||||
uw_exception: uw::_Unwind_Exception {
|
uw_exception: uw::_Unwind_Exception {
|
||||||
exception_class: EXCEPTION_CLASS,
|
exception_class: EXCEPTION_CLASS,
|
||||||
|
@ -160,44 +179,40 @@ static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
|
||||||
backtrace_size: 0
|
backtrace_size: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
#[export_name="__artiq_raise"]
|
|
||||||
#[unwind(allowed)]
|
|
||||||
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
pub unsafe extern fn raise(exception: *const Exception) -> ! {
|
||||||
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
|
// 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
|
// 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.
|
// on the hassle of having to allocate exceptions somewhere except on stack.
|
||||||
|
debug!("Trying to raise exception");
|
||||||
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
|
||||||
INFLIGHT.handled = false;
|
INFLIGHT.handled = false;
|
||||||
|
|
||||||
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
|
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
|
||||||
assert!(result == uw::_URC_END_OF_STACK);
|
assert!(result == uw::_URC_END_OF_STACK);
|
||||||
|
debug!("Result: {:?}", result);
|
||||||
|
|
||||||
INFLIGHT.backtrace_size = 0;
|
INFLIGHT.backtrace_size = 0;
|
||||||
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
|
unimplemented!("top level exception handling");
|
||||||
uncaught_exception, ptr::null_mut());
|
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_name="__artiq_reraise"]
|
|
||||||
#[unwind(allowed)]
|
|
||||||
pub unsafe extern fn reraise() -> ! {
|
pub unsafe extern fn reraise() -> ! {
|
||||||
use cslice::AsCSlice;
|
use cslice::AsCSlice;
|
||||||
|
|
||||||
if INFLIGHT.handled {
|
debug!("Re-raise");
|
||||||
match INFLIGHT.exception {
|
// current implementation uses raise as _Unwind_Resume is not working now
|
||||||
Some(ref exception) => raise(exception),
|
// would debug that later.
|
||||||
None => raise(&Exception {
|
match INFLIGHT.exception {
|
||||||
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
|
Some(ref exception) => raise(exception),
|
||||||
file: file!().as_c_slice(),
|
None => raise(&Exception {
|
||||||
line: line!(),
|
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
|
||||||
column: column!(),
|
file: file!().as_c_slice(),
|
||||||
// https://github.com/rust-lang/rfcs/pull/1719
|
line: line!(),
|
||||||
function: "__artiq_reraise".as_c_slice(),
|
column: column!(),
|
||||||
message: "No active exception to reraise".as_c_slice(),
|
// https://github.com/rust-lang/rfcs/pull/1719
|
||||||
param: [0, 0, 0]
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use libcortex_a9::{cache::dcci_slice, mutex::Mutex, sync_channel::{self, sync_ch
|
||||||
use libsupport_zynq::boot::Core1;
|
use libsupport_zynq::boot::Core1;
|
||||||
|
|
||||||
use dyld;
|
use dyld;
|
||||||
|
use crate::eh_artiq;
|
||||||
use crate::rpc;
|
use crate::rpc;
|
||||||
use crate::rtio;
|
use crate::rtio;
|
||||||
|
|
||||||
|
@ -132,10 +133,6 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn exception_unimplemented() {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! api {
|
macro_rules! api {
|
||||||
($i:ident) => ({
|
($i:ident) => ({
|
||||||
extern { static $i: u8; }
|
extern { static $i: u8; }
|
||||||
|
@ -250,12 +247,13 @@ fn resolve(required: &[u8]) -> Option<u32> {
|
||||||
api!(__aeabi_memclr8),
|
api!(__aeabi_memclr8),
|
||||||
api!(__aeabi_memclr4),
|
api!(__aeabi_memclr4),
|
||||||
api!(__aeabi_memclr),
|
api!(__aeabi_memclr),
|
||||||
|
// libc
|
||||||
|
api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }),
|
||||||
// exceptions
|
// exceptions
|
||||||
api!(_Unwind_Resume = exception_unimplemented),
|
api!(_Unwind_Resume = unwind::_Unwind_Resume),
|
||||||
api!(__artiq_personality = exception_unimplemented),
|
api!(__artiq_personality = eh_artiq::artiq_personality),
|
||||||
api!(__artiq_raise = exception_unimplemented),
|
api!(__artiq_raise = eh_artiq::raise),
|
||||||
api!(__artiq_reraise = exception_unimplemented),
|
api!(__artiq_reraise = eh_artiq::reraise),
|
||||||
|
|
||||||
];
|
];
|
||||||
api.iter()
|
api.iter()
|
||||||
|
|
|
@ -23,6 +23,7 @@ mod rtio;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
mod moninj;
|
mod moninj;
|
||||||
mod load_pl;
|
mod load_pl;
|
||||||
|
mod eh_artiq;
|
||||||
|
|
||||||
fn identifier_read(buf: &mut [u8]) -> &str {
|
fn identifier_read(buf: &mut [u8]) -> &str {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
Loading…
Reference in New Issue