From 0e7b0fcc48a255bd22126c8f3c0c04d5ab663ae7 Mon Sep 17 00:00:00 2001 From: Rafael Auler Date: Thu, 8 Mar 2018 00:46:53 +0000 Subject: [PATCH] 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 --- .../llvm/DebugInfo/DWARF/DWARFDataExtractor.h | 7 + .../llvm/DebugInfo/DWARF/DWARFDebugFrame.h | 278 ++++++++- .../llvm/DebugInfo/DWARF/DWARFExpression.h | 14 +- include/llvm/Support/ScopedPrinter.h | 2 + lib/DebugInfo/DWARF/DWARFContext.cpp | 12 +- lib/DebugInfo/DWARF/DWARFDataExtractor.cpp | 69 +++ lib/DebugInfo/DWARF/DWARFDebugFrame.cpp | 545 ++++++------------ lib/DebugInfo/DWARF/DWARFExpression.cpp | 5 +- lib/ObjectYAML/ELFYAML.cpp | 1 + .../Inputs/dwarf-exprs.exe-x86-64.yaml | 46 ++ test/tools/llvm-readobj/unwind.test | 170 ++++++ tools/llvm-readobj/CMakeLists.txt | 1 + tools/llvm-readobj/DwarfCFIEHPrinter.h | 244 ++++++++ tools/llvm-readobj/ELFDumper.cpp | 6 + 14 files changed, 1011 insertions(+), 389 deletions(-) create mode 100644 test/tools/llvm-readobj/Inputs/dwarf-exprs.exe-x86-64.yaml create mode 100644 test/tools/llvm-readobj/unwind.test create mode 100644 tools/llvm-readobj/DwarfCFIEHPrinter.h diff --git a/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h b/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h index a379d9c85b3..10e146b70ec 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h +++ b/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h @@ -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 getEncodedPointer(uint32_t *Offset, uint8_t Encoding, + uint64_t AbsPosOffset = 0) const; }; } // end namespace llvm diff --git a/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index a711fb29544..ff1c7fb3838 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -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 #include 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 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 Expression; + }; + + using InstrList = std::vector; + 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 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 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 Personality, + Optional 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 getPersonalityAddress() const { return Personality; } + Optional 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 Personality; + const Optional 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 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 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 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> Entries; + using iterator = pointee_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 Offset) const; + void dump(raw_ostream &OS, const MCRegisterInfo *MRI, + Optional 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 entries() const { + return iterator_range(Entries.begin(), Entries.end()); + } -private: - std::vector> Entries; + uint64_t getEHFrameAddress() const { return EHFrameAddress; } }; } // end namespace llvm diff --git a/include/llvm/DebugInfo/DWARF/DWARFExpression.h b/include/llvm/DebugInfo/DWARF/DWARFExpression.h index dcd486f3fb1..3fad68a9b48 100644 --- a/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ b/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -93,12 +93,13 @@ public: /// An iterator to go through the expression operations. class iterator - : public iterator_facade_base { + : public iterator_facade_base { 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; diff --git a/include/llvm/Support/ScopedPrinter.h b/include/llvm/Support/ScopedPrinter.h index 1c22da69371..964d2543f24 100644 --- a/include/llvm/Support/ScopedPrinter.h +++ b/include/llvm/Support/ScopedPrinter.h @@ -80,6 +80,8 @@ public: void resetIndent() { IndentLevel = 0; } + int getIndentLevel() { return IndentLevel; } + void setPrefix(StringRef P) { Prefix = P; } void printIndent() { diff --git a/lib/DebugInfo/DWARF/DWARFContext.cpp b/lib/DebugInfo/DWARF/DWARFContext.cpp index de7ef662afb..2b1c91ee7b0 100644 --- a/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -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(); diff --git a/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp index 861dd313fb0..03e31746139 100644 --- a/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp +++ b/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -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 +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; +} diff --git a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 3312da67804..b9dc2151e06 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -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; - struct Instruction { - Instruction(uint8_t Opcode) - : Opcode(Opcode) - {} - - uint8_t Opcode; - Operands Ops; - }; - - std::vector 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( + "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( + "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 getOperandTypes() { +ArrayRef CFIProgram::getOperandTypes() { static OperandType OpTypes[DW_CFA_restore+1][2]; + static bool Initialized = false; + if (Initialized) { + return ArrayRef(&OpTypes[0], DW_CFA_restore+1); + } + Initialized = true; #define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \ do { \ @@ -396,15 +213,13 @@ static ArrayRef getOperandTypes() { return ArrayRef(&OpTypes[0], DW_CFA_restore+1); } -static ArrayRef 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(this); - - if (!Cie) - Cie = cast(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 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 Personality; + Optional PersonalityEncoding; if (IsEH) { - Optional PersonalityEncoding; - Optional Personality; - Optional 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(StartOffset, Length, Version, - AugmentationString, AddressSize, - SegmentDescriptorSize, - CodeAlignmentFactor, - DataAlignmentFactor, - ReturnAddressRegister, - AugmentationData, FDEPointerEncoding, - LSDAPointerEncoding); + auto Cie = llvm::make_unique( + 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 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(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 Offset) const { +void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, + Optional 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); } diff --git a/lib/DebugInfo/DWARF/DWARFExpression.cpp b/lib/DebugInfo/DWARF/DWARFExpression.cpp index c704c2901ae..a9ea26c476c 100644 --- a/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -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)); diff --git a/lib/ObjectYAML/ELFYAML.cpp b/lib/ObjectYAML/ELFYAML.cpp index 928b7b2b1c2..642a89fe930 100644 --- a/lib/ObjectYAML/ELFYAML.cpp +++ b/lib/ObjectYAML/ELFYAML.cpp @@ -50,6 +50,7 @@ void ScalarEnumerationTraits::enumeration( ECase(PT_SHLIB); ECase(PT_PHDR); ECase(PT_TLS); + ECase(PT_GNU_EH_FRAME); #undef ECase IO.enumFallback(Value); } diff --git a/test/tools/llvm-readobj/Inputs/dwarf-exprs.exe-x86-64.yaml b/test/tools/llvm-readobj/Inputs/dwarf-exprs.exe-x86-64.yaml new file mode 100644 index 00000000000..5b8f3671bc3 --- /dev/null +++ b/test/tools/llvm-readobj/Inputs/dwarf-exprs.exe-x86-64.yaml @@ -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 +... diff --git a/test/tools/llvm-readobj/unwind.test b/test/tools/llvm-readobj/unwind.test new file mode 100644 index 00000000000..72c591048c8 --- /dev/null +++ b/test/tools/llvm-readobj/unwind.test @@ -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: diff --git a/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt index dafc9e10cfa..b0550f34012 100644 --- a/tools/llvm-readobj/CMakeLists.txt +++ b/tools/llvm-readobj/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS DebugInfoCodeView + DebugInfoDWARF Object BinaryFormat Support diff --git a/tools/llvm-readobj/DwarfCFIEHPrinter.h b/tools/llvm-readobj/DwarfCFIEHPrinter.h new file mode 100644 index 00000000000..6f1e35ced94 --- /dev/null +++ b/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -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 +class PrinterContext { + ScopedPrinter &W; + const object::ELFFile *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 *Obj) + : W(W), Obj(Obj) {} + + void printUnwindInformation() const; +}; + +template +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 +void PrinterContext::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 +void PrinterContext::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(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 +void PrinterContext::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(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(&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(&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 diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 36cda459225..6b6582b7566 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -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::printValue(uint64_t Type, uint64_t Value) { template void ELFDumper::printUnwindInfo() { + const unsigned Machine = Obj->getHeader()->e_machine; + if (Machine == EM_386 || Machine == EM_X86_64) { + DwarfCFIEH::PrinterContext Ctx(W, Obj); + return Ctx.printUnwindInformation(); + } W.startLine() << "UnwindInfo not implemented.\n"; }