diff options
Diffstat (limited to 'src/DwarfParser.hpp')
| -rw-r--r-- | src/DwarfParser.hpp | 722 | 
1 files changed, 722 insertions, 0 deletions
diff --git a/src/DwarfParser.hpp b/src/DwarfParser.hpp new file mode 100644 index 000000000000..26993c4e0d1b --- /dev/null +++ b/src/DwarfParser.hpp @@ -0,0 +1,722 @@ +//===--------------------------- 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 <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#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://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template <typename A> +class CFI_Parser { +public: +  typedef typename A::pint_t pint_t; + +  /// Information encoded in a CIE (Common Information Entry) +  struct CIE_Info { +    pint_t    cieStart; +    pint_t    cieLength; +    pint_t    cieInstructions; +    uint8_t   pointerEncoding; +    uint8_t   lsdaEncoding; +    uint8_t   personalityEncoding; +    uint8_t   personalityOffsetInCIE; +    pint_t    personality; +    uint32_t  codeAlignFactor; +    int       dataAlignFactor; +    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; +  }; + +  enum { +    kMaxRegisterNumber = 120 +  }; +  enum RegisterSavedWhere { +    kRegisterUnused, +    kRegisterInCFA, +    kRegisterOffsetFromCFA, +    kRegisterInRegister, +    kRegisterAtExpression, +    kRegisterIsExpression +  }; +  struct RegisterLocation { +    RegisterSavedWhere location; +    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; +    uint32_t          codeOffsetAtStackDecrement; +    bool              registersInOtherRegisters; +    bool              sameValueUsed; +    RegisterLocation  savedRegisters[kMaxRegisterNumber]; +  }; + +  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 *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); + +private: +  static bool parseInstructions(A &addressSpace, pint_t instructions, +                                pint_t instructionsEnd, const CIE_Info &cieInfo, +                                pint_t pcoffset, +                                PrologInfoStackEntry *&rememberStack, +                                PrologInfo *results); +}; + +/// Parse a FDE into a CIE_Info and an FDE_Info +template <typename A> +const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart, +                                     FDE_Info *fdeInfo, CIE_Info *cieInfo) { +  pint_t p = fdeStart; +  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"; // 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) { +    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 <typename A> +bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, +                            uint32_t sectionLength, pint_t fdeHint, +                            FDE_Info *fdeInfo, CIE_Info *cieInfo) { +  //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); +  pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; +  const pint_t ehSectionEnd = p + sectionLength; +  while (p < ehSectionEnd) { +    pint_t currentCFI = p; +    //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); +    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; // 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); +          // 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 <typename A> +const char *CFI_Parser<A>::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; +  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 = 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; +      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 <typename A> +bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace, +                                         const FDE_Info &fdeInfo, +                                         const CIE_Info &cieInfo, pint_t upToPC, +                                         PrologInfo *results) { +  // clear results +  memset(results, '\0', sizeof(PrologInfo)); +  PrologInfoStackEntry *rememberStack = NULL; + +  // parse CIE then FDE instructions +  return parseInstructions(addressSpace, cieInfo.cieInstructions, +                           cieInfo.cieStart + cieInfo.cieLength, cieInfo, +                           (pint_t)(-1), rememberStack, results) && +         parseInstructions(addressSpace, fdeInfo.fdeInstructions, +                           fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo, +                           upToPC - fdeInfo.pcStart, rememberStack, results); +} + +/// "run" the dwarf instructions +template <typename A> +bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions, +                                      pint_t instructionsEnd, +                                      const CIE_Info &cieInfo, pint_t pcoffset, +                                      PrologInfoStackEntry *&rememberStack, +                                      PrologInfo *results) { +  const bool logDwarf = false; +  pint_t p = instructions; +  pint_t codeOffset = 0; +  PrologInfo initialState = *results; +  if (logDwarf) +    fprintf(stderr, "parseInstructions(instructions=0x%0" PRIx64 ")\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=%" PRIu64 "\n", +                (uint64_t)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=%" PRIu64 "\n", +                (uint64_t)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=%" PRIu64 "\n", +                (uint64_t)codeOffset); +      break; +    case DW_CFA_offset_extended: +      reg = addressSpace.getULEB128(p, instructionsEnd); +      offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) +                                                  * cieInfo.dataAlignFactor; +      if (reg > kMaxRegisterNumber) { +        fprintf(stderr, +                "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n"); +        return false; +      } +      results->savedRegisters[reg].location = kRegisterInCFA; +      results->savedRegisters[reg].value = offset; +      if (logDwarf) +        fprintf(stderr, +                "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) { +        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=%" PRIu64 ")\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=%" PRIu64 ")\n", reg); +      break; +    case DW_CFA_same_value: +      reg = addressSpace.getULEB128(p, instructionsEnd); +      if (reg > kMaxRegisterNumber) { +        fprintf(stderr, +                "malformed DW_CFA_same_value dwarf unwind, reg too big\n"); +        return false; +      } +      // <rdar://problem/8456377> DW_CFA_same_value unsupported +      // "same value" means register was stored in frame, but its current +      // value has not changed, so no need to restore from frame. +      // We model this as if the register was never saved. +      results->savedRegisters[reg].location = kRegisterUnused; +      // set flag to disable conversion to compact unwind +      results->sameValueUsed = true; +      if (logDwarf) +        fprintf(stderr, "DW_CFA_same_value(reg=%" PRIu64 ")\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 = (int64_t)reg2; +      // set flag to disable conversion to compact unwind +      results->registersInOtherRegisters = true; +      if (logDwarf) +        fprintf(stderr, "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 +      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 = (int64_t)addressSpace.getULEB128(p, instructionsEnd); +      if (reg > kMaxRegisterNumber) { +        fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n"); +        return false; +      } +      results->cfaRegister = (uint32_t)reg; +      results->cfaRegisterOffset = (int32_t)offset; +      if (logDwarf) +        fprintf(stderr, "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) { +        fprintf( +            stderr, +            "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n"); +        return false; +      } +      results->cfaRegister = (uint32_t)reg; +      if (logDwarf) +        fprintf(stderr, "DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg); +      break; +    case DW_CFA_def_cfa_offset: +      results->cfaRegisterOffset = (int32_t) +                                  addressSpace.getULEB128(p, instructionsEnd); +      results->codeOffsetAtStackDecrement = (uint32_t)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 = (int64_t)p; +      length = addressSpace.getULEB128(p, instructionsEnd); +      p += length; +      if (logDwarf) +        fprintf(stderr, "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) { +        fprintf(stderr, +                "malformed DW_CFA_expression dwarf unwind, reg too big\n"); +        return false; +      } +      results->savedRegisters[reg].location = kRegisterAtExpression; +      results->savedRegisters[reg].value = (int64_t)p; +      length = addressSpace.getULEB128(p, instructionsEnd); +      p += length; +      if (logDwarf) +        fprintf(stderr, "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) { +        fprintf( +            stderr, +            "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n"); +        return false; +      } +      offset = +          addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor; +      results->savedRegisters[reg].location = kRegisterInCFA; +      results->savedRegisters[reg].value = offset; +      if (logDwarf) +        fprintf(stderr, "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) { +        fprintf(stderr, +                "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n"); +        return false; +      } +      results->cfaRegister = (uint32_t)reg; +      results->cfaRegisterOffset = (int32_t)offset; +      if (logDwarf) +        fprintf(stderr, +                "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); +      results->codeOffsetAtStackDecrement = (uint32_t)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 = (int64_t)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=%" PRIu64 ", offset=%" PRId64 "\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=%" PRIu64 ", offset=%" PRId64 "\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 = (int64_t)p; +      length = addressSpace.getULEB128(p, instructionsEnd); +      p += length; +      if (logDwarf) +        fprintf(stderr, "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; +      if (logDwarf) +        fprintf(stderr, "DW_CFA_GNU_args_size(%" PRIu64 ")\n", length); +      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 = (int64_t)addressSpace.getULEB128(p, instructionsEnd) +                                                    * cieInfo.dataAlignFactor; +      results->savedRegisters[reg].location = kRegisterInCFA; +      results->savedRegisters[reg].value = -offset; +      if (logDwarf) +        fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", +                offset); +      break; +    default: +      operand = opcode & 0x3F; +      switch (opcode & 0xC0) { +      case DW_CFA_offset: +        reg = operand; +        offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) +                                                    * cieInfo.dataAlignFactor; +        results->savedRegisters[reg].location = kRegisterInCFA; +        results->savedRegisters[reg].value = offset; +        if (logDwarf) +          fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n", +                  operand, offset); +        break; +      case DW_CFA_advance_loc: +        codeOffset += operand * cieInfo.codeAlignFactor; +        if (logDwarf) +          fprintf(stderr, "DW_CFA_advance_loc: new offset=%" PRIu64 "\n", +                  (uint64_t)codeOffset); +        break; +      case DW_CFA_restore: +        reg = operand; +        results->savedRegisters[reg] = initialState.savedRegisters[reg]; +        if (logDwarf) +          fprintf(stderr, "DW_CFA_restore(reg=%" PRIu64 ")\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__  | 
