diff --git a/contrib/llvm-project/libunwind/src/DwarfInstructions.hpp b/contrib/llvm-project/libunwind/src/DwarfInstructions.hpp index c39cabe1f783..ee98f538d437 100644 --- a/contrib/llvm-project/libunwind/src/DwarfInstructions.hpp +++ b/contrib/llvm-project/libunwind/src/DwarfInstructions.hpp @@ -1,828 +1,821 @@ //===-------------------------- DwarfInstructions.hpp ---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // // Processor specific interpretation of DWARF unwind info. // //===----------------------------------------------------------------------===// #ifndef __DWARF_INSTRUCTIONS_HPP__ #define __DWARF_INSTRUCTIONS_HPP__ #include #include #include #include "dwarf2.h" #include "Registers.hpp" #include "DwarfParser.hpp" #include "config.h" namespace libunwind { /// 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 int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, R ®isters, bool &isSignalFrame); private: enum { DW_X86_64_RET_ADDR = 16 }; enum { DW_X86_RET_ADDR = 8 }; typedef typename CFI_Parser::RegisterLocation RegisterLocation; typedef typename CFI_Parser::PrologInfo PrologInfo; typedef typename CFI_Parser::FDE_Info FDE_Info; typedef typename CFI_Parser::CIE_Info CIE_Info; static pint_t evaluateExpression(pint_t expression, A &addressSpace, const R ®isters, pint_t initialStackValue); static pint_t getSavedRegister(A &addressSpace, const R ®isters, pint_t cfa, const RegisterLocation &savedReg); static double getSavedFloatRegister(A &addressSpace, const R ®isters, pint_t cfa, const RegisterLocation &savedReg); static v128 getSavedVectorRegister(A &addressSpace, const R ®isters, pint_t cfa, const RegisterLocation &savedReg); static pint_t getCFA(A &addressSpace, const PrologInfo &prolog, const R ®isters) { if (prolog.cfaRegister != 0) return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + prolog.cfaRegisterOffset); if (prolog.cfaExpression != 0) return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, registers, 0); assert(0 && "getCFA(): unknown location"); __builtin_unreachable(); } }; template typename A::pint_t DwarfInstructions::getSavedRegister( A &addressSpace, const R ®isters, pint_t cfa, const RegisterLocation &savedReg) { switch (savedReg.location) { case CFI_Parser::kRegisterInCFA: return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value); case CFI_Parser::kRegisterAtExpression: return (pint_t)addressSpace.getRegister(evaluateExpression( (pint_t)savedReg.value, addressSpace, registers, cfa)); case CFI_Parser::kRegisterIsExpression: return evaluateExpression((pint_t)savedReg.value, addressSpace, registers, cfa); case CFI_Parser::kRegisterInRegister: return registers.getRegister((int)savedReg.value); - case CFI_Parser::kRegisterUndefined: - return 0; + case CFI_Parser::kRegisterUnused: case CFI_Parser::kRegisterOffsetFromCFA: // FIX ME break; } _LIBUNWIND_ABORT("unsupported restore location for register"); } template double DwarfInstructions::getSavedFloatRegister( A &addressSpace, const R ®isters, pint_t cfa, const RegisterLocation &savedReg) { switch (savedReg.location) { case CFI_Parser::kRegisterInCFA: return addressSpace.getDouble(cfa + (pint_t)savedReg.value); case CFI_Parser::kRegisterAtExpression: return addressSpace.getDouble( evaluateExpression((pint_t)savedReg.value, addressSpace, registers, cfa)); case CFI_Parser::kRegisterIsExpression: case CFI_Parser::kRegisterUnused: - case CFI_Parser::kRegisterUndefined: case CFI_Parser::kRegisterOffsetFromCFA: case CFI_Parser::kRegisterInRegister: // FIX ME break; } _LIBUNWIND_ABORT("unsupported restore location for float register"); } template v128 DwarfInstructions::getSavedVectorRegister( A &addressSpace, const R ®isters, pint_t cfa, const RegisterLocation &savedReg) { switch (savedReg.location) { case CFI_Parser::kRegisterInCFA: return addressSpace.getVector(cfa + (pint_t)savedReg.value); case CFI_Parser::kRegisterAtExpression: return addressSpace.getVector( evaluateExpression((pint_t)savedReg.value, addressSpace, registers, cfa)); case CFI_Parser::kRegisterIsExpression: case CFI_Parser::kRegisterUnused: - case CFI_Parser::kRegisterUndefined: case CFI_Parser::kRegisterOffsetFromCFA: case CFI_Parser::kRegisterInRegister: // FIX ME break; } _LIBUNWIND_ABORT("unsupported restore location for vector register"); } template int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, R ®isters, bool &isSignalFrame) { FDE_Info fdeInfo; CIE_Info cieInfo; if (CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL) { PrologInfo prolog; if (CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, R::getArch(), &prolog)) { // get pointer to cfa (architecture specific) pint_t cfa = getCFA(addressSpace, prolog, registers); // restore registers that DWARF says were saved R newRegisters = registers; pint_t returnAddress = 0; const int lastReg = R::lastDwarfRegNum(); assert(static_cast(CFI_Parser::kMaxRegisterNumber) >= lastReg && "register range too large"); assert(lastReg >= (int)cieInfo.returnAddressRegister && "register range does not contain return address register"); for (int i = 0; i <= lastReg; ++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 (i == (int)cieInfo.returnAddressRegister) 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; - } else if (i == (int)cieInfo.returnAddressRegister) { - // Leaf function keeps the return address in register and there is no - // explicit intructions how to restore it. - returnAddress = registers.getRegister(cieInfo.returnAddressRegister); } } // By definition, the CFA is the stack pointer at the call site, so // restoring SP means setting it to CFA. newRegisters.setSP(cfa); isSignalFrame = cieInfo.isSignalFrame; #if defined(_LIBUNWIND_TARGET_AARCH64) // If the target is aarch64 then the return address may have been signed // using the v8.3 pointer authentication extensions. The original // return address needs to be authenticated before the return address is // restored. autia1716 is used instead of autia as autia1716 assembles // to a NOP on pre-v8.3a architectures. if ((R::getArch() == REGISTERS_ARM64) && prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) { #if !defined(_LIBUNWIND_IS_NATIVE_ONLY) return UNW_ECROSSRASIGNING; #else register unsigned long long x17 __asm("x17") = returnAddress; register unsigned long long x16 __asm("x16") = cfa; // These are the autia1716/autib1716 instructions. The hint instructions // are used here as gcc does not assemble autia1716/autib1716 for pre // armv8.3a targets. if (cieInfo.addressesSignedWithBKey) asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 else asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 returnAddress = x17; #endif } #endif #if defined(_LIBUNWIND_TARGET_SPARC) if (R::getArch() == REGISTERS_SPARC) { // Skip call site instruction and delay slot returnAddress += 8; // Skip unimp instruction if function returns a struct if ((addressSpace.get32(returnAddress) & 0xC1C00000) == 0) returnAddress += 4; } #endif #if defined(_LIBUNWIND_TARGET_PPC64) #define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1) #define PPC64_ELFV1_R2_OFFSET 40 #define PPC64_ELFV2_R2_LOAD_INST_ENCODING 0xe8410018u // ld r2,24(r1) #define PPC64_ELFV2_R2_OFFSET 24 // If the instruction at return address is a TOC (r2) restore, // then r2 was saved and needs to be restored. // ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24, // while in ELFv1 ABI it is saved at SP + 40. if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) { pint_t sp = newRegisters.getRegister(UNW_REG_SP); pint_t r2 = 0; switch (addressSpace.get32(returnAddress)) { case PPC64_ELFV1_R2_LOAD_INST_ENCODING: r2 = addressSpace.get64(sp + PPC64_ELFV1_R2_OFFSET); break; case PPC64_ELFV2_R2_LOAD_INST_ENCODING: r2 = addressSpace.get64(sp + PPC64_ELFV2_R2_OFFSET); break; } if (r2) newRegisters.setRegister(UNW_PPC64_R2, r2); } #endif // Return address is address after call site instruction, so setting IP to // that does simualates a return. newRegisters.setIP(returnAddress); // Simulate the 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 ®isters, pint_t initialStackValue) { const bool log = false; pint_t p = expression; pint_t expressionEnd = expression + 20; // temp, until len read pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd); expressionEnd = p + length; if (log) fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n", (uint64_t)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%" PRIx64 "\n", (uint64_t)(*t)); } } uint8_t opcode = addressSpace.get8(p++); sint_t svalue, svalue2; 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%" PRIx64 "\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%" PRIx64 "\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%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_const1s: // push immediate 1 byte signed value svalue = (int8_t) addressSpace.get8(p); p += 1; *(++sp) = (pint_t)svalue; if (log) fprintf(stderr, "push 0x%" PRIx64 "\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%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_const2s: // push immediate 2 byte signed value svalue = (int16_t) addressSpace.get16(p); p += 2; *(++sp) = (pint_t)svalue; if (log) fprintf(stderr, "push 0x%" PRIx64 "\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%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_const4s: // push immediate 4 byte signed value svalue = (int32_t)addressSpace.get32(p); p += 4; *(++sp) = (pint_t)svalue; if (log) fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); break; case DW_OP_const8u: // push immediate 8 byte value value = (pint_t)addressSpace.get64(p); p += 8; *(++sp) = value; if (log) fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_const8s: // push immediate 8 byte signed value value = (pint_t)addressSpace.get64(p); p += 8; *(++sp) = value; if (log) fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_constu: // push immediate ULEB128 value value = (pint_t)addressSpace.getULEB128(p, expressionEnd); *(++sp) = value; if (log) fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_consts: // push immediate SLEB128 value svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); *(++sp) = (pint_t)svalue; if (log) fprintf(stderr, "push 0x%" PRIx64 "\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[-(int)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 = *((pint_t*)value); if (log) fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value); break; case DW_OP_abs: svalue = (sint_t)*sp; if (svalue < 0) *sp = (pint_t)(-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 = (sint_t)(*sp--); svalue2 = (sint_t)*sp; *sp = (pint_t)(svalue2 / svalue); if (log) fprintf(stderr, "div\n"); break; case DW_OP_minus: value = *sp--; *sp = *sp - value; if (log) fprintf(stderr, "minus\n"); break; case DW_OP_mod: svalue = (sint_t)(*sp--); svalue2 = (sint_t)*sp; *sp = (pint_t)(svalue2 % svalue); if (log) fprintf(stderr, "module\n"); break; case DW_OP_mul: svalue = (sint_t)(*sp--); svalue2 = (sint_t)*sp; *sp = (pint_t)(svalue2 * 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 = (sint_t)(*sp); *sp = (pint_t)(~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 += static_cast(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 = (sint_t)*sp; *sp = (pint_t)(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 = (pint_t)((sint_t)p + svalue); if (log) fprintf(stderr, "skip %" PRIu64 "\n", (uint64_t)svalue); break; case DW_OP_bra: svalue = (int16_t) addressSpace.get16(p); p += 2; if (*sp--) p = (pint_t)((sint_t)p + svalue); if (log) fprintf(stderr, "bra %" PRIu64 "\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 = static_cast(opcode - DW_OP_lit0); *(++sp) = value; if (log) fprintf(stderr, "push literal 0x%" PRIx64 "\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 = static_cast(opcode - DW_OP_reg0); *(++sp) = registers.getRegister((int)reg); if (log) fprintf(stderr, "push reg %d\n", reg); break; case DW_OP_regx: reg = static_cast(addressSpace.getULEB128(p, expressionEnd)); *(++sp) = registers.getRegister((int)reg); if (log) fprintf(stderr, "push reg %d + 0x%" PRIx64 "\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 = static_cast(opcode - DW_OP_breg0); svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); svalue += static_cast(registers.getRegister((int)reg)); *(++sp) = (pint_t)(svalue); if (log) fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); break; case DW_OP_bregx: reg = static_cast(addressSpace.getULEB128(p, expressionEnd)); svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); svalue += static_cast(registers.getRegister((int)reg)); *(++sp) = (pint_t)(svalue); if (log) fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); break; case DW_OP_fbreg: _LIBUNWIND_ABORT("DW_OP_fbreg not implemented"); break; case DW_OP_piece: _LIBUNWIND_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 = (pint_t)addressSpace.get64(value); break; default: _LIBUNWIND_ABORT("DW_OP_deref_size with bad size"); } *(++sp) = value; if (log) fprintf(stderr, "sized dereference 0x%" PRIx64 "\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: _LIBUNWIND_ABORT("DWARF opcode not implemented"); } } if (log) fprintf(stderr, "expression evaluates to 0x%" PRIx64 "\n", (uint64_t)*sp); return *sp; } } // namespace libunwind #endif // __DWARF_INSTRUCTIONS_HPP__ diff --git a/contrib/llvm-project/libunwind/src/DwarfParser.hpp b/contrib/llvm-project/libunwind/src/DwarfParser.hpp index de0eb6de9d70..b41cc7bcfda2 100644 --- a/contrib/llvm-project/libunwind/src/DwarfParser.hpp +++ b/contrib/llvm-project/libunwind/src/DwarfParser.hpp @@ -1,813 +1,812 @@ //===--------------------------- DwarfParser.hpp --------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // // Parses DWARF CFIs (FDEs and CIEs). // //===----------------------------------------------------------------------===// #ifndef __DWARF_PARSER_HPP__ #define __DWARF_PARSER_HPP__ #include #include #include #include #include "libunwind.h" #include "dwarf2.h" #include "Registers.hpp" #include "config.h" namespace libunwind { /// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. /// See DWARF Spec for details: /// http://refspecs.linuxbase.org/LSB_3.1.0/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; uint32_t codeAlignFactor; int dataAlignFactor; bool isSignalFrame; bool fdesHaveAugmentationData; uint8_t returnAddressRegister; #if defined(_LIBUNWIND_TARGET_AARCH64) bool addressesSignedWithBKey; #endif }; /// 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; }; enum { kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER }; enum RegisterSavedWhere { kRegisterUnused, - kRegisterUndefined, kRegisterInCFA, kRegisterOffsetFromCFA, kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression }; struct RegisterLocation { RegisterSavedWhere location; bool initialStateSaved; int64_t value; }; /// Information about a frame layout and registers saved determined /// by "running" the DWARF FDE "instructions" struct PrologInfo { uint32_t cfaRegister; int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset int64_t cfaExpression; // CFA = expression uint32_t spExtraArgSize; RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; enum class InitializeTime { kLazy, kNormal }; // When saving registers, this data structure is lazily initialized. PrologInfo(InitializeTime IT = InitializeTime::kNormal) { if (IT == InitializeTime::kNormal) memset(this, 0, sizeof(*this)); } void checkSaveRegister(uint64_t reg, PrologInfo &initialState) { if (!savedRegisters[reg].initialStateSaved) { initialState.savedRegisters[reg] = savedRegisters[reg]; savedRegisters[reg].initialStateSaved = true; } } void setRegister(uint64_t reg, RegisterSavedWhere newLocation, int64_t newValue, PrologInfo &initialState) { checkSaveRegister(reg, initialState); savedRegisters[reg].location = newLocation; savedRegisters[reg].value = newValue; } void setRegisterLocation(uint64_t reg, RegisterSavedWhere newLocation, PrologInfo &initialState) { checkSaveRegister(reg, initialState); savedRegisters[reg].location = newLocation; } void setRegisterValue(uint64_t reg, int64_t newValue, PrologInfo &initialState) { checkSaveRegister(reg, initialState); savedRegisters[reg].value = newValue; } void restoreRegisterToInitialState(uint64_t reg, PrologInfo &initialState) { if (savedRegisters[reg].initialStateSaved) savedRegisters[reg] = initialState.savedRegisters[reg]; // else the register still holds its initial state } }; struct PrologInfoStackEntry { PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) : next(n), info(i) {} PrologInfoStackEntry *next; PrologInfo info; }; struct RememberStack { PrologInfoStackEntry *entry; RememberStack() : entry(nullptr) {} ~RememberStack() { #if defined(_LIBUNWIND_REMEMBER_CLEANUP_NEEDED) // Clean up rememberStack. Even in the case where every // DW_CFA_remember_state is paired with a DW_CFA_restore_state, // parseInstructions can skip restore opcodes if it reaches the target PC // and stops interpreting, so we have to make sure we don't leak memory. while (entry) { PrologInfoStackEntry *next = entry->next; _LIBUNWIND_REMEMBER_FREE(entry); entry = next; } #endif } }; static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, uintptr_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, int arch, PrologInfo *results); static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); }; /// 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; pint_t cfiLength = (pint_t)addressSpace.get32(p); p += 4; if (cfiLength == 0xffffffff) { // 0xffffffff means length is really next 8 bytes cfiLength = (pint_t)addressSpace.get64(p); p += 8; } if (cfiLength == 0) return "FDE has zero length"; // zero terminator 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) { pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); pint_t endOfAug = p + augLen; if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { // 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, uintptr_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 = (sectionLength == UINTPTR_MAX) ? static_cast(-1) : (ehSectionStart + sectionLength); while (p < ehSectionEnd) { pint_t currentCFI = p; //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); pint_t cfiLength = addressSpace.get32(p); p += 4; if (cfiLength == 0xffffffff) { // 0xffffffff means length is really next 8 bytes cfiLength = (pint_t)addressSpace.get64(p); p += 8; } if (cfiLength == 0) return false; // zero terminator 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); // 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) { pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); pint_t endOfAug = p + augLen; if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { // 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; return true; } else { // pc is not in begin/range, skip this FDE } } else { // Malformed CIE, now augmentation describing pc range encoding. } } else { // malformed FDE. CIE is bad } p = nextCFI; } } return false; } /// Extract info from a CIE template const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo) { cieInfo->pointerEncoding = 0; cieInfo->lsdaEncoding = DW_EH_PE_omit; cieInfo->personalityEncoding = 0; cieInfo->personalityOffsetInCIE = 0; cieInfo->personality = 0; cieInfo->codeAlignFactor = 0; cieInfo->dataAlignFactor = 0; cieInfo->isSignalFrame = false; cieInfo->fdesHaveAugmentationData = false; #if defined(_LIBUNWIND_TARGET_AARCH64) cieInfo->addressesSignedWithBKey = false; #endif cieInfo->cieStart = cie; pint_t p = cie; pint_t cieLength = (pint_t)addressSpace.get32(p); p += 4; pint_t cieContentEnd = p + cieLength; if (cieLength == 0xffffffff) { // 0xffffffff means length is really next 8 bytes cieLength = (pint_t)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 = (uint32_t)addressSpace.getULEB128(p, cieContentEnd); // parse data alignment factor cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd); // parse return address register uint64_t raReg = (version == 1) ? addressSpace.get8(p++) : addressSpace.getULEB128(p, cieContentEnd); assert(raReg < 255 && "return address register too large"); cieInfo->returnAddressRegister = (uint8_t)raReg; // 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 = (uint8_t)(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; #if defined(_LIBUNWIND_TARGET_AARCH64) case 'B': cieInfo->addressesSignedWithBKey = true; break; #endif default: // ignore unknown letters break; } } } cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; cieInfo->cieInstructions = p; return result; } /// "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, int arch, PrologInfo *results) { // Alloca is used for the allocation of the rememberStack entries. It removes // the dependency on new/malloc but the below for loop can not be refactored // into functions. Entry could be saved during the processing of a CIE and // restored by an FDE. RememberStack rememberStack; struct ParseInfo { pint_t instructions; pint_t instructionsEnd; pint_t pcoffset; }; ParseInfo parseInfoArray[] = { {cieInfo.cieInstructions, cieInfo.cieStart + cieInfo.cieLength, (pint_t)(-1)}, {fdeInfo.fdeInstructions, fdeInfo.fdeStart + fdeInfo.fdeLength, upToPC - fdeInfo.pcStart}}; for (const auto &info : parseInfoArray) { pint_t p = info.instructions; pint_t instructionsEnd = info.instructionsEnd; pint_t pcoffset = info.pcoffset; pint_t codeOffset = 0; // initialState initialized as registers in results are modified. Use // PrologInfo accessor functions to avoid reading uninitialized data. PrologInfo initialState(PrologInfo::InitializeTime::kLazy); _LIBUNWIND_TRACE_DWARF("parseFDEInstructions(instructions=0x%0" PRIx64 ")\n", static_cast(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; ++p; switch (opcode) { case DW_CFA_nop: _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n"); break; case DW_CFA_set_loc: codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding); _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n"); break; case DW_CFA_advance_loc1: codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); p += 1; _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n", static_cast(codeOffset)); break; case DW_CFA_advance_loc2: codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); p += 2; _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n", static_cast(codeOffset)); break; case DW_CFA_advance_loc4: codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); p += 4; _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n", static_cast(codeOffset)); break; case DW_CFA_offset_extended: reg = addressSpace.getULEB128(p, instructionsEnd); offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_offset_extended DWARF unwind, reg too big"); return false; } results->setRegister(reg, kRegisterInCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", " "offset=%" PRId64 ")\n", reg, offset); break; case DW_CFA_restore_extended: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_restore_extended DWARF unwind, reg too big"); return false; } results->restoreRegisterToInitialState(reg, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", reg); break; case DW_CFA_undefined: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_undefined DWARF unwind, reg too big"); return false; } - results->setRegisterLocation(reg, kRegisterUndefined, initialState); + results->setRegisterLocation(reg, kRegisterUnused, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg); break; case DW_CFA_same_value: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_same_value DWARF unwind, reg too big"); 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->setRegisterLocation(reg, kRegisterUnused, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg); break; case DW_CFA_register: reg = addressSpace.getULEB128(p, instructionsEnd); reg2 = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_register DWARF unwind, reg too big"); return false; } if (reg2 > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_register DWARF unwind, reg2 too big"); return false; } results->setRegister(reg, kRegisterInRegister, (int64_t)reg2, initialState); _LIBUNWIND_TRACE_DWARF( "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2); break; case DW_CFA_remember_state: { // Avoid operator new because that would be an upward dependency. // Avoid malloc because it needs heap allocation. PrologInfoStackEntry *entry = (PrologInfoStackEntry *)_LIBUNWIND_REMEMBER_ALLOC( sizeof(PrologInfoStackEntry)); if (entry != NULL) { entry->next = rememberStack.entry; entry->info = *results; rememberStack.entry = entry; } else { return false; } _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n"); break; } case DW_CFA_restore_state: if (rememberStack.entry != NULL) { PrologInfoStackEntry *top = rememberStack.entry; *results = top->info; rememberStack.entry = top->next; _LIBUNWIND_REMEMBER_FREE(top); } else { return false; } _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n"); break; case DW_CFA_def_cfa: reg = addressSpace.getULEB128(p, instructionsEnd); offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big"); return false; } results->cfaRegister = (uint32_t)reg; results->cfaRegisterOffset = (int32_t)offset; _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 ")\n", reg, offset); break; case DW_CFA_def_cfa_register: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big"); return false; } results->cfaRegister = (uint32_t)reg; _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg); break; case DW_CFA_def_cfa_offset: results->cfaRegisterOffset = (int32_t)addressSpace.getULEB128(p, instructionsEnd); _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset); break; case DW_CFA_def_cfa_expression: results->cfaRegister = 0; results->cfaExpression = (int64_t)p; length = addressSpace.getULEB128(p, instructionsEnd); assert(length < static_cast(~0) && "pointer overflow"); p += static_cast(length); _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64 ", length=%" PRIu64 ")\n", results->cfaExpression, length); break; case DW_CFA_expression: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_expression DWARF unwind, reg too big"); return false; } results->setRegister(reg, kRegisterAtExpression, (int64_t)p, initialState); length = addressSpace.getULEB128(p, instructionsEnd); assert(length < static_cast(~0) && "pointer overflow"); p += static_cast(length); _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", " "expression=0x%" PRIx64 ", " "length=%" PRIu64 ")\n", reg, results->savedRegisters[reg].value, length); break; case DW_CFA_offset_extended_sf: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big"); return false; } offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->setRegister(reg, kRegisterInCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", " "offset=%" PRId64 ")\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) { _LIBUNWIND_LOG0( "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big"); return false; } results->cfaRegister = (uint32_t)reg; results->cfaRegisterOffset = (int32_t)offset; _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", " "offset=%" PRId64 ")\n", reg, offset); break; case DW_CFA_def_cfa_offset_sf: results->cfaRegisterOffset = (int32_t)(addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor); _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset); break; case DW_CFA_val_offset: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG( "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64 ") out of range\n", reg); return false; } offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", " "offset=%" PRId64 "\n", reg, offset); break; case DW_CFA_val_offset_sf: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big"); return false; } offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", " "offset=%" PRId64 "\n", reg, offset); break; case DW_CFA_val_expression: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0( "malformed DW_CFA_val_expression DWARF unwind, reg too big"); return false; } results->setRegister(reg, kRegisterIsExpression, (int64_t)p, initialState); length = addressSpace.getULEB128(p, instructionsEnd); assert(length < static_cast(~0) && "pointer overflow"); p += static_cast(length); _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", " "expression=0x%" PRIx64 ", length=%" PRIu64 ")\n", reg, results->savedRegisters[reg].value, length); break; case DW_CFA_GNU_args_size: length = addressSpace.getULEB128(p, instructionsEnd); results->spExtraArgSize = (uint32_t)length; _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length); break; case DW_CFA_GNU_negative_offset_extended: reg = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF " "unwind, reg too big"); return false; } offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->setRegister(reg, kRegisterInCFA, -offset, initialState); _LIBUNWIND_TRACE_DWARF( "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); break; #if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) // The same constant is used to represent different instructions on // AArch64 (negate_ra_state) and SPARC (window_save). static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save, "uses the same constant"); case DW_CFA_AARCH64_negate_ra_state: switch (arch) { #if defined(_LIBUNWIND_TARGET_AARCH64) case REGISTERS_ARM64: { int64_t value = results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^ 0x1; results->setRegisterValue(UNW_ARM64_RA_SIGN_STATE, value, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); } break; #endif #if defined(_LIBUNWIND_TARGET_SPARC) // case DW_CFA_GNU_window_save: case REGISTERS_SPARC: _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n"); for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) { results->setRegister(reg, kRegisterInRegister, ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0, initialState); } for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) { results->setRegister(reg, kRegisterInCFA, ((int64_t)reg - UNW_SPARC_L0) * 4, initialState); } break; #endif } break; #else (void)arch; #endif default: operand = opcode & 0x3F; switch (opcode & 0xC0) { case DW_CFA_offset: reg = operand; if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64 ") out of range", reg); return false; } offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->setRegister(reg, kRegisterInCFA, offset, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n", operand, offset); break; case DW_CFA_advance_loc: codeOffset += operand * cieInfo.codeAlignFactor; _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n", static_cast(codeOffset)); break; case DW_CFA_restore: reg = operand; if (reg > kMaxRegisterNumber) { _LIBUNWIND_LOG( "malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64 ") out of range", reg); return false; } results->restoreRegisterToInitialState(reg, initialState); _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n", static_cast(operand)); break; default: _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode); return false; } } } } return true; } } // namespace libunwind #endif // __DWARF_PARSER_HPP__