//===-------------------------- 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 #include #include #include #include "AddressSpace.hpp" #include "Registers.hpp" #define EXTRACT_BITS(value, mask) \ ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) namespace libunwind { /// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka /// unwind) by modifying a Registers_x86 register set template 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 int CompactUnwinder_x86::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 int CompactUnwinder_x86::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\n", compactEncoding, functionStart); _LIBUNWIND_ABORT("invalid compact unwind encoding"); } savedRegisters += 4; savedRegistersLocations = (savedRegistersLocations >> 3); } frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; } template int CompactUnwinder_x86::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\n", encoding, functionStart); _LIBUNWIND_ABORT("invalid compact unwind encoding"); } savedRegisters += 4; } framelessUnwind(addressSpace, savedRegisters, registers); return UNW_STEP_SUCCESS; } template void CompactUnwinder_x86::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 void CompactUnwinder_x86::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); } /// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka /// unwind) by modifying a Registers_x86_64 register set template 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 int CompactUnwinder_x86_64::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 int CompactUnwinder_x86_64::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\n", compactEncoding, functionStart); _LIBUNWIND_ABORT("invalid compact unwind encoding"); } savedRegisters += 8; savedRegistersLocations = (savedRegistersLocations >> 3); } frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; } template int CompactUnwinder_x86_64::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\n", encoding, functionStart); _LIBUNWIND_ABORT("invalid compact unwind encoding"); } savedRegisters += 8; } framelessUnwind(addressSpace, savedRegisters, registers); return UNW_STEP_SUCCESS; } template void CompactUnwinder_x86_64::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 void CompactUnwinder_x86_64::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); } /// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka /// unwind) by modifying a Registers_arm64 register set template 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 int CompactUnwinder_arm64::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 int CompactUnwinder_arm64::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 int CompactUnwinder_arm64::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; } }; // namespace libunwind #endif // __COMPACT_UNWINDER_HPP__