/* -*- 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <libunwind.h>
#include <mach-o/compact_unwind_encoding.h>

#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 <typename A>
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 <typename A>
int CompactUnwinder_x86<A>::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 <typename A>
int CompactUnwinder_x86<A>::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 <typename A>
int CompactUnwinder_x86<A>::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 <typename A>
int CompactUnwinder_x86<A>::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 <typename A>
void CompactUnwinder_x86<A>::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 <typename A>
void CompactUnwinder_x86<A>::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 <typename A>
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 <typename A>
int CompactUnwinder_x86_64<A>::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 <typename A>
int CompactUnwinder_x86_64<A>::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 <typename A>
int CompactUnwinder_x86_64<A>::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 <typename A>
int CompactUnwinder_x86_64<A>::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 <typename A>
void CompactUnwinder_x86_64<A>::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 <typename A>
void CompactUnwinder_x86_64<A>::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__




