DWARFDebugFrame.cpp [plain text]
#include "DWARFDebugFrame.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <vector>
using namespace llvm;
using namespace dwarf;
class llvm::FrameEntry {
public:
enum FrameKind {FK_CIE, FK_FDE};
FrameEntry(FrameKind K, DataExtractor D, uint64_t Offset, uint64_t Length)
: Kind(K), Data(D), Offset(Offset), Length(Length) {}
virtual ~FrameEntry() {
}
FrameKind getKind() const { return Kind; }
virtual uint64_t getOffset() const { return Offset; }
virtual void parseInstructions(uint32_t *Offset, uint32_t EndOffset);
virtual void dumpHeader(raw_ostream &OS) const = 0;
virtual void dumpInstructions(raw_ostream &OS) const;
protected:
const FrameKind Kind;
DataExtractor Data;
uint64_t Offset;
uint64_t Length;
typedef std::vector<uint64_t> Operands;
struct Instruction {
Instruction(uint8_t Opcode)
: Opcode(Opcode)
{}
uint8_t Opcode;
Operands Ops;
};
std::vector<Instruction> Instructions;
void addInstruction(uint8_t Opcode) {
Instructions.push_back(Instruction(Opcode));
}
void addInstruction(uint8_t Opcode, uint64_t Operand1) {
Instructions.push_back(Instruction(Opcode));
Instructions.back().Ops.push_back(Operand1);
}
void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) {
Instructions.push_back(Instruction(Opcode));
Instructions.back().Ops.push_back(Operand1);
Instructions.back().Ops.push_back(Operand2);
}
};
const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
void FrameEntry::parseInstructions(uint32_t *Offset, uint32_t EndOffset) {
while (*Offset < EndOffset) {
uint8_t Opcode = Data.getU8(Offset);
uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK;
if (Primary) {
uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK;
switch (Primary) {
default: llvm_unreachable("Impossible primary CFI opcode");
case DW_CFA_advance_loc:
case DW_CFA_restore:
addInstruction(Primary, Op1);
break;
case DW_CFA_offset:
addInstruction(Primary, Op1, Data.getULEB128(Offset));
break;
}
} else {
switch (Opcode) {
default: llvm_unreachable("Invalid extended CFI opcode");
case DW_CFA_nop:
case DW_CFA_remember_state:
case DW_CFA_restore_state:
addInstruction(Opcode);
break;
case DW_CFA_set_loc:
addInstruction(Opcode, Data.getAddress(Offset));
break;
case DW_CFA_advance_loc1:
addInstruction(Opcode, Data.getU8(Offset));
break;
case DW_CFA_advance_loc2:
addInstruction(Opcode, Data.getU16(Offset));
break;
case DW_CFA_advance_loc4:
addInstruction(Opcode, Data.getU32(Offset));
break;
case DW_CFA_restore_extended:
case DW_CFA_undefined:
case DW_CFA_same_value:
case DW_CFA_def_cfa_register:
case DW_CFA_def_cfa_offset:
addInstruction(Opcode, Data.getULEB128(Offset));
break;
case DW_CFA_def_cfa_offset_sf:
addInstruction(Opcode, Data.getSLEB128(Offset));
break;
case DW_CFA_offset_extended:
case DW_CFA_register:
case DW_CFA_def_cfa:
case DW_CFA_val_offset:
addInstruction(Opcode, Data.getULEB128(Offset),
Data.getULEB128(Offset));
break;
case DW_CFA_offset_extended_sf:
case DW_CFA_def_cfa_sf:
case DW_CFA_val_offset_sf:
addInstruction(Opcode, Data.getULEB128(Offset),
Data.getSLEB128(Offset));
break;
case DW_CFA_def_cfa_expression:
case DW_CFA_expression:
case DW_CFA_val_expression:
report_fatal_error("Values with expressions not implemented yet!");
}
}
}
}
void FrameEntry::dumpInstructions(raw_ostream &OS) const {
for (std::vector<Instruction>::const_iterator I = Instructions.begin(),
E = Instructions.end();
I != E; ++I) {
uint8_t Opcode = I->Opcode;
if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK)
Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK;
OS << " " << CallFrameString(Opcode) << ":\n";
}
}
namespace {
class CIE : public FrameEntry {
public:
CIE(DataExtractor D, uint64_t Offset, uint64_t Length, uint8_t Version,
SmallString<8> Augmentation, uint64_t CodeAlignmentFactor,
int64_t DataAlignmentFactor, uint64_t ReturnAddressRegister)
: FrameEntry(FK_CIE, D, Offset, Length), Version(Version),
Augmentation(Augmentation), CodeAlignmentFactor(CodeAlignmentFactor),
DataAlignmentFactor(DataAlignmentFactor),
ReturnAddressRegister(ReturnAddressRegister) {}
~CIE() {
}
void dumpHeader(raw_ostream &OS) const {
OS << format("%08x %08x %08x CIE",
(uint32_t)Offset, (uint32_t)Length, DW_CIE_ID)
<< "\n";
OS << format(" Version: %d\n", Version);
OS << " Augmentation: \"" << Augmentation << "\"\n";
OS << format(" Code alignment factor: %u\n",
(uint32_t)CodeAlignmentFactor);
OS << format(" Data alignment factor: %d\n",
(int32_t)DataAlignmentFactor);
OS << format(" Return address column: %d\n",
(int32_t)ReturnAddressRegister);
OS << "\n";
}
static bool classof(const FrameEntry *FE) {
return FE->getKind() == FK_CIE;
}
private:
uint8_t Version;
SmallString<8> Augmentation;
uint64_t CodeAlignmentFactor;
int64_t DataAlignmentFactor;
uint64_t ReturnAddressRegister;
};
class FDE : public FrameEntry {
public:
FDE(DataExtractor D, uint64_t Offset, uint64_t Length,
int64_t LinkedCIEOffset, uint64_t InitialLocation, uint64_t AddressRange)
: FrameEntry(FK_FDE, D, Offset, Length), LinkedCIEOffset(LinkedCIEOffset),
InitialLocation(InitialLocation), AddressRange(AddressRange),
LinkedCIE(NULL) {}
~FDE() {
}
void dumpHeader(raw_ostream &OS) const {
OS << format("%08x %08x %08x FDE ",
(uint32_t)Offset, (uint32_t)Length, (int32_t)LinkedCIEOffset);
OS << format("cie=%08x pc=%08x...%08x\n",
(int32_t)LinkedCIEOffset,
(uint32_t)InitialLocation,
(uint32_t)InitialLocation + (uint32_t)AddressRange);
if (LinkedCIE) {
OS << format("%p\n", LinkedCIE);
}
}
static bool classof(const FrameEntry *FE) {
return FE->getKind() == FK_FDE;
}
private:
uint64_t LinkedCIEOffset;
uint64_t InitialLocation;
uint64_t AddressRange;
CIE *LinkedCIE;
};
}
DWARFDebugFrame::DWARFDebugFrame() {
}
DWARFDebugFrame::~DWARFDebugFrame() {
for (EntryVector::iterator I = Entries.begin(), E = Entries.end();
I != E; ++I) {
delete *I;
}
}
static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data,
uint32_t Offset, int Length) {
errs() << "DUMP: ";
for (int i = 0; i < Length; ++i) {
uint8_t c = Data.getU8(&Offset);
errs().write_hex(c); errs() << " ";
}
errs() << "\n";
}
void DWARFDebugFrame::parse(DataExtractor Data) {
uint32_t Offset = 0;
while (Data.isValidOffset(Offset)) {
uint32_t StartOffset = Offset;
bool IsDWARF64 = false;
uint64_t Length = Data.getU32(&Offset);
uint64_t Id;
if (Length == UINT32_MAX) {
IsDWARF64 = true;
Length = Data.getU64(&Offset);
}
uint32_t EndStructureOffset = Offset + static_cast<uint32_t>(Length);
Id = Data.getUnsigned(&Offset, IsDWARF64 ? 8 : 4);
bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID);
FrameEntry *Entry = 0;
if (IsCIE) {
uint8_t Version = Data.getU8(&Offset);
const char *Augmentation = Data.getCStr(&Offset);
uint64_t CodeAlignmentFactor = Data.getULEB128(&Offset);
int64_t DataAlignmentFactor = Data.getSLEB128(&Offset);
uint64_t ReturnAddressRegister = Data.getULEB128(&Offset);
Entry = new CIE(Data, StartOffset, Length, Version,
StringRef(Augmentation), CodeAlignmentFactor,
DataAlignmentFactor, ReturnAddressRegister);
} else {
uint64_t CIEPointer = Id;
uint64_t InitialLocation = Data.getAddress(&Offset);
uint64_t AddressRange = Data.getAddress(&Offset);
Entry = new FDE(Data, StartOffset, Length, CIEPointer,
InitialLocation, AddressRange);
}
assert(Entry && "Expected Entry to be populated with CIE or FDE");
Entry->parseInstructions(&Offset, EndStructureOffset);
if (Offset == EndStructureOffset) {
Entries.push_back(Entry);
} else {
std::string Str;
raw_string_ostream OS(Str);
OS << format("Parsing entry instructions at %lx failed",
Entry->getOffset());
report_fatal_error(Str);
}
}
}
void DWARFDebugFrame::dump(raw_ostream &OS) const {
OS << "\n";
for (EntryVector::const_iterator I = Entries.begin(), E = Entries.end();
I != E; ++I) {
FrameEntry *Entry = *I;
Entry->dumpHeader(OS);
Entry->dumpInstructions(OS);
OS << "\n";
}
}