From ce433767d79be40375b69dd3139d04978401df3d Mon Sep 17 00:00:00 2001 From: Nick Kledzik Date: Fri, 18 Mar 2011 20:57:25 +0000 Subject: [PATCH] ld64 should build stand-alone and not need libunwind headers (cherry picked from commit 231841085f71586b0b2d8f6b63b9317783e80c9e) --- src/ld/MachOReaderRelocatable.hpp | 6 +- src/ld/dwarf2.h | 219 +++ src/ld/parsers/libunwind/AddressSpace.hpp | 439 ++++++ src/ld/parsers/libunwind/DwarfInstructions.hpp | 1726 ++++++++++++++++++++++++ src/ld/parsers/libunwind/DwarfParser.hpp | 819 +++++++++++ src/ld/parsers/libunwind/InternalMacros.h | 105 ++ src/ld/parsers/libunwind/Registers.hpp | 1050 ++++++++++++++ 7 files changed, 4361 insertions(+), 3 deletions(-) create mode 100644 src/ld/parsers/libunwind/AddressSpace.hpp create mode 100644 src/ld/parsers/libunwind/DwarfInstructions.hpp create mode 100644 src/ld/parsers/libunwind/DwarfParser.hpp create mode 100644 src/ld/parsers/libunwind/InternalMacros.h create mode 100644 src/ld/parsers/libunwind/Registers.hpp diff --git a/src/ld/MachOReaderRelocatable.hpp b/src/ld/MachOReaderRelocatable.hpp index 2aa7926..b8e5960 100644 --- src/ld/MachOReaderRelocatable.hpp +++ src/ld/MachOReaderRelocatable.hpp @@ -40,9 +40,9 @@ #include "dwarf2.h" #include "debugline.h" -#include -#include -#include +#include "libunwind/DwarfInstructions.hpp" +#include "libunwind/AddressSpace.hpp" +#include "libunwind/Registers.hpp" // // diff --git a/src/ld/dwarf2.h b/src/ld/dwarf2.h index 7a7b4f2..411dbbc 100644 --- src/ld/dwarf2.h +++ src/ld/dwarf2.h @@ -87,4 +87,223 @@ enum { DW_LNE_define_file }; + +// dwarf unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by gcc compiler +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + + #endif diff --git a/src/ld/parsers/libunwind/AddressSpace.hpp b/src/ld/parsers/libunwind/AddressSpace.hpp new file mode 100644 index 0000000..bebd3e6 --- /dev/null +++ src/ld/parsers/libunwind/AddressSpace.hpp @@ -0,0 +1,439 @@ +/* -*- 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 __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FileAbstraction.hpp" +#include "libunwind.h" +#include "InternalMacros.h" +#include "dwarf2.h" + + +#if 0 +#if __i386__ || __x86_64__ +// In 10.6 and later i386 and x86_64 don't have a __dyld section +// We need one to access private _dyld_func_lookup function. + +struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); }; + +static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL }; + + +static int my_dyld_func_lookup(const char* dyld_func_name, void **address) +{ + return (*myDyldSection.lookup)(dyld_func_name, address); +} +#else + #define my_dyld_func_lookup _dyld_func_lookup +#endif + + +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + static void* (*p)(void*, dyld_unwind_sections*) = NULL; + + if(p == NULL) + my_dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); + return p(addr, info); +} +#endif // 0 + + + +namespace libunwind { + +/// +/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the same process. It compiles away and making local unwinds very fast. +/// +class LocalAddressSpace +{ +public: + + #if __LP64__ + typedef uint64_t pint_t; + typedef int64_t sint_t; + #else + typedef uint32_t pint_t; + typedef int32_t sint_t; + #endif + uint8_t get8(pint_t addr) { return *((uint8_t*)addr); } + uint16_t get16(pint_t addr) { return *((uint16_t*)addr); } + uint32_t get32(pint_t addr) { return *((uint32_t*)addr); } + uint64_t get64(pint_t addr) { return *((uint64_t*)addr); } + double getDouble(pint_t addr) { return *((double*)addr); } + v128 getVector(pint_t addr) { return *((v128*)addr); } + uintptr_t getP(pint_t addr); + static uint64_t getULEB128(pint_t& addr, pint_t end); + static int64_t getSLEB128(pint_t& addr, pint_t end); + + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart); + +}; + +LocalAddressSpace sThisAddress; + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) +{ +#if __LP64__ + return get64(addr); +#else + return get32(addr); +#endif +} + +/* Read a ULEB128 into a 64-bit word. */ +inline uint64_t +LocalAddressSpace::getULEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + const uint8_t* pend = (uint8_t*)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if ( p == pend ) + ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + ABORT("malformed uleb128 expression"); + } + else { + result |= b << bit; + bit += 7; + } + } while ( *p++ >= 0x80 ); + addr = (pint_t)p; + return result; +} + +/* Read a SLEB128 into a 64-bit word. */ +inline int64_t +LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end) +{ + const uint8_t* p = (uint8_t*)addr; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + addr = (pint_t)p; + return result; +} + +LocalAddressSpace::pint_t +LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding) +{ + pint_t startAddr = addr; + const uint8_t* p = (uint8_t*)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + result = (int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + result = (int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + ABORT("DW_EH_PE_datarel pointer encoding not supported"); + break; + case DW_EH_PE_funcrel: + ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + ABORT("unknown pointer encoding"); + break; + } + + if ( encoding & DW_EH_PE_indirect ) + result = getP(result); + + return result; +} + + +inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart) +{ + dyld_unwind_sections info; + if ( _dyld_find_unwind_sections((void*)addr, &info) ) { + mh = (pint_t)info.mh; + dwarfStart = (pint_t)info.dwarf_section; + dwarfLen = (pint_t)info.dwarf_section_length; + compactStart = (pint_t)info.compact_unwind_section; + return true; + } + return false; +} + + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + dl_info dyldInfo; + if ( dladdr((void*)addr, &dyldInfo) ) { + if ( dyldInfo.dli_sname != NULL ) { + strlcpy(buf, dyldInfo.dli_sname, bufLen); + *offset = (addr - (pint_t)dyldInfo.dli_saddr); + return true; + } + } + return false; +} + + + +#if UNW_REMOTE + +/// +/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread +/// in the another process. The other process can be a different endianness and a different +/// pointer size and is handled by the P template parameter. +/// +template +class OtherAddressSpace +{ +public: + OtherAddressSpace(task_t task) : fTask(task) {} + + typedef typename P::uint_t pint_t; + + uint8_t get8(pint_t addr); + uint16_t get16(pint_t addr); + uint32_t get32(pint_t addr); + uint64_t get64(pint_t addr); + pint_t getP(pint_t addr); + uint64_t getULEB128(pint_t& addr, pint_t end); + int64_t getSLEB128(pint_t& addr, pint_t end); + pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding); + bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset); + bool findUnwindSections(pint_t addr, unwind_sections& info); +private: + void* localCopy(pint_t addr); + + + task_t fTask; +}; + + +template +uint8_t OtherAddressSpace

::get8(pint_t addr) +{ + return *((uint8_t*)localCopy(addr)); +} + +template +uint16_t OtherAddressSpace

::get16(pint_t addr) +{ + return P::E::get16(*(uint16_t*)localCopy(addr)); +} + +template +uint32_t OtherAddressSpace

::get32(pint_t addr) +{ + return P::E::get32(*(uint32_t*)localCopy(addr)); +} + +template +uint64_t OtherAddressSpace

::get64(pint_t addr) +{ + return P::E::get64(*(uint64_t*)localCopy(addr)); +} + +template +typename P::uint_t OtherAddressSpace

::getP(pint_t addr) +{ + return P::getP(*(uint64_t*)localCopy(addr)); +} + +template +uint64_t OtherAddressSpace

::getULEB128(pint_t& addr, pint_t end) +{ + uintptr_t size = (end - addr); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr); + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr+size); + addr += (laddr-sladdr); + return result; +} + +template +int64_t OtherAddressSpace

::getSLEB128(pint_t& addr, pint_t end) +{ + uintptr_t size = (end - addr); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)localCopy(addr); + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getSLEB128(laddr, laddr+size); + addr += (laddr-sladdr); + return result; +} + +template +void* OtherAddressSpace

::localCopy(pint_t addr) +{ + // FIX ME +} + +template +bool OtherAddressSpace

::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset) +{ + // FIX ME +} + + + +/// +/// unw_addr_space is the base class that abstract unw_addr_space_t type in libunwind.h points to. +/// +struct unw_addr_space +{ + cpu_type_t cpuType; + task_t taskPort; +}; + + +/// +/// unw_addr_space_i386 is the concrete instance that a unw_addr_space_t points to when examining +/// a 32-bit intel process. +/// +struct unw_addr_space_i386 : public unw_addr_space +{ + unw_addr_space_i386(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + + +/// +/// unw_addr_space_x86_64 is the concrete instance that a unw_addr_space_t points to when examining +/// a 64-bit intel process. +/// +struct unw_addr_space_x86_64 : public unw_addr_space +{ + unw_addr_space_x86_64(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + + +/// +/// unw_addr_space_ppc is the concrete instance that a unw_addr_space_t points to when examining +/// a 32-bit PowerPC process. +/// +struct unw_addr_space_ppc : public unw_addr_space +{ + unw_addr_space_ppc(task_t task) : oas(task) {} + OtherAddressSpace > oas; +}; + + +#endif // UNW_REMOTE + + +} // namespace libunwind + + + +#endif // __ADDRESSSPACE_HPP__ + + + + diff --git a/src/ld/parsers/libunwind/DwarfInstructions.hpp b/src/ld/parsers/libunwind/DwarfInstructions.hpp new file mode 100644 index 0000000..b04582d --- /dev/null +++ src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -0,0 +1,1726 @@ +/* -*- 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@ + */ + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "dwarf2.h" +#include "AddressSpace.hpp" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "InternalMacros.h" +//#include "CompactUnwinder.hpp" + +#define EXTRACT_BITS(value, mask) \ + ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) ) + +#define CFI_INVALID_ADDRESS ((pint_t)(-1)) + +namespace libunwind { + +/// +/// Used by linker when parsing __eh_frame section +/// +template +struct CFI_Reference { + typedef typename A::pint_t pint_t; + uint8_t encodingOfTargetAddress; + uint32_t offsetInCFI; + pint_t targetAddress; +}; +template +struct CFI_Atom_Info { + typedef typename A::pint_t pint_t; + pint_t address; + uint32_t size; + bool isCIE; + union { + struct { + CFI_Reference function; + CFI_Reference cie; + CFI_Reference lsda; + uint32_t compactUnwindInfo; + } fdeInfo; + struct { + CFI_Reference personality; + } cieInfo; + } u; +}; + +typedef void (*WarnFunc)(void* ref, uint64_t funcAddr, const char* msg); + +/// +/// DwarfInstructions maps abtract dwarf unwind instructions to a particular architecture +/// +template +class DwarfInstructions +{ +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn); + + + static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]); + + static int stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + static pint_t evaluateExpression(pint_t expression, A& addressSpace, const R& registers, pint_t initialStackValue); + static pint_t getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + static double getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + static v128 getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg); + + // x86 specific variants + static int lastRestoreReg(const Registers_x86&); + static bool isReturnAddressRegister(int regNum, const Registers_x86&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_x86&); + + static uint32_t getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + + // x86_64 specific variants + static int lastRestoreReg(const Registers_x86_64&); + static bool isReturnAddressRegister(int regNum, const Registers_x86_64&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_x86_64&); + + static uint32_t getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86_64&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); + + // ppc specific variants + static int lastRestoreReg(const Registers_ppc&); + static bool isReturnAddressRegister(int regNum, const Registers_ppc&); + static pint_t getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, const Registers_ppc&); + static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc&); + static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc&, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]); +}; + + + + +template +const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + CFI_Atom_Info* infos, uint32_t infosCount, void* ref, WarnFunc warn) +{ + typename CFI_Parser::CIE_Info cieInfo; + CFI_Atom_Info* entry = infos; + CFI_Atom_Info* end = &infos[infosCount]; + 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 + if ( entry >= end ) + return "too little space allocated for parseCFIs"; + pint_t nextCFI = p + cfiLength; + uint32_t id = addressSpace.get32(p); + if ( id == 0 ) { + // is CIE + const char* err = CFI_Parser::parseCIE(addressSpace, currentCFI, &cieInfo); + if ( err != NULL ) + return err; + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = true; + entry->u.cieInfo.personality.targetAddress = cieInfo.personality; + entry->u.cieInfo.personality.offsetInCFI = cieInfo.personalityOffsetInCIE; + entry->u.cieInfo.personality.encodingOfTargetAddress = cieInfo.personalityEncoding; + ++entry; + } + else { + // is FDE + entry->address = currentCFI; + entry->size = nextCFI - currentCFI; + entry->isCIE = false; + entry->u.fdeInfo.function.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.cie.targetAddress = CFI_INVALID_ADDRESS; + entry->u.fdeInfo.lsda.targetAddress = CFI_INVALID_ADDRESS; + 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"; + // optimize usual case where cie is same for all FDEs + if ( cieStart != cieInfo.cieStart ) { + const char* err = CFI_Parser::parseCIE(addressSpace, cieStart, &cieInfo); + if ( err != NULL ) + return err; + } + entry->u.fdeInfo.cie.targetAddress = cieStart; + entry->u.fdeInfo.cie.offsetInCFI = p-currentCFI; + entry->u.fdeInfo.cie.encodingOfTargetAddress = 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->u.fdeInfo.function.targetAddress = pcStart; + entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress; + entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding; + // 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; + pint_t offsetOfLSDAAddress = p-currentCFI; + entry->u.fdeInfo.lsda.targetAddress = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding); + entry->u.fdeInfo.lsda.offsetInCFI = offsetOfLSDAAddress; + entry->u.fdeInfo.lsda.encodingOfTargetAddress = cieInfo.lsdaEncoding; + } + } + p = endOfAug; + } + // compute compact unwind encoding + typename CFI_Parser::FDE_Info fdeInfo; + fdeInfo.fdeStart = currentCFI; + fdeInfo.fdeLength = nextCFI - currentCFI; + fdeInfo.fdeInstructions = p; + fdeInfo.pcStart = pcStart; + fdeInfo.pcEnd = pcStart + pcRange; + fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress; + typename CFI_Parser::PrologInfo prolog; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + char warningBuffer[1024]; + entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != CFI_INVALID_ADDRESS ) + entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA; + if ( warningBuffer[0] != '\0' ) + warn(ref, fdeInfo.pcStart, warningBuffer); + } + else { + warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed"); + entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy); + } + ++entry; + } + p = nextCFI; + } + if ( entry != end ) + return "wrong entry count for parseCFIs"; + return NULL; // success +} + + + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart, + pint_t* lsda, pint_t* personality, + char warningBuffer[1024]) +{ + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + R dummy; // for proper selection of architecture specific functions + if ( CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) { + *lsda = fdeInfo.lsda; + *personality = cieInfo.personality; + compact_unwind_encoding_t encoding; + encoding = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer); + if ( fdeInfo.lsda != 0 ) + encoding |= UNWIND_HAS_LSDA; + return encoding; + } + else { + strcpy(warningBuffer, "dwarf unwind instructions could not be parsed"); + return encodeToUseDwarf(dummy); + } + } + else { + strcpy(warningBuffer, "dwarf FDE could not be parsed"); + return encodeToUseDwarf(dummy); + } +} + + +template +typename A::pint_t DwarfInstructions::getSavedRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getP(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getP(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + return evaluateExpression(savedReg.value, addressSpace, registers, cfa); + + case CFI_Parser::kRegisterInRegister: + return registers.getRegister(savedReg.value); + + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + ABORT("unsupported restore location for register"); +} + +template +double DwarfInstructions::getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getDouble(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getDouble(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for float register"); +} + +template +v128 DwarfInstructions::getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa, + const typename CFI_Parser::RegisterLocation& savedReg) +{ + switch ( savedReg.location ) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getVector(cfa + savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getVector(evaluateExpression(savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + ABORT("unsupported restore location for vector register"); +} + + +template +int DwarfInstructions::stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers) +{ + //fprintf(stderr, "stepWithDwarf(pc=0x%0llX, fdeStart=0x%0llX)\n", (uint64_t)pc, (uint64_t)fdeStart); + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if ( CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) { + typename CFI_Parser::PrologInfo prolog; + if ( CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, &prolog) ) { + R newRegisters = registers; + + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that dwarf says were saved + pint_t returnAddress = 0; + for (int i=0; i <= lastRestoreReg(newRegisters); ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( registers.validFloatRegister(i) ) + newRegisters.setFloatRegister(i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( registers.validVectorRegister(i) ) + newRegisters.setVectorRegister(i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else if ( isReturnAddressRegister(i, registers) ) + returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]); + else if ( registers.validRegister(i) ) + newRegisters.setRegister(i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i])); + else + return UNW_EBADREG; + } + } + + // by definition the CFA is the stack pointer at the call site, so restoring SP means setting it to CFA + newRegisters.setSP(cfa); + + // return address is address after call site instruction, so setting IP to that does a return + newRegisters.setIP(returnAddress); + + // do the actual step by replacing the register set with the new ones + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + + + +template +typename A::pint_t DwarfInstructions::evaluateExpression(pint_t expression, A& addressSpace, + const R& registers, pint_t initialStackValue) +{ + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression+20; // just need something until length is read + uint64_t length = addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) fprintf(stderr, "evaluateExpression(): length=%llu\n", length); + pint_t stack[100]; + pint_t* sp = stack; + *(++sp) = initialStackValue; + + while ( p < expressionEnd ) { + if (log) { + for(pint_t* t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) fprintf(stderr, "dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t)addressSpace.get8(p); + p += 1; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t)addressSpace.get16(p); + p += 2; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (int32_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = svalue; + if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-reg]; + *(++sp) = value; + if (log) fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((uint64_t*)value); + if (log) fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = *sp; + if ( svalue < 0 ) + *sp = -svalue; + if (log) fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = *sp--; + *sp = *sp / svalue; + if (log) fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + svalue = *sp--; + *sp = *sp - svalue; + if (log) fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = *sp--; + *sp = *sp % svalue; + if (log) fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = *sp--; + *sp = *sp * svalue; + if (log) fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = *sp; + *sp = ~svalue; + if (log) fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += addressSpace.getULEB128(p, expressionEnd); + if (log) fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = *sp; + *sp = svalue >> value; + if (log) fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + p += svalue; + if (log) fprintf(stderr, "skip %lld\n", (uint64_t)svalue); + break; + + case DW_OP_bra: + svalue = (int16_t)addressSpace.get16(p); + p += 2; + if ( *sp-- ) + p += svalue; + if (log) fprintf(stderr, "bra %lld\n", (uint64_t)svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) fprintf(stderr, "ne\n"); + break; + + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + value = opcode - DW_OP_lit0; + *(++sp) = value; + if (log) fprintf(stderr, "push literal 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + reg = opcode - DW_OP_reg0; + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = addressSpace.getULEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg); + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + reg = opcode - DW_OP_breg0; + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_bregx: + reg = addressSpace.getULEB128(p, expressionEnd); + svalue = addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = registers.getRegister(reg) + svalue; + if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue); + break; + + case DW_OP_fbreg: + ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch ( addressSpace.get8(p++) ) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = addressSpace.get64(value); + break; + default: + ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t)value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + ABORT("dwarf opcode not implemented"); + } + + } + if (log) fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t)*sp); + return *sp; +} + + + +// +// x86_64 specific functions +// + +template +int DwarfInstructions::lastRestoreReg(const Registers_x86_64&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)DW_X86_64_RET_ADDR ); + return DW_X86_64_RET_ADDR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_x86_64&) +{ + return (regNum == DW_X86_64_RET_ADDR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_x86_64& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86_64 cfa"); +} + + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_x86_64&) +{ + return UNWIND_X86_64_MODE_DWARF; +} + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_x86&) +{ + return UNWIND_X86_MODE_DWARF; +} + + + +template +uint32_t DwarfInstructions::getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/8; + + switch ( reg ) { + case UNW_X86_64_RBX: + return UNWIND_X86_64_REG_RBX << (slotIndex*3); + case UNW_X86_64_R12: + return UNWIND_X86_64_REG_R12 << (slotIndex*3); + case UNW_X86_64_R13: + return UNWIND_X86_64_REG_R13 << (slotIndex*3); + case UNW_X86_64_R14: + return UNWIND_X86_64_REG_R14 << (slotIndex*3); + case UNW_X86_64_R15: + return UNWIND_X86_64_REG_R15 << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86_64& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == DW_X86_64_RET_ADDR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_X86_64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.cfaOffsetWasNegative ) { + strcpy(warningBuffer, "cfa had negative offset (dwarf might contain epilog)"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_64_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_X86_64_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardRBPframe = ( + (prolog.cfaRegister == UNW_X86_64_RBP) + && (prolog.cfaRegisterOffset == 16) + && (prolog.savedRegisters[UNW_X86_64_RBP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) ); + bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP); + if ( !standardRBPframe && !standardRSPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use RBP or RSP based frame"); + return UNWIND_X86_64_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool rbxSaved = false; + bool r12Saved = false; + bool r13Saved = false; + bool r14Saved = false; + bool r15Saved = false; + bool rbpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_64_MODE_DWARF; + } + switch (i) { + case UNW_X86_64_RBX: + rbxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R12: + r12Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R13: + r13Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R14: + r14Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_R15: + r15Saved = true; + ++saveRegisterCount; + break; + case UNW_X86_64_RBP: + rbpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_64_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_64_MODE_DWARF; + } + } + } + const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value; + const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value; + const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value; + const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value; + const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value; + const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardRBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | rbp | + // +--------------+ <- rbp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+16 + // | saved reg2 | + // +--------------+ <- CFA - offset+8 + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- rsp + // + encoding = UNWIND_X86_64_MODE_RBP_FRAME; + + // find save location of farthest register from rbp + int furthestCfaOffset = 0; + if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetRBX; + if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR12; + if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR13; + if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR14; + if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetR15; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int rbpOffset = furthestCfaOffset + 16; + int encodedOffset = rbpOffset/(-8); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( rbxSaved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_RBX, cfaOffsetRBX - furthestCfaOffset, encodingFailure); + if ( r12Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R12, cfaOffsetR12 - furthestCfaOffset, encodingFailure); + if ( r13Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R13, cfaOffsetR13 - furthestCfaOffset, encodingFailure); + if ( r14Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R14, cfaOffsetR14 - furthestCfaOffset, encodingFailure); + if ( r15Saved ) + encoding |= getRBPEncodedRegister(UNW_X86_64_R15, cfaOffsetR15 - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_64_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 16 + // | saved reg2 | + // +--------------+ <- CFA - 24 + // | saved reg3 | + // +--------------+ <- CFA - 32 + // | saved reg4 | + // +--------------+ <- CFA - 40 + // | saved reg5 | + // +--------------+ <- CFA - 48 + // | saved reg6 | + // +--------------+ <- CFA - 56 + // | | + // <- esp + // + + // for RSP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_64_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 8; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_64_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + if ( prolog.codeOffsetAtStackDecrement == 0 ) { + strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); + return UNWIND_X86_64_MODE_DWARF; + } + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + try { + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8; + } + catch (...) { + strcpy(warningBuffer, "stack size is large but stack subq instruction not found"); + return UNWIND_X86_64_MODE_DWARF; + } + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_64_MODE_DWARF; + } + encoding = UNWIND_X86_64_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( r15Saved ) { + if ( cfaOffsetR15 < -56 ) { + strcpy(warningBuffer, "r15 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15; + } + if ( r14Saved ) { + if ( cfaOffsetR14 < -56 ) { + strcpy(warningBuffer, "r14 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14; + } + if ( r13Saved ) { + if ( cfaOffsetR13 < -56 ) { + strcpy(warningBuffer, "r13 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13; + } + if ( r12Saved ) { + if ( cfaOffsetR12 < -56 ) { + strcpy(warningBuffer, "r12 is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12; + } + if ( rbxSaved ) { + if ( cfaOffsetRBX < -56 ) { + strcpy(warningBuffer, "rbx is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX; + } + if ( rbpSaved ) { + if ( cfaOffsetRBP < -56 ) { + strcpy(warningBuffer, "rbp is saved too far from return address"); + return UNWIND_X86_64_MODE_DWARF; + } + registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_64_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + +// +// x86 specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_x86&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)DW_X86_RET_ADDR ); + return DW_X86_RET_ADDR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_x86&) +{ + return (regNum == DW_X86_RET_ADDR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_x86& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for x86 cfa"); +} + + + + + +template +uint32_t DwarfInstructions::getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure) +{ + if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) { + failure = true; + return 0; + } + unsigned int slotIndex = regOffsetFromBaseOffset/4; + + switch ( reg ) { + case UNW_X86_EBX: + return UNWIND_X86_REG_EBX << (slotIndex*3); + case UNW_X86_ECX: + return UNWIND_X86_REG_ECX << (slotIndex*3); + case UNW_X86_EDX: + return UNWIND_X86_REG_EDX << (slotIndex*3); + case UNW_X86_EDI: + return UNWIND_X86_REG_EDI << (slotIndex*3); + case UNW_X86_ESI: + return UNWIND_X86_REG_ESI << (slotIndex*3); + } + + // invalid register + failure = true; + return 0; +} + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_x86& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + + if ( prolog.registerSavedTwiceInCIE == DW_X86_RET_ADDR ) { + warningBuffer[0] = '\0'; // silently disable conversion to compact unwind by linker + return UNWIND_X86_64_MODE_DWARF; + } + // don't create compact unwind info for unsupported dwarf kinds + if ( prolog.registerSavedMoreThanOnce ) { + strcpy(warningBuffer, "register saved more than once (might be shrink wrap)"); + return UNWIND_X86_MODE_DWARF; + } + if ( prolog.spExtraArgSize != 0 ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size"); + return UNWIND_X86_MODE_DWARF; + } + if ( prolog.sameValueUsed ) { + strcpy(warningBuffer, "dwarf uses DW_CFA_same_value"); + return UNWIND_X86_MODE_DWARF; + } + + // figure out which kind of frame this function uses + bool standardEBPframe = ( + (prolog.cfaRegister == UNW_X86_EBP) + && (prolog.cfaRegisterOffset == 8) + && (prolog.savedRegisters[UNW_X86_EBP].location == CFI_Parser::kRegisterInCFA) + && (prolog.savedRegisters[UNW_X86_EBP].value == -8) ); + bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP); + if ( !standardEBPframe && !standardESPframe ) { + // no compact encoding for this + strcpy(warningBuffer, "does not use EBP or ESP based frame"); + return UNWIND_X86_MODE_DWARF; + } + + // scan which registers are saved + int saveRegisterCount = 0; + bool ebxSaved = false; + bool ecxSaved = false; + bool edxSaved = false; + bool esiSaved = false; + bool ediSaved = false; + bool ebpSaved = false; + for (int i=0; i < 64; ++i) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterUnused ) { + if ( prolog.savedRegisters[i].location != CFI_Parser::kRegisterInCFA ) { + sprintf(warningBuffer, "register %d saved somewhere other that in frame", i); + return UNWIND_X86_MODE_DWARF; + } + switch (i) { + case UNW_X86_EBX: + ebxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ECX: + ecxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDX: + edxSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_ESI: + esiSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EDI: + ediSaved = true; + ++saveRegisterCount; + break; + case UNW_X86_EBP: + ebpSaved = true; + ++saveRegisterCount; + break; + case DW_X86_RET_ADDR: + break; + default: + sprintf(warningBuffer, "non-standard register %d being saved in prolog", i); + return UNWIND_X86_MODE_DWARF; + } + } + } + const int32_t cfaOffsetEBX = prolog.savedRegisters[UNW_X86_EBX].value; + const int32_t cfaOffsetECX = prolog.savedRegisters[UNW_X86_ECX].value; + const int32_t cfaOffsetEDX = prolog.savedRegisters[UNW_X86_EDX].value; + const int32_t cfaOffsetEDI = prolog.savedRegisters[UNW_X86_EDI].value; + const int32_t cfaOffsetESI = prolog.savedRegisters[UNW_X86_ESI].value; + const int32_t cfaOffsetEBP = prolog.savedRegisters[UNW_X86_EBP].value; + + // encode standard RBP frames + compact_unwind_encoding_t encoding = 0; + if ( standardEBPframe ) { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | ebp | + // +--------------+ <- ebp + // ~ ~ + // +--------------+ + // | saved reg3 | + // +--------------+ <- CFA - offset+8 + // | saved reg2 | + // +--------------+ <- CFA - offset+e + // | saved reg1 | + // +--------------+ <- CFA - offset + // | | + // +--------------+ + // | | + // <- esp + // + encoding = UNWIND_X86_MODE_EBP_FRAME; + + // find save location of farthest register from ebp + int furthestCfaOffset = 0; + if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEBX; + if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetECX; + if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDX; + if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetEDI; + if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) ) + furthestCfaOffset = cfaOffsetESI; + + if ( furthestCfaOffset == 0 ) { + // no registers saved, nothing more to encode + return encoding; + } + + // add stack offset to encoding + int ebpOffset = furthestCfaOffset + 8; + int encodedOffset = ebpOffset/(-4); + if ( encodedOffset > 255 ) { + strcpy(warningBuffer, "offset of saved registers too far to encode"); + return UNWIND_X86_MODE_DWARF; + } + encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET)); + + // add register saved from each stack location + bool encodingFailure = false; + if ( ebxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure); + if ( ecxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure); + if ( edxSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure); + if ( ediSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure); + if ( esiSaved ) + encoding |= getEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure); + + if ( encodingFailure ){ + strcpy(warningBuffer, "saved registers not contiguous"); + return UNWIND_X86_MODE_DWARF; + } + + return encoding; + } + else { + // | | + // +--------------+ <- CFA + // | ret addr | + // +--------------+ + // | saved reg1 | + // +--------------+ <- CFA - 8 + // | saved reg2 | + // +--------------+ <- CFA - 12 + // | saved reg3 | + // +--------------+ <- CFA - 16 + // | saved reg4 | + // +--------------+ <- CFA - 20 + // | saved reg5 | + // +--------------+ <- CFA - 24 + // | saved reg6 | + // +--------------+ <- CFA - 28 + // | | + // <- esp + // + + // for ESP based frames we need to encode stack size in unwind info + encoding = UNWIND_X86_MODE_STACK_IMMD; + uint64_t stackValue = prolog.cfaRegisterOffset / 4; + uint32_t stackAdjust = 0; + bool immedStackSize = true; + const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_FRAMELESS_STACK_SIZE); + if ( stackValue > stackMaxImmedValue ) { + // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function + pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4; + uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns); + stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4; + stackValue = functionContentAdjustStackIns - funcAddr; + immedStackSize = false; + if ( stackAdjust > 7 ) { + strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size"); + return UNWIND_X86_MODE_DWARF; + } + encoding = UNWIND_X86_MODE_STACK_IND; + } + + + // validate that saved registers are all within 6 slots abutting return address + int registers[6]; + for (int i=0; i < 6;++i) + registers[i] = 0; + if ( ebxSaved ) { + if ( cfaOffsetEBX < -28 ) { + strcpy(warningBuffer, "ebx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX; + } + if ( ecxSaved ) { + if ( cfaOffsetECX < -28 ) { + strcpy(warningBuffer, "ecx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX; + } + if ( edxSaved ) { + if ( cfaOffsetEDX < -28 ) { + strcpy(warningBuffer, "edx is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX; + } + if ( ediSaved ) { + if ( cfaOffsetEDI < -28 ) { + strcpy(warningBuffer, "edi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI; + } + if ( esiSaved ) { + if ( cfaOffsetESI < -28 ) { + strcpy(warningBuffer, "esi is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI; + } + if ( ebpSaved ) { + if ( cfaOffsetEBP < -28 ) { + strcpy(warningBuffer, "ebp is saved too far from return address"); + return UNWIND_X86_MODE_DWARF; + } + registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP; + } + + // validate that saved registers are contiguous and abut return address on stack + for (int i=0; i < saveRegisterCount; ++i) { + if ( registers[5-i] == 0 ) { + strcpy(warningBuffer, "registers not save contiguously in stack"); + return UNWIND_X86_MODE_DWARF; + } + } + + // encode register permutation + // the 10-bits are encoded differently depending on the number of registers saved + int renumregs[6]; + for (int i=6-saveRegisterCount; i < 6; ++i) { + int countless = 0; + for (int j=6-saveRegisterCount; j < i; ++j) { + if ( registers[j] < registers[i] ) + ++countless; + } + renumregs[i] = registers[i] - countless -1; + } + uint32_t permutationEncoding = 0; + switch ( saveRegisterCount ) { + case 6: + permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]); + break; + case 5: + permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]); + break; + case 4: + permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]); + break; + case 3: + permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]); + break; + case 2: + permutationEncoding |= (5*renumregs[4] + renumregs[5]); + break; + case 1: + permutationEncoding |= (renumregs[5]); + break; + } + + encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE)); + encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST)); + encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT)); + encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION)); + return encoding; + } +} + + + + + + + +// +// ppc specific functions +// +template +int DwarfInstructions::lastRestoreReg(const Registers_ppc&) +{ + COMPILE_TIME_ASSERT( (int)CFI_Parser::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR ); + return UNW_PPC_SPEFSCR; +} + +template +bool DwarfInstructions::isReturnAddressRegister(int regNum, const Registers_ppc&) +{ + return (regNum == UNW_PPC_LR); +} + +template +typename A::pint_t DwarfInstructions::getCFA(A& addressSpace, const typename CFI_Parser::PrologInfo& prolog, + const Registers_ppc& registers) +{ + if ( prolog.cfaRegister != 0 ) + return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset; + else if ( prolog.cfaExpression != 0 ) + return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0); + else + ABORT("getCFA(): unknown location for ppc cfa"); +} + + +template +compact_unwind_encoding_t DwarfInstructions::encodeToUseDwarf(const Registers_ppc&) +{ + return UNWIND_X86_MODE_DWARF; +} + + +template +compact_unwind_encoding_t DwarfInstructions::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr, + const Registers_ppc& r, const typename CFI_Parser::PrologInfo& prolog, + char warningBuffer[1024]) +{ + warningBuffer[0] = '\0'; + return UNWIND_X86_MODE_DWARF; +} + + + + +} // namespace libunwind + + +#endif // __DWARF_INSTRUCTIONS_HPP__ + + + + diff --git a/src/ld/parsers/libunwind/DwarfParser.hpp b/src/ld/parsers/libunwind/DwarfParser.hpp new file mode 100644 index 0000000..3824d2e --- /dev/null +++ src/ld/parsers/libunwind/DwarfParser.hpp @@ -0,0 +1,819 @@ +/* -*- 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@ + */ + +// +// processor specific parsing of dwarf unwind instructions +// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ + +#include +#include +#include + +#include + +#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 +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& fdes, std::vector& 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 +const char* CFI_Parser::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 +bool CFI_Parser::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 +const char* CFI_Parser::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 +uint32_t CFI_Parser::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 +const char* CFI_Parser::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, + std::vector& fdes, std::vector& 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 +bool CFI_Parser::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 +bool CFI_Parser::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; + } + // 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: + // 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__ + + + + diff --git a/src/ld/parsers/libunwind/InternalMacros.h b/src/ld/parsers/libunwind/InternalMacros.h new file mode 100644 index 0000000..25c1631 --- /dev/null +++ src/ld/parsers/libunwind/InternalMacros.h @@ -0,0 +1,105 @@ +/* -*- 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@ + */ + + + +#ifndef INTERNAL_MACROS_H +#define INTERNAL_MACROS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern void __assert_rtn(const char *, const char *, int, const char *) __attribute__((noreturn)); +#ifdef __cplusplus +} +#endif + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + + +struct v128 { unsigned int vec[4]; }; + + +#define EXPORT __attribute__((visibility("default"))) + +#define COMPILE_TIME_ASSERT( expr ) \ + extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) ); + +#define ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg) + +#if NDEBUG + #define DEBUG_MESSAGE(msg, ...) + #define DEBUG_PRINT_API(msg, ...) + #define DEBUG_PRINT_UNWINDING_TEST 0 + #define DEBUG_PRINT_UNWINDING(msg, ...) + #define DEBUG_LOG_NON_ZERO(x) x; + #define INITIALIZE_DEBUG_PRINT_API + #define INITIALIZE_DEBUG_PRINT_UNWINDING +#else + #define DEBUG_MESSAGE(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__) + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + #ifdef __cplusplus + } + #endif + #define DEBUG_LOG_NON_ZERO(x) { int _err = x; if ( _err != 0 ) fprintf(stderr, "libuwind: " #x "=%d in %s", _err, __FUNCTION__); } + #define DEBUG_PRINT_API(msg, ...) do { if ( logAPIs() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING(msg, ...) do { if ( logUnwinding() ) fprintf(stderr, msg, __VA_ARGS__); } while(0) + #define DEBUG_PRINT_UNWINDING_TEST logUnwinding() + #define INITIALIZE_DEBUG_PRINT_API bool logAPIs() { static bool log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); return log; } + #define INITIALIZE_DEBUG_PRINT_UNWINDING bool logUnwinding() { static bool log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); return log; } +#endif + + +// note hack for +// Once libgcc_s.dylib vectors to libSystem, then we can remove the $ld$hide$os10.6$ lines +#if __ppc__ + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + + +#endif // INTERNAL_MACROS_H diff --git a/src/ld/parsers/libunwind/Registers.hpp b/src/ld/parsers/libunwind/Registers.hpp new file mode 100644 index 0000000..7d39fd7 --- /dev/null +++ src/ld/parsers/libunwind/Registers.hpp @@ -0,0 +1,1050 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2007-2009 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 __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include +#include +#include +#include +#include +#include +#include + +#include "libunwind.h" +#include "InternalMacros.h" + +namespace libunwind { + + +/// +/// Registers_x86 holds the register state of a thread in a 32-bit intel process. +/// +class Registers_x86 +{ +public: + Registers_x86(); + Registers_x86(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto(); + + uint32_t getSP() const { return fRegisters.__esp; } + void setSP(uint32_t value) { fRegisters.__esp = value; } + uint32_t getIP() const { return fRegisters.__eip; } + void setIP(uint32_t value) { fRegisters.__eip = value; } + uint32_t getEBP() const { return fRegisters.__ebp; } + void setEBP(uint32_t value) { fRegisters.__ebp = value; } + uint32_t getEBX() const { return fRegisters.__ebx; } + void setEBX(uint32_t value) { fRegisters.__ebx = value; } + uint32_t getECX() const { return fRegisters.__ecx; } + void setECX(uint32_t value) { fRegisters.__ecx = value; } + uint32_t getEDX() const { return fRegisters.__edx; } + void setEDX(uint32_t value) { fRegisters.__edx = value; } + uint32_t getESI() const { return fRegisters.__esi; } + void setESI(uint32_t value) { fRegisters.__esi = value; } + uint32_t getEDI() const { return fRegisters.__edi; } + void setEDI(uint32_t value) { fRegisters.__edi = value; } + +private: + i386_thread_state_t fRegisters; +}; + +inline Registers_x86::Registers_x86(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86) < sizeof(unw_context_t) ); + fRegisters = *((i386_thread_state_t*)registers); +} + +inline Registers_x86::Registers_x86() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 7 ) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__eip; + case UNW_REG_SP: + return fRegisters.__esp; + case UNW_X86_EAX: + return fRegisters.__eax; + case UNW_X86_ECX: + return fRegisters.__ecx; + case UNW_X86_EDX: + return fRegisters.__edx; + case UNW_X86_EBX: + return fRegisters.__ebx; + case UNW_X86_EBP: + return fRegisters.__ebp; + case UNW_X86_ESP: + return fRegisters.__esp; + case UNW_X86_ESI: + return fRegisters.__esi; + case UNW_X86_EDI: + return fRegisters.__edi; + } + ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__eip = value; + return; + case UNW_REG_SP: + fRegisters.__esp = value; + return; + case UNW_X86_EAX: + fRegisters.__eax = value; + return; + case UNW_X86_ECX: + fRegisters.__ecx = value; + return; + case UNW_X86_EDX: + fRegisters.__edx = value; + return; + case UNW_X86_EBX: + fRegisters.__ebx = value; + return; + case UNW_X86_EBP: + fRegisters.__ebp = value; + return; + case UNW_X86_ESP: + fRegisters.__esp = value; + return; + case UNW_X86_ESI: + fRegisters.__esi = value; + return; + case UNW_X86_EDI: + fRegisters.__edi = value; + return; + } + ABORT("unsupported x86 register"); +} + +inline const char* Registers_x86::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int num) const +{ + ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int num, double value) +{ + ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int num) const +{ + ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int num, v128 value) +{ + ABORT("no x86 vector registers"); +} + + + + +/// +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel process. +/// +class Registers_x86_64 +{ +public: + Registers_x86_64(); + Registers_x86_64(const void* registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const{ return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char* getRegisterName(int num); + void jumpto(); + uint64_t getSP() const { return fRegisters.__rsp; } + void setSP(uint64_t value) { fRegisters.__rsp = value; } + uint64_t getIP() const { return fRegisters.__rip; } + void setIP(uint64_t value) { fRegisters.__rip = value; } + uint64_t getRBP() const { return fRegisters.__rbp; } + void setRBP(uint64_t value) { fRegisters.__rbp = value; } + uint64_t getRBX() const { return fRegisters.__rbx; } + void setRBX(uint64_t value) { fRegisters.__rbx = value; } + uint64_t getR12() const { return fRegisters.__r12; } + void setR12(uint64_t value) { fRegisters.__r12 = value; } + uint64_t getR13() const { return fRegisters.__r13; } + void setR13(uint64_t value) { fRegisters.__r13 = value; } + uint64_t getR14() const { return fRegisters.__r14; } + void setR14(uint64_t value) { fRegisters.__r14 = value; } + uint64_t getR15() const { return fRegisters.__r15; } + void setR15(uint64_t value) { fRegisters.__r15 = value; } +private: + x86_thread_state64_t fRegisters; +}; + +inline Registers_x86_64::Registers_x86_64(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_x86_64) < sizeof(unw_context_t) ); + fRegisters = *((x86_thread_state64_t*)registers); +} + +inline Registers_x86_64::Registers_x86_64() +{ + bzero(&fRegisters, sizeof(fRegisters)); +} + + +inline bool Registers_x86_64::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum > 15 ) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__rip; + case UNW_REG_SP: + return fRegisters.__rsp; + case UNW_X86_64_RAX: + return fRegisters.__rax; + case UNW_X86_64_RDX: + return fRegisters.__rdx; + case UNW_X86_64_RCX: + return fRegisters.__rcx; + case UNW_X86_64_RBX: + return fRegisters.__rbx; + case UNW_X86_64_RSI: + return fRegisters.__rsi; + case UNW_X86_64_RDI: + return fRegisters.__rdi; + case UNW_X86_64_RBP: + return fRegisters.__rbp; + case UNW_X86_64_RSP: + return fRegisters.__rsp; + case UNW_X86_64_R8: + return fRegisters.__r8; + case UNW_X86_64_R9: + return fRegisters.__r9; + case UNW_X86_64_R10: + return fRegisters.__r10; + case UNW_X86_64_R11: + return fRegisters.__r11; + case UNW_X86_64_R12: + return fRegisters.__r12; + case UNW_X86_64_R13: + return fRegisters.__r13; + case UNW_X86_64_R14: + return fRegisters.__r14; + case UNW_X86_64_R15: + return fRegisters.__r15; + } + ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) +{ + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__rip = value; + return; + case UNW_REG_SP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_RAX: + fRegisters.__rax = value; + return; + case UNW_X86_64_RDX: + fRegisters.__rdx = value; + return; + case UNW_X86_64_RCX: + fRegisters.__rcx = value; + return; + case UNW_X86_64_RBX: + fRegisters.__rbx = value; + return; + case UNW_X86_64_RSI: + fRegisters.__rsi = value; + return; + case UNW_X86_64_RDI: + fRegisters.__rdi = value; + return; + case UNW_X86_64_RBP: + fRegisters.__rbp = value; + return; + case UNW_X86_64_RSP: + fRegisters.__rsp = value; + return; + case UNW_X86_64_R8: + fRegisters.__r8 = value; + return; + case UNW_X86_64_R9: + fRegisters.__r9 = value; + return; + case UNW_X86_64_R10: + fRegisters.__r10 = value; + return; + case UNW_X86_64_R11: + fRegisters.__r11 = value; + return; + case UNW_X86_64_R12: + fRegisters.__r12 = value; + return; + case UNW_X86_64_R13: + fRegisters.__r13 = value; + return; + case UNW_X86_64_R14: + fRegisters.__r14 = value; + return; + case UNW_X86_64_R15: + fRegisters.__r15 = value; + return; + } + ABORT("unsupported x86_64 register"); +} + +inline const char* Registers_x86_64::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + default: + return "unknown register"; + } +} + +double Registers_x86_64::getFloatRegister(int num) const +{ + ABORT("no x86_64 float registers"); +} + +void Registers_x86_64::setFloatRegister(int num, double value) +{ + ABORT("no x86_64 float registers"); +} + +inline v128 Registers_x86_64::getVectorRegister(int num) const +{ + ABORT("no x86_64 vector registers"); +} + +inline void Registers_x86_64::setVectorRegister(int num, v128 value) +{ + ABORT("no x86_64 vector registers"); +} + + +/// +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC process. +/// +class Registers_ppc +{ +public: + Registers_ppc(); + Registers_ppc(const void* registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + void jumpto(); + const char* getRegisterName(int num); + uint64_t getSP() const { return fRegisters.__r1; } + void setSP(uint64_t value) { fRegisters.__r1 = value; } + uint64_t getIP() const { return fRegisters.__srr0; } + void setIP(uint64_t value) { fRegisters.__srr0 = value; } +private: + struct ppc_thread_state_t + { + unsigned int __srr0; /* Instruction address register (PC) */ + unsigned int __srr1; /* Machine state register (supervisor) */ + unsigned int __r0; + unsigned int __r1; + unsigned int __r2; + unsigned int __r3; + unsigned int __r4; + unsigned int __r5; + unsigned int __r6; + unsigned int __r7; + unsigned int __r8; + unsigned int __r9; + unsigned int __r10; + unsigned int __r11; + unsigned int __r12; + unsigned int __r13; + unsigned int __r14; + unsigned int __r15; + unsigned int __r16; + unsigned int __r17; + unsigned int __r18; + unsigned int __r19; + unsigned int __r20; + unsigned int __r21; + unsigned int __r22; + unsigned int __r23; + unsigned int __r24; + unsigned int __r25; + unsigned int __r26; + unsigned int __r27; + unsigned int __r28; + unsigned int __r29; + unsigned int __r30; + unsigned int __r31; + unsigned int __cr; /* Condition register */ + unsigned int __xer; /* User's integer exception register */ + unsigned int __lr; /* Link register */ + unsigned int __ctr; /* Count register */ + unsigned int __mq; /* MQ register (601 only) */ + unsigned int __vrsave; /* Vector Save Register */ + }; + + struct ppc_float_state_t + { + double __fpregs[32]; + + unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */ + unsigned int __fpscr; /* floating point status register */ + }; + + ppc_thread_state_t fRegisters; + ppc_float_state_t fFloatRegisters; + v128 fVectorRegisters[32]; // offset 424 +}; + + + +inline Registers_ppc::Registers_ppc(const void* registers) +{ + COMPILE_TIME_ASSERT( sizeof(Registers_ppc) < sizeof(unw_context_t) ); + fRegisters = *((ppc_thread_state_t*)registers); + fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160)); + memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() +{ + bzero(&fRegisters, sizeof(fRegisters)); + bzero(&fFloatRegisters, sizeof(fFloatRegisters)); + bzero(&fVectorRegisters, sizeof(fVectorRegisters)); +} + + +inline bool Registers_ppc::validRegister(int regNum) const +{ + if ( regNum == UNW_REG_IP ) + return true; + if ( regNum == UNW_REG_SP ) + return true; + if ( regNum == UNW_PPC_VRSAVE ) + return true; + if ( regNum < 0 ) + return false; + if ( regNum <= UNW_PPC_R31 ) + return true; + if ( regNum == UNW_PPC_MQ ) + return true; + if ( regNum == UNW_PPC_LR ) + return true; + if ( regNum == UNW_PPC_CTR ) + return true; + if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) ) + return true; + return false; +} + + +inline uint32_t Registers_ppc::getRegister(int regNum) const +{ + switch ( regNum ) { + case UNW_REG_IP: + return fRegisters.__srr0; + case UNW_REG_SP: + return fRegisters.__r1; + case UNW_PPC_R0: + return fRegisters.__r0; + case UNW_PPC_R1: + return fRegisters.__r1; + case UNW_PPC_R2: + return fRegisters.__r2; + case UNW_PPC_R3: + return fRegisters.__r3; + case UNW_PPC_R4: + return fRegisters.__r4; + case UNW_PPC_R5: + return fRegisters.__r5; + case UNW_PPC_R6: + return fRegisters.__r6; + case UNW_PPC_R7: + return fRegisters.__r7; + case UNW_PPC_R8: + return fRegisters.__r8; + case UNW_PPC_R9: + return fRegisters.__r9; + case UNW_PPC_R10: + return fRegisters.__r10; + case UNW_PPC_R11: + return fRegisters.__r11; + case UNW_PPC_R12: + return fRegisters.__r12; + case UNW_PPC_R13: + return fRegisters.__r13; + case UNW_PPC_R14: + return fRegisters.__r14; + case UNW_PPC_R15: + return fRegisters.__r15; + case UNW_PPC_R16: + return fRegisters.__r16; + case UNW_PPC_R17: + return fRegisters.__r17; + case UNW_PPC_R18: + return fRegisters.__r18; + case UNW_PPC_R19: + return fRegisters.__r19; + case UNW_PPC_R20: + return fRegisters.__r20; + case UNW_PPC_R21: + return fRegisters.__r21; + case UNW_PPC_R22: + return fRegisters.__r22; + case UNW_PPC_R23: + return fRegisters.__r23; + case UNW_PPC_R24: + return fRegisters.__r24; + case UNW_PPC_R25: + return fRegisters.__r25; + case UNW_PPC_R26: + return fRegisters.__r26; + case UNW_PPC_R27: + return fRegisters.__r27; + case UNW_PPC_R28: + return fRegisters.__r28; + case UNW_PPC_R29: + return fRegisters.__r29; + case UNW_PPC_R30: + return fRegisters.__r30; + case UNW_PPC_R31: + return fRegisters.__r31; + case UNW_PPC_LR: + return fRegisters.__lr; + case UNW_PPC_CR0: + return (fRegisters.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (fRegisters.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (fRegisters.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (fRegisters.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (fRegisters.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (fRegisters.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (fRegisters.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (fRegisters.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return fRegisters.__vrsave; + } + ABORT("unsupported ppc register"); +} + + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) +{ + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch ( regNum ) { + case UNW_REG_IP: + fRegisters.__srr0 = value; + return; + case UNW_REG_SP: + fRegisters.__r1 = value; + return; + case UNW_PPC_R0: + fRegisters.__r0 = value; + return; + case UNW_PPC_R1: + fRegisters.__r1 = value; + return; + case UNW_PPC_R2: + fRegisters.__r2 = value; + return; + case UNW_PPC_R3: + fRegisters.__r3 = value; + return; + case UNW_PPC_R4: + fRegisters.__r4 = value; + return; + case UNW_PPC_R5: + fRegisters.__r5 = value; + return; + case UNW_PPC_R6: + fRegisters.__r6 = value; + return; + case UNW_PPC_R7: + fRegisters.__r7 = value; + return; + case UNW_PPC_R8: + fRegisters.__r8 = value; + return; + case UNW_PPC_R9: + fRegisters.__r9 = value; + return; + case UNW_PPC_R10: + fRegisters.__r10 = value; + return; + case UNW_PPC_R11: + fRegisters.__r11 = value; + return; + case UNW_PPC_R12: + fRegisters.__r12 = value; + return; + case UNW_PPC_R13: + fRegisters.__r13 = value; + return; + case UNW_PPC_R14: + fRegisters.__r14 = value; + return; + case UNW_PPC_R15: + fRegisters.__r15 = value; + return; + case UNW_PPC_R16: + fRegisters.__r16 = value; + return; + case UNW_PPC_R17: + fRegisters.__r17 = value; + return; + case UNW_PPC_R18: + fRegisters.__r18 = value; + return; + case UNW_PPC_R19: + fRegisters.__r19 = value; + return; + case UNW_PPC_R20: + fRegisters.__r20 = value; + return; + case UNW_PPC_R21: + fRegisters.__r21 = value; + return; + case UNW_PPC_R22: + fRegisters.__r22 = value; + return; + case UNW_PPC_R23: + fRegisters.__r23 = value; + return; + case UNW_PPC_R24: + fRegisters.__r24 = value; + return; + case UNW_PPC_R25: + fRegisters.__r25 = value; + return; + case UNW_PPC_R26: + fRegisters.__r26 = value; + return; + case UNW_PPC_R27: + fRegisters.__r27 = value; + return; + case UNW_PPC_R28: + fRegisters.__r28 = value; + return; + case UNW_PPC_R29: + fRegisters.__r29 = value; + return; + case UNW_PPC_R30: + fRegisters.__r30 = value; + return; + case UNW_PPC_R31: + fRegisters.__r31 = value; + return; + case UNW_PPC_MQ: + fRegisters.__mq = value; + return; + case UNW_PPC_LR: + fRegisters.__lr = value; + return; + case UNW_PPC_CTR: + fRegisters.__ctr = value; + return; + case UNW_PPC_CR0: + fRegisters.__cr &= 0x0FFFFFFF; + fRegisters.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + fRegisters.__cr &= 0xF0FFFFFF; + fRegisters.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + fRegisters.__cr &= 0xFF0FFFFF; + fRegisters.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + fRegisters.__cr &= 0xFFF0FFFF; + fRegisters.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + fRegisters.__cr &= 0xFFFF0FFF; + fRegisters.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + fRegisters.__cr &= 0xFFFFF0FF; + fRegisters.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + fRegisters.__cr &= 0xFFFFFF0F; + fRegisters.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + fRegisters.__cr &= 0xFFFFFFF0; + fRegisters.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + fRegisters.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + fRegisters.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const +{ + if ( regNum < UNW_PPC_F0 ) + return false; + if ( regNum > UNW_PPC_F31 ) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const +{ + assert(validFloatRegister(regNum)); + return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) +{ + //fprintf(stderr, "Registers_ppc::setFloatRegister(%d, %g))\n", regNum, value); + assert(validFloatRegister(regNum)); + fFloatRegisters.__fpregs[regNum-UNW_PPC_F0] = value; +} + + +inline bool Registers_ppc::validVectorRegister(int regNum) const +{ + if ( regNum < UNW_PPC_V0 ) + return false; + if ( regNum > UNW_PPC_V31 ) + return false; + return true; +} + +v128 Registers_ppc::getVectorRegister(int regNum) const +{ + assert(validVectorRegister(regNum)); + v128 result = fVectorRegisters[regNum-UNW_PPC_V0]; + //fprintf(stderr, "Registers_ppc::getVectorRegister(this=%p, %d) => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, result.vec[0], result.vec[1], result.vec[2], result.vec[3]); + return result; +} + +void Registers_ppc::setVectorRegister(int regNum, v128 value) +{ + assert(validVectorRegister(regNum)); + //fprintf(stderr, "Registers_ppc::setVectorRegister(this=%p, %d) <0x%08X, 0x%08X, 0x%08X, 0x%08X> => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n", + // this, regNum, fVectorRegisters[regNum-UNW_PPC_V0].vec[0], fVectorRegisters[regNum-UNW_PPC_V0].vec[1], fVectorRegisters[regNum-UNW_PPC_V0].vec[2], + // fVectorRegisters[regNum-UNW_PPC_V0].vec[3], value.vec[0], value.vec[1], value.vec[2], value.vec[3]); + fVectorRegisters[regNum-UNW_PPC_V0] = value; +} + + +inline const char* Registers_ppc::getRegisterName(int regNum) +{ + switch ( regNum ) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + + +} + + +} // namespace libunwind + + + +#endif // __REGISTERS_HPP__ + + + + -- 2.2.1