forked from M-Labs/artiq
firmware: port the exception handling routines to Rust.
This commit is contained in:
parent
b9cbedceb1
commit
c39394b4d5
|
@ -1540,21 +1540,20 @@ 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(
|
|
||||||
ir.Constant(exnname, builtins.TStr()))
|
|
||||||
|
|
||||||
llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname))
|
llclauseexnname = self.llconst_of_const(
|
||||||
if llclauseexnnameptr is None:
|
ir.Constant(exnname, builtins.TStr()))
|
||||||
llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type,
|
llclauseexnnameptr = self.llmodule.get_global("exn.{}".format(exnname))
|
||||||
name="exn.{}".format(exnname))
|
if llclauseexnnameptr is None:
|
||||||
llclauseexnnameptr.global_constant = True
|
llclauseexnnameptr = ll.GlobalVariable(self.llmodule, llclauseexnname.type,
|
||||||
llclauseexnnameptr.initializer = llclauseexnname
|
name="exn.{}".format(exnname))
|
||||||
llclauseexnnameptr.linkage = "private"
|
llclauseexnnameptr.global_constant = True
|
||||||
llclauseexnnameptr.unnamed_addr = True
|
llclauseexnnameptr.initializer = llclauseexnname
|
||||||
|
llclauseexnnameptr.linkage = "private"
|
||||||
|
llclauseexnnameptr.unnamed_addr = True
|
||||||
lllandingpad.add_clause(ll.CatchClause(llclauseexnnameptr))
|
lllandingpad.add_clause(ll.CatchClause(llclauseexnnameptr))
|
||||||
|
|
||||||
if typ is None:
|
if typ is None:
|
||||||
|
|
|
@ -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 $@
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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 */
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 $@ $^
|
|
|
@ -1 +0,0 @@
|
||||||
#define LIBCXXABI_ARM_EHABI 0
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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__
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue