/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * * Copyright (c) 2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ // // C++ interface to lower levels of libuwind // #ifndef __COMPACT_UNWINDER_HPP__ #define __COMPACT_UNWINDER_HPP__ #include #include #include #include #include #include "AddressSpace.hpp" #include "Registers.hpp" #define EXTRACT_BITS(value, mask) \ ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) #define SUPPORT_OLD_BINARIES 0 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& registers); private: typename A::pint_t pint_t; static void frameUnwind(A& addressSpace, Registers_x86& registers); static void framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers); static int stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers); static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers, bool indirectStackSize); #if SUPPORT_OLD_BINARIES static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers); #endif }; template int CompactUnwinder_x86::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) { //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); switch ( compactEncoding & UNWIND_X86_MODE_MASK ) { #if SUPPORT_OLD_BINARIES case UNWIND_X86_MODE_COMPATIBILITY: return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers); #endif 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); } ABORT("invalid compact unwind encoding"); } template int CompactUnwinder_x86::stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) { uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); uint64_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: DEBUG_MESSAGE("bad register for EBP frame, encoding=%08X for function starting at 0x%X\n", compactEncoding, functionStart); 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& registers, 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 int 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) { int 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 - 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: DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%X\n", encoding, functionStart); ABORT("invalid compact unwind encoding"); } savedRegisters += 4; } framelessUnwind(addressSpace, savedRegisters, registers); return UNW_STEP_SUCCESS; } #if SUPPORT_OLD_BINARIES template int CompactUnwinder_x86::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers) { //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); typename A::pint_t savedRegisters; uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_SIZE); uint32_t stackSize; uint32_t stackAdjust; switch (compactEncoding & UNWIND_X86_CASE_MASK ) { case UNWIND_X86_UNWIND_INFO_UNSPECIFIED: return UNW_ENOINFO; case UNWIND_X86_EBP_FRAME_NO_REGS: frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_EBX: savedRegisters = registers.getEBP() - 4; registers.setEBX(addressSpace.get32(savedRegisters)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_ESI: savedRegisters = registers.getEBP() - 4; registers.setESI(addressSpace.get32(savedRegisters)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_EDI: savedRegisters = registers.getEBP() - 4; registers.setEDI(addressSpace.get32(savedRegisters)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_EBX_ESI: savedRegisters = registers.getEBP() - 8; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_ESI_EDI: savedRegisters = registers.getEBP() - 8; registers.setESI(addressSpace.get32(savedRegisters)); registers.setEDI(addressSpace.get32(savedRegisters+4)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_EBX_ESI_EDI: savedRegisters = registers.getEBP() - 12; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); registers.setEDI(addressSpace.get32(savedRegisters+8)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_EBP_FRAME_EBX_EDI: savedRegisters = registers.getEBP() - 8; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setEDI(addressSpace.get32(savedRegisters+4)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_NO_REGS: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*0; framelessUnwind(addressSpace, savedRegisters+4*0, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_EBX: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*1; registers.setEBX(addressSpace.get32(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+4*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_ESI: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*1; registers.setESI(addressSpace.get32(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+4*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_EDI: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*1; registers.setEDI(addressSpace.get32(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+4*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_EBX_ESI: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*2; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); framelessUnwind(addressSpace, savedRegisters+4*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_ESI_EDI: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*2; registers.setESI(addressSpace.get32(savedRegisters)); registers.setEDI(addressSpace.get32(savedRegisters+4)); framelessUnwind(addressSpace, savedRegisters+4*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_ESI_EDI_EBP: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*3; registers.setESI(addressSpace.get32(savedRegisters)); registers.setEDI(addressSpace.get32(savedRegisters+4)); registers.setEBP(addressSpace.get32(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+4*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_EBX_ESI_EDI: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*3; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); registers.setEDI(addressSpace.get32(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+4*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IMM_STK_EBX_ESI_EDI_EBP: stackSize = stackValue * 4; savedRegisters = registers.getSP() + stackSize - 4 - 4*4; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); registers.setEDI(addressSpace.get32(savedRegisters+8)); registers.setEBP(addressSpace.get32(savedRegisters+12)); framelessUnwind(addressSpace, savedRegisters+4*4, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_NO_REGS: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*0; framelessUnwind(addressSpace, savedRegisters+4*0, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_EBX: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*1; registers.setEBX(addressSpace.get32(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+4*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_ESI: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*1; registers.setESI(addressSpace.get32(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+4*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_EDI: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*1; registers.setEDI(addressSpace.get32(savedRegisters)); return UNW_STEP_SUCCESS; framelessUnwind(addressSpace, savedRegisters+4*1, registers); case UNWIND_X86_IND_STK_EBX_ESI: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*2; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); framelessUnwind(addressSpace, savedRegisters+4*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_ESI_EDI: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*2; registers.setESI(addressSpace.get32(savedRegisters)); registers.setEDI(addressSpace.get32(savedRegisters+4)); framelessUnwind(addressSpace, savedRegisters+4*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_ESI_EDI_EBP: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*3; registers.setESI(addressSpace.get32(savedRegisters)); registers.setEDI(addressSpace.get32(savedRegisters+4)); registers.setEBP(addressSpace.get32(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+4*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_EBX_ESI_EDI: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*3; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); registers.setEDI(addressSpace.get32(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+4*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_IND_STK_EBX_ESI_EDI_EBP: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST); stackSize += stackAdjust*4; savedRegisters = registers.getSP() + stackSize - 4 - 4*4; registers.setEBX(addressSpace.get32(savedRegisters)); registers.setESI(addressSpace.get32(savedRegisters+4)); registers.setEDI(addressSpace.get32(savedRegisters+8)); registers.setEBP(addressSpace.get32(savedRegisters+12)); framelessUnwind(addressSpace, savedRegisters+4*4, registers); return UNW_STEP_SUCCESS; default: DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%X\n", compactEncoding & UNWIND_X86_CASE_MASK, functionStart); ABORT("unknown compact unwind encoding"); } return UNW_EINVAL; } #endif // SUPPORT_OLD_BINARIES template void CompactUnwinder_x86::frameUnwind(A& addressSpace, Registers_x86& registers) { 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(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& registers) { // return address is on stack after last saved register registers.setIP(addressSpace.get32(returnAddressLocation)); // old esp is before return address registers.setSP(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& registers); private: typename A::pint_t pint_t; static void frameUnwind(A& addressSpace, Registers_x86_64& registers); static void framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers); static int stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers, bool indirectStackSize); #if SUPPORT_OLD_BINARIES static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers); #endif }; template int CompactUnwinder_x86_64::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) { //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding); switch ( compactEncoding & UNWIND_X86_64_MODE_MASK ) { #if SUPPORT_OLD_BINARIES case UNWIND_X86_64_MODE_COMPATIBILITY: return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers); #endif 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); } ABORT("invalid compact unwind encoding"); } template int CompactUnwinder_x86_64::stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) { 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: DEBUG_MESSAGE("bad register for RBP frame, encoding=%08X for function starting at 0x%llX\n", compactEncoding, functionStart); 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& registers, 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 int 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) { int 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: DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%llX\n", encoding, functionStart); ABORT("invalid compact unwind encoding"); } savedRegisters += 8; } framelessUnwind(addressSpace, savedRegisters, registers); return UNW_STEP_SUCCESS; } #if SUPPORT_OLD_BINARIES template int CompactUnwinder_x86_64::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers) { uint64_t savedRegisters; uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_SIZE); uint64_t stackSize; uint32_t stackAdjust; switch (compactEncoding & UNWIND_X86_64_CASE_MASK ) { case UNWIND_X86_64_UNWIND_INFO_UNSPECIFIED: return UNW_ENOINFO; case UNWIND_X86_64_RBP_FRAME_NO_REGS: frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_RBP_FRAME_RBX: savedRegisters = registers.getRBP() - 8*1; registers.setRBX(addressSpace.get64(savedRegisters)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_RBP_FRAME_RBX_R12: savedRegisters = registers.getRBP() - 8*2; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13: savedRegisters = registers.getRBP() - 8*3; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14: savedRegisters = registers.getRBP() - 8*4; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); registers.setR14(addressSpace.get64(savedRegisters+24)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14_R15: savedRegisters = registers.getRBP() - 8*5; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); registers.setR14(addressSpace.get64(savedRegisters+24)); registers.setR15(addressSpace.get64(savedRegisters+32)); frameUnwind(addressSpace, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_NO_REGS: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*0; framelessUnwind(addressSpace, savedRegisters+8*0, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*1; registers.setRBX(addressSpace.get64(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+8*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_R12: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*2; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+8*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_RBP: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*2; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+8*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_R12_R13: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*3; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); framelessUnwind(addressSpace, savedRegisters+8*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*4; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); registers.setR14(addressSpace.get64(savedRegisters+24)); framelessUnwind(addressSpace, savedRegisters+8*4, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14_R15: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*5; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); registers.setR14(addressSpace.get64(savedRegisters+24)); registers.setR15(addressSpace.get64(savedRegisters+32)); framelessUnwind(addressSpace, savedRegisters+8*5, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14_R15: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*6; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); registers.setR13(addressSpace.get64(savedRegisters+24)); registers.setR14(addressSpace.get64(savedRegisters+32)); registers.setR15(addressSpace.get64(savedRegisters+40)); framelessUnwind(addressSpace, savedRegisters+8*6, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_RBP_R12: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*3; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); framelessUnwind(addressSpace, savedRegisters+8*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*4; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); registers.setR13(addressSpace.get64(savedRegisters+24)); framelessUnwind(addressSpace, savedRegisters+8*4, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14: stackSize = stackValue * 8; savedRegisters = registers.getSP() + stackSize - 8 - 8*5; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); registers.setR13(addressSpace.get64(savedRegisters+24)); registers.setR14(addressSpace.get64(savedRegisters+32)); framelessUnwind(addressSpace, savedRegisters+8*5, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_NO_REGS: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*0; framelessUnwind(addressSpace, savedRegisters+8*0, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*1; registers.setRBX(addressSpace.get64(savedRegisters)); framelessUnwind(addressSpace, savedRegisters+8*1, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_R12: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*2; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+8*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_RBP: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*2; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); framelessUnwind(addressSpace, savedRegisters+8*2, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_R12_R13: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*3; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); framelessUnwind(addressSpace, savedRegisters+8*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*4; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); registers.setR14(addressSpace.get64(savedRegisters+24)); framelessUnwind(addressSpace, savedRegisters+8*4, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14_R15: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*5; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setR12(addressSpace.get64(savedRegisters+8)); registers.setR13(addressSpace.get64(savedRegisters+16)); registers.setR14(addressSpace.get64(savedRegisters+24)); registers.setR15(addressSpace.get64(savedRegisters+32)); framelessUnwind(addressSpace, savedRegisters+8*5, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14_R15: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*6; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); registers.setR13(addressSpace.get64(savedRegisters+24)); registers.setR14(addressSpace.get64(savedRegisters+32)); registers.setR15(addressSpace.get64(savedRegisters+40)); framelessUnwind(addressSpace, savedRegisters+8*6, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_RBP_R12: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*3; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); framelessUnwind(addressSpace, savedRegisters+8*3, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*4; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); registers.setR13(addressSpace.get64(savedRegisters+24)); framelessUnwind(addressSpace, savedRegisters+8*4, registers); return UNW_STEP_SUCCESS; case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14: stackSize = addressSpace.get32(functionStart+stackValue); stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST); stackSize += stackAdjust*8; savedRegisters = registers.getSP() + stackSize - 8 - 8*5; registers.setRBX(addressSpace.get64(savedRegisters)); registers.setRBP(addressSpace.get64(savedRegisters+8)); registers.setR12(addressSpace.get64(savedRegisters+16)); registers.setR13(addressSpace.get64(savedRegisters+24)); registers.setR14(addressSpace.get64(savedRegisters+32)); framelessUnwind(addressSpace, savedRegisters+8*5, registers); return UNW_STEP_SUCCESS; default: DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%llX\n", compactEncoding & UNWIND_X86_64_CASE_MASK, functionStart); ABORT("unknown compact unwind encoding"); } return UNW_EINVAL; } #endif // SUPPORT_OLD_BINARIES template void CompactUnwinder_x86_64::frameUnwind(A& addressSpace, Registers_x86_64& registers) { 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& registers) { // return address is on stack after last saved register registers.setIP(addressSpace.get64(returnAddressLocation)); // old esp is before return address registers.setSP(returnAddressLocation+8); } }; // namespace libunwind #endif // __COMPACT_UNWINDER_HPP__