From c39394b4d5f800cdc3fd524c17ad589c3f3f3097 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 4 Feb 2017 07:59:02 +0000 Subject: [PATCH] firmware: port the exception handling routines to Rust. --- .../compiler/transforms/llvm_ir_generator.py | 23 +- artiq/firmware/ksupport/Makefile | 2 +- artiq/firmware/ksupport/api.rs | 8 +- artiq/firmware/ksupport/artiq_personality.c | 470 ------------------ artiq/firmware/ksupport/artiq_personality.h | 41 -- artiq/firmware/ksupport/eh.rs | 433 ++++++++++++++++ artiq/firmware/ksupport/lib.rs | 59 ++- artiq/firmware/runtime/rpc_proto.rs | 4 +- artiq/test/libartiq_support/Makefile | 4 - artiq/test/libartiq_support/__cxxabi_config.h | 1 - artiq/test/libartiq_support/artiq_terminate.c | 26 - artiq/test/libartiq_support/artiq_time.c | 15 - artiq/test/libartiq_support/lib.rs | 64 +++ artiq/test/libartiq_support/unwind.h | 329 ------------ artiq/test/lit/lit.cfg | 5 +- 15 files changed, 556 insertions(+), 928 deletions(-) delete mode 100644 artiq/firmware/ksupport/artiq_personality.c delete mode 100644 artiq/firmware/ksupport/artiq_personality.h create mode 100644 artiq/firmware/ksupport/eh.rs delete mode 100644 artiq/test/libartiq_support/Makefile delete mode 100644 artiq/test/libartiq_support/__cxxabi_config.h delete mode 100644 artiq/test/libartiq_support/artiq_terminate.c delete mode 100644 artiq/test/libartiq_support/artiq_time.c create mode 100644 artiq/test/libartiq_support/lib.rs delete mode 100644 artiq/test/libartiq_support/unwind.h diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index b51c9e288..0240d8c61 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -1540,21 +1540,20 @@ class LLVMIRGenerator: for target, typ in insn.clauses(): if typ is None: - llclauseexnnameptr = ll.Constant( - self.llty_of_type(builtins.TStr()).as_pointer(), None) + exnname = "" # see the comment in ksupport/eh.rs else: exnname = "{}:{}".format(typ.id, typ.name) - llclauseexnname = self.llconst_of_const( - ir.Constant(exnname, builtins.TStr())) - llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname)) - if llclauseexnnameptr is None: - llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type, - name="exn.{}".format(exnname)) - llclauseexnnameptr.global_constant = True - llclauseexnnameptr.initializer = llclauseexnname - llclauseexnnameptr.linkage = "private" - llclauseexnnameptr.unnamed_addr = True + llclauseexnname = self.llconst_of_const( + ir.Constant(exnname, builtins.TStr())) + llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname)) + if llclauseexnnameptr is None: + llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type, + name="exn.{}".format(exnname)) + llclauseexnnameptr.global_constant = True + llclauseexnnameptr.initializer = llclauseexnname + llclauseexnnameptr.linkage = "private" + llclauseexnnameptr.unnamed_addr = True lllandingpad.add_clause(ll.CatchClause(llclauseexnnameptr)) if typ is None: diff --git a/artiq/firmware/ksupport/Makefile b/artiq/firmware/ksupport/Makefile index d62932b1d..e4f50e38c 100644 --- a/artiq/firmware/ksupport/Makefile +++ b/artiq/firmware/ksupport/Makefile @@ -22,7 +22,7 @@ all: ksupport.elf $(RUSTOUT)/libksupport.a: $(cargo) --manifest-path $(KSUPPORT_DIRECTORY)/Cargo.toml -ksupport.elf: $(RUSTOUT)/libksupport.a glue.o artiq_personality.o +ksupport.elf: $(RUSTOUT)/libksupport.a glue.o $(LD) $(LDFLAGS) -T $(KSUPPORT_DIRECTORY)/ksupport.ld -o $@ $^ \ -lbase -lm -lcompiler-rt -ldyld -lunwind @chmod -x $@ diff --git a/artiq/firmware/ksupport/api.rs b/artiq/firmware/ksupport/api.rs index ea1093caf..a38d17f9a 100644 --- a/artiq/firmware/ksupport/api.rs +++ b/artiq/firmware/ksupport/api.rs @@ -74,10 +74,10 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(round), /* exceptions */ - api!(_Unwind_Resume), - api!(__artiq_personality), - api!(__artiq_raise), - api!(__artiq_reraise), + api!(_Unwind_Resume = ::unwind::_Unwind_Resume), + api!(__artiq_personality = ::eh::personality), + api!(__artiq_raise = ::eh::raise), + api!(__artiq_reraise = ::eh::reraise), /* proxified syscalls */ api!(core_log), diff --git a/artiq/firmware/ksupport/artiq_personality.c b/artiq/firmware/ksupport/artiq_personality.c deleted file mode 100644 index 510e3f9a1..000000000 --- a/artiq/firmware/ksupport/artiq_personality.c +++ /dev/null @@ -1,470 +0,0 @@ -#include -#include -#include -#include -#include -#include "artiq_personality.h" - -/* Logging */ - -#ifndef NDEBUG -#define EH_LOG0(fmt) fprintf(stderr, "%s: " fmt "\n", __func__) -#define EH_LOG(fmt, ...) fprintf(stderr, "%s: " fmt "\n", __func__, __VA_ARGS__) -#else -#define EH_LOG0(fmt) -#define EH_LOG(fmt, ...) -#endif - -#define EH_FAIL(err) \ - do { \ - fprintf(stderr, "%s fatal: %s\n", __func__, err); \ - abort(); \ - } while(0) - -#define EH_ASSERT(expr) \ - if(!(expr)) EH_FAIL(#expr) - -/* DWARF format handling */ - -enum { - DW_EH_PE_absptr = 0x00, - DW_EH_PE_uleb128 = 0x01, - DW_EH_PE_udata2 = 0x02, - DW_EH_PE_udata4 = 0x03, - DW_EH_PE_udata8 = 0x04, - DW_EH_PE_sleb128 = 0x09, - DW_EH_PE_sdata2 = 0x0A, - DW_EH_PE_sdata4 = 0x0B, - DW_EH_PE_sdata8 = 0x0C, - DW_EH_PE_pcrel = 0x10, - DW_EH_PE_textrel = 0x20, - DW_EH_PE_datarel = 0x30, - DW_EH_PE_funcrel = 0x40, - DW_EH_PE_aligned = 0x50, - DW_EH_PE_indirect = 0x80, - DW_EH_PE_omit = 0xFF -}; - -// Read a uleb128 encoded value and advance pointer -// See Variable Length Data in: http://dwarfstd.org/Dwarf3.pdf -static uintptr_t readULEB128(const uint8_t **data) { - uintptr_t result = 0; - uintptr_t shift = 0; - unsigned char byte; - const uint8_t *p = *data; - - do { - byte = *p++; - result |= (byte & 0x7f) << shift; - shift += 7; - } - while (byte & 0x80); - - *data = p; - - return result; -} - -// Read a sleb128 encoded value and advance pointer -// See Variable Length Data in: http://dwarfstd.org/Dwarf3.pdf -static uintptr_t readSLEB128(const uint8_t **data) { - uintptr_t result = 0; - uintptr_t shift = 0; - unsigned char byte; - const uint8_t *p = *data; - - do { - byte = *p++; - result |= (byte & 0x7f) << shift; - shift += 7; - } - while (byte & 0x80); - - *data = p; - - if ((byte & 0x40) && (shift < (sizeof(result) << 3))) { - result |= (~0 << shift); - } - - return result; -} - -static unsigned getEncodingSize(uint8_t Encoding) { - if (Encoding == DW_EH_PE_omit) - return 0; - - switch (Encoding & 0x0F) { - case DW_EH_PE_absptr: - return sizeof(uintptr_t); - case DW_EH_PE_udata2: - return sizeof(uint16_t); - case DW_EH_PE_udata4: - return sizeof(uint32_t); - case DW_EH_PE_udata8: - return sizeof(uint64_t); - case DW_EH_PE_sdata2: - return sizeof(int16_t); - case DW_EH_PE_sdata4: - return sizeof(int32_t); - case DW_EH_PE_sdata8: - return sizeof(int64_t); - default: - // not supported - abort(); - } -} - -// Read a pointer encoded value and advance pointer -// See Variable Length Data in: http://dwarfstd.org/Dwarf3.pdf -static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { - uintptr_t result = 0; - const uint8_t *p = *data; - - if (encoding == DW_EH_PE_omit) - return(result); - - // first get value - switch (encoding & 0x0F) { - case DW_EH_PE_absptr: - memcpy(&result, p, sizeof(uintptr_t)); - p += sizeof(uintptr_t); - break; - case DW_EH_PE_uleb128: - result = readULEB128(&p); - break; - // Note: This case has not been tested - case DW_EH_PE_sleb128: - result = readSLEB128(&p); - break; - case DW_EH_PE_udata2: - { - uint16_t valu16; - memcpy(&valu16, p, sizeof(uint16_t)); - result = valu16; - } - p += sizeof(uint16_t); - break; - case DW_EH_PE_udata4: - { - uint32_t valu32; - memcpy(&valu32, p, sizeof(uint32_t)); - result = valu32; - } - p += sizeof(uint32_t); - break; - case DW_EH_PE_udata8: - { - uint64_t valu64; - memcpy(&valu64, p, sizeof(uint64_t)); - result = valu64; - } - p += sizeof(uint64_t); - break; - case DW_EH_PE_sdata2: - { - int16_t val16; - memcpy(&val16, p, sizeof(int16_t)); - result = val16; - } - p += sizeof(int16_t); - break; - case DW_EH_PE_sdata4: - { - int32_t val32; - memcpy(&val32, p, sizeof(int32_t)); - result = val32; - } - p += sizeof(int32_t); - break; - case DW_EH_PE_sdata8: - { - int64_t val64; - memcpy(&val64, p, sizeof(int64_t)); - result = val64; - } - p += sizeof(int64_t); - break; - default: - // not supported - abort(); - break; - } - - // then add relative offset - switch (encoding & 0x70) { - case DW_EH_PE_absptr: - // do nothing - break; - case DW_EH_PE_pcrel: - result += (uintptr_t)(*data); - break; - case DW_EH_PE_textrel: - case DW_EH_PE_datarel: - case DW_EH_PE_funcrel: - case DW_EH_PE_aligned: - default: - // not supported - abort(); - break; - } - - // then apply indirection - if (encoding & DW_EH_PE_indirect) { - result = *((uintptr_t*)result); - } - - *data = p; - - return result; -} - - -/* Raising */ - -#define ARTIQ_EXCEPTION_CLASS 0x4152545141525451LL // 'ARTQARTQ' - -static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc); -static _Unwind_Reason_Code __artiq_uncaught_exception( - int version, _Unwind_Action actions, uint64_t exceptionClass, - struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context, - void *stop_parameter); - -struct artiq_raised_exception { - struct _Unwind_Exception unwind; - struct artiq_exception artiq; - int handled; - uintptr_t backtrace[1024]; - size_t backtrace_size; -}; - -static struct artiq_raised_exception inflight; - -void __artiq_raise(struct artiq_exception *artiq_exn) { - EH_LOG("===> raise (name=%.*s, msg=%.*s, params=[%lld,%lld,%lld])", - (int)artiq_exn->name.len, (const char*)artiq_exn->name.ptr, - (int)artiq_exn->name.len, (const char*)artiq_exn->message.ptr, - (long long int)artiq_exn->param[0], - (long long int)artiq_exn->param[1], - (long long int)artiq_exn->param[2]); - - memmove(&inflight.artiq, artiq_exn, sizeof(struct artiq_exception)); - inflight.unwind.exception_class = ARTIQ_EXCEPTION_CLASS; - inflight.unwind.exception_cleanup = &__artiq_cleanup; - inflight.handled = 0; - inflight.backtrace_size = 0; - - _Unwind_Reason_Code result = _Unwind_RaiseException(&inflight.unwind); - EH_ASSERT((result == _URC_END_OF_STACK) && - "Unexpected error during unwinding"); - - // If we're here, there are no handlers, only cleanups. - // Force unwinding anyway; we shall stop at nothing except the end of stack. - result = _Unwind_ForcedUnwind(&inflight.unwind, &__artiq_uncaught_exception, - NULL); - EH_FAIL("_Unwind_ForcedUnwind should not return"); -} - -void __artiq_reraise() { - if(inflight.handled) { - EH_LOG0("===> reraise"); - __artiq_raise(&inflight.artiq); - } else { - EH_LOG0("===> resume"); - _Unwind_Resume(&inflight.unwind); - abort(); - } -} - -/* Unwinding */ - -// The code below does not refer to the `inflight` global. - -static void __artiq_cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc) { - EH_LOG0("===> cleanup"); - struct artiq_raised_exception *inflight = (struct artiq_raised_exception*) exc; - // The in-flight exception is statically allocated, so we don't need to free it. - // But, we clear it to mark it as processed. - memset(&inflight->artiq, 0, sizeof(struct artiq_exception)); -} - -static _Unwind_Reason_Code __artiq_uncaught_exception( - int version, _Unwind_Action actions, uint64_t exceptionClass, - struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context, - void *stop_parameter) { - struct artiq_raised_exception *inflight = - (struct artiq_raised_exception*)exceptionObject; - EH_ASSERT(inflight->backtrace_size < - sizeof(inflight->backtrace) / sizeof(inflight->backtrace[0]) && - "Out of space for backtrace"); - - uintptr_t pc = _Unwind_GetIP(context); - uintptr_t funcStart = _Unwind_GetRegionStart(context); - uintptr_t pcOffset = pc - funcStart; - EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset); - - inflight->backtrace[inflight->backtrace_size] = funcStart + pcOffset; - ++inflight->backtrace_size; - - if(actions & _UA_END_OF_STACK) { - EH_LOG0("end of stack"); - struct slice backtrace = { inflight->backtrace, inflight->backtrace_size }; - __artiq_terminate(&inflight->artiq, backtrace); - } else { - EH_LOG0("continue"); - return _URC_NO_REASON; - } -} - -_Unwind_Reason_Code __artiq_personality( - int version, _Unwind_Action actions, uint64_t exceptionClass, - struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context); -_Unwind_Reason_Code __artiq_personality( - int version, _Unwind_Action actions, uint64_t exceptionClass, - struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) { - EH_LOG("===> entry (actions =%s%s%s%s; class=%08lx; object=%p, context=%p)", - (actions & _UA_SEARCH_PHASE ? " search" : ""), - (actions & _UA_CLEANUP_PHASE ? " cleanup" : ""), - (actions & _UA_HANDLER_FRAME ? " handler" : ""), - (actions & _UA_FORCE_UNWIND ? " force-unwind" : ""), - exceptionClass, exceptionObject, context); - EH_ASSERT((exceptionClass == ARTIQ_EXCEPTION_CLASS) && - "Foreign exceptions are not supported"); - - struct artiq_raised_exception *inflight = - (struct artiq_raised_exception*)exceptionObject; - EH_LOG("=> exception name=%.*s", - (int)inflight->artiq.name.len, (const char*)inflight->artiq.name.ptr); - - // Get a pointer to LSDA. If there's no LSDA, this function doesn't - // actually handle any exceptions. - const uint8_t *lsda = (const uint8_t*) _Unwind_GetLanguageSpecificData(context); - if(lsda == NULL) - return _URC_CONTINUE_UNWIND; - - EH_LOG("lsda=%p", lsda); - - // Get the current instruction pointer and offset it before next - // instruction in the current frame which threw the exception. - uintptr_t pc = _Unwind_GetIP(context) - 1; - - // Get beginning of the current frame's code. - uintptr_t funcStart = _Unwind_GetRegionStart(context); - uintptr_t pcOffset = pc - funcStart; - - EH_LOG("=> pc=%p (%p+%p)", (void*)pc, (void*)funcStart, (void*)pcOffset); - - // Parse LSDA header. - uint8_t lpStartEncoding = *lsda++; - if (lpStartEncoding != DW_EH_PE_omit) { - readEncodedPointer(&lsda, lpStartEncoding); - } - - uint8_t ttypeEncoding = *lsda++; - const uint8_t *classInfo = NULL; - if (ttypeEncoding != DW_EH_PE_omit) { - // Calculate type info locations in emitted dwarf code which - // were flagged by type info arguments to llvm.eh.selector - // intrinsic - uintptr_t classInfoOffset = readULEB128(&lsda); - classInfo = lsda + classInfoOffset; - EH_LOG("classInfo=%p", classInfo); - } - - // Walk call-site table looking for range that includes current PC. - uint8_t callSiteEncoding = *lsda++; - uint32_t callSiteTableLength = readULEB128(&lsda); - const uint8_t *callSiteTableStart = lsda; - const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength; - const uint8_t *actionTableStart = callSiteTableEnd; - const uint8_t *callSitePtr = callSiteTableStart; - - while(callSitePtr < callSiteTableEnd) { - uintptr_t start = readEncodedPointer(&callSitePtr, - callSiteEncoding); - uintptr_t length = readEncodedPointer(&callSitePtr, - callSiteEncoding); - uintptr_t landingPad = readEncodedPointer(&callSitePtr, - callSiteEncoding); - uintptr_t actionValue = readULEB128(&callSitePtr); - - EH_LOG("call site (start=+%p, len=%d, landingPad=+%p, actionValue=%d)", - (void*)start, (int)length, (void*)landingPad, (int)actionValue); - - if(landingPad == 0) { - EH_LOG0("no landing pad, skipping"); - continue; - } - - if((start <= pcOffset) && (pcOffset < (start + length))) { - EH_LOG0("=> call site matches pc"); - - int exceptionMatched = 0; - if(actionValue) { - const uint8_t *actionEntry = actionTableStart + (actionValue - 1); - EH_LOG("actionEntry=%p", actionEntry); - - for(;;) { - // Each emitted DWARF action corresponds to a 2 tuple of - // type info address offset, and action offset to the next - // emitted action. - intptr_t typeInfoOffset = readSLEB128(&actionEntry); - const uint8_t *tempActionEntry = actionEntry; - intptr_t actionOffset = readSLEB128(&tempActionEntry); - EH_LOG("typeInfoOffset=%p actionOffset=%p", - (void*)typeInfoOffset, (void*)actionOffset); - EH_ASSERT((typeInfoOffset >= 0) && "Filter clauses are not supported"); - - if(typeInfoOffset > 0) { - unsigned encodingSize = getEncodingSize(ttypeEncoding); - const uint8_t *typeInfoPtrPtr = classInfo - typeInfoOffset * encodingSize; - const struct slice *typeInfoPtr = - (const struct slice *)readEncodedPointer(&typeInfoPtrPtr, ttypeEncoding); - EH_LOG("encodingSize=%u typeInfoPtrPtr=%p typeInfoPtr=%p", - encodingSize, typeInfoPtrPtr, (void*)typeInfoPtr); - if(typeInfoPtr != NULL) { - EH_LOG("typeInfo=%.*s", (int)typeInfoPtr->len, (const char *)typeInfoPtr->ptr); - } - - if(typeInfoPtr == NULL || - (inflight->artiq.name.len == typeInfoPtr->len && - !memcmp(inflight->artiq.name.ptr, typeInfoPtr->ptr, typeInfoPtr->len))) { - EH_LOG0("matching action found"); - exceptionMatched = 1; - break; - } - } - - if (!actionOffset) - break; - - actionEntry += actionOffset; - } - } - - if(!(actions & _UA_SEARCH_PHASE)) { - EH_LOG0("=> jumping to landing pad"); - - if(actions & _UA_HANDLER_FRAME) - inflight->handled = 1; - - _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), - (uintptr_t)exceptionObject); - _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), - (uintptr_t)&inflight->artiq); - _Unwind_SetIP(context, funcStart + landingPad); - - return _URC_INSTALL_CONTEXT; - } else if(exceptionMatched) { - EH_LOG0("=> handler found"); - - return _URC_HANDLER_FOUND; - } else { - EH_LOG0("=> handler not found"); - - return _URC_CONTINUE_UNWIND; - } - } - } - - return _URC_CONTINUE_UNWIND; -} diff --git a/artiq/firmware/ksupport/artiq_personality.h b/artiq/firmware/ksupport/artiq_personality.h deleted file mode 100644 index 4848e47c0..000000000 --- a/artiq/firmware/ksupport/artiq_personality.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __ARTIQ_PERSONALITY_H -#define __ARTIQ_PERSONALITY_H - -#include -#include - -struct slice { - void *ptr; - size_t len; -}; - -struct artiq_exception { - struct slice name; - struct slice file; - int32_t line; - int32_t column; - struct slice function; - struct slice message; - int64_t param[3]; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/* Provided by the runtime */ -void __artiq_raise(struct artiq_exception *artiq_exn) - __attribute__((noreturn)); -void __artiq_reraise(void) - __attribute__((noreturn)); - -/* Called by the runtime */ -void __artiq_terminate(struct artiq_exception *artiq_exn, - struct slice backtrace) - __attribute__((noreturn)); - -#ifdef __cplusplus -} -#endif - -#endif /* __ARTIQ_PERSONALITY_H */ diff --git a/artiq/firmware/ksupport/eh.rs b/artiq/firmware/ksupport/eh.rs new file mode 100644 index 000000000..7d908340d --- /dev/null +++ b/artiq/firmware/ksupport/eh.rs @@ -0,0 +1,433 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(non_upper_case_globals, non_camel_case_types, dead_code)] + +use core::{ptr, mem}; +use cslice::CSlice; +use unwind as uw; +use libc::{c_int, c_void}; + +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; +} + +const DW_EH_PE_omit: u8 = 0xFF; +const DW_EH_PE_absptr: u8 = 0x00; + +const DW_EH_PE_uleb128: u8 = 0x01; +const DW_EH_PE_udata2: u8 = 0x02; +const DW_EH_PE_udata4: u8 = 0x03; +const DW_EH_PE_udata8: u8 = 0x04; +const DW_EH_PE_sleb128: u8 = 0x09; +const DW_EH_PE_sdata2: u8 = 0x0A; +const DW_EH_PE_sdata4: u8 = 0x0B; +const DW_EH_PE_sdata8: u8 = 0x0C; + +const DW_EH_PE_pcrel: u8 = 0x10; +const DW_EH_PE_textrel: u8 = 0x20; +const DW_EH_PE_datarel: u8 = 0x30; +const DW_EH_PE_funcrel: u8 = 0x40; +const DW_EH_PE_aligned: u8 = 0x50; + +const DW_EH_PE_indirect: u8 = 0x80; + +#[derive(Clone)] +struct DwarfReader { + pub ptr: *const u8, +} + +#[repr(C,packed)] +#[derive(Clone, Copy)] +struct Unaligned(T); + +// This contortion is required due to https://github.com/rust-lang/rust/issues/27060. +impl Unaligned { + fn get(self) -> T { self.0 } +} + +impl DwarfReader { + fn new(ptr: *const u8) -> DwarfReader { + DwarfReader { ptr: ptr } + } + + // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned + // on a 4-byte boundary. This may cause problems on platforms with strict + // alignment requirements. By wrapping data in a "packed" struct, we are + // telling the backend to generate "misalignment-safe" code. + unsafe fn read(&mut self) -> T { + let result = *(self.ptr as *const Unaligned); + self.ptr = self.ptr.offset(mem::size_of::() as isize); + result.get() + } + + // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable + // Length Data". + unsafe fn read_uleb128(&mut self) -> u64 { + let mut shift: usize = 0; + let mut result: u64 = 0; + let mut byte: u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + result + } + + unsafe fn read_sleb128(&mut self) -> i64 { + let mut shift: usize = 0; + let mut result: u64 = 0; + let mut byte: u8; + loop { + byte = self.read::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < 8 * mem::size_of::() && (byte & 0x40) != 0 { + result |= (!0 as u64) << shift; + } + result as i64 + } + + unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize { + fn round_up(unrounded: usize, align: usize) -> usize { + debug_assert!(align.is_power_of_two()); + (unrounded + align - 1) & !(align - 1) + } + + debug_assert!(encoding != DW_EH_PE_omit); + + // DW_EH_PE_aligned implies it's an absolute pointer value + if encoding == DW_EH_PE_aligned { + self.ptr = round_up(self.ptr as usize, mem::size_of::()) as *const u8; + return self.read::() + } + + let value_ptr = self.ptr; + let mut result = match encoding & 0x0F { + DW_EH_PE_absptr => self.read::(), + DW_EH_PE_uleb128 => self.read_uleb128() as usize, + DW_EH_PE_udata2 => self.read::() as usize, + DW_EH_PE_udata4 => self.read::() as usize, + DW_EH_PE_udata8 => self.read::() as usize, + DW_EH_PE_sleb128 => self.read_sleb128() as usize, + DW_EH_PE_sdata2 => self.read::() as usize, + DW_EH_PE_sdata4 => self.read::() as usize, + DW_EH_PE_sdata8 => self.read::() as usize, + _ => panic!(), + }; + + result += match encoding & 0x70 { + DW_EH_PE_absptr => 0, + // relative to address of the encoded value, despite the name + DW_EH_PE_pcrel => value_ptr as usize, + _ => panic!(), + }; + + if encoding & DW_EH_PE_indirect != 0 { + result = *(result as *const usize); + } + + result + } +} + +fn encoding_size(encoding: u8) -> usize { + if encoding == DW_EH_PE_omit { + return 0 + } + + match encoding & 0x0F { + DW_EH_PE_absptr => mem::size_of::(), + DW_EH_PE_udata2 => 2, + DW_EH_PE_udata4 => 4, + DW_EH_PE_udata8 => 8, + DW_EH_PE_sdata2 => 2, + DW_EH_PE_sdata4 => 4, + DW_EH_PE_sdata8 => 8, + _ => panic!() + } +} + +pub enum EHAction { + None, + Cleanup(usize), + Catch(usize), + Terminate, +} + +unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize, + exn_name: CSlice) -> EHAction { + if lsda.is_null() { + return EHAction::None + } + + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::(); + // base address for landing pad offsets + let lpad_base = if start_encoding != DW_EH_PE_omit { + reader.read_encoded_pointer(start_encoding) + } else { + func_start + }; + + let ttype_encoding = reader.read::(); + let ttype_encoding_size = encoding_size(ttype_encoding) as isize; + + let class_info; + if ttype_encoding != DW_EH_PE_omit { + let class_info_offset = reader.read_uleb128(); + class_info = reader.ptr.offset(class_info_offset as isize); + } else { + class_info = ptr::null(); + } + assert!(!class_info.is_null()); + + let call_site_encoding = reader.read::(); + let call_site_table_length = reader.read_uleb128(); + let action_table = reader.ptr.offset(call_site_table_length as isize); + + while reader.ptr < action_table { + let cs_start = reader.read_encoded_pointer(call_site_encoding); + let cs_len = reader.read_encoded_pointer(call_site_encoding); + let cs_lpad = reader.read_encoded_pointer(call_site_encoding); + let cs_action = reader.read_uleb128(); + + if ip < func_start + cs_start { + // Callsite table is sorted by cs_start, so if we've passed the ip, we + // may stop searching. + break + } + if ip > func_start + cs_start + cs_len { + continue + } + + if cs_lpad == 0 { + return EHAction::None + } + + let lpad = lpad_base + cs_lpad; + if cs_action == 0 { + return EHAction::Cleanup(lpad) + } + + let action_entry = action_table.offset((cs_action - 1) as isize); + let mut action_reader = DwarfReader::new(action_entry); + loop { + let type_info_offset = action_reader.read_sleb128() as isize; + let action_offset = action_reader.clone().read_sleb128() as isize; + assert!(type_info_offset >= 0); + + if type_info_offset > 0 { + let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size); + let type_info_ptr = DwarfReader::new(type_info_ptr_ptr) + .read_encoded_pointer(ttype_encoding); + let type_info = *(type_info_ptr as *const CSlice); + + if type_info.as_ref() == exn_name.as_ref() { + return EHAction::Catch(lpad) + } + + if type_info.len() == 0 { + // This is a catch-all clause. We don't compare type_info_ptr with null here + // because, in PIC mode, the OR1K LLVM backend emits a literal zero + // encoded with DW_EH_PE_pcrel, which of course doesn't result in + // a proper null pointer. + return EHAction::Catch(lpad) + } + } + + if action_offset == 0 { + break + } else { + action_reader.ptr = action_reader.ptr.offset(action_offset) + } + } + + return EHAction::None + } + + // the function has a personality but no landing pads; this is fine + EHAction::None +} + +#[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>, + 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 = 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: false, + backtrace: [0; MAX_BACKTRACE_SIZE], + backtrace_size: 0 +}; + +#[export_name="__artiq_raise"] +pub unsafe extern fn raise(exception: *const Exception) -> ! { + // Zing! The Exception<'a> as Exception<'static> cast 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)); + 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"] +pub unsafe extern fn reraise() -> ! { + if INFLIGHT.handled { + raise(&INFLIGHT.exception.unwrap()) + } else { + uw::_Unwind_Resume(&mut INFLIGHT.uw_exception) + } +} diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index 462c930cf..c762da4e5 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -1,9 +1,10 @@ -#![feature(lang_items, asm, libc)] +#![feature(lang_items, asm, libc, panic_unwind)] #![no_std] extern crate alloc_none; #[macro_use] extern crate std_artiq as std; +extern crate unwind; extern crate libc; extern crate byteorder; extern crate board; @@ -22,37 +23,33 @@ mod rpc_proto; mod dyld; mod api; -#[allow(improper_ctypes)] -extern { - fn __artiq_raise(exn: *const ::kernel_proto::Exception) -> !; -} +use core::{mem, ptr, str}; +use std::io::Cursor; +use cslice::{CSlice, AsCSlice}; +use kernel_proto::*; +use dyld::Library; macro_rules! artiq_raise { ($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({ - let exn = $crate::kernel_proto::Exception { - name: concat!("0:artiq.coredevice.exceptions.", $name), - file: file!(), + use cslice::AsCSlice; + let exn = $crate::eh::Exception { + name: concat!("0:artiq.coredevice.exceptions.", $name).as_bytes().as_c_slice(), + file: file!().as_bytes().as_c_slice(), line: line!(), column: column!(), // https://github.com/rust-lang/rfcs/pull/1719 - function: "(Rust function)", - message: $message, + function: "(Rust function)".as_bytes().as_c_slice(), + message: $message.as_bytes().as_c_slice(), param: [$param0, $param1, $param2] }; #[allow(unused_unsafe)] - unsafe { $crate::__artiq_raise(&exn as *const _) } + unsafe { $crate::eh::raise(&exn) } }); ($name:expr, $message:expr) => ({ artiq_raise!($name, $message, 0, 0, 0) }); } -use core::{mem, ptr, str}; -use std::io::Cursor; -use cslice::{CSlice, CMutSlice, AsCSlice}; -use kernel_proto::*; -use dyld::Library; - fn send(request: &Message) { unsafe { mailbox::send(request as *const _ as usize) } while !mailbox::acknowledged() {} @@ -90,6 +87,7 @@ macro_rules! println { #[path = "../runtime/rpc_queue.rs"] mod rpc_queue; mod rtio; +mod eh; #[no_mangle] #[lang = "panic_fmt"] @@ -160,14 +158,23 @@ extern fn recv_rpc(slot: *mut ()) -> usize { recv!(&RpcRecvReply(ref result) => { match result { &Ok(alloc_size) => alloc_size, - &Err(ref exception) => unsafe { __artiq_raise(exception as *const _) } + &Err(ref exception) => + unsafe { + eh::raise(&eh::Exception { + name: exception.name.as_bytes().as_c_slice(), + file: exception.file.as_bytes().as_c_slice(), + line: exception.line, + column: exception.column, + function: exception.function.as_bytes().as_c_slice(), + message: exception.message.as_bytes().as_c_slice(), + param: exception.param + }) + } } }) } -#[no_mangle] -pub extern fn __artiq_terminate(exception: *const kernel_proto::Exception, - mut backtrace: CMutSlice) -> ! { +fn terminate(exception: &eh::Exception, mut backtrace: &mut [usize]) -> ! { let mut cursor = 0; for index in 0..backtrace.len() { if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS { @@ -179,7 +186,15 @@ pub extern fn __artiq_terminate(exception: *const kernel_proto::Exception, send(&NowSave(unsafe { NOW })); send(&RunException { - exception: unsafe { (*exception).clone() }, + exception: kernel_proto::Exception { + name: str::from_utf8(exception.name.as_ref()).unwrap(), + file: str::from_utf8(exception.file.as_ref()).unwrap(), + line: exception.line, + column: exception.column, + function: str::from_utf8(exception.function.as_ref()).unwrap(), + message: str::from_utf8(exception.message.as_ref()).unwrap(), + param: exception.param, + }, backtrace: backtrace }); loop {} diff --git a/artiq/firmware/runtime/rpc_proto.rs b/artiq/firmware/runtime/rpc_proto.rs index 83c716432..bae3ea079 100644 --- a/artiq/firmware/runtime/rpc_proto.rs +++ b/artiq/firmware/runtime/rpc_proto.rs @@ -109,7 +109,7 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: write_u64(writer, *ptr)), Tag::String => consume_value!(CSlice, |ptr| - write_string(writer, str::from_utf8_unchecked((*ptr).as_ref()))), + write_string(writer, str::from_utf8((*ptr).as_ref()).unwrap())), Tag::Tuple(it, arity) => { let mut it = it.clone(); write_u8(writer, arity)?; @@ -141,7 +141,7 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io:: Tag::Keyword(it) => { struct Keyword<'a> { name: CSlice<'a, u8>, contents: () }; consume_value!(Keyword, |ptr| { - write_string(writer, str::from_utf8_unchecked((*ptr).name.as_ref()))?; + write_string(writer, str::from_utf8((*ptr).name.as_ref()).unwrap())?; let tag = it.clone().next().expect("truncated tag"); let mut data = &(*ptr).contents as *const (); send_value(writer, tag, &mut data) diff --git a/artiq/test/libartiq_support/Makefile b/artiq/test/libartiq_support/Makefile deleted file mode 100644 index 0eca4569c..000000000 --- a/artiq/test/libartiq_support/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -CC ?= clang - -libartiq_support.so: ../../firmware/ksupport/artiq_personality.c artiq_terminate.c artiq_time.c - $(CC) -std=c99 -Wall -Werror -I. -I../../firmware/ksupport -g -fPIC -shared -o $@ $^ diff --git a/artiq/test/libartiq_support/__cxxabi_config.h b/artiq/test/libartiq_support/__cxxabi_config.h deleted file mode 100644 index 42cd6fe5c..000000000 --- a/artiq/test/libartiq_support/__cxxabi_config.h +++ /dev/null @@ -1 +0,0 @@ -#define LIBCXXABI_ARM_EHABI 0 diff --git a/artiq/test/libartiq_support/artiq_terminate.c b/artiq/test/libartiq_support/artiq_terminate.c deleted file mode 100644 index 455566a42..000000000 --- a/artiq/test/libartiq_support/artiq_terminate.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include -#include - -#define __USE_GNU -#include - -void __artiq_terminate(struct artiq_exception *exn, - struct slice backtrace) { - printf("Uncaught %.*s: %.*s (%"PRIi64", %"PRIi64", %"PRIi64")\n" - "at %.*s:%"PRIi32":%"PRIi32"\n", - (int)exn->name.len, (const char *)exn->name.ptr, - (int)exn->message.len, (const char *)exn->message.ptr, - exn->param[0], exn->param[1], exn->param[1], - (int)exn->file.len, (const char *)exn->file.ptr, - exn->line, - exn->column + 1); - - for(size_t i = 0; i < backtrace.len; i++) { - printf("at %"PRIxPTR"\n", ((uintptr_t*)backtrace.ptr)[i]); - } - - exit(1); -} diff --git a/artiq/test/libartiq_support/artiq_time.c b/artiq/test/libartiq_support/artiq_time.c deleted file mode 100644 index 18de56424..000000000 --- a/artiq/test/libartiq_support/artiq_time.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -int64_t now = 0; - -int watchdog_set(long long ms) -{ - printf("watchdog_set %lld\n", ms); - return ms; -} - -void watchdog_clear(int id) -{ - printf("watchdog_clear %d\n", id); -} diff --git a/artiq/test/libartiq_support/lib.rs b/artiq/test/libartiq_support/lib.rs new file mode 100644 index 000000000..7dba1b7b8 --- /dev/null +++ b/artiq/test/libartiq_support/lib.rs @@ -0,0 +1,64 @@ +#![feature(libc, panic_unwind)] +#![crate_name = "artiq_support"] +#![crate_type = "cdylib"] + +extern crate std as core; +extern crate libc; +extern crate unwind; + +// Note: this does *not* match the cslice crate! +// ARTIQ Python has the slice length field fixed at 32 bits, even on 64-bit platforms. +mod cslice { + use core::marker::PhantomData; + use core::convert::AsRef; + use core::slice; + + #[repr(C)] + #[derive(Clone, Copy)] + pub struct CSlice<'a, T> { + base: *const T, + len: u32, + phantom: PhantomData<&'a ()> + } + + impl<'a, T> AsRef<[T]> for CSlice<'a, T> { + fn as_ref(&self) -> &[T] { + unsafe { + slice::from_raw_parts(self.base, self.len as usize) + } + } + } +} + +#[path = "../../firmware/ksupport/eh.rs"] +pub mod eh; + +use std::{str, process}; + +fn terminate(exception: &eh::Exception, mut _backtrace: &mut [usize]) -> ! { + println!("Uncaught {}: {} ({}, {}, {})", + str::from_utf8(exception.name.as_ref()).unwrap(), + str::from_utf8(exception.message.as_ref()).unwrap(), + exception.param[0], + exception.param[1], + exception.param[2]); + println!("at {}:{}:{}", + str::from_utf8(exception.file.as_ref()).unwrap(), + exception.line, + exception.column); + process::exit(1); +} + +#[export_name = "now"] +pub static mut NOW: i64 = 0; + +#[export_name = "watchdog_set"] +pub extern fn watchdog_set(ms: i64) -> i32 { + println!("watchdog_set {}", ms); + ms as i32 +} + +#[export_name = "watchdog_clear"] +pub extern fn watchdog_clear(id: i32) { + println!("watchdog_clear {}", id); +} diff --git a/artiq/test/libartiq_support/unwind.h b/artiq/test/libartiq_support/unwind.h deleted file mode 100644 index 86001bbb5..000000000 --- a/artiq/test/libartiq_support/unwind.h +++ /dev/null @@ -1,329 +0,0 @@ -//===------------------------------- unwind.h -----------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -// -// C++ ABI Level 1 ABI documented at: -// http://mentorembedded.github.io/cxx-abi/abi-eh.html -// -//===----------------------------------------------------------------------===// - -#ifndef __UNWIND_H__ -#define __UNWIND_H__ - -#include -#include - -#if defined(__APPLE__) -#define LIBUNWIND_UNAVAIL __attribute__ (( unavailable )) -#else -#define LIBUNWIND_UNAVAIL -#endif - -#include <__cxxabi_config.h> - -typedef enum { - _URC_NO_REASON = 0, - _URC_OK = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8, -#if LIBCXXABI_ARM_EHABI - _URC_FAILURE = 9 -#endif -} _Unwind_Reason_Code; - -typedef enum { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16 // gcc extension to C++ ABI -} _Unwind_Action; - -typedef struct _Unwind_Context _Unwind_Context; // opaque - -#if LIBCXXABI_ARM_EHABI -typedef uint32_t _Unwind_State; - -static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0; -static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1; -static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2; -/* Undocumented flag for force unwinding. */ -static const _Unwind_State _US_FORCE_UNWIND = 8; - -typedef uint32_t _Unwind_EHT_Header; - -struct _Unwind_Control_Block; -typedef struct _Unwind_Control_Block _Unwind_Control_Block; -typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */ - -struct _Unwind_Control_Block { - uint64_t exception_class; - void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*); - - /* Unwinder cache, private fields for the unwinder's use */ - struct { - uint32_t reserved1; /* init reserved1 to 0, then don't touch */ - uint32_t reserved2; - uint32_t reserved3; - uint32_t reserved4; - uint32_t reserved5; - } unwinder_cache; - - /* Propagation barrier cache (valid after phase 1): */ - struct { - uint32_t sp; - uint32_t bitpattern[5]; - } barrier_cache; - - /* Cleanup cache (preserved over cleanup): */ - struct { - uint32_t bitpattern[4]; - } cleanup_cache; - - /* Pr cache (for pr's benefit): */ - struct { - uint32_t fnstart; /* function start address */ - _Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */ - uint32_t additional; - uint32_t reserved1; - } pr_cache; - - long long int :0; /* Enforce the 8-byte alignment */ -}; - -typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) - (_Unwind_State state, - _Unwind_Exception* exceptionObject, - struct _Unwind_Context* context); - -typedef _Unwind_Reason_Code (*__personality_routine) - (_Unwind_State state, - _Unwind_Exception* exceptionObject, - struct _Unwind_Context* context); -#else -struct _Unwind_Context; // opaque -struct _Unwind_Exception; // forward declaration -typedef struct _Unwind_Exception _Unwind_Exception; - -struct _Unwind_Exception { - uint64_t exception_class; - void (*exception_cleanup)(_Unwind_Reason_Code reason, - _Unwind_Exception *exc); - uintptr_t private_1; // non-zero means forced unwind - uintptr_t private_2; // holds sp that phase1 found for phase2 to use -#ifndef __LP64__ - // The gcc implementation of _Unwind_Exception used attribute mode on the - // above fields which had the side effect of causing this whole struct to - // round up to 32 bytes in size. To be more explicit, we add pad fields - // added for binary compatibility. - uint32_t reserved[3]; -#endif -}; - -typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) - (int version, - _Unwind_Action actions, - uint64_t exceptionClass, - _Unwind_Exception* exceptionObject, - struct _Unwind_Context* context, - void* stop_parameter ); - -typedef _Unwind_Reason_Code (*__personality_routine) - (int version, - _Unwind_Action actions, - uint64_t exceptionClass, - _Unwind_Exception* exceptionObject, - struct _Unwind_Context* context); -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// -// The following are the base functions documented by the C++ ABI -// -#ifdef __USING_SJLJ_EXCEPTIONS__ -extern _Unwind_Reason_Code - _Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object); -extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object); -#else -extern _Unwind_Reason_Code - _Unwind_RaiseException(_Unwind_Exception *exception_object); -extern void _Unwind_Resume(_Unwind_Exception *exception_object); -#endif -extern void _Unwind_DeleteException(_Unwind_Exception *exception_object); - -#if LIBCXXABI_ARM_EHABI -typedef enum { - _UVRSC_CORE = 0, /* integer register */ - _UVRSC_VFP = 1, /* vfp */ - _UVRSC_WMMXD = 3, /* Intel WMMX data register */ - _UVRSC_WMMXC = 4 /* Intel WMMX control register */ -} _Unwind_VRS_RegClass; - -typedef enum { - _UVRSD_UINT32 = 0, - _UVRSD_VFPX = 1, - _UVRSD_UINT64 = 3, - _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5 -} _Unwind_VRS_DataRepresentation; - -typedef enum { - _UVRSR_OK = 0, - _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2 -} _Unwind_VRS_Result; - -extern void _Unwind_Complete(_Unwind_Exception* exception_object); - -extern _Unwind_VRS_Result -_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, - uint32_t regno, _Unwind_VRS_DataRepresentation representation, - void *valuep); - -extern _Unwind_VRS_Result -_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, - uint32_t regno, _Unwind_VRS_DataRepresentation representation, - void *valuep); - -extern _Unwind_VRS_Result -_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, - uint32_t discriminator, - _Unwind_VRS_DataRepresentation representation); -#endif - -extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index); -extern void _Unwind_SetGR(struct _Unwind_Context *context, int index, - uintptr_t new_value); -extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context); -extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value); - -extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context); -extern uintptr_t - _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context); -#ifdef __USING_SJLJ_EXCEPTIONS__ -extern _Unwind_Reason_Code - _Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object, - _Unwind_Stop_Fn stop, void *stop_parameter); -#else -extern _Unwind_Reason_Code - _Unwind_ForcedUnwind(_Unwind_Exception *exception_object, - _Unwind_Stop_Fn stop, void *stop_parameter); -#endif - -#ifdef __USING_SJLJ_EXCEPTIONS__ -typedef struct _Unwind_FunctionContext *_Unwind_FunctionContext_t; -extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc); -extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc); -#endif - -// -// The following are semi-suppoted extensions to the C++ ABI -// - -// -// called by __cxa_rethrow(). -// -#ifdef __USING_SJLJ_EXCEPTIONS__ -extern _Unwind_Reason_Code - _Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object); -#else -extern _Unwind_Reason_Code - _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object); -#endif - -// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the -// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack -// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON. -typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, - void *); -extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); - -// _Unwind_GetCFA is a gcc extension that can be called from within a -// personality handler to get the CFA (stack pointer before call) of -// current frame. -extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *); - - -// _Unwind_GetIPInfo is a gcc extension that can be called from within a -// personality handler. Similar to _Unwind_GetIP() but also returns in -// *ipBefore a non-zero value if the instruction pointer is at or before the -// instruction causing the unwind. Normally, in a function call, the IP returned -// is the return address which is after the call instruction and may be past the -// end of the function containing the call instruction. -extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, - int *ipBefore); - - -// __register_frame() is used with dynamically generated code to register the -// FDE for a generated (JIT) code. The FDE must use pc-rel addressing to point -// to its function and optional LSDA. -// __register_frame() has existed in all versions of Mac OS X, but in 10.4 and -// 10.5 it was buggy and did not actually register the FDE with the unwinder. -// In 10.6 and later it does register properly. -extern void __register_frame(const void *fde); -extern void __deregister_frame(const void *fde); - -// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has -// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind -// info" which the runtime uses in preference to dwarf unwind info. This -// function will only work if the target function has an FDE but no compact -// unwind info. -struct dwarf_eh_bases { - uintptr_t tbase; - uintptr_t dbase; - uintptr_t func; -}; -extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *); - - -// This function attempts to find the start (address of first instruction) of -// a function given an address inside the function. It only works if the -// function has an FDE (dwarf unwind info). -// This function is unimplemented on Mac OS X 10.6 and later. Instead, use -// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result. -extern void *_Unwind_FindEnclosingFunction(void *pc); - -// Mac OS X does not support text-rel and data-rel addressing so these functions -// are unimplemented -extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context) - LIBUNWIND_UNAVAIL; -extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context) - LIBUNWIND_UNAVAIL; - -// Mac OS X 10.4 and 10.5 had implementations of these functions in -// libgcc_s.dylib, but they never worked. -/// These functions are no longer available on Mac OS X. -extern void __register_frame_info_bases(const void *fde, void *ob, void *tb, - void *db) LIBUNWIND_UNAVAIL; -extern void __register_frame_info(const void *fde, void *ob) - LIBUNWIND_UNAVAIL; -extern void __register_frame_info_table_bases(const void *fde, void *ob, - void *tb, void *db) - LIBUNWIND_UNAVAIL; -extern void __register_frame_info_table(const void *fde, void *ob) - LIBUNWIND_UNAVAIL; -extern void __register_frame_table(const void *fde) - LIBUNWIND_UNAVAIL; -extern void *__deregister_frame_info(const void *fde) - LIBUNWIND_UNAVAIL; -extern void *__deregister_frame_info_bases(const void *fde) - LIBUNWIND_UNAVAIL; - -#ifdef __cplusplus -} -#endif - -#endif // __UNWIND_H__ diff --git a/artiq/test/lit/lit.cfg b/artiq/test/lit/lit.cfg index 84826e04e..3963ceec4 100644 --- a/artiq/test/lit/lit.cfg +++ b/artiq/test/lit/lit.cfg @@ -27,11 +27,14 @@ config.substitutions.append( ("%not", not_) ) if os.name == "posix": support_build = os.path.join(root, "libartiq_support") - if subprocess.call(["make", "-sC", support_build]) != 0: + if subprocess.call(["rustc", os.path.join(support_build, "lib.rs"), + "--out-dir", support_build, + "-Cpanic=abort", "-g"]) != 0: lit_config.fatal("Unable to build JIT support library") support_lib = os.path.join(support_build, "libartiq_support.so") config.environment["LIBARTIQ_SUPPORT"] = support_lib + config.environment["RUST_BACKTRACE"] = "1" config.available_features.add("exceptions") config.available_features.add("time")