| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| // |
| // 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" |
| #include "libunwind_ext.h" |
| |
| #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_AARCH64_X19, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { |
| registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { |
| registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { |
| registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { |
| registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| |
| if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V8, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V9, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V10, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V11, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V12, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V13, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V14, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V15, |
| 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_AARCH64_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_AARCH64_X19, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { |
| registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { |
| registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { |
| registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { |
| registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| |
| if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V8, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V9, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V10, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V11, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V12, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V13, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| } |
| if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { |
| registers.setFloatRegister(UNW_AARCH64_V14, |
| addressSpace.getDouble(savedRegisterLoc)); |
| savedRegisterLoc -= 8; |
| registers.setFloatRegister(UNW_AARCH64_V15, |
| 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__ |