The AddressSpace.hpp header declares two classes: LocalAddressSpace and RemoteAddressSpace. These classes are only used in a very small number of source files, but passed in as template arguments to many other classes. Let's go ahead and only include AddressSpace.hpp in source files where at least one of these two classes is mentioned. This gets rid of a cyclic header dependency that was already present, but only caused breakage on macOS until recently. Reported by: Marshall Clow llvm-svn: 297364
699 lines
25 KiB
C++
699 lines
25 KiB
C++
//===-------------------------- CompactUnwinder.hpp -----------------------===//
|
|
//
|
|
// 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.
|
|
//
|
|
//
|
|
// Does runtime stack unwinding using compact unwind encodings.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef __COMPACT_UNWINDER_HPP__
|
|
#define __COMPACT_UNWINDER_HPP__
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <libunwind.h>
|
|
#include <mach-o/compact_unwind_encoding.h>
|
|
|
|
#include "Registers.hpp"
|
|
|
|
#define EXTRACT_BITS(value, mask) \
|
|
((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
|
|
|
|
namespace libunwind {
|
|
|
|
#if defined(_LIBUNWIND_TARGET_I386)
|
|
/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
|
|
/// unwind) by modifying a Registers_x86 register set
|
|
template <typename A>
|
|
class CompactUnwinder_x86 {
|
|
public:
|
|
|
|
static int stepWithCompactEncoding(compact_unwind_encoding_t info,
|
|
uint32_t functionStart, A &addressSpace,
|
|
Registers_x86 ®isters);
|
|
|
|
private:
|
|
typename A::pint_t pint_t;
|
|
|
|
static void frameUnwind(A &addressSpace, Registers_x86 ®isters);
|
|
static void framelessUnwind(A &addressSpace,
|
|
typename A::pint_t returnAddressLocation,
|
|
Registers_x86 ®isters);
|
|
static int
|
|
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
|
|
uint32_t functionStart, A &addressSpace,
|
|
Registers_x86 ®isters);
|
|
static int stepWithCompactEncodingFrameless(
|
|
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
|
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize);
|
|
};
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_x86<A>::stepWithCompactEncoding(
|
|
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
|
A &addressSpace, Registers_x86 ®isters) {
|
|
switch (compactEncoding & UNWIND_X86_MODE_MASK) {
|
|
case UNWIND_X86_MODE_EBP_FRAME:
|
|
return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
|
|
addressSpace, registers);
|
|
case UNWIND_X86_MODE_STACK_IMMD:
|
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
|
addressSpace, registers, false);
|
|
case UNWIND_X86_MODE_STACK_IND:
|
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
|
addressSpace, registers, true);
|
|
}
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
|
|
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
|
A &addressSpace, Registers_x86 ®isters) {
|
|
uint32_t savedRegistersOffset =
|
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
|
|
uint32_t savedRegistersLocations =
|
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
|
|
|
|
uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
|
|
for (int i = 0; i < 5; ++i) {
|
|
switch (savedRegistersLocations & 0x7) {
|
|
case UNWIND_X86_REG_NONE:
|
|
// no register saved in this slot
|
|
break;
|
|
case UNWIND_X86_REG_EBX:
|
|
registers.setEBX(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_ECX:
|
|
registers.setECX(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_EDX:
|
|
registers.setEDX(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_EDI:
|
|
registers.setEDI(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_ESI:
|
|
registers.setESI(addressSpace.get32(savedRegisters));
|
|
break;
|
|
default:
|
|
(void)functionStart;
|
|
_LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for "
|
|
"function starting at 0x%X",
|
|
compactEncoding, functionStart);
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
savedRegisters += 4;
|
|
savedRegistersLocations = (savedRegistersLocations >> 3);
|
|
}
|
|
frameUnwind(addressSpace, registers);
|
|
return UNW_STEP_SUCCESS;
|
|
}
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
|
|
compact_unwind_encoding_t encoding, uint32_t functionStart,
|
|
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) {
|
|
uint32_t stackSizeEncoded =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
|
|
uint32_t stackAdjust =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
|
|
uint32_t regCount =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
|
|
uint32_t permutation =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
|
|
uint32_t stackSize = stackSizeEncoded * 4;
|
|
if (indirectStackSize) {
|
|
// stack size is encoded in subl $xxx,%esp instruction
|
|
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
|
stackSize = subl + 4 * stackAdjust;
|
|
}
|
|
// decompress permutation
|
|
uint32_t permunreg[6];
|
|
switch (regCount) {
|
|
case 6:
|
|
permunreg[0] = permutation / 120;
|
|
permutation -= (permunreg[0] * 120);
|
|
permunreg[1] = permutation / 24;
|
|
permutation -= (permunreg[1] * 24);
|
|
permunreg[2] = permutation / 6;
|
|
permutation -= (permunreg[2] * 6);
|
|
permunreg[3] = permutation / 2;
|
|
permutation -= (permunreg[3] * 2);
|
|
permunreg[4] = permutation;
|
|
permunreg[5] = 0;
|
|
break;
|
|
case 5:
|
|
permunreg[0] = permutation / 120;
|
|
permutation -= (permunreg[0] * 120);
|
|
permunreg[1] = permutation / 24;
|
|
permutation -= (permunreg[1] * 24);
|
|
permunreg[2] = permutation / 6;
|
|
permutation -= (permunreg[2] * 6);
|
|
permunreg[3] = permutation / 2;
|
|
permutation -= (permunreg[3] * 2);
|
|
permunreg[4] = permutation;
|
|
break;
|
|
case 4:
|
|
permunreg[0] = permutation / 60;
|
|
permutation -= (permunreg[0] * 60);
|
|
permunreg[1] = permutation / 12;
|
|
permutation -= (permunreg[1] * 12);
|
|
permunreg[2] = permutation / 3;
|
|
permutation -= (permunreg[2] * 3);
|
|
permunreg[3] = permutation;
|
|
break;
|
|
case 3:
|
|
permunreg[0] = permutation / 20;
|
|
permutation -= (permunreg[0] * 20);
|
|
permunreg[1] = permutation / 4;
|
|
permutation -= (permunreg[1] * 4);
|
|
permunreg[2] = permutation;
|
|
break;
|
|
case 2:
|
|
permunreg[0] = permutation / 5;
|
|
permutation -= (permunreg[0] * 5);
|
|
permunreg[1] = permutation;
|
|
break;
|
|
case 1:
|
|
permunreg[0] = permutation;
|
|
break;
|
|
}
|
|
// re-number registers back to standard numbers
|
|
int registersSaved[6];
|
|
bool used[7] = { false, false, false, false, false, false, false };
|
|
for (uint32_t i = 0; i < regCount; ++i) {
|
|
uint32_t renum = 0;
|
|
for (int u = 1; u < 7; ++u) {
|
|
if (!used[u]) {
|
|
if (renum == permunreg[i]) {
|
|
registersSaved[i] = u;
|
|
used[u] = true;
|
|
break;
|
|
}
|
|
++renum;
|
|
}
|
|
}
|
|
}
|
|
uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
|
|
for (uint32_t i = 0; i < regCount; ++i) {
|
|
switch (registersSaved[i]) {
|
|
case UNWIND_X86_REG_EBX:
|
|
registers.setEBX(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_ECX:
|
|
registers.setECX(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_EDX:
|
|
registers.setEDX(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_EDI:
|
|
registers.setEDI(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_ESI:
|
|
registers.setESI(addressSpace.get32(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_REG_EBP:
|
|
registers.setEBP(addressSpace.get32(savedRegisters));
|
|
break;
|
|
default:
|
|
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
|
"function starting at 0x%X",
|
|
encoding, functionStart);
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
savedRegisters += 4;
|
|
}
|
|
framelessUnwind(addressSpace, savedRegisters, registers);
|
|
return UNW_STEP_SUCCESS;
|
|
}
|
|
|
|
|
|
template <typename A>
|
|
void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
|
|
Registers_x86 ®isters) {
|
|
typename A::pint_t bp = registers.getEBP();
|
|
// ebp points to old ebp
|
|
registers.setEBP(addressSpace.get32(bp));
|
|
// old esp is ebp less saved ebp and return address
|
|
registers.setSP((uint32_t)bp + 8);
|
|
// pop return address into eip
|
|
registers.setIP(addressSpace.get32(bp + 4));
|
|
}
|
|
|
|
template <typename A>
|
|
void CompactUnwinder_x86<A>::framelessUnwind(
|
|
A &addressSpace, typename A::pint_t returnAddressLocation,
|
|
Registers_x86 ®isters) {
|
|
// return address is on stack after last saved register
|
|
registers.setIP(addressSpace.get32(returnAddressLocation));
|
|
// old esp is before return address
|
|
registers.setSP((uint32_t)returnAddressLocation + 4);
|
|
}
|
|
#endif // _LIBUNWIND_TARGET_I386
|
|
|
|
|
|
#if defined(_LIBUNWIND_TARGET_X86_64)
|
|
/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
|
|
/// unwind) by modifying a Registers_x86_64 register set
|
|
template <typename A>
|
|
class CompactUnwinder_x86_64 {
|
|
public:
|
|
|
|
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
|
uint64_t functionStart, A &addressSpace,
|
|
Registers_x86_64 ®isters);
|
|
|
|
private:
|
|
typename A::pint_t pint_t;
|
|
|
|
static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters);
|
|
static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
|
|
Registers_x86_64 ®isters);
|
|
static int
|
|
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
|
|
uint64_t functionStart, A &addressSpace,
|
|
Registers_x86_64 ®isters);
|
|
static int stepWithCompactEncodingFrameless(
|
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
|
A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize);
|
|
};
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
|
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
|
A &addressSpace, Registers_x86_64 ®isters) {
|
|
switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
|
|
case UNWIND_X86_64_MODE_RBP_FRAME:
|
|
return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
|
|
addressSpace, registers);
|
|
case UNWIND_X86_64_MODE_STACK_IMMD:
|
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
|
addressSpace, registers, false);
|
|
case UNWIND_X86_64_MODE_STACK_IND:
|
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
|
addressSpace, registers, true);
|
|
}
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
|
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
|
A &addressSpace, Registers_x86_64 ®isters) {
|
|
uint32_t savedRegistersOffset =
|
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
|
|
uint32_t savedRegistersLocations =
|
|
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
|
|
|
|
uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
|
|
for (int i = 0; i < 5; ++i) {
|
|
switch (savedRegistersLocations & 0x7) {
|
|
case UNWIND_X86_64_REG_NONE:
|
|
// no register saved in this slot
|
|
break;
|
|
case UNWIND_X86_64_REG_RBX:
|
|
registers.setRBX(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R12:
|
|
registers.setR12(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R13:
|
|
registers.setR13(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R14:
|
|
registers.setR14(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R15:
|
|
registers.setR15(addressSpace.get64(savedRegisters));
|
|
break;
|
|
default:
|
|
(void)functionStart;
|
|
_LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
|
|
"function starting at 0x%llX",
|
|
compactEncoding, functionStart);
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
savedRegisters += 8;
|
|
savedRegistersLocations = (savedRegistersLocations >> 3);
|
|
}
|
|
frameUnwind(addressSpace, registers);
|
|
return UNW_STEP_SUCCESS;
|
|
}
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
|
|
compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
|
|
Registers_x86_64 ®isters, bool indirectStackSize) {
|
|
uint32_t stackSizeEncoded =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
|
|
uint32_t stackAdjust =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
|
|
uint32_t regCount =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
|
|
uint32_t permutation =
|
|
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
|
|
uint32_t stackSize = stackSizeEncoded * 8;
|
|
if (indirectStackSize) {
|
|
// stack size is encoded in subl $xxx,%esp instruction
|
|
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
|
stackSize = subl + 8 * stackAdjust;
|
|
}
|
|
// decompress permutation
|
|
uint32_t permunreg[6];
|
|
switch (regCount) {
|
|
case 6:
|
|
permunreg[0] = permutation / 120;
|
|
permutation -= (permunreg[0] * 120);
|
|
permunreg[1] = permutation / 24;
|
|
permutation -= (permunreg[1] * 24);
|
|
permunreg[2] = permutation / 6;
|
|
permutation -= (permunreg[2] * 6);
|
|
permunreg[3] = permutation / 2;
|
|
permutation -= (permunreg[3] * 2);
|
|
permunreg[4] = permutation;
|
|
permunreg[5] = 0;
|
|
break;
|
|
case 5:
|
|
permunreg[0] = permutation / 120;
|
|
permutation -= (permunreg[0] * 120);
|
|
permunreg[1] = permutation / 24;
|
|
permutation -= (permunreg[1] * 24);
|
|
permunreg[2] = permutation / 6;
|
|
permutation -= (permunreg[2] * 6);
|
|
permunreg[3] = permutation / 2;
|
|
permutation -= (permunreg[3] * 2);
|
|
permunreg[4] = permutation;
|
|
break;
|
|
case 4:
|
|
permunreg[0] = permutation / 60;
|
|
permutation -= (permunreg[0] * 60);
|
|
permunreg[1] = permutation / 12;
|
|
permutation -= (permunreg[1] * 12);
|
|
permunreg[2] = permutation / 3;
|
|
permutation -= (permunreg[2] * 3);
|
|
permunreg[3] = permutation;
|
|
break;
|
|
case 3:
|
|
permunreg[0] = permutation / 20;
|
|
permutation -= (permunreg[0] * 20);
|
|
permunreg[1] = permutation / 4;
|
|
permutation -= (permunreg[1] * 4);
|
|
permunreg[2] = permutation;
|
|
break;
|
|
case 2:
|
|
permunreg[0] = permutation / 5;
|
|
permutation -= (permunreg[0] * 5);
|
|
permunreg[1] = permutation;
|
|
break;
|
|
case 1:
|
|
permunreg[0] = permutation;
|
|
break;
|
|
}
|
|
// re-number registers back to standard numbers
|
|
int registersSaved[6];
|
|
bool used[7] = { false, false, false, false, false, false, false };
|
|
for (uint32_t i = 0; i < regCount; ++i) {
|
|
uint32_t renum = 0;
|
|
for (int u = 1; u < 7; ++u) {
|
|
if (!used[u]) {
|
|
if (renum == permunreg[i]) {
|
|
registersSaved[i] = u;
|
|
used[u] = true;
|
|
break;
|
|
}
|
|
++renum;
|
|
}
|
|
}
|
|
}
|
|
uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
|
|
for (uint32_t i = 0; i < regCount; ++i) {
|
|
switch (registersSaved[i]) {
|
|
case UNWIND_X86_64_REG_RBX:
|
|
registers.setRBX(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R12:
|
|
registers.setR12(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R13:
|
|
registers.setR13(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R14:
|
|
registers.setR14(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_R15:
|
|
registers.setR15(addressSpace.get64(savedRegisters));
|
|
break;
|
|
case UNWIND_X86_64_REG_RBP:
|
|
registers.setRBP(addressSpace.get64(savedRegisters));
|
|
break;
|
|
default:
|
|
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
|
"function starting at 0x%llX",
|
|
encoding, functionStart);
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
savedRegisters += 8;
|
|
}
|
|
framelessUnwind(addressSpace, savedRegisters, registers);
|
|
return UNW_STEP_SUCCESS;
|
|
}
|
|
|
|
|
|
template <typename A>
|
|
void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
|
|
Registers_x86_64 ®isters) {
|
|
uint64_t rbp = registers.getRBP();
|
|
// ebp points to old ebp
|
|
registers.setRBP(addressSpace.get64(rbp));
|
|
// old esp is ebp less saved ebp and return address
|
|
registers.setSP(rbp + 16);
|
|
// pop return address into eip
|
|
registers.setIP(addressSpace.get64(rbp + 8));
|
|
}
|
|
|
|
template <typename A>
|
|
void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
|
|
uint64_t returnAddressLocation,
|
|
Registers_x86_64 ®isters) {
|
|
// return address is on stack after last saved register
|
|
registers.setIP(addressSpace.get64(returnAddressLocation));
|
|
// old esp is before return address
|
|
registers.setSP(returnAddressLocation + 8);
|
|
}
|
|
#endif // _LIBUNWIND_TARGET_X86_64
|
|
|
|
|
|
|
|
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
|
/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
|
|
/// unwind) by modifying a Registers_arm64 register set
|
|
template <typename A>
|
|
class CompactUnwinder_arm64 {
|
|
public:
|
|
|
|
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
|
uint64_t functionStart, A &addressSpace,
|
|
Registers_arm64 ®isters);
|
|
|
|
private:
|
|
typename A::pint_t pint_t;
|
|
|
|
static int
|
|
stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
|
|
uint64_t functionStart, A &addressSpace,
|
|
Registers_arm64 ®isters);
|
|
static int stepWithCompactEncodingFrameless(
|
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
|
A &addressSpace, Registers_arm64 ®isters);
|
|
};
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
|
|
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
|
A &addressSpace, Registers_arm64 ®isters) {
|
|
switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
|
|
case UNWIND_ARM64_MODE_FRAME:
|
|
return stepWithCompactEncodingFrame(compactEncoding, functionStart,
|
|
addressSpace, registers);
|
|
case UNWIND_ARM64_MODE_FRAMELESS:
|
|
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
|
addressSpace, registers);
|
|
}
|
|
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
|
}
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
|
|
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
|
Registers_arm64 ®isters) {
|
|
uint32_t stackSize =
|
|
16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
|
|
|
|
uint64_t savedRegisterLoc = registers.getSP() + stackSize;
|
|
|
|
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
|
|
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D8,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D9,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D10,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D11,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D12,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D13,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D14,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D15,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
|
|
// subtract stack size off of sp
|
|
registers.setSP(savedRegisterLoc);
|
|
|
|
// set pc to be value in lr
|
|
registers.setIP(registers.getRegister(UNW_ARM64_LR));
|
|
|
|
return UNW_STEP_SUCCESS;
|
|
}
|
|
|
|
template <typename A>
|
|
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
|
|
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
|
Registers_arm64 ®isters) {
|
|
uint64_t savedRegisterLoc = registers.getFP() - 8;
|
|
|
|
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X19, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X20, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X21, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X22, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X23, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X24, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X25, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X26, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
|
registers.setRegister(UNW_ARM64_X27, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setRegister(UNW_ARM64_X28, addressSpace.get64(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
|
|
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D8,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D9,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D10,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D11,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D12,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D13,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
|
registers.setFloatRegister(UNW_ARM64_D14,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
registers.setFloatRegister(UNW_ARM64_D15,
|
|
addressSpace.getDouble(savedRegisterLoc));
|
|
savedRegisterLoc -= 8;
|
|
}
|
|
|
|
uint64_t fp = registers.getFP();
|
|
// fp points to old fp
|
|
registers.setFP(addressSpace.get64(fp));
|
|
// old sp is fp less saved fp and lr
|
|
registers.setSP(fp + 16);
|
|
// pop return address into pc
|
|
registers.setIP(addressSpace.get64(fp + 8));
|
|
|
|
return UNW_STEP_SUCCESS;
|
|
}
|
|
#endif // _LIBUNWIND_TARGET_AARCH64
|
|
|
|
|
|
} // namespace libunwind
|
|
|
|
#endif // __COMPACT_UNWINDER_HPP__
|