//===--------------------------- DwarfParser.hpp --------------------------===// // // The LLVM Compiler Infrastructure // // This file is dual licensed under the MIT and the University of Illinois Open // Source Licenses. See LICENSE.TXT for details. // // // Parses DWARF CFIs (FDEs and CIEs). // //===----------------------------------------------------------------------===// #ifndef __DWARF_PARSER_HPP__ #define __DWARF_PARSER_HPP__ #include #include #include "dwarf2.h" #include "AddressSpace.hpp" namespace _Unwind { /// 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; pint_t personality; uint32_t codeAlignFactor; int dataAlignFactor; uint8_t pointerEncoding; uint8_t lsdaEncoding; uint8_t personalityEncoding; uint8_t personalityOffsetInCIE; bool isSignalFrame; bool fdesHaveAugmentationData; uint8_t returnAddressRegister; }; /// 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; }; /// Information about a frame layout and registers saved determined /// by "running" the DWARF FDE "instructions" enum { kMaxRegisterNumber = R::LAST_REGISTER + 1 }; 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; RegisterLocation savedRegisters[kMaxRegisterNumber]; }; struct PrologInfoStackEntry { PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) : next(n), info(i) {} PrologInfoStackEntry *next; PrologInfo info; }; static void findPCRange(A &, pint_t, pint_t &, pint_t &); static bool decodeFDE(A &, pint_t, FDE_Info *, CIE_Info *, unw_proc_info_t *ctx); static bool parseFDEInstructions(A &, const FDE_Info &, const CIE_Info &, pint_t, PrologInfo *, unw_proc_info_t *ctx); static bool parseCIE(A &, pint_t, CIE_Info *); private: static bool parseInstructions(A &, pint_t, pint_t, const CIE_Info &, pint_t, PrologInfoStackEntry *&, PrologInfo *, unw_proc_info_t *ctx); }; /// /// Parse a FDE and return the last PC it covers. /// template void CFI_Parser::findPCRange(A &addressSpace, pint_t fde, pint_t &pcStart, pint_t &pcEnd) { pcStart = 0; pcEnd = 0; pint_t p = fde; uint64_t cfiLength = addressSpace.get32(p); p += 4; if (cfiLength == 0xffffffff) { // 0xffffffff means length is really the next 8 Bytes. cfiLength = addressSpace.get64(p); p += 8; } if (cfiLength == 0) return; uint32_t ciePointer = addressSpace.get32(p); if (ciePointer == 0) return; pint_t nextCFI = p + cfiLength; pint_t cieStart = p - ciePointer; typename CFI_Parser::CIE_Info cieInfo; if (!parseCIE(addressSpace, cieStart, &cieInfo)) return; p += 4; // Parse pc begin and range. pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding, NULL); pcEnd = pcStart + addressSpace.getEncodedP( p, nextCFI, cieInfo.pointerEncoding & 0x0F, NULL); } /// /// Parse a FDE into a CIE_Info and an FDE_Info /// template bool CFI_Parser::decodeFDE(A &addressSpace, pint_t fdeStart, FDE_Info *fdeInfo, CIE_Info *cieInfo, unw_proc_info_t *ctx) { pint_t p = fdeStart; uint64_t cfiLength = addressSpace.get32(p); p += 4; if (cfiLength == 0xffffffff) { // 0xffffffff means length is really the next 8 Bytes. cfiLength = addressSpace.get64(p); p += 8; } if (cfiLength == 0) return false; uint32_t ciePointer = addressSpace.get32(p); if (ciePointer == 0) return false; pint_t nextCFI = p + cfiLength; pint_t cieStart = p - ciePointer; if (!parseCIE(addressSpace, cieStart, cieInfo)) return false; p += 4; // Parse pc begin and range. pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding, ctx); pint_t pcRange = addressSpace.getEncodedP( p, nextCFI, cieInfo->pointerEncoding & 0x0F, ctx); // 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 != 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, ctx) != 0) { // Reset pointer and re-parse LSDA address. p = lsdaStart; fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding, ctx); } } p = endOfAug; } fdeInfo->fdeStart = fdeStart; fdeInfo->fdeLength = nextCFI - fdeStart; fdeInfo->fdeInstructions = p; fdeInfo->pcStart = pcStart; fdeInfo->pcEnd = pcStart + pcRange; return true; } /// Extract info from a CIE template bool 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; 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 the next 8 Bytes. cieLength = addressSpace.get64(p); p += 8; cieContentEnd = p + cieLength; } if (cieLength == 0) return true; // CIE ID is always 0 if (addressSpace.get32(p) != 0) return false; p += 4; // Version is always 1 or 3 uint8_t version = addressSpace.get8(p); if (version != 1 && version != 3) return false; ++p; // Save start of augmentation string and find end. pint_t strStart = p; while (addressSpace.get8(p) != 0) ++p; ++p; // Parse code alignment factor cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd); // Parse data alignment factor cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd); // Parse return address register cieInfo->returnAddressRegister = R::dwarf2regno((uint8_t)addressSpace.getULEB128(p, cieContentEnd)); // Parse augmentation data based on augmentation string. 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, NULL); 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 true; } /// "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, unw_proc_info_t *ctx) { // Clear results. memset(results, 0, sizeof(*results)); PrologInfoStackEntry *rememberStack = NULL; // First parse the CIE then FDE instructions. if (!parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart + cieInfo.cieLength, cieInfo, (pint_t)(-1), rememberStack, results, ctx)) return false; return parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, upToPC - fdeInfo.pcStart, rememberStack, results, ctx); } /// "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, unw_proc_info_t *ctx) { pint_t p = instructions; uint32_t codeOffset = 0; PrologInfo initialState = *results; // 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: break; case DW_CFA_set_loc: codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding, ctx); break; case DW_CFA_advance_loc1: codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); p += 1; break; case DW_CFA_advance_loc2: codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); p += 2; break; case DW_CFA_advance_loc4: codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); p += 4; break; case DW_CFA_offset_extended: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg].location = kRegisterInCFA; results->savedRegisters[reg].value = offset; break; case DW_CFA_restore_extended: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg] = initialState.savedRegisters[reg]; break; case DW_CFA_undefined: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg].location = kRegisterUnused; break; case DW_CFA_same_value: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; // "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; break; case DW_CFA_register: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); reg2 = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; if (reg2 > kMaxRegisterNumber) return false; results->savedRegisters[reg].location = kRegisterInRegister; results->savedRegisters[reg].value = 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) return false; entry->next = rememberStack; entry->info = *results; rememberStack = entry; break; case DW_CFA_restore_state: if (rememberStack == NULL) return false; { PrologInfoStackEntry *top = rememberStack; *results = top->info; rememberStack = top->next; free((char *)top); } break; case DW_CFA_def_cfa: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); offset = addressSpace.getULEB128(p, instructionsEnd); if (reg > kMaxRegisterNumber) return false; results->cfaRegister = reg; results->cfaRegisterOffset = offset; break; case DW_CFA_def_cfa_register: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; results->cfaRegister = reg; break; case DW_CFA_def_cfa_offset: results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd); results->codeOffsetAtStackDecrement = codeOffset; break; case DW_CFA_def_cfa_expression: results->cfaRegister = 0; results->cfaExpression = p; length = addressSpace.getULEB128(p, instructionsEnd); p += length; break; case DW_CFA_expression: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg].location = kRegisterAtExpression; results->savedRegisters[reg].value = p; length = addressSpace.getULEB128(p, instructionsEnd); p += length; break; case DW_CFA_offset_extended_sf: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->savedRegisters[reg].location = kRegisterInCFA; results->savedRegisters[reg].value = offset; break; case DW_CFA_def_cfa_sf: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; if (reg > kMaxRegisterNumber) return false; results->cfaRegister = reg; results->cfaRegisterOffset = offset; break; case DW_CFA_def_cfa_offset_sf: results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->codeOffsetAtStackDecrement = codeOffset; break; case DW_CFA_val_offset: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg].location = kRegisterOffsetFromCFA; results->savedRegisters[reg].value = offset; break; case DW_CFA_val_offset_sf: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->savedRegisters[reg].location = kRegisterOffsetFromCFA; results->savedRegisters[reg].value = offset; break; case DW_CFA_val_expression: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg].location = kRegisterIsExpression; results->savedRegisters[reg].value = p; length = addressSpace.getULEB128(p, instructionsEnd); p += length; break; case DW_CFA_GNU_window_save: #if defined(__sparc__) for (reg = 8; reg < 16; ++reg) { results->savedRegisters[reg].location = kRegisterInRegister; results->savedRegisters[reg].value = reg + 16; } for (reg = 16; reg < 32; ++reg) { results->savedRegisters[reg].location = kRegisterInCFA; results->savedRegisters[reg].value = (reg - 16) * sizeof(typename R::reg_t); } break; #else return false; #endif case DW_CFA_GNU_args_size: offset = addressSpace.getULEB128(p, instructionsEnd); results->spExtraArgSize = offset; break; case DW_CFA_GNU_negative_offset_extended: reg = R::dwarf2regno(addressSpace.getULEB128(p, instructionsEnd)); if (reg > kMaxRegisterNumber) return false; offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->savedRegisters[reg].location = kRegisterInCFA; results->savedRegisters[reg].value = -offset; break; default: operand = opcode & 0x3F; switch (opcode & 0xC0) { case DW_CFA_offset: reg = R::dwarf2regno(operand); if (reg > kMaxRegisterNumber) return false; offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; results->savedRegisters[reg].location = kRegisterInCFA; results->savedRegisters[reg].value = offset; break; case DW_CFA_advance_loc: codeOffset += operand * cieInfo.codeAlignFactor; break; case DW_CFA_restore: reg = R::dwarf2regno(operand); if (reg > kMaxRegisterNumber) return false; results->savedRegisters[reg] = initialState.savedRegisters[reg]; break; default: return false; } } } return true; } } // namespace _Unwind #endif // __DWARF_PARSER_HPP__