/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
 *
 * Copyright (c) 2008-2010 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@
 */
 
//
// processor specific parsing of dwarf unwind instructions
//

#ifndef __DWARF_PARSER_HPP__
#define __DWARF_PARSER_HPP__

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <vector>

#include "libunwind.h"
#include "dwarf2.h"

#include "AddressSpace.hpp"


namespace libunwind {


///
/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
/// See Dwarf Spec for details: 
///    http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
///
template <typename A>
class CFI_Parser
{
public:
	typedef typename A::pint_t		pint_t;	

	///
	/// Information encoded in a CIE (Common Information Entry)
	///  
	struct CIE_Info {
		pint_t		cieStart;
		pint_t		cieLength;
		pint_t		cieInstructions;
		uint8_t		pointerEncoding;
		uint8_t		lsdaEncoding;
		uint8_t		personalityEncoding;
		uint8_t		personalityOffsetInCIE;
		pint_t		personality;
		int			codeAlignFactor;
		int			dataAlignFactor;
		bool		isSignalFrame;
		bool		fdesHaveAugmentationData;
	};
	
	///
	/// Information about an FDE (Frame Description Entry)
	///  
	struct FDE_Info {
		pint_t		fdeStart;
		pint_t		fdeLength;
		pint_t		fdeInstructions;
		pint_t		pcStart;
		pint_t		pcEnd;
		pint_t		lsda;
	};

	///
	/// Used by linker when parsing __eh_frame section
	///  
	struct FDE_Reference {
		pint_t		address;
		uint32_t	offsetInFDE;
		uint8_t		encodingOfAddress;
	};
	struct FDE_Atom_Info {
		pint_t			fdeAddress;
		FDE_Reference	function;
		FDE_Reference	cie;
		FDE_Reference	lsda;
	};
	struct CIE_Atom_Info {
		pint_t			cieAddress;
		FDE_Reference	personality;
	};
	
	
	///
	/// Information about a frame layout and registers saved determined 
	/// by "running" the dwarf FDE "instructions"
	///  
	enum { kMaxRegisterNumber = 120 };
	enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA,
							kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ;
	struct RegisterLocation {
		RegisterSavedWhere	location;
		int64_t				value;
	};
	struct PrologInfo {
		uint32_t			cfaRegister;		
		int32_t				cfaRegisterOffset;	// CFA = (cfaRegister)+cfaRegisterOffset
		int64_t				cfaExpression;		// CFA = expression
		uint32_t			spExtraArgSize;
		uint32_t			codeOffsetAtStackDecrement;
		uint8_t				registerSavedTwiceInCIE;
		bool				registersInOtherRegisters;
		bool				registerSavedMoreThanOnce;
		bool				cfaOffsetWasNegative;
		bool				sameValueUsed;
		RegisterLocation	savedRegisters[kMaxRegisterNumber];	// from where to restore registers
	};

	struct PrologInfoStackEntry {
								PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i)
									: next(n), info(i) {}
		PrologInfoStackEntry*	next;
		PrologInfo				info;
	};

	static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo);
	static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo);
	static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results);
	static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, 
								std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies);
	static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength);

	static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo);

private:
	static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo, 
								pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results);
};


///
/// Parse a FDE into a CIE_Info and an FDE_Info 
///  
template <typename A>
const char* CFI_Parser<A>::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo)
{
	pint_t p = fdeStart;
	uint64_t cfiLength = addressSpace.get32(p);
	p += 4;
	if ( cfiLength == 0xffffffff ) {
		// 0xffffffff means length is really next 8 bytes
		cfiLength = addressSpace.get64(p);
		p += 8;
	}
	if ( cfiLength == 0 ) 
		return "FDE has zero length";	// end marker
	uint32_t ciePointer = addressSpace.get32(p);
	if ( ciePointer == 0 ) 
		return "FDE is really a CIE";	// this is a CIE not an FDE
	pint_t nextCFI = p + cfiLength;
	pint_t cieStart = p-ciePointer;
	const char* err = parseCIE(addressSpace, cieStart, cieInfo);
	if (err != NULL)
		return err;
	p += 4;
	// parse pc begin and range
	pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
	pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
	// parse rest of info
	fdeInfo->lsda = 0;
	// check for augmentation length
	if ( cieInfo->fdesHaveAugmentationData ) {
		uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
		pint_t endOfAug = p + augLen;
		if ( cieInfo->lsdaEncoding != 0 ) {
			// peek at value (without indirection).  Zero means no lsda
			pint_t lsdaStart = p;
			if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) {
				// reset pointer and re-parse lsda address
				p = lsdaStart;
				fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
			}
		}
		p = endOfAug;
	}
	fdeInfo->fdeStart = fdeStart;
	fdeInfo->fdeLength = nextCFI - fdeStart;
	fdeInfo->fdeInstructions = p;
	fdeInfo->pcStart = pcStart;
	fdeInfo->pcEnd = pcStart+pcRange;
	return NULL; // success
}


///
/// Scan an eh_frame section to find an FDE for a pc
///  
template <typename A>
bool CFI_Parser<A>::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo)
{
	//fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
	pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
	const pint_t ehSectionEnd = p + sectionLength;
	while ( p < ehSectionEnd ) {
		pint_t currentCFI = p;
		//fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
		uint64_t cfiLength = addressSpace.get32(p);
		p += 4;
		if ( cfiLength == 0xffffffff ) {
			// 0xffffffff means length is really next 8 bytes
			cfiLength = addressSpace.get64(p);
			p += 8;
		}
		if ( cfiLength == 0 ) 
			return false;	// end marker
		uint32_t id = addressSpace.get32(p);
		if ( id == 0 ) {
			// skip over CIEs
			p += cfiLength;
		}
		else {
			// process FDE to see if it covers pc
			pint_t nextCFI = p + cfiLength;
			uint32_t ciePointer = addressSpace.get32(p);
			pint_t cieStart = p-ciePointer;
			// validate pointer to CIE is within section
			if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) {
				if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) {
					p += 4;
					// parse pc begin and range
					pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
					pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
					//fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
					// test if pc is within the function this FDE covers
					if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) {
						// parse rest of info
						fdeInfo->lsda = 0;
						// check for augmentation length
						if ( cieInfo->fdesHaveAugmentationData ) {
							uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
							pint_t endOfAug = p + augLen;
							if ( cieInfo->lsdaEncoding != 0 ) {
								// peek at value (without indirection).  Zero means no lsda
								pint_t lsdaStart = p;
								if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) {
									// reset pointer and re-parse lsda address
									p = lsdaStart;
									fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
								}
							}
							p = endOfAug;
						}
						fdeInfo->fdeStart = currentCFI;
						fdeInfo->fdeLength = nextCFI - currentCFI;
						fdeInfo->fdeInstructions = p;
						fdeInfo->pcStart = pcStart;
						fdeInfo->pcEnd = pcStart+pcRange;
						//fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
						return true;
					}
					else {
						//fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
						// pc is not in begin/range, skip this FDE
					}
				}
				else {
					// malformed CIE, now augmentation describing pc range encoding
					//fprintf(stderr, "malformed CIE\n");
				}
			}
			else {
				// malformed FDE.  CIE is bad
				//fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n",
				//	(uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd);
			}
			p = nextCFI;
		}
	}
	//fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc);
	return false;
}



///
/// Extract info from a CIE
///  
template <typename A>
const char* CFI_Parser<A>::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo)
{
	//fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie);
	cieInfo->pointerEncoding = 0;
	cieInfo->lsdaEncoding = 0;
	cieInfo->personalityEncoding = 0;
	cieInfo->personalityOffsetInCIE = 0;
	cieInfo->personality = 0;
	cieInfo->codeAlignFactor = 0;
	cieInfo->dataAlignFactor = 0;
	cieInfo->isSignalFrame = false;
	cieInfo->fdesHaveAugmentationData = false;
	cieInfo->cieStart = cie;
	pint_t p = cie;
	uint64_t cieLength = addressSpace.get32(p);
	p += 4;
	pint_t cieContentEnd = p + cieLength;
	if ( cieLength == 0xffffffff ) {
		// 0xffffffff means length is really next 8 bytes
		cieLength = addressSpace.get64(p);
		p += 8;
		cieContentEnd = p + cieLength;
	}
	if ( cieLength == 0 ) 
		return NULL;	
	// CIE ID is always 0
	if ( addressSpace.get32(p) != 0 ) 
		return "CIE ID is not zero";
	p += 4;
	// Version is always 1 or 3
	uint8_t version = addressSpace.get8(p);
	if ( (version != 1) && (version != 3) )
		return "CIE version is not 1 or 3";
	++p;
	// save start of augmentation string and find end
	pint_t strStart = p;
	while ( addressSpace.get8(p) != 0 )
		++p;
	++p;
	// parse code aligment factor
	cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd);
	// parse data alignment factor
	cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd);
	// parse return address register
	addressSpace.getULEB128(p, cieContentEnd);
	// parse augmentation data based on augmentation string
	const char* result = NULL;
	if ( addressSpace.get8(strStart) == 'z' ) {
		// parse augmentation data length 
		addressSpace.getULEB128(p, cieContentEnd);
		for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) {
			switch ( addressSpace.get8(s) ) {
				case 'z':
					cieInfo->fdesHaveAugmentationData = true;
					break;
				case 'P':
					cieInfo->personalityEncoding = addressSpace.get8(p);
					++p;
					cieInfo->personalityOffsetInCIE = p-cie;
					cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
					break;
				case 'L':
					cieInfo->lsdaEncoding = addressSpace.get8(p);
					++p;
					break;
				case 'R':
					cieInfo->pointerEncoding = addressSpace.get8(p);
					++p;
					break;
				case 'S':
					cieInfo->isSignalFrame = true;
					break;
				default:
					// ignore unknown letters
					break;
			}
		}
	}
	cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
	cieInfo->cieInstructions = p;
	return result;
}


template <typename A>
uint32_t CFI_Parser<A>::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength)
{
	uint32_t count = 0;
	const pint_t ehSectionEnd = ehSectionStart + sectionLength;
	for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
		uint64_t cfiLength = addressSpace.get32(p);
		p += 4;
		if ( cfiLength == 0xffffffff ) {
			// 0xffffffff means length is really next 8 bytes
			cfiLength = addressSpace.get64(p);
			p += 8;
		}
		if ( cfiLength == 0 ) 
			return count;	// end marker
		++count;
		p += cfiLength;
	}
	return count;
}



template <typename A>
const char* CFI_Parser<A>::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, 
								  std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies)
{
	const pint_t ehSectionEnd = ehSectionStart + sectionLength;
	for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
		pint_t currentCFI = p;
		uint64_t cfiLength = addressSpace.get32(p);
		p += 4;
		if ( cfiLength == 0xffffffff ) {
			// 0xffffffff means length is really next 8 bytes
			cfiLength = addressSpace.get64(p);
			p += 8;
		}
		if ( cfiLength == 0 ) 
			return NULL;	// end marker
		uint32_t id = addressSpace.get32(p);
		if ( id == 0 ) {
			// is CIE
			CIE_Info cieInfo;
			const char* err = parseCIE(addressSpace, currentCFI, &cieInfo);
			if ( err != NULL ) 
				return err;
			CIE_Atom_Info entry;
			entry.cieAddress = currentCFI;
			entry.personality.address = cieInfo.personality;
			entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE;
			entry.personality.encodingOfAddress = cieInfo.personalityEncoding;
			cies.push_back(entry);
			p += cfiLength;
		}
		else {
			// is FDE
			FDE_Atom_Info entry;
			entry.fdeAddress = currentCFI;
			entry.function.address = 0;
			entry.cie.address = 0;
			entry.lsda.address = 0;
			pint_t nextCFI = p + cfiLength;
			uint32_t ciePointer = addressSpace.get32(p);
			pint_t cieStart = p-ciePointer;
			// validate pointer to CIE is within section
			if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) )
				return "FDE points to CIE outside __eh_frame section";
			CIE_Info cieInfo;
			const char* err = parseCIE(addressSpace, cieStart, &cieInfo);
			if ( err != NULL ) 
				return err;
			entry.cie.address = cieStart;
			entry.cie.offsetInFDE = p-currentCFI;
			entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel;
			p += 4;
			// parse pc begin and range
			pint_t offsetOfFunctionAddress = p-currentCFI;
			pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding);
			pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F);
			//fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
			// test if pc is within the function this FDE covers
			entry.function.address = pcStart;
			entry.function.offsetInFDE = offsetOfFunctionAddress;
			entry.function.encodingOfAddress = cieInfo.pointerEncoding;
			// skip over augmentation length
			if ( cieInfo.fdesHaveAugmentationData ) {
				uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
				pint_t endOfAug = p + augLen;
				if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) {
					pint_t offsetOfLSDAAddress = p-currentCFI;
					entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding);
					entry.lsda.offsetInFDE = offsetOfLSDAAddress;
					entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding;
				}
				p = endOfAug;
			}
			fdes.push_back(entry);
			p = nextCFI;
		}
	}
	return NULL; // success
}

	

///
/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE
///  
template <typename A>
bool CFI_Parser<A>::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results)
{
	// clear results
	bzero(results, sizeof(PrologInfo));
	PrologInfoStackEntry* rememberStack = NULL;

	// parse CIE then FDE instructions
	return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength, 
						cieInfo, (pint_t)(-1), rememberStack, results)
	    && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength, 
							cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results);
}


///
/// "run" the dwarf instructions
///  
template <typename A>
bool CFI_Parser<A>::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo,
									pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results)
{
	const bool logDwarf = false;
	pint_t p = instructions;
	uint32_t codeOffset = 0;
	PrologInfo initialState = *results;
	if ( logDwarf ) fprintf(stderr, "parseInstructions(instructions=0x%0llX)\n", (uint64_t)instructionsEnd);
	
	// see Dwarf Spec, section 6.4.2 for details on unwind opcodes
	while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) {
		uint64_t reg;
		uint64_t reg2;
		int64_t offset;
		uint64_t length;
		uint8_t opcode = addressSpace.get8(p);
		uint8_t operand;
		PrologInfoStackEntry* entry;
		++p;
		switch (opcode) {
			case DW_CFA_nop:
				if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n");
				break;
			case DW_CFA_set_loc:
				codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
				if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n");
				break;
			case DW_CFA_advance_loc1:
				codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
				p += 1;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset);
				break;
			case DW_CFA_advance_loc2:
				codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
				p += 2;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset);
				break;
			case DW_CFA_advance_loc4:
				codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
				p += 4;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset);
				break;
			case DW_CFA_offset_extended:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n");
					return false;
				}
				if ( results->savedRegisters[reg].location != kRegisterUnused ) 
					results->registerSavedMoreThanOnce = true;
				results->savedRegisters[reg].location = kRegisterInCFA;
				results->savedRegisters[reg].value = offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset);
				break;
			case DW_CFA_restore_extended:
				reg = addressSpace.getULEB128(p, instructionsEnd);;
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n");
					return false;
				}
				results->savedRegisters[reg] = initialState.savedRegisters[reg];
				if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg);
				break;
			case DW_CFA_undefined:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n");
					return false;
				}
				results->savedRegisters[reg].location = kRegisterUnused;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg);
				break;
			case DW_CFA_same_value:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n");
					return false;
				}
				// <rdar://problem/8456377> DW_CFA_same_value unsupported
				// "same value" means register was stored in frame, but its current
				// value has not changed, so no need to restore from frame.
				// We model this as if the register was never saved.
				results->savedRegisters[reg].location = kRegisterUnused;
				// set flag to disable conversion to compact unwind
				results->sameValueUsed = true;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg);
				break;
			case DW_CFA_register:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				reg2 = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n");
					return false;
				}
				if ( reg2 > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n");
					return false;
				}
				results->savedRegisters[reg].location = kRegisterInRegister;
				results->savedRegisters[reg].value = reg2;
				// set flag to disable conversion to compact unwind
				results->registersInOtherRegisters = true;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2);
				break;
			case DW_CFA_remember_state:
				// avoid operator new, because that would be an upward dependency
				entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry));
				if ( entry != NULL ) {
					entry->next = rememberStack;
					entry->info = *results;
					rememberStack = entry;
				}
				else {
					return false;
				}
				if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n");
				break;
			case DW_CFA_restore_state:
				if ( rememberStack != NULL ) {
					PrologInfoStackEntry* top = rememberStack;
					*results = top->info;
					rememberStack = top->next;
					free((char*)top);
				}
				else {
					return false;
				}
				if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n");
				break;
			case DW_CFA_def_cfa:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				offset = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n");
					return false;
				}
				results->cfaRegister = reg;
				results->cfaRegisterOffset = offset;
				if ( offset > 0x80000000 ) 
					results->cfaOffsetWasNegative = true;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset);
				break;
			case DW_CFA_def_cfa_register:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n");
					return false;
				}
				results->cfaRegister = reg;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg);
				break;
			case DW_CFA_def_cfa_offset:
				results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd);
				results->codeOffsetAtStackDecrement = codeOffset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset);
				break;
			case DW_CFA_def_cfa_expression:
				results->cfaRegister = 0;
				results->cfaExpression = p;
				length = addressSpace.getULEB128(p, instructionsEnd);
				p += length;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n", 
													results->cfaExpression, length);
				break;
			case DW_CFA_expression:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n");
					return false;
				}
				results->savedRegisters[reg].location = kRegisterAtExpression;
				results->savedRegisters[reg].value = p;
				length = addressSpace.getULEB128(p, instructionsEnd);
				p += length;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n", 
													reg, results->savedRegisters[reg].value, length);
				break;
			case DW_CFA_offset_extended_sf:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n");
					return false;
				}
				offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				if ( results->savedRegisters[reg].location != kRegisterUnused ) 
					results->registerSavedMoreThanOnce = true;
				results->savedRegisters[reg].location = kRegisterInCFA;
				results->savedRegisters[reg].value = offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset);
				break;
			case DW_CFA_def_cfa_sf:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n");
					return false;
				}
				results->cfaRegister = reg;
				results->cfaRegisterOffset = offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset);
				break;
			case DW_CFA_def_cfa_offset_sf:
				results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				results->codeOffsetAtStackDecrement = codeOffset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset);
				break;
			case DW_CFA_val_offset:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
				results->savedRegisters[reg].value = offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset);
				break;
			case DW_CFA_val_offset_sf:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n");
					return false;
				}
				offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
				results->savedRegisters[reg].value = offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset);
				break;
			case DW_CFA_val_expression:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n");
					return false;
				}
				results->savedRegisters[reg].location = kRegisterIsExpression;
				results->savedRegisters[reg].value = p;
				length = addressSpace.getULEB128(p, instructionsEnd);
				p += length;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n", 
													reg, results->savedRegisters[reg].value, length);
				break;
			case DW_CFA_GNU_args_size:
				offset = addressSpace.getULEB128(p, instructionsEnd);
				results->spExtraArgSize = offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset);
				break;
			case DW_CFA_GNU_negative_offset_extended:
				reg = addressSpace.getULEB128(p, instructionsEnd);
				if ( reg > kMaxRegisterNumber ) {
					fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n");
					return false;
				}
				offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
				if ( results->savedRegisters[reg].location != kRegisterUnused ) 
					results->registerSavedMoreThanOnce = true;
				results->savedRegisters[reg].location = kRegisterInCFA;
				results->savedRegisters[reg].value = -offset;
				if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset);
				break;
			default:
				operand = opcode & 0x3F;
				switch ( opcode & 0xC0 ) {
					case DW_CFA_offset:
						reg = operand;
						offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
						if ( results->savedRegisters[reg].location != kRegisterUnused ) {
							// look for idiom of PC saved twice in CIE to mean disable compact unwind encoding
							if ( (pcoffset == (pint_t)(-1)) 
								&& (results->savedRegisters[reg].location == kRegisterInCFA) 
								&& (results->savedRegisters[reg].value == offset)  )
								results->registerSavedTwiceInCIE = reg;
							else
								results->registerSavedMoreThanOnce = true;
						}
						results->savedRegisters[reg].location = kRegisterInCFA;
						results->savedRegisters[reg].value = offset;
						if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset);
						break;
					case DW_CFA_advance_loc:
						codeOffset += operand * cieInfo.codeAlignFactor;
						if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset);
						break;
					case DW_CFA_restore:
						// <rdar://problem/7503075> Python crashes when handling an exception thrown by an obj-c object
						// libffi uses DW_CFA_restore in the middle of some custom dwarf, so it is not a good epilog flag
						//return true; // gcc-4.5 starts the epilog with this
						reg = operand;
						results->savedRegisters[reg] = initialState.savedRegisters[reg];
						if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg);
						break;
					default: 
						if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode);
						return false;
				}
		}
	}

	return true;
}


} // namespace libunwind 


#endif // __DWARF_PARSER_HPP__




