#ifndef __DWARF_PARSER_HPP__
#define __DWARF_PARSER_HPP__
#include <cstdint>
#include <cstdlib>
#include "dwarf2.h"
#include "AddressSpace.hpp"
namespace _Unwind {
template <typename A, typename R> class CFI_Parser {
public:
typedef typename A::pint_t pint_t;
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;
};
struct FDE_Info {
pint_t fdeStart;
pint_t fdeLength;
pint_t fdeInstructions;
pint_t pcStart;
pint_t pcEnd;
pint_t lsda;
};
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;
int64_t cfaExpression;
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);
};
template <typename A, typename R>
void CFI_Parser<A, R>::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) {
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<A, R>::CIE_Info cieInfo;
if (!parseCIE(addressSpace, cieStart, &cieInfo))
return;
p += 4;
pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding, NULL);
pcEnd = pcStart + addressSpace.getEncodedP(
p, nextCFI, cieInfo.pointerEncoding & 0x0F, NULL);
}
template <typename A, typename R>
bool CFI_Parser<A, R>::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) {
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;
pint_t pcStart =
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding, ctx);
pint_t pcRange = addressSpace.getEncodedP(
p, nextCFI, cieInfo->pointerEncoding & 0x0F, ctx);
fdeInfo->lsda = 0;
if (cieInfo->fdesHaveAugmentationData) {
uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
pint_t endOfAug = p + augLen;
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
pint_t lsdaStart = p;
if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F,
ctx) != 0) {
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;
}
template <typename A, typename R>
bool CFI_Parser<A, R>::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) {
cieLength = addressSpace.get64(p);
p += 8;
cieContentEnd = p + cieLength;
}
if (cieLength == 0)
return true;
if (addressSpace.get32(p) != 0)
return false;
p += 4;
uint8_t version = addressSpace.get8(p);
if (version != 1 && version != 3)
return false;
++p;
pint_t strStart = p;
while (addressSpace.get8(p) != 0)
++p;
++p;
cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd);
cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd);
cieInfo->returnAddressRegister = R::dwarf2regno((uint8_t)addressSpace.getULEB128(p, cieContentEnd));
if (addressSpace.get8(strStart) == 'z') {
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:
break;
}
}
}
cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
cieInfo->cieInstructions = p;
return true;
}
template <typename A, typename R>
bool CFI_Parser<A, R>::parseFDEInstructions(A &addressSpace,
const FDE_Info &fdeInfo,
const CIE_Info &cieInfo,
pint_t upToPC, PrologInfo *results,
unw_proc_info_t *ctx) {
memset(results, 0, sizeof(*results));
PrologInfoStackEntry *rememberStack = NULL;
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);
}
template <typename A, typename R>
bool
CFI_Parser<A, R>::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;
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;
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:
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;
}
}
#endif