forked from M-Labs/artiq
1
0
Fork 0

firmware: port the exception handling routines to Rust.

This commit is contained in:
whitequark 2017-02-04 07:59:02 +00:00
parent b9cbedceb1
commit c39394b4d5
15 changed files with 556 additions and 928 deletions

View File

@ -1540,13 +1540,12 @@ class LLVMIRGenerator:
for target, typ in insn.clauses(): for target, typ in insn.clauses():
if typ is None: if typ is None:
llclauseexnnameptr = ll.Constant( exnname = "" # see the comment in ksupport/eh.rs
self.llty_of_type(builtins.TStr()).as_pointer(), None)
else: else:
exnname = "{}:{}".format(typ.id, typ.name) exnname = "{}:{}".format(typ.id, typ.name)
llclauseexnname = self.llconst_of_const( llclauseexnname = self.llconst_of_const(
ir.Constant(exnname, builtins.TStr())) ir.Constant(exnname, builtins.TStr()))
llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname)) llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname))
if llclauseexnnameptr is None: if llclauseexnnameptr is None:
llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type, llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type,

View File

@ -22,7 +22,7 @@ all: ksupport.elf
$(RUSTOUT)/libksupport.a: $(RUSTOUT)/libksupport.a:
$(cargo) --manifest-path $(KSUPPORT_DIRECTORY)/Cargo.toml $(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 $@ $^ \ $(LD) $(LDFLAGS) -T $(KSUPPORT_DIRECTORY)/ksupport.ld -o $@ $^ \
-lbase -lm -lcompiler-rt -ldyld -lunwind -lbase -lm -lcompiler-rt -ldyld -lunwind
@chmod -x $@ @chmod -x $@

View File

@ -74,10 +74,10 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(round), api!(round),
/* exceptions */ /* exceptions */
api!(_Unwind_Resume), api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
api!(__artiq_personality), api!(__artiq_personality = ::eh::personality),
api!(__artiq_raise), api!(__artiq_raise = ::eh::raise),
api!(__artiq_reraise), api!(__artiq_reraise = ::eh::reraise),
/* proxified syscalls */ /* proxified syscalls */
api!(core_log), api!(core_log),

View File

@ -1,470 +0,0 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unwind.h>
#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;
}

View File

@ -1,41 +0,0 @@
#ifndef __ARTIQ_PERSONALITY_H
#define __ARTIQ_PERSONALITY_H
#include <stdint.h>
#include <stddef.h>
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 */

View File

@ -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 <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_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>(T);
// This contortion is required due to https://github.com/rust-lang/rust/issues/27060.
impl<T> Unaligned<T> {
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<T: Copy>(&mut self) -> T {
let result = *(self.ptr as *const Unaligned<T>);
self.ptr = self.ptr.offset(mem::size_of::<T>() 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::<u8>();
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::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (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::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() 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::<usize>(),
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<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// 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::<u8>();
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::<u8>();
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<u8>);
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<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 = 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, 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"]
pub unsafe extern fn reraise() -> ! {
if INFLIGHT.handled {
raise(&INFLIGHT.exception.unwrap())
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
}
}

View File

@ -1,9 +1,10 @@
#![feature(lang_items, asm, libc)] #![feature(lang_items, asm, libc, panic_unwind)]
#![no_std] #![no_std]
extern crate alloc_none; extern crate alloc_none;
#[macro_use] #[macro_use]
extern crate std_artiq as std; extern crate std_artiq as std;
extern crate unwind;
extern crate libc; extern crate libc;
extern crate byteorder; extern crate byteorder;
extern crate board; extern crate board;
@ -22,37 +23,33 @@ mod rpc_proto;
mod dyld; mod dyld;
mod api; mod api;
#[allow(improper_ctypes)] use core::{mem, ptr, str};
extern { use std::io::Cursor;
fn __artiq_raise(exn: *const ::kernel_proto::Exception) -> !; use cslice::{CSlice, AsCSlice};
} use kernel_proto::*;
use dyld::Library;
macro_rules! artiq_raise { macro_rules! artiq_raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({ ($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
let exn = $crate::kernel_proto::Exception { use cslice::AsCSlice;
name: concat!("0:artiq.coredevice.exceptions.", $name), let exn = $crate::eh::Exception {
file: file!(), name: concat!("0:artiq.coredevice.exceptions.", $name).as_bytes().as_c_slice(),
file: file!().as_bytes().as_c_slice(),
line: line!(), line: line!(),
column: column!(), column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719 // https://github.com/rust-lang/rfcs/pull/1719
function: "(Rust function)", function: "(Rust function)".as_bytes().as_c_slice(),
message: $message, message: $message.as_bytes().as_c_slice(),
param: [$param0, $param1, $param2] param: [$param0, $param1, $param2]
}; };
#[allow(unused_unsafe)] #[allow(unused_unsafe)]
unsafe { $crate::__artiq_raise(&exn as *const _) } unsafe { $crate::eh::raise(&exn) }
}); });
($name:expr, $message:expr) => ({ ($name:expr, $message:expr) => ({
artiq_raise!($name, $message, 0, 0, 0) 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) { fn send(request: &Message) {
unsafe { mailbox::send(request as *const _ as usize) } unsafe { mailbox::send(request as *const _ as usize) }
while !mailbox::acknowledged() {} while !mailbox::acknowledged() {}
@ -90,6 +87,7 @@ macro_rules! println {
#[path = "../runtime/rpc_queue.rs"] #[path = "../runtime/rpc_queue.rs"]
mod rpc_queue; mod rpc_queue;
mod rtio; mod rtio;
mod eh;
#[no_mangle] #[no_mangle]
#[lang = "panic_fmt"] #[lang = "panic_fmt"]
@ -160,14 +158,23 @@ extern fn recv_rpc(slot: *mut ()) -> usize {
recv!(&RpcRecvReply(ref result) => { recv!(&RpcRecvReply(ref result) => {
match result { match result {
&Ok(alloc_size) => alloc_size, &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] fn terminate(exception: &eh::Exception, mut backtrace: &mut [usize]) -> ! {
pub extern fn __artiq_terminate(exception: *const kernel_proto::Exception,
mut backtrace: CMutSlice<usize>) -> ! {
let mut cursor = 0; let mut cursor = 0;
for index in 0..backtrace.len() { for index in 0..backtrace.len() {
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS { 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(&NowSave(unsafe { NOW }));
send(&RunException { 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 backtrace: backtrace
}); });
loop {} loop {}

View File

@ -109,7 +109,7 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::
write_u64(writer, *ptr)), write_u64(writer, *ptr)),
Tag::String => Tag::String =>
consume_value!(CSlice<u8>, |ptr| consume_value!(CSlice<u8>, |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) => { Tag::Tuple(it, arity) => {
let mut it = it.clone(); let mut it = it.clone();
write_u8(writer, arity)?; write_u8(writer, arity)?;
@ -141,7 +141,7 @@ unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::
Tag::Keyword(it) => { Tag::Keyword(it) => {
struct Keyword<'a> { name: CSlice<'a, u8>, contents: () }; struct Keyword<'a> { name: CSlice<'a, u8>, contents: () };
consume_value!(Keyword, |ptr| { 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 tag = it.clone().next().expect("truncated tag");
let mut data = &(*ptr).contents as *const (); let mut data = &(*ptr).contents as *const ();
send_value(writer, tag, &mut data) send_value(writer, tag, &mut data)

View File

@ -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 $@ $^

View File

@ -1 +0,0 @@
#define LIBCXXABI_ARM_EHABI 0

View File

@ -1,26 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unwind.h>
#include <artiq_personality.h>
#define __USE_GNU
#include <dlfcn.h>
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);
}

View File

@ -1,15 +0,0 @@
#include <stdint.h>
#include <stdio.h>
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);
}

View File

@ -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);
}

View File

@ -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 <stdint.h>
#include <stddef.h>
#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__

View File

@ -27,11 +27,14 @@ config.substitutions.append( ("%not", not_) )
if os.name == "posix": if os.name == "posix":
support_build = os.path.join(root, "libartiq_support") 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") lit_config.fatal("Unable to build JIT support library")
support_lib = os.path.join(support_build, "libartiq_support.so") support_lib = os.path.join(support_build, "libartiq_support.so")
config.environment["LIBARTIQ_SUPPORT"] = support_lib config.environment["LIBARTIQ_SUPPORT"] = support_lib
config.environment["RUST_BACKTRACE"] = "1"
config.available_features.add("exceptions") config.available_features.add("exceptions")
config.available_features.add("time") config.available_features.add("time")