to reflect the new license. These used slightly different spellings that defeated my regular expressions. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351648
766 lines
29 KiB
C++
766 lines
29 KiB
C++
//===--------------------------- 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 <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#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 <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;
|
|
#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,
|
|
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 + 1];
|
|
};
|
|
|
|
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,
|
|
int arch, 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, int arch,
|
|
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;
|
|
#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 = 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 <typename A>
|
|
bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
|
|
const FDE_Info &fdeInfo,
|
|
const CIE_Info &cieInfo, pint_t upToPC,
|
|
int arch, 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, arch, results) &&
|
|
parseInstructions(addressSpace, fdeInfo.fdeInstructions,
|
|
fdeInfo.fdeStart + fdeInfo.fdeLength, cieInfo,
|
|
upToPC - fdeInfo.pcStart, rememberStack, arch,
|
|
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,
|
|
int arch, PrologInfo *results) {
|
|
pint_t p = instructions;
|
|
pint_t codeOffset = 0;
|
|
PrologInfo initialState = *results;
|
|
|
|
_LIBUNWIND_TRACE_DWARF("parseInstructions(instructions=0x%0" PRIx64 ")\n",
|
|
static_cast<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;
|
|
#if !defined(_LIBUNWIND_NO_HEAP)
|
|
PrologInfoStackEntry *entry;
|
|
#endif
|
|
++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<uint64_t>(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<uint64_t>(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<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) {
|
|
_LIBUNWIND_LOG0(
|
|
"malformed DW_CFA_offset_extended DWARF unwind, reg too big");
|
|
return false;
|
|
}
|
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
|
results->savedRegisters[reg].value = offset;
|
|
_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->savedRegisters[reg] = initialState.savedRegisters[reg];
|
|
_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->savedRegisters[reg].location = kRegisterUnused;
|
|
_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;
|
|
}
|
|
// <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;
|
|
_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->savedRegisters[reg].location = kRegisterInRegister;
|
|
results->savedRegisters[reg].value = (int64_t)reg2;
|
|
// set flag to disable conversion to compact unwind
|
|
results->registersInOtherRegisters = true;
|
|
_LIBUNWIND_TRACE_DWARF(
|
|
"DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2);
|
|
break;
|
|
#if !defined(_LIBUNWIND_NO_HEAP)
|
|
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;
|
|
}
|
|
_LIBUNWIND_TRACE_DWARF("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;
|
|
}
|
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n");
|
|
break;
|
|
#endif
|
|
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);
|
|
results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
|
|
_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<pint_t>(~0) && "pointer overflow");
|
|
p += static_cast<pint_t>(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->savedRegisters[reg].location = kRegisterAtExpression;
|
|
results->savedRegisters[reg].value = (int64_t)p;
|
|
length = addressSpace.getULEB128(p, instructionsEnd);
|
|
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
|
p += static_cast<pint_t>(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->savedRegisters[reg].location = kRegisterInCFA;
|
|
results->savedRegisters[reg].value = offset;
|
|
_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);
|
|
results->codeOffsetAtStackDecrement = (uint32_t)codeOffset;
|
|
_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->savedRegisters[reg].location = kRegisterOffsetFromCFA;
|
|
results->savedRegisters[reg].value = offset;
|
|
_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->savedRegisters[reg].location = kRegisterOffsetFromCFA;
|
|
results->savedRegisters[reg].value = offset;
|
|
_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->savedRegisters[reg].location = kRegisterIsExpression;
|
|
results->savedRegisters[reg].value = (int64_t)p;
|
|
length = addressSpace.getULEB128(p, instructionsEnd);
|
|
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
|
p += static_cast<pint_t>(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->savedRegisters[reg].location = kRegisterInCFA;
|
|
results->savedRegisters[reg].value = -offset;
|
|
_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:
|
|
results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1;
|
|
_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->savedRegisters[reg].location = kRegisterInRegister;
|
|
results->savedRegisters[reg].value =
|
|
((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0;
|
|
}
|
|
|
|
for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
|
|
results->savedRegisters[reg].location = kRegisterInCFA;
|
|
results->savedRegisters[reg].value =
|
|
((int64_t)reg - UNW_SPARC_L0) * 4;
|
|
}
|
|
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->savedRegisters[reg].location = kRegisterInCFA;
|
|
results->savedRegisters[reg].value = offset;
|
|
_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<uint64_t>(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->savedRegisters[reg] = initialState.savedRegisters[reg];
|
|
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n",
|
|
static_cast<uint64_t>(operand));
|
|
break;
|
|
default:
|
|
_LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace libunwind
|
|
|
|
#endif // __DWARF_PARSER_HPP__
|