952 changes: 952 additions & 0 deletions libcxxabi/src/Unwind/Unwind-EHABI.cpp

Large diffs are not rendered by default.

264 changes: 252 additions & 12 deletions libcxxabi/src/Unwind/UnwindCursor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
#ifndef __UNWINDCURSOR_HPP__
#define __UNWINDCURSOR_HPP__

#include <algorithm>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unwind.h>

#if __APPLE__
#include <mach-o/dyld.h>
Expand Down Expand Up @@ -370,15 +372,18 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
virtual unw_word_t getReg(int) = 0;
virtual void setReg(int, unw_word_t) = 0;
virtual bool validFloatReg(int) = 0;
virtual double getFloatReg(int) = 0;
virtual void setFloatReg(int, double) = 0;
virtual unw_fpreg_t getFloatReg(int) = 0;
virtual void setFloatReg(int, unw_fpreg_t) = 0;
virtual int step() = 0;
virtual void getInfo(unw_proc_info_t *) = 0;
virtual void jumpto() = 0;
virtual bool isSignalFrame() = 0;
virtual bool getFunctionName(char *bf, size_t ln, unw_word_t *off) = 0;
virtual void setInfoBasedOnIPRegister(bool isReturnAddr = false) = 0;
virtual const char *getRegisterName(int num) = 0;
#if __arm__
virtual void saveVFPAsX() = 0;
#endif
};


Expand All @@ -395,20 +400,27 @@ class UnwindCursor : public AbstractUnwindCursor{
virtual unw_word_t getReg(int);
virtual void setReg(int, unw_word_t);
virtual bool validFloatReg(int);
virtual double getFloatReg(int);
virtual void setFloatReg(int, double);
virtual unw_fpreg_t getFloatReg(int);
virtual void setFloatReg(int, unw_fpreg_t);
virtual int step();
virtual void getInfo(unw_proc_info_t *);
virtual void jumpto();
virtual bool isSignalFrame();
virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off);
virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false);
virtual const char *getRegisterName(int num);
#if __arm__
virtual void saveVFPAsX();
#endif

void operator delete(void *, size_t) {}

private:

#if LIBCXXABI_ARM_EHABI
bool getInfoFromEHABISection(pint_t pc, const UnwindInfoSections &sects);
#endif

#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections &sects,
uint32_t fdeSectionOffsetHint=0);
Expand Down Expand Up @@ -486,7 +498,9 @@ class UnwindCursor : public AbstractUnwindCursor{
}
return false;
}
#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND

#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
compact_unwind_encoding_t dwarfEncoding() const {
R dummy;
return dwarfEncoding(dummy);
Expand All @@ -507,7 +521,7 @@ class UnwindCursor : public AbstractUnwindCursor{
compact_unwind_encoding_t dwarfEncoding(Registers_arm64 &) const {
return UNWIND_ARM64_MODE_DWARF;
}
#endif // _LIBUNWIND_SUPPORT_COMPACT_UNWIND
#endif // _LIBUNWIND_SUPPORT_DWARF_UNWIND


A &_addressSpace;
Expand All @@ -524,14 +538,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
_isSignalFrame(false) {
static_assert(sizeof(UnwindCursor<A, R>) < sizeof(unw_cursor_t),
"UnwindCursor<> does not fit in unw_cursor_t");

bzero(&_info, sizeof(_info));
memset(&_info, 0, sizeof(_info));
}

template <typename A, typename R>
UnwindCursor<A, R>::UnwindCursor(A &as, void *)
: _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) {
bzero(&_info, sizeof(_info));
memset(&_info, 0, sizeof(_info));
// FIXME
// fill in _registers from thread arg
}
Expand All @@ -558,19 +571,25 @@ bool UnwindCursor<A, R>::validFloatReg(int regNum) {
}

template <typename A, typename R>
double UnwindCursor<A, R>::getFloatReg(int regNum) {
unw_fpreg_t UnwindCursor<A, R>::getFloatReg(int regNum) {
return _registers.getFloatRegister(regNum);
}

template <typename A, typename R>
void UnwindCursor<A, R>::setFloatReg(int regNum, double value) {
void UnwindCursor<A, R>::setFloatReg(int regNum, unw_fpreg_t value) {
_registers.setFloatRegister(regNum, value);
}

template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
_registers.jumpto();
}

#if __arm__
template <typename A, typename R> void UnwindCursor<A, R>::saveVFPAsX() {
_registers.saveVFPAsX();
}
#endif

template <typename A, typename R>
const char *UnwindCursor<A, R>::getRegisterName(int regNum) {
return _registers.getRegisterName(regNum);
Expand All @@ -580,6 +599,212 @@ template <typename A, typename R> bool UnwindCursor<A, R>::isSignalFrame() {
return _isSignalFrame;
}

#if LIBCXXABI_ARM_EHABI
struct EHABIIndexEntry {
uint32_t functionOffset;
uint32_t data;
};

// Unable to unwind in the ARM index table (section 5 EHABI).
#define UNW_EXIDX_CANTUNWIND 0x1

static inline uint32_t signExtendPrel31(uint32_t data) {
return data | ((data & 0x40000000u) << 1);
}

extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr0(
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
extern "C" _Unwind_Reason_Code __aeabi_unwind_cpp_pr2(
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);

template<typename A>
struct EHABISectionIterator {
typedef EHABISectionIterator _Self;

typedef std::random_access_iterator_tag iterator_category;
typedef typename A::pint_t value_type;
typedef typename A::pint_t* pointer;
typedef typename A::pint_t& reference;
typedef size_t size_type;
typedef size_t difference_type;

static _Self begin(A& addressSpace, const UnwindInfoSections& sects) {
return _Self(addressSpace, sects, 0);
}
static _Self end(A& addressSpace, const UnwindInfoSections& sects) {
return _Self(addressSpace, sects, sects.arm_section_length);
}

EHABISectionIterator(A& addressSpace, const UnwindInfoSections& sects, size_t i)
: _addressSpace(&addressSpace), _sects(&sects), _i(i) {}

_Self& operator++() { ++_i; return *this; }
_Self& operator+=(size_t a) { _i += a; return *this; }
_Self& operator--() { assert(_i > 0); --_i; return *this; }
_Self& operator-=(size_t a) { assert(_i >= a); _i -= a; return *this; }

_Self operator+(size_t a) { _Self out = *this; out._i += a; return out; }
_Self operator-(size_t a) { assert(_i >= a); _Self out = *this; out._i -= a; return out; }

size_t operator-(const _Self& other) { return _i - other._i; }

bool operator==(const _Self& other) const {
assert(_addressSpace == other._addressSpace);
assert(_sects == other._sects);
return _i == other._i;
}

typename A::pint_t operator*() const { return functionAddress(); }

typename A::pint_t functionAddress() const {
typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof(
EHABIIndexEntry, _i, functionOffset);
return indexAddr + signExtendPrel31(_addressSpace->get32(indexAddr));
}

typename A::pint_t dataAddress() {
typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof(
EHABIIndexEntry, _i, data);
return indexAddr;
}

private:
size_t _i;
A* _addressSpace;
const UnwindInfoSections* _sects;
};

template <typename A, typename R>
bool UnwindCursor<A, R>::getInfoFromEHABISection(
pint_t pc,
const UnwindInfoSections &sects) {
EHABISectionIterator<A> begin =
EHABISectionIterator<A>::begin(_addressSpace, sects);
EHABISectionIterator<A> end =
EHABISectionIterator<A>::end(_addressSpace, sects);

EHABISectionIterator<A> itNextPC = std::upper_bound(begin, end, pc);
if (itNextPC == begin || itNextPC == end)
return false;
EHABISectionIterator<A> itThisPC = itNextPC - 1;

pint_t thisPC = itThisPC.functionAddress();
pint_t nextPC = itNextPC.functionAddress();
pint_t indexDataAddr = itThisPC.dataAddress();

if (indexDataAddr == 0)
return false;

uint32_t indexData = _addressSpace.get32(indexDataAddr);
if (indexData == UNW_EXIDX_CANTUNWIND)
return false;

// If the high bit is set, the exception handling table entry is inline inside
// the index table entry on the second word (aka |indexDataAddr|). Otherwise,
// the table points at an offset in the exception handling table (section 5 EHABI).
pint_t exceptionTableAddr;
uint32_t exceptionTableData;
bool isSingleWordEHT;
if (indexData & 0x80000000) {
exceptionTableAddr = indexDataAddr;
// TODO(ajwong): Should this data be 0?
exceptionTableData = indexData;
isSingleWordEHT = true;
} else {
exceptionTableAddr = indexDataAddr + signExtendPrel31(indexData);
exceptionTableData = _addressSpace.get32(exceptionTableAddr);
isSingleWordEHT = false;
}

// Now we know the 3 things:
// exceptionTableAddr -- exception handler table entry.
// exceptionTableData -- the data inside the first word of the eht entry.
// isSingleWordEHT -- whether the entry is in the index.
unw_word_t personalityRoutine = 0xbadf00d;
bool scope32 = false;
uintptr_t lsda = 0xbadf00d;

// If the high bit in the exception handling table entry is set, the entry is
// in compact form (section 6.3 EHABI).
if (exceptionTableData & 0x80000000) {
// Grab the index of the personality routine from the compact form.
int choice = (exceptionTableData & 0x0f000000) >> 24;
int extraWords = 0;
switch (choice) {
case 0:
personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr0;
extraWords = 0;
scope32 = false;
break;
case 1:
personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr1;
extraWords = (exceptionTableData & 0x00ff0000) >> 16;
scope32 = false;
break;
case 2:
personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr2;
extraWords = (exceptionTableData & 0x00ff0000) >> 16;
scope32 = true;
break;
default:
_LIBUNWIND_ABORT("unknown personality routine");
return false;
}

if (isSingleWordEHT) {
if (extraWords != 0) {
_LIBUNWIND_ABORT("index inlined table detected but pr function "
"requires extra words");
return false;
}
}
} else {
pint_t personalityAddr =
exceptionTableAddr + signExtendPrel31(exceptionTableData);
personalityRoutine = personalityAddr;

// ARM EHABI # 6.2, # 9.2
//
// +---- ehtp
// v
// +--------------------------------------+
// | +--------+--------+--------+-------+ |
// | |0| prel31 to personalityRoutine | |
// | +--------+--------+--------+-------+ |
// | | N | unwind opcodes | | <-- UnwindData
// | +--------+--------+--------+-------+ |
// | | Word 2 unwind opcodes | |
// | +--------+--------+--------+-------+ |
// | ... |
// | +--------+--------+--------+-------+ |
// | | Word N unwind opcodes | |
// | +--------+--------+--------+-------+ |
// | | LSDA | | <-- lsda
// | | ... | |
// | +--------+--------+--------+-------+ |
// +--------------------------------------+

uint32_t *UnwindData = reinterpret_cast<uint32_t*>(exceptionTableAddr) + 1;
uint32_t FirstDataWord = *UnwindData;
size_t N = ((FirstDataWord >> 24) & 0xff);
size_t NDataWords = N + 1;
lsda = reinterpret_cast<uintptr_t>(UnwindData + NDataWords);
}

_info.start_ip = thisPC;
_info.end_ip = nextPC;
_info.handler = personalityRoutine;
_info.unwind_info = exceptionTableAddr;
_info.lsda = lsda;
// flags is pr_cache.additional. See EHABI #7.2 for definition of bit 0.
_info.flags = isSingleWordEHT ? 1 : 0 | scope32 ? 0x2 : 0; // Use enum?

return true;
}
#endif

#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
template <typename A, typename R>
bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
Expand Down Expand Up @@ -913,6 +1138,11 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc,
template <typename A, typename R>
void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
pint_t pc = (pint_t)this->getReg(UNW_REG_IP);
#if LIBCXXABI_ARM_EHABI
// Remove the thumb bit so the IP represents the actual instruction address.
// This matches the behaviour of _Unwind_GetIP on arm.
pc &= (pint_t)~0x1;
#endif

// If the last line of a function is a "throw" the compiler sometimes
// emits no instructions after the call to __cxa_throw. This means
Expand Down Expand Up @@ -957,6 +1187,12 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
}
}
#endif

#if LIBCXXABI_ARM_EHABI
// If there is ARM EHABI unwind info, look there next.
if (sects.arm_section != 0 && this->getInfoFromEHABISection(pc, sects))
return;
#endif
}

#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
Expand Down Expand Up @@ -1026,7 +1262,7 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {

template <typename A, typename R>
int UnwindCursor<A, R>::step() {
// Bottom of stack is defined is when no unwind info cannot be found.
// Bottom of stack is defined is when unwind info cannot be found.
if (_unwindInfoMissing)
return UNW_STEP_END;

Expand All @@ -1036,8 +1272,12 @@ int UnwindCursor<A, R>::step() {
result = this->stepWithCompactEncoding();
#elif _LIBUNWIND_SUPPORT_DWARF_UNWIND
result = this->stepWithDwarfFDE();
#elif LIBCXXABI_ARM_EHABI
result = UNW_STEP_SUCCESS;
#else
#error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or _LIBUNWIND_SUPPORT_DWARF_UNWIND
#error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \
_LIBUNWIND_SUPPORT_DWARF_UNWIND or \
LIBCXXABI_ARM_EHABI
#endif

// update info based on new PC
Expand Down
21 changes: 17 additions & 4 deletions libcxxabi/src/Unwind/UnwindLevel1-gcc-ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,21 @@

/// Called by __cxa_rethrow().
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) {
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) {
_LIBUNWIND_TRACE_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), "
"private_1=%ld\n",
exception_object, exception_object->private_1);
"private_1=%ld\n",
exception_object,
#if LIBCXXABI_ARM_EHABI
(long)exception_object->unwinder_cache.reserved1);
#else
(long)exception_object->private_1);
#endif

#if LIBCXXABI_ARM_EHABI
// _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
// which is in the same position as private_1 below.
return _Unwind_RaiseException(exception_object);
#else
// If this is non-forced and a stopping place was found, then this is a
// re-throw.
// Call _Unwind_RaiseException() as if this was a new exception
Expand All @@ -42,6 +53,7 @@ _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) {
_Unwind_Resume(exception_object);
_LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()"
" which unexpectedly returned");
#endif
}


Expand Down Expand Up @@ -116,7 +128,8 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
unw_get_proc_info(&cursor, &frameInfo);
_LIBUNWIND_TRACE_UNWINDING(
" _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n",
frameInfo.start_ip, functionName, frameInfo.lsda, &cursor);
(long long)frameInfo.start_ip, functionName, (long long)frameInfo.lsda,
&cursor);
}

// call trace function with this frame
Expand Down
24 changes: 12 additions & 12 deletions libcxxabi/src/Unwind/UnwindLevel1.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
#include "unwind.h"
#include "config.h"

#if _LIBUNWIND_BUILD_ZERO_COST_APIS
#if _LIBUNWIND_BUILD_ZERO_COST_APIS && !LIBCXXABI_ARM_EHABI

static _Unwind_Reason_Code
unwind_phase1(unw_context_t *uc, struct _Unwind_Exception *exception_object) {
unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) {
unw_cursor_t cursor1;
unw_init_local(&cursor1, uc);

Expand Down Expand Up @@ -117,7 +117,7 @@ unwind_phase1(unw_context_t *uc, struct _Unwind_Exception *exception_object) {


static _Unwind_Reason_Code
unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) {
unwind_phase2(unw_context_t *uc, _Unwind_Exception *exception_object) {
unw_cursor_t cursor2;
unw_init_local(&cursor2, uc);

Expand Down Expand Up @@ -187,7 +187,7 @@ unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) {
if (sp == exception_object->private_2) {
// Phase 1 said we would stop at this frame, but we did not...
_LIBUNWIND_ABORT("during phase1 personality function said it would "
"stop here, but now if phase2 it did not stop here");
"stop here, but now in phase2 it did not stop here");
}
break;
case _URC_INSTALL_CONTEXT:
Expand All @@ -202,7 +202,7 @@ unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) {
unw_get_reg(&cursor2, UNW_REG_SP, &sp);
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering "
"user code with ip=0x%llX, sp=0x%llX\n",
exception_object, pc, sp);
exception_object, pc, sp);
}
unw_resume(&cursor2);
// unw_resume() only returns if there was an error.
Expand All @@ -223,7 +223,7 @@ unwind_phase2(unw_context_t *uc, struct _Unwind_Exception *exception_object) {

static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc,
struct _Unwind_Exception *exception_object,
_Unwind_Exception *exception_object,
_Unwind_Stop_Fn stop, void *stop_parameter) {
unw_cursor_t cursor2;
unw_init_local(&cursor2, uc);
Expand All @@ -237,7 +237,7 @@ unwind_phase2_forced(unw_context_t *uc,
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step "
"failed => _URC_END_OF_STACK\n",
exception_object);
return _URC_FATAL_PHASE1_ERROR;
return _URC_FATAL_PHASE2_ERROR;
}

// When tracing, print state information.
Expand Down Expand Up @@ -324,7 +324,7 @@ unwind_phase2_forced(unw_context_t *uc,

/// Called by __cxa_throw. Only returns if there is a fatal error.
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_RaiseException(struct _Unwind_Exception *exception_object) {
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)\n",
exception_object);
unw_context_t uc;
Expand Down Expand Up @@ -358,7 +358,7 @@ _Unwind_RaiseException(struct _Unwind_Exception *exception_object) {
/// is implemented by having the code call __cxa_rethrow() which
/// in turn calls _Unwind_Resume_or_Rethrow().
_LIBUNWIND_EXPORT void
_Unwind_Resume(struct _Unwind_Exception *exception_object) {
_Unwind_Resume(_Unwind_Exception *exception_object) {
_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)\n", exception_object);
unw_context_t uc;
unw_getcontext(&uc);
Expand All @@ -380,7 +380,7 @@ _Unwind_Resume(struct _Unwind_Exception *exception_object) {
/// Unwinds stack, calling "stop" function at each frame.
/// Could be used to implement longjmp().
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object,
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
_Unwind_Stop_Fn stop, void *stop_parameter) {
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n",
exception_object, stop);
Expand Down Expand Up @@ -484,12 +484,12 @@ _Unwind_GetRegionStart(struct _Unwind_Context *context) {
/// Called by personality handler during phase 2 if a foreign exception
// is caught.
_LIBUNWIND_EXPORT void
_Unwind_DeleteException(struct _Unwind_Exception *exception_object) {
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)\n",
exception_object);
if (exception_object->exception_cleanup != NULL)
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
exception_object);
}

#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS
#endif // _LIBUNWIND_BUILD_ZERO_COST_APIS && !LIBCXXABI_ARM_EHABI
88 changes: 86 additions & 2 deletions libcxxabi/src/Unwind/UnwindRegistersRestore.S
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,13 @@ DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind15Registers_arm646jumptoEv)
#elif __arm__

@
@ void libunwind::Registers_arm::jumpto()
@ void libunwind::Registers_arm::restoreCoreAndJumpTo()
@
@ On entry:
@ thread_state pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm6jumptoEv)
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv)
@ Use lr as base so that r0 can be restored.
mov lr, r0
@ 32bit thumb-2 restrictions for ldm:
Expand All @@ -326,6 +326,90 @@ DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm6jumptoEv)
ldm lr, {r0-r12}
ldr sp, [lr, #52]
ldr lr, [lr, #60] @ restore pc into lr
#if _ARM_ARCH > 4
bx lr
#else
mov pc, lr
#endif

@
@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMDEPy)
@ VFP and iwMMX instructions are only available when compiling with the flags
@ that enable them. We don't want to do that in the library (because we don't
@ want the compiler to generate instructions that access those) but this is
@ only accessed if the personality routine needs these registers. Use of
@ these registers implies they are, actually, available on the target, so
@ it's ok to execute.
@ So, generate the instruction using the corresponding coprocessor mnemonic.
ldc p11, cr0, [r0], {#0x20} @ fldmiad r0, {d0-d15}
mov pc, lr

@
@ static void libunwind::Registers_arm::restoreVFPWithFLDMX(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMXEPy)
ldc p11, cr0, [r0], {#0x21} @ fldmiax r0, {d0-d15}
mov pc, lr

@
@ static void libunwind::Registers_arm::restoreVFPv3(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm12restoreVFPv3EPy)
ldcl p11, cr0, [r0], {#0x20} @ vldm r0, {d16-d31}
mov pc, lr

@
@ static void libunwind::Registers_arm::restoreiWMMX(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm12restoreiWMMXEPy)
ldcl p1, cr0, [r0], #8 @ wldrd wR0, [r0], #8
ldcl p1, cr1, [r0], #8 @ wldrd wR1, [r0], #8
ldcl p1, cr2, [r0], #8 @ wldrd wR2, [r0], #8
ldcl p1, cr3, [r0], #8 @ wldrd wR3, [r0], #8
ldcl p1, cr4, [r0], #8 @ wldrd wR4, [r0], #8
ldcl p1, cr5, [r0], #8 @ wldrd wR5, [r0], #8
ldcl p1, cr6, [r0], #8 @ wldrd wR6, [r0], #8
ldcl p1, cr7, [r0], #8 @ wldrd wR7, [r0], #8
ldcl p1, cr8, [r0], #8 @ wldrd wR8, [r0], #8
ldcl p1, cr9, [r0], #8 @ wldrd wR9, [r0], #8
ldcl p1, cr10, [r0], #8 @ wldrd wR10, [r0], #8
ldcl p1, cr11, [r0], #8 @ wldrd wR11, [r0], #8
ldcl p1, cr12, [r0], #8 @ wldrd wR12, [r0], #8
ldcl p1, cr13, [r0], #8 @ wldrd wR13, [r0], #8
ldcl p1, cr14, [r0], #8 @ wldrd wR14, [r0], #8
ldcl p1, cr15, [r0], #8 @ wldrd wR15, [r0], #8
mov pc, lr

@
@ static void libunwind::Registers_arm::restoreiWMMXControl(unw_uint32_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm19restoreiWMMXControlEPj)
ldc2 p1, cr8, [r0], #4 @ wldrw wCGR0, [r0], #4
ldc2 p1, cr9, [r0], #4 @ wldrw wCGR1, [r0], #4
ldc2 p1, cr10, [r0], #4 @ wldrw wCGR2, [r0], #4
ldc2 p1, cr11, [r0], #4 @ wldrw wCGR3, [r0], #4
mov pc, lr

#endif
92 changes: 91 additions & 1 deletion libcxxabi/src/Unwind/UnwindRegistersSave.S
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
@
@ On entry:
@ thread_state pointer is in r0
@
@ Per EHABI #4.7 this only saves the core integer registers.
@ EHABI #7.4.5 notes that in general all VRS registers should be restored
@ however this is very hard to do for VFP registers because it is unknown
@ to the lbirary how many registers are implemented by the architecture.
@ Instead, VFP registers are demand saved by logic external to unw_getcontext.
@
.p2align 2
DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
Expand All @@ -297,7 +303,91 @@ DEFINE_LIBUNWIND_FUNCTION(unw_getcontext)
str sp, [r0, #52]
str lr, [r0, #56]
str lr, [r0, #60] @ store return address as pc
mov r0, #0 @ return UNW_ESUCCESS
mov r0, #0 @ return UNW_ESUCCESS
#if _ARM_ARCH > 4
bx lr
#else
mov pc, lr
#endif

@
@ static void libunwind::Registers_arm::saveVFPWithFSTMD(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMDEPy)
stc p11, cr0, [r0], {#0x20} @ fstmiad r0, {d0-d15}
mov pc, lr

@
@ static void libunwind::Registers_arm::saveVFPWithFSTMX(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMXEPy)
stc p11, cr0, [r0], {#0x21} @ fstmiax r0, {d0-d15}
mov pc, lr

@
@ static void libunwind::Registers_arm::saveVFPv3(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm9saveVFPv3EPy)
@ VFP and iwMMX instructions are only available when compiling with the flags
@ that enable them. We don't want to do that in the library (because we don't
@ want the compiler to generate instructions that access those) but this is
@ only accessed if the personality routine needs these registers. Use of
@ these registers implies they are, actually, available on the target, so
@ it's ok to execute.
@ So, generate the instructions using the corresponding coprocessor mnemonic.
stcl p11, cr0, [r0], {#0x20} @ vldm r0, {d16-d31}
mov pc, lr

@
@ static void libunwind::Registers_arm::saveiWMMX(unw_fpreg_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm9saveiWMMXEPy)
stcl p1, cr0, [r0], #8 @ wstrd wR0, [r0], #8
stcl p1, cr1, [r0], #8 @ wstrd wR1, [r0], #8
stcl p1, cr2, [r0], #8 @ wstrd wR2, [r0], #8
stcl p1, cr3, [r0], #8 @ wstrd wR3, [r0], #8
stcl p1, cr4, [r0], #8 @ wstrd wR4, [r0], #8
stcl p1, cr5, [r0], #8 @ wstrd wR5, [r0], #8
stcl p1, cr6, [r0], #8 @ wstrd wR6, [r0], #8
stcl p1, cr7, [r0], #8 @ wstrd wR7, [r0], #8
stcl p1, cr8, [r0], #8 @ wstrd wR8, [r0], #8
stcl p1, cr9, [r0], #8 @ wstrd wR9, [r0], #8
stcl p1, cr10, [r0], #8 @ wstrd wR10, [r0], #8
stcl p1, cr11, [r0], #8 @ wstrd wR11, [r0], #8
stcl p1, cr12, [r0], #8 @ wstrd wR12, [r0], #8
stcl p1, cr13, [r0], #8 @ wstrd wR13, [r0], #8
stcl p1, cr14, [r0], #8 @ wstrd wR14, [r0], #8
stcl p1, cr15, [r0], #8 @ wstrd wR15, [r0], #8
mov pc, lr

@
@ static void libunwind::Registers_arm::saveiWMMXControl(unw_uint32_t* values)
@
@ On entry:
@ values pointer is in r0
@
.p2align 2
DEFINE_LIBUNWIND_PRIVATE_FUNCTION(_ZN9libunwind13Registers_arm16saveiWMMXControlEPj)
stc2 p1, cr8, [r0], #4 @ wstrw wCGR0, [r0], #4
stc2 p1, cr9, [r0], #4 @ wstrw wCGR1, [r0], #4
stc2 p1, cr10, [r0], #4 @ wstrw wCGR2, [r0], #4
stc2 p1, cr11, [r0], #4 @ wstrw wCGR3, [r0], #4
mov pc, lr

#endif
28 changes: 25 additions & 3 deletions libcxxabi/src/Unwind/libunwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@

#include <libunwind.h>

#ifndef NDEBUG
#include <cstdlib> // getenv
#endif
#include <new>

#include "libunwind_ext.h"
#include "config.h"

#include <stdlib.h>


#if _LIBUNWIND_BUILD_ZERO_COST_APIS

Expand Down Expand Up @@ -50,6 +55,9 @@ _LIBUNWIND_EXPORT int unw_init_local(unw_cursor_t *cursor,
#elif __arm64__
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm64>(
context, LocalAddressSpace::sThisAddressSpace);
#elif LIBCXXABI_ARM_EHABI
new ((void *)cursor) UnwindCursor<LocalAddressSpace, Registers_arm>(
context, LocalAddressSpace::sThisAddressSpace);
#endif
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
co->setInfoBasedOnIPRegister();
Expand Down Expand Up @@ -161,7 +169,7 @@ _LIBUNWIND_EXPORT int unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
_LIBUNWIND_EXPORT int unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
unw_word_t value) {
_LIBUNWIND_TRACE_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n",
cursor, regNum, value);
cursor, regNum, (long long)value);
typedef LocalAddressSpace::pint_t pint_t;
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
if (co->validReg(regNum)) {
Expand Down Expand Up @@ -193,8 +201,13 @@ _LIBUNWIND_EXPORT int unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
/// Set value of specified float register at cursor position in stack frame.
_LIBUNWIND_EXPORT int unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
unw_fpreg_t value) {
#if LIBCXXABI_ARM_EHABI
_LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)\n",
cursor, regNum, value);
#else
_LIBUNWIND_TRACE_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n",
cursor, regNum, value);
cursor, regNum, value);
#endif
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
if (co->validFloatReg(regNum)) {
co->setFloatReg(regNum, value);
Expand Down Expand Up @@ -239,7 +252,7 @@ _LIBUNWIND_EXPORT int unw_resume(unw_cursor_t *cursor) {
_LIBUNWIND_EXPORT int unw_get_proc_name(unw_cursor_t *cursor, char *buf,
size_t bufLen, unw_word_t *offset) {
_LIBUNWIND_TRACE_API("unw_get_proc_name(cursor=%p, &buf=%p,"
"bufLen=%ld)\n", cursor, buf, bufLen);
"bufLen=%zu)\n", cursor, buf, bufLen);
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
if (co->getFunctionName(buf, bufLen, offset))
return UNW_ESUCCESS;
Expand Down Expand Up @@ -274,6 +287,15 @@ _LIBUNWIND_EXPORT int unw_is_signal_frame(unw_cursor_t *cursor) {
return co->isSignalFrame();
}

#if __arm__
// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD
_LIBUNWIND_EXPORT void unw_save_vfp_as_X(unw_cursor_t *cursor) {
_LIBUNWIND_TRACE_API("unw_fpreg_save_vfp_as_X(cursor=%p)\n", cursor);
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
return co->saveVFPAsX();
}
#endif


#if _LIBUNWIND_SUPPORT_DWARF_UNWIND
/// SPI: walks cached dwarf entries
Expand Down