Reland "[DebugInfo] Support DWARF expressions in eh_frame"

Summary:
Original change was D43313 (r326932) and reverted by r326953 because it
broke an LLD test and a windows build. The LLD test was already fixed in
lld commit r326944 (thanks maskray). This is the original change with
the windows build fixed.

llvm-svn: 326970
This commit is contained in:
Rafael Auler 2018-03-08 00:46:53 +00:00
parent a4259cd3a6
commit 86fb7bf2bc
14 changed files with 1011 additions and 389 deletions

View File

@ -44,6 +44,13 @@ public:
uint64_t getRelocatedAddress(uint32_t *Off, uint64_t *SecIx = nullptr) const {
return getRelocatedValue(getAddressSize(), Off, SecIx);
}
/// Extracts a DWARF-encoded pointer in \p Offset using \p Encoding.
/// There is a DWARF encoding that uses a PC-relative adjustment.
/// For these values, \p AbsPosOffset is used to fix them, which should
/// reflect the absolute address of this pointer.
Optional<uint64_t> getEncodedPointer(uint32_t *Offset, uint8_t Encoding,
uint64_t AbsPosOffset = 0) const;
};
} // end namespace llvm

View File

@ -10,40 +10,290 @@
#ifndef LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
#define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H
#include "llvm/Support/DataExtractor.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <vector>
namespace llvm {
class FrameEntry;
class raw_ostream;
/// \brief A parsed .debug_frame or .eh_frame section
///
namespace dwarf {
/// Represent a sequence of Call Frame Information instructions that, when read
/// in order, construct a table mapping PC to frame state. This can also be
/// referred to as "CFI rules" in DWARF literature to avoid confusion with
/// computer programs in the broader sense, and in this context each instruction
/// would be a rule to establish the mapping. Refer to pg. 172 in the DWARF5
/// manual, "6.4.1 Structure of Call Frame Information".
class CFIProgram {
public:
typedef SmallVector<uint64_t, 2> Operands;
/// An instruction consists of a DWARF CFI opcode and an optional sequence of
/// operands. If it refers to an expression, then this expression has its own
/// sequence of operations and operands handled separately by DWARFExpression.
struct Instruction {
Instruction(uint8_t Opcode) : Opcode(Opcode) {}
uint8_t Opcode;
Operands Ops;
// Associated DWARF expression in case this instruction refers to one
Optional<DWARFExpression> Expression;
};
using InstrList = std::vector<Instruction>;
using iterator = InstrList::iterator;
using const_iterator = InstrList::const_iterator;
iterator begin() { return Instructions.begin(); }
const_iterator begin() const { return Instructions.begin(); }
iterator end() { return Instructions.end(); }
const_iterator end() const { return Instructions.end(); }
unsigned size() const { return (unsigned)Instructions.size(); }
bool empty() const { return Instructions.empty(); }
CFIProgram(uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor)
: CodeAlignmentFactor(CodeAlignmentFactor),
DataAlignmentFactor(DataAlignmentFactor) {}
/// Parse and store a sequence of CFI instructions from Data,
/// starting at *Offset and ending at EndOffset. *Offset is updated
/// to EndOffset upon successful parsing, or indicates the offset
/// where a problem occurred in case an error is returned.
Error parse(DataExtractor Data, uint32_t *Offset, uint32_t EndOffset);
void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
unsigned IndentLevel = 1) const;
private:
std::vector<Instruction> Instructions;
const uint64_t CodeAlignmentFactor;
const int64_t DataAlignmentFactor;
/// Convenience method to add a new instruction with the given opcode.
void addInstruction(uint8_t Opcode) {
Instructions.push_back(Instruction(Opcode));
}
/// Add a new single-operand instruction.
void addInstruction(uint8_t Opcode, uint64_t Operand1) {
Instructions.push_back(Instruction(Opcode));
Instructions.back().Ops.push_back(Operand1);
}
/// Add a new instruction that has two operands.
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);
}
/// Types of operands to CFI instructions
/// In DWARF, this type is implicitly tied to a CFI instruction opcode and
/// thus this type doesn't need to be explictly written to the file (this is
/// not a DWARF encoding). The relationship of instrs to operand types can
/// be obtained from getOperandTypes() and is only used to simplify
/// instruction printing.
enum OperandType {
OT_Unset,
OT_None,
OT_Address,
OT_Offset,
OT_FactoredCodeOffset,
OT_SignedFactDataOffset,
OT_UnsignedFactDataOffset,
OT_Register,
OT_Expression
};
/// Retrieve the array describing the types of operands according to the enum
/// above. This is indexed by opcode.
static ArrayRef<OperandType[2]> getOperandTypes();
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
void printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
const Instruction &Instr, unsigned OperandIdx,
uint64_t Operand) const;
};
/// An entry in either debug_frame or eh_frame. This entry can be a CIE or an
/// FDE.
class FrameEntry {
public:
enum FrameKind { FK_CIE, FK_FDE };
FrameEntry(FrameKind K, uint64_t Offset, uint64_t Length, uint64_t CodeAlign,
int64_t DataAlign)
: Kind(K), Offset(Offset), Length(Length), CFIs(CodeAlign, DataAlign) {}
virtual ~FrameEntry() {}
FrameKind getKind() const { return Kind; }
uint64_t getOffset() const { return Offset; }
uint64_t getLength() const { return Length; }
const CFIProgram &cfis() const { return CFIs; }
CFIProgram &cfis() { return CFIs; }
/// Dump the instructions in this CFI fragment
virtual void dump(raw_ostream &OS, const MCRegisterInfo *MRI,
bool IsEH) const = 0;
protected:
const FrameKind Kind;
/// Offset of this entry in the section.
const uint64_t Offset;
/// Entry length as specified in DWARF.
const uint64_t Length;
CFIProgram CFIs;
};
/// DWARF Common Information Entry (CIE)
class CIE : public FrameEntry {
public:
// CIEs (and FDEs) are simply container classes, so the only sensible way to
// create them is by providing the full parsed contents in the constructor.
CIE(uint64_t Offset, uint64_t Length, uint8_t Version,
SmallString<8> Augmentation, uint8_t AddressSize,
uint8_t SegmentDescriptorSize, uint64_t CodeAlignmentFactor,
int64_t DataAlignmentFactor, uint64_t ReturnAddressRegister,
SmallString<8> AugmentationData, uint32_t FDEPointerEncoding,
uint32_t LSDAPointerEncoding, Optional<uint64_t> Personality,
Optional<uint32_t> PersonalityEnc)
: FrameEntry(FK_CIE, Offset, Length, CodeAlignmentFactor,
DataAlignmentFactor),
Version(Version), Augmentation(std::move(Augmentation)),
AddressSize(AddressSize), SegmentDescriptorSize(SegmentDescriptorSize),
CodeAlignmentFactor(CodeAlignmentFactor),
DataAlignmentFactor(DataAlignmentFactor),
ReturnAddressRegister(ReturnAddressRegister),
AugmentationData(std::move(AugmentationData)),
FDEPointerEncoding(FDEPointerEncoding),
LSDAPointerEncoding(LSDAPointerEncoding), Personality(Personality),
PersonalityEnc(PersonalityEnc) {}
static bool classof(const FrameEntry *FE) { return FE->getKind() == FK_CIE; }
StringRef getAugmentationString() const { return Augmentation; }
uint64_t getCodeAlignmentFactor() const { return CodeAlignmentFactor; }
int64_t getDataAlignmentFactor() const { return DataAlignmentFactor; }
uint8_t getVersion() const { return Version; }
uint64_t getReturnAddressRegister() const { return ReturnAddressRegister; }
Optional<uint64_t> getPersonalityAddress() const { return Personality; }
Optional<uint32_t> getPersonalityEncoding() const { return PersonalityEnc; }
uint32_t getFDEPointerEncoding() const { return FDEPointerEncoding; }
uint32_t getLSDAPointerEncoding() const { return LSDAPointerEncoding; }
void dump(raw_ostream &OS, const MCRegisterInfo *MRI,
bool IsEH) const override;
private:
/// The following fields are defined in section 6.4.1 of the DWARF standard v4
const uint8_t Version;
const SmallString<8> Augmentation;
const uint8_t AddressSize;
const uint8_t SegmentDescriptorSize;
const uint64_t CodeAlignmentFactor;
const int64_t DataAlignmentFactor;
const uint64_t ReturnAddressRegister;
// The following are used when the CIE represents an EH frame entry.
const SmallString<8> AugmentationData;
const uint32_t FDEPointerEncoding;
const uint32_t LSDAPointerEncoding;
const Optional<uint64_t> Personality;
const Optional<uint32_t> PersonalityEnc;
};
/// DWARF Frame Description Entry (FDE)
class FDE : public FrameEntry {
public:
// Each FDE has a CIE it's "linked to". Our FDE contains is constructed with
// an offset to the CIE (provided by parsing the FDE header). The CIE itself
// is obtained lazily once it's actually required.
FDE(uint64_t Offset, uint64_t Length, int64_t LinkedCIEOffset,
uint64_t InitialLocation, uint64_t AddressRange, CIE *Cie,
Optional<uint64_t> LSDAAddress)
: FrameEntry(FK_FDE, Offset, Length,
Cie ? Cie->getCodeAlignmentFactor() : 0,
Cie ? Cie->getDataAlignmentFactor() : 0),
LinkedCIEOffset(LinkedCIEOffset), InitialLocation(InitialLocation),
AddressRange(AddressRange), LinkedCIE(Cie), LSDAAddress(LSDAAddress) {}
~FDE() override = default;
const CIE *getLinkedCIE() const { return LinkedCIE; }
uint64_t getInitialLocation() const { return InitialLocation; }
uint64_t getAddressRange() const { return AddressRange; }
Optional<uint64_t> getLSDAAddress() const { return LSDAAddress; }
void dump(raw_ostream &OS, const MCRegisterInfo *MRI,
bool IsEH) const override;
static bool classof(const FrameEntry *FE) { return FE->getKind() == FK_FDE; }
private:
/// The following fields are defined in section 6.4.1 of the DWARF standard v3
const uint64_t LinkedCIEOffset;
const uint64_t InitialLocation;
const uint64_t AddressRange;
const CIE *LinkedCIE;
const Optional<uint64_t> LSDAAddress;
};
} // end namespace dwarf
/// A parsed .debug_frame or .eh_frame section
class DWARFDebugFrame {
// True if this is parsing an eh_frame section.
bool IsEH;
const bool IsEH;
// Not zero for sane pointer values coming out of eh_frame
const uint64_t EHFrameAddress;
std::vector<std::unique_ptr<dwarf::FrameEntry>> Entries;
using iterator = pointee_iterator<decltype(Entries)::const_iterator>;
/// Return the entry at the given offset or nullptr.
dwarf::FrameEntry *getEntryAtOffset(uint64_t Offset) const;
public:
DWARFDebugFrame(bool IsEH);
// If IsEH is true, assume it is a .eh_frame section. Otherwise,
// it is a .debug_frame section. EHFrameAddress should be different
// than zero for correct parsing of .eh_frame addresses when they
// use a PC-relative encoding.
DWARFDebugFrame(bool IsEH = false, uint64_t EHFrameAddress = 0);
~DWARFDebugFrame();
/// Dump the section data into the given stream.
void dump(raw_ostream &OS, Optional<uint64_t> Offset) const;
void dump(raw_ostream &OS, const MCRegisterInfo *MRI,
Optional<uint64_t> Offset) const;
/// \brief Parse the section from raw data.
/// data is assumed to be pointing to the beginning of the section.
void parse(DataExtractor Data);
/// Parse the section from raw data. \p Data is assumed to contain the whole
/// frame section contents to be parsed.
void parse(DWARFDataExtractor Data);
/// Return whether the section has any entries.
bool empty() const { return Entries.empty(); }
/// Return the entry at the given offset or nullptr.
FrameEntry *getEntryAtOffset(uint64_t Offset) const;
/// DWARF Frame entries accessors
iterator begin() const { return Entries.begin(); }
iterator end() const { return Entries.end(); }
iterator_range<iterator> entries() const {
return iterator_range<iterator>(Entries.begin(), Entries.end());
}
private:
std::vector<std::unique_ptr<FrameEntry>> Entries;
uint64_t getEHFrameAddress() const { return EHFrameAddress; }
};
} // end namespace llvm

View File

@ -93,12 +93,13 @@ public:
/// An iterator to go through the expression operations.
class iterator
: public iterator_facade_base<iterator, std::forward_iterator_tag, Operation> {
: public iterator_facade_base<iterator, std::forward_iterator_tag,
Operation> {
friend class DWARFExpression;
DWARFExpression *Expr;
const DWARFExpression *Expr;
uint32_t Offset;
Operation Op;
iterator(DWARFExpression *Expr, uint32_t Offset)
iterator(const DWARFExpression *Expr, uint32_t Offset)
: Expr(Expr), Offset(Offset) {
Op.Error =
Offset >= Expr->Data.getData().size() ||
@ -127,10 +128,11 @@ public:
assert(AddressSize == 8 || AddressSize == 4);
}
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, Data.getData().size()); }
iterator begin() const { return iterator(this, 0); }
iterator end() const { return iterator(this, Data.getData().size()); }
void print(raw_ostream &OS, const MCRegisterInfo *RegInfo);
void print(raw_ostream &OS, const MCRegisterInfo *RegInfo,
bool IsEH = false) const;
private:
DataExtractor Data;

View File

@ -80,6 +80,8 @@ public:
void resetIndent() { IndentLevel = 0; }
int getIndentLevel() { return IndentLevel; }
void setPrefix(StringRef P) { Prefix = P; }
void printIndent() {

View File

@ -349,11 +349,11 @@ void DWARFContext::dump(
if (shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame,
DObj->getDebugFrameSection()))
getDebugFrame()->dump(OS, DumpOffset);
getDebugFrame()->dump(OS, getRegisterInfo(), DumpOffset);
if (shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame,
DObj->getEHFrameSection()))
getEHFrame()->dump(OS, DumpOffset);
getEHFrame()->dump(OS, getRegisterInfo(), DumpOffset);
if (DumpType & DIDT_DebugMacro) {
if (Explicit || !getDebugMacro()->empty()) {
@ -712,8 +712,8 @@ const DWARFDebugFrame *DWARFContext::getDebugFrame() {
// provides this information). This problem is fixed in DWARFv4
// See this dwarf-discuss discussion for more details:
// http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html
DataExtractor debugFrameData(DObj->getDebugFrameSection(), isLittleEndian(),
DObj->getAddressSize());
DWARFDataExtractor debugFrameData(DObj->getDebugFrameSection(),
isLittleEndian(), DObj->getAddressSize());
DebugFrame.reset(new DWARFDebugFrame(false /* IsEH */));
DebugFrame->parse(debugFrameData);
return DebugFrame.get();
@ -723,8 +723,8 @@ const DWARFDebugFrame *DWARFContext::getEHFrame() {
if (EHFrame)
return EHFrame.get();
DataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(),
DObj->getAddressSize());
DWARFDataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(),
DObj->getAddressSize());
DebugFrame.reset(new DWARFDebugFrame(true /* IsEH */));
DebugFrame->parse(debugFrameData);
return DebugFrame.get();

View File

@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
using namespace llvm;
@ -25,3 +26,71 @@ uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint32_t *Off,
*SecNdx = Rel->SectionIndex;
return getUnsigned(Off, Size) + Rel->Value;
}
Optional<uint64_t>
DWARFDataExtractor::getEncodedPointer(uint32_t *Offset, uint8_t Encoding,
uint64_t PCRelOffset) const {
if (Encoding == dwarf::DW_EH_PE_omit)
return None;
uint64_t Result = 0;
uint32_t OldOffset = *Offset;
// First get value
switch (Encoding & 0x0F) {
case dwarf::DW_EH_PE_absptr:
switch (getAddressSize()) {
case 2:
case 4:
case 8:
Result = getUnsigned(Offset, getAddressSize());
break;
default:
return None;
}
break;
case dwarf::DW_EH_PE_uleb128:
Result = getULEB128(Offset);
break;
case dwarf::DW_EH_PE_sleb128:
Result = getSLEB128(Offset);
break;
case dwarf::DW_EH_PE_udata2:
Result = getUnsigned(Offset, 2);
break;
case dwarf::DW_EH_PE_udata4:
Result = getUnsigned(Offset, 4);
break;
case dwarf::DW_EH_PE_udata8:
Result = getUnsigned(Offset, 8);
break;
case dwarf::DW_EH_PE_sdata2:
Result = getSigned(Offset, 2);
break;
case dwarf::DW_EH_PE_sdata4:
Result = getSigned(Offset, 4);
break;
case dwarf::DW_EH_PE_sdata8:
Result = getSigned(Offset, 8);
break;
default:
return None;
}
// Then add relative offset, if required
switch (Encoding & 0x70) {
case dwarf::DW_EH_PE_absptr:
// do nothing
break;
case dwarf::DW_EH_PE_pcrel:
Result += PCRelOffset;
break;
case dwarf::DW_EH_PE_datarel:
case dwarf::DW_EH_PE_textrel:
case dwarf::DW_EH_PE_funcrel:
case dwarf::DW_EH_PE_aligned:
default:
*Offset = OldOffset;
return None;
}
return Result;
}

View File

@ -8,10 +8,8 @@
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
@ -31,87 +29,13 @@
using namespace llvm;
using namespace dwarf;
/// \brief Abstract frame entry defining the common interface concrete
/// entries implement.
class llvm::FrameEntry {
public:
enum FrameKind {FK_CIE, FK_FDE};
FrameEntry(FrameKind K, uint64_t Offset, uint64_t Length)
: Kind(K), Offset(Offset), Length(Length) {}
virtual ~FrameEntry() = default;
FrameKind getKind() const { return Kind; }
virtual uint64_t getOffset() const { return Offset; }
/// Parse and store a sequence of CFI instructions from Data,
/// starting at *Offset and ending at EndOffset. If everything
/// goes well, *Offset should be equal to EndOffset when this method
/// returns. Otherwise, an error occurred.
virtual void parseInstructions(DataExtractor Data, uint32_t *Offset,
uint32_t EndOffset);
/// Dump the entry header to the given output stream.
virtual void dumpHeader(raw_ostream &OS) const = 0;
/// Dump the entry's instructions to the given output stream.
virtual void dumpInstructions(raw_ostream &OS) const;
/// Dump the entire entry to the given output stream.
void dump(raw_ostream &OS) const {
dumpHeader(OS);
dumpInstructions(OS);
OS << "\n";
}
protected:
const FrameKind Kind;
/// \brief Offset of this entry in the section.
uint64_t Offset;
/// \brief Entry length as specified in DWARF.
uint64_t Length;
/// An entry may contain CFI instructions. An instruction consists of an
/// opcode and an optional sequence of operands.
using Operands = std::vector<uint64_t>;
struct Instruction {
Instruction(uint8_t Opcode)
: Opcode(Opcode)
{}
uint8_t Opcode;
Operands Ops;
};
std::vector<Instruction> Instructions;
/// Convenience methods to add a new instruction with the given opcode and
/// operands to the Instructions vector.
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);
}
};
// See DWARF standard v3, section 7.23
const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;
void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset,
uint32_t EndOffset) {
Error CFIProgram::parse(DataExtractor Data, uint32_t *Offset,
uint32_t EndOffset) {
while (*Offset < EndOffset) {
uint8_t Opcode = Data.getU8(Offset);
// Some instructions have a primary opcode encoded in the top bits.
@ -122,67 +46,73 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset,
// bits of the opcode itself.
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;
default:
return make_error<StringError>(
"Invalid primary CFI opcode",
std::make_error_code(std::errc::illegal_byte_sequence));
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 {
// Extended opcode - its value is Opcode itself.
switch (Opcode) {
default: llvm_unreachable("Invalid extended CFI opcode");
case DW_CFA_nop:
case DW_CFA_remember_state:
case DW_CFA_restore_state:
case DW_CFA_GNU_window_save:
// No operands
addInstruction(Opcode);
break;
case DW_CFA_set_loc:
// Operands: Address
addInstruction(Opcode, Data.getAddress(Offset));
break;
case DW_CFA_advance_loc1:
// Operands: 1-byte delta
addInstruction(Opcode, Data.getU8(Offset));
break;
case DW_CFA_advance_loc2:
// Operands: 2-byte delta
addInstruction(Opcode, Data.getU16(Offset));
break;
case DW_CFA_advance_loc4:
// Operands: 4-byte delta
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:
case DW_CFA_GNU_args_size:
// Operands: ULEB128
addInstruction(Opcode, Data.getULEB128(Offset));
break;
case DW_CFA_def_cfa_offset_sf:
// Operands: SLEB128
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: {
// Operands: ULEB128, ULEB128
// Note: We can not embed getULEB128 directly into function
// argument list. getULEB128 changes Offset and order of evaluation
// for arguments is unspecified.
auto op1 = Data.getULEB128(Offset);
auto op2 = Data.getULEB128(Offset);
addInstruction(Opcode, op1, op2);
break;
default:
return make_error<StringError>(
"Invalid extended CFI opcode",
std::make_error_code(std::errc::illegal_byte_sequence));
case DW_CFA_nop:
case DW_CFA_remember_state:
case DW_CFA_restore_state:
case DW_CFA_GNU_window_save:
// No operands
addInstruction(Opcode);
break;
case DW_CFA_set_loc:
// Operands: Address
addInstruction(Opcode, Data.getAddress(Offset));
break;
case DW_CFA_advance_loc1:
// Operands: 1-byte delta
addInstruction(Opcode, Data.getU8(Offset));
break;
case DW_CFA_advance_loc2:
// Operands: 2-byte delta
addInstruction(Opcode, Data.getU16(Offset));
break;
case DW_CFA_advance_loc4:
// Operands: 4-byte delta
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:
case DW_CFA_GNU_args_size:
// Operands: ULEB128
addInstruction(Opcode, Data.getULEB128(Offset));
break;
case DW_CFA_def_cfa_offset_sf:
// Operands: SLEB128
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: {
// Operands: ULEB128, ULEB128
// Note: We can not embed getULEB128 directly into function
// argument list. getULEB128 changes Offset and order of evaluation
// for arguments is unspecified.
auto op1 = Data.getULEB128(Offset);
auto op2 = Data.getULEB128(Offset);
addInstruction(Opcode, op1, op2);
break;
}
case DW_CFA_offset_extended_sf:
case DW_CFA_def_cfa_sf:
@ -194,162 +124,49 @@ void FrameEntry::parseInstructions(DataExtractor Data, uint32_t *Offset,
addInstruction(Opcode, op1, op2);
break;
}
case DW_CFA_def_cfa_expression:
// FIXME: Parse the actual instruction.
*Offset += Data.getULEB128(Offset);
case DW_CFA_def_cfa_expression: {
uint32_t ExprLength = Data.getULEB128(Offset);
addInstruction(Opcode, 0);
DataExtractor Extractor(
Data.getData().slice(*Offset, *Offset + ExprLength),
Data.isLittleEndian(), Data.getAddressSize());
Instructions.back().Expression = DWARFExpression(
Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION);
*Offset += ExprLength;
break;
}
case DW_CFA_expression:
case DW_CFA_val_expression: {
// FIXME: Parse the actual instruction.
Data.getULEB128(Offset);
*Offset += Data.getULEB128(Offset);
auto RegNum = Data.getULEB128(Offset);
auto BlockLength = Data.getULEB128(Offset);
addInstruction(Opcode, RegNum, 0);
DataExtractor Extractor(
Data.getData().slice(*Offset, *Offset + BlockLength),
Data.isLittleEndian(), Data.getAddressSize());
Instructions.back().Expression = DWARFExpression(
Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION);
*Offset += BlockLength;
break;
}
}
}
}
return Error::success();
}
namespace {
/// \brief DWARF Common Information Entry (CIE)
class CIE : public FrameEntry {
public:
// CIEs (and FDEs) are simply container classes, so the only sensible way to
// create them is by providing the full parsed contents in the constructor.
CIE(uint64_t Offset, uint64_t Length, uint8_t Version,
SmallString<8> Augmentation, uint8_t AddressSize,
uint8_t SegmentDescriptorSize, uint64_t CodeAlignmentFactor,
int64_t DataAlignmentFactor, uint64_t ReturnAddressRegister,
SmallString<8> AugmentationData, uint32_t FDEPointerEncoding,
uint32_t LSDAPointerEncoding)
: FrameEntry(FK_CIE, Offset, Length), Version(Version),
Augmentation(std::move(Augmentation)), AddressSize(AddressSize),
SegmentDescriptorSize(SegmentDescriptorSize),
CodeAlignmentFactor(CodeAlignmentFactor),
DataAlignmentFactor(DataAlignmentFactor),
ReturnAddressRegister(ReturnAddressRegister),
AugmentationData(std::move(AugmentationData)),
FDEPointerEncoding(FDEPointerEncoding),
LSDAPointerEncoding(LSDAPointerEncoding) {}
~CIE() override = default;
StringRef getAugmentationString() const { return Augmentation; }
uint64_t getCodeAlignmentFactor() const { return CodeAlignmentFactor; }
int64_t getDataAlignmentFactor() const { return DataAlignmentFactor; }
uint32_t getFDEPointerEncoding() const {
return FDEPointerEncoding;
}
uint32_t getLSDAPointerEncoding() const {
return LSDAPointerEncoding;
}
void dumpHeader(raw_ostream &OS) const override {
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";
if (Version >= 4) {
OS << format(" Address size: %u\n",
(uint32_t)AddressSize);
OS << format(" Segment desc size: %u\n",
(uint32_t)SegmentDescriptorSize);
}
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);
if (!AugmentationData.empty()) {
OS << " Augmentation data: ";
for (uint8_t Byte : AugmentationData)
OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf);
OS << "\n";
}
OS << "\n";
}
static bool classof(const FrameEntry *FE) {
return FE->getKind() == FK_CIE;
}
private:
/// The following fields are defined in section 6.4.1 of the DWARF standard v4
uint8_t Version;
SmallString<8> Augmentation;
uint8_t AddressSize;
uint8_t SegmentDescriptorSize;
uint64_t CodeAlignmentFactor;
int64_t DataAlignmentFactor;
uint64_t ReturnAddressRegister;
// The following are used when the CIE represents an EH frame entry.
SmallString<8> AugmentationData;
uint32_t FDEPointerEncoding;
uint32_t LSDAPointerEncoding;
};
/// \brief DWARF Frame Description Entry (FDE)
class FDE : public FrameEntry {
public:
// Each FDE has a CIE it's "linked to". Our FDE contains is constructed with
// an offset to the CIE (provided by parsing the FDE header). The CIE itself
// is obtained lazily once it's actually required.
FDE(uint64_t Offset, uint64_t Length, int64_t LinkedCIEOffset,
uint64_t InitialLocation, uint64_t AddressRange,
CIE *Cie)
: FrameEntry(FK_FDE, Offset, Length), LinkedCIEOffset(LinkedCIEOffset),
InitialLocation(InitialLocation), AddressRange(AddressRange),
LinkedCIE(Cie) {}
~FDE() override = default;
CIE *getLinkedCIE() const { return LinkedCIE; }
void dumpHeader(raw_ostream &OS) const override {
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);
}
static bool classof(const FrameEntry *FE) {
return FE->getKind() == FK_FDE;
}
private:
/// The following fields are defined in section 6.4.1 of the DWARF standard v3
uint64_t LinkedCIEOffset;
uint64_t InitialLocation;
uint64_t AddressRange;
CIE *LinkedCIE;
};
/// \brief Types of operands to CF instructions.
enum OperandType {
OT_Unset,
OT_None,
OT_Address,
OT_Offset,
OT_FactoredCodeOffset,
OT_SignedFactDataOffset,
OT_UnsignedFactDataOffset,
OT_Register,
OT_Expression
};
} // end anonymous namespace
/// \brief Initialize the array describing the types of operands.
static ArrayRef<OperandType[2]> getOperandTypes() {
ArrayRef<CFIProgram::OperandType[2]> CFIProgram::getOperandTypes() {
static OperandType OpTypes[DW_CFA_restore+1][2];
static bool Initialized = false;
if (Initialized) {
return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1);
}
Initialized = true;
#define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \
do { \
@ -396,15 +213,13 @@ static ArrayRef<OperandType[2]> getOperandTypes() {
return ArrayRef<OperandType[2]>(&OpTypes[0], DW_CFA_restore+1);
}
static ArrayRef<OperandType[2]> OpTypes = getOperandTypes();
/// \brief Print \p Opcode's operand number \p OperandIdx which has
/// value \p Operand.
static void printOperand(raw_ostream &OS, uint8_t Opcode, unsigned OperandIdx,
uint64_t Operand, uint64_t CodeAlignmentFactor,
int64_t DataAlignmentFactor) {
/// Print \p Opcode's operand number \p OperandIdx which has value \p Operand.
void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI,
bool IsEH, const Instruction &Instr,
unsigned OperandIdx, uint64_t Operand) const {
assert(OperandIdx < 2);
OperandType Type = OpTypes[Opcode][OperandIdx];
uint8_t Opcode = Instr.Opcode;
OperandType Type = getOperandTypes()[Opcode][OperandIdx];
switch (Type) {
case OT_Unset: {
@ -449,36 +264,68 @@ static void printOperand(raw_ostream &OS, uint8_t Opcode, unsigned OperandIdx,
OS << format(" reg%" PRId64, Operand);
break;
case OT_Expression:
OS << " expression";
assert(Instr.Expression && "missing DWARFExpression object");
OS << " ";
Instr.Expression->print(OS, MRI, IsEH);
break;
}
}
void FrameEntry::dumpInstructions(raw_ostream &OS) const {
uint64_t CodeAlignmentFactor = 0;
int64_t DataAlignmentFactor = 0;
const CIE *Cie = dyn_cast<CIE>(this);
if (!Cie)
Cie = cast<FDE>(this)->getLinkedCIE();
if (Cie) {
CodeAlignmentFactor = Cie->getCodeAlignmentFactor();
DataAlignmentFactor = Cie->getDataAlignmentFactor();
}
void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH,
unsigned IndentLevel) const {
for (const auto &Instr : Instructions) {
uint8_t Opcode = Instr.Opcode;
if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK)
Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK;
OS << " " << CallFrameString(Opcode) << ":";
OS.indent(2 * IndentLevel);
OS << CallFrameString(Opcode) << ":";
for (unsigned i = 0; i < Instr.Ops.size(); ++i)
printOperand(OS, Opcode, i, Instr.Ops[i], CodeAlignmentFactor,
DataAlignmentFactor);
printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]);
OS << '\n';
}
}
DWARFDebugFrame::DWARFDebugFrame(bool IsEH) : IsEH(IsEH) {}
void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) 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";
if (Version >= 4) {
OS << format(" Address size: %u\n", (uint32_t)AddressSize);
OS << format(" Segment desc size: %u\n",
(uint32_t)SegmentDescriptorSize);
}
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);
if (Personality)
OS << format(" Personality Address: %08x\n", *Personality);
if (!AugmentationData.empty()) {
OS << " Augmentation data: ";
for (uint8_t Byte : AugmentationData)
OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf);
OS << "\n";
}
OS << "\n";
CFIs.dump(OS, MRI, IsEH);
OS << "\n";
}
void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) 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 (LSDAAddress)
OS << format(" LSDA Address: %08x\n", *LSDAAddress);
CFIs.dump(OS, MRI, IsEH);
OS << "\n";
}
DWARFDebugFrame::DWARFDebugFrame(bool IsEH, uint64_t EHFrameAddress)
: IsEH(IsEH), EHFrameAddress(EHFrameAddress) {}
DWARFDebugFrame::~DWARFDebugFrame() = default;
@ -492,40 +339,6 @@ static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data,
errs() << "\n";
}
static unsigned getSizeForEncoding(const DataExtractor &Data,
unsigned symbolEncoding) {
unsigned format = symbolEncoding & 0x0f;
switch (format) {
default: llvm_unreachable("Unknown Encoding");
case DW_EH_PE_absptr:
case DW_EH_PE_signed:
return Data.getAddressSize();
case DW_EH_PE_udata2:
case DW_EH_PE_sdata2:
return 2;
case DW_EH_PE_udata4:
case DW_EH_PE_sdata4:
return 4;
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
return 8;
}
}
static uint64_t readPointer(const DataExtractor &Data, uint32_t &Offset,
unsigned Encoding) {
switch (getSizeForEncoding(Data, Encoding)) {
case 2:
return Data.getU16(&Offset);
case 4:
return Data.getU32(&Offset);
case 8:
return Data.getU64(&Offset);
default:
llvm_unreachable("Illegal data size");
}
}
// This is a workaround for old compilers which do not allow
// noreturn attribute usage in lambdas. Once the support for those
// compilers are phased out, we can remove this and return back to
@ -539,7 +352,7 @@ static void LLVM_ATTRIBUTE_NORETURN ReportError(uint32_t StartOffset,
report_fatal_error(Str);
}
void DWARFDebugFrame::parse(DataExtractor Data) {
void DWARFDebugFrame::parse(DWARFDataExtractor Data) {
uint32_t Offset = 0;
DenseMap<uint32_t, CIE *> CIEs;
@ -569,9 +382,8 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
// The Id field's size depends on the DWARF format
Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4);
bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) ||
Id == DW_CIE_ID ||
(IsEH && !Id));
bool IsCIE =
((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id));
if (IsCIE) {
uint8_t Version = Data.getU8(&Offset);
@ -589,10 +401,9 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
StringRef AugmentationData("");
uint32_t FDEPointerEncoding = DW_EH_PE_omit;
uint32_t LSDAPointerEncoding = DW_EH_PE_omit;
Optional<uint64_t> Personality;
Optional<uint32_t> PersonalityEncoding;
if (IsEH) {
Optional<uint32_t> PersonalityEncoding;
Optional<uint64_t> Personality;
Optional<uint64_t> AugmentationLength;
uint32_t StartAugmentationOffset;
uint32_t EndAugmentationOffset;
@ -611,7 +422,9 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
ReportError(StartOffset,
"Duplicate personality in entry at %lx");
PersonalityEncoding = Data.getU8(&Offset);
Personality = readPointer(Data, Offset, *PersonalityEncoding);
Personality = Data.getEncodedPointer(
&Offset, *PersonalityEncoding,
EHFrameAddress ? EHFrameAddress + Offset : 0);
break;
}
case 'R':
@ -639,14 +452,11 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
}
}
auto Cie = llvm::make_unique<CIE>(StartOffset, Length, Version,
AugmentationString, AddressSize,
SegmentDescriptorSize,
CodeAlignmentFactor,
DataAlignmentFactor,
ReturnAddressRegister,
AugmentationData, FDEPointerEncoding,
LSDAPointerEncoding);
auto Cie = llvm::make_unique<CIE>(
StartOffset, Length, Version, AugmentationString, AddressSize,
SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor,
ReturnAddressRegister, AugmentationData, FDEPointerEncoding,
LSDAPointerEncoding, Personality, PersonalityEncoding);
CIEs[StartOffset] = Cie.get();
Entries.emplace_back(std::move(Cie));
} else {
@ -654,6 +464,7 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
uint64_t CIEPointer = Id;
uint64_t InitialLocation = 0;
uint64_t AddressRange = 0;
Optional<uint64_t> LSDAAddress;
CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer];
if (IsEH) {
@ -662,10 +473,15 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
ReportError(StartOffset,
"Parsing FDE data at %lx failed due to missing CIE");
InitialLocation = readPointer(Data, Offset,
Cie->getFDEPointerEncoding());
AddressRange = readPointer(Data, Offset,
Cie->getFDEPointerEncoding());
if (auto Val = Data.getEncodedPointer(
&Offset, Cie->getFDEPointerEncoding(),
EHFrameAddress ? EHFrameAddress + Offset : 0)) {
InitialLocation = *Val;
}
if (auto Val = Data.getEncodedPointer(
&Offset, Cie->getFDEPointerEncoding(), 0)) {
AddressRange = *Val;
}
StringRef AugmentationString = Cie->getAugmentationString();
if (!AugmentationString.empty()) {
@ -676,8 +492,11 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
Offset + static_cast<uint32_t>(AugmentationLength);
// Decode the LSDA if the CIE augmentation string said we should.
if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit)
readPointer(Data, Offset, Cie->getLSDAPointerEncoding());
if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) {
LSDAAddress = Data.getEncodedPointer(
&Offset, Cie->getLSDAPointerEncoding(),
EHFrameAddress ? Offset + EHFrameAddress : 0);
}
if (Offset != EndAugmentationOffset)
ReportError(StartOffset, "Parsing augmentation data at %lx failed");
@ -689,10 +508,13 @@ void DWARFDebugFrame::parse(DataExtractor Data) {
Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer,
InitialLocation, AddressRange,
Cie));
Cie, LSDAAddress));
}
Entries.back()->parseInstructions(Data, &Offset, EndStructureOffset);
if (Error E =
Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) {
report_fatal_error(toString(std::move(E)));
}
if (Offset != EndStructureOffset)
ReportError(StartOffset, "Parsing entry instructions at %lx failed");
@ -709,14 +531,15 @@ FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const {
return nullptr;
}
void DWARFDebugFrame::dump(raw_ostream &OS, Optional<uint64_t> Offset) const {
void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI,
Optional<uint64_t> Offset) const {
if (Offset) {
if (auto *Entry = getEntryAtOffset(*Offset))
Entry->dump(OS);
Entry->dump(OS, MRI, IsEH);
return;
}
OS << "\n";
for (const auto &Entry : Entries)
Entry->dump(OS);
Entry->dump(OS, MRI, IsEH);
}

View File

@ -258,9 +258,10 @@ bool DWARFExpression::Operation::print(raw_ostream &OS,
return true;
}
void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo) {
void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo,
bool IsEH) const {
for (auto &Op : *this) {
if (!Op.print(OS, this, RegInfo, /* isEH */ false)) {
if (!Op.print(OS, this, RegInfo, IsEH)) {
uint32_t FailOffset = Op.getEndOffset();
while (FailOffset < Data.getData().size())
OS << format(" %02x", Data.getU8(&FailOffset));

View File

@ -50,6 +50,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_PT>::enumeration(
ECase(PT_SHLIB);
ECase(PT_PHDR);
ECase(PT_TLS);
ECase(PT_GNU_EH_FRAME);
#undef ECase
IO.enumFallback<Hex32>(Value);
}

View File

@ -0,0 +1,46 @@
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_EXEC
Machine: EM_X86_64
Entry: 0x0000000000400000
Sections:
- Name: .text
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
Address: 0x0000000000400000
AddressAlign: 16
Content: 50C704240020400031C05AC3
- Name: .eh_frame_hdr
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x00000000004013c0
AddressAlign: 4
Content: 011B033B3C00000006000000E0F0FFFF8800000010F1FFFF58000000F6F1FFFFB000000010F2FFFFD000000090FEFFFF0001000000FFFFFF30010000
- Name: .eh_frame
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x0000000000401400
AddressAlign: 8
Content: 1400000000000000017A5200017810011B0C070890010710140000001C000000B0F0FFFF2A00000000000000000000001400000000000000017A5200017810011B0C070890010000240000001C00000050F0FFFF20000000000E10460E184A0F0B770880003F1A3B2A332422000000001C000000440000003EF1FFFF1000000000410E108602430D064B0C07080000002C0000006400000038F1FFFF7F0C000000450C0A00491006027600450F0376780603660C0C0A00450C070800000000002C0000009400000088FDFFFF6600000000410E108602430D06428F03458E04478D058C06488307024B0C07080000000014000000C4000000C8FDFFFF01000000000000000000000000000000
Symbols:
Global:
- Name: myfunc
Type: STT_FUNC
Section: .text
Value: 0x0000000000400000
ProgramHeaders:
- Type: PT_LOAD
Flags: [ PF_X, PF_R ]
VAddr: 0x00400000
PAddr: 0x00400000
Sections:
- Section: .text
- Type: PT_GNU_EH_FRAME
Flags: [ PF_X, PF_R ]
VAddr: 0x004013C0
PAddr: 0x004013C0
Sections:
- Section: .eh_frame_hdr
...

View File

@ -0,0 +1,170 @@
RUN: yaml2obj %p/Inputs/dwarf-exprs.exe-x86-64.yaml > %t.exe
RUN: llvm-readobj -unwind %t.exe | FileCheck %s
CHECK: EH_FRAME Header [
CHECK-NEXT: Address: 0x4013c0
CHECK-NEXT: Offset: 0x27c
CHECK-NEXT: Size: 0x3c
CHECK-NEXT: Corresponding Section: .eh_frame_hdr
CHECK-NEXT: Header {
CHECK-NEXT: version: 1
CHECK-NEXT: eh_frame_ptr_enc: 0x1b
CHECK-NEXT: fde_count_enc: 0x3
CHECK-NEXT: table_enc: 0x3b
CHECK-NEXT: eh_frame_ptr: 0x401400
CHECK-NEXT: fde_count: 6
CHECK-NEXT: entry 0 {
CHECK-NEXT: initial_location: 0x4004a0
CHECK-NEXT: address: 0x401448
CHECK-NEXT: }
CHECK-NEXT: entry 1 {
CHECK-NEXT: initial_location: 0x4004d0
CHECK-NEXT: address: 0x401418
CHECK-NEXT: }
CHECK-NEXT: entry 2 {
CHECK-NEXT: initial_location: 0x4005b6
CHECK-NEXT: address: 0x401470
CHECK-NEXT: }
CHECK-NEXT: entry 3 {
CHECK-NEXT: initial_location: 0x4005d0
CHECK-NEXT: address: 0x401490
CHECK-NEXT: }
CHECK-NEXT: entry 4 {
CHECK-NEXT: initial_location: 0x401250
CHECK-NEXT: address: 0x4014c0
CHECK-NEXT: }
CHECK-NEXT: entry 5 {
CHECK-NEXT: initial_location: 0x4012c0
CHECK-NEXT: address: 0x4014f0
CHECK-NEXT: }
CHECK-NEXT: }
CHECK-NEXT:]
CHECK: .eh_frame section at offset 0x2b8 address 0x401400:
CHECK-NEXT: [0x401400] CIE length=20
CHECK-NEXT: version: 1
CHECK-NEXT: augmentation: zR
CHECK-NEXT: code_alignment_factor: 1
CHECK-NEXT: data_alignment_factor: -8
CHECK-NEXT: return_address_register: 16
CHECK: Program:
CHECK-NEXT: DW_CFA_def_cfa: reg7 +8
CHECK-NEXT: DW_CFA_offset: reg16 -8
CHECK-NEXT: DW_CFA_undefined: reg16
CHECK: [0x401418] FDE length=20 cie=[0x401400]
CHECK-NEXT: initial_location: 0x4004d0
CHECK-NEXT: address_range: 0x2a (end : 0x4004fa)
CHECK: Program:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK: [0x401430] CIE length=20
CHECK-NEXT: version: 1
CHECK-NEXT: augmentation: zR
CHECK-NEXT: code_alignment_factor: 1
CHECK-NEXT: data_alignment_factor: -8
CHECK-NEXT: return_address_register: 16
CHECK: Program:
CHECK-NEXT: DW_CFA_def_cfa: reg7 +8
CHECK-NEXT: DW_CFA_offset: reg16 -8
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK: [0x401448] FDE length=36 cie=[0x401430]
CHECK-NEXT: initial_location: 0x4004a0
CHECK-NEXT: address_range: 0x20 (end : 0x4004c0)
CHECK: Program:
CHECK-NEXT: DW_CFA_def_cfa_offset: +16
CHECK-NEXT: DW_CFA_advance_loc: 6
CHECK-NEXT: DW_CFA_def_cfa_offset: +24
CHECK-NEXT: DW_CFA_advance_loc: 10
CHECK-NEXT: DW_CFA_def_cfa_expression: DW_OP_breg7 +8, DW_OP_breg16 +0, DW_OP_lit15, DW_OP_and, DW_OP_lit11, DW_OP_ge, DW_OP_lit3, DW_OP_shl, DW_OP_plus
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK: [0x401470] FDE length=28 cie=[0x401430]
CHECK-NEXT: initial_location: 0x4005b6
CHECK-NEXT: address_range: 0x10 (end : 0x4005c6)
CHECK: Program:
CHECK-NEXT: DW_CFA_advance_loc: 1
CHECK-NEXT: DW_CFA_def_cfa_offset: +16
CHECK-NEXT: DW_CFA_offset: reg6 -16
CHECK-NEXT: DW_CFA_advance_loc: 3
CHECK-NEXT: DW_CFA_def_cfa_register: reg6
CHECK-NEXT: DW_CFA_advance_loc: 11
CHECK-NEXT: DW_CFA_def_cfa: reg7 +8
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK: [0x401490] FDE length=44 cie=[0x401430]
CHECK-NEXT: initial_location: 0x4005d0
CHECK-NEXT: address_range: 0xc7f (end : 0x40124f)
CHECK: Program:
CHECK-NEXT: DW_CFA_advance_loc: 5
CHECK-NEXT: DW_CFA_def_cfa: reg10 +0
CHECK-NEXT: DW_CFA_advance_loc: 9
CHECK-NEXT: DW_CFA_expression: reg6 DW_OP_breg6 +0
CHECK-NEXT: DW_CFA_advance_loc: 5
CHECK-NEXT: DW_CFA_def_cfa_expression: DW_OP_breg6 -8, DW_OP_deref
CHECK-NEXT: DW_CFA_advance_loc2: 3174
CHECK-NEXT: DW_CFA_def_cfa: reg10 +0
CHECK-NEXT: DW_CFA_advance_loc: 5
CHECK-NEXT: DW_CFA_def_cfa: reg7 +8
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK: [0x4014c0] FDE length=44 cie=[0x401430]
CHECK-NEXT: initial_location: 0x401250
CHECK-NEXT: address_range: 0x66 (end : 0x4012b6)
CHECK: Program:
CHECK-NEXT: DW_CFA_advance_loc: 1
CHECK-NEXT: DW_CFA_def_cfa_offset: +16
CHECK-NEXT: DW_CFA_offset: reg6 -16
CHECK-NEXT: DW_CFA_advance_loc: 3
CHECK-NEXT: DW_CFA_def_cfa_register: reg6
CHECK-NEXT: DW_CFA_advance_loc: 2
CHECK-NEXT: DW_CFA_offset: reg15 -24
CHECK-NEXT: DW_CFA_advance_loc: 5
CHECK-NEXT: DW_CFA_offset: reg14 -32
CHECK-NEXT: DW_CFA_advance_loc: 7
CHECK-NEXT: DW_CFA_offset: reg13 -40
CHECK-NEXT: DW_CFA_offset: reg12 -48
CHECK-NEXT: DW_CFA_advance_loc: 8
CHECK-NEXT: DW_CFA_offset: reg3 -56
CHECK-NEXT: DW_CFA_advance_loc1: 75
CHECK-NEXT: DW_CFA_def_cfa: reg7 +8
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK: [0x4014f0] FDE length=20 cie=[0x401430]
CHECK-NEXT: initial_location: 0x4012c0
CHECK-NEXT: address_range: 0x1 (end : 0x4012c1)
CHECK: Program:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:
CHECK-NEXT: DW_CFA_nop:

View File

@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
DebugInfoCodeView
DebugInfoDWARF
Object
BinaryFormat
Support

View File

@ -0,0 +1,244 @@
//===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H
#define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H
#include "Error.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFTypes.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/Debug.h"
#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/type_traits.h"
namespace llvm {
namespace DwarfCFIEH {
template <typename ELFT>
class PrinterContext {
ScopedPrinter &W;
const object::ELFFile<ELFT> *Obj;
void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const;
void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const;
public:
PrinterContext(ScopedPrinter &W, const object::ELFFile<ELFT> *Obj)
: W(W), Obj(Obj) {}
void printUnwindInformation() const;
};
template <class ELFO>
static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj,
uint64_t Addr) {
auto Sections = Obj->sections();
if (Error E = Sections.takeError())
reportError(toString(std::move(E)));
for (const auto &Shdr : *Sections)
if (Shdr.sh_addr == Addr)
return &Shdr;
return nullptr;
}
template <typename ELFT>
void PrinterContext<ELFT>::printUnwindInformation() const {
const typename ELFT::Phdr *EHFramePhdr = nullptr;
auto PHs = Obj->program_headers();
if (Error E = PHs.takeError())
reportError(toString(std::move(E)));
for (const auto &Phdr : *PHs) {
if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) {
EHFramePhdr = &Phdr;
if (Phdr.p_memsz != Phdr.p_filesz)
reportError("p_memsz does not match p_filesz for GNU_EH_FRAME");
break;
}
}
if (EHFramePhdr)
printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr,
EHFramePhdr->p_memsz);
auto Sections = Obj->sections();
if (Error E = Sections.takeError())
reportError(toString(std::move(E)));
for (const auto &Shdr : *Sections) {
auto SectionName = Obj->getSectionName(&Shdr);
if (Error E = SectionName.takeError())
reportError(toString(std::move(E)));
if (*SectionName == ".eh_frame")
printEHFrame(&Shdr);
}
}
template <typename ELFT>
void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
uint64_t EHFrameHdrAddress,
uint64_t EHFrameHdrSize) const {
ListScope L(W, "EH_FRAME Header");
W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress);
W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset);
W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize);
const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress);
if (EHFrameHdrShdr) {
auto SectionName = Obj->getSectionName(EHFrameHdrShdr);
if (Error E = SectionName.takeError())
reportError(toString(std::move(E)));
W.printString("Corresponding Section", *SectionName);
}
DataExtractor DE(
StringRef(reinterpret_cast<const char *>(Obj->base()) + EHFrameHdrOffset,
EHFrameHdrSize),
ELFT::TargetEndianness == support::endianness::little,
ELFT::Is64Bits ? 8 : 4);
DictScope D(W, "Header");
uint32_t Offset = 0;
auto Version = DE.getU8(&Offset);
W.printNumber("version", Version);
if (Version != 1)
reportError("only version 1 of .eh_frame_hdr is supported");
uint64_t EHFramePtrEnc = DE.getU8(&Offset);
W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc);
if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4))
reportError("unexpected encoding eh_frame_ptr_enc");
uint64_t FDECountEnc = DE.getU8(&Offset);
W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc);
if (FDECountEnc != dwarf::DW_EH_PE_udata4)
reportError("unexpected encoding fde_count_enc");
uint64_t TableEnc = DE.getU8(&Offset);
W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc);
if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4))
reportError("unexpected encoding table_enc");
auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4;
W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr);
auto FDECount = DE.getUnsigned(&Offset, 4);
W.printNumber("fde_count", FDECount);
unsigned NumEntries = 0;
uint64_t PrevPC = 0;
while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) {
DictScope D(W, std::string("entry ") + std::to_string(NumEntries));
auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
W.startLine() << format("initial_location: 0x%" PRIx64 "\n", InitialPC);
auto Address = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
W.startLine() << format("address: 0x%" PRIx64 "\n", Address);
if (InitialPC < PrevPC)
reportError("initial_location is out of order");
PrevPC = InitialPC;
++NumEntries;
}
}
template <typename ELFT>
void PrinterContext<ELFT>::printEHFrame(
const typename ELFT::Shdr *EHFrameShdr) const {
uint64_t Address = EHFrameShdr->sh_addr;
uint64_t ShOffset = EHFrameShdr->sh_offset;
W.startLine() << format(".eh_frame section at offset 0x%" PRIx64
" address 0x%" PRIx64 ":\n",
ShOffset, Address);
W.indent();
auto Result = Obj->getSectionContents(EHFrameShdr);
if (Error E = Result.takeError())
reportError(toString(std::move(E)));
auto Contents = Result.get();
DWARFDataExtractor DE(
StringRef(reinterpret_cast<const char *>(Contents.data()),
Contents.size()),
ELFT::TargetEndianness == support::endianness::little,
ELFT::Is64Bits ? 8 : 4);
DWARFDebugFrame EHFrame(/*IsEH=*/true, /*EHFrameAddress=*/Address);
EHFrame.parse(DE);
for (const auto &Entry : EHFrame) {
if (const auto *CIE = dyn_cast<dwarf::CIE>(&Entry)) {
W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n",
Address + CIE->getOffset(),
CIE->getLength());
W.indent();
W.printNumber("version", CIE->getVersion());
W.printString("augmentation", CIE->getAugmentationString());
W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor());
W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor());
W.printNumber("return_address_register", CIE->getReturnAddressRegister());
W.getOStream() << "\n";
W.startLine() << "Program:\n";
W.indent();
CIE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
W.unindent();
W.unindent();
W.getOStream() << "\n";
} else if (const auto *FDE = dyn_cast<dwarf::FDE>(&Entry)) {
W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64
" cie=[0x%" PRIx64 "]\n",
Address + FDE->getOffset(),
FDE->getLength(),
Address + FDE->getLinkedCIE()->getOffset());
W.indent();
W.startLine() << format("initial_location: 0x%" PRIx64 "\n",
FDE->getInitialLocation());
W.startLine()
<< format("address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
FDE->getAddressRange(),
FDE->getInitialLocation() + FDE->getAddressRange());
W.getOStream() << "\n";
W.startLine() << "Program:\n";
W.indent();
FDE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
W.unindent();
W.unindent();
W.getOStream() << "\n";
} else {
llvm_unreachable("unexpected DWARF frame kind");
}
}
W.unindent();
}
}
}
#endif

View File

@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "ARMEHABIPrinter.h"
#include "DwarfCFIEHPrinter.h"
#include "Error.h"
#include "ObjDumper.h"
#include "StackMapPrinter.h"
@ -1808,6 +1809,11 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
template<class ELFT>
void ELFDumper<ELFT>::printUnwindInfo() {
const unsigned Machine = Obj->getHeader()->e_machine;
if (Machine == EM_386 || Machine == EM_X86_64) {
DwarfCFIEH::PrinterContext<ELFT> Ctx(W, Obj);
return Ctx.printUnwindInformation();
}
W.startLine() << "UnwindInfo not implemented.\n";
}