minix/sys/lib/libunwind/DwarfParser.hpp
Ben Gras b029fb598a new libunwind, updated to netbsd b1f513eedd
existing libunwind used '0' in lsda_encoding as 'not present,'
whereas that is a valid encoding and does occur and would be
ignored. a missing encoding is actually 0xff.

The commit that addresses this is:

commit 8d4b51028d1a12b58d616f4b605254a877caafcf
Author: joerg <joerg>
Date:   Tue Mar 11 23:52:17 2014 +0000

    0 is a valid LSDA encoding and can be seen in statically linked
    programs. Initialize lsdaEncoding to DW_EH_PE_omit and check for that
    value to decide whether a value should be decoded.

more bugfixes are necessary. this update is up to:

commit b1f513eedd332426d88acbb118b6e9070966dcb9
Author: joerg <joerg>
Date:   Wed May 14 22:13:36 2014 +0000

    Lazy VFP processing works a lot better if the functions contain a return
    instruction.
2014-07-28 17:06:01 +02:00

546 lines
18 KiB
C++

//===--------------------------- 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 <cstdint>
#include <cstdlib>
#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 <typename A, typename R> 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 <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) {
// 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<A, R>::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 <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) {
// 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 <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) {
// 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 aligment factor
cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd);
// Parse data alignment factor
cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd);
// Parse return address register
cieInfo->returnAddressRegister = (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 <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) {
// 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 <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;
// 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__