diff options
Diffstat (limited to 'llvm/tools/llvm-readobj')
21 files changed, 13765 insertions, 0 deletions
diff --git a/llvm/tools/llvm-readobj/ARMEHABIPrinter.h b/llvm/tools/llvm-readobj/ARMEHABIPrinter.h new file mode 100644 index 000000000000..2c0912038c31 --- /dev/null +++ b/llvm/tools/llvm-readobj/ARMEHABIPrinter.h @@ -0,0 +1,592 @@ +//===--- ARMEHABIPrinter.h - ARM EHABI Unwind Information Printer ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H +#define LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H + +#include "Error.h" +#include "llvm-readobj.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/ARMEHABI.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/type_traits.h" + +namespace llvm { +namespace ARM { +namespace EHABI { + +class OpcodeDecoder { + ScopedPrinter &SW; + raw_ostream &OS; + + struct RingEntry { + uint8_t Mask; + uint8_t Value; + void (OpcodeDecoder::*Routine)(const uint8_t *Opcodes, unsigned &OI); + }; + static ArrayRef<RingEntry> ring(); + + void Decode_00xxxxxx(const uint8_t *Opcodes, unsigned &OI); + void Decode_01xxxxxx(const uint8_t *Opcodes, unsigned &OI); + void Decode_1000iiii_iiiiiiii(const uint8_t *Opcodes, unsigned &OI); + void Decode_10011101(const uint8_t *Opcodes, unsigned &OI); + void Decode_10011111(const uint8_t *Opcodes, unsigned &OI); + void Decode_1001nnnn(const uint8_t *Opcodes, unsigned &OI); + void Decode_10100nnn(const uint8_t *Opcodes, unsigned &OI); + void Decode_10101nnn(const uint8_t *Opcodes, unsigned &OI); + void Decode_10110000(const uint8_t *Opcodes, unsigned &OI); + void Decode_10110001_0000iiii(const uint8_t *Opcodes, unsigned &OI); + void Decode_10110010_uleb128(const uint8_t *Opcodes, unsigned &OI); + void Decode_10110011_sssscccc(const uint8_t *Opcodes, unsigned &OI); + void Decode_101101nn(const uint8_t *Opcodes, unsigned &OI); + void Decode_10111nnn(const uint8_t *Opcodes, unsigned &OI); + void Decode_11000110_sssscccc(const uint8_t *Opcodes, unsigned &OI); + void Decode_11000111_0000iiii(const uint8_t *Opcodes, unsigned &OI); + void Decode_11001000_sssscccc(const uint8_t *Opcodes, unsigned &OI); + void Decode_11001001_sssscccc(const uint8_t *Opcodes, unsigned &OI); + void Decode_11001yyy(const uint8_t *Opcodes, unsigned &OI); + void Decode_11000nnn(const uint8_t *Opcodes, unsigned &OI); + void Decode_11010nnn(const uint8_t *Opcodes, unsigned &OI); + void Decode_11xxxyyy(const uint8_t *Opcodes, unsigned &OI); + + void PrintGPR(uint16_t GPRMask); + void PrintRegisters(uint32_t Mask, StringRef Prefix); + +public: + OpcodeDecoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {} + void Decode(const uint8_t *Opcodes, off_t Offset, size_t Length); +}; + +inline ArrayRef<OpcodeDecoder::RingEntry> OpcodeDecoder::ring() { + static const OpcodeDecoder::RingEntry Ring[] = { + {0xc0, 0x00, &OpcodeDecoder::Decode_00xxxxxx}, + {0xc0, 0x40, &OpcodeDecoder::Decode_01xxxxxx}, + {0xf0, 0x80, &OpcodeDecoder::Decode_1000iiii_iiiiiiii}, + {0xff, 0x9d, &OpcodeDecoder::Decode_10011101}, + {0xff, 0x9f, &OpcodeDecoder::Decode_10011111}, + {0xf0, 0x90, &OpcodeDecoder::Decode_1001nnnn}, + {0xf8, 0xa0, &OpcodeDecoder::Decode_10100nnn}, + {0xf8, 0xa8, &OpcodeDecoder::Decode_10101nnn}, + {0xff, 0xb0, &OpcodeDecoder::Decode_10110000}, + {0xff, 0xb1, &OpcodeDecoder::Decode_10110001_0000iiii}, + {0xff, 0xb2, &OpcodeDecoder::Decode_10110010_uleb128}, + {0xff, 0xb3, &OpcodeDecoder::Decode_10110011_sssscccc}, + {0xfc, 0xb4, &OpcodeDecoder::Decode_101101nn}, + {0xf8, 0xb8, &OpcodeDecoder::Decode_10111nnn}, + {0xff, 0xc6, &OpcodeDecoder::Decode_11000110_sssscccc}, + {0xff, 0xc7, &OpcodeDecoder::Decode_11000111_0000iiii}, + {0xff, 0xc8, &OpcodeDecoder::Decode_11001000_sssscccc}, + {0xff, 0xc9, &OpcodeDecoder::Decode_11001001_sssscccc}, + {0xc8, 0xc8, &OpcodeDecoder::Decode_11001yyy}, + {0xf8, 0xc0, &OpcodeDecoder::Decode_11000nnn}, + {0xf8, 0xd0, &OpcodeDecoder::Decode_11010nnn}, + {0xc0, 0xc0, &OpcodeDecoder::Decode_11xxxyyy}, + }; + return makeArrayRef(Ring); +} + +inline void OpcodeDecoder::Decode_00xxxxxx(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; vsp = vsp + %u\n", Opcode, + ((Opcode & 0x3f) << 2) + 4); +} +inline void OpcodeDecoder::Decode_01xxxxxx(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; vsp = vsp - %u\n", Opcode, + ((Opcode & 0x3f) << 2) + 4); +} +inline void OpcodeDecoder::Decode_1000iiii_iiiiiiii(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + + uint16_t GPRMask = (Opcode1 << 4) | ((Opcode0 & 0x0f) << 12); + SW.startLine() + << format("0x%02X 0x%02X ; %s", + Opcode0, Opcode1, GPRMask ? "pop " : "refuse to unwind"); + if (GPRMask) + PrintGPR(GPRMask); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_10011101(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; reserved (ARM MOVrr)\n", Opcode); +} +inline void OpcodeDecoder::Decode_10011111(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; reserved (WiMMX MOVrr)\n", Opcode); +} +inline void OpcodeDecoder::Decode_1001nnnn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; vsp = r%u\n", Opcode, (Opcode & 0x0f)); +} +inline void OpcodeDecoder::Decode_10100nnn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; pop ", Opcode); + PrintGPR((((1 << ((Opcode & 0x7) + 1)) - 1) << 4)); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_10101nnn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; pop ", Opcode); + PrintGPR((((1 << ((Opcode & 0x7) + 1)) - 1) << 4) | (1 << 14)); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_10110000(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; finish\n", Opcode); +} +inline void OpcodeDecoder::Decode_10110001_0000iiii(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + + SW.startLine() + << format("0x%02X 0x%02X ; %s", Opcode0, Opcode1, + ((Opcode1 & 0xf0) || Opcode1 == 0x00) ? "spare" : "pop "); + if (((Opcode1 & 0xf0) == 0x00) && Opcode1) + PrintGPR((Opcode1 & 0x0f)); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_10110010_uleb128(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ", Opcode); + + SmallVector<uint8_t, 4> ULEB; + do { ULEB.push_back(Opcodes[OI ^ 3]); } while (Opcodes[OI++ ^ 3] & 0x80); + + for (unsigned BI = 0, BE = ULEB.size(); BI != BE; ++BI) + OS << format("0x%02X ", ULEB[BI]); + + uint64_t Value = 0; + for (unsigned BI = 0, BE = ULEB.size(); BI != BE; ++BI) + Value = Value | ((ULEB[BI] & 0x7f) << (7 * BI)); + + OS << format("; vsp = vsp + %" PRIu64 "\n", 0x204 + (Value << 2)); +} +inline void OpcodeDecoder::Decode_10110011_sssscccc(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1); + uint8_t Start = ((Opcode1 & 0xf0) >> 4); + uint8_t Count = ((Opcode1 & 0x0f) >> 0); + PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_101101nn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; spare\n", Opcode); +} +inline void OpcodeDecoder::Decode_10111nnn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; pop ", Opcode); + PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 8), "d"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11000110_sssscccc(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1); + uint8_t Start = ((Opcode1 & 0xf0) >> 4); + uint8_t Count = ((Opcode1 & 0x0f) >> 0); + PrintRegisters((((1 << (Count + 1)) - 1) << Start), "wR"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11000111_0000iiii(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + SW.startLine() + << format("0x%02X 0x%02X ; %s", Opcode0, Opcode1, + ((Opcode1 & 0xf0) || Opcode1 == 0x00) ? "spare" : "pop "); + if ((Opcode1 & 0xf0) == 0x00 && Opcode1) + PrintRegisters(Opcode1 & 0x0f, "wCGR"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11001000_sssscccc(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1); + uint8_t Start = 16 + ((Opcode1 & 0xf0) >> 4); + uint8_t Count = ((Opcode1 & 0x0f) >> 0); + PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11001001_sssscccc(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode0 = Opcodes[OI++ ^ 3]; + uint8_t Opcode1 = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1); + uint8_t Start = ((Opcode1 & 0xf0) >> 4); + uint8_t Count = ((Opcode1 & 0x0f) >> 0); + PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11001yyy(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; spare\n", Opcode); +} +inline void OpcodeDecoder::Decode_11000nnn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; pop ", Opcode); + PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 10), "wR"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11010nnn(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; pop ", Opcode); + PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 8), "d"); + OS << '\n'; +} +inline void OpcodeDecoder::Decode_11xxxyyy(const uint8_t *Opcodes, + unsigned &OI) { + uint8_t Opcode = Opcodes[OI++ ^ 3]; + SW.startLine() << format("0x%02X ; spare\n", Opcode); +} + +inline void OpcodeDecoder::PrintGPR(uint16_t GPRMask) { + static const char *GPRRegisterNames[16] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "fp", "ip", "sp", "lr", "pc" + }; + + OS << '{'; + bool Comma = false; + for (unsigned RI = 0, RE = 17; RI < RE; ++RI) { + if (GPRMask & (1 << RI)) { + if (Comma) + OS << ", "; + OS << GPRRegisterNames[RI]; + Comma = true; + } + } + OS << '}'; +} + +inline void OpcodeDecoder::PrintRegisters(uint32_t VFPMask, StringRef Prefix) { + OS << '{'; + bool Comma = false; + for (unsigned RI = 0, RE = 32; RI < RE; ++RI) { + if (VFPMask & (1 << RI)) { + if (Comma) + OS << ", "; + OS << Prefix << RI; + Comma = true; + } + } + OS << '}'; +} + +inline void OpcodeDecoder::Decode(const uint8_t *Opcodes, off_t Offset, + size_t Length) { + for (unsigned OCI = Offset; OCI < Length + Offset; ) { + bool Decoded = false; + for (const auto &RE : ring()) { + if ((Opcodes[OCI ^ 3] & RE.Mask) == RE.Value) { + (this->*RE.Routine)(Opcodes, OCI); + Decoded = true; + break; + } + } + if (!Decoded) + SW.startLine() << format("0x%02X ; reserved\n", Opcodes[OCI++ ^ 3]); + } +} + +template <typename ET> +class PrinterContext { + typedef typename ET::Sym Elf_Sym; + typedef typename ET::Shdr Elf_Shdr; + typedef typename ET::Rel Elf_Rel; + typedef typename ET::Word Elf_Word; + + ScopedPrinter &SW; + const object::ELFFile<ET> *ELF; + StringRef FileName; + const Elf_Shdr *Symtab; + ArrayRef<Elf_Word> ShndxTable; + + static const size_t IndexTableEntrySize; + + static uint64_t PREL31(uint32_t Address, uint32_t Place) { + uint64_t Location = Address & 0x7fffffff; + if (Location & 0x04000000) + Location |= (uint64_t) ~0x7fffffff; + return Location + Place; + } + + ErrorOr<StringRef> FunctionAtAddress(unsigned Section, uint64_t Address) const; + const Elf_Shdr *FindExceptionTable(unsigned IndexTableIndex, + off_t IndexTableOffset) const; + + void PrintIndexTable(unsigned SectionIndex, const Elf_Shdr *IT) const; + void PrintExceptionTable(const Elf_Shdr *IT, const Elf_Shdr *EHT, + uint64_t TableEntryOffset) const; + void PrintOpcodes(const uint8_t *Entry, size_t Length, off_t Offset) const; + +public: + PrinterContext(ScopedPrinter &SW, const object::ELFFile<ET> *ELF, + StringRef FileName, const Elf_Shdr *Symtab) + : SW(SW), ELF(ELF), FileName(FileName), Symtab(Symtab) {} + + void PrintUnwindInformation() const; +}; + +template <typename ET> +const size_t PrinterContext<ET>::IndexTableEntrySize = 8; + +template <typename ET> +ErrorOr<StringRef> +PrinterContext<ET>::FunctionAtAddress(unsigned Section, + uint64_t Address) const { + if (!Symtab) + return readobj_error::unknown_symbol; + auto StrTableOrErr = ELF->getStringTableForSymtab(*Symtab); + if (!StrTableOrErr) + reportError(StrTableOrErr.takeError(), FileName); + StringRef StrTable = *StrTableOrErr; + + for (const Elf_Sym &Sym : unwrapOrError(FileName, ELF->symbols(Symtab))) + if (Sym.st_shndx == Section && Sym.st_value == Address && + Sym.getType() == ELF::STT_FUNC) { + auto NameOrErr = Sym.getName(StrTable); + if (!NameOrErr) { + // TODO: Actually report errors helpfully. + consumeError(NameOrErr.takeError()); + return readobj_error::unknown_symbol; + } + return *NameOrErr; + } + return readobj_error::unknown_symbol; +} + +template <typename ET> +const typename ET::Shdr * +PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex, + off_t IndexTableOffset) const { + /// Iterate through the sections, searching for the relocation section + /// associated with the unwind index table section specified by + /// IndexSectionIndex. Iterate the associated section searching for the + /// relocation associated with the index table entry specified by + /// IndexTableOffset. The symbol is the section symbol for the exception + /// handling table. Use this symbol to recover the actual exception handling + /// table. + + for (const Elf_Shdr &Sec : unwrapOrError(FileName, ELF->sections())) { + if (Sec.sh_type != ELF::SHT_REL || Sec.sh_info != IndexSectionIndex) + continue; + + auto SymTabOrErr = ELF->getSection(Sec.sh_link); + if (!SymTabOrErr) + reportError(SymTabOrErr.takeError(), FileName); + const Elf_Shdr *SymTab = *SymTabOrErr; + + for (const Elf_Rel &R : unwrapOrError(FileName, ELF->rels(&Sec))) { + if (R.r_offset != static_cast<unsigned>(IndexTableOffset)) + continue; + + typename ET::Rela RelA; + RelA.r_offset = R.r_offset; + RelA.r_info = R.r_info; + RelA.r_addend = 0; + + const Elf_Sym *Symbol = + unwrapOrError(FileName, ELF->getRelocationSymbol(&RelA, SymTab)); + + auto Ret = ELF->getSection(Symbol, SymTab, ShndxTable); + if (!Ret) + report_fatal_error(errorToErrorCode(Ret.takeError()).message()); + return *Ret; + } + } + return nullptr; +} + +template <typename ET> +void PrinterContext<ET>::PrintExceptionTable(const Elf_Shdr *IT, + const Elf_Shdr *EHT, + uint64_t TableEntryOffset) const { + Expected<ArrayRef<uint8_t>> Contents = ELF->getSectionContents(EHT); + if (!Contents) + return; + + /// ARM EHABI Section 6.2 - The generic model + /// + /// An exception-handling table entry for the generic model is laid out as: + /// + /// 3 3 + /// 1 0 0 + /// +-+------------------------------+ + /// |0| personality routine offset | + /// +-+------------------------------+ + /// | personality routine data ... | + /// + /// + /// ARM EHABI Section 6.3 - The ARM-defined compact model + /// + /// An exception-handling table entry for the compact model looks like: + /// + /// 3 3 2 2 2 2 + /// 1 0 8 7 4 3 0 + /// +-+---+----+-----------------------+ + /// |1| 0 | Ix | data for pers routine | + /// +-+---+----+-----------------------+ + /// | more personality routine data | + + const support::ulittle32_t Word = + *reinterpret_cast<const support::ulittle32_t *>(Contents->data() + TableEntryOffset); + + if (Word & 0x80000000) { + SW.printString("Model", StringRef("Compact")); + + unsigned PersonalityIndex = (Word & 0x0f000000) >> 24; + SW.printNumber("PersonalityIndex", PersonalityIndex); + + switch (PersonalityIndex) { + case AEABI_UNWIND_CPP_PR0: + PrintOpcodes(Contents->data() + TableEntryOffset, 3, 1); + break; + case AEABI_UNWIND_CPP_PR1: + case AEABI_UNWIND_CPP_PR2: + unsigned AdditionalWords = (Word & 0x00ff0000) >> 16; + PrintOpcodes(Contents->data() + TableEntryOffset, 2 + 4 * AdditionalWords, + 2); + break; + } + } else { + SW.printString("Model", StringRef("Generic")); + + uint64_t Address = PREL31(Word, EHT->sh_addr); + SW.printHex("PersonalityRoutineAddress", Address); + if (ErrorOr<StringRef> Name = FunctionAtAddress(EHT->sh_link, Address)) + SW.printString("PersonalityRoutineName", *Name); + } +} + +template <typename ET> +void PrinterContext<ET>::PrintOpcodes(const uint8_t *Entry, + size_t Length, off_t Offset) const { + ListScope OCC(SW, "Opcodes"); + OpcodeDecoder(OCC.W).Decode(Entry, Offset, Length); +} + +template <typename ET> +void PrinterContext<ET>::PrintIndexTable(unsigned SectionIndex, + const Elf_Shdr *IT) const { + Expected<ArrayRef<uint8_t>> Contents = ELF->getSectionContents(IT); + if (!Contents) + return; + + /// ARM EHABI Section 5 - Index Table Entries + /// * The first word contains a PREL31 offset to the start of a function with + /// bit 31 clear + /// * The second word contains one of: + /// - The PREL31 offset of the start of the table entry for the function, + /// with bit 31 clear + /// - The exception-handling table entry itself with bit 31 set + /// - The special bit pattern EXIDX_CANTUNWIND, indicating that associated + /// frames cannot be unwound + + const support::ulittle32_t *Data = + reinterpret_cast<const support::ulittle32_t *>(Contents->data()); + const unsigned Entries = IT->sh_size / IndexTableEntrySize; + + ListScope E(SW, "Entries"); + for (unsigned Entry = 0; Entry < Entries; ++Entry) { + DictScope E(SW, "Entry"); + + const support::ulittle32_t Word0 = + Data[Entry * (IndexTableEntrySize / sizeof(*Data)) + 0]; + const support::ulittle32_t Word1 = + Data[Entry * (IndexTableEntrySize / sizeof(*Data)) + 1]; + + if (Word0 & 0x80000000) { + errs() << "corrupt unwind data in section " << SectionIndex << "\n"; + continue; + } + + const uint64_t Offset = PREL31(Word0, IT->sh_addr); + SW.printHex("FunctionAddress", Offset); + if (ErrorOr<StringRef> Name = FunctionAtAddress(IT->sh_link, Offset)) + SW.printString("FunctionName", *Name); + + if (Word1 == EXIDX_CANTUNWIND) { + SW.printString("Model", StringRef("CantUnwind")); + continue; + } + + if (Word1 & 0x80000000) { + SW.printString("Model", StringRef("Compact (Inline)")); + + unsigned PersonalityIndex = (Word1 & 0x0f000000) >> 24; + SW.printNumber("PersonalityIndex", PersonalityIndex); + + PrintOpcodes(Contents->data() + Entry * IndexTableEntrySize + 4, 3, 1); + } else { + const Elf_Shdr *EHT = + FindExceptionTable(SectionIndex, Entry * IndexTableEntrySize + 4); + + if (EHT) + if (auto Name = ELF->getSectionName(EHT)) + SW.printString("ExceptionHandlingTable", *Name); + + uint64_t TableEntryOffset = PREL31(Word1, IT->sh_addr); + SW.printHex("TableEntryOffset", TableEntryOffset); + + if (EHT) + PrintExceptionTable(IT, EHT, TableEntryOffset); + } + } +} + +template <typename ET> +void PrinterContext<ET>::PrintUnwindInformation() const { + DictScope UI(SW, "UnwindInformation"); + + int SectionIndex = 0; + for (const Elf_Shdr &Sec : unwrapOrError(FileName, ELF->sections())) { + if (Sec.sh_type == ELF::SHT_ARM_EXIDX) { + DictScope UIT(SW, "UnwindIndexTable"); + + SW.printNumber("SectionIndex", SectionIndex); + if (auto SectionName = ELF->getSectionName(&Sec)) + SW.printString("SectionName", *SectionName); + SW.printHex("SectionOffset", Sec.sh_offset); + + PrintIndexTable(SectionIndex, &Sec); + } + ++SectionIndex; + } +} +} +} +} + +#endif diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp new file mode 100644 index 000000000000..3e026f58871b --- /dev/null +++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -0,0 +1,1110 @@ +//===-- ARMWinEHPrinter.cpp - Windows on ARM EH Data Printer ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Windows on ARM uses a series of serialised data structures (RuntimeFunction) +// to create a table of information for unwinding. In order to conserve space, +// there are two different ways that this data is represented. +// +// For functions with canonical forms for the prologue and epilogue, the data +// can be stored in a "packed" form. In this case, the data is packed into the +// RuntimeFunction's remaining 30-bits and can fully describe the entire frame. +// +// +---------------------------------------+ +// | Function Entry Address | +// +---------------------------------------+ +// | Packed Form Data | +// +---------------------------------------+ +// +// This layout is parsed by Decoder::dumpPackedEntry. No unwind bytecode is +// associated with such a frame as they can be derived from the provided data. +// The decoder does not synthesize this data as it is unnecessary for the +// purposes of validation, with the synthesis being required only by a proper +// unwinder. +// +// For functions that are large or do not match canonical forms, the data is +// split up into two portions, with the actual data residing in the "exception +// data" table (.xdata) with a reference to the entry from the "procedure data" +// (.pdata) entry. +// +// The exception data contains information about the frame setup, all of the +// epilogue scopes (for functions for which there are multiple exit points) and +// the associated exception handler. Additionally, the entry contains byte-code +// describing how to unwind the function (c.f. Decoder::decodeOpcodes). +// +// +---------------------------------------+ +// | Function Entry Address | +// +---------------------------------------+ +// | Exception Data Entry Address | +// +---------------------------------------+ +// +// This layout is parsed by Decoder::dumpUnpackedEntry. Such an entry must +// first resolve the exception data entry address. This structure +// (ExceptionDataRecord) has a variable sized header +// (c.f. ARM::WinEH::HeaderWords) and encodes most of the same information as +// the packed form. However, because this information is insufficient to +// synthesize the unwinding, there are associated unwinding bytecode which make +// up the bulk of the Decoder. +// +// The decoder itself is table-driven, using the first byte to determine the +// opcode and dispatching to the associated printing routine. The bytecode +// itself is a variable length instruction encoding that can fully describe the +// state of the stack and the necessary operations for unwinding to the +// beginning of the frame. +// +// The byte-code maintains a 1-1 instruction mapping, indicating both the width +// of the instruction (Thumb2 instructions are variable length, 16 or 32 bits +// wide) allowing the program to unwind from any point in the prologue, body, or +// epilogue of the function. + +#include "ARMWinEHPrinter.h" +#include "Error.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ARMWinEH.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support; + +namespace llvm { +raw_ostream &operator<<(raw_ostream &OS, const ARM::WinEH::ReturnType &RT) { + switch (RT) { + case ARM::WinEH::ReturnType::RT_POP: + OS << "pop {pc}"; + break; + case ARM::WinEH::ReturnType::RT_B: + OS << "b target"; + break; + case ARM::WinEH::ReturnType::RT_BW: + OS << "b.w target"; + break; + case ARM::WinEH::ReturnType::RT_NoEpilogue: + OS << "(no epilogue)"; + break; + } + return OS; +} +} + +static std::string formatSymbol(StringRef Name, uint64_t Address, + uint64_t Offset = 0) { + std::string Buffer; + raw_string_ostream OS(Buffer); + + if (!Name.empty()) + OS << Name << " "; + + if (Offset) + OS << format("+0x%X (0x%" PRIX64 ")", Offset, Address); + else if (!Name.empty()) + OS << format("(0x%" PRIX64 ")", Address); + else + OS << format("0x%" PRIX64, Address); + + return OS.str(); +} + +namespace llvm { +namespace ARM { +namespace WinEH { +const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction); + +// TODO name the uops more appropriately +const Decoder::RingEntry Decoder::Ring[] = { + { 0x80, 0x00, 1, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit) + { 0xc0, 0x80, 2, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit) + { 0xf0, 0xc0, 1, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit) + { 0xf8, 0xd0, 1, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit) + { 0xf8, 0xd8, 1, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit) + { 0xf8, 0xe0, 1, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit) + { 0xfc, 0xe8, 2, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit) + { 0xfe, 0xec, 2, &Decoder::opcode_1110110L }, // UOP_POP (16-bit) + { 0xff, 0xee, 2, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit) + // UOP_PUSH_MACHINE_FRAME + // UOP_PUSH_CONTEXT + // UOP_PUSH_TRAP_FRAME + // UOP_REDZONE_RESTORE_LR + { 0xff, 0xef, 2, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit) + { 0xff, 0xf5, 2, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit) + { 0xff, 0xf6, 2, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit) + { 0xff, 0xf7, 3, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit) + { 0xff, 0xf8, 4, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit) + { 0xff, 0xf9, 3, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit) + { 0xff, 0xfa, 4, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit) + { 0xff, 0xfb, 1, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit) + { 0xff, 0xfc, 1, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit) + { 0xff, 0xfd, 1, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END + { 0xff, 0xfe, 1, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END + { 0xff, 0xff, 1, &Decoder::opcode_11111111 }, // UOP_END +}; + + +// Unwind opcodes for ARM64. +// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling +const Decoder::RingEntry Decoder::Ring64[] = { + { 0xe0, 0x00, 1, &Decoder::opcode_alloc_s }, + { 0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x }, + { 0xc0, 0x40, 1, &Decoder::opcode_save_fplr }, + { 0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x }, + { 0xf8, 0xc0, 2, &Decoder::opcode_alloc_m }, + { 0xfc, 0xc8, 2, &Decoder::opcode_save_regp }, + { 0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x }, + { 0xfc, 0xd0, 2, &Decoder::opcode_save_reg }, + { 0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x }, + { 0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair }, + { 0xfe, 0xd8, 2, &Decoder::opcode_save_fregp }, + { 0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x }, + { 0xfe, 0xdc, 2, &Decoder::opcode_save_freg }, + { 0xff, 0xde, 2, &Decoder::opcode_save_freg_x }, + { 0xff, 0xe0, 4, &Decoder::opcode_alloc_l }, + { 0xff, 0xe1, 1, &Decoder::opcode_setfp }, + { 0xff, 0xe2, 2, &Decoder::opcode_addfp }, + { 0xff, 0xe3, 1, &Decoder::opcode_nop }, + { 0xff, 0xe4, 1, &Decoder::opcode_end }, + { 0xff, 0xe5, 1, &Decoder::opcode_end_c }, +}; + +void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) { + static const char * const GPRRegisterNames[16] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "r11", "ip", "sp", "lr", "pc", + }; + + const uint16_t GPRMask = std::get<0>(RegisterMask); + const uint16_t VFPMask = std::get<1>(RegisterMask); + + OS << '{'; + bool Comma = false; + for (unsigned RI = 0, RE = 11; RI < RE; ++RI) { + if (GPRMask & (1 << RI)) { + if (Comma) + OS << ", "; + OS << GPRRegisterNames[RI]; + Comma = true; + } + } + for (unsigned RI = 0, RE = 32; RI < RE; ++RI) { + if (VFPMask & (1 << RI)) { + if (Comma) + OS << ", "; + OS << "d" << unsigned(RI); + Comma = true; + } + } + for (unsigned RI = 11, RE = 16; RI < RE; ++RI) { + if (GPRMask & (1 << RI)) { + if (Comma) + OS << ", "; + OS << GPRRegisterNames[RI]; + Comma = true; + } + } + OS << '}'; +} + +ErrorOr<object::SectionRef> +Decoder::getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) { + for (const auto &Section : COFF.sections()) { + uint64_t Address = Section.getAddress(); + uint64_t Size = Section.getSize(); + + if (VA >= Address && (VA - Address) <= Size) + return Section; + } + return readobj_error::unknown_symbol; +} + +ErrorOr<object::SymbolRef> Decoder::getSymbol(const COFFObjectFile &COFF, + uint64_t VA, bool FunctionOnly) { + for (const auto &Symbol : COFF.symbols()) { + Expected<SymbolRef::Type> Type = Symbol.getType(); + if (!Type) + return errorToErrorCode(Type.takeError()); + if (FunctionOnly && *Type != SymbolRef::ST_Function) + continue; + + Expected<uint64_t> Address = Symbol.getAddress(); + if (!Address) + return errorToErrorCode(Address.takeError()); + if (*Address == VA) + return Symbol; + } + return readobj_error::unknown_symbol; +} + +ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &, + const SectionRef &Section, + uint64_t Offset) { + for (const auto &Relocation : Section.relocations()) { + uint64_t RelocationOffset = Relocation.getOffset(); + if (RelocationOffset == Offset) + return *Relocation.getSymbol(); + } + return readobj_error::unknown_symbol; +} + +bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint8_t Imm = OC[Offset] & 0x7f; + SW.startLine() << format("0x%02x ; %s sp, #(%u * 4)\n", + OC[Offset], + static_cast<const char *>(Prologue ? "sub" : "add"), + Imm); + ++Offset; + return false; +} + +bool Decoder::opcode_10Lxxxxx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Link = (OC[Offset] & 0x20) >> 5; + uint16_t RegisterMask = (Link << (Prologue ? 14 : 15)) + | ((OC[Offset + 0] & 0x1f) << 8) + | ((OC[Offset + 1] & 0xff) << 0); + assert((~RegisterMask & (1 << 13)) && "sp must not be set"); + assert((~RegisterMask & (1 << (Prologue ? 15 : 14))) && "pc must not be set"); + + SW.startLine() << format("0x%02x 0x%02x ; %s.w ", + OC[Offset + 0], OC[Offset + 1], + Prologue ? "push" : "pop"); + printRegisters(std::make_pair(RegisterMask, 0)); + OS << '\n'; + + Offset += 2; + return false; +} + +bool Decoder::opcode_1100xxxx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + if (Prologue) + SW.startLine() << format("0x%02x ; mov r%u, sp\n", + OC[Offset], OC[Offset] & 0xf); + else + SW.startLine() << format("0x%02x ; mov sp, r%u\n", + OC[Offset], OC[Offset] & 0xf); + ++Offset; + return false; +} + +bool Decoder::opcode_11010Lxx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Link = (OC[Offset] & 0x4) >> 3; + unsigned Count = (OC[Offset] & 0x3); + + uint16_t GPRMask = (Link << (Prologue ? 14 : 15)) + | (((1 << (Count + 1)) - 1) << 4); + + SW.startLine() << format("0x%02x ; %s ", OC[Offset], + Prologue ? "push" : "pop"); + printRegisters(std::make_pair(GPRMask, 0)); + OS << '\n'; + + ++Offset; + return false; +} + +bool Decoder::opcode_11011Lxx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Link = (OC[Offset] & 0x4) >> 2; + unsigned Count = (OC[Offset] & 0x3) + 4; + + uint16_t GPRMask = (Link << (Prologue ? 14 : 15)) + | (((1 << (Count + 1)) - 1) << 4); + + SW.startLine() << format("0x%02x ; %s.w ", OC[Offset], + Prologue ? "push" : "pop"); + printRegisters(std::make_pair(GPRMask, 0)); + OS << '\n'; + + ++Offset; + return false; +} + +bool Decoder::opcode_11100xxx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned High = (OC[Offset] & 0x7); + uint32_t VFPMask = (((1 << (High + 1)) - 1) << 8); + + SW.startLine() << format("0x%02x ; %s ", OC[Offset], + Prologue ? "vpush" : "vpop"); + printRegisters(std::make_pair(0, VFPMask)); + OS << '\n'; + + ++Offset; + return false; +} + +bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint16_t Imm = ((OC[Offset + 0] & 0x03) << 8) | ((OC[Offset + 1] & 0xff) << 0); + + SW.startLine() << format("0x%02x 0x%02x ; %s.w sp, #(%u * 4)\n", + OC[Offset + 0], OC[Offset + 1], + static_cast<const char *>(Prologue ? "sub" : "add"), + Imm); + + Offset += 2; + return false; +} + +bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint8_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15)) + | ((OC[Offset + 1] & 0xff) << 0); + + SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0], + OC[Offset + 1], Prologue ? "push" : "pop"); + printRegisters(std::make_pair(GPRMask, 0)); + OS << '\n'; + + Offset += 2; + return false; +} + +bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + assert(!Prologue && "may not be used in prologue"); + + if (OC[Offset + 1] & 0xf0) + SW.startLine() << format("0x%02x 0x%02x ; reserved\n", + OC[Offset + 0], OC[Offset + 1]); + else + SW.startLine() + << format("0x%02x 0x%02x ; microsoft-specific (type: %u)\n", + OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] & 0x0f); + + Offset += 2; + return false; +} + +bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + assert(!Prologue && "may not be used in prologue"); + + if (OC[Offset + 1] & 0xf0) + SW.startLine() << format("0x%02x 0x%02x ; reserved\n", + OC[Offset + 0], OC[Offset + 1]); + else + SW.startLine() + << format("0x%02x 0x%02x ; ldr.w lr, [sp], #%u\n", + OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] << 2); + + Offset += 2; + return false; +} + +bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Start = (OC[Offset + 1] & 0xf0) >> 4; + unsigned End = (OC[Offset + 1] & 0x0f) >> 0; + uint32_t VFPMask = ((1 << (End - Start)) - 1) << Start; + + SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0], + OC[Offset + 1], Prologue ? "vpush" : "vpop"); + printRegisters(std::make_pair(0, VFPMask)); + OS << '\n'; + + Offset += 2; + return false; +} + +bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Start = (OC[Offset + 1] & 0xf0) >> 4; + unsigned End = (OC[Offset + 1] & 0x0f) >> 0; + uint32_t VFPMask = ((1 << (End - Start)) - 1) << 16; + + SW.startLine() << format("0x%02x 0x%02x ; %s ", OC[Offset + 0], + OC[Offset + 1], Prologue ? "vpush" : "vpop"); + printRegisters(std::make_pair(0, VFPMask)); + OS << '\n'; + + Offset += 2; + return false; +} + +bool Decoder::opcode_11110111(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0); + + SW.startLine() << format("0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n", + OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], + static_cast<const char *>(Prologue ? "sub" : "add"), + Imm); + + Offset += 3; + return false; +} + +bool Decoder::opcode_11111000(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Imm = (OC[Offset + 1] << 16) + | (OC[Offset + 2] << 8) + | (OC[Offset + 3] << 0); + + SW.startLine() + << format("0x%02x 0x%02x 0x%02x 0x%02x ; %s sp, sp, #(%u * 4)\n", + OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3], + static_cast<const char *>(Prologue ? "sub" : "add"), Imm); + + Offset += 4; + return false; +} + +bool Decoder::opcode_11111001(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0); + + SW.startLine() + << format("0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n", + OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], + static_cast<const char *>(Prologue ? "sub" : "add"), Imm); + + Offset += 3; + return false; +} + +bool Decoder::opcode_11111010(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Imm = (OC[Offset + 1] << 16) + | (OC[Offset + 2] << 8) + | (OC[Offset + 3] << 0); + + SW.startLine() + << format("0x%02x 0x%02x 0x%02x 0x%02x ; %s.w sp, sp, #(%u * 4)\n", + OC[Offset + 0], OC[Offset + 1], OC[Offset + 2], OC[Offset + 3], + static_cast<const char *>(Prologue ? "sub" : "add"), Imm); + + Offset += 4; + return false; +} + +bool Decoder::opcode_11111011(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; nop\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_11111100(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; nop.w\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_11111101(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; b\n", OC[Offset]); + ++Offset; + return true; +} + +bool Decoder::opcode_11111110(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; b.w\n", OC[Offset]); + ++Offset; + return true; +} + +bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + ++Offset; + return true; +} + +// ARM64 unwind codes start here. +bool Decoder::opcode_alloc_s(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t NumBytes = (OC[Offset] & 0x1F) << 4; + SW.startLine() << format("0x%02x ; %s sp, #%u\n", OC[Offset], + static_cast<const char *>(Prologue ? "sub" : "add"), + NumBytes); + ++Offset; + return false; +} + +bool Decoder::opcode_save_r19r20_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Off = (OC[Offset] & 0x1F) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x ; stp x19, x20, [sp, #-%u]!\n", OC[Offset], Off); + else + SW.startLine() << format( + "0x%02x ; ldp x19, x20, [sp], #%u\n", OC[Offset], Off); + ++Offset; + return false; +} + +bool Decoder::opcode_save_fplr(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Off = (OC[Offset] & 0x3F) << 3; + SW.startLine() << format( + "0x%02x ; %s x29, x30, [sp, #%u]\n", OC[Offset], + static_cast<const char *>(Prologue ? "stp" : "ldp"), Off); + ++Offset; + return false; +} + +bool Decoder::opcode_save_fplr_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Off = ((OC[Offset] & 0x3F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x ; stp x29, x30, [sp, #-%u]!\n", OC[Offset], Off); + else + SW.startLine() << format( + "0x%02x ; ldp x29, x30, [sp], #%u\n", OC[Offset], Off); + ++Offset; + return false; +} + +bool Decoder::opcode_alloc_m(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t NumBytes = ((OC[Offset] & 0x07) << 8); + NumBytes |= (OC[Offset + 1] & 0xFF); + NumBytes <<= 4; + SW.startLine() << format("0x%02x%02x ; %s sp, #%u\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "sub" : "add"), + NumBytes); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_regp(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = ((OC[Offset] & 0x03) << 8); + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 19; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format( + "0x%02x%02x ; %s x%u, x%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "stp" : "ldp"), Reg, Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_regp_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = ((OC[Offset] & 0x03) << 8); + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 19; + uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x%02x ; stp x%u, x%u, [sp, #-%u]!\n", + OC[Offset], OC[Offset + 1], Reg, + Reg + 1, Off); + else + SW.startLine() << format( + "0x%02x%02x ; ldp x%u, x%u, [sp], #%u\n", + OC[Offset], OC[Offset + 1], Reg, + Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_reg(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x03) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 19; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s x%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "str" : "ldr"), + Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_reg_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xE0); + Reg >>= 5; + Reg += 19; + uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3; + if (Prologue) + SW.startLine() << format("0x%02x%02x ; str x%u, [sp, #%u]!\n", + OC[Offset], OC[Offset + 1], Reg, Off); + else + SW.startLine() << format("0x%02x%02x ; ldr x%u, [sp], #%u\n", + OC[Offset], OC[Offset + 1], Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_lrpair(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg *= 2; + Reg += 19; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s x%u, lr, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "stp" : "ldp"), + Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_fregp(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 8; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s d%u, d%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "stp" : "ldp"), + Reg, Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_fregp_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 8; + uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x%02x ; stp d%u, d%u, [sp, #-%u]!\n", OC[Offset], + OC[Offset + 1], Reg, Reg + 1, Off); + else + SW.startLine() << format( + "0x%02x%02x ; ldp d%u, d%u, [sp], #%u\n", OC[Offset], + OC[Offset + 1], Reg, Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_freg(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 8; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s d%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "str" : "ldr"), + Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_freg_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = ((OC[Offset + 1] & 0xE0) >> 5) + 8; + uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x%02x ; str d%u, [sp, #-%u]!\n", OC[Offset], + OC[Offset + 1], Reg, Off); + else + SW.startLine() << format( + "0x%02x%02x ; ldr d%u, [sp], #%u\n", OC[Offset], + OC[Offset + 1], Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_alloc_l(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Off = + (OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) | (OC[Offset + 3] << 0); + Off <<= 4; + SW.startLine() << format( + "0x%02x%02x%02x%02x ; %s sp, #%u\n", OC[Offset], OC[Offset + 1], + OC[Offset + 2], OC[Offset + 3], + static_cast<const char *>(Prologue ? "sub" : "add"), Off); + Offset += 4; + return false; +} + +bool Decoder::opcode_setfp(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; mov fp, sp\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_addfp(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + unsigned NumBytes = OC[Offset + 1] << 3; + SW.startLine() << format("0x%02x%02x ; add fp, sp, #%u\n", + OC[Offset], OC[Offset + 1], NumBytes); + Offset += 2; + return false; +} + +bool Decoder::opcode_nop(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; nop\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_end(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; end\n", OC[Offset]); + ++Offset; + return true; +} + +bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; end_c\n", OC[Offset]); + ++Offset; + return true; +} + +void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, + bool Prologue) { + assert((!Prologue || Offset == 0) && "prologue should always use offset 0"); + const RingEntry* DecodeRing = isAArch64 ? Ring64 : Ring; + bool Terminated = false; + for (unsigned OI = Offset, OE = Opcodes.size(); !Terminated && OI < OE; ) { + for (unsigned DI = 0;; ++DI) { + if ((isAArch64 && (DI >= array_lengthof(Ring64))) || + (!isAArch64 && (DI >= array_lengthof(Ring)))) { + SW.startLine() << format("0x%02x ; Bad opcode!\n", + Opcodes.data()[OI]); + ++OI; + break; + } + + if ((Opcodes[OI] & DecodeRing[DI].Mask) == DecodeRing[DI].Value) { + if (OI + DecodeRing[DI].Length > OE) { + SW.startLine() << format("Opcode 0x%02x goes past the unwind data\n", + Opcodes[OI]); + OI += DecodeRing[DI].Length; + break; + } + Terminated = + (this->*DecodeRing[DI].Routine)(Opcodes.data(), OI, 0, Prologue); + break; + } + } + } +} + +bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, + const SectionRef &Section, + uint64_t FunctionAddress, uint64_t VA) { + ArrayRef<uint8_t> Contents; + if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents)) + return false; + + uint64_t SectionVA = Section.getAddress(); + uint64_t Offset = VA - SectionVA; + const ulittle32_t *Data = + reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset); + + // Sanity check to ensure that the .xdata header is present. + // A header is one or two words, followed by at least one word to describe + // the unwind codes. Applicable to both ARM and AArch64. + if (Contents.size() - Offset < 8) + report_fatal_error(".xdata must be at least 8 bytes in size"); + + const ExceptionDataRecord XData(Data, isAArch64); + DictScope XRS(SW, "ExceptionData"); + SW.printNumber("FunctionLength", + isAArch64 ? XData.FunctionLengthInBytesAArch64() : + XData.FunctionLengthInBytesARM()); + SW.printNumber("Version", XData.Vers()); + SW.printBoolean("ExceptionData", XData.X()); + SW.printBoolean("EpiloguePacked", XData.E()); + if (!isAArch64) + SW.printBoolean("Fragment", XData.F()); + SW.printNumber(XData.E() ? "EpilogueOffset" : "EpilogueScopes", + XData.EpilogueCount()); + uint64_t ByteCodeLength = XData.CodeWords() * sizeof(uint32_t); + SW.printNumber("ByteCodeLength", ByteCodeLength); + + if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XData) - + (XData.E() ? 0 : XData.EpilogueCount() * 4) - + (XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength) { + SW.flush(); + report_fatal_error("Malformed unwind data"); + } + + if (XData.E()) { + ArrayRef<uint8_t> UC = XData.UnwindByteCode(); + if (isAArch64 || !XData.F()) { + ListScope PS(SW, "Prologue"); + decodeOpcodes(UC, 0, /*Prologue=*/true); + } + if (XData.EpilogueCount()) { + ListScope ES(SW, "Epilogue"); + decodeOpcodes(UC, XData.EpilogueCount(), /*Prologue=*/false); + } + } else { + { + ListScope PS(SW, "Prologue"); + decodeOpcodes(XData.UnwindByteCode(), 0, /*Prologue=*/true); + } + ArrayRef<ulittle32_t> EpilogueScopes = XData.EpilogueScopes(); + ListScope ESS(SW, "EpilogueScopes"); + for (const EpilogueScope ES : EpilogueScopes) { + DictScope ESES(SW, "EpilogueScope"); + SW.printNumber("StartOffset", ES.EpilogueStartOffset()); + if (!isAArch64) + SW.printNumber("Condition", ES.Condition()); + SW.printNumber("EpilogueStartIndex", + isAArch64 ? ES.EpilogueStartIndexAArch64() + : ES.EpilogueStartIndexARM()); + if (ES.ES & ~0xffc3ffff) + SW.printNumber("ReservedBits", (ES.ES >> 18) & 0xF); + + ListScope Opcodes(SW, "Opcodes"); + decodeOpcodes(XData.UnwindByteCode(), + isAArch64 ? ES.EpilogueStartIndexAArch64() + : ES.EpilogueStartIndexARM(), + /*Prologue=*/false); + } + } + + if (XData.X()) { + const uint32_t Address = XData.ExceptionHandlerRVA(); + const uint32_t Parameter = XData.ExceptionHandlerParameter(); + const size_t HandlerOffset = HeaderWords(XData) + + (XData.E() ? 0 : XData.EpilogueCount()) + + XData.CodeWords(); + + ErrorOr<SymbolRef> Symbol = getRelocatedSymbol( + COFF, Section, Offset + HandlerOffset * sizeof(uint32_t)); + if (!Symbol) + Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true); + if (!Symbol) { + ListScope EHS(SW, "ExceptionHandler"); + SW.printString("Routine", "(null)"); + return true; + } + + Expected<StringRef> Name = Symbol->getName(); + if (!Name) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(Name.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + + ListScope EHS(SW, "ExceptionHandler"); + SW.printString("Routine", formatSymbol(*Name, Address)); + SW.printHex("Parameter", Parameter); + } + + return true; +} + +bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, + const SectionRef Section, uint64_t Offset, + unsigned Index, const RuntimeFunction &RF) { + assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked && + "packed entry cannot be treated as an unpacked entry"); + + ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); + if (!Function) + Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true); + + ErrorOr<SymbolRef> XDataRecord = getRelocatedSymbol(COFF, Section, Offset + 4); + if (!XDataRecord) + XDataRecord = getSymbol(COFF, RF.ExceptionInformationRVA()); + + if (!RF.BeginAddress && !Function) + return false; + if (!RF.UnwindData && !XDataRecord) + return false; + + StringRef FunctionName; + uint64_t FunctionAddress; + if (Function) { + Expected<StringRef> FunctionNameOrErr = Function->getName(); + if (!FunctionNameOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + FunctionName = *FunctionNameOrErr; + Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); + if (!FunctionAddressOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + FunctionAddress = *FunctionAddressOrErr; + } else { + FunctionAddress = COFF.getImageBase() + RF.BeginAddress; + } + + SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + + if (XDataRecord) { + Expected<StringRef> Name = XDataRecord->getName(); + if (!Name) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(Name.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + + Expected<uint64_t> AddressOrErr = XDataRecord->getAddress(); + if (!AddressOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(AddressOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + uint64_t Address = *AddressOrErr; + + SW.printString("ExceptionRecord", formatSymbol(*Name, Address)); + + Expected<section_iterator> SIOrErr = XDataRecord->getSection(); + if (!SIOrErr) { + // TODO: Actually report errors helpfully. + consumeError(SIOrErr.takeError()); + return false; + } + section_iterator SI = *SIOrErr; + + // FIXME: Do we need to add an offset from the relocation? + return dumpXDataRecord(COFF, *SI, FunctionAddress, + RF.ExceptionInformationRVA()); + } else { + uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA(); + SW.printString("ExceptionRecord", formatSymbol("", Address)); + + ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address); + if (!Section) + return false; + + return dumpXDataRecord(COFF, *Section, FunctionAddress, Address); + } +} + +bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, + const SectionRef Section, uint64_t Offset, + unsigned Index, const RuntimeFunction &RF) { + assert((RF.Flag() == RuntimeFunctionFlag::RFF_Packed || + RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && + "unpacked entry cannot be treated as a packed entry"); + + ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); + if (!Function) + Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true); + + StringRef FunctionName; + uint64_t FunctionAddress; + if (Function) { + Expected<StringRef> FunctionNameOrErr = Function->getName(); + if (!FunctionNameOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + FunctionName = *FunctionNameOrErr; + Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); + if (!FunctionAddressOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + FunctionAddress = *FunctionAddressOrErr; + } else { + FunctionAddress = COFF.getPE32Header()->ImageBase + RF.BeginAddress; + } + + SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + if (!isAArch64) + SW.printBoolean("Fragment", + RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); + SW.printNumber("FunctionLength", RF.FunctionLength()); + SW.startLine() << "ReturnType: " << RF.Ret() << '\n'; + SW.printBoolean("HomedParameters", RF.H()); + SW.startLine() << "SavedRegisters: "; + printRegisters(SavedRegisterMask(RF)); + OS << '\n'; + SW.printNumber("StackAdjustment", StackAdjustment(RF) << 2); + + return true; +} + +bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF, + const SectionRef Section, unsigned Index, + ArrayRef<uint8_t> Contents) { + uint64_t Offset = PDataEntrySize * Index; + const ulittle32_t *Data = + reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset); + + const RuntimeFunction Entry(Data); + DictScope RFS(SW, "RuntimeFunction"); + if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked) + return dumpUnpackedEntry(COFF, Section, Offset, Index, Entry); + if (isAArch64) { + SW.startLine() << "Packed unwind data not yet supported for ARM64\n"; + return true; + } + return dumpPackedEntry(COFF, Section, Offset, Index, Entry); +} + +void Decoder::dumpProcedureData(const COFFObjectFile &COFF, + const SectionRef Section) { + ArrayRef<uint8_t> Contents; + if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents)) + return; + + if (Contents.size() % PDataEntrySize) { + errs() << ".pdata content is not " << PDataEntrySize << "-byte aligned\n"; + return; + } + + for (unsigned EI = 0, EE = Contents.size() / PDataEntrySize; EI < EE; ++EI) + if (!dumpProcedureDataEntry(COFF, Section, EI, Contents)) + break; +} + +Error Decoder::dumpProcedureData(const COFFObjectFile &COFF) { + for (const auto &Section : COFF.sections()) { + Expected<StringRef> NameOrErr = + COFF.getSectionName(COFF.getCOFFSection(Section)); + if (!NameOrErr) + return NameOrErr.takeError(); + + if (NameOrErr->startswith(".pdata")) + dumpProcedureData(COFF, Section); + } + return Error::success(); +} +} +} +} diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h new file mode 100644 index 000000000000..5de7062cb1d7 --- /dev/null +++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h @@ -0,0 +1,165 @@ +//===--- ARMWinEHPrinter.h - Windows on ARM Unwind Information Printer ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H +#define LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H + +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/ScopedPrinter.h" + +namespace llvm { +namespace ARM { +namespace WinEH { +class RuntimeFunction; + +class Decoder { + static const size_t PDataEntrySize; + + ScopedPrinter &SW; + raw_ostream &OS; + bool isAArch64; + + struct RingEntry { + uint8_t Mask; + uint8_t Value; + uint8_t Length; + bool (Decoder::*Routine)(const uint8_t *, unsigned &, unsigned, bool); + }; + static const RingEntry Ring[]; + static const RingEntry Ring64[]; + + bool opcode_0xxxxxxx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_10Lxxxxx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_1100xxxx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11010Lxx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11011Lxx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11100xxx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_111010xx(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_1110110L(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11101110(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11101111(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11110101(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11110110(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11110111(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111000(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111001(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111010(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111011(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111100(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111101(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111110(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_11111111(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + + // ARM64 unwind codes start here. + bool opcode_alloc_s(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_save_r19r20_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fplr(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fplr_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_alloc_m(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_save_regp(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_regp_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_reg(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_reg_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_lrpair(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fregp(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fregp_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_freg(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_freg_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_alloc_l(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_setfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_addfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_nop(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_end(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_end_c(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_save_next(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + + void decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, + bool Prologue); + + void printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask); + + ErrorOr<object::SectionRef> + getSectionContaining(const object::COFFObjectFile &COFF, uint64_t Address); + + ErrorOr<object::SymbolRef> + getSymbol(const object::COFFObjectFile &COFF, uint64_t Address, + bool FunctionOnly = false); + + ErrorOr<object::SymbolRef> + getRelocatedSymbol(const object::COFFObjectFile &COFF, + const object::SectionRef &Section, uint64_t Offset); + + bool dumpXDataRecord(const object::COFFObjectFile &COFF, + const object::SectionRef &Section, + uint64_t FunctionAddress, uint64_t VA); + bool dumpUnpackedEntry(const object::COFFObjectFile &COFF, + const object::SectionRef Section, uint64_t Offset, + unsigned Index, const RuntimeFunction &Entry); + bool dumpPackedEntry(const object::COFFObjectFile &COFF, + const object::SectionRef Section, uint64_t Offset, + unsigned Index, const RuntimeFunction &Entry); + bool dumpProcedureDataEntry(const object::COFFObjectFile &COFF, + const object::SectionRef Section, unsigned Entry, + ArrayRef<uint8_t> Contents); + void dumpProcedureData(const object::COFFObjectFile &COFF, + const object::SectionRef Section); + +public: + Decoder(ScopedPrinter &SW, bool isAArch64) : SW(SW), + OS(SW.getOStream()), + isAArch64(isAArch64) {} + Error dumpProcedureData(const object::COFFObjectFile &COFF); +}; +} +} +} + +#endif diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp new file mode 100644 index 000000000000..9b2c6adb9d93 --- /dev/null +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -0,0 +1,1984 @@ +//===-- COFFDumper.cpp - COFF-specific dumper -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the COFF-specific dumper for llvm-readobj. +/// +//===----------------------------------------------------------------------===// + +#include "ARMWinEHPrinter.h" +#include "Error.h" +#include "ObjDumper.h" +#include "StackMapPrinter.h" +#include "Win64EHDumper.h" +#include "llvm-readobj.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeHashing.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/DebugInfo/CodeView/TypeTableCollection.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Win64EH.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::codeview; +using namespace llvm::support; +using namespace llvm::Win64EH; + +static inline Error createError(const Twine &Err) { + return make_error<StringError>(Err, object_error::parse_failed); +} + +namespace { + +struct LoadConfigTables { + uint64_t SEHTableVA = 0; + uint64_t SEHTableCount = 0; + uint32_t GuardFlags = 0; + uint64_t GuardFidTableVA = 0; + uint64_t GuardFidTableCount = 0; + uint64_t GuardLJmpTableVA = 0; + uint64_t GuardLJmpTableCount = 0; +}; + +class COFFDumper : public ObjDumper { +public: + friend class COFFObjectDumpDelegate; + COFFDumper(const llvm::object::COFFObjectFile *Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj), Writer(Writer), Types(100) {} + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printUnwindInfo() override; + + void printNeededLibraries() override; + + void printCOFFImports() override; + void printCOFFExports() override; + void printCOFFDirectives() override; + void printCOFFBaseReloc() override; + void printCOFFDebugDirectory() override; + void printCOFFResources() override; + void printCOFFLoadConfig() override; + void printCodeViewDebugInfo() override; + void mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs, + llvm::codeview::MergingTypeTableBuilder &CVTypes, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVIDs, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVTypes, + bool GHash) override; + void printStackMap() const override; + void printAddrsig() override; +private: + void printSymbols() override; + void printDynamicSymbols() override; + void printSymbol(const SymbolRef &Sym); + void printRelocation(const SectionRef &Section, const RelocationRef &Reloc, + uint64_t Bias = 0); + void printDataDirectory(uint32_t Index, const std::string &FieldName); + + void printDOSHeader(const dos_header *DH); + template <class PEHeader> void printPEHeader(const PEHeader *Hdr); + void printBaseOfDataField(const pe32_header *Hdr); + void printBaseOfDataField(const pe32plus_header *Hdr); + template <typename T> + void printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables); + typedef void (*PrintExtraCB)(raw_ostream &, const uint8_t *); + void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize, + PrintExtraCB PrintExtra = 0); + + void printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section); + void printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section); + StringRef getTypeName(TypeIndex Ty); + StringRef getFileNameForFileOffset(uint32_t FileOffset); + void printFileNameForOffset(StringRef Label, uint32_t FileOffset); + void printTypeIndex(StringRef FieldName, TypeIndex TI) { + // Forward to CVTypeDumper for simplicity. + codeview::printTypeIndex(Writer, FieldName, TI, Types); + } + + void printCodeViewSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + StringRef SectionContents); + + void printCodeViewFileChecksums(StringRef Subsection); + + void printCodeViewInlineeLines(StringRef Subsection); + + void printRelocatedField(StringRef Label, const coff_section *Sec, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym = nullptr); + + uint32_t countTotalTableEntries(ResourceSectionRef RSF, + const coff_resource_dir_table &Table, + StringRef Level); + + void printResourceDirectoryTable(ResourceSectionRef RSF, + const coff_resource_dir_table &Table, + StringRef Level); + + void printBinaryBlockWithRelocs(StringRef Label, const SectionRef &Sec, + StringRef SectionContents, StringRef Block); + + /// Given a .debug$S section, find the string table and file checksum table. + void initializeFileAndStringTables(BinaryStreamReader &Reader); + + void cacheRelocations(); + + std::error_code resolveSymbol(const coff_section *Section, uint64_t Offset, + SymbolRef &Sym); + std::error_code resolveSymbolName(const coff_section *Section, + uint64_t Offset, StringRef &Name); + std::error_code resolveSymbolName(const coff_section *Section, + StringRef SectionContents, + const void *RelocPtr, StringRef &Name); + void printImportedSymbols(iterator_range<imported_symbol_iterator> Range); + void printDelayImportedSymbols( + const DelayImportDirectoryEntryRef &I, + iterator_range<imported_symbol_iterator> Range); + + typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy; + + const llvm::object::COFFObjectFile *Obj; + bool RelocCached = false; + RelocMapTy RelocMap; + + DebugChecksumsSubsectionRef CVFileChecksumTable; + + DebugStringTableSubsectionRef CVStringTable; + + /// Track the compilation CPU type. S_COMPILE3 symbol records typically come + /// first, but if we don't see one, just assume an X64 CPU type. It is common. + CPUType CompilationCPUType = CPUType::X64; + + ScopedPrinter &Writer; + BinaryByteStream TypeContents; + LazyRandomTypeCollection Types; +}; + +class COFFObjectDumpDelegate : public SymbolDumpDelegate { +public: + COFFObjectDumpDelegate(COFFDumper &CD, const SectionRef &SR, + const COFFObjectFile *Obj, StringRef SectionContents) + : CD(CD), SR(SR), SectionContents(SectionContents) { + Sec = Obj->getCOFFSection(SR); + } + + uint32_t getRecordOffset(BinaryStreamReader Reader) override { + ArrayRef<uint8_t> Data; + if (auto EC = Reader.readLongestContiguousChunk(Data)) { + llvm::consumeError(std::move(EC)); + return 0; + } + return Data.data() - SectionContents.bytes_begin(); + } + + void printRelocatedField(StringRef Label, uint32_t RelocOffset, + uint32_t Offset, StringRef *RelocSym) override { + CD.printRelocatedField(Label, Sec, RelocOffset, Offset, RelocSym); + } + + void printBinaryBlockWithRelocs(StringRef Label, + ArrayRef<uint8_t> Block) override { + StringRef SBlock(reinterpret_cast<const char *>(Block.data()), + Block.size()); + if (opts::CodeViewSubsectionBytes) + CD.printBinaryBlockWithRelocs(Label, SR, SectionContents, SBlock); + } + + StringRef getFileNameForFileOffset(uint32_t FileOffset) override { + return CD.getFileNameForFileOffset(FileOffset); + } + + DebugStringTableSubsectionRef getStringTable() override { + return CD.CVStringTable; + } + +private: + COFFDumper &CD; + const SectionRef &SR; + const coff_section *Sec; + StringRef SectionContents; +}; + +} // end namespace + +namespace llvm { + +std::error_code createCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(Obj); + if (!COFFObj) + return readobj_error::unsupported_obj_file_format; + + Result.reset(new COFFDumper(COFFObj, Writer)); + return readobj_error::success; +} + +} // namespace llvm + +// Given a section and an offset into this section the function returns the +// symbol used for the relocation at the offset. +std::error_code COFFDumper::resolveSymbol(const coff_section *Section, + uint64_t Offset, SymbolRef &Sym) { + cacheRelocations(); + const auto &Relocations = RelocMap[Section]; + auto SymI = Obj->symbol_end(); + for (const auto &Relocation : Relocations) { + uint64_t RelocationOffset = Relocation.getOffset(); + + if (RelocationOffset == Offset) { + SymI = Relocation.getSymbol(); + break; + } + } + if (SymI == Obj->symbol_end()) + return readobj_error::unknown_symbol; + Sym = *SymI; + return readobj_error::success; +} + +// Given a section and an offset into this section the function returns the name +// of the symbol used for the relocation at the offset. +std::error_code COFFDumper::resolveSymbolName(const coff_section *Section, + uint64_t Offset, + StringRef &Name) { + SymbolRef Symbol; + if (std::error_code EC = resolveSymbol(Section, Offset, Symbol)) + return EC; + Expected<StringRef> NameOrErr = Symbol.getName(); + if (!NameOrErr) + return errorToErrorCode(NameOrErr.takeError()); + Name = *NameOrErr; + return std::error_code(); +} + +// Helper for when you have a pointer to real data and you want to know about +// relocations against it. +std::error_code COFFDumper::resolveSymbolName(const coff_section *Section, + StringRef SectionContents, + const void *RelocPtr, + StringRef &Name) { + assert(SectionContents.data() < RelocPtr && + RelocPtr < SectionContents.data() + SectionContents.size() && + "pointer to relocated object is not in section"); + uint64_t Offset = ptrdiff_t(reinterpret_cast<const char *>(RelocPtr) - + SectionContents.data()); + return resolveSymbolName(Section, Offset, Name); +} + +void COFFDumper::printRelocatedField(StringRef Label, const coff_section *Sec, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym) { + StringRef SymStorage; + StringRef &Symbol = RelocSym ? *RelocSym : SymStorage; + if (!resolveSymbolName(Sec, RelocOffset, Symbol)) + W.printSymbolOffset(Label, Symbol, Offset); + else + W.printHex(Label, RelocOffset); +} + +void COFFDumper::printBinaryBlockWithRelocs(StringRef Label, + const SectionRef &Sec, + StringRef SectionContents, + StringRef Block) { + W.printBinaryBlock(Label, Block); + + assert(SectionContents.begin() < Block.begin() && + SectionContents.end() >= Block.end() && + "Block is not contained in SectionContents"); + uint64_t OffsetStart = Block.data() - SectionContents.data(); + uint64_t OffsetEnd = OffsetStart + Block.size(); + + W.flush(); + cacheRelocations(); + ListScope D(W, "BlockRelocations"); + const coff_section *Section = Obj->getCOFFSection(Sec); + const auto &Relocations = RelocMap[Section]; + for (const auto &Relocation : Relocations) { + uint64_t RelocationOffset = Relocation.getOffset(); + if (OffsetStart <= RelocationOffset && RelocationOffset < OffsetEnd) + printRelocation(Sec, Relocation, OffsetStart); + } +} + +static const EnumEntry<COFF::MachineTypes> ImageFileMachineType[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_UNKNOWN ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_AM33 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_AMD64 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARM64 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_ARMNT ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_EBC ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_I386 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_IA64 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_M32R ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPS16 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPSFPU ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPSFPU16), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_POWERPC ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_POWERPCFP), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_R4000 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH3 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH3DSP ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH4 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH5 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_THUMB ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_WCEMIPSV2) +}; + +static const EnumEntry<COFF::Characteristics> ImageFileCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_RELOCS_STRIPPED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_EXECUTABLE_IMAGE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_LINE_NUMS_STRIPPED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_LOCAL_SYMS_STRIPPED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_AGGRESSIVE_WS_TRIM ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_LARGE_ADDRESS_AWARE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_BYTES_REVERSED_LO ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_32BIT_MACHINE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_DEBUG_STRIPPED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_NET_RUN_FROM_SWAP ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_SYSTEM ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_DLL ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_UP_SYSTEM_ONLY ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_BYTES_REVERSED_HI ) +}; + +static const EnumEntry<COFF::WindowsSubsystem> PEWindowsSubsystem[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_UNKNOWN ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_NATIVE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_WINDOWS_GUI ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_WINDOWS_CUI ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_POSIX_CUI ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_APPLICATION ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_EFI_ROM ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SUBSYSTEM_XBOX ), +}; + +static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NO_SEH ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_NO_BIND ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_APPCONTAINER ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_GUARD_CF ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE), +}; + +static const EnumEntry<COFF::SectionCharacteristics> +ImageSectionCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NO_PAD ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_CODE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_INITIALIZED_DATA ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_UNINITIALIZED_DATA), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_OTHER ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_INFO ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_REMOVE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_COMDAT ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_GPREL ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_PURGEABLE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_16BIT ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_LOCKED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_PRELOAD ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_1BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_2BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_4BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_8BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_16BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_32BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_64BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_128BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_256BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_512BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_1024BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_2048BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_4096BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_ALIGN_8192BYTES ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_LNK_NRELOC_OVFL ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_DISCARDABLE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_NOT_CACHED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_NOT_PAGED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_SHARED ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_EXECUTE ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_READ ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_MEM_WRITE ) +}; + +static const EnumEntry<COFF::SymbolBaseType> ImageSymType[] = { + { "Null" , COFF::IMAGE_SYM_TYPE_NULL }, + { "Void" , COFF::IMAGE_SYM_TYPE_VOID }, + { "Char" , COFF::IMAGE_SYM_TYPE_CHAR }, + { "Short" , COFF::IMAGE_SYM_TYPE_SHORT }, + { "Int" , COFF::IMAGE_SYM_TYPE_INT }, + { "Long" , COFF::IMAGE_SYM_TYPE_LONG }, + { "Float" , COFF::IMAGE_SYM_TYPE_FLOAT }, + { "Double", COFF::IMAGE_SYM_TYPE_DOUBLE }, + { "Struct", COFF::IMAGE_SYM_TYPE_STRUCT }, + { "Union" , COFF::IMAGE_SYM_TYPE_UNION }, + { "Enum" , COFF::IMAGE_SYM_TYPE_ENUM }, + { "MOE" , COFF::IMAGE_SYM_TYPE_MOE }, + { "Byte" , COFF::IMAGE_SYM_TYPE_BYTE }, + { "Word" , COFF::IMAGE_SYM_TYPE_WORD }, + { "UInt" , COFF::IMAGE_SYM_TYPE_UINT }, + { "DWord" , COFF::IMAGE_SYM_TYPE_DWORD } +}; + +static const EnumEntry<COFF::SymbolComplexType> ImageSymDType[] = { + { "Null" , COFF::IMAGE_SYM_DTYPE_NULL }, + { "Pointer" , COFF::IMAGE_SYM_DTYPE_POINTER }, + { "Function", COFF::IMAGE_SYM_DTYPE_FUNCTION }, + { "Array" , COFF::IMAGE_SYM_DTYPE_ARRAY } +}; + +static const EnumEntry<COFF::SymbolStorageClass> ImageSymClass[] = { + { "EndOfFunction" , COFF::IMAGE_SYM_CLASS_END_OF_FUNCTION }, + { "Null" , COFF::IMAGE_SYM_CLASS_NULL }, + { "Automatic" , COFF::IMAGE_SYM_CLASS_AUTOMATIC }, + { "External" , COFF::IMAGE_SYM_CLASS_EXTERNAL }, + { "Static" , COFF::IMAGE_SYM_CLASS_STATIC }, + { "Register" , COFF::IMAGE_SYM_CLASS_REGISTER }, + { "ExternalDef" , COFF::IMAGE_SYM_CLASS_EXTERNAL_DEF }, + { "Label" , COFF::IMAGE_SYM_CLASS_LABEL }, + { "UndefinedLabel" , COFF::IMAGE_SYM_CLASS_UNDEFINED_LABEL }, + { "MemberOfStruct" , COFF::IMAGE_SYM_CLASS_MEMBER_OF_STRUCT }, + { "Argument" , COFF::IMAGE_SYM_CLASS_ARGUMENT }, + { "StructTag" , COFF::IMAGE_SYM_CLASS_STRUCT_TAG }, + { "MemberOfUnion" , COFF::IMAGE_SYM_CLASS_MEMBER_OF_UNION }, + { "UnionTag" , COFF::IMAGE_SYM_CLASS_UNION_TAG }, + { "TypeDefinition" , COFF::IMAGE_SYM_CLASS_TYPE_DEFINITION }, + { "UndefinedStatic", COFF::IMAGE_SYM_CLASS_UNDEFINED_STATIC }, + { "EnumTag" , COFF::IMAGE_SYM_CLASS_ENUM_TAG }, + { "MemberOfEnum" , COFF::IMAGE_SYM_CLASS_MEMBER_OF_ENUM }, + { "RegisterParam" , COFF::IMAGE_SYM_CLASS_REGISTER_PARAM }, + { "BitField" , COFF::IMAGE_SYM_CLASS_BIT_FIELD }, + { "Block" , COFF::IMAGE_SYM_CLASS_BLOCK }, + { "Function" , COFF::IMAGE_SYM_CLASS_FUNCTION }, + { "EndOfStruct" , COFF::IMAGE_SYM_CLASS_END_OF_STRUCT }, + { "File" , COFF::IMAGE_SYM_CLASS_FILE }, + { "Section" , COFF::IMAGE_SYM_CLASS_SECTION }, + { "WeakExternal" , COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL }, + { "CLRToken" , COFF::IMAGE_SYM_CLASS_CLR_TOKEN } +}; + +static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = { + { "NoDuplicates", COFF::IMAGE_COMDAT_SELECT_NODUPLICATES }, + { "Any" , COFF::IMAGE_COMDAT_SELECT_ANY }, + { "SameSize" , COFF::IMAGE_COMDAT_SELECT_SAME_SIZE }, + { "ExactMatch" , COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH }, + { "Associative" , COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE }, + { "Largest" , COFF::IMAGE_COMDAT_SELECT_LARGEST }, + { "Newest" , COFF::IMAGE_COMDAT_SELECT_NEWEST } +}; + +static const EnumEntry<COFF::DebugType> ImageDebugType[] = { + { "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN }, + { "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF }, + { "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW }, + { "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO }, + { "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC }, + { "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION }, + { "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP }, + { "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC }, + { "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC }, + { "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND }, + { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 }, + { "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID }, + { "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE }, + { "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO }, + { "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG }, + { "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX }, + { "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO }, +}; + +static const EnumEntry<COFF::WeakExternalCharacteristics> +WeakExternalCharacteristics[] = { + { "NoLibrary", COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY }, + { "Library" , COFF::IMAGE_WEAK_EXTERN_SEARCH_LIBRARY }, + { "Alias" , COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS } +}; + +static const EnumEntry<uint32_t> SubSectionTypes[] = { + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, Symbols), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, Lines), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, StringTable), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, FileChecksums), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, FrameData), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, InlineeLines), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, CrossScopeImports), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, CrossScopeExports), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, ILLines), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, FuncMDTokenMap), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, TypeMDTokenMap), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, MergedAssemblyInput), + LLVM_READOBJ_ENUM_CLASS_ENT(DebugSubsectionKind, CoffSymbolRVA), +}; + +static const EnumEntry<uint32_t> FrameDataFlags[] = { + LLVM_READOBJ_ENUM_ENT(FrameData, HasSEH), + LLVM_READOBJ_ENUM_ENT(FrameData, HasEH), + LLVM_READOBJ_ENUM_ENT(FrameData, IsFunctionStart), +}; + +static const EnumEntry<uint8_t> FileChecksumKindNames[] = { + LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, None), + LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, MD5), + LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA1), + LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256), +}; + +template <typename T> +static std::error_code getSymbolAuxData(const COFFObjectFile *Obj, + COFFSymbolRef Symbol, + uint8_t AuxSymbolIdx, const T *&Aux) { + ArrayRef<uint8_t> AuxData = Obj->getSymbolAuxData(Symbol); + AuxData = AuxData.slice(AuxSymbolIdx * Obj->getSymbolTableEntrySize()); + Aux = reinterpret_cast<const T*>(AuxData.data()); + return readobj_error::success; +} + +void COFFDumper::cacheRelocations() { + if (RelocCached) + return; + RelocCached = true; + + for (const SectionRef &S : Obj->sections()) { + const coff_section *Section = Obj->getCOFFSection(S); + + for (const RelocationRef &Reloc : S.relocations()) + RelocMap[Section].push_back(Reloc); + + // Sort relocations by address. + llvm::sort(RelocMap[Section], [](RelocationRef L, RelocationRef R) { + return L.getOffset() < R.getOffset(); + }); + } +} + +void COFFDumper::printDataDirectory(uint32_t Index, + const std::string &FieldName) { + const data_directory *Data; + if (Obj->getDataDirectory(Index, Data)) + return; + W.printHex(FieldName + "RVA", Data->RelativeVirtualAddress); + W.printHex(FieldName + "Size", Data->Size); +} + +void COFFDumper::printFileHeaders() { + time_t TDS = Obj->getTimeDateStamp(); + char FormattedTime[20] = { }; + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + + { + DictScope D(W, "ImageFileHeader"); + W.printEnum ("Machine", Obj->getMachine(), + makeArrayRef(ImageFileMachineType)); + W.printNumber("SectionCount", Obj->getNumberOfSections()); + W.printHex ("TimeDateStamp", FormattedTime, Obj->getTimeDateStamp()); + W.printHex ("PointerToSymbolTable", Obj->getPointerToSymbolTable()); + W.printNumber("SymbolCount", Obj->getNumberOfSymbols()); + W.printNumber("OptionalHeaderSize", Obj->getSizeOfOptionalHeader()); + W.printFlags ("Characteristics", Obj->getCharacteristics(), + makeArrayRef(ImageFileCharacteristics)); + } + + // Print PE header. This header does not exist if this is an object file and + // not an executable. + if (const pe32_header *PEHeader = Obj->getPE32Header()) + printPEHeader<pe32_header>(PEHeader); + + if (const pe32plus_header *PEPlusHeader = Obj->getPE32PlusHeader()) + printPEHeader<pe32plus_header>(PEPlusHeader); + + if (const dos_header *DH = Obj->getDOSHeader()) + printDOSHeader(DH); +} + +void COFFDumper::printDOSHeader(const dos_header *DH) { + DictScope D(W, "DOSHeader"); + W.printString("Magic", StringRef(DH->Magic, sizeof(DH->Magic))); + W.printNumber("UsedBytesInTheLastPage", DH->UsedBytesInTheLastPage); + W.printNumber("FileSizeInPages", DH->FileSizeInPages); + W.printNumber("NumberOfRelocationItems", DH->NumberOfRelocationItems); + W.printNumber("HeaderSizeInParagraphs", DH->HeaderSizeInParagraphs); + W.printNumber("MinimumExtraParagraphs", DH->MinimumExtraParagraphs); + W.printNumber("MaximumExtraParagraphs", DH->MaximumExtraParagraphs); + W.printNumber("InitialRelativeSS", DH->InitialRelativeSS); + W.printNumber("InitialSP", DH->InitialSP); + W.printNumber("Checksum", DH->Checksum); + W.printNumber("InitialIP", DH->InitialIP); + W.printNumber("InitialRelativeCS", DH->InitialRelativeCS); + W.printNumber("AddressOfRelocationTable", DH->AddressOfRelocationTable); + W.printNumber("OverlayNumber", DH->OverlayNumber); + W.printNumber("OEMid", DH->OEMid); + W.printNumber("OEMinfo", DH->OEMinfo); + W.printNumber("AddressOfNewExeHeader", DH->AddressOfNewExeHeader); +} + +template <class PEHeader> +void COFFDumper::printPEHeader(const PEHeader *Hdr) { + DictScope D(W, "ImageOptionalHeader"); + W.printHex ("Magic", Hdr->Magic); + W.printNumber("MajorLinkerVersion", Hdr->MajorLinkerVersion); + W.printNumber("MinorLinkerVersion", Hdr->MinorLinkerVersion); + W.printNumber("SizeOfCode", Hdr->SizeOfCode); + W.printNumber("SizeOfInitializedData", Hdr->SizeOfInitializedData); + W.printNumber("SizeOfUninitializedData", Hdr->SizeOfUninitializedData); + W.printHex ("AddressOfEntryPoint", Hdr->AddressOfEntryPoint); + W.printHex ("BaseOfCode", Hdr->BaseOfCode); + printBaseOfDataField(Hdr); + W.printHex ("ImageBase", Hdr->ImageBase); + W.printNumber("SectionAlignment", Hdr->SectionAlignment); + W.printNumber("FileAlignment", Hdr->FileAlignment); + W.printNumber("MajorOperatingSystemVersion", + Hdr->MajorOperatingSystemVersion); + W.printNumber("MinorOperatingSystemVersion", + Hdr->MinorOperatingSystemVersion); + W.printNumber("MajorImageVersion", Hdr->MajorImageVersion); + W.printNumber("MinorImageVersion", Hdr->MinorImageVersion); + W.printNumber("MajorSubsystemVersion", Hdr->MajorSubsystemVersion); + W.printNumber("MinorSubsystemVersion", Hdr->MinorSubsystemVersion); + W.printNumber("SizeOfImage", Hdr->SizeOfImage); + W.printNumber("SizeOfHeaders", Hdr->SizeOfHeaders); + W.printEnum ("Subsystem", Hdr->Subsystem, makeArrayRef(PEWindowsSubsystem)); + W.printFlags ("Characteristics", Hdr->DLLCharacteristics, + makeArrayRef(PEDLLCharacteristics)); + W.printNumber("SizeOfStackReserve", Hdr->SizeOfStackReserve); + W.printNumber("SizeOfStackCommit", Hdr->SizeOfStackCommit); + W.printNumber("SizeOfHeapReserve", Hdr->SizeOfHeapReserve); + W.printNumber("SizeOfHeapCommit", Hdr->SizeOfHeapCommit); + W.printNumber("NumberOfRvaAndSize", Hdr->NumberOfRvaAndSize); + + if (Hdr->NumberOfRvaAndSize > 0) { + DictScope D(W, "DataDirectory"); + static const char * const directory[] = { + "ExportTable", "ImportTable", "ResourceTable", "ExceptionTable", + "CertificateTable", "BaseRelocationTable", "Debug", "Architecture", + "GlobalPtr", "TLSTable", "LoadConfigTable", "BoundImport", "IAT", + "DelayImportDescriptor", "CLRRuntimeHeader", "Reserved" + }; + + for (uint32_t i = 0; i < Hdr->NumberOfRvaAndSize; ++i) + printDataDirectory(i, directory[i]); + } +} + +void COFFDumper::printCOFFDebugDirectory() { + ListScope LS(W, "DebugDirectory"); + for (const debug_directory &D : Obj->debug_directories()) { + char FormattedTime[20] = {}; + time_t TDS = D.TimeDateStamp; + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + DictScope S(W, "DebugEntry"); + W.printHex("Characteristics", D.Characteristics); + W.printHex("TimeDateStamp", FormattedTime, D.TimeDateStamp); + W.printHex("MajorVersion", D.MajorVersion); + W.printHex("MinorVersion", D.MinorVersion); + W.printEnum("Type", D.Type, makeArrayRef(ImageDebugType)); + W.printHex("SizeOfData", D.SizeOfData); + W.printHex("AddressOfRawData", D.AddressOfRawData); + W.printHex("PointerToRawData", D.PointerToRawData); + if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) { + const codeview::DebugInfo *DebugInfo; + StringRef PDBFileName; + if (std::error_code EC = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + DictScope PDBScope(W, "PDBInfo"); + W.printHex("PDBSignature", DebugInfo->Signature.CVSignature); + if (DebugInfo->Signature.CVSignature == OMF::Signature::PDB70) { + W.printBinary("PDBGUID", makeArrayRef(DebugInfo->PDB70.Signature)); + W.printNumber("PDBAge", DebugInfo->PDB70.Age); + W.printString("PDBFileName", PDBFileName); + } + } else if (D.SizeOfData != 0) { + // FIXME: Type values of 12 and 13 are commonly observed but are not in + // the documented type enum. Figure out what they mean. + ArrayRef<uint8_t> RawData; + if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, + D.SizeOfData, RawData)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printBinaryBlock("RawData", RawData); + } + } +} + +void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count, + uint64_t EntrySize, PrintExtraCB PrintExtra) { + uintptr_t TableStart, TableEnd; + if (std::error_code EC = Obj->getVaPtr(TableVA, TableStart)) + reportError(errorCodeToError(EC), Obj->getFileName()); + if (std::error_code EC = + Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd)) + reportError(errorCodeToError(EC), Obj->getFileName()); + TableEnd++; + for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) { + uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I); + raw_ostream &OS = W.startLine(); + OS << W.hex(Obj->getImageBase() + RVA); + if (PrintExtra) + PrintExtra(OS, reinterpret_cast<const uint8_t *>(I)); + OS << '\n'; + } +} + +void COFFDumper::printCOFFLoadConfig() { + LoadConfigTables Tables; + if (Obj->is64()) + printCOFFLoadConfig(Obj->getLoadConfig64(), Tables); + else + printCOFFLoadConfig(Obj->getLoadConfig32(), Tables); + + if (Tables.SEHTableVA) { + ListScope LS(W, "SEHTable"); + printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4); + } + + if (Tables.GuardFidTableVA) { + ListScope LS(W, "GuardFidTable"); + if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) { + auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { + uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4); + if (Flags) + OS << " flags " << utohexstr(Flags); + }; + printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5, + PrintGuardFlags); + } else { + printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4); + } + } + + if (Tables.GuardLJmpTableVA) { + ListScope LS(W, "GuardLJmpTable"); + printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4); + } +} + +template <typename T> +void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) { + if (!Conf) + return; + + ListScope LS(W, "LoadConfig"); + char FormattedTime[20] = {}; + time_t TDS = Conf->TimeDateStamp; + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + W.printHex("Size", Conf->Size); + + // Print everything before SecurityCookie. The vast majority of images today + // have all these fields. + if (Conf->Size < offsetof(T, SEHandlerTable)) + return; + W.printHex("TimeDateStamp", FormattedTime, TDS); + W.printHex("MajorVersion", Conf->MajorVersion); + W.printHex("MinorVersion", Conf->MinorVersion); + W.printHex("GlobalFlagsClear", Conf->GlobalFlagsClear); + W.printHex("GlobalFlagsSet", Conf->GlobalFlagsSet); + W.printHex("CriticalSectionDefaultTimeout", + Conf->CriticalSectionDefaultTimeout); + W.printHex("DeCommitFreeBlockThreshold", Conf->DeCommitFreeBlockThreshold); + W.printHex("DeCommitTotalFreeThreshold", Conf->DeCommitTotalFreeThreshold); + W.printHex("LockPrefixTable", Conf->LockPrefixTable); + W.printHex("MaximumAllocationSize", Conf->MaximumAllocationSize); + W.printHex("VirtualMemoryThreshold", Conf->VirtualMemoryThreshold); + W.printHex("ProcessHeapFlags", Conf->ProcessHeapFlags); + W.printHex("ProcessAffinityMask", Conf->ProcessAffinityMask); + W.printHex("CSDVersion", Conf->CSDVersion); + W.printHex("DependentLoadFlags", Conf->DependentLoadFlags); + W.printHex("EditList", Conf->EditList); + W.printHex("SecurityCookie", Conf->SecurityCookie); + + // Print the safe SEH table if present. + if (Conf->Size < offsetof(coff_load_configuration32, GuardCFCheckFunction)) + return; + W.printHex("SEHandlerTable", Conf->SEHandlerTable); + W.printNumber("SEHandlerCount", Conf->SEHandlerCount); + + Tables.SEHTableVA = Conf->SEHandlerTable; + Tables.SEHTableCount = Conf->SEHandlerCount; + + // Print everything before CodeIntegrity. (2015) + if (Conf->Size < offsetof(T, CodeIntegrity)) + return; + W.printHex("GuardCFCheckFunction", Conf->GuardCFCheckFunction); + W.printHex("GuardCFCheckDispatch", Conf->GuardCFCheckDispatch); + W.printHex("GuardCFFunctionTable", Conf->GuardCFFunctionTable); + W.printNumber("GuardCFFunctionCount", Conf->GuardCFFunctionCount); + W.printHex("GuardFlags", Conf->GuardFlags); + + Tables.GuardFidTableVA = Conf->GuardCFFunctionTable; + Tables.GuardFidTableCount = Conf->GuardCFFunctionCount; + Tables.GuardFlags = Conf->GuardFlags; + + // Print the rest. (2017) + if (Conf->Size < sizeof(T)) + return; + W.printHex("GuardAddressTakenIatEntryTable", + Conf->GuardAddressTakenIatEntryTable); + W.printNumber("GuardAddressTakenIatEntryCount", + Conf->GuardAddressTakenIatEntryCount); + W.printHex("GuardLongJumpTargetTable", Conf->GuardLongJumpTargetTable); + W.printNumber("GuardLongJumpTargetCount", Conf->GuardLongJumpTargetCount); + W.printHex("DynamicValueRelocTable", Conf->DynamicValueRelocTable); + W.printHex("CHPEMetadataPointer", Conf->CHPEMetadataPointer); + W.printHex("GuardRFFailureRoutine", Conf->GuardRFFailureRoutine); + W.printHex("GuardRFFailureRoutineFunctionPointer", + Conf->GuardRFFailureRoutineFunctionPointer); + W.printHex("DynamicValueRelocTableOffset", + Conf->DynamicValueRelocTableOffset); + W.printNumber("DynamicValueRelocTableSection", + Conf->DynamicValueRelocTableSection); + W.printHex("GuardRFVerifyStackPointerFunctionPointer", + Conf->GuardRFVerifyStackPointerFunctionPointer); + W.printHex("HotPatchTableOffset", Conf->HotPatchTableOffset); + + Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable; + Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount; +} + +void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) { + W.printHex("BaseOfData", Hdr->BaseOfData); +} + +void COFFDumper::printBaseOfDataField(const pe32plus_header *) {} + +void COFFDumper::printCodeViewDebugInfo() { + // Print types first to build CVUDTNames, then print symbols. + for (const SectionRef &S : Obj->sections()) { + StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName()); + // .debug$T is a standard CodeView type section, while .debug$P is the same + // format but used for MSVC precompiled header object files. + if (SectionName == ".debug$T" || SectionName == ".debug$P") + printCodeViewTypeSection(SectionName, S); + } + for (const SectionRef &S : Obj->sections()) { + StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName()); + if (SectionName == ".debug$S") + printCodeViewSymbolSection(SectionName, S); + } +} + +void COFFDumper::initializeFileAndStringTables(BinaryStreamReader &Reader) { + while (Reader.bytesRemaining() > 0 && + (!CVFileChecksumTable.valid() || !CVStringTable.valid())) { + // The section consists of a number of subsection in the following format: + // |SubSectionType|SubSectionSize|Contents...| + uint32_t SubType, SubSectionSize; + + if (Error E = Reader.readInteger(SubType)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Reader.readInteger(SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); + + StringRef Contents; + if (Error E = Reader.readFixedString(Contents, SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); + + BinaryStreamRef ST(Contents, support::little); + switch (DebugSubsectionKind(SubType)) { + case DebugSubsectionKind::FileChecksums: + if (Error E = CVFileChecksumTable.initialize(ST)) + reportError(std::move(E), Obj->getFileName()); + break; + case DebugSubsectionKind::StringTable: + if (Error E = CVStringTable.initialize(ST)) + reportError(std::move(E), Obj->getFileName()); + break; + default: + break; + } + + uint32_t PaddedSize = alignTo(SubSectionSize, 4); + if (Error E = Reader.skip(PaddedSize - SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); + } +} + +void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, + const SectionRef &Section) { + StringRef SectionContents = + unwrapOrError(Obj->getFileName(), Section.getContents()); + StringRef Data = SectionContents; + + SmallVector<StringRef, 10> FunctionNames; + StringMap<StringRef> FunctionLineTables; + + ListScope D(W, "CodeViewDebugInfo"); + // Print the section to allow correlation with printSectionHeaders. + W.printNumber("Section", SectionName, Obj->getSectionID(Section)); + + uint32_t Magic; + if (Error E = consume(Data, Magic)) + reportError(std::move(E), Obj->getFileName()); + + W.printHex("Magic", Magic); + if (Magic != COFF::DEBUG_SECTION_MAGIC) + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + + BinaryStreamReader FSReader(Data, support::little); + initializeFileAndStringTables(FSReader); + + // TODO: Convert this over to using ModuleSubstreamVisitor. + while (!Data.empty()) { + // The section consists of a number of subsection in the following format: + // |SubSectionType|SubSectionSize|Contents...| + uint32_t SubType, SubSectionSize; + if (Error E = consume(Data, SubType)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = consume(Data, SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); + + ListScope S(W, "Subsection"); + // Dump the subsection as normal even if the ignore bit is set. + if (SubType & SubsectionIgnoreFlag) { + W.printHex("IgnoredSubsectionKind", SubType); + SubType &= ~SubsectionIgnoreFlag; + } + W.printEnum("SubSectionType", SubType, makeArrayRef(SubSectionTypes)); + W.printHex("SubSectionSize", SubSectionSize); + + // Get the contents of the subsection. + if (SubSectionSize > Data.size()) + return reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + StringRef Contents = Data.substr(0, SubSectionSize); + + // Add SubSectionSize to the current offset and align that offset to find + // the next subsection. + size_t SectionOffset = Data.data() - SectionContents.data(); + size_t NextOffset = SectionOffset + SubSectionSize; + NextOffset = alignTo(NextOffset, 4); + if (NextOffset > SectionContents.size()) + return reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + Data = SectionContents.drop_front(NextOffset); + + // Optionally print the subsection bytes in case our parsing gets confused + // later. + if (opts::CodeViewSubsectionBytes) + printBinaryBlockWithRelocs("SubSectionContents", Section, SectionContents, + Contents); + + switch (DebugSubsectionKind(SubType)) { + case DebugSubsectionKind::Symbols: + printCodeViewSymbolsSubsection(Contents, Section, SectionContents); + break; + + case DebugSubsectionKind::InlineeLines: + printCodeViewInlineeLines(Contents); + break; + + case DebugSubsectionKind::FileChecksums: + printCodeViewFileChecksums(Contents); + break; + + case DebugSubsectionKind::Lines: { + // Holds a PC to file:line table. Some data to parse this subsection is + // stored in the other subsections, so just check sanity and store the + // pointers for deferred processing. + + if (SubSectionSize < 12) { + // There should be at least three words to store two function + // relocations and size of the code. + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + return; + } + + StringRef LinkageName; + if (std::error_code EC = resolveSymbolName(Obj->getCOFFSection(Section), + SectionOffset, LinkageName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + W.printString("LinkageName", LinkageName); + if (FunctionLineTables.count(LinkageName) != 0) { + // Saw debug info for this function already? + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + return; + } + + FunctionLineTables[LinkageName] = Contents; + FunctionNames.push_back(LinkageName); + break; + } + case DebugSubsectionKind::FrameData: { + // First four bytes is a relocation against the function. + BinaryStreamReader SR(Contents, llvm::support::little); + + DebugFrameDataSubsectionRef FrameData; + if (Error E = FrameData.initialize(SR)) + reportError(std::move(E), Obj->getFileName()); + + StringRef LinkageName; + if (std::error_code EC = + resolveSymbolName(Obj->getCOFFSection(Section), SectionContents, + FrameData.getRelocPtr(), LinkageName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printString("LinkageName", LinkageName); + + // To find the active frame description, search this array for the + // smallest PC range that includes the current PC. + for (const auto &FD : FrameData) { + StringRef FrameFunc = unwrapOrError( + Obj->getFileName(), CVStringTable.getString(FD.FrameFunc)); + + DictScope S(W, "FrameData"); + W.printHex("RvaStart", FD.RvaStart); + W.printHex("CodeSize", FD.CodeSize); + W.printHex("LocalSize", FD.LocalSize); + W.printHex("ParamsSize", FD.ParamsSize); + W.printHex("MaxStackSize", FD.MaxStackSize); + W.printHex("PrologSize", FD.PrologSize); + W.printHex("SavedRegsSize", FD.SavedRegsSize); + W.printFlags("Flags", FD.Flags, makeArrayRef(FrameDataFlags)); + + // The FrameFunc string is a small RPN program. It can be broken up into + // statements that end in the '=' operator, which assigns the value on + // the top of the stack to the previously pushed variable. Variables can + // be temporary values ($T0) or physical registers ($esp). Print each + // assignment on its own line to make these programs easier to read. + { + ListScope FFS(W, "FrameFunc"); + while (!FrameFunc.empty()) { + size_t EqOrEnd = FrameFunc.find('='); + if (EqOrEnd == StringRef::npos) + EqOrEnd = FrameFunc.size(); + else + ++EqOrEnd; + StringRef Stmt = FrameFunc.substr(0, EqOrEnd); + W.printString(Stmt); + FrameFunc = FrameFunc.drop_front(EqOrEnd).trim(); + } + } + } + break; + } + + // Do nothing for unrecognized subsections. + default: + break; + } + W.flush(); + } + + // Dump the line tables now that we've read all the subsections and know all + // the required information. + for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) { + StringRef Name = FunctionNames[I]; + ListScope S(W, "FunctionLineTable"); + W.printString("LinkageName", Name); + + BinaryStreamReader Reader(FunctionLineTables[Name], support::little); + + DebugLinesSubsectionRef LineInfo; + if (Error E = LineInfo.initialize(Reader)) + reportError(std::move(E), Obj->getFileName()); + + W.printHex("Flags", LineInfo.header()->Flags); + W.printHex("CodeSize", LineInfo.header()->CodeSize); + for (const auto &Entry : LineInfo) { + + ListScope S(W, "FilenameSegment"); + printFileNameForOffset("Filename", Entry.NameIndex); + uint32_t ColumnIndex = 0; + for (const auto &Line : Entry.LineNumbers) { + if (Line.Offset >= LineInfo.header()->CodeSize) { + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + return; + } + + std::string PC = formatv("+{0:X}", uint32_t(Line.Offset)); + ListScope PCScope(W, PC); + codeview::LineInfo LI(Line.Flags); + + if (LI.isAlwaysStepInto()) + W.printString("StepInto", StringRef("Always")); + else if (LI.isNeverStepInto()) + W.printString("StepInto", StringRef("Never")); + else + W.printNumber("LineNumberStart", LI.getStartLine()); + W.printNumber("LineNumberEndDelta", LI.getLineDelta()); + W.printBoolean("IsStatement", LI.isStatement()); + if (LineInfo.hasColumnInfo()) { + W.printNumber("ColStart", Entry.Columns[ColumnIndex].StartColumn); + W.printNumber("ColEnd", Entry.Columns[ColumnIndex].EndColumn); + ++ColumnIndex; + } + } + } + } +} + +void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + StringRef SectionContents) { + ArrayRef<uint8_t> BinaryData(Subsection.bytes_begin(), + Subsection.bytes_end()); + auto CODD = std::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj, + SectionContents); + CVSymbolDumper CVSD(W, Types, CodeViewContainer::ObjectFile, std::move(CODD), + CompilationCPUType, opts::CodeViewSubsectionBytes); + CVSymbolArray Symbols; + BinaryStreamReader Reader(BinaryData, llvm::support::little); + if (Error E = Reader.readArray(Symbols, Reader.getLength())) { + W.flush(); + reportError(std::move(E), Obj->getFileName()); + } + + if (Error E = CVSD.dump(Symbols)) { + W.flush(); + reportError(std::move(E), Obj->getFileName()); + } + CompilationCPUType = CVSD.getCompilationCPUType(); + W.flush(); +} + +void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) { + BinaryStreamRef Stream(Subsection, llvm::support::little); + DebugChecksumsSubsectionRef Checksums; + if (Error E = Checksums.initialize(Stream)) + reportError(std::move(E), Obj->getFileName()); + + for (auto &FC : Checksums) { + DictScope S(W, "FileChecksum"); + + StringRef Filename = unwrapOrError( + Obj->getFileName(), CVStringTable.getString(FC.FileNameOffset)); + W.printHex("Filename", Filename, FC.FileNameOffset); + W.printHex("ChecksumSize", FC.Checksum.size()); + W.printEnum("ChecksumKind", uint8_t(FC.Kind), + makeArrayRef(FileChecksumKindNames)); + + W.printBinary("ChecksumBytes", FC.Checksum); + } +} + +void COFFDumper::printCodeViewInlineeLines(StringRef Subsection) { + BinaryStreamReader SR(Subsection, llvm::support::little); + DebugInlineeLinesSubsectionRef Lines; + if (Error E = Lines.initialize(SR)) + reportError(std::move(E), Obj->getFileName()); + + for (auto &Line : Lines) { + DictScope S(W, "InlineeSourceLine"); + printTypeIndex("Inlinee", Line.Header->Inlinee); + printFileNameForOffset("FileID", Line.Header->FileID); + W.printNumber("SourceLineNum", Line.Header->SourceLineNum); + + if (Lines.hasExtraFiles()) { + W.printNumber("ExtraFileCount", Line.ExtraFiles.size()); + ListScope ExtraFiles(W, "ExtraFiles"); + for (const auto &FID : Line.ExtraFiles) { + printFileNameForOffset("FileID", FID); + } + } + } +} + +StringRef COFFDumper::getFileNameForFileOffset(uint32_t FileOffset) { + // The file checksum subsection should precede all references to it. + if (!CVFileChecksumTable.valid() || !CVStringTable.valid()) + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + + auto Iter = CVFileChecksumTable.getArray().at(FileOffset); + + // Check if the file checksum table offset is valid. + if (Iter == CVFileChecksumTable.end()) + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + + return unwrapOrError(Obj->getFileName(), + CVStringTable.getString(Iter->FileNameOffset)); +} + +void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) { + W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset); +} + +void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs, + MergingTypeTableBuilder &CVTypes, + GlobalTypeTableBuilder &GlobalCVIDs, + GlobalTypeTableBuilder &GlobalCVTypes, + bool GHash) { + for (const SectionRef &S : Obj->sections()) { + StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName()); + if (SectionName == ".debug$T") { + StringRef Data = unwrapOrError(Obj->getFileName(), S.getContents()); + uint32_t Magic; + if (Error E = consume(Data, Magic)) + reportError(std::move(E), Obj->getFileName()); + + if (Magic != 4) + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + + CVTypeArray Types; + BinaryStreamReader Reader(Data, llvm::support::little); + if (auto EC = Reader.readArray(Types, Reader.getLength())) { + consumeError(std::move(EC)); + W.flush(); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + } + SmallVector<TypeIndex, 128> SourceToDest; + Optional<uint32_t> PCHSignature; + if (GHash) { + std::vector<GloballyHashedType> Hashes = + GloballyHashedType::hashTypes(Types); + if (Error E = + mergeTypeAndIdRecords(GlobalCVIDs, GlobalCVTypes, SourceToDest, + Types, Hashes, PCHSignature)) + return reportError(std::move(E), Obj->getFileName()); + } else { + if (Error E = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, + PCHSignature)) + return reportError(std::move(E), Obj->getFileName()); + } + } + } +} + +void COFFDumper::printCodeViewTypeSection(StringRef SectionName, + const SectionRef &Section) { + ListScope D(W, "CodeViewTypes"); + W.printNumber("Section", SectionName, Obj->getSectionID(Section)); + + StringRef Data = unwrapOrError(Obj->getFileName(), Section.getContents()); + if (opts::CodeViewSubsectionBytes) + W.printBinaryBlock("Data", Data); + + uint32_t Magic; + if (Error E = consume(Data, Magic)) + reportError(std::move(E), Obj->getFileName()); + + W.printHex("Magic", Magic); + if (Magic != COFF::DEBUG_SECTION_MAGIC) + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + + Types.reset(Data, 100); + + TypeDumpVisitor TDV(Types, &W, opts::CodeViewSubsectionBytes); + if (Error E = codeview::visitTypeStream(Types, TDV)) + reportError(std::move(E), Obj->getFileName()); + + W.flush(); +} + +void COFFDumper::printSectionHeaders() { + ListScope SectionsD(W, "Sections"); + int SectionNumber = 0; + for (const SectionRef &Sec : Obj->sections()) { + ++SectionNumber; + const coff_section *Section = Obj->getCOFFSection(Sec); + + StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName()); + + DictScope D(W, "Section"); + W.printNumber("Number", SectionNumber); + W.printBinary("Name", Name, Section->Name); + W.printHex ("VirtualSize", Section->VirtualSize); + W.printHex ("VirtualAddress", Section->VirtualAddress); + W.printNumber("RawDataSize", Section->SizeOfRawData); + W.printHex ("PointerToRawData", Section->PointerToRawData); + W.printHex ("PointerToRelocations", Section->PointerToRelocations); + W.printHex ("PointerToLineNumbers", Section->PointerToLinenumbers); + W.printNumber("RelocationCount", Section->NumberOfRelocations); + W.printNumber("LineNumberCount", Section->NumberOfLinenumbers); + W.printFlags ("Characteristics", Section->Characteristics, + makeArrayRef(ImageSectionCharacteristics), + COFF::SectionCharacteristics(0x00F00000)); + + if (opts::SectionRelocations) { + ListScope D(W, "Relocations"); + for (const RelocationRef &Reloc : Sec.relocations()) + printRelocation(Sec, Reloc); + } + + if (opts::SectionSymbols) { + ListScope D(W, "Symbols"); + for (const SymbolRef &Symbol : Obj->symbols()) { + if (!Sec.containsSymbol(Symbol)) + continue; + + printSymbol(Symbol); + } + } + + if (opts::SectionData && + !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { + StringRef Data = unwrapOrError(Obj->getFileName(), Sec.getContents()); + W.printBinaryBlock("SectionData", Data); + } + } +} + +void COFFDumper::printRelocations() { + ListScope D(W, "Relocations"); + + int SectionNumber = 0; + for (const SectionRef &Section : Obj->sections()) { + ++SectionNumber; + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); + + bool PrintedGroup = false; + for (const RelocationRef &Reloc : Section.relocations()) { + if (!PrintedGroup) { + W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; + W.indent(); + PrintedGroup = true; + } + + printRelocation(Section, Reloc); + } + + if (PrintedGroup) { + W.unindent(); + W.startLine() << "}\n"; + } + } +} + +void COFFDumper::printRelocation(const SectionRef &Section, + const RelocationRef &Reloc, uint64_t Bias) { + uint64_t Offset = Reloc.getOffset() - Bias; + uint64_t RelocType = Reloc.getType(); + SmallString<32> RelocName; + StringRef SymbolName; + Reloc.getTypeName(RelocName); + symbol_iterator Symbol = Reloc.getSymbol(); + int64_t SymbolIndex = -1; + if (Symbol != Obj->symbol_end()) { + Expected<StringRef> SymbolNameOrErr = Symbol->getName(); + if (!SymbolNameOrErr) + reportError(SymbolNameOrErr.takeError(), Obj->getFileName()); + + SymbolName = *SymbolNameOrErr; + SymbolIndex = Obj->getSymbolIndex(Obj->getCOFFSymbol(*Symbol)); + } + + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printHex("Offset", Offset); + W.printNumber("Type", RelocName, RelocType); + W.printString("Symbol", SymbolName.empty() ? "-" : SymbolName); + W.printNumber("SymbolIndex", SymbolIndex); + } else { + raw_ostream& OS = W.startLine(); + OS << W.hex(Offset) + << " " << RelocName + << " " << (SymbolName.empty() ? "-" : SymbolName) + << " (" << SymbolIndex << ")" + << "\n"; + } +} + +void COFFDumper::printSymbols() { + ListScope Group(W, "Symbols"); + + for (const SymbolRef &Symbol : Obj->symbols()) + printSymbol(Symbol); +} + +void COFFDumper::printDynamicSymbols() { ListScope Group(W, "DynamicSymbols"); } + +static Expected<StringRef> +getSectionName(const llvm::object::COFFObjectFile *Obj, int32_t SectionNumber, + const coff_section *Section) { + if (Section) + return Obj->getSectionName(Section); + if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) + return StringRef("IMAGE_SYM_DEBUG"); + if (SectionNumber == llvm::COFF::IMAGE_SYM_ABSOLUTE) + return StringRef("IMAGE_SYM_ABSOLUTE"); + if (SectionNumber == llvm::COFF::IMAGE_SYM_UNDEFINED) + return StringRef("IMAGE_SYM_UNDEFINED"); + return StringRef(""); +} + +void COFFDumper::printSymbol(const SymbolRef &Sym) { + DictScope D(W, "Symbol"); + + COFFSymbolRef Symbol = Obj->getCOFFSymbol(Sym); + const coff_section *Section; + if (std::error_code EC = Obj->getSection(Symbol.getSectionNumber(), Section)) { + W.startLine() << "Invalid section number: " << EC.message() << "\n"; + W.flush(); + return; + } + + StringRef SymbolName; + if (Obj->getSymbolName(Symbol, SymbolName)) + SymbolName = ""; + + StringRef SectionName; + if (Expected<StringRef> NameOrErr = + getSectionName(Obj, Symbol.getSectionNumber(), Section)) + SectionName = *NameOrErr; + + W.printString("Name", SymbolName); + W.printNumber("Value", Symbol.getValue()); + W.printNumber("Section", SectionName, Symbol.getSectionNumber()); + W.printEnum ("BaseType", Symbol.getBaseType(), makeArrayRef(ImageSymType)); + W.printEnum ("ComplexType", Symbol.getComplexType(), + makeArrayRef(ImageSymDType)); + W.printEnum ("StorageClass", Symbol.getStorageClass(), + makeArrayRef(ImageSymClass)); + W.printNumber("AuxSymbolCount", Symbol.getNumberOfAuxSymbols()); + + for (uint8_t I = 0; I < Symbol.getNumberOfAuxSymbols(); ++I) { + if (Symbol.isFunctionDefinition()) { + const coff_aux_function_definition *Aux; + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + DictScope AS(W, "AuxFunctionDef"); + W.printNumber("TagIndex", Aux->TagIndex); + W.printNumber("TotalSize", Aux->TotalSize); + W.printHex("PointerToLineNumber", Aux->PointerToLinenumber); + W.printHex("PointerToNextFunction", Aux->PointerToNextFunction); + + } else if (Symbol.isAnyUndefined()) { + const coff_aux_weak_external *Aux; + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + Expected<COFFSymbolRef> Linked = Obj->getSymbol(Aux->TagIndex); + if (!Linked) + reportError(Linked.takeError(), Obj->getFileName()); + + StringRef LinkedName; + if (std::error_code EC = Obj->getSymbolName(*Linked, LinkedName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + DictScope AS(W, "AuxWeakExternal"); + W.printNumber("Linked", LinkedName, Aux->TagIndex); + W.printEnum ("Search", Aux->Characteristics, + makeArrayRef(WeakExternalCharacteristics)); + + } else if (Symbol.isFileRecord()) { + const char *FileName; + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, FileName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + DictScope AS(W, "AuxFileRecord"); + + StringRef Name(FileName, Symbol.getNumberOfAuxSymbols() * + Obj->getSymbolTableEntrySize()); + W.printString("FileName", Name.rtrim(StringRef("\0", 1))); + break; + } else if (Symbol.isSectionDefinition()) { + const coff_aux_section_definition *Aux; + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + int32_t AuxNumber = Aux->getNumber(Symbol.isBigObj()); + + DictScope AS(W, "AuxSectionDef"); + W.printNumber("Length", Aux->Length); + W.printNumber("RelocationCount", Aux->NumberOfRelocations); + W.printNumber("LineNumberCount", Aux->NumberOfLinenumbers); + W.printHex("Checksum", Aux->CheckSum); + W.printNumber("Number", AuxNumber); + W.printEnum("Selection", Aux->Selection, makeArrayRef(ImageCOMDATSelect)); + + if (Section && Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT + && Aux->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + const coff_section *Assoc; + StringRef AssocName = ""; + if (std::error_code EC = Obj->getSection(AuxNumber, Assoc)) + reportError(errorCodeToError(EC), Obj->getFileName()); + Expected<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc); + if (!Res) + reportError(Res.takeError(), Obj->getFileName()); + AssocName = *Res; + + W.printNumber("AssocSection", AssocName, AuxNumber); + } + } else if (Symbol.isCLRToken()) { + const coff_aux_clr_token *Aux; + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + Expected<COFFSymbolRef> ReferredSym = + Obj->getSymbol(Aux->SymbolTableIndex); + if (!ReferredSym) + reportError(ReferredSym.takeError(), Obj->getFileName()); + + StringRef ReferredName; + if (std::error_code EC = Obj->getSymbolName(*ReferredSym, ReferredName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + DictScope AS(W, "AuxCLRToken"); + W.printNumber("AuxType", Aux->AuxType); + W.printNumber("Reserved", Aux->Reserved); + W.printNumber("SymbolTableIndex", ReferredName, Aux->SymbolTableIndex); + + } else { + W.startLine() << "<unhandled auxiliary record>\n"; + } + } +} + +void COFFDumper::printUnwindInfo() { + ListScope D(W, "UnwindInformation"); + switch (Obj->getMachine()) { + case COFF::IMAGE_FILE_MACHINE_AMD64: { + Win64EH::Dumper Dumper(W); + Win64EH::Dumper::SymbolResolver + Resolver = [](const object::coff_section *Section, uint64_t Offset, + SymbolRef &Symbol, void *user_data) -> std::error_code { + COFFDumper *Dumper = reinterpret_cast<COFFDumper *>(user_data); + return Dumper->resolveSymbol(Section, Offset, Symbol); + }; + Win64EH::Dumper::Context Ctx(*Obj, Resolver, this); + Dumper.printData(Ctx); + break; + } + case COFF::IMAGE_FILE_MACHINE_ARM64: + case COFF::IMAGE_FILE_MACHINE_ARMNT: { + ARM::WinEH::Decoder Decoder(W, Obj->getMachine() == + COFF::IMAGE_FILE_MACHINE_ARM64); + // TODO Propagate the error. + consumeError(Decoder.dumpProcedureData(*Obj)); + break; + } + default: + W.printEnum("unsupported Image Machine", Obj->getMachine(), + makeArrayRef(ImageFileMachineType)); + break; + } +} + +void COFFDumper::printNeededLibraries() { + ListScope D(W, "NeededLibraries"); + + using LibsTy = std::vector<StringRef>; + LibsTy Libs; + + for (const ImportDirectoryEntryRef &DirRef : Obj->import_directories()) { + StringRef Name; + if (!DirRef.getName(Name)) + Libs.push_back(Name); + } + + llvm::stable_sort(Libs); + + for (const auto &L : Libs) { + W.startLine() << L << "\n"; + } +} + +void COFFDumper::printImportedSymbols( + iterator_range<imported_symbol_iterator> Range) { + for (const ImportedSymbolRef &I : Range) { + StringRef Sym; + if (std::error_code EC = I.getSymbolName(Sym)) + reportError(errorCodeToError(EC), Obj->getFileName()); + uint16_t Ordinal; + if (std::error_code EC = I.getOrdinal(Ordinal)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printNumber("Symbol", Sym, Ordinal); + } +} + +void COFFDumper::printDelayImportedSymbols( + const DelayImportDirectoryEntryRef &I, + iterator_range<imported_symbol_iterator> Range) { + int Index = 0; + for (const ImportedSymbolRef &S : Range) { + DictScope Import(W, "Import"); + StringRef Sym; + if (std::error_code EC = S.getSymbolName(Sym)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + uint16_t Ordinal; + if (std::error_code EC = S.getOrdinal(Ordinal)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printNumber("Symbol", Sym, Ordinal); + + uint64_t Addr; + if (std::error_code EC = I.getImportAddress(Index++, Addr)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printHex("Address", Addr); + } +} + +void COFFDumper::printCOFFImports() { + // Regular imports + for (const ImportDirectoryEntryRef &I : Obj->import_directories()) { + DictScope Import(W, "Import"); + StringRef Name; + if (std::error_code EC = I.getName(Name)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printString("Name", Name); + uint32_t ILTAddr; + if (std::error_code EC = I.getImportLookupTableRVA(ILTAddr)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printHex("ImportLookupTableRVA", ILTAddr); + uint32_t IATAddr; + if (std::error_code EC = I.getImportAddressTableRVA(IATAddr)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printHex("ImportAddressTableRVA", IATAddr); + // The import lookup table can be missing with certain older linkers, so + // fall back to the import address table in that case. + if (ILTAddr) + printImportedSymbols(I.lookup_table_symbols()); + else + printImportedSymbols(I.imported_symbols()); + } + + // Delay imports + for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) { + DictScope Import(W, "DelayImport"); + StringRef Name; + if (std::error_code EC = I.getName(Name)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printString("Name", Name); + const delay_import_directory_table_entry *Table; + if (std::error_code EC = I.getDelayImportTable(Table)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printHex("Attributes", Table->Attributes); + W.printHex("ModuleHandle", Table->ModuleHandle); + W.printHex("ImportAddressTable", Table->DelayImportAddressTable); + W.printHex("ImportNameTable", Table->DelayImportNameTable); + W.printHex("BoundDelayImportTable", Table->BoundDelayImportTable); + W.printHex("UnloadDelayImportTable", Table->UnloadDelayImportTable); + printDelayImportedSymbols(I, I.imported_symbols()); + } +} + +void COFFDumper::printCOFFExports() { + for (const ExportDirectoryEntryRef &E : Obj->export_directories()) { + DictScope Export(W, "Export"); + + StringRef Name; + uint32_t Ordinal, RVA; + + if (std::error_code EC = E.getSymbolName(Name)) + reportError(errorCodeToError(EC), Obj->getFileName()); + if (std::error_code EC = E.getOrdinal(Ordinal)) + reportError(errorCodeToError(EC), Obj->getFileName()); + if (std::error_code EC = E.getExportRVA(RVA)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + W.printNumber("Ordinal", Ordinal); + W.printString("Name", Name); + W.printHex("RVA", RVA); + } +} + +void COFFDumper::printCOFFDirectives() { + for (const SectionRef &Section : Obj->sections()) { + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); + if (Name != ".drectve") + continue; + + StringRef Contents = + unwrapOrError(Obj->getFileName(), Section.getContents()); + W.printString("Directive(s)", Contents); + } +} + +static std::string getBaseRelocTypeName(uint8_t Type) { + switch (Type) { + case COFF::IMAGE_REL_BASED_ABSOLUTE: return "ABSOLUTE"; + case COFF::IMAGE_REL_BASED_HIGH: return "HIGH"; + case COFF::IMAGE_REL_BASED_LOW: return "LOW"; + case COFF::IMAGE_REL_BASED_HIGHLOW: return "HIGHLOW"; + case COFF::IMAGE_REL_BASED_HIGHADJ: return "HIGHADJ"; + case COFF::IMAGE_REL_BASED_ARM_MOV32T: return "ARM_MOV32(T)"; + case COFF::IMAGE_REL_BASED_DIR64: return "DIR64"; + default: return "unknown (" + llvm::utostr(Type) + ")"; + } +} + +void COFFDumper::printCOFFBaseReloc() { + ListScope D(W, "BaseReloc"); + for (const BaseRelocRef &I : Obj->base_relocs()) { + uint8_t Type; + uint32_t RVA; + if (std::error_code EC = I.getRVA(RVA)) + reportError(errorCodeToError(EC), Obj->getFileName()); + if (std::error_code EC = I.getType(Type)) + reportError(errorCodeToError(EC), Obj->getFileName()); + DictScope Import(W, "Entry"); + W.printString("Type", getBaseRelocTypeName(Type)); + W.printHex("Address", RVA); + } +} + +void COFFDumper::printCOFFResources() { + ListScope ResourcesD(W, "Resources"); + for (const SectionRef &S : Obj->sections()) { + StringRef Name = unwrapOrError(Obj->getFileName(), S.getName()); + if (!Name.startswith(".rsrc")) + continue; + + StringRef Ref = unwrapOrError(Obj->getFileName(), S.getContents()); + + if ((Name == ".rsrc") || (Name == ".rsrc$01")) { + ResourceSectionRef RSF; + Error E = RSF.load(Obj, S); + if (E) + reportError(std::move(E), Obj->getFileName()); + auto &BaseTable = unwrapOrError(Obj->getFileName(), RSF.getBaseTable()); + W.printNumber("Total Number of Resources", + countTotalTableEntries(RSF, BaseTable, "Type")); + W.printHex("Base Table Address", + Obj->getCOFFSection(S)->PointerToRawData); + W.startLine() << "\n"; + printResourceDirectoryTable(RSF, BaseTable, "Type"); + } + if (opts::SectionData) + W.printBinaryBlock(Name.str() + " Data", Ref); + } +} + +uint32_t +COFFDumper::countTotalTableEntries(ResourceSectionRef RSF, + const coff_resource_dir_table &Table, + StringRef Level) { + uint32_t TotalEntries = 0; + for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; + i++) { + auto Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i)); + if (Entry.Offset.isSubDir()) { + StringRef NextLevel; + if (Level == "Name") + NextLevel = "Language"; + else + NextLevel = "Name"; + auto &NextTable = + unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(Entry)); + TotalEntries += countTotalTableEntries(RSF, NextTable, NextLevel); + } else { + TotalEntries += 1; + } + } + return TotalEntries; +} + +void COFFDumper::printResourceDirectoryTable( + ResourceSectionRef RSF, const coff_resource_dir_table &Table, + StringRef Level) { + + W.printNumber("Number of String Entries", Table.NumberOfNameEntries); + W.printNumber("Number of ID Entries", Table.NumberOfIDEntries); + + // Iterate through level in resource directory tree. + for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; + i++) { + auto Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i)); + StringRef Name; + SmallString<20> IDStr; + raw_svector_ostream OS(IDStr); + if (i < Table.NumberOfNameEntries) { + ArrayRef<UTF16> RawEntryNameString = + unwrapOrError(Obj->getFileName(), RSF.getEntryNameString(Entry)); + std::vector<UTF16> EndianCorrectedNameString; + if (llvm::sys::IsBigEndianHost) { + EndianCorrectedNameString.resize(RawEntryNameString.size() + 1); + std::copy(RawEntryNameString.begin(), RawEntryNameString.end(), + EndianCorrectedNameString.begin() + 1); + EndianCorrectedNameString[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; + RawEntryNameString = makeArrayRef(EndianCorrectedNameString); + } + std::string EntryNameString; + if (!llvm::convertUTF16ToUTF8String(RawEntryNameString, EntryNameString)) + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); + OS << ": "; + OS << EntryNameString; + } else { + if (Level == "Type") { + OS << ": "; + printResourceTypeName(Entry.Identifier.ID, OS); + } else { + OS << ": (ID " << Entry.Identifier.ID << ")"; + } + } + Name = StringRef(IDStr); + ListScope ResourceType(W, Level.str() + Name.str()); + if (Entry.Offset.isSubDir()) { + W.printHex("Table Offset", Entry.Offset.value()); + StringRef NextLevel; + if (Level == "Name") + NextLevel = "Language"; + else + NextLevel = "Name"; + auto &NextTable = + unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(Entry)); + printResourceDirectoryTable(RSF, NextTable, NextLevel); + } else { + W.printHex("Entry Offset", Entry.Offset.value()); + char FormattedTime[20] = {}; + time_t TDS = time_t(Table.TimeDateStamp); + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + W.printHex("Time/Date Stamp", FormattedTime, Table.TimeDateStamp); + W.printNumber("Major Version", Table.MajorVersion); + W.printNumber("Minor Version", Table.MinorVersion); + W.printNumber("Characteristics", Table.Characteristics); + ListScope DataScope(W, "Data"); + auto &DataEntry = + unwrapOrError(Obj->getFileName(), RSF.getEntryData(Entry)); + W.printHex("DataRVA", DataEntry.DataRVA); + W.printNumber("DataSize", DataEntry.DataSize); + W.printNumber("Codepage", DataEntry.Codepage); + W.printNumber("Reserved", DataEntry.Reserved); + StringRef Contents = + unwrapOrError(Obj->getFileName(), RSF.getContents(DataEntry)); + W.printBinaryBlock("Data", Contents); + } + } +} + +void COFFDumper::printStackMap() const { + object::SectionRef StackMapSection; + for (auto Sec : Obj->sections()) { + StringRef Name; + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == ".llvm_stackmaps") { + StackMapSection = Sec; + break; + } + } + + if (StackMapSection == object::SectionRef()) + return; + + StringRef StackMapContents = + unwrapOrError(Obj->getFileName(), StackMapSection.getContents()); + ArrayRef<uint8_t> StackMapContentsArray = + arrayRefFromStringRef(StackMapContents); + + if (Obj->isLittleEndian()) + prettyPrintStackMap( + W, StackMapParser<support::little>(StackMapContentsArray)); + else + prettyPrintStackMap( + W, StackMapParser<support::big>(StackMapContentsArray)); +} + +void COFFDumper::printAddrsig() { + object::SectionRef AddrsigSection; + for (auto Sec : Obj->sections()) { + StringRef Name; + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == ".llvm_addrsig") { + AddrsigSection = Sec; + break; + } + } + + if (AddrsigSection == object::SectionRef()) + return; + + StringRef AddrsigContents = + unwrapOrError(Obj->getFileName(), AddrsigSection.getContents()); + ArrayRef<uint8_t> AddrsigContentsArray(AddrsigContents.bytes_begin(), + AddrsigContents.size()); + + ListScope L(W, "Addrsig"); + const uint8_t *Cur = AddrsigContents.bytes_begin(); + const uint8_t *End = AddrsigContents.bytes_end(); + while (Cur != End) { + unsigned Size; + const char *Err; + uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err); + if (Err) + reportError(createError(Err), Obj->getFileName()); + + Expected<COFFSymbolRef> Sym = Obj->getSymbol(SymIndex); + if (!Sym) + reportError(Sym.takeError(), Obj->getFileName()); + + StringRef SymName; + if (std::error_code EC = Obj->getSymbolName(*Sym, SymName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + + W.printNumber("Sym", SymName, SymIndex); + Cur += Size; + } +} + +void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, + ArrayRef<ArrayRef<uint8_t>> IpiRecords, + ArrayRef<ArrayRef<uint8_t>> TpiRecords) { + TypeTableCollection TpiTypes(TpiRecords); + { + ListScope S(Writer, "MergedTypeStream"); + TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes); + if (Error Err = codeview::visitTypeStream(TpiTypes, TDV)) + reportError(std::move(Err), "<?>"); + Writer.flush(); + } + + // Flatten the id stream and print it next. The ID stream refers to names from + // the type stream. + TypeTableCollection IpiTypes(IpiRecords); + { + ListScope S(Writer, "MergedIDStream"); + TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes); + TDV.setIpiTypes(IpiTypes); + if (Error Err = codeview::visitTypeStream(IpiTypes, TDV)) + reportError(std::move(Err), "<?>"); + Writer.flush(); + } +} diff --git a/llvm/tools/llvm-readobj/COFFImportDumper.cpp b/llvm/tools/llvm-readobj/COFFImportDumper.cpp new file mode 100644 index 000000000000..c9d5e82263db --- /dev/null +++ b/llvm/tools/llvm-readobj/COFFImportDumper.cpp @@ -0,0 +1,58 @@ +//===-- COFFImportDumper.cpp - COFF import library dumper -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the COFF import library dumper for llvm-readobj. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm::object; + +namespace llvm { + +void dumpCOFFImportFile(const COFFImportFile *File, ScopedPrinter &Writer) { + Writer.startLine() << '\n'; + Writer.printString("File", File->getFileName()); + Writer.printString("Format", "COFF-import-file"); + + const coff_import_header *H = File->getCOFFImportHeader(); + switch (H->getType()) { + case COFF::IMPORT_CODE: Writer.printString("Type", "code"); break; + case COFF::IMPORT_DATA: Writer.printString("Type", "data"); break; + case COFF::IMPORT_CONST: Writer.printString("Type", "const"); break; + } + + switch (H->getNameType()) { + case COFF::IMPORT_ORDINAL: + Writer.printString("Name type", "ordinal"); + break; + case COFF::IMPORT_NAME: + Writer.printString("Name type", "name"); + break; + case COFF::IMPORT_NAME_NOPREFIX: + Writer.printString("Name type", "noprefix"); + break; + case COFF::IMPORT_NAME_UNDECORATE: + Writer.printString("Name type", "undecorate"); + break; + } + + for (const object::BasicSymbolRef &Sym : File->symbols()) { + raw_ostream &OS = Writer.startLine(); + OS << "Symbol: "; + cantFail(Sym.printName(OS)); + OS << "\n"; + } +} + +} // namespace llvm diff --git a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h new file mode 100644 index 000000000000..0a365d4fe72a --- /dev/null +++ b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -0,0 +1,255 @@ +//===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H +#define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H + +#include "Error.h" +#include "llvm-readobj.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/ELFObjectFile.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::ELFObjectFile<ELFT> *ObjF; + + 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::ELFObjectFile<ELFT> *ObjF) + : W(W), ObjF(ObjF) {} + + void printUnwindInformation() const; +}; + +template <class ELFT> +static const typename object::ELFObjectFile<ELFT>::Elf_Shdr * +findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) { + auto Sections = ObjF->getELFFile()->sections(); + if (Error E = Sections.takeError()) + reportError(std::move(E), ObjF->getFileName()); + + for (const auto &Shdr : *Sections) + if (Shdr.sh_addr == Addr) + return &Shdr; + return nullptr; +} + +template <typename ELFT> +void PrinterContext<ELFT>::printUnwindInformation() const { + const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const typename ELFT::Phdr *EHFramePhdr = nullptr; + + auto PHs = Obj->program_headers(); + if (Error E = PHs.takeError()) + reportError(std::move(E), ObjF->getFileName()); + + for (const auto &Phdr : *PHs) { + if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) { + EHFramePhdr = &Phdr; + if (Phdr.p_memsz != Phdr.p_filesz) + reportError(object::createError( + "p_memsz does not match p_filesz for GNU_EH_FRAME"), + ObjF->getFileName()); + break; + } + } + + if (EHFramePhdr) + printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr, + EHFramePhdr->p_memsz); + + auto Sections = Obj->sections(); + if (Error E = Sections.takeError()) + reportError(std::move(E), ObjF->getFileName()); + + for (const auto &Shdr : *Sections) { + auto SectionName = Obj->getSectionName(&Shdr); + if (Error E = SectionName.takeError()) + reportError(std::move(E), ObjF->getFileName()); + + if (*SectionName == ".eh_frame") + printEHFrame(&Shdr); + } +} + +template <typename ELFT> +void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, + uint64_t EHFrameHdrAddress, + uint64_t EHFrameHdrSize) const { + DictScope L(W, "EHFrameHeader"); + 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 object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const auto *EHFrameHdrShdr = findSectionByAddress(ObjF, EHFrameHdrAddress); + if (EHFrameHdrShdr) { + auto SectionName = Obj->getSectionName(EHFrameHdrShdr); + if (Error E = SectionName.takeError()) + reportError(std::move(E), ObjF->getFileName()); + + W.printString("Corresponding Section", *SectionName); + } + + DataExtractor DE(makeArrayRef(Obj->base() + EHFrameHdrOffset, EHFrameHdrSize), + ELFT::TargetEndianness == support::endianness::little, + ELFT::Is64Bits ? 8 : 4); + + DictScope D(W, "Header"); + uint64_t Offset = 0; + + auto Version = DE.getU8(&Offset); + W.printNumber("version", Version); + if (Version != 1) + reportError( + object::createError("only version 1 of .eh_frame_hdr is supported"), + ObjF->getFileName()); + + 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(object::createError("unexpected encoding eh_frame_ptr_enc"), + ObjF->getFileName()); + + uint64_t FDECountEnc = DE.getU8(&Offset); + W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc); + if (FDECountEnc != dwarf::DW_EH_PE_udata4) + reportError(object::createError("unexpected encoding fde_count_enc"), + ObjF->getFileName()); + + 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(object::createError("unexpected encoding table_enc"), + ObjF->getFileName()); + + 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(object::createError("initial_location is out of order"), + ObjF->getFileName()); + + 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(); + + const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); + auto Result = Obj->getSectionContents(EHFrameShdr); + if (Error E = Result.takeError()) + reportError(std::move(E), ObjF->getFileName()); + + 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(Triple::ArchType(ObjF->getArch()), /*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 diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp new file mode 100644 index 000000000000..57144882c4b4 --- /dev/null +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -0,0 +1,6120 @@ +//===- ELFDumper.cpp - ELF-specific dumper --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the ELF-specific dumper for llvm-readobj. +/// +//===----------------------------------------------------------------------===// + +#include "ARMEHABIPrinter.h" +#include "DwarfCFIEHPrinter.h" +#include "Error.h" +#include "ObjDumper.h" +#include "StackMapPrinter.h" +#include "llvm-readobj.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/RelocationResolver.h" +#include "llvm/Object/StackMapParser.h" +#include "llvm/Support/AMDGPUMetadata.h" +#include "llvm/Support/ARMAttributeParser.h" +#include "llvm/Support/ARMBuildAttributes.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MipsABIFlags.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cinttypes> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <iterator> +#include <memory> +#include <string> +#include <system_error> +#include <unordered_set> +#include <vector> + +using namespace llvm; +using namespace llvm::object; +using namespace ELF; + +#define LLVM_READOBJ_ENUM_CASE(ns, enum) \ + case ns::enum: \ + return #enum; + +#define ENUM_ENT(enum, altName) \ + { #enum, altName, ELF::enum } + +#define ENUM_ENT_1(enum) \ + { #enum, #enum, ELF::enum } + +#define LLVM_READOBJ_PHDR_ENUM(ns, enum) \ + case ns::enum: \ + return std::string(#enum).substr(3); + +#define TYPEDEF_ELF_TYPES(ELFT) \ + using ELFO = ELFFile<ELFT>; \ + using Elf_Addr = typename ELFT::Addr; \ + using Elf_Shdr = typename ELFT::Shdr; \ + using Elf_Sym = typename ELFT::Sym; \ + using Elf_Dyn = typename ELFT::Dyn; \ + using Elf_Dyn_Range = typename ELFT::DynRange; \ + using Elf_Rel = typename ELFT::Rel; \ + using Elf_Rela = typename ELFT::Rela; \ + using Elf_Relr = typename ELFT::Relr; \ + using Elf_Rel_Range = typename ELFT::RelRange; \ + using Elf_Rela_Range = typename ELFT::RelaRange; \ + using Elf_Relr_Range = typename ELFT::RelrRange; \ + using Elf_Phdr = typename ELFT::Phdr; \ + using Elf_Half = typename ELFT::Half; \ + using Elf_Ehdr = typename ELFT::Ehdr; \ + using Elf_Word = typename ELFT::Word; \ + using Elf_Hash = typename ELFT::Hash; \ + using Elf_GnuHash = typename ELFT::GnuHash; \ + using Elf_Note = typename ELFT::Note; \ + using Elf_Sym_Range = typename ELFT::SymRange; \ + using Elf_Versym = typename ELFT::Versym; \ + using Elf_Verneed = typename ELFT::Verneed; \ + using Elf_Vernaux = typename ELFT::Vernaux; \ + using Elf_Verdef = typename ELFT::Verdef; \ + using Elf_Verdaux = typename ELFT::Verdaux; \ + using Elf_CGProfile = typename ELFT::CGProfile; \ + using uintX_t = typename ELFT::uint; + +namespace { + +template <class ELFT> class DumpStyle; + +/// Represents a contiguous uniform range in the file. We cannot just create a +/// range directly because when creating one of these from the .dynamic table +/// the size, entity size and virtual address are different entries in arbitrary +/// order (DT_REL, DT_RELSZ, DT_RELENT for example). +struct DynRegionInfo { + DynRegionInfo(StringRef ObjName) : FileName(ObjName) {} + DynRegionInfo(const void *A, uint64_t S, uint64_t ES, StringRef ObjName) + : Addr(A), Size(S), EntSize(ES), FileName(ObjName) {} + + /// Address in current address space. + const void *Addr = nullptr; + /// Size in bytes of the region. + uint64_t Size = 0; + /// Size of each entity in the region. + uint64_t EntSize = 0; + + /// Name of the file. Used for error reporting. + StringRef FileName; + + template <typename Type> ArrayRef<Type> getAsArrayRef() const { + const Type *Start = reinterpret_cast<const Type *>(Addr); + if (!Start) + return {Start, Start}; + if (EntSize != sizeof(Type) || Size % EntSize) { + // TODO: Add a section index to this warning. + reportWarning(createError("invalid section size (" + Twine(Size) + + ") or entity size (" + Twine(EntSize) + ")"), + FileName); + return {Start, Start}; + } + return {Start, Start + (Size / EntSize)}; + } +}; + +template <typename ELFT> class ELFDumper : public ObjDumper { +public: + ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, ScopedPrinter &Writer); + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printDynamicRelocations() override; + void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; + void printHashSymbols() override; + void printUnwindInfo() override; + + void printDynamicTable() override; + void printNeededLibraries() override; + void printProgramHeaders(bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printHashTable() override; + void printGnuHashTable() override; + void printLoadName() override; + void printVersionInfo() override; + void printGroupSections() override; + + void printArchSpecificInfo() override; + + void printStackMap() const override; + + void printHashHistogram() override; + + void printCGProfile() override; + void printAddrsig() override; + + void printNotes() override; + + void printELFLinkerOptions() override; + void printStackSizes() override; + + const object::ELFObjectFile<ELFT> *getElfObject() const { return ObjF; }; + +private: + std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle; + + TYPEDEF_ELF_TYPES(ELFT) + + DynRegionInfo checkDRI(DynRegionInfo DRI) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + if (DRI.Addr < Obj->base() || + reinterpret_cast<const uint8_t *>(DRI.Addr) + DRI.Size > + Obj->base() + Obj->getBufSize()) + reportError(errorCodeToError(llvm::object::object_error::parse_failed), + ObjF->getFileName()); + return DRI; + } + + DynRegionInfo createDRIFrom(const Elf_Phdr *P, uintX_t EntSize) { + return checkDRI({ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, + EntSize, ObjF->getFileName()}); + } + + DynRegionInfo createDRIFrom(const Elf_Shdr *S) { + return checkDRI({ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, + S->sh_entsize, ObjF->getFileName()}); + } + + void printAttributes(); + void printMipsReginfo(); + void printMipsOptions(); + + std::pair<const Elf_Phdr *, const Elf_Shdr *> + findDynamic(const ELFFile<ELFT> *Obj); + void loadDynamicTable(const ELFFile<ELFT> *Obj); + void parseDynamicTable(); + + StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb, + bool &IsDefault) const; + void LoadVersionMap() const; + void LoadVersionNeeds(const Elf_Shdr *ec) const; + void LoadVersionDefs(const Elf_Shdr *sec) const; + + const object::ELFObjectFile<ELFT> *ObjF; + DynRegionInfo DynRelRegion; + DynRegionInfo DynRelaRegion; + DynRegionInfo DynRelrRegion; + DynRegionInfo DynPLTRelRegion; + DynRegionInfo DynSymRegion; + DynRegionInfo DynamicTable; + StringRef DynamicStringTable; + std::string SOName = "<Not found>"; + const Elf_Hash *HashTable = nullptr; + const Elf_GnuHash *GnuHashTable = nullptr; + const Elf_Shdr *DotSymtabSec = nullptr; + const Elf_Shdr *DotCGProfileSec = nullptr; + const Elf_Shdr *DotAddrsigSec = nullptr; + StringRef DynSymtabName; + ArrayRef<Elf_Word> ShndxTable; + + const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version + const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r + const Elf_Shdr *SymbolVersionDefSection = nullptr; // .gnu.version_d + + // Records for each version index the corresponding Verdef or Vernaux entry. + // This is filled the first time LoadVersionMap() is called. + class VersionMapEntry : public PointerIntPair<const void *, 1> { + public: + // If the integer is 0, this is an Elf_Verdef*. + // If the integer is 1, this is an Elf_Vernaux*. + VersionMapEntry() : PointerIntPair<const void *, 1>(nullptr, 0) {} + VersionMapEntry(const Elf_Verdef *verdef) + : PointerIntPair<const void *, 1>(verdef, 0) {} + VersionMapEntry(const Elf_Vernaux *vernaux) + : PointerIntPair<const void *, 1>(vernaux, 1) {} + + bool isNull() const { return getPointer() == nullptr; } + bool isVerdef() const { return !isNull() && getInt() == 0; } + bool isVernaux() const { return !isNull() && getInt() == 1; } + const Elf_Verdef *getVerdef() const { + return isVerdef() ? (const Elf_Verdef *)getPointer() : nullptr; + } + const Elf_Vernaux *getVernaux() const { + return isVernaux() ? (const Elf_Vernaux *)getPointer() : nullptr; + } + }; + mutable SmallVector<VersionMapEntry, 16> VersionMap; + +public: + Elf_Dyn_Range dynamic_table() const { + // A valid .dynamic section contains an array of entries terminated + // with a DT_NULL entry. However, sometimes the section content may + // continue past the DT_NULL entry, so to dump the section correctly, + // we first find the end of the entries by iterating over them. + Elf_Dyn_Range Table = DynamicTable.getAsArrayRef<Elf_Dyn>(); + + size_t Size = 0; + while (Size < Table.size()) + if (Table[Size++].getTag() == DT_NULL) + break; + + return Table.slice(0, Size); + } + + Elf_Sym_Range dynamic_symbols() const { + return DynSymRegion.getAsArrayRef<Elf_Sym>(); + } + + Elf_Rel_Range dyn_rels() const; + Elf_Rela_Range dyn_relas() const; + Elf_Relr_Range dyn_relrs() const; + std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, + bool IsDynamic) const; + void getSectionNameIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym, + StringRef &SectionName, + unsigned &SectionIndex) const; + Expected<std::string> getStaticSymbolName(uint32_t Index) const; + std::string getDynamicString(uint64_t Value) const; + StringRef getSymbolVersionByIndex(StringRef StrTab, + uint32_t VersionSymbolIndex, + bool &IsDefault) const; + + void printSymbolsHelper(bool IsDynamic) const; + void printDynamicEntry(raw_ostream &OS, uint64_t Type, uint64_t Value) const; + + const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; } + const Elf_Shdr *getDotCGProfileSec() const { return DotCGProfileSec; } + const Elf_Shdr *getDotAddrsigSec() const { return DotAddrsigSec; } + ArrayRef<Elf_Word> getShndxTable() const { return ShndxTable; } + StringRef getDynamicStringTable() const { return DynamicStringTable; } + const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; } + const DynRegionInfo &getDynRelaRegion() const { return DynRelaRegion; } + const DynRegionInfo &getDynRelrRegion() const { return DynRelrRegion; } + const DynRegionInfo &getDynPLTRelRegion() const { return DynPLTRelRegion; } + const DynRegionInfo &getDynamicTableRegion() const { return DynamicTable; } + const Elf_Hash *getHashTable() const { return HashTable; } + const Elf_GnuHash *getGnuHashTable() const { return GnuHashTable; } +}; + +template <class ELFT> +void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const { + StringRef StrTable, SymtabName; + size_t Entries = 0; + Elf_Sym_Range Syms(nullptr, nullptr); + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + if (IsDynamic) { + StrTable = DynamicStringTable; + Syms = dynamic_symbols(); + SymtabName = DynSymtabName; + if (DynSymRegion.Addr) + Entries = DynSymRegion.Size / DynSymRegion.EntSize; + } else { + if (!DotSymtabSec) + return; + StrTable = unwrapOrError(ObjF->getFileName(), + Obj->getStringTableForSymtab(*DotSymtabSec)); + Syms = unwrapOrError(ObjF->getFileName(), Obj->symbols(DotSymtabSec)); + SymtabName = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DotSymtabSec)); + Entries = DotSymtabSec->getEntityCount(); + } + if (Syms.begin() == Syms.end()) + return; + + // The st_other field has 2 logical parts. The first two bits hold the symbol + // visibility (STV_*) and the remainder hold other platform-specific values. + bool NonVisibilityBitsUsed = llvm::find_if(Syms, [](const Elf_Sym &S) { + return S.st_other & ~0x3; + }) != Syms.end(); + + ELFDumperStyle->printSymtabMessage(Obj, SymtabName, Entries, + NonVisibilityBitsUsed); + for (const auto &Sym : Syms) + ELFDumperStyle->printSymbol(Obj, &Sym, Syms.begin(), StrTable, IsDynamic, + NonVisibilityBitsUsed); +} + +template <class ELFT> class MipsGOTParser; + +template <typename ELFT> class DumpStyle { +public: + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Addr = typename ELFT::Addr; + + DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) { + FileName = this->Dumper->getElfObject()->getFileName(); + + // Dumper reports all non-critical errors as warnings. + // It does not print the same warning more than once. + WarningHandler = [this](const Twine &Msg) { + if (Warnings.insert(Msg.str()).second) + reportWarning(createError(Msg), FileName); + return Error::success(); + }; + } + + virtual ~DumpStyle() = default; + + virtual void printFileHeaders(const ELFFile<ELFT> *Obj) = 0; + virtual void printGroupSections(const ELFFile<ELFT> *Obj) = 0; + virtual void printRelocations(const ELFFile<ELFT> *Obj) = 0; + virtual void printSectionHeaders(const ELFFile<ELFT> *Obj) = 0; + virtual void printSymbols(const ELFFile<ELFT> *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) = 0; + virtual void printHashSymbols(const ELFFile<ELFT> *Obj) {} + virtual void printDynamic(const ELFFile<ELFT> *Obj) {} + virtual void printDynamicRelocations(const ELFFile<ELFT> *Obj) = 0; + virtual void printSymtabMessage(const ELFFile<ELFT> *Obj, StringRef Name, + size_t Offset, bool NonVisibilityBitsUsed) {} + virtual void printSymbol(const ELFFile<ELFT> *Obj, const Elf_Sym *Symbol, + const Elf_Sym *FirstSym, StringRef StrTable, + bool IsDynamic, bool NonVisibilityBitsUsed) = 0; + virtual void printProgramHeaders(const ELFFile<ELFT> *Obj, + bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) = 0; + virtual void printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0; + virtual void printCGProfile(const ELFFile<ELFT> *Obj) = 0; + virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0; + virtual void printNotes(const ELFFile<ELFT> *Obj) = 0; + virtual void printELFLinkerOptions(const ELFFile<ELFT> *Obj) = 0; + virtual void printStackSizes(const ELFObjectFile<ELFT> *Obj) = 0; + void printNonRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj, + std::function<void()> PrintHeader); + void printRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj, + std::function<void()> PrintHeader); + void printFunctionStackSize(const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, + SectionRef FunctionSec, + const StringRef SectionName, DataExtractor Data, + uint64_t *Offset); + void printStackSize(const ELFObjectFile<ELFT> *Obj, RelocationRef Rel, + SectionRef FunctionSec, + const StringRef &StackSizeSectionName, + const RelocationResolver &Resolver, DataExtractor Data); + virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0; + virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0; + virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0; + virtual void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) = 0; + const ELFDumper<ELFT> *dumper() const { return Dumper; } + +protected: + std::function<Error(const Twine &Msg)> WarningHandler; + StringRef FileName; + +private: + std::unordered_set<std::string> Warnings; + const ELFDumper<ELFT> *Dumper; +}; + +template <typename ELFT> class GNUStyle : public DumpStyle<ELFT> { + formatted_raw_ostream &OS; + +public: + TYPEDEF_ELF_TYPES(ELFT) + + GNUStyle(ScopedPrinter &W, ELFDumper<ELFT> *Dumper) + : DumpStyle<ELFT>(Dumper), + OS(static_cast<formatted_raw_ostream&>(W.getOStream())) { + assert (&W.getOStream() == &llvm::fouts()); + } + + void printFileHeaders(const ELFO *Obj) override; + void printGroupSections(const ELFFile<ELFT> *Obj) override; + void printRelocations(const ELFO *Obj) override; + void printSectionHeaders(const ELFO *Obj) override; + void printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) override; + void printHashSymbols(const ELFO *Obj) override; + void printDynamic(const ELFFile<ELFT> *Obj) override; + void printDynamicRelocations(const ELFO *Obj) override; + void printSymtabMessage(const ELFO *Obj, StringRef Name, size_t Offset, + bool NonVisibilityBitsUsed) override; + void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printCGProfile(const ELFFile<ELFT> *Obj) override; + void printAddrsig(const ELFFile<ELFT> *Obj) override; + void printNotes(const ELFFile<ELFT> *Obj) override; + void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override; + void printStackSizes(const ELFObjectFile<ELFT> *Obj) override; + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override; + +private: + struct Field { + std::string Str; + unsigned Column; + + Field(StringRef S, unsigned Col) : Str(S), Column(Col) {} + Field(unsigned Col) : Column(Col) {} + }; + + template <typename T, typename TEnum> + std::string printEnum(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) { + for (const auto &EnumItem : EnumValues) + if (EnumItem.Value == Value) + return EnumItem.AltName; + return to_hexString(Value, false); + } + + template <typename T, typename TEnum> + std::string printFlags(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues, + TEnum EnumMask1 = {}, TEnum EnumMask2 = {}, + TEnum EnumMask3 = {}) { + std::string Str; + for (const auto &Flag : EnumValues) { + if (Flag.Value == 0) + continue; + + TEnum EnumMask{}; + if (Flag.Value & EnumMask1) + EnumMask = EnumMask1; + else if (Flag.Value & EnumMask2) + EnumMask = EnumMask2; + else if (Flag.Value & EnumMask3) + EnumMask = EnumMask3; + bool IsEnum = (Flag.Value & EnumMask) != 0; + if ((!IsEnum && (Value & Flag.Value) == Flag.Value) || + (IsEnum && (Value & EnumMask) == Flag.Value)) { + if (!Str.empty()) + Str += ", "; + Str += Flag.AltName; + } + } + return Str; + } + + formatted_raw_ostream &printField(struct Field F) { + if (F.Column != 0) + OS.PadToColumn(F.Column); + OS << F.Str; + OS.flush(); + return OS; + } + void printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, uint32_t Sym, + StringRef StrTable, uint32_t Bucket); + void printRelocHeader(unsigned SType); + void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, + const Elf_Rela &R, bool IsRela); + void printRelocation(const ELFO *Obj, const Elf_Sym *Sym, + StringRef SymbolName, const Elf_Rela &R, bool IsRela); + void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, + StringRef StrTable, bool IsDynamic, + bool NonVisibilityBitsUsed) override; + std::string getSymbolSectionNdx(const ELFO *Obj, const Elf_Sym *Symbol, + const Elf_Sym *FirstSym); + void printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela); + bool checkTLSSections(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + bool checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + bool checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + bool checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + void printProgramHeaders(const ELFO *Obj); + void printSectionMapping(const ELFO *Obj); +}; + +template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> { +public: + TYPEDEF_ELF_TYPES(ELFT) + + LLVMStyle(ScopedPrinter &W, ELFDumper<ELFT> *Dumper) + : DumpStyle<ELFT>(Dumper), W(W) {} + + void printFileHeaders(const ELFO *Obj) override; + void printGroupSections(const ELFFile<ELFT> *Obj) override; + void printRelocations(const ELFO *Obj) override; + void printRelocations(const Elf_Shdr *Sec, const ELFO *Obj); + void printSectionHeaders(const ELFO *Obj) override; + void printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) override; + void printDynamic(const ELFFile<ELFT> *Obj) override; + void printDynamicRelocations(const ELFO *Obj) override; + void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printCGProfile(const ELFFile<ELFT> *Obj) override; + void printAddrsig(const ELFFile<ELFT> *Obj) override; + void printNotes(const ELFFile<ELFT> *Obj) override; + void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override; + void printStackSizes(const ELFObjectFile<ELFT> *Obj) override; + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override; + +private: + void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); + void printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel); + void printSymbols(const ELFO *Obj); + void printDynamicSymbols(const ELFO *Obj); + void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, + StringRef StrTable, bool IsDynamic, + bool /*NonVisibilityBitsUsed*/) override; + void printProgramHeaders(const ELFO *Obj); + void printSectionMapping(const ELFO *Obj) {} + + ScopedPrinter &W; +}; + +} // end anonymous namespace + +namespace llvm { + +template <class ELFT> +static std::error_code createELFDumper(const ELFObjectFile<ELFT> *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + Result.reset(new ELFDumper<ELFT>(Obj, Writer)); + return readobj_error::success; +} + +std::error_code createELFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + // Little-endian 32-bit + if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) + return createELFDumper(ELFObj, Writer, Result); + + // Big-endian 32-bit + if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) + return createELFDumper(ELFObj, Writer, Result); + + // Little-endian 64-bit + if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) + return createELFDumper(ELFObj, Writer, Result); + + // Big-endian 64-bit + if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) + return createELFDumper(ELFObj, Writer, Result); + + return readobj_error::unsupported_obj_file_format; +} + +} // end namespace llvm + +// Iterate through the versions needed section, and place each Elf_Vernaux +// in the VersionMap according to its index. +template <class ELFT> +void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *Sec) const { + unsigned VerneedSize = Sec->sh_size; // Size of section in bytes + unsigned VerneedEntries = Sec->sh_info; // Number of Verneed entries + const uint8_t *VerneedStart = reinterpret_cast<const uint8_t *>( + ObjF->getELFFile()->base() + Sec->sh_offset); + const uint8_t *VerneedEnd = VerneedStart + VerneedSize; + // The first Verneed entry is at the start of the section. + const uint8_t *VerneedBuf = VerneedStart; + for (unsigned VerneedIndex = 0; VerneedIndex < VerneedEntries; + ++VerneedIndex) { + if (VerneedBuf + sizeof(Elf_Verneed) > VerneedEnd) + report_fatal_error("Section ended unexpectedly while scanning " + "version needed records."); + const Elf_Verneed *Verneed = + reinterpret_cast<const Elf_Verneed *>(VerneedBuf); + if (Verneed->vn_version != ELF::VER_NEED_CURRENT) + report_fatal_error("Unexpected verneed version"); + // Iterate through the Vernaux entries + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (unsigned VernauxIndex = 0; VernauxIndex < Verneed->vn_cnt; + ++VernauxIndex) { + if (VernauxBuf + sizeof(Elf_Vernaux) > VerneedEnd) + report_fatal_error("Section ended unexpected while scanning auxiliary " + "version needed records."); + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(VernauxBuf); + size_t Index = Vernaux->vna_other & ELF::VERSYM_VERSION; + if (Index >= VersionMap.size()) + VersionMap.resize(Index + 1); + VersionMap[Index] = VersionMapEntry(Vernaux); + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } +} + +// Iterate through the version definitions, and place each Elf_Verdef +// in the VersionMap according to its index. +template <class ELFT> +void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *Sec) const { + unsigned VerdefSize = Sec->sh_size; // Size of section in bytes + unsigned VerdefEntries = Sec->sh_info; // Number of Verdef entries + const uint8_t *VerdefStart = reinterpret_cast<const uint8_t *>( + ObjF->getELFFile()->base() + Sec->sh_offset); + const uint8_t *VerdefEnd = VerdefStart + VerdefSize; + // The first Verdef entry is at the start of the section. + const uint8_t *VerdefBuf = VerdefStart; + for (unsigned VerdefIndex = 0; VerdefIndex < VerdefEntries; ++VerdefIndex) { + if (VerdefBuf + sizeof(Elf_Verdef) > VerdefEnd) + report_fatal_error("Section ended unexpectedly while scanning " + "version definitions."); + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf); + if (Verdef->vd_version != ELF::VER_DEF_CURRENT) + report_fatal_error("Unexpected verdef version"); + size_t Index = Verdef->vd_ndx & ELF::VERSYM_VERSION; + if (Index >= VersionMap.size()) + VersionMap.resize(Index + 1); + VersionMap[Index] = VersionMapEntry(Verdef); + VerdefBuf += Verdef->vd_next; + } +} + +template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const { + // If there is no dynamic symtab or version table, there is nothing to do. + if (!DynSymRegion.Addr || !SymbolVersionSection) + return; + + // Has the VersionMap already been loaded? + if (!VersionMap.empty()) + return; + + // The first two version indexes are reserved. + // Index 0 is LOCAL, index 1 is GLOBAL. + VersionMap.push_back(VersionMapEntry()); + VersionMap.push_back(VersionMapEntry()); + + if (SymbolVersionDefSection) + LoadVersionDefs(SymbolVersionDefSection); + + if (SymbolVersionNeedSection) + LoadVersionNeeds(SymbolVersionNeedSection); +} + +template <typename ELFT> +StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, + const Elf_Sym *Sym, + bool &IsDefault) const { + // This is a dynamic symbol. Look in the GNU symbol version table. + if (!SymbolVersionSection) { + // No version table. + IsDefault = false; + return ""; + } + + // Determine the position in the symbol table of this entry. + size_t EntryIndex = (reinterpret_cast<uintptr_t>(Sym) - + reinterpret_cast<uintptr_t>(DynSymRegion.Addr)) / + sizeof(Elf_Sym); + + // Get the corresponding version index entry. + const Elf_Versym *Versym = unwrapOrError( + ObjF->getFileName(), ObjF->getELFFile()->template getEntry<Elf_Versym>( + SymbolVersionSection, EntryIndex)); + return this->getSymbolVersionByIndex(StrTab, Versym->vs_index, IsDefault); +} + +static std::string maybeDemangle(StringRef Name) { + return opts::Demangle ? demangle(Name) : Name.str(); +} + +template <typename ELFT> +Expected<std::string> +ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + Expected<const typename ELFT::Sym *> SymOrErr = + Obj->getSymbol(DotSymtabSec, Index); + if (!SymOrErr) + return SymOrErr.takeError(); + + Expected<StringRef> StrTabOrErr = Obj->getStringTableForSymtab(*DotSymtabSec); + if (!StrTabOrErr) + return StrTabOrErr.takeError(); + + Expected<StringRef> NameOrErr = (*SymOrErr)->getName(*StrTabOrErr); + if (!NameOrErr) + return NameOrErr.takeError(); + return maybeDemangle(*NameOrErr); +} + +template <typename ELFT> +StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(StringRef StrTab, + uint32_t SymbolVersionIndex, + bool &IsDefault) const { + size_t VersionIndex = SymbolVersionIndex & VERSYM_VERSION; + + // Special markers for unversioned symbols. + if (VersionIndex == VER_NDX_LOCAL || VersionIndex == VER_NDX_GLOBAL) { + IsDefault = false; + return ""; + } + + // Lookup this symbol in the version table. + LoadVersionMap(); + if (VersionIndex >= VersionMap.size() || VersionMap[VersionIndex].isNull()) + reportError(createError("Invalid version entry"), ObjF->getFileName()); + const VersionMapEntry &Entry = VersionMap[VersionIndex]; + + // Get the version name string. + size_t NameOffset; + if (Entry.isVerdef()) { + // The first Verdaux entry holds the name. + NameOffset = Entry.getVerdef()->getAux()->vda_name; + IsDefault = !(SymbolVersionIndex & VERSYM_HIDDEN); + } else { + NameOffset = Entry.getVernaux()->vna_name; + IsDefault = false; + } + if (NameOffset >= StrTab.size()) + reportError(createError("Invalid string offset"), ObjF->getFileName()); + return StrTab.data() + NameOffset; +} + +template <typename ELFT> +std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, + StringRef StrTable, + bool IsDynamic) const { + std::string SymbolName = maybeDemangle( + unwrapOrError(ObjF->getFileName(), Symbol->getName(StrTable))); + + if (SymbolName.empty() && Symbol->getType() == ELF::STT_SECTION) { + unsigned SectionIndex; + StringRef SectionName; + Elf_Sym_Range Syms = unwrapOrError( + ObjF->getFileName(), ObjF->getELFFile()->symbols(DotSymtabSec)); + getSectionNameIndex(Symbol, Syms.begin(), SectionName, SectionIndex); + return SectionName; + } + + if (!IsDynamic) + return SymbolName; + + bool IsDefault; + StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); + if (!Version.empty()) { + SymbolName += (IsDefault ? "@@" : "@"); + SymbolName += Version; + } + return SymbolName; +} + +template <typename ELFT> +void ELFDumper<ELFT>::getSectionNameIndex(const Elf_Sym *Symbol, + const Elf_Sym *FirstSym, + StringRef &SectionName, + unsigned &SectionIndex) const { + SectionIndex = Symbol->st_shndx; + if (Symbol->isUndefined()) + SectionName = "Undefined"; + else if (Symbol->isProcessorSpecific()) + SectionName = "Processor Specific"; + else if (Symbol->isOSSpecific()) + SectionName = "Operating System Specific"; + else if (Symbol->isAbsolute()) + SectionName = "Absolute"; + else if (Symbol->isCommon()) + SectionName = "Common"; + else if (Symbol->isReserved() && SectionIndex != SHN_XINDEX) + SectionName = "Reserved"; + else { + if (SectionIndex == SHN_XINDEX) + SectionIndex = unwrapOrError(ObjF->getFileName(), + object::getExtendedSymbolTableIndex<ELFT>( + Symbol, FirstSym, ShndxTable)); + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const typename ELFT::Shdr *Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSection(SectionIndex)); + SectionName = unwrapOrError(ObjF->getFileName(), Obj->getSectionName(Sec)); + } +} + +template <class ELFO> +static const typename ELFO::Elf_Shdr * +findNotEmptySectionByAddress(const ELFO *Obj, StringRef FileName, + uint64_t Addr) { + for (const auto &Shdr : unwrapOrError(FileName, Obj->sections())) + if (Shdr.sh_addr == Addr && Shdr.sh_size > 0) + return &Shdr; + return nullptr; +} + +template <class ELFO> +static const typename ELFO::Elf_Shdr * +findSectionByName(const ELFO &Obj, StringRef FileName, StringRef Name) { + for (const auto &Shdr : unwrapOrError(FileName, Obj.sections())) + if (Name == unwrapOrError(FileName, Obj.getSectionName(&Shdr))) + return &Shdr; + return nullptr; +} + +static const EnumEntry<unsigned> ElfClass[] = { + {"None", "none", ELF::ELFCLASSNONE}, + {"32-bit", "ELF32", ELF::ELFCLASS32}, + {"64-bit", "ELF64", ELF::ELFCLASS64}, +}; + +static const EnumEntry<unsigned> ElfDataEncoding[] = { + {"None", "none", ELF::ELFDATANONE}, + {"LittleEndian", "2's complement, little endian", ELF::ELFDATA2LSB}, + {"BigEndian", "2's complement, big endian", ELF::ELFDATA2MSB}, +}; + +static const EnumEntry<unsigned> ElfObjectFileType[] = { + {"None", "NONE (none)", ELF::ET_NONE}, + {"Relocatable", "REL (Relocatable file)", ELF::ET_REL}, + {"Executable", "EXEC (Executable file)", ELF::ET_EXEC}, + {"SharedObject", "DYN (Shared object file)", ELF::ET_DYN}, + {"Core", "CORE (Core file)", ELF::ET_CORE}, +}; + +static const EnumEntry<unsigned> ElfOSABI[] = { + {"SystemV", "UNIX - System V", ELF::ELFOSABI_NONE}, + {"HPUX", "UNIX - HP-UX", ELF::ELFOSABI_HPUX}, + {"NetBSD", "UNIX - NetBSD", ELF::ELFOSABI_NETBSD}, + {"GNU/Linux", "UNIX - GNU", ELF::ELFOSABI_LINUX}, + {"GNU/Hurd", "GNU/Hurd", ELF::ELFOSABI_HURD}, + {"Solaris", "UNIX - Solaris", ELF::ELFOSABI_SOLARIS}, + {"AIX", "UNIX - AIX", ELF::ELFOSABI_AIX}, + {"IRIX", "UNIX - IRIX", ELF::ELFOSABI_IRIX}, + {"FreeBSD", "UNIX - FreeBSD", ELF::ELFOSABI_FREEBSD}, + {"TRU64", "UNIX - TRU64", ELF::ELFOSABI_TRU64}, + {"Modesto", "Novell - Modesto", ELF::ELFOSABI_MODESTO}, + {"OpenBSD", "UNIX - OpenBSD", ELF::ELFOSABI_OPENBSD}, + {"OpenVMS", "VMS - OpenVMS", ELF::ELFOSABI_OPENVMS}, + {"NSK", "HP - Non-Stop Kernel", ELF::ELFOSABI_NSK}, + {"AROS", "AROS", ELF::ELFOSABI_AROS}, + {"FenixOS", "FenixOS", ELF::ELFOSABI_FENIXOS}, + {"CloudABI", "CloudABI", ELF::ELFOSABI_CLOUDABI}, + {"Standalone", "Standalone App", ELF::ELFOSABI_STANDALONE} +}; + +static const EnumEntry<unsigned> SymVersionFlags[] = { + {"Base", "BASE", VER_FLG_BASE}, + {"Weak", "WEAK", VER_FLG_WEAK}, + {"Info", "INFO", VER_FLG_INFO}}; + +static const EnumEntry<unsigned> AMDGPUElfOSABI[] = { + {"AMDGPU_HSA", "AMDGPU - HSA", ELF::ELFOSABI_AMDGPU_HSA}, + {"AMDGPU_PAL", "AMDGPU - PAL", ELF::ELFOSABI_AMDGPU_PAL}, + {"AMDGPU_MESA3D", "AMDGPU - MESA3D", ELF::ELFOSABI_AMDGPU_MESA3D} +}; + +static const EnumEntry<unsigned> ARMElfOSABI[] = { + {"ARM", "ARM", ELF::ELFOSABI_ARM} +}; + +static const EnumEntry<unsigned> C6000ElfOSABI[] = { + {"C6000_ELFABI", "Bare-metal C6000", ELF::ELFOSABI_C6000_ELFABI}, + {"C6000_LINUX", "Linux C6000", ELF::ELFOSABI_C6000_LINUX} +}; + +static const EnumEntry<unsigned> ElfMachineType[] = { + ENUM_ENT(EM_NONE, "None"), + ENUM_ENT(EM_M32, "WE32100"), + ENUM_ENT(EM_SPARC, "Sparc"), + ENUM_ENT(EM_386, "Intel 80386"), + ENUM_ENT(EM_68K, "MC68000"), + ENUM_ENT(EM_88K, "MC88000"), + ENUM_ENT(EM_IAMCU, "EM_IAMCU"), + ENUM_ENT(EM_860, "Intel 80860"), + ENUM_ENT(EM_MIPS, "MIPS R3000"), + ENUM_ENT(EM_S370, "IBM System/370"), + ENUM_ENT(EM_MIPS_RS3_LE, "MIPS R3000 little-endian"), + ENUM_ENT(EM_PARISC, "HPPA"), + ENUM_ENT(EM_VPP500, "Fujitsu VPP500"), + ENUM_ENT(EM_SPARC32PLUS, "Sparc v8+"), + ENUM_ENT(EM_960, "Intel 80960"), + ENUM_ENT(EM_PPC, "PowerPC"), + ENUM_ENT(EM_PPC64, "PowerPC64"), + ENUM_ENT(EM_S390, "IBM S/390"), + ENUM_ENT(EM_SPU, "SPU"), + ENUM_ENT(EM_V800, "NEC V800 series"), + ENUM_ENT(EM_FR20, "Fujistsu FR20"), + ENUM_ENT(EM_RH32, "TRW RH-32"), + ENUM_ENT(EM_RCE, "Motorola RCE"), + ENUM_ENT(EM_ARM, "ARM"), + ENUM_ENT(EM_ALPHA, "EM_ALPHA"), + ENUM_ENT(EM_SH, "Hitachi SH"), + ENUM_ENT(EM_SPARCV9, "Sparc v9"), + ENUM_ENT(EM_TRICORE, "Siemens Tricore"), + ENUM_ENT(EM_ARC, "ARC"), + ENUM_ENT(EM_H8_300, "Hitachi H8/300"), + ENUM_ENT(EM_H8_300H, "Hitachi H8/300H"), + ENUM_ENT(EM_H8S, "Hitachi H8S"), + ENUM_ENT(EM_H8_500, "Hitachi H8/500"), + ENUM_ENT(EM_IA_64, "Intel IA-64"), + ENUM_ENT(EM_MIPS_X, "Stanford MIPS-X"), + ENUM_ENT(EM_COLDFIRE, "Motorola Coldfire"), + ENUM_ENT(EM_68HC12, "Motorola MC68HC12 Microcontroller"), + ENUM_ENT(EM_MMA, "Fujitsu Multimedia Accelerator"), + ENUM_ENT(EM_PCP, "Siemens PCP"), + ENUM_ENT(EM_NCPU, "Sony nCPU embedded RISC processor"), + ENUM_ENT(EM_NDR1, "Denso NDR1 microprocesspr"), + ENUM_ENT(EM_STARCORE, "Motorola Star*Core processor"), + ENUM_ENT(EM_ME16, "Toyota ME16 processor"), + ENUM_ENT(EM_ST100, "STMicroelectronics ST100 processor"), + ENUM_ENT(EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor"), + ENUM_ENT(EM_X86_64, "Advanced Micro Devices X86-64"), + ENUM_ENT(EM_PDSP, "Sony DSP processor"), + ENUM_ENT(EM_PDP10, "Digital Equipment Corp. PDP-10"), + ENUM_ENT(EM_PDP11, "Digital Equipment Corp. PDP-11"), + ENUM_ENT(EM_FX66, "Siemens FX66 microcontroller"), + ENUM_ENT(EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller"), + ENUM_ENT(EM_ST7, "STMicroelectronics ST7 8-bit microcontroller"), + ENUM_ENT(EM_68HC16, "Motorola MC68HC16 Microcontroller"), + ENUM_ENT(EM_68HC11, "Motorola MC68HC11 Microcontroller"), + ENUM_ENT(EM_68HC08, "Motorola MC68HC08 Microcontroller"), + ENUM_ENT(EM_68HC05, "Motorola MC68HC05 Microcontroller"), + ENUM_ENT(EM_SVX, "Silicon Graphics SVx"), + ENUM_ENT(EM_ST19, "STMicroelectronics ST19 8-bit microcontroller"), + ENUM_ENT(EM_VAX, "Digital VAX"), + ENUM_ENT(EM_CRIS, "Axis Communications 32-bit embedded processor"), + ENUM_ENT(EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu"), + ENUM_ENT(EM_FIREPATH, "Element 14 64-bit DSP processor"), + ENUM_ENT(EM_ZSP, "LSI Logic's 16-bit DSP processor"), + ENUM_ENT(EM_MMIX, "Donald Knuth's educational 64-bit processor"), + ENUM_ENT(EM_HUANY, "Harvard Universitys's machine-independent object format"), + ENUM_ENT(EM_PRISM, "Vitesse Prism"), + ENUM_ENT(EM_AVR, "Atmel AVR 8-bit microcontroller"), + ENUM_ENT(EM_FR30, "Fujitsu FR30"), + ENUM_ENT(EM_D10V, "Mitsubishi D10V"), + ENUM_ENT(EM_D30V, "Mitsubishi D30V"), + ENUM_ENT(EM_V850, "NEC v850"), + ENUM_ENT(EM_M32R, "Renesas M32R (formerly Mitsubishi M32r)"), + ENUM_ENT(EM_MN10300, "Matsushita MN10300"), + ENUM_ENT(EM_MN10200, "Matsushita MN10200"), + ENUM_ENT(EM_PJ, "picoJava"), + ENUM_ENT(EM_OPENRISC, "OpenRISC 32-bit embedded processor"), + ENUM_ENT(EM_ARC_COMPACT, "EM_ARC_COMPACT"), + ENUM_ENT(EM_XTENSA, "Tensilica Xtensa Processor"), + ENUM_ENT(EM_VIDEOCORE, "Alphamosaic VideoCore processor"), + ENUM_ENT(EM_TMM_GPP, "Thompson Multimedia General Purpose Processor"), + ENUM_ENT(EM_NS32K, "National Semiconductor 32000 series"), + ENUM_ENT(EM_TPC, "Tenor Network TPC processor"), + ENUM_ENT(EM_SNP1K, "EM_SNP1K"), + ENUM_ENT(EM_ST200, "STMicroelectronics ST200 microcontroller"), + ENUM_ENT(EM_IP2K, "Ubicom IP2xxx 8-bit microcontrollers"), + ENUM_ENT(EM_MAX, "MAX Processor"), + ENUM_ENT(EM_CR, "National Semiconductor CompactRISC"), + ENUM_ENT(EM_F2MC16, "Fujitsu F2MC16"), + ENUM_ENT(EM_MSP430, "Texas Instruments msp430 microcontroller"), + ENUM_ENT(EM_BLACKFIN, "Analog Devices Blackfin"), + ENUM_ENT(EM_SE_C33, "S1C33 Family of Seiko Epson processors"), + ENUM_ENT(EM_SEP, "Sharp embedded microprocessor"), + ENUM_ENT(EM_ARCA, "Arca RISC microprocessor"), + ENUM_ENT(EM_UNICORE, "Unicore"), + ENUM_ENT(EM_EXCESS, "eXcess 16/32/64-bit configurable embedded CPU"), + ENUM_ENT(EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor"), + ENUM_ENT(EM_ALTERA_NIOS2, "Altera Nios"), + ENUM_ENT(EM_CRX, "National Semiconductor CRX microprocessor"), + ENUM_ENT(EM_XGATE, "Motorola XGATE embedded processor"), + ENUM_ENT(EM_C166, "Infineon Technologies xc16x"), + ENUM_ENT(EM_M16C, "Renesas M16C"), + ENUM_ENT(EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller"), + ENUM_ENT(EM_CE, "Freescale Communication Engine RISC core"), + ENUM_ENT(EM_M32C, "Renesas M32C"), + ENUM_ENT(EM_TSK3000, "Altium TSK3000 core"), + ENUM_ENT(EM_RS08, "Freescale RS08 embedded processor"), + ENUM_ENT(EM_SHARC, "EM_SHARC"), + ENUM_ENT(EM_ECOG2, "Cyan Technology eCOG2 microprocessor"), + ENUM_ENT(EM_SCORE7, "SUNPLUS S+Core"), + ENUM_ENT(EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor"), + ENUM_ENT(EM_VIDEOCORE3, "Broadcom VideoCore III processor"), + ENUM_ENT(EM_LATTICEMICO32, "Lattice Mico32"), + ENUM_ENT(EM_SE_C17, "Seiko Epson C17 family"), + ENUM_ENT(EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family"), + ENUM_ENT(EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family"), + ENUM_ENT(EM_TI_C5500, "Texas Instruments TMS320C55x DSP family"), + ENUM_ENT(EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor"), + ENUM_ENT(EM_CYPRESS_M8C, "Cypress M8C microprocessor"), + ENUM_ENT(EM_R32C, "Renesas R32C series microprocessors"), + ENUM_ENT(EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family"), + ENUM_ENT(EM_HEXAGON, "Qualcomm Hexagon"), + ENUM_ENT(EM_8051, "Intel 8051 and variants"), + ENUM_ENT(EM_STXP7X, "STMicroelectronics STxP7x family"), + ENUM_ENT(EM_NDS32, "Andes Technology compact code size embedded RISC processor family"), + ENUM_ENT(EM_ECOG1, "Cyan Technology eCOG1 microprocessor"), + ENUM_ENT(EM_ECOG1X, "Cyan Technology eCOG1X family"), + ENUM_ENT(EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core microcontrollers"), + ENUM_ENT(EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor"), + ENUM_ENT(EM_MANIK, "M2000 Reconfigurable RISC Microprocessor"), + ENUM_ENT(EM_CRAYNV2, "Cray Inc. NV2 vector architecture"), + ENUM_ENT(EM_RX, "Renesas RX"), + ENUM_ENT(EM_METAG, "Imagination Technologies Meta processor architecture"), + ENUM_ENT(EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture"), + ENUM_ENT(EM_ECOG16, "Cyan Technology eCOG16 family"), + ENUM_ENT(EM_CR16, "Xilinx MicroBlaze"), + ENUM_ENT(EM_ETPU, "Freescale Extended Time Processing Unit"), + ENUM_ENT(EM_SLE9X, "Infineon Technologies SLE9X core"), + ENUM_ENT(EM_L10M, "EM_L10M"), + ENUM_ENT(EM_K10M, "EM_K10M"), + ENUM_ENT(EM_AARCH64, "AArch64"), + ENUM_ENT(EM_AVR32, "Atmel Corporation 32-bit microprocessor family"), + ENUM_ENT(EM_STM8, "STMicroeletronics STM8 8-bit microcontroller"), + ENUM_ENT(EM_TILE64, "Tilera TILE64 multicore architecture family"), + ENUM_ENT(EM_TILEPRO, "Tilera TILEPro multicore architecture family"), + ENUM_ENT(EM_CUDA, "NVIDIA CUDA architecture"), + ENUM_ENT(EM_TILEGX, "Tilera TILE-Gx multicore architecture family"), + ENUM_ENT(EM_CLOUDSHIELD, "EM_CLOUDSHIELD"), + ENUM_ENT(EM_COREA_1ST, "EM_COREA_1ST"), + ENUM_ENT(EM_COREA_2ND, "EM_COREA_2ND"), + ENUM_ENT(EM_ARC_COMPACT2, "EM_ARC_COMPACT2"), + ENUM_ENT(EM_OPEN8, "EM_OPEN8"), + ENUM_ENT(EM_RL78, "Renesas RL78"), + ENUM_ENT(EM_VIDEOCORE5, "Broadcom VideoCore V processor"), + ENUM_ENT(EM_78KOR, "EM_78KOR"), + ENUM_ENT(EM_56800EX, "EM_56800EX"), + ENUM_ENT(EM_AMDGPU, "EM_AMDGPU"), + ENUM_ENT(EM_RISCV, "RISC-V"), + ENUM_ENT(EM_LANAI, "EM_LANAI"), + ENUM_ENT(EM_BPF, "EM_BPF"), +}; + +static const EnumEntry<unsigned> ElfSymbolBindings[] = { + {"Local", "LOCAL", ELF::STB_LOCAL}, + {"Global", "GLOBAL", ELF::STB_GLOBAL}, + {"Weak", "WEAK", ELF::STB_WEAK}, + {"Unique", "UNIQUE", ELF::STB_GNU_UNIQUE}}; + +static const EnumEntry<unsigned> ElfSymbolVisibilities[] = { + {"DEFAULT", "DEFAULT", ELF::STV_DEFAULT}, + {"INTERNAL", "INTERNAL", ELF::STV_INTERNAL}, + {"HIDDEN", "HIDDEN", ELF::STV_HIDDEN}, + {"PROTECTED", "PROTECTED", ELF::STV_PROTECTED}}; + +static const EnumEntry<unsigned> AMDGPUSymbolTypes[] = { + { "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL } +}; + +static const char *getGroupType(uint32_t Flag) { + if (Flag & ELF::GRP_COMDAT) + return "COMDAT"; + else + return "(unknown)"; +} + +static const EnumEntry<unsigned> ElfSectionFlags[] = { + ENUM_ENT(SHF_WRITE, "W"), + ENUM_ENT(SHF_ALLOC, "A"), + ENUM_ENT(SHF_EXCLUDE, "E"), + ENUM_ENT(SHF_EXECINSTR, "X"), + ENUM_ENT(SHF_MERGE, "M"), + ENUM_ENT(SHF_STRINGS, "S"), + ENUM_ENT(SHF_INFO_LINK, "I"), + ENUM_ENT(SHF_LINK_ORDER, "L"), + ENUM_ENT(SHF_OS_NONCONFORMING, "o"), + ENUM_ENT(SHF_GROUP, "G"), + ENUM_ENT(SHF_TLS, "T"), + ENUM_ENT(SHF_MASKOS, "o"), + ENUM_ENT(SHF_MASKPROC, "p"), + ENUM_ENT_1(SHF_COMPRESSED), +}; + +static const EnumEntry<unsigned> ElfXCoreSectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, XCORE_SHF_CP_SECTION), + LLVM_READOBJ_ENUM_ENT(ELF, XCORE_SHF_DP_SECTION) +}; + +static const EnumEntry<unsigned> ElfARMSectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, SHF_ARM_PURECODE) +}; + +static const EnumEntry<unsigned> ElfHexagonSectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, SHF_HEX_GPREL) +}; + +static const EnumEntry<unsigned> ElfMipsSectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NODUPES), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NAMES ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_LOCAL ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NOSTRIP), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_GPREL ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_MERGE ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_ADDR ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_STRING ) +}; + +static const EnumEntry<unsigned> ElfX86_64SectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, SHF_X86_64_LARGE) +}; + +static std::string getGNUFlags(uint64_t Flags) { + std::string Str; + for (auto Entry : ElfSectionFlags) { + uint64_t Flag = Entry.Value & Flags; + Flags &= ~Entry.Value; + switch (Flag) { + case ELF::SHF_WRITE: + case ELF::SHF_ALLOC: + case ELF::SHF_EXECINSTR: + case ELF::SHF_MERGE: + case ELF::SHF_STRINGS: + case ELF::SHF_INFO_LINK: + case ELF::SHF_LINK_ORDER: + case ELF::SHF_OS_NONCONFORMING: + case ELF::SHF_GROUP: + case ELF::SHF_TLS: + case ELF::SHF_EXCLUDE: + Str += Entry.AltName; + break; + default: + if (Flag & ELF::SHF_MASKOS) + Str += "o"; + else if (Flag & ELF::SHF_MASKPROC) + Str += "p"; + else if (Flag) + Str += "x"; + } + } + return Str; +} + +static const char *getElfSegmentType(unsigned Arch, unsigned Type) { + // Check potentially overlapped processor-specific + // program header type. + switch (Arch) { + case ELF::EM_ARM: + switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); } + break; + case ELF::EM_MIPS: + case ELF::EM_MIPS_RS3_LE: + switch (Type) { + LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_REGINFO); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_RTPROC); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_OPTIONS); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_ABIFLAGS); + } + break; + } + + switch (Type) { + LLVM_READOBJ_ENUM_CASE(ELF, PT_NULL ); + LLVM_READOBJ_ENUM_CASE(ELF, PT_LOAD ); + LLVM_READOBJ_ENUM_CASE(ELF, PT_DYNAMIC); + LLVM_READOBJ_ENUM_CASE(ELF, PT_INTERP ); + LLVM_READOBJ_ENUM_CASE(ELF, PT_NOTE ); + LLVM_READOBJ_ENUM_CASE(ELF, PT_SHLIB ); + LLVM_READOBJ_ENUM_CASE(ELF, PT_PHDR ); + LLVM_READOBJ_ENUM_CASE(ELF, PT_TLS ); + + LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_EH_FRAME); + LLVM_READOBJ_ENUM_CASE(ELF, PT_SUNW_UNWIND); + + LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_STACK); + LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_RELRO); + + LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_RANDOMIZE); + LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_WXNEEDED); + LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_BOOTDATA); + + default: + return ""; + } +} + +static std::string getElfPtType(unsigned Arch, unsigned Type) { + switch (Type) { + LLVM_READOBJ_PHDR_ENUM(ELF, PT_NULL) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_LOAD) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_DYNAMIC) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_INTERP) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_NOTE) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_SHLIB) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_PHDR) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_TLS) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_GNU_EH_FRAME) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_SUNW_UNWIND) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_GNU_STACK) + LLVM_READOBJ_PHDR_ENUM(ELF, PT_GNU_RELRO) + default: + // All machine specific PT_* types + switch (Arch) { + case ELF::EM_ARM: + if (Type == ELF::PT_ARM_EXIDX) + return "EXIDX"; + break; + case ELF::EM_MIPS: + case ELF::EM_MIPS_RS3_LE: + switch (Type) { + case PT_MIPS_REGINFO: + return "REGINFO"; + case PT_MIPS_RTPROC: + return "RTPROC"; + case PT_MIPS_OPTIONS: + return "OPTIONS"; + case PT_MIPS_ABIFLAGS: + return "ABIFLAGS"; + } + break; + } + } + return std::string("<unknown>: ") + to_string(format_hex(Type, 1)); +} + +static const EnumEntry<unsigned> ElfSegmentFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, PF_X), + LLVM_READOBJ_ENUM_ENT(ELF, PF_W), + LLVM_READOBJ_ENUM_ENT(ELF, PF_R) +}; + +static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = { + ENUM_ENT(EF_MIPS_NOREORDER, "noreorder"), + ENUM_ENT(EF_MIPS_PIC, "pic"), + ENUM_ENT(EF_MIPS_CPIC, "cpic"), + ENUM_ENT(EF_MIPS_ABI2, "abi2"), + ENUM_ENT(EF_MIPS_32BITMODE, "32bitmode"), + ENUM_ENT(EF_MIPS_FP64, "fp64"), + ENUM_ENT(EF_MIPS_NAN2008, "nan2008"), + ENUM_ENT(EF_MIPS_ABI_O32, "o32"), + ENUM_ENT(EF_MIPS_ABI_O64, "o64"), + ENUM_ENT(EF_MIPS_ABI_EABI32, "eabi32"), + ENUM_ENT(EF_MIPS_ABI_EABI64, "eabi64"), + ENUM_ENT(EF_MIPS_MACH_3900, "3900"), + ENUM_ENT(EF_MIPS_MACH_4010, "4010"), + ENUM_ENT(EF_MIPS_MACH_4100, "4100"), + ENUM_ENT(EF_MIPS_MACH_4650, "4650"), + ENUM_ENT(EF_MIPS_MACH_4120, "4120"), + ENUM_ENT(EF_MIPS_MACH_4111, "4111"), + ENUM_ENT(EF_MIPS_MACH_SB1, "sb1"), + ENUM_ENT(EF_MIPS_MACH_OCTEON, "octeon"), + ENUM_ENT(EF_MIPS_MACH_XLR, "xlr"), + ENUM_ENT(EF_MIPS_MACH_OCTEON2, "octeon2"), + ENUM_ENT(EF_MIPS_MACH_OCTEON3, "octeon3"), + ENUM_ENT(EF_MIPS_MACH_5400, "5400"), + ENUM_ENT(EF_MIPS_MACH_5900, "5900"), + ENUM_ENT(EF_MIPS_MACH_5500, "5500"), + ENUM_ENT(EF_MIPS_MACH_9000, "9000"), + ENUM_ENT(EF_MIPS_MACH_LS2E, "loongson-2e"), + ENUM_ENT(EF_MIPS_MACH_LS2F, "loongson-2f"), + ENUM_ENT(EF_MIPS_MACH_LS3A, "loongson-3a"), + ENUM_ENT(EF_MIPS_MICROMIPS, "micromips"), + ENUM_ENT(EF_MIPS_ARCH_ASE_M16, "mips16"), + ENUM_ENT(EF_MIPS_ARCH_ASE_MDMX, "mdmx"), + ENUM_ENT(EF_MIPS_ARCH_1, "mips1"), + ENUM_ENT(EF_MIPS_ARCH_2, "mips2"), + ENUM_ENT(EF_MIPS_ARCH_3, "mips3"), + ENUM_ENT(EF_MIPS_ARCH_4, "mips4"), + ENUM_ENT(EF_MIPS_ARCH_5, "mips5"), + ENUM_ENT(EF_MIPS_ARCH_32, "mips32"), + ENUM_ENT(EF_MIPS_ARCH_64, "mips64"), + ENUM_ENT(EF_MIPS_ARCH_32R2, "mips32r2"), + ENUM_ENT(EF_MIPS_ARCH_64R2, "mips64r2"), + ENUM_ENT(EF_MIPS_ARCH_32R6, "mips32r6"), + ENUM_ENT(EF_MIPS_ARCH_64R6, "mips64r6") +}; + +static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX902), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX904), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC) +}; + +static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = { + ENUM_ENT(EF_RISCV_RVC, "RVC"), + ENUM_ENT(EF_RISCV_FLOAT_ABI_SINGLE, "single-float ABI"), + ENUM_ENT(EF_RISCV_FLOAT_ABI_DOUBLE, "double-float ABI"), + ENUM_ENT(EF_RISCV_FLOAT_ABI_QUAD, "quad-float ABI"), + ENUM_ENT(EF_RISCV_RVE, "RVE") +}; + +static const EnumEntry<unsigned> ElfSymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL), + LLVM_READOBJ_ENUM_ENT(ELF, STV_HIDDEN), + LLVM_READOBJ_ENUM_ENT(ELF, STV_PROTECTED) +}; + +static const EnumEntry<unsigned> ElfMipsSymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PIC), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_MICROMIPS) +}; + +static const EnumEntry<unsigned> ElfMips16SymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_MIPS16) +}; + +static const char *getElfMipsOptionsOdkType(unsigned Odk) { + switch (Odk) { + LLVM_READOBJ_ENUM_CASE(ELF, ODK_NULL); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_REGINFO); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_EXCEPTIONS); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAD); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWPATCH); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_FILL); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_TAGS); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWAND); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWOR); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_GP_GROUP); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_IDENT); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAGESIZE); + default: + return "Unknown"; + } +} + +template <typename ELFT> +std::pair<const typename ELFT::Phdr *, const typename ELFT::Shdr *> +ELFDumper<ELFT>::findDynamic(const ELFFile<ELFT> *Obj) { + // Try to locate the PT_DYNAMIC header. + const Elf_Phdr *DynamicPhdr = nullptr; + for (const Elf_Phdr &Phdr : + unwrapOrError(ObjF->getFileName(), Obj->program_headers())) { + if (Phdr.p_type != ELF::PT_DYNAMIC) + continue; + DynamicPhdr = &Phdr; + break; + } + + // Try to locate the .dynamic section in the sections header table. + const Elf_Shdr *DynamicSec = nullptr; + for (const Elf_Shdr &Sec : + unwrapOrError(ObjF->getFileName(), Obj->sections())) { + if (Sec.sh_type != ELF::SHT_DYNAMIC) + continue; + DynamicSec = &Sec; + break; + } + + if (DynamicPhdr && DynamicPhdr->p_offset + DynamicPhdr->p_filesz > + ObjF->getMemoryBufferRef().getBufferSize()) { + reportWarning( + createError( + "PT_DYNAMIC segment offset + size exceeds the size of the file"), + ObjF->getFileName()); + // Don't use the broken dynamic header. + DynamicPhdr = nullptr; + } + + if (DynamicPhdr && DynamicSec) { + StringRef Name = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DynamicSec)); + if (DynamicSec->sh_addr + DynamicSec->sh_size > + DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz || + DynamicSec->sh_addr < DynamicPhdr->p_vaddr) + reportWarning(createError("The SHT_DYNAMIC section '" + Name + + "' is not contained within the " + "PT_DYNAMIC segment"), + ObjF->getFileName()); + + if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr) + reportWarning(createError("The SHT_DYNAMIC section '" + Name + + "' is not at the start of " + "PT_DYNAMIC segment"), + ObjF->getFileName()); + } + + return std::make_pair(DynamicPhdr, DynamicSec); +} + +template <typename ELFT> +void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { + const Elf_Phdr *DynamicPhdr; + const Elf_Shdr *DynamicSec; + std::tie(DynamicPhdr, DynamicSec) = findDynamic(Obj); + if (!DynamicPhdr && !DynamicSec) + return; + + DynRegionInfo FromPhdr(ObjF->getFileName()); + bool IsPhdrTableValid = false; + if (DynamicPhdr) { + FromPhdr = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn)); + IsPhdrTableValid = !FromPhdr.getAsArrayRef<Elf_Dyn>().empty(); + } + + // Locate the dynamic table described in a section header. + // Ignore sh_entsize and use the expected value for entry size explicitly. + // This allows us to dump dynamic sections with a broken sh_entsize + // field. + DynRegionInfo FromSec(ObjF->getFileName()); + bool IsSecTableValid = false; + if (DynamicSec) { + FromSec = + checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset, + DynamicSec->sh_size, sizeof(Elf_Dyn), ObjF->getFileName()}); + IsSecTableValid = !FromSec.getAsArrayRef<Elf_Dyn>().empty(); + } + + // When we only have information from one of the SHT_DYNAMIC section header or + // PT_DYNAMIC program header, just use that. + if (!DynamicPhdr || !DynamicSec) { + if ((DynamicPhdr && IsPhdrTableValid) || (DynamicSec && IsSecTableValid)) { + DynamicTable = DynamicPhdr ? FromPhdr : FromSec; + parseDynamicTable(); + } else { + reportWarning(createError("no valid dynamic table was found"), + ObjF->getFileName()); + } + return; + } + + // At this point we have tables found from the section header and from the + // dynamic segment. Usually they match, but we have to do sanity checks to + // verify that. + + if (FromPhdr.Addr != FromSec.Addr) + reportWarning(createError("SHT_DYNAMIC section header and PT_DYNAMIC " + "program header disagree about " + "the location of the dynamic table"), + ObjF->getFileName()); + + if (!IsPhdrTableValid && !IsSecTableValid) { + reportWarning(createError("no valid dynamic table was found"), + ObjF->getFileName()); + return; + } + + // Information in the PT_DYNAMIC program header has priority over the information + // in a section header. + if (IsPhdrTableValid) { + if (!IsSecTableValid) + reportWarning( + createError( + "SHT_DYNAMIC dynamic table is invalid: PT_DYNAMIC will be used"), + ObjF->getFileName()); + DynamicTable = FromPhdr; + } else { + reportWarning( + createError( + "PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used"), + ObjF->getFileName()); + DynamicTable = FromSec; + } + + parseDynamicTable(); +} + +template <typename ELFT> +ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, + ScopedPrinter &Writer) + : ObjDumper(Writer), ObjF(ObjF), DynRelRegion(ObjF->getFileName()), + DynRelaRegion(ObjF->getFileName()), DynRelrRegion(ObjF->getFileName()), + DynPLTRelRegion(ObjF->getFileName()), DynSymRegion(ObjF->getFileName()), + DynamicTable(ObjF->getFileName()) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + for (const Elf_Shdr &Sec : + unwrapOrError(ObjF->getFileName(), Obj->sections())) { + switch (Sec.sh_type) { + case ELF::SHT_SYMTAB: + if (!DotSymtabSec) + DotSymtabSec = &Sec; + break; + case ELF::SHT_DYNSYM: + if (!DynSymRegion.Size) { + DynSymRegion = createDRIFrom(&Sec); + // This is only used (if Elf_Shdr present)for naming section in GNU + // style + DynSymtabName = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec)); + + if (Expected<StringRef> E = Obj->getStringTableForSymtab(Sec)) + DynamicStringTable = *E; + else + reportWarning(E.takeError(), ObjF->getFileName()); + } + break; + case ELF::SHT_SYMTAB_SHNDX: + ShndxTable = unwrapOrError(ObjF->getFileName(), Obj->getSHNDXTable(Sec)); + break; + case ELF::SHT_GNU_versym: + if (!SymbolVersionSection) + SymbolVersionSection = &Sec; + break; + case ELF::SHT_GNU_verdef: + if (!SymbolVersionDefSection) + SymbolVersionDefSection = &Sec; + break; + case ELF::SHT_GNU_verneed: + if (!SymbolVersionNeedSection) + SymbolVersionNeedSection = &Sec; + break; + case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: + if (!DotCGProfileSec) + DotCGProfileSec = &Sec; + break; + case ELF::SHT_LLVM_ADDRSIG: + if (!DotAddrsigSec) + DotAddrsigSec = &Sec; + break; + } + } + + loadDynamicTable(Obj); + + if (opts::Output == opts::GNU) + ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this)); + else + ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this)); +} + +static const char *getTypeString(unsigned Arch, uint64_t Type) { +#define DYNAMIC_TAG(n, v) + switch (Arch) { + + case EM_AARCH64: + switch (Type) { +#define AARCH64_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef AARCH64_DYNAMIC_TAG + } + break; + + case EM_HEXAGON: + switch (Type) { +#define HEXAGON_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef HEXAGON_DYNAMIC_TAG + } + break; + + case EM_MIPS: + switch (Type) { +#define MIPS_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef MIPS_DYNAMIC_TAG + } + break; + + case EM_PPC64: + switch (Type) { +#define PPC64_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef PPC64_DYNAMIC_TAG + } + break; + } +#undef DYNAMIC_TAG + switch (Type) { +// Now handle all dynamic tags except the architecture specific ones +#define AARCH64_DYNAMIC_TAG(name, value) +#define MIPS_DYNAMIC_TAG(name, value) +#define HEXAGON_DYNAMIC_TAG(name, value) +#define PPC64_DYNAMIC_TAG(name, value) +// Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc. +#define DYNAMIC_TAG_MARKER(name, value) +#define DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef DYNAMIC_TAG +#undef AARCH64_DYNAMIC_TAG +#undef MIPS_DYNAMIC_TAG +#undef HEXAGON_DYNAMIC_TAG +#undef PPC64_DYNAMIC_TAG +#undef DYNAMIC_TAG_MARKER + default: + return "unknown"; + } +} + +template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { + auto toMappedAddr = [&](uint64_t Tag, uint64_t VAddr) -> const uint8_t * { + auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr); + if (!MappedAddrOrError) { + Error Err = + createError("Unable to parse DT_" + + Twine(getTypeString( + ObjF->getELFFile()->getHeader()->e_machine, Tag)) + + ": " + llvm::toString(MappedAddrOrError.takeError())); + + reportWarning(std::move(Err), ObjF->getFileName()); + return nullptr; + } + return MappedAddrOrError.get(); + }; + + uint64_t SONameOffset = 0; + const char *StringTableBegin = nullptr; + uint64_t StringTableSize = 0; + for (const Elf_Dyn &Dyn : dynamic_table()) { + switch (Dyn.d_tag) { + case ELF::DT_HASH: + HashTable = reinterpret_cast<const Elf_Hash *>( + toMappedAddr(Dyn.getTag(), Dyn.getPtr())); + break; + case ELF::DT_GNU_HASH: + GnuHashTable = reinterpret_cast<const Elf_GnuHash *>( + toMappedAddr(Dyn.getTag(), Dyn.getPtr())); + break; + case ELF::DT_STRTAB: + StringTableBegin = reinterpret_cast<const char *>( + toMappedAddr(Dyn.getTag(), Dyn.getPtr())); + break; + case ELF::DT_STRSZ: + StringTableSize = Dyn.getVal(); + break; + case ELF::DT_SYMTAB: { + // Often we find the information about the dynamic symbol table + // location in the SHT_DYNSYM section header. However, the value in + // DT_SYMTAB has priority, because it is used by dynamic loaders to + // locate .dynsym at runtime. The location we find in the section header + // and the location we find here should match. If we can't map the + // DT_SYMTAB value to an address (e.g. when there are no program headers), we + // ignore its value. + if (const uint8_t *VA = toMappedAddr(Dyn.getTag(), Dyn.getPtr())) { + // EntSize is non-zero if the dynamic symbol table has been found via a + // section header. + if (DynSymRegion.EntSize && VA != DynSymRegion.Addr) + reportWarning( + createError( + "SHT_DYNSYM section header and DT_SYMTAB disagree about " + "the location of the dynamic symbol table"), + ObjF->getFileName()); + + DynSymRegion.Addr = VA; + DynSymRegion.EntSize = sizeof(Elf_Sym); + } + break; + } + case ELF::DT_RELA: + DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + break; + case ELF::DT_RELASZ: + DynRelaRegion.Size = Dyn.getVal(); + break; + case ELF::DT_RELAENT: + DynRelaRegion.EntSize = Dyn.getVal(); + break; + case ELF::DT_SONAME: + SONameOffset = Dyn.getVal(); + break; + case ELF::DT_REL: + DynRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + break; + case ELF::DT_RELSZ: + DynRelRegion.Size = Dyn.getVal(); + break; + case ELF::DT_RELENT: + DynRelRegion.EntSize = Dyn.getVal(); + break; + case ELF::DT_RELR: + case ELF::DT_ANDROID_RELR: + DynRelrRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + break; + case ELF::DT_RELRSZ: + case ELF::DT_ANDROID_RELRSZ: + DynRelrRegion.Size = Dyn.getVal(); + break; + case ELF::DT_RELRENT: + case ELF::DT_ANDROID_RELRENT: + DynRelrRegion.EntSize = Dyn.getVal(); + break; + case ELF::DT_PLTREL: + if (Dyn.getVal() == DT_REL) + DynPLTRelRegion.EntSize = sizeof(Elf_Rel); + else if (Dyn.getVal() == DT_RELA) + DynPLTRelRegion.EntSize = sizeof(Elf_Rela); + else + reportError(createError(Twine("unknown DT_PLTREL value of ") + + Twine((uint64_t)Dyn.getVal())), + ObjF->getFileName()); + break; + case ELF::DT_JMPREL: + DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + break; + case ELF::DT_PLTRELSZ: + DynPLTRelRegion.Size = Dyn.getVal(); + break; + } + } + if (StringTableBegin) + DynamicStringTable = StringRef(StringTableBegin, StringTableSize); + SOName = getDynamicString(SONameOffset); +} + +template <typename ELFT> +typename ELFDumper<ELFT>::Elf_Rel_Range ELFDumper<ELFT>::dyn_rels() const { + return DynRelRegion.getAsArrayRef<Elf_Rel>(); +} + +template <typename ELFT> +typename ELFDumper<ELFT>::Elf_Rela_Range ELFDumper<ELFT>::dyn_relas() const { + return DynRelaRegion.getAsArrayRef<Elf_Rela>(); +} + +template <typename ELFT> +typename ELFDumper<ELFT>::Elf_Relr_Range ELFDumper<ELFT>::dyn_relrs() const { + return DynRelrRegion.getAsArrayRef<Elf_Relr>(); +} + +template <class ELFT> void ELFDumper<ELFT>::printFileHeaders() { + ELFDumperStyle->printFileHeaders(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printSectionHeaders() { + ELFDumperStyle->printSectionHeaders(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printRelocations() { + ELFDumperStyle->printRelocations(ObjF->getELFFile()); +} + +template <class ELFT> +void ELFDumper<ELFT>::printProgramHeaders( + bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { + ELFDumperStyle->printProgramHeaders(ObjF->getELFFile(), PrintProgramHeaders, + PrintSectionMapping); +} + +template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() { + // Dump version symbol section. + ELFDumperStyle->printVersionSymbolSection(ObjF->getELFFile(), + SymbolVersionSection); + + // Dump version definition section. + ELFDumperStyle->printVersionDefinitionSection(ObjF->getELFFile(), + SymbolVersionDefSection); + + // Dump version dependency section. + ELFDumperStyle->printVersionDependencySection(ObjF->getELFFile(), + SymbolVersionNeedSection); +} + +template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocations() { + ELFDumperStyle->printDynamicRelocations(ObjF->getELFFile()); +} + +template <class ELFT> +void ELFDumper<ELFT>::printSymbols(bool PrintSymbols, + bool PrintDynamicSymbols) { + ELFDumperStyle->printSymbols(ObjF->getELFFile(), PrintSymbols, + PrintDynamicSymbols); +} + +template <class ELFT> void ELFDumper<ELFT>::printHashSymbols() { + ELFDumperStyle->printHashSymbols(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() { + ELFDumperStyle->printHashHistogram(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printCGProfile() { + ELFDumperStyle->printCGProfile(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printNotes() { + ELFDumperStyle->printNotes(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printELFLinkerOptions() { + ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printStackSizes() { + ELFDumperStyle->printStackSizes(ObjF); +} + +#define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ + { #enum, prefix##_##enum } + +static const EnumEntry<unsigned> ElfDynamicDTFlags[] = { + LLVM_READOBJ_DT_FLAG_ENT(DF, ORIGIN), + LLVM_READOBJ_DT_FLAG_ENT(DF, SYMBOLIC), + LLVM_READOBJ_DT_FLAG_ENT(DF, TEXTREL), + LLVM_READOBJ_DT_FLAG_ENT(DF, BIND_NOW), + LLVM_READOBJ_DT_FLAG_ENT(DF, STATIC_TLS) +}; + +static const EnumEntry<unsigned> ElfDynamicDTFlags1[] = { + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOW), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAL), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, GROUP), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODELETE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, LOADFLTR), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, INITFIRST), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOOPEN), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, ORIGIN), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, DIRECT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, TRANS), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, INTERPOSE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODEFLIB), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODUMP), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, CONFALT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, ENDFILTEE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELDNE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELPND), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODIRECT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, IGNMULDEF), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOKSYMS), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOHDR), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, EDITED), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NORELOC), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, SYMINTPOSE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAUDIT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON) +}; + +static const EnumEntry<unsigned> ElfDynamicDTMipsFlags[] = { + LLVM_READOBJ_DT_FLAG_ENT(RHF, NONE), + LLVM_READOBJ_DT_FLAG_ENT(RHF, QUICKSTART), + LLVM_READOBJ_DT_FLAG_ENT(RHF, NOTPOT), + LLVM_READOBJ_DT_FLAG_ENT(RHS, NO_LIBRARY_REPLACEMENT), + LLVM_READOBJ_DT_FLAG_ENT(RHF, NO_MOVE), + LLVM_READOBJ_DT_FLAG_ENT(RHF, SGI_ONLY), + LLVM_READOBJ_DT_FLAG_ENT(RHF, GUARANTEE_INIT), + LLVM_READOBJ_DT_FLAG_ENT(RHF, DELTA_C_PLUS_PLUS), + LLVM_READOBJ_DT_FLAG_ENT(RHF, GUARANTEE_START_INIT), + LLVM_READOBJ_DT_FLAG_ENT(RHF, PIXIE), + LLVM_READOBJ_DT_FLAG_ENT(RHF, DEFAULT_DELAY_LOAD), + LLVM_READOBJ_DT_FLAG_ENT(RHF, REQUICKSTART), + LLVM_READOBJ_DT_FLAG_ENT(RHF, REQUICKSTARTED), + LLVM_READOBJ_DT_FLAG_ENT(RHF, CORD), + LLVM_READOBJ_DT_FLAG_ENT(RHF, NO_UNRES_UNDEF), + LLVM_READOBJ_DT_FLAG_ENT(RHF, RLD_ORDER_SAFE) +}; + +#undef LLVM_READOBJ_DT_FLAG_ENT + +template <typename T, typename TFlag> +void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) { + using FlagEntry = EnumEntry<TFlag>; + using FlagVector = SmallVector<FlagEntry, 10>; + FlagVector SetFlags; + + for (const auto &Flag : Flags) { + if (Flag.Value == 0) + continue; + + if ((Value & Flag.Value) == Flag.Value) + SetFlags.push_back(Flag); + } + + for (const auto &Flag : SetFlags) { + OS << Flag.Name << " "; + } +} + +template <class ELFT> +void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, + uint64_t Value) const { + const char *ConvChar = + (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64; + + // Handle custom printing of architecture specific tags + switch (ObjF->getELFFile()->getHeader()->e_machine) { + case EM_AARCH64: + switch (Type) { + case DT_AARCH64_BTI_PLT: + case DT_AARCH64_PAC_PLT: + OS << Value; + return; + default: + break; + } + break; + case EM_HEXAGON: + switch (Type) { + case DT_HEXAGON_VER: + OS << Value; + return; + case DT_HEXAGON_SYMSZ: + case DT_HEXAGON_PLT: + OS << format(ConvChar, Value); + return; + default: + break; + } + break; + case EM_MIPS: + switch (Type) { + case DT_MIPS_RLD_VERSION: + case DT_MIPS_LOCAL_GOTNO: + case DT_MIPS_SYMTABNO: + case DT_MIPS_UNREFEXTNO: + OS << Value; + return; + case DT_MIPS_TIME_STAMP: + case DT_MIPS_ICHECKSUM: + case DT_MIPS_IVERSION: + case DT_MIPS_BASE_ADDRESS: + case DT_MIPS_MSYM: + case DT_MIPS_CONFLICT: + case DT_MIPS_LIBLIST: + case DT_MIPS_CONFLICTNO: + case DT_MIPS_LIBLISTNO: + case DT_MIPS_GOTSYM: + case DT_MIPS_HIPAGENO: + case DT_MIPS_RLD_MAP: + case DT_MIPS_DELTA_CLASS: + case DT_MIPS_DELTA_CLASS_NO: + case DT_MIPS_DELTA_INSTANCE: + case DT_MIPS_DELTA_RELOC: + case DT_MIPS_DELTA_RELOC_NO: + case DT_MIPS_DELTA_SYM: + case DT_MIPS_DELTA_SYM_NO: + case DT_MIPS_DELTA_CLASSSYM: + case DT_MIPS_DELTA_CLASSSYM_NO: + case DT_MIPS_CXX_FLAGS: + case DT_MIPS_PIXIE_INIT: + case DT_MIPS_SYMBOL_LIB: + case DT_MIPS_LOCALPAGE_GOTIDX: + case DT_MIPS_LOCAL_GOTIDX: + case DT_MIPS_HIDDEN_GOTIDX: + case DT_MIPS_PROTECTED_GOTIDX: + case DT_MIPS_OPTIONS: + case DT_MIPS_INTERFACE: + case DT_MIPS_DYNSTR_ALIGN: + case DT_MIPS_INTERFACE_SIZE: + case DT_MIPS_RLD_TEXT_RESOLVE_ADDR: + case DT_MIPS_PERF_SUFFIX: + case DT_MIPS_COMPACT_SIZE: + case DT_MIPS_GP_VALUE: + case DT_MIPS_AUX_DYNAMIC: + case DT_MIPS_PLTGOT: + case DT_MIPS_RWPLT: + case DT_MIPS_RLD_MAP_REL: + OS << format(ConvChar, Value); + return; + case DT_MIPS_FLAGS: + printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS); + return; + default: + break; + } + break; + default: + break; + } + + switch (Type) { + case DT_PLTREL: + if (Value == DT_REL) { + OS << "REL"; + break; + } else if (Value == DT_RELA) { + OS << "RELA"; + break; + } + LLVM_FALLTHROUGH; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_PREINIT_ARRAY: + case DT_DEBUG: + case DT_VERDEF: + case DT_VERNEED: + case DT_VERSYM: + case DT_GNU_HASH: + case DT_NULL: + OS << format(ConvChar, Value); + break; + case DT_RELACOUNT: + case DT_RELCOUNT: + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + OS << Value; + break; + case DT_PLTRELSZ: + case DT_RELASZ: + case DT_RELAENT: + case DT_STRSZ: + case DT_SYMENT: + case DT_RELSZ: + case DT_RELENT: + case DT_INIT_ARRAYSZ: + case DT_FINI_ARRAYSZ: + case DT_PREINIT_ARRAYSZ: + case DT_ANDROID_RELSZ: + case DT_ANDROID_RELASZ: + OS << Value << " (bytes)"; + break; + case DT_NEEDED: + case DT_SONAME: + case DT_AUXILIARY: + case DT_USED: + case DT_FILTER: + case DT_RPATH: + case DT_RUNPATH: { + const std::map<uint64_t, const char*> TagNames = { + {DT_NEEDED, "Shared library"}, + {DT_SONAME, "Library soname"}, + {DT_AUXILIARY, "Auxiliary library"}, + {DT_USED, "Not needed object"}, + {DT_FILTER, "Filter library"}, + {DT_RPATH, "Library rpath"}, + {DT_RUNPATH, "Library runpath"}, + }; + OS << TagNames.at(Type) << ": [" << getDynamicString(Value) << "]"; + break; + } + case DT_FLAGS: + printFlags(Value, makeArrayRef(ElfDynamicDTFlags), OS); + break; + case DT_FLAGS_1: + printFlags(Value, makeArrayRef(ElfDynamicDTFlags1), OS); + break; + default: + OS << format(ConvChar, Value); + break; + } +} + +template <class ELFT> +std::string ELFDumper<ELFT>::getDynamicString(uint64_t Value) const { + if (DynamicStringTable.empty()) + return "<String table is empty or was not found>"; + if (Value < DynamicStringTable.size()) + return DynamicStringTable.data() + Value; + return Twine("<Invalid offset 0x" + utohexstr(Value) + ">").str(); +} + +template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() { + DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF); + Ctx.printUnwindInformation(); +} + +namespace { + +template <> void ELFDumper<ELF32LE>::printUnwindInfo() { + const ELFFile<ELF32LE> *Obj = ObjF->getELFFile(); + const unsigned Machine = Obj->getHeader()->e_machine; + if (Machine == EM_ARM) { + ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, ObjF->getFileName(), + DotSymtabSec); + Ctx.PrintUnwindInformation(); + } + DwarfCFIEH::PrinterContext<ELF32LE> Ctx(W, ObjF); + Ctx.printUnwindInformation(); +} + +} // end anonymous namespace + +template <class ELFT> void ELFDumper<ELFT>::printDynamicTable() { + ELFDumperStyle->printDynamic(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() { + ListScope D(W, "NeededLibraries"); + + std::vector<std::string> Libs; + for (const auto &Entry : dynamic_table()) + if (Entry.d_tag == ELF::DT_NEEDED) + Libs.push_back(getDynamicString(Entry.d_un.d_val)); + + llvm::stable_sort(Libs); + + for (const auto &L : Libs) + W.startLine() << L << "\n"; +} + +template <typename ELFT> void ELFDumper<ELFT>::printHashTable() { + DictScope D(W, "HashTable"); + if (!HashTable) + return; + W.printNumber("Num Buckets", HashTable->nbucket); + W.printNumber("Num Chains", HashTable->nchain); + W.printList("Buckets", HashTable->buckets()); + W.printList("Chains", HashTable->chains()); +} + +template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() { + DictScope D(W, "GnuHashTable"); + if (!GnuHashTable) + return; + W.printNumber("Num Buckets", GnuHashTable->nbuckets); + W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx); + W.printNumber("Num Mask Words", GnuHashTable->maskwords); + W.printNumber("Shift Count", GnuHashTable->shift2); + W.printHexList("Bloom Filter", GnuHashTable->filter()); + W.printList("Buckets", GnuHashTable->buckets()); + Elf_Sym_Range Syms = dynamic_symbols(); + unsigned NumSyms = std::distance(Syms.begin(), Syms.end()); + if (!NumSyms) + reportError(createError("No dynamic symbol section"), ObjF->getFileName()); + W.printHexList("Values", GnuHashTable->values(NumSyms)); +} + +template <typename ELFT> void ELFDumper<ELFT>::printLoadName() { + W.printString("LoadName", SOName); +} + +template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + switch (Obj->getHeader()->e_machine) { + case EM_ARM: + printAttributes(); + break; + case EM_MIPS: { + ELFDumperStyle->printMipsABIFlags(ObjF); + printMipsOptions(); + printMipsReginfo(); + + MipsGOTParser<ELFT> Parser(Obj, ObjF->getFileName(), dynamic_table(), + dynamic_symbols()); + if (Parser.hasGot()) + ELFDumperStyle->printMipsGOT(Parser); + if (Parser.hasPlt()) + ELFDumperStyle->printMipsPLT(Parser); + break; + } + default: + break; + } +} + +template <class ELFT> void ELFDumper<ELFT>::printAttributes() { + W.startLine() << "Attributes not implemented.\n"; +} + +namespace { + +template <> void ELFDumper<ELF32LE>::printAttributes() { + const ELFFile<ELF32LE> *Obj = ObjF->getELFFile(); + if (Obj->getHeader()->e_machine != EM_ARM) { + W.startLine() << "Attributes not implemented.\n"; + return; + } + + DictScope BA(W, "BuildAttributes"); + for (const ELFO::Elf_Shdr &Sec : + unwrapOrError(ObjF->getFileName(), Obj->sections())) { + if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES) + continue; + + ArrayRef<uint8_t> Contents = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(&Sec)); + if (Contents[0] != ARMBuildAttrs::Format_Version) { + errs() << "unrecognised FormatVersion: 0x" + << Twine::utohexstr(Contents[0]) << '\n'; + continue; + } + + W.printHex("FormatVersion", Contents[0]); + if (Contents.size() == 1) + continue; + + ARMAttributeParser(&W).Parse(Contents, true); + } +} + +template <class ELFT> class MipsGOTParser { +public: + TYPEDEF_ELF_TYPES(ELFT) + using Entry = typename ELFO::Elf_Addr; + using Entries = ArrayRef<Entry>; + + const bool IsStatic; + const ELFO * const Obj; + + MipsGOTParser(const ELFO *Obj, StringRef FileName, Elf_Dyn_Range DynTable, + Elf_Sym_Range DynSyms); + + bool hasGot() const { return !GotEntries.empty(); } + bool hasPlt() const { return !PltEntries.empty(); } + + uint64_t getGp() const; + + const Entry *getGotLazyResolver() const; + const Entry *getGotModulePointer() const; + const Entry *getPltLazyResolver() const; + const Entry *getPltModulePointer() const; + + Entries getLocalEntries() const; + Entries getGlobalEntries() const; + Entries getOtherEntries() const; + Entries getPltEntries() const; + + uint64_t getGotAddress(const Entry * E) const; + int64_t getGotOffset(const Entry * E) const; + const Elf_Sym *getGotSym(const Entry *E) const; + + uint64_t getPltAddress(const Entry * E) const; + const Elf_Sym *getPltSym(const Entry *E) const; + + StringRef getPltStrTable() const { return PltStrTable; } + +private: + const Elf_Shdr *GotSec; + size_t LocalNum; + size_t GlobalNum; + + const Elf_Shdr *PltSec; + const Elf_Shdr *PltRelSec; + const Elf_Shdr *PltSymTable; + StringRef FileName; + + Elf_Sym_Range GotDynSyms; + StringRef PltStrTable; + + Entries GotEntries; + Entries PltEntries; +}; + +} // end anonymous namespace + +template <class ELFT> +MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName, + Elf_Dyn_Range DynTable, + Elf_Sym_Range DynSyms) + : IsStatic(DynTable.empty()), Obj(Obj), GotSec(nullptr), LocalNum(0), + GlobalNum(0), PltSec(nullptr), PltRelSec(nullptr), PltSymTable(nullptr), + FileName(FileName) { + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed GOT description. + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + + // Find static GOT secton. + if (IsStatic) { + GotSec = findSectionByName(*Obj, FileName, ".got"); + if (!GotSec) + return; + + ArrayRef<uint8_t> Content = + unwrapOrError(FileName, Obj->getSectionContents(GotSec)); + GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()), + Content.size() / sizeof(Entry)); + LocalNum = GotEntries.size(); + return; + } + + // Lookup dynamic table tags which define GOT/PLT layouts. + Optional<uint64_t> DtPltGot; + Optional<uint64_t> DtLocalGotNum; + Optional<uint64_t> DtGotSym; + Optional<uint64_t> DtMipsPltGot; + Optional<uint64_t> DtJmpRel; + for (const auto &Entry : DynTable) { + switch (Entry.getTag()) { + case ELF::DT_PLTGOT: + DtPltGot = Entry.getVal(); + break; + case ELF::DT_MIPS_LOCAL_GOTNO: + DtLocalGotNum = Entry.getVal(); + break; + case ELF::DT_MIPS_GOTSYM: + DtGotSym = Entry.getVal(); + break; + case ELF::DT_MIPS_PLTGOT: + DtMipsPltGot = Entry.getVal(); + break; + case ELF::DT_JMPREL: + DtJmpRel = Entry.getVal(); + break; + } + } + + // Find dynamic GOT section. + if (DtPltGot || DtLocalGotNum || DtGotSym) { + if (!DtPltGot) + report_fatal_error("Cannot find PLTGOT dynamic table tag."); + if (!DtLocalGotNum) + report_fatal_error("Cannot find MIPS_LOCAL_GOTNO dynamic table tag."); + if (!DtGotSym) + report_fatal_error("Cannot find MIPS_GOTSYM dynamic table tag."); + + size_t DynSymTotal = DynSyms.size(); + if (*DtGotSym > DynSymTotal) + reportError( + createError("MIPS_GOTSYM exceeds a number of dynamic symbols"), + FileName); + + GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot); + if (!GotSec) + reportError(createError("There is no not empty GOT section at 0x" + + Twine::utohexstr(*DtPltGot)), + FileName); + + LocalNum = *DtLocalGotNum; + GlobalNum = DynSymTotal - *DtGotSym; + + ArrayRef<uint8_t> Content = + unwrapOrError(FileName, Obj->getSectionContents(GotSec)); + GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()), + Content.size() / sizeof(Entry)); + GotDynSyms = DynSyms.drop_front(*DtGotSym); + } + + // Find PLT section. + if (DtMipsPltGot || DtJmpRel) { + if (!DtMipsPltGot) + report_fatal_error("Cannot find MIPS_PLTGOT dynamic table tag."); + if (!DtJmpRel) + report_fatal_error("Cannot find JMPREL dynamic table tag."); + + PltSec = findNotEmptySectionByAddress(Obj, FileName, * DtMipsPltGot); + if (!PltSec) + report_fatal_error("There is no not empty PLTGOT section at 0x " + + Twine::utohexstr(*DtMipsPltGot)); + + PltRelSec = findNotEmptySectionByAddress(Obj, FileName, * DtJmpRel); + if (!PltRelSec) + report_fatal_error("There is no not empty RELPLT section at 0x" + + Twine::utohexstr(*DtJmpRel)); + + ArrayRef<uint8_t> PltContent = + unwrapOrError(FileName, Obj->getSectionContents(PltSec)); + PltEntries = Entries(reinterpret_cast<const Entry *>(PltContent.data()), + PltContent.size() / sizeof(Entry)); + + PltSymTable = unwrapOrError(FileName, Obj->getSection(PltRelSec->sh_link)); + PltStrTable = + unwrapOrError(FileName, Obj->getStringTableForSymtab(*PltSymTable)); + } +} + +template <class ELFT> uint64_t MipsGOTParser<ELFT>::getGp() const { + return GotSec->sh_addr + 0x7ff0; +} + +template <class ELFT> +const typename MipsGOTParser<ELFT>::Entry * +MipsGOTParser<ELFT>::getGotLazyResolver() const { + return LocalNum > 0 ? &GotEntries[0] : nullptr; +} + +template <class ELFT> +const typename MipsGOTParser<ELFT>::Entry * +MipsGOTParser<ELFT>::getGotModulePointer() const { + if (LocalNum < 2) + return nullptr; + const Entry &E = GotEntries[1]; + if ((E >> (sizeof(Entry) * 8 - 1)) == 0) + return nullptr; + return &E; +} + +template <class ELFT> +typename MipsGOTParser<ELFT>::Entries +MipsGOTParser<ELFT>::getLocalEntries() const { + size_t Skip = getGotModulePointer() ? 2 : 1; + if (LocalNum - Skip <= 0) + return Entries(); + return GotEntries.slice(Skip, LocalNum - Skip); +} + +template <class ELFT> +typename MipsGOTParser<ELFT>::Entries +MipsGOTParser<ELFT>::getGlobalEntries() const { + if (GlobalNum == 0) + return Entries(); + return GotEntries.slice(LocalNum, GlobalNum); +} + +template <class ELFT> +typename MipsGOTParser<ELFT>::Entries +MipsGOTParser<ELFT>::getOtherEntries() const { + size_t OtherNum = GotEntries.size() - LocalNum - GlobalNum; + if (OtherNum == 0) + return Entries(); + return GotEntries.slice(LocalNum + GlobalNum, OtherNum); +} + +template <class ELFT> +uint64_t MipsGOTParser<ELFT>::getGotAddress(const Entry *E) const { + int64_t Offset = std::distance(GotEntries.data(), E) * sizeof(Entry); + return GotSec->sh_addr + Offset; +} + +template <class ELFT> +int64_t MipsGOTParser<ELFT>::getGotOffset(const Entry *E) const { + int64_t Offset = std::distance(GotEntries.data(), E) * sizeof(Entry); + return Offset - 0x7ff0; +} + +template <class ELFT> +const typename MipsGOTParser<ELFT>::Elf_Sym * +MipsGOTParser<ELFT>::getGotSym(const Entry *E) const { + int64_t Offset = std::distance(GotEntries.data(), E); + return &GotDynSyms[Offset - LocalNum]; +} + +template <class ELFT> +const typename MipsGOTParser<ELFT>::Entry * +MipsGOTParser<ELFT>::getPltLazyResolver() const { + return PltEntries.empty() ? nullptr : &PltEntries[0]; +} + +template <class ELFT> +const typename MipsGOTParser<ELFT>::Entry * +MipsGOTParser<ELFT>::getPltModulePointer() const { + return PltEntries.size() < 2 ? nullptr : &PltEntries[1]; +} + +template <class ELFT> +typename MipsGOTParser<ELFT>::Entries +MipsGOTParser<ELFT>::getPltEntries() const { + if (PltEntries.size() <= 2) + return Entries(); + return PltEntries.slice(2, PltEntries.size() - 2); +} + +template <class ELFT> +uint64_t MipsGOTParser<ELFT>::getPltAddress(const Entry *E) const { + int64_t Offset = std::distance(PltEntries.data(), E) * sizeof(Entry); + return PltSec->sh_addr + Offset; +} + +template <class ELFT> +const typename MipsGOTParser<ELFT>::Elf_Sym * +MipsGOTParser<ELFT>::getPltSym(const Entry *E) const { + int64_t Offset = std::distance(getPltEntries().data(), E); + if (PltRelSec->sh_type == ELF::SHT_REL) { + Elf_Rel_Range Rels = unwrapOrError(FileName, Obj->rels(PltRelSec)); + return unwrapOrError(FileName, + Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); + } else { + Elf_Rela_Range Rels = unwrapOrError(FileName, Obj->relas(PltRelSec)); + return unwrapOrError(FileName, + Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); + } +} + +static const EnumEntry<unsigned> ElfMipsISAExtType[] = { + {"None", Mips::AFL_EXT_NONE}, + {"Broadcom SB-1", Mips::AFL_EXT_SB1}, + {"Cavium Networks Octeon", Mips::AFL_EXT_OCTEON}, + {"Cavium Networks Octeon2", Mips::AFL_EXT_OCTEON2}, + {"Cavium Networks OcteonP", Mips::AFL_EXT_OCTEONP}, + {"Cavium Networks Octeon3", Mips::AFL_EXT_OCTEON3}, + {"LSI R4010", Mips::AFL_EXT_4010}, + {"Loongson 2E", Mips::AFL_EXT_LOONGSON_2E}, + {"Loongson 2F", Mips::AFL_EXT_LOONGSON_2F}, + {"Loongson 3A", Mips::AFL_EXT_LOONGSON_3A}, + {"MIPS R4650", Mips::AFL_EXT_4650}, + {"MIPS R5900", Mips::AFL_EXT_5900}, + {"MIPS R10000", Mips::AFL_EXT_10000}, + {"NEC VR4100", Mips::AFL_EXT_4100}, + {"NEC VR4111/VR4181", Mips::AFL_EXT_4111}, + {"NEC VR4120", Mips::AFL_EXT_4120}, + {"NEC VR5400", Mips::AFL_EXT_5400}, + {"NEC VR5500", Mips::AFL_EXT_5500}, + {"RMI Xlr", Mips::AFL_EXT_XLR}, + {"Toshiba R3900", Mips::AFL_EXT_3900} +}; + +static const EnumEntry<unsigned> ElfMipsASEFlags[] = { + {"DSP", Mips::AFL_ASE_DSP}, + {"DSPR2", Mips::AFL_ASE_DSPR2}, + {"Enhanced VA Scheme", Mips::AFL_ASE_EVA}, + {"MCU", Mips::AFL_ASE_MCU}, + {"MDMX", Mips::AFL_ASE_MDMX}, + {"MIPS-3D", Mips::AFL_ASE_MIPS3D}, + {"MT", Mips::AFL_ASE_MT}, + {"SmartMIPS", Mips::AFL_ASE_SMARTMIPS}, + {"VZ", Mips::AFL_ASE_VIRT}, + {"MSA", Mips::AFL_ASE_MSA}, + {"MIPS16", Mips::AFL_ASE_MIPS16}, + {"microMIPS", Mips::AFL_ASE_MICROMIPS}, + {"XPA", Mips::AFL_ASE_XPA}, + {"CRC", Mips::AFL_ASE_CRC}, + {"GINV", Mips::AFL_ASE_GINV}, +}; + +static const EnumEntry<unsigned> ElfMipsFpABIType[] = { + {"Hard or soft float", Mips::Val_GNU_MIPS_ABI_FP_ANY}, + {"Hard float (double precision)", Mips::Val_GNU_MIPS_ABI_FP_DOUBLE}, + {"Hard float (single precision)", Mips::Val_GNU_MIPS_ABI_FP_SINGLE}, + {"Soft float", Mips::Val_GNU_MIPS_ABI_FP_SOFT}, + {"Hard float (MIPS32r2 64-bit FPU 12 callee-saved)", + Mips::Val_GNU_MIPS_ABI_FP_OLD_64}, + {"Hard float (32-bit CPU, Any FPU)", Mips::Val_GNU_MIPS_ABI_FP_XX}, + {"Hard float (32-bit CPU, 64-bit FPU)", Mips::Val_GNU_MIPS_ABI_FP_64}, + {"Hard float compat (32-bit CPU, 64-bit FPU)", + Mips::Val_GNU_MIPS_ABI_FP_64A} +}; + +static const EnumEntry<unsigned> ElfMipsFlags1[] { + {"ODDSPREG", Mips::AFL_FLAGS1_ODDSPREG}, +}; + +static int getMipsRegisterSize(uint8_t Flag) { + switch (Flag) { + case Mips::AFL_REG_NONE: + return 0; + case Mips::AFL_REG_32: + return 32; + case Mips::AFL_REG_64: + return 64; + case Mips::AFL_REG_128: + return 128; + default: + return -1; + } +} + +template <class ELFT> +static void printMipsReginfoData(ScopedPrinter &W, + const Elf_Mips_RegInfo<ELFT> &Reginfo) { + W.printHex("GP", Reginfo.ri_gp_value); + W.printHex("General Mask", Reginfo.ri_gprmask); + W.printHex("Co-Proc Mask0", Reginfo.ri_cprmask[0]); + W.printHex("Co-Proc Mask1", Reginfo.ri_cprmask[1]); + W.printHex("Co-Proc Mask2", Reginfo.ri_cprmask[2]); + W.printHex("Co-Proc Mask3", Reginfo.ri_cprmask[3]); +} + +template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *Shdr = findSectionByName(*Obj, ObjF->getFileName(), ".reginfo"); + if (!Shdr) { + W.startLine() << "There is no .reginfo section in the file.\n"; + return; + } + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Mips_RegInfo<ELFT>)) { + W.startLine() << "The .reginfo section has a wrong size.\n"; + return; + } + + DictScope GS(W, "MIPS RegInfo"); + auto *Reginfo = reinterpret_cast<const Elf_Mips_RegInfo<ELFT> *>(Sec.data()); + printMipsReginfoData(W, *Reginfo); +} + +template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *Shdr = + findSectionByName(*Obj, ObjF->getFileName(), ".MIPS.options"); + if (!Shdr) { + W.startLine() << "There is no .MIPS.options section in the file.\n"; + return; + } + + DictScope GS(W, "MIPS Options"); + + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); + while (!Sec.empty()) { + if (Sec.size() < sizeof(Elf_Mips_Options<ELFT>)) { + W.startLine() << "The .MIPS.options section has a wrong size.\n"; + return; + } + auto *O = reinterpret_cast<const Elf_Mips_Options<ELFT> *>(Sec.data()); + DictScope GS(W, getElfMipsOptionsOdkType(O->kind)); + switch (O->kind) { + case ODK_REGINFO: + printMipsReginfoData(W, O->getRegInfo()); + break; + default: + W.startLine() << "Unsupported MIPS options tag.\n"; + break; + } + Sec = Sec.slice(O->size); + } +} + +template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *StackMapSection = nullptr; + for (const auto &Sec : unwrapOrError(ObjF->getFileName(), Obj->sections())) { + StringRef Name = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec)); + if (Name == ".llvm_stackmaps") { + StackMapSection = &Sec; + break; + } + } + + if (!StackMapSection) + return; + + ArrayRef<uint8_t> StackMapContentsArray = unwrapOrError( + ObjF->getFileName(), Obj->getSectionContents(StackMapSection)); + + prettyPrintStackMap( + W, StackMapParser<ELFT::TargetEndianness>(StackMapContentsArray)); +} + +template <class ELFT> void ELFDumper<ELFT>::printGroupSections() { + ELFDumperStyle->printGroupSections(ObjF->getELFFile()); +} + +template <class ELFT> void ELFDumper<ELFT>::printAddrsig() { + ELFDumperStyle->printAddrsig(ObjF->getELFFile()); +} + +static inline void printFields(formatted_raw_ostream &OS, StringRef Str1, + StringRef Str2) { + OS.PadToColumn(2u); + OS << Str1; + OS.PadToColumn(37u); + OS << Str2 << "\n"; + OS.flush(); +} + +template <class ELFT> +static std::string getSectionHeadersNumString(const ELFFile<ELFT> *Obj, + StringRef FileName) { + const typename ELFT::Ehdr *ElfHeader = Obj->getHeader(); + if (ElfHeader->e_shnum != 0) + return to_string(ElfHeader->e_shnum); + + ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections()); + if (Arr.empty()) + return "0"; + return "0 (" + to_string(Arr[0].sh_size) + ")"; +} + +template <class ELFT> +static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj, + StringRef FileName) { + const typename ELFT::Ehdr *ElfHeader = Obj->getHeader(); + if (ElfHeader->e_shstrndx != SHN_XINDEX) + return to_string(ElfHeader->e_shstrndx); + + ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections()); + if (Arr.empty()) + return "65535 (corrupt: out of range)"; + return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) + + ")"; +} + +template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) { + const Elf_Ehdr *e = Obj->getHeader(); + OS << "ELF Header:\n"; + OS << " Magic: "; + std::string Str; + for (int i = 0; i < ELF::EI_NIDENT; i++) + OS << format(" %02x", static_cast<int>(e->e_ident[i])); + OS << "\n"; + Str = printEnum(e->e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass)); + printFields(OS, "Class:", Str); + Str = printEnum(e->e_ident[ELF::EI_DATA], makeArrayRef(ElfDataEncoding)); + printFields(OS, "Data:", Str); + OS.PadToColumn(2u); + OS << "Version:"; + OS.PadToColumn(37u); + OS << to_hexString(e->e_ident[ELF::EI_VERSION]); + if (e->e_version == ELF::EV_CURRENT) + OS << " (current)"; + OS << "\n"; + Str = printEnum(e->e_ident[ELF::EI_OSABI], makeArrayRef(ElfOSABI)); + printFields(OS, "OS/ABI:", Str); + Str = "0x" + to_hexString(e->e_ident[ELF::EI_ABIVERSION]); + printFields(OS, "ABI Version:", Str); + Str = printEnum(e->e_type, makeArrayRef(ElfObjectFileType)); + printFields(OS, "Type:", Str); + Str = printEnum(e->e_machine, makeArrayRef(ElfMachineType)); + printFields(OS, "Machine:", Str); + Str = "0x" + to_hexString(e->e_version); + printFields(OS, "Version:", Str); + Str = "0x" + to_hexString(e->e_entry); + printFields(OS, "Entry point address:", Str); + Str = to_string(e->e_phoff) + " (bytes into file)"; + printFields(OS, "Start of program headers:", Str); + Str = to_string(e->e_shoff) + " (bytes into file)"; + printFields(OS, "Start of section headers:", Str); + std::string ElfFlags; + if (e->e_machine == EM_MIPS) + ElfFlags = + printFlags(e->e_flags, makeArrayRef(ElfHeaderMipsFlags), + unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), + unsigned(ELF::EF_MIPS_MACH)); + else if (e->e_machine == EM_RISCV) + ElfFlags = printFlags(e->e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + Str = "0x" + to_hexString(e->e_flags); + if (!ElfFlags.empty()) + Str = Str + ", " + ElfFlags; + printFields(OS, "Flags:", Str); + Str = to_string(e->e_ehsize) + " (bytes)"; + printFields(OS, "Size of this header:", Str); + Str = to_string(e->e_phentsize) + " (bytes)"; + printFields(OS, "Size of program headers:", Str); + Str = to_string(e->e_phnum); + printFields(OS, "Number of program headers:", Str); + Str = to_string(e->e_shentsize) + " (bytes)"; + printFields(OS, "Size of section headers:", Str); + Str = getSectionHeadersNumString(Obj, this->FileName); + printFields(OS, "Number of section headers:", Str); + Str = getSectionHeaderTableIndexString(Obj, this->FileName); + printFields(OS, "Section header string table index:", Str); +} + +namespace { +struct GroupMember { + StringRef Name; + uint64_t Index; +}; + +struct GroupSection { + StringRef Name; + std::string Signature; + uint64_t ShName; + uint64_t Index; + uint32_t Link; + uint32_t Info; + uint32_t Type; + std::vector<GroupMember> Members; +}; + +template <class ELFT> +std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj, + StringRef FileName) { + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Word = typename ELFT::Word; + + std::vector<GroupSection> Ret; + uint64_t I = 0; + for (const Elf_Shdr &Sec : unwrapOrError(FileName, Obj->sections())) { + ++I; + if (Sec.sh_type != ELF::SHT_GROUP) + continue; + + const Elf_Shdr *Symtab = + unwrapOrError(FileName, Obj->getSection(Sec.sh_link)); + StringRef StrTable = + unwrapOrError(FileName, Obj->getStringTableForSymtab(*Symtab)); + const Elf_Sym *Sym = unwrapOrError( + FileName, Obj->template getEntry<Elf_Sym>(Symtab, Sec.sh_info)); + auto Data = unwrapOrError( + FileName, Obj->template getSectionContentsAsArray<Elf_Word>(&Sec)); + + StringRef Name = unwrapOrError(FileName, Obj->getSectionName(&Sec)); + StringRef Signature = StrTable.data() + Sym->st_name; + Ret.push_back({Name, + maybeDemangle(Signature), + Sec.sh_name, + I - 1, + Sec.sh_link, + Sec.sh_info, + Data[0], + {}}); + + std::vector<GroupMember> &GM = Ret.back().Members; + for (uint32_t Ndx : Data.slice(1)) { + auto Sec = unwrapOrError(FileName, Obj->getSection(Ndx)); + const StringRef Name = unwrapOrError(FileName, Obj->getSectionName(Sec)); + GM.push_back({Name, Ndx}); + } + } + return Ret; +} + +DenseMap<uint64_t, const GroupSection *> +mapSectionsToGroups(ArrayRef<GroupSection> Groups) { + DenseMap<uint64_t, const GroupSection *> Ret; + for (const GroupSection &G : Groups) + for (const GroupMember &GM : G.Members) + Ret.insert({GM.Index, &G}); + return Ret; +} + +} // namespace + +template <class ELFT> void GNUStyle<ELFT>::printGroupSections(const ELFO *Obj) { + std::vector<GroupSection> V = getGroups<ELFT>(Obj, this->FileName); + DenseMap<uint64_t, const GroupSection *> Map = mapSectionsToGroups(V); + for (const GroupSection &G : V) { + OS << "\n" + << getGroupType(G.Type) << " group section [" + << format_decimal(G.Index, 5) << "] `" << G.Name << "' [" << G.Signature + << "] contains " << G.Members.size() << " sections:\n" + << " [Index] Name\n"; + for (const GroupMember &GM : G.Members) { + const GroupSection *MainGroup = Map[GM.Index]; + if (MainGroup != &G) { + OS.flush(); + errs() << "Error: section [" << format_decimal(GM.Index, 5) + << "] in group section [" << format_decimal(G.Index, 5) + << "] already in group section [" + << format_decimal(MainGroup->Index, 5) << "]"; + errs().flush(); + continue; + } + OS << " [" << format_decimal(GM.Index, 5) << "] " << GM.Name << "\n"; + } + } + + if (V.empty()) + OS << "There are no section groups in this file.\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, + const Elf_Rela &R, bool IsRela) { + const Elf_Sym *Sym = + unwrapOrError(this->FileName, Obj->getRelocationSymbol(&R, SymTab)); + std::string TargetName; + if (Sym && Sym->getType() == ELF::STT_SECTION) { + const Elf_Shdr *Sec = unwrapOrError( + this->FileName, + Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable())); + TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec)); + } else if (Sym) { + StringRef StrTable = + unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab)); + TargetName = this->dumper()->getFullSymbolName( + Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); + } + printRelocation(Obj, Sym, TargetName, R, IsRela); +} + +template <class ELFT> +void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Sym *Sym, + StringRef SymbolName, const Elf_Rela &R, + bool IsRela) { + // First two fields are bit width dependent. The rest of them are fixed width. + unsigned Bias = ELFT::Is64Bits ? 8 : 0; + Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias}; + unsigned Width = ELFT::Is64Bits ? 16 : 8; + + Fields[0].Str = to_string(format_hex_no_prefix(R.r_offset, Width)); + Fields[1].Str = to_string(format_hex_no_prefix(R.r_info, Width)); + + SmallString<32> RelocName; + Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); + Fields[2].Str = RelocName.c_str(); + + if (Sym && (!SymbolName.empty() || Sym->getValue() != 0)) + Fields[3].Str = to_string(format_hex_no_prefix(Sym->getValue(), Width)); + + Fields[4].Str = SymbolName; + for (const Field &F : Fields) + printField(F); + + std::string Addend; + if (IsRela) { + int64_t RelAddend = R.r_addend; + if (!SymbolName.empty()) { + if (R.r_addend < 0) { + Addend = " - "; + RelAddend = std::abs(RelAddend); + } else + Addend = " + "; + } + + Addend += to_hexString(RelAddend, false); + } + OS << Addend << "\n"; +} + +template <class ELFT> void GNUStyle<ELFT>::printRelocHeader(unsigned SType) { + bool IsRela = SType == ELF::SHT_RELA || SType == ELF::SHT_ANDROID_RELA; + bool IsRelr = SType == ELF::SHT_RELR || SType == ELF::SHT_ANDROID_RELR; + if (ELFT::Is64Bits) + OS << " "; + else + OS << " "; + if (IsRelr && opts::RawRelr) + OS << "Data "; + else + OS << "Offset"; + if (ELFT::Is64Bits) + OS << " Info Type" + << " Symbol's Value Symbol's Name"; + else + OS << " Info Type Sym. Value Symbol's Name"; + if (IsRela) + OS << " + Addend"; + OS << "\n"; +} + +template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { + bool HasRelocSections = false; + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { + if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA && + Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_REL && + Sec.sh_type != ELF::SHT_ANDROID_RELA && + Sec.sh_type != ELF::SHT_ANDROID_RELR) + continue; + HasRelocSections = true; + StringRef Name = unwrapOrError(this->FileName, Obj->getSectionName(&Sec)); + unsigned Entries = Sec.getEntityCount(); + std::vector<Elf_Rela> AndroidRelas; + if (Sec.sh_type == ELF::SHT_ANDROID_REL || + Sec.sh_type == ELF::SHT_ANDROID_RELA) { + // Android's packed relocation section needs to be unpacked first + // to get the actual number of entries. + AndroidRelas = unwrapOrError(this->FileName, Obj->android_relas(&Sec)); + Entries = AndroidRelas.size(); + } + std::vector<Elf_Rela> RelrRelas; + if (!opts::RawRelr && (Sec.sh_type == ELF::SHT_RELR || + Sec.sh_type == ELF::SHT_ANDROID_RELR)) { + // .relr.dyn relative relocation section needs to be unpacked first + // to get the actual number of entries. + Elf_Relr_Range Relrs = unwrapOrError(this->FileName, Obj->relrs(&Sec)); + RelrRelas = unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); + Entries = RelrRelas.size(); + } + uintX_t Offset = Sec.sh_offset; + OS << "\nRelocation section '" << Name << "' at offset 0x" + << to_hexString(Offset, false) << " contains " << Entries + << " entries:\n"; + printRelocHeader(Sec.sh_type); + const Elf_Shdr *SymTab = + unwrapOrError(this->FileName, Obj->getSection(Sec.sh_link)); + switch (Sec.sh_type) { + case ELF::SHT_REL: + for (const auto &R : unwrapOrError(this->FileName, Obj->rels(&Sec))) { + Elf_Rela Rela; + Rela.r_offset = R.r_offset; + Rela.r_info = R.r_info; + Rela.r_addend = 0; + printRelocation(Obj, SymTab, Rela, false); + } + break; + case ELF::SHT_RELA: + for (const auto &R : unwrapOrError(this->FileName, Obj->relas(&Sec))) + printRelocation(Obj, SymTab, R, true); + break; + case ELF::SHT_RELR: + case ELF::SHT_ANDROID_RELR: + if (opts::RawRelr) + for (const auto &R : unwrapOrError(this->FileName, Obj->relrs(&Sec))) + OS << to_string(format_hex_no_prefix(R, ELFT::Is64Bits ? 16 : 8)) + << "\n"; + else + for (const auto &R : RelrRelas) + printRelocation(Obj, SymTab, R, false); + break; + case ELF::SHT_ANDROID_REL: + case ELF::SHT_ANDROID_RELA: + for (const auto &R : AndroidRelas) + printRelocation(Obj, SymTab, R, Sec.sh_type == ELF::SHT_ANDROID_RELA); + break; + } + } + if (!HasRelocSections) + OS << "\nThere are no relocations in this file.\n"; +} + +// Print the offset of a particular section from anyone of the ranges: +// [SHT_LOOS, SHT_HIOS], [SHT_LOPROC, SHT_HIPROC], [SHT_LOUSER, SHT_HIUSER]. +// If 'Type' does not fall within any of those ranges, then a string is +// returned as '<unknown>' followed by the type value. +static std::string getSectionTypeOffsetString(unsigned Type) { + if (Type >= SHT_LOOS && Type <= SHT_HIOS) + return "LOOS+0x" + to_hexString(Type - SHT_LOOS); + else if (Type >= SHT_LOPROC && Type <= SHT_HIPROC) + return "LOPROC+0x" + to_hexString(Type - SHT_LOPROC); + else if (Type >= SHT_LOUSER && Type <= SHT_HIUSER) + return "LOUSER+0x" + to_hexString(Type - SHT_LOUSER); + return "0x" + to_hexString(Type) + ": <unknown>"; +} + +static std::string getSectionTypeString(unsigned Arch, unsigned Type) { + using namespace ELF; + + switch (Arch) { + case EM_ARM: + switch (Type) { + case SHT_ARM_EXIDX: + return "ARM_EXIDX"; + case SHT_ARM_PREEMPTMAP: + return "ARM_PREEMPTMAP"; + case SHT_ARM_ATTRIBUTES: + return "ARM_ATTRIBUTES"; + case SHT_ARM_DEBUGOVERLAY: + return "ARM_DEBUGOVERLAY"; + case SHT_ARM_OVERLAYSECTION: + return "ARM_OVERLAYSECTION"; + } + break; + case EM_X86_64: + switch (Type) { + case SHT_X86_64_UNWIND: + return "X86_64_UNWIND"; + } + break; + case EM_MIPS: + case EM_MIPS_RS3_LE: + switch (Type) { + case SHT_MIPS_REGINFO: + return "MIPS_REGINFO"; + case SHT_MIPS_OPTIONS: + return "MIPS_OPTIONS"; + case SHT_MIPS_DWARF: + return "MIPS_DWARF"; + case SHT_MIPS_ABIFLAGS: + return "MIPS_ABIFLAGS"; + } + break; + } + switch (Type) { + case SHT_NULL: + return "NULL"; + case SHT_PROGBITS: + return "PROGBITS"; + case SHT_SYMTAB: + return "SYMTAB"; + case SHT_STRTAB: + return "STRTAB"; + case SHT_RELA: + return "RELA"; + case SHT_HASH: + return "HASH"; + case SHT_DYNAMIC: + return "DYNAMIC"; + case SHT_NOTE: + return "NOTE"; + case SHT_NOBITS: + return "NOBITS"; + case SHT_REL: + return "REL"; + case SHT_SHLIB: + return "SHLIB"; + case SHT_DYNSYM: + return "DYNSYM"; + case SHT_INIT_ARRAY: + return "INIT_ARRAY"; + case SHT_FINI_ARRAY: + return "FINI_ARRAY"; + case SHT_PREINIT_ARRAY: + return "PREINIT_ARRAY"; + case SHT_GROUP: + return "GROUP"; + case SHT_SYMTAB_SHNDX: + return "SYMTAB SECTION INDICES"; + case SHT_ANDROID_REL: + return "ANDROID_REL"; + case SHT_ANDROID_RELA: + return "ANDROID_RELA"; + case SHT_RELR: + case SHT_ANDROID_RELR: + return "RELR"; + case SHT_LLVM_ODRTAB: + return "LLVM_ODRTAB"; + case SHT_LLVM_LINKER_OPTIONS: + return "LLVM_LINKER_OPTIONS"; + case SHT_LLVM_CALL_GRAPH_PROFILE: + return "LLVM_CALL_GRAPH_PROFILE"; + case SHT_LLVM_ADDRSIG: + return "LLVM_ADDRSIG"; + case SHT_LLVM_DEPENDENT_LIBRARIES: + return "LLVM_DEPENDENT_LIBRARIES"; + case SHT_LLVM_SYMPART: + return "LLVM_SYMPART"; + case SHT_LLVM_PART_EHDR: + return "LLVM_PART_EHDR"; + case SHT_LLVM_PART_PHDR: + return "LLVM_PART_PHDR"; + // FIXME: Parse processor specific GNU attributes + case SHT_GNU_ATTRIBUTES: + return "ATTRIBUTES"; + case SHT_GNU_HASH: + return "GNU_HASH"; + case SHT_GNU_verdef: + return "VERDEF"; + case SHT_GNU_verneed: + return "VERNEED"; + case SHT_GNU_versym: + return "VERSYM"; + default: + return getSectionTypeOffsetString(Type); + } + return ""; +} + +template <class ELFT> +void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { + unsigned Bias = ELFT::Is64Bits ? 0 : 8; + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + OS << "There are " << to_string(Sections.size()) + << " section headers, starting at offset " + << "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n"; + OS << "Section Headers:\n"; + Field Fields[11] = { + {"[Nr]", 2}, {"Name", 7}, {"Type", 25}, + {"Address", 41}, {"Off", 58 - Bias}, {"Size", 65 - Bias}, + {"ES", 72 - Bias}, {"Flg", 75 - Bias}, {"Lk", 79 - Bias}, + {"Inf", 82 - Bias}, {"Al", 86 - Bias}}; + for (auto &F : Fields) + printField(F); + OS << "\n"; + + const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); + size_t SectionIndex = 0; + for (const Elf_Shdr &Sec : Sections) { + Fields[0].Str = to_string(SectionIndex); + Fields[1].Str = unwrapOrError<StringRef>( + ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); + Fields[2].Str = + getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type); + Fields[3].Str = + to_string(format_hex_no_prefix(Sec.sh_addr, ELFT::Is64Bits ? 16 : 8)); + Fields[4].Str = to_string(format_hex_no_prefix(Sec.sh_offset, 6)); + Fields[5].Str = to_string(format_hex_no_prefix(Sec.sh_size, 6)); + Fields[6].Str = to_string(format_hex_no_prefix(Sec.sh_entsize, 2)); + Fields[7].Str = getGNUFlags(Sec.sh_flags); + Fields[8].Str = to_string(Sec.sh_link); + Fields[9].Str = to_string(Sec.sh_info); + Fields[10].Str = to_string(Sec.sh_addralign); + + OS.PadToColumn(Fields[0].Column); + OS << "[" << right_justify(Fields[0].Str, 2) << "]"; + for (int i = 1; i < 7; i++) + printField(Fields[i]); + OS.PadToColumn(Fields[7].Column); + OS << right_justify(Fields[7].Str, 3); + OS.PadToColumn(Fields[8].Column); + OS << right_justify(Fields[8].Str, 2); + OS.PadToColumn(Fields[9].Column); + OS << right_justify(Fields[9].Str, 3); + OS.PadToColumn(Fields[10].Column); + OS << right_justify(Fields[10].Str, 2); + OS << "\n"; + ++SectionIndex; + } + OS << "Key to Flags:\n" + << " W (write), A (alloc), X (execute), M (merge), S (strings), l " + "(large)\n" + << " I (info), L (link order), G (group), T (TLS), E (exclude),\ + x (unknown)\n" + << " O (extra OS processing required) o (OS specific),\ + p (processor specific)\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name, + size_t Entries, + bool NonVisibilityBitsUsed) { + if (!Name.empty()) + OS << "\nSymbol table '" << Name << "' contains " << Entries + << " entries:\n"; + else + OS << "\n Symbol table for image:\n"; + + if (ELFT::Is64Bits) + OS << " Num: Value Size Type Bind Vis"; + else + OS << " Num: Value Size Type Bind Vis"; + + if (NonVisibilityBitsUsed) + OS << " "; + OS << " Ndx Name\n"; +} + +template <class ELFT> +std::string GNUStyle<ELFT>::getSymbolSectionNdx(const ELFO *Obj, + const Elf_Sym *Symbol, + const Elf_Sym *FirstSym) { + unsigned SectionIndex = Symbol->st_shndx; + switch (SectionIndex) { + case ELF::SHN_UNDEF: + return "UND"; + case ELF::SHN_ABS: + return "ABS"; + case ELF::SHN_COMMON: + return "COM"; + case ELF::SHN_XINDEX: + return to_string(format_decimal( + unwrapOrError(this->FileName, + object::getExtendedSymbolTableIndex<ELFT>( + Symbol, FirstSym, this->dumper()->getShndxTable())), + 3)); + default: + // Find if: + // Processor specific + if (SectionIndex >= ELF::SHN_LOPROC && SectionIndex <= ELF::SHN_HIPROC) + return std::string("PRC[0x") + + to_string(format_hex_no_prefix(SectionIndex, 4)) + "]"; + // OS specific + if (SectionIndex >= ELF::SHN_LOOS && SectionIndex <= ELF::SHN_HIOS) + return std::string("OS[0x") + + to_string(format_hex_no_prefix(SectionIndex, 4)) + "]"; + // Architecture reserved: + if (SectionIndex >= ELF::SHN_LORESERVE && + SectionIndex <= ELF::SHN_HIRESERVE) + return std::string("RSV[0x") + + to_string(format_hex_no_prefix(SectionIndex, 4)) + "]"; + // A normal section with an index + return to_string(format_decimal(SectionIndex, 3)); + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, + const Elf_Sym *FirstSym, StringRef StrTable, + bool IsDynamic, bool NonVisibilityBitsUsed) { + static int Idx = 0; + static bool Dynamic = true; + + // If this function was called with a different value from IsDynamic + // from last call, happens when we move from dynamic to static symbol + // table, "Num" field should be reset. + if (!Dynamic != !IsDynamic) { + Idx = 0; + Dynamic = false; + } + + unsigned Bias = ELFT::Is64Bits ? 8 : 0; + Field Fields[8] = {0, 8, 17 + Bias, 23 + Bias, + 31 + Bias, 38 + Bias, 48 + Bias, 51 + Bias}; + Fields[0].Str = to_string(format_decimal(Idx++, 6)) + ":"; + Fields[1].Str = to_string( + format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 16 : 8)); + Fields[2].Str = to_string(format_decimal(Symbol->st_size, 5)); + + unsigned char SymbolType = Symbol->getType(); + if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && + SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) + Fields[3].Str = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + else + Fields[3].Str = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); + + Fields[4].Str = + printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); + Fields[5].Str = + printEnum(Symbol->getVisibility(), makeArrayRef(ElfSymbolVisibilities)); + if (Symbol->st_other & ~0x3) + Fields[5].Str += + " [<other: " + to_string(format_hex(Symbol->st_other, 2)) + ">]"; + + Fields[6].Column += NonVisibilityBitsUsed ? 13 : 0; + Fields[6].Str = getSymbolSectionNdx(Obj, Symbol, FirstSym); + + Fields[7].Str = + this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic); + for (auto &Entry : Fields) + printField(Entry); + OS << "\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, + uint32_t Sym, StringRef StrTable, + uint32_t Bucket) { + unsigned Bias = ELFT::Is64Bits ? 8 : 0; + Field Fields[9] = {0, 6, 11, 20 + Bias, 25 + Bias, + 34 + Bias, 41 + Bias, 49 + Bias, 53 + Bias}; + Fields[0].Str = to_string(format_decimal(Sym, 5)); + Fields[1].Str = to_string(format_decimal(Bucket, 3)) + ":"; + + const auto Symbol = FirstSym + Sym; + Fields[2].Str = to_string( + format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 16 : 8)); + Fields[3].Str = to_string(format_decimal(Symbol->st_size, 5)); + + unsigned char SymbolType = Symbol->getType(); + if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && + SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) + Fields[4].Str = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + else + Fields[4].Str = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); + + Fields[5].Str = + printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); + Fields[6].Str = + printEnum(Symbol->getVisibility(), makeArrayRef(ElfSymbolVisibilities)); + Fields[7].Str = getSymbolSectionNdx(Obj, Symbol, FirstSym); + Fields[8].Str = this->dumper()->getFullSymbolName(Symbol, StrTable, true); + + for (auto &Entry : Fields) + printField(Entry); + OS << "\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) { + if (!PrintSymbols && !PrintDynamicSymbols) + return; + // GNU readelf prints both the .dynsym and .symtab with --symbols. + this->dumper()->printSymbolsHelper(true); + if (PrintSymbols) + this->dumper()->printSymbolsHelper(false); +} + +template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) { + if (this->dumper()->getDynamicStringTable().empty()) + return; + auto StringTable = this->dumper()->getDynamicStringTable(); + auto DynSyms = this->dumper()->dynamic_symbols(); + + // Try printing .hash + if (auto SysVHash = this->dumper()->getHashTable()) { + OS << "\n Symbol table of .hash for image:\n"; + if (ELFT::Is64Bits) + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + else + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + OS << "\n"; + + auto Buckets = SysVHash->buckets(); + auto Chains = SysVHash->chains(); + for (uint32_t Buc = 0; Buc < SysVHash->nbucket; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + std::vector<bool> Visited(SysVHash->nchain); + for (uint32_t Ch = Buckets[Buc]; Ch < SysVHash->nchain; Ch = Chains[Ch]) { + if (Ch == ELF::STN_UNDEF) + break; + + if (Visited[Ch]) { + reportWarning( + createError(".hash section is invalid: bucket " + Twine(Ch) + + ": a cycle was detected in the linked chain"), + this->FileName); + break; + } + + printHashedSymbol(Obj, &DynSyms[0], Ch, StringTable, Buc); + Visited[Ch] = true; + } + } + } + + // Try printing .gnu.hash + if (auto GnuHash = this->dumper()->getGnuHashTable()) { + OS << "\n Symbol table of .gnu.hash for image:\n"; + if (ELFT::Is64Bits) + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + else + OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; + OS << "\n"; + auto Buckets = GnuHash->buckets(); + for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + uint32_t Index = Buckets[Buc]; + uint32_t GnuHashable = Index - GnuHash->symndx; + // Print whole chain + while (true) { + printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc); + // Chain ends at symbol with stopper bit + if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1) + break; + } + } + } +} + +static inline std::string printPhdrFlags(unsigned Flag) { + std::string Str; + Str = (Flag & PF_R) ? "R" : " "; + Str += (Flag & PF_W) ? "W" : " "; + Str += (Flag & PF_X) ? "E" : " "; + return Str; +} + +// SHF_TLS sections are only in PT_TLS, PT_LOAD or PT_GNU_RELRO +// PT_TLS must only have SHF_TLS sections +template <class ELFT> +bool GNUStyle<ELFT>::checkTLSSections(const Elf_Phdr &Phdr, + const Elf_Shdr &Sec) { + return (((Sec.sh_flags & ELF::SHF_TLS) && + ((Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) || + (Phdr.p_type == ELF::PT_GNU_RELRO))) || + (!(Sec.sh_flags & ELF::SHF_TLS) && Phdr.p_type != ELF::PT_TLS)); +} + +// Non-SHT_NOBITS must have its offset inside the segment +// Only non-zero section can be at end of segment +template <class ELFT> +bool GNUStyle<ELFT>::checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { + if (Sec.sh_type == ELF::SHT_NOBITS) + return true; + bool IsSpecial = + (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0); + // .tbss is special, it only has memory in PT_TLS and has NOBITS properties + auto SectionSize = + (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size; + if (Sec.sh_offset >= Phdr.p_offset) + return ((Sec.sh_offset + SectionSize <= Phdr.p_filesz + Phdr.p_offset) + /*only non-zero sized sections at end*/ + && (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz)); + return false; +} + +// SHF_ALLOC must have VMA inside segment +// Only non-zero section can be at end of segment +template <class ELFT> +bool GNUStyle<ELFT>::checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { + if (!(Sec.sh_flags & ELF::SHF_ALLOC)) + return true; + bool IsSpecial = + (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0); + // .tbss is special, it only has memory in PT_TLS and has NOBITS properties + auto SectionSize = + (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size; + if (Sec.sh_addr >= Phdr.p_vaddr) + return ((Sec.sh_addr + SectionSize <= Phdr.p_vaddr + Phdr.p_memsz) && + (Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz)); + return false; +} + +// No section with zero size must be at start or end of PT_DYNAMIC +template <class ELFT> +bool GNUStyle<ELFT>::checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { + if (Phdr.p_type != ELF::PT_DYNAMIC || Sec.sh_size != 0 || Phdr.p_memsz == 0) + return true; + // Is section within the phdr both based on offset and VMA ? + return ((Sec.sh_type == ELF::SHT_NOBITS) || + (Sec.sh_offset > Phdr.p_offset && + Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz)) && + (!(Sec.sh_flags & ELF::SHF_ALLOC) || + (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz)); +} + +template <class ELFT> +void GNUStyle<ELFT>::printProgramHeaders( + const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(Obj); + + // Display the section mapping along with the program headers, unless + // -section-mapping is explicitly set to false. + if (PrintSectionMapping != cl::BOU_FALSE) + printSectionMapping(Obj); +} + +template <class ELFT> +void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { + unsigned Bias = ELFT::Is64Bits ? 8 : 0; + const Elf_Ehdr *Header = Obj->getHeader(); + Field Fields[8] = {2, 17, 26, 37 + Bias, + 48 + Bias, 56 + Bias, 64 + Bias, 68 + Bias}; + OS << "\nElf file type is " + << printEnum(Header->e_type, makeArrayRef(ElfObjectFileType)) << "\n" + << "Entry point " << format_hex(Header->e_entry, 3) << "\n" + << "There are " << Header->e_phnum << " program headers," + << " starting at offset " << Header->e_phoff << "\n\n" + << "Program Headers:\n"; + if (ELFT::Is64Bits) + OS << " Type Offset VirtAddr PhysAddr " + << " FileSiz MemSiz Flg Align\n"; + else + OS << " Type Offset VirtAddr PhysAddr FileSiz " + << "MemSiz Flg Align\n"; + + unsigned Width = ELFT::Is64Bits ? 18 : 10; + unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7; + for (const auto &Phdr : + unwrapOrError(this->FileName, Obj->program_headers())) { + Fields[0].Str = getElfPtType(Header->e_machine, Phdr.p_type); + Fields[1].Str = to_string(format_hex(Phdr.p_offset, 8)); + Fields[2].Str = to_string(format_hex(Phdr.p_vaddr, Width)); + Fields[3].Str = to_string(format_hex(Phdr.p_paddr, Width)); + Fields[4].Str = to_string(format_hex(Phdr.p_filesz, SizeWidth)); + Fields[5].Str = to_string(format_hex(Phdr.p_memsz, SizeWidth)); + Fields[6].Str = printPhdrFlags(Phdr.p_flags); + Fields[7].Str = to_string(format_hex(Phdr.p_align, 1)); + for (auto Field : Fields) + printField(Field); + if (Phdr.p_type == ELF::PT_INTERP) { + OS << "\n [Requesting program interpreter: "; + OS << reinterpret_cast<const char *>(Obj->base()) + Phdr.p_offset << "]"; + } + OS << "\n"; + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) { + OS << "\n Section to Segment mapping:\n Segment Sections...\n"; + DenseSet<const Elf_Shdr *> BelongsToSegment; + int Phnum = 0; + for (const Elf_Phdr &Phdr : + unwrapOrError(this->FileName, Obj->program_headers())) { + std::string Sections; + OS << format(" %2.2d ", Phnum++); + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { + // Check if each section is in a segment and then print mapping. + // readelf additionally makes sure it does not print zero sized sections + // at end of segments and for PT_DYNAMIC both start and end of section + // .tbss must only be shown in PT_TLS section. + bool TbssInNonTLS = (Sec.sh_type == ELF::SHT_NOBITS) && + ((Sec.sh_flags & ELF::SHF_TLS) != 0) && + Phdr.p_type != ELF::PT_TLS; + if (!TbssInNonTLS && checkTLSSections(Phdr, Sec) && + checkoffsets(Phdr, Sec) && checkVMA(Phdr, Sec) && + checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL)) { + Sections += + unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + + " "; + BelongsToSegment.insert(&Sec); + } + } + OS << Sections << "\n"; + OS.flush(); + } + + // Display sections that do not belong to a segment. + std::string Sections; + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { + if (BelongsToSegment.find(&Sec) == BelongsToSegment.end()) + Sections += + unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + ' '; + } + if (!Sections.empty()) { + OS << " None " << Sections << '\n'; + OS.flush(); + } +} + +namespace { +template <class ELFT> struct RelSymbol { + const typename ELFT::Sym *Sym; + std::string Name; +}; + +template <class ELFT> +RelSymbol<ELFT> getSymbolForReloc(const ELFFile<ELFT> *Obj, StringRef FileName, + const ELFDumper<ELFT> *Dumper, + const typename ELFT::Rela &Reloc) { + uint32_t SymIndex = Reloc.getSymbol(Obj->isMips64EL()); + const typename ELFT::Sym *Sym = Dumper->dynamic_symbols().begin() + SymIndex; + Expected<StringRef> ErrOrName = Sym->getName(Dumper->getDynamicStringTable()); + + std::string Name; + if (ErrOrName) { + Name = maybeDemangle(*ErrOrName); + } else { + reportWarning( + createError("unable to get name of the dynamic symbol with index " + + Twine(SymIndex) + ": " + toString(ErrOrName.takeError())), + FileName); + Name = "<corrupt>"; + } + + return {Sym, std::move(Name)}; +} +} // namespace + +template <class ELFT> +void GNUStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, + bool IsRela) { + RelSymbol<ELFT> S = getSymbolForReloc(Obj, this->FileName, this->dumper(), R); + printRelocation(Obj, S.Sym, S.Name, R, IsRela); +} + +template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) { + Elf_Dyn_Range Table = this->dumper()->dynamic_table(); + if (Table.empty()) + return; + + const DynRegionInfo &DynamicTableRegion = + this->dumper()->getDynamicTableRegion(); + + OS << "Dynamic section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(DynamicTableRegion.Addr) - + Obj->base(), + 1) + << " contains " << Table.size() << " entries:\n"; + + bool Is64 = ELFT::Is64Bits; + if (Is64) + OS << " Tag Type Name/Value\n"; + else + OS << " Tag Type Name/Value\n"; + for (auto Entry : Table) { + uintX_t Tag = Entry.getTag(); + std::string TypeString = std::string("(") + + getTypeString(Obj->getHeader()->e_machine, Tag) + + ")"; + OS << " " << format_hex(Tag, Is64 ? 18 : 10) + << format(" %-20s ", TypeString.c_str()); + this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal()); + OS << "\n"; + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { + const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion(); + const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion(); + const DynRegionInfo &DynRelrRegion = this->dumper()->getDynRelrRegion(); + const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion(); + if (DynRelaRegion.Size > 0) { + OS << "\n'RELA' relocation section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(DynRelaRegion.Addr) - + Obj->base(), + 1) + << " contains " << DynRelaRegion.Size << " bytes:\n"; + printRelocHeader(ELF::SHT_RELA); + for (const Elf_Rela &Rela : this->dumper()->dyn_relas()) + printDynamicRelocation(Obj, Rela, true); + } + if (DynRelRegion.Size > 0) { + OS << "\n'REL' relocation section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(DynRelRegion.Addr) - + Obj->base(), + 1) + << " contains " << DynRelRegion.Size << " bytes:\n"; + printRelocHeader(ELF::SHT_REL); + for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) { + Elf_Rela Rela; + Rela.r_offset = Rel.r_offset; + Rela.r_info = Rel.r_info; + Rela.r_addend = 0; + printDynamicRelocation(Obj, Rela, false); + } + } + if (DynRelrRegion.Size > 0) { + OS << "\n'RELR' relocation section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(DynRelrRegion.Addr) - + Obj->base(), + 1) + << " contains " << DynRelrRegion.Size << " bytes:\n"; + printRelocHeader(ELF::SHT_REL); + Elf_Relr_Range Relrs = this->dumper()->dyn_relrs(); + std::vector<Elf_Rela> RelrRelas = + unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); + for (const Elf_Rela &Rela : RelrRelas) { + printDynamicRelocation(Obj, Rela, false); + } + } + if (DynPLTRelRegion.Size) { + OS << "\n'PLT' relocation section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(DynPLTRelRegion.Addr) - + Obj->base(), + 1) + << " contains " << DynPLTRelRegion.Size << " bytes:\n"; + } + if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) { + printRelocHeader(ELF::SHT_RELA); + for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>()) + printDynamicRelocation(Obj, Rela, true); + } else { + printRelocHeader(ELF::SHT_REL); + for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) { + Elf_Rela Rela; + Rela.r_offset = Rel.r_offset; + Rela.r_info = Rel.r_info; + Rela.r_addend = 0; + printDynamicRelocation(Obj, Rela, false); + } + } +} + +template <class ELFT> +static void printGNUVersionSectionProlog(formatted_raw_ostream &OS, + const Twine &Name, unsigned EntriesNum, + const ELFFile<ELFT> *Obj, + const typename ELFT::Shdr *Sec, + StringRef FileName) { + StringRef SecName = unwrapOrError(FileName, Obj->getSectionName(Sec)); + OS << Name << " section '" << SecName << "' " + << "contains " << EntriesNum << " entries:\n"; + + const typename ELFT::Shdr *SymTab = + unwrapOrError(FileName, Obj->getSection(Sec->sh_link)); + StringRef SymTabName = unwrapOrError(FileName, Obj->getSectionName(SymTab)); + OS << " Addr: " << format_hex_no_prefix(Sec->sh_addr, 16) + << " Offset: " << format_hex(Sec->sh_offset, 8) + << " Link: " << Sec->sh_link << " (" << SymTabName << ")\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned Entries = Sec->sh_size / sizeof(Elf_Versym); + printGNUVersionSectionProlog(OS, "Version symbols", Entries, Obj, Sec, + this->FileName); + + const uint8_t *VersymBuf = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const ELFDumper<ELFT> *Dumper = this->dumper(); + StringRef StrTable = Dumper->getDynamicStringTable(); + + // readelf prints 4 entries per line. + for (uint64_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) { + OS << " " << format_hex_no_prefix(VersymRow, 3) << ":"; + + for (uint64_t VersymIndex = 0; + (VersymIndex < 4) && (VersymIndex + VersymRow) < Entries; + ++VersymIndex) { + const Elf_Versym *Versym = + reinterpret_cast<const Elf_Versym *>(VersymBuf); + switch (Versym->vs_index) { + case 0: + OS << " 0 (*local*) "; + break; + case 1: + OS << " 1 (*global*) "; + break; + default: + OS << format("%4x%c", Versym->vs_index & VERSYM_VERSION, + Versym->vs_index & VERSYM_HIDDEN ? 'h' : ' '); + + bool IsDefault = true; + std::string VersionName = Dumper->getSymbolVersionByIndex( + StrTable, Versym->vs_index, IsDefault); + + if (!VersionName.empty()) + VersionName = "(" + VersionName + ")"; + else + VersionName = "(*invalid*)"; + OS << left_justify(VersionName, 13); + } + VersymBuf += sizeof(Elf_Versym); + } + OS << '\n'; + } + OS << '\n'; +} + +static std::string versionFlagToString(unsigned Flags) { + if (Flags == 0) + return "none"; + + std::string Ret; + auto AddFlag = [&Ret, &Flags](unsigned Flag, StringRef Name) { + if (!(Flags & Flag)) + return; + if (!Ret.empty()) + Ret += " | "; + Ret += Name; + Flags &= ~Flag; + }; + + AddFlag(VER_FLG_BASE, "BASE"); + AddFlag(VER_FLG_WEAK, "WEAK"); + AddFlag(VER_FLG_INFO, "INFO"); + AddFlag(~0, "<unknown>"); + return Ret; +} + +template <class ELFT> +void GNUStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned VerDefsNum = Sec->sh_info; + printGNUVersionSectionProlog(OS, "Version definition", VerDefsNum, Obj, Sec, + this->FileName); + + const Elf_Shdr *StrTabSec = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + StringRef StringTable( + reinterpret_cast<const char *>(Obj->base() + StrTabSec->sh_offset), + (size_t)StrTabSec->sh_size); + + const uint8_t *VerdefBuf = + unwrapOrError(this->FileName, Obj->getSectionContents(Sec)).data(); + const uint8_t *Begin = VerdefBuf; + + while (VerDefsNum--) { + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf); + OS << format(" 0x%04x: Rev: %u Flags: %s Index: %u Cnt: %u", + VerdefBuf - Begin, (unsigned)Verdef->vd_version, + versionFlagToString(Verdef->vd_flags).c_str(), + (unsigned)Verdef->vd_ndx, (unsigned)Verdef->vd_cnt); + + const uint8_t *VerdauxBuf = VerdefBuf + Verdef->vd_aux; + const Elf_Verdaux *Verdaux = + reinterpret_cast<const Elf_Verdaux *>(VerdauxBuf); + OS << format(" Name: %s\n", + StringTable.drop_front(Verdaux->vda_name).data()); + + for (unsigned I = 1; I < Verdef->vd_cnt; ++I) { + VerdauxBuf += Verdaux->vda_next; + Verdaux = reinterpret_cast<const Elf_Verdaux *>(VerdauxBuf); + OS << format(" 0x%04x: Parent %u: %s\n", VerdauxBuf - Begin, I, + StringTable.drop_front(Verdaux->vda_name).data()); + } + + VerdefBuf += Verdef->vd_next; + } + OS << '\n'; +} + +template <class ELFT> +void GNUStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned VerneedNum = Sec->sh_info; + printGNUVersionSectionProlog(OS, "Version needs", VerneedNum, Obj, Sec, + this->FileName); + + ArrayRef<uint8_t> SecData = + unwrapOrError(this->FileName, Obj->getSectionContents(Sec)); + + const Elf_Shdr *StrTabSec = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + StringRef StringTable = { + reinterpret_cast<const char *>(Obj->base() + StrTabSec->sh_offset), + (size_t)StrTabSec->sh_size}; + + const uint8_t *VerneedBuf = SecData.data(); + for (unsigned I = 0; I < VerneedNum; ++I) { + const Elf_Verneed *Verneed = + reinterpret_cast<const Elf_Verneed *>(VerneedBuf); + + OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", + reinterpret_cast<const uint8_t *>(Verneed) - SecData.begin(), + (unsigned)Verneed->vn_version, + StringTable.drop_front(Verneed->vn_file).data(), + (unsigned)Verneed->vn_cnt); + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(VernauxBuf); + + OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", + reinterpret_cast<const uint8_t *>(Vernaux) - SecData.begin(), + StringTable.drop_front(Vernaux->vna_name).data(), + versionFlagToString(Vernaux->vna_flags).c_str(), + (unsigned)Vernaux->vna_other); + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } + OS << '\n'; +} + +// Hash histogram shows statistics of how efficient the hash was for the +// dynamic symbol table. The table shows number of hash buckets for different +// lengths of chains as absolute number and percentage of the total buckets. +// Additionally cumulative coverage of symbols for each set of buckets. +template <class ELFT> +void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { + // Print histogram for .hash section + if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) { + size_t NBucket = HashTable->nbucket; + size_t NChain = HashTable->nchain; + ArrayRef<Elf_Word> Buckets = HashTable->buckets(); + ArrayRef<Elf_Word> Chains = HashTable->chains(); + size_t TotalSyms = 0; + // If hash table is correct, we have at least chains with 0 length + size_t MaxChain = 1; + size_t CumulativeNonZero = 0; + + if (NChain == 0 || NBucket == 0) + return; + + std::vector<size_t> ChainLen(NBucket, 0); + // Go over all buckets and and note chain lengths of each bucket (total + // unique chain lengths). + for (size_t B = 0; B < NBucket; B++) { + std::vector<bool> Visited(NChain); + for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) { + if (C == ELF::STN_UNDEF) + break; + if (Visited[C]) { + reportWarning( + createError(".hash section is invalid: bucket " + Twine(C) + + ": a cycle was detected in the linked chain"), + this->FileName); + break; + } + Visited[C] = true; + if (MaxChain <= ++ChainLen[B]) + MaxChain++; + } + TotalSyms += ChainLen[B]; + } + + if (!TotalSyms) + return; + + std::vector<size_t> Count(MaxChain, 0) ; + // Count how long is the chain for each bucket + for (size_t B = 0; B < NBucket; B++) + ++Count[ChainLen[B]]; + // Print Number of buckets with each chain lengths and their cumulative + // coverage of the symbols + OS << "Histogram for bucket list length (total of " << NBucket + << " buckets)\n" + << " Length Number % of total Coverage\n"; + for (size_t I = 0; I < MaxChain; I++) { + CumulativeNonZero += Count[I] * I; + OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I], + (Count[I] * 100.0) / NBucket, + (CumulativeNonZero * 100.0) / TotalSyms); + } + } + + // Print histogram for .gnu.hash section + if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) { + size_t NBucket = GnuHashTable->nbuckets; + ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets(); + unsigned NumSyms = this->dumper()->dynamic_symbols().size(); + if (!NumSyms) + return; + ArrayRef<Elf_Word> Chains = GnuHashTable->values(NumSyms); + size_t Symndx = GnuHashTable->symndx; + size_t TotalSyms = 0; + size_t MaxChain = 1; + size_t CumulativeNonZero = 0; + + if (Chains.empty() || NBucket == 0) + return; + + std::vector<size_t> ChainLen(NBucket, 0); + + for (size_t B = 0; B < NBucket; B++) { + if (!Buckets[B]) + continue; + size_t Len = 1; + for (size_t C = Buckets[B] - Symndx; + C < Chains.size() && (Chains[C] & 1) == 0; C++) + if (MaxChain < ++Len) + MaxChain++; + ChainLen[B] = Len; + TotalSyms += Len; + } + MaxChain++; + + if (!TotalSyms) + return; + + std::vector<size_t> Count(MaxChain, 0) ; + for (size_t B = 0; B < NBucket; B++) + ++Count[ChainLen[B]]; + // Print Number of buckets with each chain lengths and their cumulative + // coverage of the symbols + OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket + << " buckets)\n" + << " Length Number % of total Coverage\n"; + for (size_t I = 0; I <MaxChain; I++) { + CumulativeNonZero += Count[I] * I; + OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I], + (Count[I] * 100.0) / NBucket, + (CumulativeNonZero * 100.0) / TotalSyms); + } + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { + OS << "GNUStyle::printCGProfile not implemented\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { + reportError(createError("--addrsig: not implemented"), this->FileName); +} + +static StringRef getGenericNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_VERSION, "NT_VERSION (version)"}, + {ELF::NT_ARCH, "NT_ARCH (architecture)"}, + {ELF::NT_GNU_BUILD_ATTRIBUTE_OPEN, "OPEN"}, + {ELF::NT_GNU_BUILD_ATTRIBUTE_FUNC, "func"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return Note.Name; + + return ""; +} + +static StringRef getCoreNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_PRSTATUS, "NT_PRSTATUS (prstatus structure)"}, + {ELF::NT_FPREGSET, "NT_FPREGSET (floating point registers)"}, + {ELF::NT_PRPSINFO, "NT_PRPSINFO (prpsinfo structure)"}, + {ELF::NT_TASKSTRUCT, "NT_TASKSTRUCT (task structure)"}, + {ELF::NT_AUXV, "NT_AUXV (auxiliary vector)"}, + {ELF::NT_PSTATUS, "NT_PSTATUS (pstatus structure)"}, + {ELF::NT_FPREGS, "NT_FPREGS (floating point registers)"}, + {ELF::NT_PSINFO, "NT_PSINFO (psinfo structure)"}, + {ELF::NT_LWPSTATUS, "NT_LWPSTATUS (lwpstatus_t structure)"}, + {ELF::NT_LWPSINFO, "NT_LWPSINFO (lwpsinfo_t structure)"}, + {ELF::NT_WIN32PSTATUS, "NT_WIN32PSTATUS (win32_pstatus structure)"}, + + {ELF::NT_PPC_VMX, "NT_PPC_VMX (ppc Altivec registers)"}, + {ELF::NT_PPC_VSX, "NT_PPC_VSX (ppc VSX registers)"}, + {ELF::NT_PPC_TAR, "NT_PPC_TAR (ppc TAR register)"}, + {ELF::NT_PPC_PPR, "NT_PPC_PPR (ppc PPR register)"}, + {ELF::NT_PPC_DSCR, "NT_PPC_DSCR (ppc DSCR register)"}, + {ELF::NT_PPC_EBB, "NT_PPC_EBB (ppc EBB registers)"}, + {ELF::NT_PPC_PMU, "NT_PPC_PMU (ppc PMU registers)"}, + {ELF::NT_PPC_TM_CGPR, "NT_PPC_TM_CGPR (ppc checkpointed GPR registers)"}, + {ELF::NT_PPC_TM_CFPR, + "NT_PPC_TM_CFPR (ppc checkpointed floating point registers)"}, + {ELF::NT_PPC_TM_CVMX, + "NT_PPC_TM_CVMX (ppc checkpointed Altivec registers)"}, + {ELF::NT_PPC_TM_CVSX, "NT_PPC_TM_CVSX (ppc checkpointed VSX registers)"}, + {ELF::NT_PPC_TM_SPR, "NT_PPC_TM_SPR (ppc TM special purpose registers)"}, + {ELF::NT_PPC_TM_CTAR, "NT_PPC_TM_CTAR (ppc checkpointed TAR register)"}, + {ELF::NT_PPC_TM_CPPR, "NT_PPC_TM_CPPR (ppc checkpointed PPR register)"}, + {ELF::NT_PPC_TM_CDSCR, + "NT_PPC_TM_CDSCR (ppc checkpointed DSCR register)"}, + + {ELF::NT_386_TLS, "NT_386_TLS (x86 TLS information)"}, + {ELF::NT_386_IOPERM, "NT_386_IOPERM (x86 I/O permissions)"}, + {ELF::NT_X86_XSTATE, "NT_X86_XSTATE (x86 XSAVE extended state)"}, + + {ELF::NT_S390_HIGH_GPRS, + "NT_S390_HIGH_GPRS (s390 upper register halves)"}, + {ELF::NT_S390_TIMER, "NT_S390_TIMER (s390 timer register)"}, + {ELF::NT_S390_TODCMP, "NT_S390_TODCMP (s390 TOD comparator register)"}, + {ELF::NT_S390_TODPREG, + "NT_S390_TODPREG (s390 TOD programmable register)"}, + {ELF::NT_S390_CTRS, "NT_S390_CTRS (s390 control registers)"}, + {ELF::NT_S390_PREFIX, "NT_S390_PREFIX (s390 prefix register)"}, + {ELF::NT_S390_LAST_BREAK, + "NT_S390_LAST_BREAK (s390 last breaking event address)"}, + {ELF::NT_S390_SYSTEM_CALL, + "NT_S390_SYSTEM_CALL (s390 system call restart data)"}, + {ELF::NT_S390_TDB, "NT_S390_TDB (s390 transaction diagnostic block)"}, + {ELF::NT_S390_VXRS_LOW, + "NT_S390_VXRS_LOW (s390 vector registers 0-15 upper half)"}, + {ELF::NT_S390_VXRS_HIGH, + "NT_S390_VXRS_HIGH (s390 vector registers 16-31)"}, + {ELF::NT_S390_GS_CB, "NT_S390_GS_CB (s390 guarded-storage registers)"}, + {ELF::NT_S390_GS_BC, + "NT_S390_GS_BC (s390 guarded-storage broadcast control)"}, + + {ELF::NT_ARM_VFP, "NT_ARM_VFP (arm VFP registers)"}, + {ELF::NT_ARM_TLS, "NT_ARM_TLS (AArch TLS registers)"}, + {ELF::NT_ARM_HW_BREAK, + "NT_ARM_HW_BREAK (AArch hardware breakpoint registers)"}, + {ELF::NT_ARM_HW_WATCH, + "NT_ARM_HW_WATCH (AArch hardware watchpoint registers)"}, + + {ELF::NT_FILE, "NT_FILE (mapped files)"}, + {ELF::NT_PRXFPREG, "NT_PRXFPREG (user_xfpregs structure)"}, + {ELF::NT_SIGINFO, "NT_SIGINFO (siginfo_t data)"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return Note.Name; + + return ""; +} + +static std::string getGNUNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_GNU_ABI_TAG, "NT_GNU_ABI_TAG (ABI version tag)"}, + {ELF::NT_GNU_HWCAP, "NT_GNU_HWCAP (DSO-supplied software HWCAP info)"}, + {ELF::NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID (unique build ID bitstring)"}, + {ELF::NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION (gold version)"}, + {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return std::string(Note.Name); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return OS.str(); +} + +static std::string getFreeBSDNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"}, + {ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"}, + {ELF::NT_FREEBSD_PROCSTAT_FILES, "NT_PROCSTAT_FILES (files data)"}, + {ELF::NT_FREEBSD_PROCSTAT_VMMAP, "NT_PROCSTAT_VMMAP (vmmap data)"}, + {ELF::NT_FREEBSD_PROCSTAT_GROUPS, "NT_PROCSTAT_GROUPS (groups data)"}, + {ELF::NT_FREEBSD_PROCSTAT_UMASK, "NT_PROCSTAT_UMASK (umask data)"}, + {ELF::NT_FREEBSD_PROCSTAT_RLIMIT, "NT_PROCSTAT_RLIMIT (rlimit data)"}, + {ELF::NT_FREEBSD_PROCSTAT_OSREL, "NT_PROCSTAT_OSREL (osreldate data)"}, + {ELF::NT_FREEBSD_PROCSTAT_PSSTRINGS, + "NT_PROCSTAT_PSSTRINGS (ps_strings data)"}, + {ELF::NT_FREEBSD_PROCSTAT_AUXV, "NT_PROCSTAT_AUXV (auxv data)"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return std::string(Note.Name); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return OS.str(); +} + +static std::string getAMDNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = {{ELF::NT_AMD_AMDGPU_HSA_METADATA, + "NT_AMD_AMDGPU_HSA_METADATA (HSA Metadata)"}, + {ELF::NT_AMD_AMDGPU_ISA, "NT_AMD_AMDGPU_ISA (ISA Version)"}, + {ELF::NT_AMD_AMDGPU_PAL_METADATA, + "NT_AMD_AMDGPU_PAL_METADATA (PAL Metadata)"}}; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return std::string(Note.Name); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return OS.str(); +} + +static std::string getAMDGPUNoteTypeName(const uint32_t NT) { + if (NT == ELF::NT_AMDGPU_METADATA) + return std::string("NT_AMDGPU_METADATA (AMDGPU Metadata)"); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return OS.str(); +} + +template <typename ELFT> +static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, + ArrayRef<uint8_t> Data) { + std::string str; + raw_string_ostream OS(str); + uint32_t PrData; + auto DumpBit = [&](uint32_t Flag, StringRef Name) { + if (PrData & Flag) { + PrData &= ~Flag; + OS << Name; + if (PrData) + OS << ", "; + } + }; + + switch (Type) { + default: + OS << format("<application-specific type 0x%x>", Type); + return OS.str(); + case GNU_PROPERTY_STACK_SIZE: { + OS << "stack size: "; + if (DataSize == sizeof(typename ELFT::uint)) + OS << formatv("{0:x}", + (uint64_t)(*(const typename ELFT::Addr *)Data.data())); + else + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); + } + case GNU_PROPERTY_NO_COPY_ON_PROTECTED: + OS << "no copy on protected"; + if (DataSize) + OS << format(" <corrupt length: 0x%x>", DataSize); + return OS.str(); + case GNU_PROPERTY_AARCH64_FEATURE_1_AND: + case GNU_PROPERTY_X86_FEATURE_1_AND: + OS << ((Type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) ? "aarch64 feature: " + : "x86 feature: "); + if (DataSize != 4) { + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); + } + PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); + if (PrData == 0) { + OS << "<None>"; + return OS.str(); + } + if (Type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { + DumpBit(GNU_PROPERTY_AARCH64_FEATURE_1_BTI, "BTI"); + DumpBit(GNU_PROPERTY_AARCH64_FEATURE_1_PAC, "PAC"); + } else { + DumpBit(GNU_PROPERTY_X86_FEATURE_1_IBT, "IBT"); + DumpBit(GNU_PROPERTY_X86_FEATURE_1_SHSTK, "SHSTK"); + } + if (PrData) + OS << format("<unknown flags: 0x%x>", PrData); + return OS.str(); + case GNU_PROPERTY_X86_ISA_1_NEEDED: + case GNU_PROPERTY_X86_ISA_1_USED: + OS << "x86 ISA " + << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: "); + if (DataSize != 4) { + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); + } + PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); + if (PrData == 0) { + OS << "<None>"; + return OS.str(); + } + DumpBit(GNU_PROPERTY_X86_ISA_1_CMOV, "CMOV"); + DumpBit(GNU_PROPERTY_X86_ISA_1_SSE, "SSE"); + DumpBit(GNU_PROPERTY_X86_ISA_1_SSE2, "SSE2"); + DumpBit(GNU_PROPERTY_X86_ISA_1_SSE3, "SSE3"); + DumpBit(GNU_PROPERTY_X86_ISA_1_SSSE3, "SSSE3"); + DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_1, "SSE4_1"); + DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_2, "SSE4_2"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX, "AVX"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX2, "AVX2"); + DumpBit(GNU_PROPERTY_X86_ISA_1_FMA, "FMA"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512F, "AVX512F"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512CD, "AVX512CD"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512ER, "AVX512ER"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512PF, "AVX512PF"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512VL, "AVX512VL"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512DQ, "AVX512DQ"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512BW, "AVX512BW"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4FMAPS, "AVX512_4FMAPS"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4VNNIW, "AVX512_4VNNIW"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_BITALG, "AVX512_BITALG"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_IFMA, "AVX512_IFMA"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI, "AVX512_VBMI"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI2, "AVX512_VBMI2"); + DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VNNI, "AVX512_VNNI"); + if (PrData) + OS << format("<unknown flags: 0x%x>", PrData); + return OS.str(); + break; + case GNU_PROPERTY_X86_FEATURE_2_NEEDED: + case GNU_PROPERTY_X86_FEATURE_2_USED: + OS << "x86 feature " + << (Type == GNU_PROPERTY_X86_FEATURE_2_NEEDED ? "needed: " : "used: "); + if (DataSize != 4) { + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); + } + PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); + if (PrData == 0) { + OS << "<None>"; + return OS.str(); + } + DumpBit(GNU_PROPERTY_X86_FEATURE_2_X86, "x86"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_X87, "x87"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_MMX, "MMX"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XMM, "XMM"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_YMM, "YMM"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_ZMM, "ZMM"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_FXSR, "FXSR"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVE, "XSAVE"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVEOPT, "XSAVEOPT"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVEC, "XSAVEC"); + if (PrData) + OS << format("<unknown flags: 0x%x>", PrData); + return OS.str(); + } +} + +template <typename ELFT> +static SmallVector<std::string, 4> getGNUPropertyList(ArrayRef<uint8_t> Arr) { + using Elf_Word = typename ELFT::Word; + + SmallVector<std::string, 4> Properties; + while (Arr.size() >= 8) { + uint32_t Type = *reinterpret_cast<const Elf_Word *>(Arr.data()); + uint32_t DataSize = *reinterpret_cast<const Elf_Word *>(Arr.data() + 4); + Arr = Arr.drop_front(8); + + // Take padding size into account if present. + uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint)); + std::string str; + raw_string_ostream OS(str); + if (Arr.size() < PaddedSize) { + OS << format("<corrupt type (0x%x) datasz: 0x%x>", Type, DataSize); + Properties.push_back(OS.str()); + break; + } + Properties.push_back( + getGNUProperty<ELFT>(Type, DataSize, Arr.take_front(PaddedSize))); + Arr = Arr.drop_front(PaddedSize); + } + + if (!Arr.empty()) + Properties.push_back("<corrupted GNU_PROPERTY_TYPE_0>"); + + return Properties; +} + +struct GNUAbiTag { + std::string OSName; + std::string ABI; + bool IsValid; +}; + +template <typename ELFT> static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) { + typedef typename ELFT::Word Elf_Word; + + ArrayRef<Elf_Word> Words(reinterpret_cast<const Elf_Word *>(Desc.begin()), + reinterpret_cast<const Elf_Word *>(Desc.end())); + + if (Words.size() < 4) + return {"", "", /*IsValid=*/false}; + + static const char *OSNames[] = { + "Linux", "Hurd", "Solaris", "FreeBSD", "NetBSD", "Syllable", "NaCl", + }; + StringRef OSName = "Unknown"; + if (Words[0] < array_lengthof(OSNames)) + OSName = OSNames[Words[0]]; + uint32_t Major = Words[1], Minor = Words[2], Patch = Words[3]; + std::string str; + raw_string_ostream ABI(str); + ABI << Major << "." << Minor << "." << Patch; + return {OSName, ABI.str(), /*IsValid=*/true}; +} + +static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) { + std::string str; + raw_string_ostream OS(str); + for (const auto &B : Desc) + OS << format_hex_no_prefix(B, 2); + return OS.str(); +} + +static StringRef getGNUGoldVersion(ArrayRef<uint8_t> Desc) { + return StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); +} + +template <typename ELFT> +static void printGNUNote(raw_ostream &OS, uint32_t NoteType, + ArrayRef<uint8_t> Desc) { + switch (NoteType) { + default: + return; + case ELF::NT_GNU_ABI_TAG: { + const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); + if (!AbiTag.IsValid) + OS << " <corrupt GNU_ABI_TAG>"; + else + OS << " OS: " << AbiTag.OSName << ", ABI: " << AbiTag.ABI; + break; + } + case ELF::NT_GNU_BUILD_ID: { + OS << " Build ID: " << getGNUBuildId(Desc); + break; + } + case ELF::NT_GNU_GOLD_VERSION: + OS << " Version: " << getGNUGoldVersion(Desc); + break; + case ELF::NT_GNU_PROPERTY_TYPE_0: + OS << " Properties:"; + for (const auto &Property : getGNUPropertyList<ELFT>(Desc)) + OS << " " << Property << "\n"; + break; + } + OS << '\n'; +} + +struct AMDNote { + std::string Type; + std::string Value; +}; + +template <typename ELFT> +static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { + switch (NoteType) { + default: + return {"", ""}; + case ELF::NT_AMD_AMDGPU_HSA_METADATA: + return { + "HSA Metadata", + std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; + case ELF::NT_AMD_AMDGPU_ISA: + return { + "ISA Version", + std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; + } +} + +struct AMDGPUNote { + std::string Type; + std::string Value; +}; + +template <typename ELFT> +static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { + switch (NoteType) { + default: + return {"", ""}; + case ELF::NT_AMDGPU_METADATA: { + auto MsgPackString = + StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); + msgpack::Document MsgPackDoc; + if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false)) + return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + + AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true); + if (!Verifier.verify(MsgPackDoc.getRoot())) + return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + + std::string HSAMetadataString; + raw_string_ostream StrOS(HSAMetadataString); + MsgPackDoc.toYAML(StrOS); + + return {"AMDGPU Metadata", StrOS.str()}; + } + } +} + +struct CoreFileMapping { + uint64_t Start, End, Offset; + StringRef Filename; +}; + +struct CoreNote { + uint64_t PageSize; + std::vector<CoreFileMapping> Mappings; +}; + +static Expected<CoreNote> readCoreNote(DataExtractor Desc) { + // Expected format of the NT_FILE note description: + // 1. # of file mappings (call it N) + // 2. Page size + // 3. N (start, end, offset) triples + // 4. N packed filenames (null delimited) + // Each field is an Elf_Addr, except for filenames which are char* strings. + + CoreNote Ret; + const int Bytes = Desc.getAddressSize(); + + if (!Desc.isValidOffsetForAddress(2)) + return createStringError(object_error::parse_failed, + "malformed note: header too short"); + if (Desc.getData().back() != 0) + return createStringError(object_error::parse_failed, + "malformed note: not NUL terminated"); + + uint64_t DescOffset = 0; + uint64_t FileCount = Desc.getAddress(&DescOffset); + Ret.PageSize = Desc.getAddress(&DescOffset); + + if (!Desc.isValidOffsetForAddress(3 * FileCount * Bytes)) + return createStringError(object_error::parse_failed, + "malformed note: too short for number of files"); + + uint64_t FilenamesOffset = 0; + DataExtractor Filenames( + Desc.getData().drop_front(DescOffset + 3 * FileCount * Bytes), + Desc.isLittleEndian(), Desc.getAddressSize()); + + Ret.Mappings.resize(FileCount); + for (CoreFileMapping &Mapping : Ret.Mappings) { + if (!Filenames.isValidOffsetForDataOfSize(FilenamesOffset, 1)) + return createStringError(object_error::parse_failed, + "malformed note: too few filenames"); + Mapping.Start = Desc.getAddress(&DescOffset); + Mapping.End = Desc.getAddress(&DescOffset); + Mapping.Offset = Desc.getAddress(&DescOffset); + Mapping.Filename = Filenames.getCStrRef(&FilenamesOffset); + } + + return Ret; +} + +template <typename ELFT> +static void printCoreNote(raw_ostream &OS, const CoreNote &Note) { + // Length of "0x<address>" string. + const int FieldWidth = ELFT::Is64Bits ? 18 : 10; + + OS << " Page size: " << format_decimal(Note.PageSize, 0) << '\n'; + OS << " " << right_justify("Start", FieldWidth) << " " + << right_justify("End", FieldWidth) << " " + << right_justify("Page Offset", FieldWidth) << '\n'; + for (const CoreFileMapping &Mapping : Note.Mappings) { + OS << " " << format_hex(Mapping.Start, FieldWidth) << " " + << format_hex(Mapping.End, FieldWidth) << " " + << format_hex(Mapping.Offset, FieldWidth) << "\n " + << Mapping.Filename << '\n'; + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { + auto PrintHeader = [&](const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { + OS << "Displaying notes found at file offset " << format_hex(Offset, 10) + << " with length " << format_hex(Size, 10) << ":\n" + << " Owner Data size \tDescription\n"; + }; + + auto ProcessNote = [&](const Elf_Note &Note) { + StringRef Name = Note.getName(); + ArrayRef<uint8_t> Descriptor = Note.getDesc(); + Elf_Word Type = Note.getType(); + + // Print the note owner/type. + OS << " " << left_justify(Name, 20) << ' ' + << format_hex(Descriptor.size(), 10) << '\t'; + if (Name == "GNU") { + OS << getGNUNoteTypeName(Type) << '\n'; + } else if (Name == "FreeBSD") { + OS << getFreeBSDNoteTypeName(Type) << '\n'; + } else if (Name == "AMD") { + OS << getAMDNoteTypeName(Type) << '\n'; + } else if (Name == "AMDGPU") { + OS << getAMDGPUNoteTypeName(Type) << '\n'; + } else { + StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE + ? getCoreNoteTypeName(Type) + : getGenericNoteTypeName(Type); + if (!NoteType.empty()) + OS << NoteType << '\n'; + else + OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n"; + } + + // Print the description, or fallback to printing raw bytes for unknown + // owners. + if (Name == "GNU") { + printGNUNote<ELFT>(OS, Type, Descriptor); + } else if (Name == "AMD") { + const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + OS << " " << N.Type << ":\n " << N.Value << '\n'; + } else if (Name == "AMDGPU") { + const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + OS << " " << N.Type << ":\n " << N.Value << '\n'; + } else if (Name == "CORE") { + if (Type == ELF::NT_FILE) { + DataExtractor DescExtractor(Descriptor, + ELFT::TargetEndianness == support::little, + sizeof(Elf_Addr)); + Expected<CoreNote> Note = readCoreNote(DescExtractor); + if (Note) + printCoreNote<ELFT>(OS, *Note); + else + reportWarning(Note.takeError(), this->FileName); + } + } else if (!Descriptor.empty()) { + OS << " description data:"; + for (uint8_t B : Descriptor) + OS << " " << format("%02x", B); + OS << '\n'; + } + }; + + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + if (Obj->getHeader()->e_type != ELF::ET_CORE && !Sections.empty()) { + for (const auto &S : Sections) { + if (S.sh_type != SHT_NOTE) + continue; + PrintHeader(S.sh_offset, S.sh_size); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(S, Err)) + ProcessNote(Note); + if (Err) + reportError(std::move(Err), this->FileName); + } + } else { + for (const auto &P : + unwrapOrError(this->FileName, Obj->program_headers())) { + if (P.p_type != PT_NOTE) + continue; + PrintHeader(P.p_offset, P.p_filesz); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(P, Err)) + ProcessNote(Note); + if (Err) + reportError(std::move(Err), this->FileName); + } + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { + OS << "printELFLinkerOptions not implemented!\n"; +} + +// Used for printing section names in places where possible errors can be +// ignored. +static StringRef getSectionName(const SectionRef &Sec) { + Expected<StringRef> NameOrErr = Sec.getName(); + if (NameOrErr) + return *NameOrErr; + consumeError(NameOrErr.takeError()); + return "<?>"; +} + +// Used for printing symbol names in places where possible errors can be +// ignored. +static std::string getSymbolName(const ELFSymbolRef &Sym) { + Expected<StringRef> NameOrErr = Sym.getName(); + if (NameOrErr) + return maybeDemangle(*NameOrErr); + consumeError(NameOrErr.takeError()); + return "<?>"; +} + +template <class ELFT> +void DumpStyle<ELFT>::printFunctionStackSize( + const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, SectionRef FunctionSec, + const StringRef SectionName, DataExtractor Data, uint64_t *Offset) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + SymbolRef FuncSym; + for (const ELFSymbolRef &Symbol : Obj->symbols()) { + Expected<uint64_t> SymAddrOrErr = Symbol.getAddress(); + if (!SymAddrOrErr) { + consumeError(SymAddrOrErr.takeError()); + continue; + } + if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) { + // Check if the symbol is in the right section. + if (FunctionSec.containsSymbol(Symbol)) { + FuncSym = Symbol; + break; + } + } + } + + std::string FuncName = "?"; + // A valid SymbolRef has a non-null object file pointer. + if (FuncSym.BasicSymbolRef::getObject()) + FuncName = getSymbolName(FuncSym); + else + reportWarning( + createError("could not identify function symbol for stack size entry"), + Obj->getFileName()); + + // Extract the size. The expectation is that Offset is pointing to the right + // place, i.e. past the function address. + uint64_t PrevOffset = *Offset; + uint64_t StackSize = Data.getULEB128(Offset); + // getULEB128() does not advance Offset if it is not able to extract a valid + // integer. + if (*Offset == PrevOffset) + reportError( + createStringError(object_error::parse_failed, + "could not extract a valid stack size in section %s", + SectionName.data()), + Obj->getFileName()); + + printStackSizeEntry(StackSize, FuncName); +} + +template <class ELFT> +void GNUStyle<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + OS.PadToColumn(2); + OS << format_decimal(Size, 11); + OS.PadToColumn(18); + OS << FuncName << "\n"; +} + +template <class ELFT> +void DumpStyle<ELFT>::printStackSize(const ELFObjectFile<ELFT> *Obj, + RelocationRef Reloc, + SectionRef FunctionSec, + const StringRef &StackSizeSectionName, + const RelocationResolver &Resolver, + DataExtractor Data) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + object::symbol_iterator RelocSym = Reloc.getSymbol(); + uint64_t RelocSymValue = 0; + StringRef FileStr = Obj->getFileName(); + if (RelocSym != Obj->symbol_end()) { + // Ensure that the relocation symbol is in the function section, i.e. the + // section where the functions whose stack sizes we are reporting are + // located. + auto SectionOrErr = RelocSym->getSection(); + if (!SectionOrErr) { + reportWarning( + createError("cannot identify the section for relocation symbol '" + + getSymbolName(*RelocSym) + "'"), + FileStr); + consumeError(SectionOrErr.takeError()); + } else if (*SectionOrErr != FunctionSec) { + reportWarning(createError("relocation symbol '" + + getSymbolName(*RelocSym) + + "' is not in the expected section"), + FileStr); + // Pretend that the symbol is in the correct section and report its + // stack size anyway. + FunctionSec = **SectionOrErr; + } + + Expected<uint64_t> RelocSymValueOrErr = RelocSym->getValue(); + if (RelocSymValueOrErr) + RelocSymValue = *RelocSymValueOrErr; + else + consumeError(RelocSymValueOrErr.takeError()); + } + + uint64_t Offset = Reloc.getOffset(); + if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) + reportError( + createStringError(object_error::parse_failed, + "found invalid relocation offset into section %s " + "while trying to extract a stack size entry", + StackSizeSectionName.data()), + FileStr); + + uint64_t Addend = Data.getAddress(&Offset); + uint64_t SymValue = Resolver(Reloc, RelocSymValue, Addend); + this->printFunctionStackSize(Obj, SymValue, FunctionSec, StackSizeSectionName, + Data, &Offset); +} + +template <class ELFT> +void DumpStyle<ELFT>::printNonRelocatableStackSizes( + const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + const ELFFile<ELFT> *EF = Obj->getELFFile(); + StringRef FileStr = Obj->getFileName(); + for (const SectionRef &Sec : Obj->sections()) { + StringRef SectionName = getSectionName(Sec); + if (SectionName != ".stack_sizes") + continue; + PrintHeader(); + const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl()); + ArrayRef<uint8_t> Contents = + unwrapOrError(this->FileName, EF->getSectionContents(ElfSec)); + DataExtractor Data(Contents, Obj->isLittleEndian(), sizeof(Elf_Addr)); + // A .stack_sizes section header's sh_link field is supposed to point + // to the section that contains the functions whose stack sizes are + // described in it. + const Elf_Shdr *FunctionELFSec = + unwrapOrError(this->FileName, EF->getSection(ElfSec->sh_link)); + uint64_t Offset = 0; + while (Offset < Contents.size()) { + // The function address is followed by a ULEB representing the stack + // size. Check for an extra byte before we try to process the entry. + if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) { + reportError( + createStringError( + object_error::parse_failed, + "section %s ended while trying to extract a stack size entry", + SectionName.data()), + FileStr); + } + uint64_t SymValue = Data.getAddress(&Offset); + printFunctionStackSize(Obj, SymValue, Obj->toSectionRef(FunctionELFSec), + SectionName, Data, &Offset); + } + } +} + +template <class ELFT> +void DumpStyle<ELFT>::printRelocatableStackSizes( + const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader) { + const ELFFile<ELFT> *EF = Obj->getELFFile(); + + // Build a map between stack size sections and their corresponding relocation + // sections. + llvm::MapVector<SectionRef, SectionRef> StackSizeRelocMap; + const SectionRef NullSection{}; + + for (const SectionRef &Sec : Obj->sections()) { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Sec.getName()) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + // A stack size section that we haven't encountered yet is mapped to the + // null section until we find its corresponding relocation section. + if (SectionName == ".stack_sizes") + if (StackSizeRelocMap.count(Sec) == 0) { + StackSizeRelocMap[Sec] = NullSection; + continue; + } + + // Check relocation sections if they are relocating contents of a + // stack sizes section. + const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl()); + uint32_t SectionType = ElfSec->sh_type; + if (SectionType != ELF::SHT_RELA && SectionType != ELF::SHT_REL) + continue; + + Expected<section_iterator> RelSecOrErr = Sec.getRelocatedSection(); + if (!RelSecOrErr) + reportError(createStringError(object_error::parse_failed, + "%s: failed to get a relocated section: %s", + SectionName.data(), + toString(RelSecOrErr.takeError()).c_str()), + Obj->getFileName()); + + const Elf_Shdr *ContentsSec = + Obj->getSection((*RelSecOrErr)->getRawDataRefImpl()); + Expected<StringRef> ContentsSectionNameOrErr = + EF->getSectionName(ContentsSec); + if (!ContentsSectionNameOrErr) { + consumeError(ContentsSectionNameOrErr.takeError()); + continue; + } + if (*ContentsSectionNameOrErr != ".stack_sizes") + continue; + // Insert a mapping from the stack sizes section to its relocation section. + StackSizeRelocMap[Obj->toSectionRef(ContentsSec)] = Sec; + } + + for (const auto &StackSizeMapEntry : StackSizeRelocMap) { + PrintHeader(); + const SectionRef &StackSizesSec = StackSizeMapEntry.first; + const SectionRef &RelocSec = StackSizeMapEntry.second; + + // Warn about stack size sections without a relocation section. + StringRef StackSizeSectionName = getSectionName(StackSizesSec); + if (RelocSec == NullSection) { + reportWarning(createError("section " + StackSizeSectionName + + " does not have a corresponding " + "relocation section"), + Obj->getFileName()); + continue; + } + + // A .stack_sizes section header's sh_link field is supposed to point + // to the section that contains the functions whose stack sizes are + // described in it. + const Elf_Shdr *StackSizesELFSec = + Obj->getSection(StackSizesSec.getRawDataRefImpl()); + const SectionRef FunctionSec = Obj->toSectionRef(unwrapOrError( + this->FileName, EF->getSection(StackSizesELFSec->sh_link))); + + bool (*IsSupportedFn)(uint64_t); + RelocationResolver Resolver; + std::tie(IsSupportedFn, Resolver) = getRelocationResolver(*Obj); + auto Contents = unwrapOrError(this->FileName, StackSizesSec.getContents()); + DataExtractor Data(Contents, Obj->isLittleEndian(), sizeof(Elf_Addr)); + for (const RelocationRef &Reloc : RelocSec.relocations()) { + if (!IsSupportedFn || !IsSupportedFn(Reloc.getType())) + reportError(createStringError( + object_error::parse_failed, + "unsupported relocation type in section %s: %s", + getSectionName(RelocSec).data(), + EF->getRelocationTypeName(Reloc.getType()).data()), + Obj->getFileName()); + this->printStackSize(Obj, Reloc, FunctionSec, StackSizeSectionName, + Resolver, Data); + } + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printStackSizes(const ELFObjectFile<ELFT> *Obj) { + bool HeaderHasBeenPrinted = false; + auto PrintHeader = [&]() { + if (HeaderHasBeenPrinted) + return; + OS << "\nStack Sizes:\n"; + OS.PadToColumn(9); + OS << "Size"; + OS.PadToColumn(18); + OS << "Function\n"; + HeaderHasBeenPrinted = true; + }; + + // For non-relocatable objects, look directly for sections whose name starts + // with .stack_sizes and process the contents. + if (Obj->isRelocatableObject()) + this->printRelocatableStackSizes(Obj, PrintHeader); + else + this->printNonRelocatableStackSizes(Obj, PrintHeader); +} + +template <class ELFT> +void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { + size_t Bias = ELFT::Is64Bits ? 8 : 0; + auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) { + OS.PadToColumn(2); + OS << format_hex_no_prefix(Parser.getGotAddress(E), 8 + Bias); + OS.PadToColumn(11 + Bias); + OS << format_decimal(Parser.getGotOffset(E), 6) << "(gp)"; + OS.PadToColumn(22 + Bias); + OS << format_hex_no_prefix(*E, 8 + Bias); + OS.PadToColumn(31 + 2 * Bias); + OS << Purpose << "\n"; + }; + + OS << (Parser.IsStatic ? "Static GOT:\n" : "Primary GOT:\n"); + OS << " Canonical gp value: " + << format_hex_no_prefix(Parser.getGp(), 8 + Bias) << "\n\n"; + + OS << " Reserved entries:\n"; + if (ELFT::Is64Bits) + OS << " Address Access Initial Purpose\n"; + else + OS << " Address Access Initial Purpose\n"; + PrintEntry(Parser.getGotLazyResolver(), "Lazy resolver"); + if (Parser.getGotModulePointer()) + PrintEntry(Parser.getGotModulePointer(), "Module pointer (GNU extension)"); + + if (!Parser.getLocalEntries().empty()) { + OS << "\n"; + OS << " Local entries:\n"; + if (ELFT::Is64Bits) + OS << " Address Access Initial\n"; + else + OS << " Address Access Initial\n"; + for (auto &E : Parser.getLocalEntries()) + PrintEntry(&E, ""); + } + + if (Parser.IsStatic) + return; + + if (!Parser.getGlobalEntries().empty()) { + OS << "\n"; + OS << " Global entries:\n"; + if (ELFT::Is64Bits) + OS << " Address Access Initial Sym.Val." + << " Type Ndx Name\n"; + else + OS << " Address Access Initial Sym.Val. Type Ndx Name\n"; + for (auto &E : Parser.getGlobalEntries()) { + const Elf_Sym *Sym = Parser.getGotSym(&E); + std::string SymName = this->dumper()->getFullSymbolName( + Sym, this->dumper()->getDynamicStringTable(), false); + + OS.PadToColumn(2); + OS << to_string(format_hex_no_prefix(Parser.getGotAddress(&E), 8 + Bias)); + OS.PadToColumn(11 + Bias); + OS << to_string(format_decimal(Parser.getGotOffset(&E), 6)) + "(gp)"; + OS.PadToColumn(22 + Bias); + OS << to_string(format_hex_no_prefix(E, 8 + Bias)); + OS.PadToColumn(31 + 2 * Bias); + OS << to_string(format_hex_no_prefix(Sym->st_value, 8 + Bias)); + OS.PadToColumn(40 + 3 * Bias); + OS << printEnum(Sym->getType(), makeArrayRef(ElfSymbolTypes)); + OS.PadToColumn(48 + 3 * Bias); + OS << getSymbolSectionNdx(Parser.Obj, Sym, + this->dumper()->dynamic_symbols().begin()); + OS.PadToColumn(52 + 3 * Bias); + OS << SymName << "\n"; + } + } + + if (!Parser.getOtherEntries().empty()) + OS << "\n Number of TLS and multi-GOT entries " + << Parser.getOtherEntries().size() << "\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { + size_t Bias = ELFT::Is64Bits ? 8 : 0; + auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) { + OS.PadToColumn(2); + OS << format_hex_no_prefix(Parser.getPltAddress(E), 8 + Bias); + OS.PadToColumn(11 + Bias); + OS << format_hex_no_prefix(*E, 8 + Bias); + OS.PadToColumn(20 + 2 * Bias); + OS << Purpose << "\n"; + }; + + OS << "PLT GOT:\n\n"; + + OS << " Reserved entries:\n"; + OS << " Address Initial Purpose\n"; + PrintEntry(Parser.getPltLazyResolver(), "PLT lazy resolver"); + if (Parser.getPltModulePointer()) + PrintEntry(Parser.getPltModulePointer(), "Module pointer"); + + if (!Parser.getPltEntries().empty()) { + OS << "\n"; + OS << " Entries:\n"; + OS << " Address Initial Sym.Val. Type Ndx Name\n"; + for (auto &E : Parser.getPltEntries()) { + const Elf_Sym *Sym = Parser.getPltSym(&E); + std::string SymName = this->dumper()->getFullSymbolName( + Sym, this->dumper()->getDynamicStringTable(), false); + + OS.PadToColumn(2); + OS << to_string(format_hex_no_prefix(Parser.getPltAddress(&E), 8 + Bias)); + OS.PadToColumn(11 + Bias); + OS << to_string(format_hex_no_prefix(E, 8 + Bias)); + OS.PadToColumn(20 + 2 * Bias); + OS << to_string(format_hex_no_prefix(Sym->st_value, 8 + Bias)); + OS.PadToColumn(29 + 3 * Bias); + OS << printEnum(Sym->getType(), makeArrayRef(ElfSymbolTypes)); + OS.PadToColumn(37 + 3 * Bias); + OS << getSymbolSectionNdx(Parser.Obj, Sym, + this->dumper()->dynamic_symbols().begin()); + OS.PadToColumn(41 + 3 * Bias); + OS << SymName << "\n"; + } + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printMipsABIFlags(const ELFObjectFile<ELFT> *ObjF) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *Shdr = + findSectionByName(*Obj, ObjF->getFileName(), ".MIPS.abiflags"); + if (!Shdr) + return; + + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) + reportError(createError(".MIPS.abiflags section has a wrong size"), + ObjF->getFileName()); + + auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec.data()); + + OS << "MIPS ABI Flags Version: " << Flags->version << "\n\n"; + OS << "ISA: MIPS" << int(Flags->isa_level); + if (Flags->isa_rev > 1) + OS << "r" << int(Flags->isa_rev); + OS << "\n"; + OS << "GPR size: " << getMipsRegisterSize(Flags->gpr_size) << "\n"; + OS << "CPR1 size: " << getMipsRegisterSize(Flags->cpr1_size) << "\n"; + OS << "CPR2 size: " << getMipsRegisterSize(Flags->cpr2_size) << "\n"; + OS << "FP ABI: " << printEnum(Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)) + << "\n"; + OS << "ISA Extension: " + << printEnum(Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)) << "\n"; + if (Flags->ases == 0) + OS << "ASEs: None\n"; + else + // FIXME: Print each flag on a separate line. + OS << "ASEs: " << printFlags(Flags->ases, makeArrayRef(ElfMipsASEFlags)) + << "\n"; + OS << "FLAGS 1: " << format_hex_no_prefix(Flags->flags1, 8, false) << "\n"; + OS << "FLAGS 2: " << format_hex_no_prefix(Flags->flags2, 8, false) << "\n"; + OS << "\n"; +} + +template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) { + const Elf_Ehdr *E = Obj->getHeader(); + { + DictScope D(W, "ElfHeader"); + { + DictScope D(W, "Ident"); + W.printBinary("Magic", makeArrayRef(E->e_ident).slice(ELF::EI_MAG0, 4)); + W.printEnum("Class", E->e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass)); + W.printEnum("DataEncoding", E->e_ident[ELF::EI_DATA], + makeArrayRef(ElfDataEncoding)); + W.printNumber("FileVersion", E->e_ident[ELF::EI_VERSION]); + + auto OSABI = makeArrayRef(ElfOSABI); + if (E->e_ident[ELF::EI_OSABI] >= ELF::ELFOSABI_FIRST_ARCH && + E->e_ident[ELF::EI_OSABI] <= ELF::ELFOSABI_LAST_ARCH) { + switch (E->e_machine) { + case ELF::EM_AMDGPU: + OSABI = makeArrayRef(AMDGPUElfOSABI); + break; + case ELF::EM_ARM: + OSABI = makeArrayRef(ARMElfOSABI); + break; + case ELF::EM_TI_C6000: + OSABI = makeArrayRef(C6000ElfOSABI); + break; + } + } + W.printEnum("OS/ABI", E->e_ident[ELF::EI_OSABI], OSABI); + W.printNumber("ABIVersion", E->e_ident[ELF::EI_ABIVERSION]); + W.printBinary("Unused", makeArrayRef(E->e_ident).slice(ELF::EI_PAD)); + } + + W.printEnum("Type", E->e_type, makeArrayRef(ElfObjectFileType)); + W.printEnum("Machine", E->e_machine, makeArrayRef(ElfMachineType)); + W.printNumber("Version", E->e_version); + W.printHex("Entry", E->e_entry); + W.printHex("ProgramHeaderOffset", E->e_phoff); + W.printHex("SectionHeaderOffset", E->e_shoff); + if (E->e_machine == EM_MIPS) + W.printFlags("Flags", E->e_flags, makeArrayRef(ElfHeaderMipsFlags), + unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), + unsigned(ELF::EF_MIPS_MACH)); + else if (E->e_machine == EM_AMDGPU) + W.printFlags("Flags", E->e_flags, makeArrayRef(ElfHeaderAMDGPUFlags), + unsigned(ELF::EF_AMDGPU_MACH)); + else if (E->e_machine == EM_RISCV) + W.printFlags("Flags", E->e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + else + W.printFlags("Flags", E->e_flags); + W.printNumber("HeaderSize", E->e_ehsize); + W.printNumber("ProgramHeaderEntrySize", E->e_phentsize); + W.printNumber("ProgramHeaderCount", E->e_phnum); + W.printNumber("SectionHeaderEntrySize", E->e_shentsize); + W.printString("SectionHeaderCount", + getSectionHeadersNumString(Obj, this->FileName)); + W.printString("StringTableSectionIndex", + getSectionHeaderTableIndexString(Obj, this->FileName)); + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printGroupSections(const ELFO *Obj) { + DictScope Lists(W, "Groups"); + std::vector<GroupSection> V = getGroups<ELFT>(Obj, this->FileName); + DenseMap<uint64_t, const GroupSection *> Map = mapSectionsToGroups(V); + for (const GroupSection &G : V) { + DictScope D(W, "Group"); + W.printNumber("Name", G.Name, G.ShName); + W.printNumber("Index", G.Index); + W.printNumber("Link", G.Link); + W.printNumber("Info", G.Info); + W.printHex("Type", getGroupType(G.Type), G.Type); + W.startLine() << "Signature: " << G.Signature << "\n"; + + ListScope L(W, "Section(s) in group"); + for (const GroupMember &GM : G.Members) { + const GroupSection *MainGroup = Map[GM.Index]; + if (MainGroup != &G) { + W.flush(); + errs() << "Error: " << GM.Name << " (" << GM.Index + << ") in a group " + G.Name + " (" << G.Index + << ") is already in a group " + MainGroup->Name + " (" + << MainGroup->Index << ")\n"; + errs().flush(); + continue; + } + W.startLine() << GM.Name << " (" << GM.Index << ")\n"; + } + } + + if (V.empty()) + W.startLine() << "There are no group sections in the file.\n"; +} + +template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) { + ListScope D(W, "Relocations"); + + int SectionNumber = -1; + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { + ++SectionNumber; + + if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA && + Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_REL && + Sec.sh_type != ELF::SHT_ANDROID_RELA && + Sec.sh_type != ELF::SHT_ANDROID_RELR) + continue; + + StringRef Name = unwrapOrError(this->FileName, Obj->getSectionName(&Sec)); + + W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; + W.indent(); + + printRelocations(&Sec, Obj); + + W.unindent(); + W.startLine() << "}\n"; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { + const Elf_Shdr *SymTab = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + + switch (Sec->sh_type) { + case ELF::SHT_REL: + for (const Elf_Rel &R : unwrapOrError(this->FileName, Obj->rels(Sec))) { + Elf_Rela Rela; + Rela.r_offset = R.r_offset; + Rela.r_info = R.r_info; + Rela.r_addend = 0; + printRelocation(Obj, Rela, SymTab); + } + break; + case ELF::SHT_RELA: + for (const Elf_Rela &R : unwrapOrError(this->FileName, Obj->relas(Sec))) + printRelocation(Obj, R, SymTab); + break; + case ELF::SHT_RELR: + case ELF::SHT_ANDROID_RELR: { + Elf_Relr_Range Relrs = unwrapOrError(this->FileName, Obj->relrs(Sec)); + if (opts::RawRelr) { + for (const Elf_Relr &R : Relrs) + W.startLine() << W.hex(R) << "\n"; + } else { + std::vector<Elf_Rela> RelrRelas = + unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); + for (const Elf_Rela &R : RelrRelas) + printRelocation(Obj, R, SymTab); + } + break; + } + case ELF::SHT_ANDROID_REL: + case ELF::SHT_ANDROID_RELA: + for (const Elf_Rela &R : + unwrapOrError(this->FileName, Obj->android_relas(Sec))) + printRelocation(Obj, R, SymTab); + break; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, + const Elf_Shdr *SymTab) { + SmallString<32> RelocName; + Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); + std::string TargetName; + const Elf_Sym *Sym = + unwrapOrError(this->FileName, Obj->getRelocationSymbol(&Rel, SymTab)); + if (Sym && Sym->getType() == ELF::STT_SECTION) { + const Elf_Shdr *Sec = unwrapOrError( + this->FileName, + Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable())); + TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec)); + } else if (Sym) { + StringRef StrTable = + unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab)); + TargetName = this->dumper()->getFullSymbolName( + Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); + } + + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printHex("Offset", Rel.r_offset); + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); + W.printNumber("Symbol", !TargetName.empty() ? TargetName : "-", + Rel.getSymbol(Obj->isMips64EL())); + W.printHex("Addend", Rel.r_addend); + } else { + raw_ostream &OS = W.startLine(); + OS << W.hex(Rel.r_offset) << " " << RelocName << " " + << (!TargetName.empty() ? TargetName : "-") << " " << W.hex(Rel.r_addend) + << "\n"; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { + ListScope SectionsD(W, "Sections"); + + int SectionIndex = -1; + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); + for (const Elf_Shdr &Sec : Sections) { + StringRef Name = unwrapOrError( + ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); + DictScope SectionD(W, "Section"); + W.printNumber("Index", ++SectionIndex); + W.printNumber("Name", Name, Sec.sh_name); + W.printHex( + "Type", + object::getELFSectionTypeName(Obj->getHeader()->e_machine, Sec.sh_type), + Sec.sh_type); + std::vector<EnumEntry<unsigned>> SectionFlags(std::begin(ElfSectionFlags), + std::end(ElfSectionFlags)); + switch (Obj->getHeader()->e_machine) { + case EM_ARM: + SectionFlags.insert(SectionFlags.end(), std::begin(ElfARMSectionFlags), + std::end(ElfARMSectionFlags)); + break; + case EM_HEXAGON: + SectionFlags.insert(SectionFlags.end(), + std::begin(ElfHexagonSectionFlags), + std::end(ElfHexagonSectionFlags)); + break; + case EM_MIPS: + SectionFlags.insert(SectionFlags.end(), std::begin(ElfMipsSectionFlags), + std::end(ElfMipsSectionFlags)); + break; + case EM_X86_64: + SectionFlags.insert(SectionFlags.end(), std::begin(ElfX86_64SectionFlags), + std::end(ElfX86_64SectionFlags)); + break; + case EM_XCORE: + SectionFlags.insert(SectionFlags.end(), std::begin(ElfXCoreSectionFlags), + std::end(ElfXCoreSectionFlags)); + break; + default: + // Nothing to do. + break; + } + W.printFlags("Flags", Sec.sh_flags, makeArrayRef(SectionFlags)); + W.printHex("Address", Sec.sh_addr); + W.printHex("Offset", Sec.sh_offset); + W.printNumber("Size", Sec.sh_size); + W.printNumber("Link", Sec.sh_link); + W.printNumber("Info", Sec.sh_info); + W.printNumber("AddressAlignment", Sec.sh_addralign); + W.printNumber("EntrySize", Sec.sh_entsize); + + if (opts::SectionRelocations) { + ListScope D(W, "Relocations"); + printRelocations(&Sec, Obj); + } + + if (opts::SectionSymbols) { + ListScope D(W, "Symbols"); + const Elf_Shdr *Symtab = this->dumper()->getDotSymtabSec(); + StringRef StrTable = + unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*Symtab)); + + for (const Elf_Sym &Sym : + unwrapOrError(this->FileName, Obj->symbols(Symtab))) { + const Elf_Shdr *SymSec = unwrapOrError( + this->FileName, + Obj->getSection(&Sym, Symtab, this->dumper()->getShndxTable())); + if (SymSec == &Sec) + printSymbol( + Obj, &Sym, + unwrapOrError(this->FileName, Obj->symbols(Symtab)).begin(), + StrTable, false, false); + } + } + + if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) { + ArrayRef<uint8_t> Data = + unwrapOrError(this->FileName, Obj->getSectionContents(&Sec)); + W.printBinaryBlock( + "SectionData", + StringRef(reinterpret_cast<const char *>(Data.data()), Data.size())); + } + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, + const Elf_Sym *First, StringRef StrTable, + bool IsDynamic, + bool /*NonVisibilityBitsUsed*/) { + unsigned SectionIndex = 0; + StringRef SectionName; + this->dumper()->getSectionNameIndex(Symbol, First, SectionName, SectionIndex); + std::string FullSymbolName = + this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic); + unsigned char SymbolType = Symbol->getType(); + + DictScope D(W, "Symbol"); + W.printNumber("Name", FullSymbolName, Symbol->st_name); + W.printHex("Value", Symbol->st_value); + W.printNumber("Size", Symbol->st_size); + W.printEnum("Binding", Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); + if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && + SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) + W.printEnum("Type", SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + else + W.printEnum("Type", SymbolType, makeArrayRef(ElfSymbolTypes)); + if (Symbol->st_other == 0) + // Usually st_other flag is zero. Do not pollute the output + // by flags enumeration in that case. + W.printNumber("Other", 0); + else { + std::vector<EnumEntry<unsigned>> SymOtherFlags(std::begin(ElfSymOtherFlags), + std::end(ElfSymOtherFlags)); + if (Obj->getHeader()->e_machine == EM_MIPS) { + // Someones in their infinite wisdom decided to make STO_MIPS_MIPS16 + // flag overlapped with other ST_MIPS_xxx flags. So consider both + // cases separately. + if ((Symbol->st_other & STO_MIPS_MIPS16) == STO_MIPS_MIPS16) + SymOtherFlags.insert(SymOtherFlags.end(), + std::begin(ElfMips16SymOtherFlags), + std::end(ElfMips16SymOtherFlags)); + else + SymOtherFlags.insert(SymOtherFlags.end(), + std::begin(ElfMipsSymOtherFlags), + std::end(ElfMipsSymOtherFlags)); + } + W.printFlags("Other", Symbol->st_other, makeArrayRef(SymOtherFlags), 0x3u); + } + W.printHex("Section", SectionName, SectionIndex); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) { + if (PrintSymbols) + printSymbols(Obj); + if (PrintDynamicSymbols) + printDynamicSymbols(Obj); +} + +template <class ELFT> void LLVMStyle<ELFT>::printSymbols(const ELFO *Obj) { + ListScope Group(W, "Symbols"); + this->dumper()->printSymbolsHelper(false); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) { + ListScope Group(W, "DynamicSymbols"); + this->dumper()->printSymbolsHelper(true); +} + +template <class ELFT> void LLVMStyle<ELFT>::printDynamic(const ELFFile<ELFT> *Obj) { + Elf_Dyn_Range Table = this->dumper()->dynamic_table(); + if (Table.empty()) + return; + + raw_ostream &OS = W.getOStream(); + W.startLine() << "DynamicSection [ (" << Table.size() << " entries)\n"; + + bool Is64 = ELFT::Is64Bits; + if (Is64) + W.startLine() << " Tag Type Name/Value\n"; + else + W.startLine() << " Tag Type Name/Value\n"; + for (auto Entry : Table) { + uintX_t Tag = Entry.getTag(); + W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " " + << format("%-21s", + getTypeString(Obj->getHeader()->e_machine, Tag)); + this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal()); + OS << "\n"; + } + + W.startLine() << "]\n"; +} + +template <class ELFT> +void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { + const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion(); + const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion(); + const DynRegionInfo &DynRelrRegion = this->dumper()->getDynRelrRegion(); + const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion(); + if (DynRelRegion.Size && DynRelaRegion.Size) + report_fatal_error("There are both REL and RELA dynamic relocations"); + W.startLine() << "Dynamic Relocations {\n"; + W.indent(); + if (DynRelaRegion.Size > 0) + for (const Elf_Rela &Rela : this->dumper()->dyn_relas()) + printDynamicRelocation(Obj, Rela); + else + for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) { + Elf_Rela Rela; + Rela.r_offset = Rel.r_offset; + Rela.r_info = Rel.r_info; + Rela.r_addend = 0; + printDynamicRelocation(Obj, Rela); + } + if (DynRelrRegion.Size > 0) { + Elf_Relr_Range Relrs = this->dumper()->dyn_relrs(); + std::vector<Elf_Rela> RelrRelas = + unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); + for (const Elf_Rela &Rela : RelrRelas) + printDynamicRelocation(Obj, Rela); + } + if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) + for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>()) + printDynamicRelocation(Obj, Rela); + else + for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) { + Elf_Rela Rela; + Rela.r_offset = Rel.r_offset; + Rela.r_info = Rel.r_info; + Rela.r_addend = 0; + printDynamicRelocation(Obj, Rela); + } + W.unindent(); + W.startLine() << "}\n"; +} + +template <class ELFT> +void LLVMStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) { + SmallString<32> RelocName; + Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); + std::string SymbolName = + getSymbolForReloc(Obj, this->FileName, this->dumper(), Rel).Name; + + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printHex("Offset", Rel.r_offset); + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); + W.printString("Symbol", !SymbolName.empty() ? SymbolName : "-"); + W.printHex("Addend", Rel.r_addend); + } else { + raw_ostream &OS = W.startLine(); + OS << W.hex(Rel.r_offset) << " " << RelocName << " " + << (!SymbolName.empty() ? SymbolName : "-") << " " << W.hex(Rel.r_addend) + << "\n"; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printProgramHeaders( + const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(Obj); + if (PrintSectionMapping == cl::BOU_TRUE) + printSectionMapping(Obj); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { + ListScope L(W, "ProgramHeaders"); + + for (const Elf_Phdr &Phdr : + unwrapOrError(this->FileName, Obj->program_headers())) { + DictScope P(W, "ProgramHeader"); + W.printHex("Type", + getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type), + Phdr.p_type); + W.printHex("Offset", Phdr.p_offset); + W.printHex("VirtualAddress", Phdr.p_vaddr); + W.printHex("PhysicalAddress", Phdr.p_paddr); + W.printNumber("FileSize", Phdr.p_filesz); + W.printNumber("MemSize", Phdr.p_memsz); + W.printFlags("Flags", Phdr.p_flags, makeArrayRef(ElfSegmentFlags)); + W.printNumber("Alignment", Phdr.p_align); + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + ListScope SS(W, "VersionSymbols"); + if (!Sec) + return; + + const uint8_t *VersymBuf = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const ELFDumper<ELFT> *Dumper = this->dumper(); + StringRef StrTable = Dumper->getDynamicStringTable(); + + // Same number of entries in the dynamic symbol table (DT_SYMTAB). + for (const Elf_Sym &Sym : Dumper->dynamic_symbols()) { + DictScope S(W, "Symbol"); + const Elf_Versym *Versym = reinterpret_cast<const Elf_Versym *>(VersymBuf); + std::string FullSymbolName = + Dumper->getFullSymbolName(&Sym, StrTable, true /* IsDynamic */); + W.printNumber("Version", Versym->vs_index & VERSYM_VERSION); + W.printString("Name", FullSymbolName); + VersymBuf += sizeof(Elf_Versym); + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + ListScope SD(W, "VersionDefinitions"); + if (!Sec) + return; + + const uint8_t *SecStartAddress = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; + const uint8_t *VerdefBuf = SecStartAddress; + const Elf_Shdr *StrTab = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + + unsigned VerDefsNum = Sec->sh_info; + while (VerDefsNum--) { + if (VerdefBuf + sizeof(Elf_Verdef) > SecEndAddress) + // FIXME: report_fatal_error is not a good way to report error. We should + // emit a parsing error here and below. + report_fatal_error("invalid offset in the section"); + + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf); + DictScope Def(W, "Definition"); + W.printNumber("Version", Verdef->vd_version); + W.printEnum("Flags", Verdef->vd_flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Verdef->vd_ndx); + W.printNumber("Hash", Verdef->vd_hash); + W.printString("Name", StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + + Verdef->getAux()->vda_name))); + if (!Verdef->vd_cnt) + report_fatal_error("at least one definition string must exist"); + if (Verdef->vd_cnt > 2) + report_fatal_error("more than one predecessor is not expected"); + + if (Verdef->vd_cnt == 2) { + const uint8_t *VerdauxBuf = + VerdefBuf + Verdef->vd_aux + Verdef->getAux()->vda_next; + const Elf_Verdaux *Verdaux = + reinterpret_cast<const Elf_Verdaux *>(VerdauxBuf); + W.printString("Predecessor", + StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + Verdaux->vda_name))); + } + VerdefBuf += Verdef->vd_next; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + ListScope SD(W, "VersionRequirements"); + if (!Sec) + return; + + const uint8_t *SecData = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const Elf_Shdr *StrTab = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + + const uint8_t *VerneedBuf = SecData; + unsigned VerneedNum = Sec->sh_info; + for (unsigned I = 0; I < VerneedNum; ++I) { + const Elf_Verneed *Verneed = + reinterpret_cast<const Elf_Verneed *>(VerneedBuf); + DictScope Entry(W, "Dependency"); + W.printNumber("Version", Verneed->vn_version); + W.printNumber("Count", Verneed->vn_cnt); + W.printString("FileName", + StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + Verneed->vn_file))); + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + ListScope L(W, "Entries"); + for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(VernauxBuf); + DictScope Entry(W, "Entry"); + W.printNumber("Hash", Vernaux->vna_hash); + W.printEnum("Flags", Vernaux->vna_flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Vernaux->vna_other); + W.printString("Name", + StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + Vernaux->vna_name))); + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { + W.startLine() << "Hash Histogram not implemented!\n"; +} + +template <class ELFT> +void LLVMStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { + ListScope L(W, "CGProfile"); + if (!this->dumper()->getDotCGProfileSec()) + return; + auto CGProfile = unwrapOrError( + this->FileName, Obj->template getSectionContentsAsArray<Elf_CGProfile>( + this->dumper()->getDotCGProfileSec())); + for (const Elf_CGProfile &CGPE : CGProfile) { + DictScope D(W, "CGProfileEntry"); + W.printNumber( + "From", + unwrapOrError(this->FileName, + this->dumper()->getStaticSymbolName(CGPE.cgp_from)), + CGPE.cgp_from); + W.printNumber( + "To", + unwrapOrError(this->FileName, + this->dumper()->getStaticSymbolName(CGPE.cgp_to)), + CGPE.cgp_to); + W.printNumber("Weight", CGPE.cgp_weight); + } +} + +static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) { + std::vector<uint64_t> Ret; + const uint8_t *Cur = Data.begin(); + const uint8_t *End = Data.end(); + while (Cur != End) { + unsigned Size; + const char *Err; + Ret.push_back(decodeULEB128(Cur, &Size, End, &Err)); + if (Err) + return createError(Err); + Cur += Size; + } + return Ret; +} + +template <class ELFT> +void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { + ListScope L(W, "Addrsig"); + if (!this->dumper()->getDotAddrsigSec()) + return; + ArrayRef<uint8_t> Contents = unwrapOrError( + this->FileName, + Obj->getSectionContents(this->dumper()->getDotAddrsigSec())); + Expected<std::vector<uint64_t>> V = toULEB128Array(Contents); + if (!V) { + reportWarning(V.takeError(), this->FileName); + return; + } + + for (uint64_t Sym : *V) { + Expected<std::string> NameOrErr = this->dumper()->getStaticSymbolName(Sym); + if (NameOrErr) { + W.printNumber("Sym", *NameOrErr, Sym); + continue; + } + reportWarning(NameOrErr.takeError(), this->FileName); + W.printNumber("Sym", "<?>", Sym); + } +} + +template <typename ELFT> +static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, + ScopedPrinter &W) { + switch (NoteType) { + default: + return; + case ELF::NT_GNU_ABI_TAG: { + const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); + if (!AbiTag.IsValid) { + W.printString("ABI", "<corrupt GNU_ABI_TAG>"); + } else { + W.printString("OS", AbiTag.OSName); + W.printString("ABI", AbiTag.ABI); + } + break; + } + case ELF::NT_GNU_BUILD_ID: { + W.printString("Build ID", getGNUBuildId(Desc)); + break; + } + case ELF::NT_GNU_GOLD_VERSION: + W.printString("Version", getGNUGoldVersion(Desc)); + break; + case ELF::NT_GNU_PROPERTY_TYPE_0: + ListScope D(W, "Property"); + for (const auto &Property : getGNUPropertyList<ELFT>(Desc)) + W.printString(Property); + break; + } +} + +static void printCoreNoteLLVMStyle(const CoreNote &Note, ScopedPrinter &W) { + W.printNumber("Page Size", Note.PageSize); + for (const CoreFileMapping &Mapping : Note.Mappings) { + ListScope D(W, "Mapping"); + W.printHex("Start", Mapping.Start); + W.printHex("End", Mapping.End); + W.printHex("Offset", Mapping.Offset); + W.printString("Filename", Mapping.Filename); + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { + ListScope L(W, "Notes"); + + auto PrintHeader = [&](const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { + W.printHex("Offset", Offset); + W.printHex("Size", Size); + }; + + auto ProcessNote = [&](const Elf_Note &Note) { + DictScope D2(W, "Note"); + StringRef Name = Note.getName(); + ArrayRef<uint8_t> Descriptor = Note.getDesc(); + Elf_Word Type = Note.getType(); + + // Print the note owner/type. + W.printString("Owner", Name); + W.printHex("Data size", Descriptor.size()); + if (Name == "GNU") { + W.printString("Type", getGNUNoteTypeName(Type)); + } else if (Name == "FreeBSD") { + W.printString("Type", getFreeBSDNoteTypeName(Type)); + } else if (Name == "AMD") { + W.printString("Type", getAMDNoteTypeName(Type)); + } else if (Name == "AMDGPU") { + W.printString("Type", getAMDGPUNoteTypeName(Type)); + } else { + StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE + ? getCoreNoteTypeName(Type) + : getGenericNoteTypeName(Type); + if (!NoteType.empty()) + W.printString("Type", NoteType); + else + W.printString("Type", + "Unknown (" + to_string(format_hex(Type, 10)) + ")"); + } + + // Print the description, or fallback to printing raw bytes for unknown + // owners. + if (Name == "GNU") { + printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W); + } else if (Name == "AMD") { + const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + W.printString(N.Type, N.Value); + } else if (Name == "AMDGPU") { + const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + W.printString(N.Type, N.Value); + } else if (Name == "CORE") { + if (Type == ELF::NT_FILE) { + DataExtractor DescExtractor(Descriptor, + ELFT::TargetEndianness == support::little, + sizeof(Elf_Addr)); + Expected<CoreNote> Note = readCoreNote(DescExtractor); + if (Note) + printCoreNoteLLVMStyle(*Note, W); + else + reportWarning(Note.takeError(), this->FileName); + } + } else if (!Descriptor.empty()) { + W.printBinaryBlock("Description data", Descriptor); + } + }; + + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + if (Obj->getHeader()->e_type != ELF::ET_CORE && !Sections.empty()) { + for (const auto &S : Sections) { + if (S.sh_type != SHT_NOTE) + continue; + DictScope D(W, "NoteSection"); + PrintHeader(S.sh_offset, S.sh_size); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(S, Err)) + ProcessNote(Note); + if (Err) + reportError(std::move(Err), this->FileName); + } + } else { + for (const auto &P : + unwrapOrError(this->FileName, Obj->program_headers())) { + if (P.p_type != PT_NOTE) + continue; + DictScope D(W, "NoteSection"); + PrintHeader(P.p_offset, P.p_filesz); + Error Err = Error::success(); + for (const auto &Note : Obj->notes(P, Err)) + ProcessNote(Note); + if (Err) + reportError(std::move(Err), this->FileName); + } + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { + ListScope L(W, "LinkerOptions"); + + for (const Elf_Shdr &Shdr : unwrapOrError(this->FileName, Obj->sections())) { + if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS) + continue; + + ArrayRef<uint8_t> Contents = + unwrapOrError(this->FileName, Obj->getSectionContents(&Shdr)); + for (const uint8_t *P = Contents.begin(), *E = Contents.end(); P < E; ) { + StringRef Key = StringRef(reinterpret_cast<const char *>(P)); + StringRef Value = + StringRef(reinterpret_cast<const char *>(P) + Key.size() + 1); + + W.printString(Key, Value); + + P = P + Key.size() + Value.size() + 2; + } + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printStackSizes(const ELFObjectFile<ELFT> *Obj) { + ListScope L(W, "StackSizes"); + if (Obj->isRelocatableObject()) + this->printRelocatableStackSizes(Obj, []() {}); + else + this->printNonRelocatableStackSizes(Obj, []() {}); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + DictScope D(W, "Entry"); + W.printString("Function", FuncName); + W.printHex("Size", Size); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { + auto PrintEntry = [&](const Elf_Addr *E) { + W.printHex("Address", Parser.getGotAddress(E)); + W.printNumber("Access", Parser.getGotOffset(E)); + W.printHex("Initial", *E); + }; + + DictScope GS(W, Parser.IsStatic ? "Static GOT" : "Primary GOT"); + + W.printHex("Canonical gp value", Parser.getGp()); + { + ListScope RS(W, "Reserved entries"); + { + DictScope D(W, "Entry"); + PrintEntry(Parser.getGotLazyResolver()); + W.printString("Purpose", StringRef("Lazy resolver")); + } + + if (Parser.getGotModulePointer()) { + DictScope D(W, "Entry"); + PrintEntry(Parser.getGotModulePointer()); + W.printString("Purpose", StringRef("Module pointer (GNU extension)")); + } + } + { + ListScope LS(W, "Local entries"); + for (auto &E : Parser.getLocalEntries()) { + DictScope D(W, "Entry"); + PrintEntry(&E); + } + } + + if (Parser.IsStatic) + return; + + { + ListScope GS(W, "Global entries"); + for (auto &E : Parser.getGlobalEntries()) { + DictScope D(W, "Entry"); + + PrintEntry(&E); + + const Elf_Sym *Sym = Parser.getGotSym(&E); + W.printHex("Value", Sym->st_value); + W.printEnum("Type", Sym->getType(), makeArrayRef(ElfSymbolTypes)); + + unsigned SectionIndex = 0; + StringRef SectionName; + this->dumper()->getSectionNameIndex( + Sym, this->dumper()->dynamic_symbols().begin(), SectionName, + SectionIndex); + W.printHex("Section", SectionName, SectionIndex); + + std::string SymName = this->dumper()->getFullSymbolName( + Sym, this->dumper()->getDynamicStringTable(), true); + W.printNumber("Name", SymName, Sym->st_name); + } + } + + W.printNumber("Number of TLS and multi-GOT entries", + uint64_t(Parser.getOtherEntries().size())); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { + auto PrintEntry = [&](const Elf_Addr *E) { + W.printHex("Address", Parser.getPltAddress(E)); + W.printHex("Initial", *E); + }; + + DictScope GS(W, "PLT GOT"); + + { + ListScope RS(W, "Reserved entries"); + { + DictScope D(W, "Entry"); + PrintEntry(Parser.getPltLazyResolver()); + W.printString("Purpose", StringRef("PLT lazy resolver")); + } + + if (auto E = Parser.getPltModulePointer()) { + DictScope D(W, "Entry"); + PrintEntry(E); + W.printString("Purpose", StringRef("Module pointer")); + } + } + { + ListScope LS(W, "Entries"); + for (auto &E : Parser.getPltEntries()) { + DictScope D(W, "Entry"); + PrintEntry(&E); + + const Elf_Sym *Sym = Parser.getPltSym(&E); + W.printHex("Value", Sym->st_value); + W.printEnum("Type", Sym->getType(), makeArrayRef(ElfSymbolTypes)); + + unsigned SectionIndex = 0; + StringRef SectionName; + this->dumper()->getSectionNameIndex( + Sym, this->dumper()->dynamic_symbols().begin(), SectionName, + SectionIndex); + W.printHex("Section", SectionName, SectionIndex); + + std::string SymName = + this->dumper()->getFullSymbolName(Sym, Parser.getPltStrTable(), true); + W.printNumber("Name", SymName, Sym->st_name); + } + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printMipsABIFlags(const ELFObjectFile<ELFT> *ObjF) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *Shdr = + findSectionByName(*Obj, ObjF->getFileName(), ".MIPS.abiflags"); + if (!Shdr) { + W.startLine() << "There is no .MIPS.abiflags section in the file.\n"; + return; + } + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) { + W.startLine() << "The .MIPS.abiflags section has a wrong size.\n"; + return; + } + + auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec.data()); + + raw_ostream &OS = W.getOStream(); + DictScope GS(W, "MIPS ABI Flags"); + + W.printNumber("Version", Flags->version); + W.startLine() << "ISA: "; + if (Flags->isa_rev <= 1) + OS << format("MIPS%u", Flags->isa_level); + else + OS << format("MIPS%ur%u", Flags->isa_level, Flags->isa_rev); + OS << "\n"; + W.printEnum("ISA Extension", Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)); + W.printFlags("ASEs", Flags->ases, makeArrayRef(ElfMipsASEFlags)); + W.printEnum("FP ABI", Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)); + W.printNumber("GPR size", getMipsRegisterSize(Flags->gpr_size)); + W.printNumber("CPR1 size", getMipsRegisterSize(Flags->cpr1_size)); + W.printNumber("CPR2 size", getMipsRegisterSize(Flags->cpr2_size)); + W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1)); + W.printHex("Flags 2", Flags->flags2); +} diff --git a/llvm/tools/llvm-readobj/Error.cpp b/llvm/tools/llvm-readobj/Error.cpp new file mode 100644 index 000000000000..1010f18a58c8 --- /dev/null +++ b/llvm/tools/llvm-readobj/Error.cpp @@ -0,0 +1,56 @@ +//===- Error.cpp - system_error extensions for llvm-readobj -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines a new error_category for the llvm-readobj tool. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +namespace { +// FIXME: This class is only here to support the transition to llvm::Error. It +// will be removed once this transition is complete. Clients should prefer to +// deal with the Error value directly, rather than converting to error_code. +class _readobj_error_category : public std::error_category { +public: + const char* name() const noexcept override; + std::string message(int ev) const override; +}; +} // namespace + +const char *_readobj_error_category::name() const noexcept { + return "llvm.readobj"; +} + +std::string _readobj_error_category::message(int EV) const { + switch (static_cast<readobj_error>(EV)) { + case readobj_error::success: return "Success"; + case readobj_error::file_not_found: + return "No such file."; + case readobj_error::unsupported_file_format: + return "The file was not recognized as a valid object file."; + case readobj_error::unrecognized_file_format: + return "Unrecognized file type."; + case readobj_error::unsupported_obj_file_format: + return "Unsupported object file format."; + case readobj_error::unknown_symbol: + return "Unknown symbol."; + } + llvm_unreachable("An enumerator of readobj_error does not have a message " + "defined."); +} + +namespace llvm { +const std::error_category &readobj_category() { + static _readobj_error_category o; + return o; +} +} // namespace llvm diff --git a/llvm/tools/llvm-readobj/Error.h b/llvm/tools/llvm-readobj/Error.h new file mode 100644 index 000000000000..f390e1b96f8a --- /dev/null +++ b/llvm/tools/llvm-readobj/Error.h @@ -0,0 +1,40 @@ +//===- Error.h - system_error extensions for llvm-readobj -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This declares a new error_category for the llvm-readobj tool. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_ERROR_H +#define LLVM_TOOLS_LLVM_READOBJ_ERROR_H + +#include <system_error> + +namespace llvm { +const std::error_category &readobj_category(); + +enum class readobj_error { + success = 0, + file_not_found, + unsupported_file_format, + unrecognized_file_format, + unsupported_obj_file_format, + unknown_symbol +}; + +inline std::error_code make_error_code(readobj_error e) { + return std::error_code(static_cast<int>(e), readobj_category()); +} + +} // namespace llvm + +namespace std { +template <> struct is_error_code_enum<llvm::readobj_error> : std::true_type {}; +} + +#endif diff --git a/llvm/tools/llvm-readobj/MachODumper.cpp b/llvm/tools/llvm-readobj/MachODumper.cpp new file mode 100644 index 000000000000..20a60b3df699 --- /dev/null +++ b/llvm/tools/llvm-readobj/MachODumper.cpp @@ -0,0 +1,889 @@ +//===- MachODumper.cpp - Object file dumping utility for llvm -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the MachO-specific dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "StackMapPrinter.h" +#include "llvm-readobj.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace object; + +namespace { + +class MachODumper : public ObjDumper { +public: + MachODumper(const MachOObjectFile *Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj) {} + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printUnwindInfo() override; + void printStackMap() const override; + + void printNeededLibraries() override; + + // MachO-specific. + void printMachODataInCode() override; + void printMachOVersionMin() override; + void printMachODysymtab() override; + void printMachOSegment() override; + void printMachOIndirectSymbols() override; + void printMachOLinkerOptions () override; + +private: + template<class MachHeader> + void printFileHeaders(const MachHeader &Header); + + void printSymbols() override; + void printDynamicSymbols() override; + void printSymbol(const SymbolRef &Symbol); + + void printRelocation(const RelocationRef &Reloc); + + void printRelocation(const MachOObjectFile *Obj, const RelocationRef &Reloc); + + void printSectionHeaders(const MachOObjectFile *Obj); + + const MachOObjectFile *Obj; +}; + +} // namespace + + +namespace llvm { + +std::error_code createMachODumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(Obj); + if (!MachOObj) + return readobj_error::unsupported_obj_file_format; + + Result.reset(new MachODumper(MachOObj, Writer)); + return readobj_error::success; +} + +} // namespace llvm + +static const EnumEntry<uint32_t> MachOMagics[] = { + { "Magic", MachO::MH_MAGIC }, + { "Cigam", MachO::MH_CIGAM }, + { "Magic64", MachO::MH_MAGIC_64 }, + { "Cigam64", MachO::MH_CIGAM_64 }, + { "FatMagic", MachO::FAT_MAGIC }, + { "FatCigam", MachO::FAT_CIGAM }, +}; + +static const EnumEntry<uint32_t> MachOHeaderFileTypes[] = { + { "Relocatable", MachO::MH_OBJECT }, + { "Executable", MachO::MH_EXECUTE }, + { "FixedVMLibrary", MachO::MH_FVMLIB }, + { "Core", MachO::MH_CORE }, + { "PreloadedExecutable", MachO::MH_PRELOAD }, + { "DynamicLibrary", MachO::MH_DYLIB }, + { "DynamicLinker", MachO::MH_DYLINKER }, + { "Bundle", MachO::MH_BUNDLE }, + { "DynamicLibraryStub", MachO::MH_DYLIB_STUB }, + { "DWARFSymbol", MachO::MH_DSYM }, + { "KextBundle", MachO::MH_KEXT_BUNDLE }, +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuTypes[] = { + { "Any" , static_cast<uint32_t>(MachO::CPU_TYPE_ANY) }, + { "X86" , MachO::CPU_TYPE_X86 }, + { "X86-64" , MachO::CPU_TYPE_X86_64 }, + { "Mc98000" , MachO::CPU_TYPE_MC98000 }, + { "Arm" , MachO::CPU_TYPE_ARM }, + { "Arm64" , MachO::CPU_TYPE_ARM64 }, + { "Sparc" , MachO::CPU_TYPE_SPARC }, + { "PowerPC" , MachO::CPU_TYPE_POWERPC }, + { "PowerPC64" , MachO::CPU_TYPE_POWERPC64 }, +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesX86[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_I386_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_386), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_486), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_486SX), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_586), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTPRO), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTII_M3), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTII_M5), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_CELERON), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_CELERON_MOBILE), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3_M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3_XEON), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_4), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_4_M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ITANIUM), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ITANIUM_2), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_XEON), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_XEON_MP), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesX64[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_64_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_ARCH1), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_64_H), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V4T), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V6), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V5), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V5TEJ), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_XSCALE), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7S), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7K), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V6M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7EM), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM64[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64E), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesSPARC[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_SPARC_ALL), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesPPC[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_601), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_602), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603e), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603ev), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_604), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_604e), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_620), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_750), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_7400), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_7450), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_970), +}; + +static const EnumEntry<uint32_t> MachOHeaderFlags[] = { + LLVM_READOBJ_ENUM_ENT(MachO, MH_NOUNDEFS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_INCRLINK), + LLVM_READOBJ_ENUM_ENT(MachO, MH_DYLDLINK), + LLVM_READOBJ_ENUM_ENT(MachO, MH_BINDATLOAD), + LLVM_READOBJ_ENUM_ENT(MachO, MH_PREBOUND), + LLVM_READOBJ_ENUM_ENT(MachO, MH_SPLIT_SEGS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_LAZY_INIT), + LLVM_READOBJ_ENUM_ENT(MachO, MH_TWOLEVEL), + LLVM_READOBJ_ENUM_ENT(MachO, MH_FORCE_FLAT), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NOMULTIDEFS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NOFIXPREBINDING), + LLVM_READOBJ_ENUM_ENT(MachO, MH_PREBINDABLE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_ALLMODSBOUND), + LLVM_READOBJ_ENUM_ENT(MachO, MH_SUBSECTIONS_VIA_SYMBOLS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_CANONICAL), + LLVM_READOBJ_ENUM_ENT(MachO, MH_WEAK_DEFINES), + LLVM_READOBJ_ENUM_ENT(MachO, MH_BINDS_TO_WEAK), + LLVM_READOBJ_ENUM_ENT(MachO, MH_ALLOW_STACK_EXECUTION), + LLVM_READOBJ_ENUM_ENT(MachO, MH_ROOT_SAFE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_SETUID_SAFE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NO_REEXPORTED_DYLIBS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_PIE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_DEAD_STRIPPABLE_DYLIB), + LLVM_READOBJ_ENUM_ENT(MachO, MH_HAS_TLV_DESCRIPTORS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NO_HEAP_EXECUTION), + LLVM_READOBJ_ENUM_ENT(MachO, MH_APP_EXTENSION_SAFE), +}; + +static const EnumEntry<unsigned> MachOSectionTypes[] = { + { "Regular" , MachO::S_REGULAR }, + { "ZeroFill" , MachO::S_ZEROFILL }, + { "CStringLiterals" , MachO::S_CSTRING_LITERALS }, + { "4ByteLiterals" , MachO::S_4BYTE_LITERALS }, + { "8ByteLiterals" , MachO::S_8BYTE_LITERALS }, + { "LiteralPointers" , MachO::S_LITERAL_POINTERS }, + { "NonLazySymbolPointers" , MachO::S_NON_LAZY_SYMBOL_POINTERS }, + { "LazySymbolPointers" , MachO::S_LAZY_SYMBOL_POINTERS }, + { "SymbolStubs" , MachO::S_SYMBOL_STUBS }, + { "ModInitFuncPointers" , MachO::S_MOD_INIT_FUNC_POINTERS }, + { "ModTermFuncPointers" , MachO::S_MOD_TERM_FUNC_POINTERS }, + { "Coalesced" , MachO::S_COALESCED }, + { "GBZeroFill" , MachO::S_GB_ZEROFILL }, + { "Interposing" , MachO::S_INTERPOSING }, + { "16ByteLiterals" , MachO::S_16BYTE_LITERALS }, + { "DTraceDOF" , MachO::S_DTRACE_DOF }, + { "LazyDylibSymbolPointers" , MachO::S_LAZY_DYLIB_SYMBOL_POINTERS }, + { "ThreadLocalRegular" , MachO::S_THREAD_LOCAL_REGULAR }, + { "ThreadLocalZerofill" , MachO::S_THREAD_LOCAL_ZEROFILL }, + { "ThreadLocalVariables" , MachO::S_THREAD_LOCAL_VARIABLES }, + { "ThreadLocalVariablePointers" , MachO::S_THREAD_LOCAL_VARIABLE_POINTERS }, + { "ThreadLocalInitFunctionPointers", MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS } +}; + +static const EnumEntry<unsigned> MachOSectionAttributes[] = { + { "LocReloc" , 1 << 0 /*S_ATTR_LOC_RELOC */ }, + { "ExtReloc" , 1 << 1 /*S_ATTR_EXT_RELOC */ }, + { "SomeInstructions" , 1 << 2 /*S_ATTR_SOME_INSTRUCTIONS */ }, + { "Debug" , 1 << 17 /*S_ATTR_DEBUG */ }, + { "SelfModifyingCode", 1 << 18 /*S_ATTR_SELF_MODIFYING_CODE*/ }, + { "LiveSupport" , 1 << 19 /*S_ATTR_LIVE_SUPPORT */ }, + { "NoDeadStrip" , 1 << 20 /*S_ATTR_NO_DEAD_STRIP */ }, + { "StripStaticSyms" , 1 << 21 /*S_ATTR_STRIP_STATIC_SYMS */ }, + { "NoTOC" , 1 << 22 /*S_ATTR_NO_TOC */ }, + { "PureInstructions" , 1 << 23 /*S_ATTR_PURE_INSTRUCTIONS */ }, +}; + +static const EnumEntry<unsigned> MachOSymbolRefTypes[] = { + { "UndefinedNonLazy", 0 }, + { "ReferenceFlagUndefinedLazy", 1 }, + { "ReferenceFlagDefined", 2 }, + { "ReferenceFlagPrivateDefined", 3 }, + { "ReferenceFlagPrivateUndefinedNonLazy", 4 }, + { "ReferenceFlagPrivateUndefinedLazy", 5 } +}; + +static const EnumEntry<unsigned> MachOSymbolFlags[] = { + { "ReferencedDynamically", 0x10 }, + { "NoDeadStrip", 0x20 }, + { "WeakRef", 0x40 }, + { "WeakDef", 0x80 }, + { "AltEntry", 0x200 }, +}; + +static const EnumEntry<unsigned> MachOSymbolTypes[] = { + { "Undef", 0x0 }, + { "Abs", 0x2 }, + { "Indirect", 0xA }, + { "PreboundUndef", 0xC }, + { "Section", 0xE } +}; + +namespace { + struct MachOSection { + ArrayRef<char> Name; + ArrayRef<char> SegmentName; + uint64_t Address; + uint64_t Size; + uint32_t Offset; + uint32_t Alignment; + uint32_t RelocationTableOffset; + uint32_t NumRelocationTableEntries; + uint32_t Flags; + uint32_t Reserved1; + uint32_t Reserved2; + uint32_t Reserved3; + }; + + struct MachOSegment { + std::string CmdName; + std::string SegName; + uint64_t cmdsize; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; + }; + + struct MachOSymbol { + uint32_t StringIndex; + uint8_t Type; + uint8_t SectionIndex; + uint16_t Flags; + uint64_t Value; + }; +} + +static std::string getMask(uint32_t prot) +{ + // TODO (davide): This always assumes prot is valid. + // Catch mistakes and report if needed. + std::string Prot; + Prot = ""; + Prot += (prot & MachO::VM_PROT_READ) ? "r" : "-"; + Prot += (prot & MachO::VM_PROT_WRITE) ? "w" : "-"; + Prot += (prot & MachO::VM_PROT_EXECUTE) ? "x" : "-"; + return Prot; +} + +static void getSection(const MachOObjectFile *Obj, + DataRefImpl Sec, + MachOSection &Section) { + if (!Obj->is64Bit()) { + MachO::section Sect = Obj->getSection(Sec); + Section.Address = Sect.addr; + Section.Size = Sect.size; + Section.Offset = Sect.offset; + Section.Alignment = Sect.align; + Section.RelocationTableOffset = Sect.reloff; + Section.NumRelocationTableEntries = Sect.nreloc; + Section.Flags = Sect.flags; + Section.Reserved1 = Sect.reserved1; + Section.Reserved2 = Sect.reserved2; + return; + } + MachO::section_64 Sect = Obj->getSection64(Sec); + Section.Address = Sect.addr; + Section.Size = Sect.size; + Section.Offset = Sect.offset; + Section.Alignment = Sect.align; + Section.RelocationTableOffset = Sect.reloff; + Section.NumRelocationTableEntries = Sect.nreloc; + Section.Flags = Sect.flags; + Section.Reserved1 = Sect.reserved1; + Section.Reserved2 = Sect.reserved2; + Section.Reserved3 = Sect.reserved3; +} + +static void getSegment(const MachOObjectFile *Obj, + const MachOObjectFile::LoadCommandInfo &L, + MachOSegment &Segment) { + if (!Obj->is64Bit()) { + MachO::segment_command SC = Obj->getSegmentLoadCommand(L); + Segment.CmdName = "LC_SEGMENT"; + Segment.SegName = SC.segname; + Segment.cmdsize = SC.cmdsize; + Segment.vmaddr = SC.vmaddr; + Segment.vmsize = SC.vmsize; + Segment.fileoff = SC.fileoff; + Segment.filesize = SC.filesize; + Segment.maxprot = SC.maxprot; + Segment.initprot = SC.initprot; + Segment.nsects = SC.nsects; + Segment.flags = SC.flags; + return; + } + MachO::segment_command_64 SC = Obj->getSegment64LoadCommand(L); + Segment.CmdName = "LC_SEGMENT_64"; + Segment.SegName = SC.segname; + Segment.cmdsize = SC.cmdsize; + Segment.vmaddr = SC.vmaddr; + Segment.vmsize = SC.vmsize; + Segment.fileoff = SC.fileoff; + Segment.filesize = SC.filesize; + Segment.maxprot = SC.maxprot; + Segment.initprot = SC.initprot; + Segment.nsects = SC.nsects; + Segment.flags = SC.flags; +} + +static void getSymbol(const MachOObjectFile *Obj, + DataRefImpl DRI, + MachOSymbol &Symbol) { + if (!Obj->is64Bit()) { + MachO::nlist Entry = Obj->getSymbolTableEntry(DRI); + Symbol.StringIndex = Entry.n_strx; + Symbol.Type = Entry.n_type; + Symbol.SectionIndex = Entry.n_sect; + Symbol.Flags = Entry.n_desc; + Symbol.Value = Entry.n_value; + return; + } + MachO::nlist_64 Entry = Obj->getSymbol64TableEntry(DRI); + Symbol.StringIndex = Entry.n_strx; + Symbol.Type = Entry.n_type; + Symbol.SectionIndex = Entry.n_sect; + Symbol.Flags = Entry.n_desc; + Symbol.Value = Entry.n_value; +} + +void MachODumper::printFileHeaders() { + DictScope H(W, "MachHeader"); + if (!Obj->is64Bit()) { + printFileHeaders(Obj->getHeader()); + } else { + printFileHeaders(Obj->getHeader64()); + W.printHex("Reserved", Obj->getHeader64().reserved); + } +} + +template<class MachHeader> +void MachODumper::printFileHeaders(const MachHeader &Header) { + W.printEnum("Magic", Header.magic, makeArrayRef(MachOMagics)); + W.printEnum("CpuType", Header.cputype, makeArrayRef(MachOHeaderCpuTypes)); + uint32_t subtype = Header.cpusubtype & ~MachO::CPU_SUBTYPE_MASK; + switch (Header.cputype) { + case MachO::CPU_TYPE_X86: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesX86)); + break; + case MachO::CPU_TYPE_X86_64: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesX64)); + break; + case MachO::CPU_TYPE_ARM: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesARM)); + break; + case MachO::CPU_TYPE_POWERPC: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesPPC)); + break; + case MachO::CPU_TYPE_SPARC: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesSPARC)); + break; + case MachO::CPU_TYPE_ARM64: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesARM64)); + break; + case MachO::CPU_TYPE_POWERPC64: + default: + W.printHex("CpuSubtype", subtype); + } + W.printEnum("FileType", Header.filetype, makeArrayRef(MachOHeaderFileTypes)); + W.printNumber("NumOfLoadCommands", Header.ncmds); + W.printNumber("SizeOfLoadCommands", Header.sizeofcmds); + W.printFlags("Flags", Header.flags, makeArrayRef(MachOHeaderFlags)); +} + +void MachODumper::printSectionHeaders() { return printSectionHeaders(Obj); } + +void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) { + ListScope Group(W, "Sections"); + + int SectionIndex = -1; + for (const SectionRef &Section : Obj->sections()) { + ++SectionIndex; + + MachOSection MOSection; + getSection(Obj, Section.getRawDataRefImpl(), MOSection); + DataRefImpl DR = Section.getRawDataRefImpl(); + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); + ArrayRef<char> RawName = Obj->getSectionRawName(DR); + StringRef SegmentName = Obj->getSectionFinalSegmentName(DR); + ArrayRef<char> RawSegmentName = Obj->getSectionRawFinalSegmentName(DR); + + DictScope SectionD(W, "Section"); + W.printNumber("Index", SectionIndex); + W.printBinary("Name", Name, RawName); + W.printBinary("Segment", SegmentName, RawSegmentName); + W.printHex("Address", MOSection.Address); + W.printHex("Size", MOSection.Size); + W.printNumber("Offset", MOSection.Offset); + W.printNumber("Alignment", MOSection.Alignment); + W.printHex("RelocationOffset", MOSection.RelocationTableOffset); + W.printNumber("RelocationCount", MOSection.NumRelocationTableEntries); + W.printEnum("Type", MOSection.Flags & 0xFF, + makeArrayRef(MachOSectionTypes)); + W.printFlags("Attributes", MOSection.Flags >> 8, + makeArrayRef(MachOSectionAttributes)); + W.printHex("Reserved1", MOSection.Reserved1); + W.printHex("Reserved2", MOSection.Reserved2); + if (Obj->is64Bit()) + W.printHex("Reserved3", MOSection.Reserved3); + + if (opts::SectionRelocations) { + ListScope D(W, "Relocations"); + for (const RelocationRef &Reloc : Section.relocations()) + printRelocation(Reloc); + } + + if (opts::SectionSymbols) { + ListScope D(W, "Symbols"); + for (const SymbolRef &Symbol : Obj->symbols()) { + if (!Section.containsSymbol(Symbol)) + continue; + + printSymbol(Symbol); + } + } + + if (opts::SectionData && !Section.isBSS()) + W.printBinaryBlock("SectionData", unwrapOrError(Obj->getFileName(), + Section.getContents())); + } +} + +void MachODumper::printRelocations() { + ListScope D(W, "Relocations"); + + std::error_code EC; + for (const SectionRef &Section : Obj->sections()) { + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); + bool PrintedGroup = false; + for (const RelocationRef &Reloc : Section.relocations()) { + if (!PrintedGroup) { + W.startLine() << "Section " << Name << " {\n"; + W.indent(); + PrintedGroup = true; + } + + printRelocation(Reloc); + } + + if (PrintedGroup) { + W.unindent(); + W.startLine() << "}\n"; + } + } +} + +void MachODumper::printRelocation(const RelocationRef &Reloc) { + return printRelocation(Obj, Reloc); +} + +void MachODumper::printRelocation(const MachOObjectFile *Obj, + const RelocationRef &Reloc) { + uint64_t Offset = Reloc.getOffset(); + SmallString<32> RelocName; + Reloc.getTypeName(RelocName); + + DataRefImpl DR = Reloc.getRawDataRefImpl(); + MachO::any_relocation_info RE = Obj->getRelocation(DR); + bool IsScattered = Obj->isRelocationScattered(RE); + bool IsExtern = !IsScattered && Obj->getPlainRelocationExternal(RE); + + StringRef TargetName; + if (IsExtern) { + symbol_iterator Symbol = Reloc.getSymbol(); + if (Symbol != Obj->symbol_end()) { + Expected<StringRef> TargetNameOrErr = Symbol->getName(); + if (!TargetNameOrErr) + reportError(TargetNameOrErr.takeError(), Obj->getFileName()); + TargetName = *TargetNameOrErr; + } + } else if (!IsScattered) { + section_iterator SecI = Obj->getRelocationSection(DR); + if (SecI != Obj->section_end()) + TargetName = unwrapOrError(Obj->getFileName(), SecI->getName()); + } + if (TargetName.empty()) + TargetName = "-"; + + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printHex("Offset", Offset); + W.printNumber("PCRel", Obj->getAnyRelocationPCRel(RE)); + W.printNumber("Length", Obj->getAnyRelocationLength(RE)); + W.printNumber("Type", RelocName, Obj->getAnyRelocationType(RE)); + if (IsScattered) { + W.printHex("Value", Obj->getScatteredRelocationValue(RE)); + } else { + const char *Kind = IsExtern ? "Symbol" : "Section"; + W.printNumber(Kind, TargetName, Obj->getPlainRelocationSymbolNum(RE)); + } + } else { + SmallString<32> SymbolNameOrOffset("0x"); + if (IsScattered) { + // Scattered relocations don't really have an associated symbol for some + // reason, even if one exists in the symtab at the correct address. + SymbolNameOrOffset += utohexstr(Obj->getScatteredRelocationValue(RE)); + } else { + SymbolNameOrOffset = TargetName; + } + + raw_ostream& OS = W.startLine(); + OS << W.hex(Offset) + << " " << Obj->getAnyRelocationPCRel(RE) + << " " << Obj->getAnyRelocationLength(RE); + if (IsScattered) + OS << " n/a"; + else + OS << " " << Obj->getPlainRelocationExternal(RE); + OS << " " << RelocName + << " " << IsScattered + << " " << SymbolNameOrOffset + << "\n"; + } +} + +void MachODumper::printSymbols() { + ListScope Group(W, "Symbols"); + + for (const SymbolRef &Symbol : Obj->symbols()) { + printSymbol(Symbol); + } +} + +void MachODumper::printDynamicSymbols() { + ListScope Group(W, "DynamicSymbols"); +} + +void MachODumper::printSymbol(const SymbolRef &Symbol) { + StringRef SymbolName; + Expected<StringRef> SymbolNameOrErr = Symbol.getName(); + if (!SymbolNameOrErr) { + // TODO: Actually report errors helpfully. + consumeError(SymbolNameOrErr.takeError()); + } else + SymbolName = *SymbolNameOrErr; + + MachOSymbol MOSymbol; + getSymbol(Obj, Symbol.getRawDataRefImpl(), MOSymbol); + + StringRef SectionName = ""; + Expected<section_iterator> SecIOrErr = Symbol.getSection(); + if (!SecIOrErr) + reportError(SecIOrErr.takeError(), Obj->getFileName()); + + section_iterator SecI = *SecIOrErr; + if (SecI != Obj->section_end()) + SectionName = unwrapOrError(Obj->getFileName(), SecI->getName()); + + DictScope D(W, "Symbol"); + W.printNumber("Name", SymbolName, MOSymbol.StringIndex); + if (MOSymbol.Type & MachO::N_STAB) { + W.printHex("Type", "SymDebugTable", MOSymbol.Type); + } else { + if (MOSymbol.Type & MachO::N_PEXT) + W.startLine() << "PrivateExtern\n"; + if (MOSymbol.Type & MachO::N_EXT) + W.startLine() << "Extern\n"; + W.printEnum("Type", uint8_t(MOSymbol.Type & MachO::N_TYPE), + makeArrayRef(MachOSymbolTypes)); + } + W.printHex("Section", SectionName, MOSymbol.SectionIndex); + W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0xF), + makeArrayRef(MachOSymbolRefTypes)); + W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0xF), + makeArrayRef(MachOSymbolFlags)); + W.printHex("Value", MOSymbol.Value); +} + +void MachODumper::printUnwindInfo() { + W.startLine() << "UnwindInfo not implemented.\n"; +} + +void MachODumper::printStackMap() const { + object::SectionRef StackMapSection; + for (auto Sec : Obj->sections()) { + StringRef Name; + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == "__llvm_stackmaps") { + StackMapSection = Sec; + break; + } + } + + if (StackMapSection == object::SectionRef()) + return; + + StringRef StackMapContents = + unwrapOrError(Obj->getFileName(), StackMapSection.getContents()); + ArrayRef<uint8_t> StackMapContentsArray = + arrayRefFromStringRef(StackMapContents); + + if (Obj->isLittleEndian()) + prettyPrintStackMap( + W, StackMapParser<support::little>(StackMapContentsArray)); + else + prettyPrintStackMap( + W, StackMapParser<support::big>(StackMapContentsArray)); +} + +void MachODumper::printNeededLibraries() { + ListScope D(W, "NeededLibraries"); + + using LibsTy = std::vector<StringRef>; + LibsTy Libs; + + for (const auto &Command : Obj->load_commands()) { + if (Command.C.cmd == MachO::LC_LOAD_DYLIB || + Command.C.cmd == MachO::LC_ID_DYLIB || + Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || + Command.C.cmd == MachO::LC_REEXPORT_DYLIB || + Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || + Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { + MachO::dylib_command Dl = Obj->getDylibIDLoadCommand(Command); + if (Dl.dylib.name < Dl.cmdsize) { + auto *P = static_cast<const char*>(Command.Ptr) + Dl.dylib.name; + Libs.push_back(P); + } + } + } + + llvm::stable_sort(Libs); + + for (const auto &L : Libs) { + W.startLine() << L << "\n"; + } +} + +void MachODumper::printMachODataInCode() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_DATA_IN_CODE) { + MachO::linkedit_data_command LLC = Obj->getLinkeditDataLoadCommand(Load); + DictScope Group(W, "DataInCode"); + W.printNumber("Data offset", LLC.dataoff); + W.printNumber("Data size", LLC.datasize); + ListScope D(W, "Data entries"); + unsigned NumRegions = LLC.datasize / sizeof(MachO::data_in_code_entry); + for (unsigned i = 0; i < NumRegions; ++i) { + MachO::data_in_code_entry DICE = Obj->getDataInCodeTableEntry( + LLC.dataoff, i); + DictScope Group(W, "Entry"); + W.printNumber("Index", i); + W.printNumber("Offset", DICE.offset); + W.printNumber("Length", DICE.length); + W.printNumber("Kind", DICE.kind); + } + } + } +} + +void MachODumper::printMachOVersionMin() { + for (const auto &Load : Obj->load_commands()) { + StringRef Cmd; + switch (Load.C.cmd) { + case MachO::LC_VERSION_MIN_MACOSX: + Cmd = "LC_VERSION_MIN_MACOSX"; + break; + case MachO::LC_VERSION_MIN_IPHONEOS: + Cmd = "LC_VERSION_MIN_IPHONEOS"; + break; + case MachO::LC_VERSION_MIN_TVOS: + Cmd = "LC_VERSION_MIN_TVOS"; + break; + case MachO::LC_VERSION_MIN_WATCHOS: + Cmd = "LC_VERSION_MIN_WATCHOS"; + break; + case MachO::LC_BUILD_VERSION: + Cmd = "LC_BUILD_VERSION"; + break; + default: + continue; + } + + DictScope Group(W, "MinVersion"); + // Handle LC_BUILD_VERSION. + if (Load.C.cmd == MachO::LC_BUILD_VERSION) { + MachO::build_version_command BVC = Obj->getBuildVersionLoadCommand(Load); + W.printString("Cmd", Cmd); + W.printNumber("Size", BVC.cmdsize); + W.printString("Platform", + MachOObjectFile::getBuildPlatform(BVC.platform)); + W.printString("Version", MachOObjectFile::getVersionString(BVC.minos)); + if (BVC.sdk) + W.printString("SDK", MachOObjectFile::getVersionString(BVC.sdk)); + else + W.printString("SDK", StringRef("n/a")); + continue; + } + + MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load); + W.printString("Cmd", Cmd); + W.printNumber("Size", VMC.cmdsize); + SmallString<32> Version; + Version = utostr(MachOObjectFile::getVersionMinMajor(VMC, false)) + "." + + utostr(MachOObjectFile::getVersionMinMinor(VMC, false)); + uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, false); + if (Update != 0) + Version += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, false)); + W.printString("Version", Version); + SmallString<32> SDK; + if (VMC.sdk == 0) + SDK = "n/a"; + else { + SDK = utostr(MachOObjectFile::getVersionMinMajor(VMC, true)) + "." + + utostr(MachOObjectFile::getVersionMinMinor(VMC, true)); + uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, true); + if (Update != 0) + SDK += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, true)); + } + W.printString("SDK", SDK); + } +} + +void MachODumper::printMachODysymtab() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_DYSYMTAB) { + MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand(); + DictScope Group(W, "Dysymtab"); + W.printNumber("ilocalsym", DLC.ilocalsym); + W.printNumber("nlocalsym", DLC.nlocalsym); + W.printNumber("iextdefsym", DLC.iextdefsym); + W.printNumber("nextdefsym", DLC.nextdefsym); + W.printNumber("iundefsym", DLC.iundefsym); + W.printNumber("nundefsym", DLC.nundefsym); + W.printNumber("tocoff", DLC.tocoff); + W.printNumber("ntoc", DLC.ntoc); + W.printNumber("modtaboff", DLC.modtaboff); + W.printNumber("nmodtab", DLC.nmodtab); + W.printNumber("extrefsymoff", DLC.extrefsymoff); + W.printNumber("nextrefsyms", DLC.nextrefsyms); + W.printNumber("indirectsymoff", DLC.indirectsymoff); + W.printNumber("nindirectsyms", DLC.nindirectsyms); + W.printNumber("extreloff", DLC.extreloff); + W.printNumber("nextrel", DLC.nextrel); + W.printNumber("locreloff", DLC.locreloff); + W.printNumber("nlocrel", DLC.nlocrel); + } + } +} + +void MachODumper::printMachOSegment() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_SEGMENT || Load.C.cmd == MachO::LC_SEGMENT_64) { + MachOSegment MOSegment; + getSegment(Obj, Load, MOSegment); + DictScope Group(W, "Segment"); + W.printString("Cmd", MOSegment.CmdName); + W.printString("Name", MOSegment.SegName); + W.printNumber("Size", MOSegment.cmdsize); + W.printHex("vmaddr", MOSegment.vmaddr); + W.printHex("vmsize", MOSegment.vmsize); + W.printNumber("fileoff", MOSegment.fileoff); + W.printNumber("filesize", MOSegment.filesize); + W.printString("maxprot", getMask(MOSegment.maxprot)); + W.printString("initprot", getMask(MOSegment.initprot)); + W.printNumber("nsects", MOSegment.nsects); + W.printHex("flags", MOSegment.flags); + } + } +} + +void MachODumper::printMachOIndirectSymbols() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_DYSYMTAB) { + MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand(); + DictScope Group(W, "Indirect Symbols"); + W.printNumber("Number", DLC.nindirectsyms); + ListScope D(W, "Symbols"); + for (unsigned i = 0; i < DLC.nindirectsyms; ++i) { + DictScope Group(W, "Entry"); + W.printNumber("Entry Index", i); + W.printHex("Symbol Index", Obj->getIndirectSymbolTableEntry(DLC, i)); + } + } + } +} + +void MachODumper::printMachOLinkerOptions() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_LINKER_OPTION) { + MachO::linker_option_command LOLC = Obj->getLinkerOptionLoadCommand(Load); + DictScope Group(W, "Linker Options"); + W.printNumber("Size", LOLC.cmdsize); + ListScope D(W, "Strings"); + uint64_t DataSize = LOLC.cmdsize - sizeof(MachO::linker_option_command); + const char *P = Load.Ptr + sizeof(MachO::linker_option_command); + StringRef Data(P, DataSize); + for (unsigned i = 0; i < LOLC.count; ++i) { + std::pair<StringRef,StringRef> Split = Data.split('\0'); + W.printString("Value", Split.first); + Data = Split.second; + } + } + } +} diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp new file mode 100644 index 000000000000..9e5ebd99ac37 --- /dev/null +++ b/llvm/tools/llvm-readobj/ObjDumper.cpp @@ -0,0 +1,172 @@ +//===-- ObjDumper.cpp - Base dumper class -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements ObjDumper. +/// +//===----------------------------------------------------------------------===// + +#include "ObjDumper.h" +#include "Error.h" +#include "llvm-readobj.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" +#include <map> + +namespace llvm { + +static inline Error createError(const Twine &Msg) { + return createStringError(object::object_error::parse_failed, Msg); +} + +ObjDumper::ObjDumper(ScopedPrinter &Writer) : W(Writer) {} + +ObjDumper::~ObjDumper() { +} + +static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) { + for (size_t i = 0; i < Len; i++) + W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.'); +} + +static std::vector<object::SectionRef> +getSectionRefsByNameOrIndex(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections) { + std::vector<object::SectionRef> Ret; + std::map<std::string, bool> SecNames; + std::map<unsigned, bool> SecIndices; + unsigned SecIndex; + for (StringRef Section : Sections) { + if (!Section.getAsInteger(0, SecIndex)) + SecIndices.emplace(SecIndex, false); + else + SecNames.emplace(Section, false); + } + + SecIndex = Obj->isELF() ? 0 : 1; + for (object::SectionRef SecRef : Obj->sections()) { + StringRef SecName = unwrapOrError(Obj->getFileName(), SecRef.getName()); + auto NameIt = SecNames.find(SecName); + if (NameIt != SecNames.end()) + NameIt->second = true; + auto IndexIt = SecIndices.find(SecIndex); + if (IndexIt != SecIndices.end()) + IndexIt->second = true; + if (NameIt != SecNames.end() || IndexIt != SecIndices.end()) + Ret.push_back(SecRef); + SecIndex++; + } + + for (const std::pair<std::string, bool> &S : SecNames) + if (!S.second) + reportWarning( + createError(formatv("could not find section '{0}'", S.first).str()), + Obj->getFileName()); + + for (std::pair<unsigned, bool> S : SecIndices) + if (!S.second) + reportWarning( + createError(formatv("could not find section {0}", S.first).str()), + Obj->getFileName()); + + return Ret; +} + +void ObjDumper::printSectionsAsString(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections) { + bool First = true; + for (object::SectionRef Section : + getSectionRefsByNameOrIndex(Obj, Sections)) { + StringRef SectionName = + unwrapOrError(Obj->getFileName(), Section.getName()); + + if (!First) + W.startLine() << '\n'; + First = false; + W.startLine() << "String dump of section '" << SectionName << "':\n"; + + StringRef SectionContent = + unwrapOrError(Obj->getFileName(), Section.getContents()); + + const uint8_t *SecContent = SectionContent.bytes_begin(); + const uint8_t *CurrentWord = SecContent; + const uint8_t *SecEnd = SectionContent.bytes_end(); + + while (CurrentWord <= SecEnd) { + size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord), + SecEnd - CurrentWord); + if (!WordSize) { + CurrentWord++; + continue; + } + W.startLine() << format("[%6tx] ", CurrentWord - SecContent); + printAsPrintable(W.startLine(), CurrentWord, WordSize); + W.startLine() << '\n'; + CurrentWord += WordSize + 1; + } + } +} + +void ObjDumper::printSectionsAsHex(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections) { + bool First = true; + for (object::SectionRef Section : + getSectionRefsByNameOrIndex(Obj, Sections)) { + StringRef SectionName = + unwrapOrError(Obj->getFileName(), Section.getName()); + + if (!First) + W.startLine() << '\n'; + First = false; + W.startLine() << "Hex dump of section '" << SectionName << "':\n"; + + StringRef SectionContent = + unwrapOrError(Obj->getFileName(), Section.getContents()); + const uint8_t *SecContent = SectionContent.bytes_begin(); + const uint8_t *SecEnd = SecContent + SectionContent.size(); + + for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) { + const uint8_t *TmpSecPtr = SecPtr; + uint8_t i; + uint8_t k; + + W.startLine() << format_hex(Section.getAddress() + (SecPtr - SecContent), + 10); + W.startLine() << ' '; + for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) { + for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) { + uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr)); + W.startLine() << format_hex_no_prefix(Val, 2); + } + W.startLine() << ' '; + } + + // We need to print the correct amount of spaces to match the format. + // We are adding the (4 - i) last rows that are 8 characters each. + // Then, the (4 - i) spaces that are in between the rows. + // Least, if we cut in a middle of a row, we add the remaining characters, + // which is (8 - (k * 2)). + if (i < 4) + W.startLine() << format("%*c", (4 - i) * 8 + (4 - i) + (8 - (k * 2)), + ' '); + + TmpSecPtr = SecPtr; + for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i) + W.startLine() << (isPrint(TmpSecPtr[i]) + ? static_cast<char>(TmpSecPtr[i]) + : '.'); + + W.startLine() << '\n'; + } + } +} + +} // namespace llvm diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h new file mode 100644 index 000000000000..2ba441342499 --- /dev/null +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -0,0 +1,144 @@ +//===-- ObjDumper.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_OBJDUMPER_H +#define LLVM_TOOLS_LLVM_READOBJ_OBJDUMPER_H + +#include <memory> +#include <system_error> + +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" + +namespace llvm { +namespace object { +class COFFImportFile; +class ObjectFile; +} +namespace codeview { +class GlobalTypeTableBuilder; +class MergingTypeTableBuilder; +} // namespace codeview + +class ScopedPrinter; + +class ObjDumper { +public: + ObjDumper(ScopedPrinter &Writer); + virtual ~ObjDumper(); + + virtual void printFileHeaders() = 0; + virtual void printSectionHeaders() = 0; + virtual void printRelocations() = 0; + virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) { + if (PrintSymbols) + printSymbols(); + if (PrintDynamicSymbols) + printDynamicSymbols(); + } + virtual void printProgramHeaders(bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(); + if (PrintSectionMapping == cl::BOU_TRUE) + printSectionMapping(); + } + + virtual void printUnwindInfo() = 0; + + // Only implemented for ELF at this time. + virtual void printDynamicRelocations() { } + virtual void printDynamicTable() { } + virtual void printNeededLibraries() { } + virtual void printSectionAsHex(StringRef SectionName) {} + virtual void printHashTable() { } + virtual void printGnuHashTable() { } + virtual void printHashSymbols() {} + virtual void printLoadName() {} + virtual void printVersionInfo() {} + virtual void printGroupSections() {} + virtual void printHashHistogram() {} + virtual void printCGProfile() {} + virtual void printAddrsig() {} + virtual void printNotes() {} + virtual void printELFLinkerOptions() {} + virtual void printStackSizes() {} + virtual void printArchSpecificInfo() { } + + // Only implemented for PE/COFF. + virtual void printCOFFImports() { } + virtual void printCOFFExports() { } + virtual void printCOFFDirectives() { } + virtual void printCOFFBaseReloc() { } + virtual void printCOFFDebugDirectory() { } + virtual void printCOFFResources() {} + virtual void printCOFFLoadConfig() { } + virtual void printCodeViewDebugInfo() { } + virtual void + mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs, + llvm::codeview::MergingTypeTableBuilder &CVTypes, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVIDs, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVTypes, + bool GHash) {} + + // Only implemented for MachO. + virtual void printMachODataInCode() { } + virtual void printMachOVersionMin() { } + virtual void printMachODysymtab() { } + virtual void printMachOSegment() { } + virtual void printMachOIndirectSymbols() { } + virtual void printMachOLinkerOptions() { } + + virtual void printStackMap() const = 0; + + void printSectionsAsString(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections); + void printSectionsAsHex(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections); + +protected: + ScopedPrinter &W; + +private: + virtual void printSymbols() {} + virtual void printDynamicSymbols() {} + virtual void printProgramHeaders() {} + virtual void printSectionMapping() {} +}; + +std::error_code createCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + +std::error_code createELFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + +std::error_code createMachODumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + +std::error_code createWasmDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + +std::error_code createXCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + +void dumpCOFFImportFile(const object::COFFImportFile *File, + ScopedPrinter &Writer); + +void dumpCodeViewMergedTypes(ScopedPrinter &Writer, + ArrayRef<ArrayRef<uint8_t>> IpiRecords, + ArrayRef<ArrayRef<uint8_t>> TpiRecords); + +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-readobj/StackMapPrinter.h b/llvm/tools/llvm-readobj/StackMapPrinter.h new file mode 100644 index 000000000000..ef7575640268 --- /dev/null +++ b/llvm/tools/llvm-readobj/StackMapPrinter.h @@ -0,0 +1,81 @@ +//===-------- StackMapPrinter.h - Pretty-print stackmaps --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_STACKMAPPRINTER_H +#define LLVM_TOOLS_LLVM_READOBJ_STACKMAPPRINTER_H + +#include "llvm/Object/StackMapParser.h" +#include "llvm/Support/ScopedPrinter.h" + +namespace llvm { + +// Pretty print a stackmap to the given ostream. +template <typename StackMapParserT> +void prettyPrintStackMap(ScopedPrinter &W, const StackMapParserT &SMP) { + + W.printNumber("LLVM StackMap Version", SMP.getVersion()); + W.printNumber("Num Functions", SMP.getNumFunctions()); + + // Functions: + for (const auto &F : SMP.functions()) + W.startLine() << " Function address: " << F.getFunctionAddress() + << ", stack size: " << F.getStackSize() + << ", callsite record count: " << F.getRecordCount() << "\n"; + + // Constants: + W.printNumber("Num Constants", SMP.getNumConstants()); + unsigned ConstantIndex = 0; + for (const auto &C : SMP.constants()) + W.startLine() << " #" << ++ConstantIndex << ": " << C.getValue() << "\n"; + + // Records: + W.printNumber("Num Records", SMP.getNumRecords()); + for (const auto &R : SMP.records()) { + W.startLine() << " Record ID: " << R.getID() + << ", instruction offset: " << R.getInstructionOffset() + << "\n"; + W.startLine() << " " << R.getNumLocations() << " locations:\n"; + + unsigned LocationIndex = 0; + for (const auto &Loc : R.locations()) { + raw_ostream &OS = W.startLine(); + OS << " #" << ++LocationIndex << ": "; + switch (Loc.getKind()) { + case StackMapParserT::LocationKind::Register: + OS << "Register R#" << Loc.getDwarfRegNum(); + break; + case StackMapParserT::LocationKind::Direct: + OS << "Direct R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset(); + break; + case StackMapParserT::LocationKind::Indirect: + OS << "Indirect [R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset() + << "]"; + break; + case StackMapParserT::LocationKind::Constant: + OS << "Constant " << Loc.getSmallConstant(); + break; + case StackMapParserT::LocationKind::ConstantIndex: + OS << "ConstantIndex #" << Loc.getConstantIndex() << " (" + << SMP.getConstant(Loc.getConstantIndex()).getValue() << ")"; + break; + } + OS << ", size: " << Loc.getSizeInBytes() << "\n"; + } + + raw_ostream &OS = W.startLine(); + OS << " " << R.getNumLiveOuts() << " live-outs: [ "; + for (const auto &LO : R.liveouts()) + OS << "R#" << LO.getDwarfRegNum() << " (" + << LO.getSizeInBytes() << "-bytes) "; + OS << "]\n"; + } +} + +} + +#endif diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp new file mode 100644 index 000000000000..dfab9f40d71b --- /dev/null +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -0,0 +1,256 @@ +//===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Wasm-specific dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace object; + +namespace { + +static const EnumEntry<unsigned> WasmSymbolTypes[] = { +#define ENUM_ENTRY(X) \ + { #X, wasm::WASM_SYMBOL_TYPE_##X } + ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), + ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), +#undef ENUM_ENTRY +}; + +static const EnumEntry<uint32_t> WasmSectionTypes[] = { +#define ENUM_ENTRY(X) \ + { #X, wasm::WASM_SEC_##X } + ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), + ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), + ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT), + ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), + ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), +#undef ENUM_ENTRY +}; + +static const EnumEntry<unsigned> WasmSymbolFlags[] = { +#define ENUM_ENTRY(X) \ + { #X, wasm::WASM_SYMBOL_##X } + ENUM_ENTRY(BINDING_GLOBAL), + ENUM_ENTRY(BINDING_WEAK), + ENUM_ENTRY(BINDING_LOCAL), + ENUM_ENTRY(VISIBILITY_DEFAULT), + ENUM_ENTRY(VISIBILITY_HIDDEN), + ENUM_ENTRY(UNDEFINED), + ENUM_ENTRY(EXPORTED), + ENUM_ENTRY(EXPLICIT_NAME), + ENUM_ENTRY(NO_STRIP), +#undef ENUM_ENTRY +}; + +class WasmDumper : public ObjDumper { +public: + WasmDumper(const WasmObjectFile *Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj) {} + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printUnwindInfo() override { llvm_unreachable("unimplemented"); } + void printStackMap() const override { llvm_unreachable("unimplemented"); } + +protected: + void printSymbol(const SymbolRef &Sym); + void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); + +private: + void printSymbols() override; + void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } + + const WasmObjectFile *Obj; +}; + +void WasmDumper::printFileHeaders() { + W.printHex("Version", Obj->getHeader().Version); +} + +void WasmDumper::printRelocation(const SectionRef &Section, + const RelocationRef &Reloc) { + SmallString<64> RelocTypeName; + uint64_t RelocType = Reloc.getType(); + Reloc.getTypeName(RelocTypeName); + const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc); + + StringRef SymName; + symbol_iterator SI = Reloc.getSymbol(); + if (SI != Obj->symbol_end()) + SymName = unwrapOrError(Obj->getFileName(), SI->getName()); + + bool HasAddend = false; + switch (RelocType) { + case wasm::R_WASM_MEMORY_ADDR_LEB: + case wasm::R_WASM_MEMORY_ADDR_SLEB: + case wasm::R_WASM_MEMORY_ADDR_I32: + case wasm::R_WASM_FUNCTION_OFFSET_I32: + case wasm::R_WASM_SECTION_OFFSET_I32: + HasAddend = true; + break; + default: + break; + } + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printNumber("Type", RelocTypeName, RelocType); + W.printHex("Offset", Reloc.getOffset()); + if (!SymName.empty()) + W.printString("Symbol", SymName); + else + W.printHex("Index", WasmReloc.Index); + if (HasAddend) + W.printNumber("Addend", WasmReloc.Addend); + } else { + raw_ostream &OS = W.startLine(); + OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " "; + if (!SymName.empty()) + OS << SymName; + else + OS << WasmReloc.Index; + if (HasAddend) + OS << " " << WasmReloc.Addend; + OS << "\n"; + } +} + +void WasmDumper::printRelocations() { + ListScope D(W, "Relocations"); + + int SectionNumber = 0; + for (const SectionRef &Section : Obj->sections()) { + bool PrintedGroup = false; + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); + + ++SectionNumber; + + for (const RelocationRef &Reloc : Section.relocations()) { + if (!PrintedGroup) { + W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; + W.indent(); + PrintedGroup = true; + } + + printRelocation(Section, Reloc); + } + + if (PrintedGroup) { + W.unindent(); + W.startLine() << "}\n"; + } + } +} + +void WasmDumper::printSymbols() { + ListScope Group(W, "Symbols"); + + for (const SymbolRef &Symbol : Obj->symbols()) + printSymbol(Symbol); +} + +void WasmDumper::printSectionHeaders() { + ListScope Group(W, "Sections"); + for (const SectionRef &Section : Obj->sections()) { + const WasmSection &WasmSec = Obj->getWasmSection(Section); + DictScope SectionD(W, "Section"); + W.printEnum("Type", WasmSec.Type, makeArrayRef(WasmSectionTypes)); + W.printNumber("Size", static_cast<uint64_t>(WasmSec.Content.size())); + W.printNumber("Offset", WasmSec.Offset); + switch (WasmSec.Type) { + case wasm::WASM_SEC_CUSTOM: + W.printString("Name", WasmSec.Name); + if (WasmSec.Name == "linking") { + const wasm::WasmLinkingData &LinkingData = Obj->linkingData(); + if (!LinkingData.InitFunctions.empty()) { + ListScope Group(W, "InitFunctions"); + for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions) + W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n"; + } + } + break; + case wasm::WASM_SEC_DATA: { + ListScope Group(W, "Segments"); + for (const WasmSegment &Segment : Obj->dataSegments()) { + const wasm::WasmDataSegment &Seg = Segment.Data; + DictScope Group(W, "Segment"); + if (!Seg.Name.empty()) + W.printString("Name", Seg.Name); + W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size())); + if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST) + W.printNumber("Offset", Seg.Offset.Value.Int32); + } + break; + } + case wasm::WASM_SEC_MEMORY: + ListScope Group(W, "Memories"); + for (const wasm::WasmLimits &Memory : Obj->memories()) { + DictScope Group(W, "Memory"); + W.printNumber("InitialPages", Memory.Initial); + if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { + W.printNumber("MaxPages", WasmSec.Offset); + } + } + break; + } + + if (opts::SectionRelocations) { + ListScope D(W, "Relocations"); + for (const RelocationRef &Reloc : Section.relocations()) + printRelocation(Section, Reloc); + } + + if (opts::SectionData) { + W.printBinaryBlock("SectionData", WasmSec.Content); + } + } +} + +void WasmDumper::printSymbol(const SymbolRef &Sym) { + DictScope D(W, "Symbol"); + WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); + W.printString("Name", Symbol.Info.Name); + W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes)); + W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags)); + + if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { + W.printString("ImportName", Symbol.Info.ImportName); + W.printString("ImportModule", Symbol.Info.ImportModule); + } + if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { + W.printHex("ElementIndex", Symbol.Info.ElementIndex); + } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { + W.printHex("Offset", Symbol.Info.DataRef.Offset); + W.printHex("Segment", Symbol.Info.DataRef.Segment); + W.printHex("Size", Symbol.Info.DataRef.Size); + } +} + +} // namespace + +namespace llvm { + +std::error_code createWasmDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + const auto *WasmObj = dyn_cast<WasmObjectFile>(Obj); + assert(WasmObj && "createWasmDumper called with non-wasm object"); + + Result.reset(new WasmDumper(WasmObj, Writer)); + return readobj_error::success; +} + +} // namespace llvm diff --git a/llvm/tools/llvm-readobj/Win64EHDumper.cpp b/llvm/tools/llvm-readobj/Win64EHDumper.cpp new file mode 100644 index 000000000000..fa268ce9d434 --- /dev/null +++ b/llvm/tools/llvm-readobj/Win64EHDumper.cpp @@ -0,0 +1,340 @@ +//===- Win64EHDumper.cpp - Win64 EH Printer ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Win64EHDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::Win64EH; + +static const EnumEntry<unsigned> UnwindFlags[] = { + { "ExceptionHandler", UNW_ExceptionHandler }, + { "TerminateHandler", UNW_TerminateHandler }, + { "ChainInfo" , UNW_ChainInfo } +}; + +static const EnumEntry<unsigned> UnwindOpInfo[] = { + { "RAX", 0 }, + { "RCX", 1 }, + { "RDX", 2 }, + { "RBX", 3 }, + { "RSP", 4 }, + { "RBP", 5 }, + { "RSI", 6 }, + { "RDI", 7 }, + { "R8", 8 }, + { "R9", 9 }, + { "R10", 10 }, + { "R11", 11 }, + { "R12", 12 }, + { "R13", 13 }, + { "R14", 14 }, + { "R15", 15 } +}; + +static uint64_t getOffsetOfLSDA(const UnwindInfo& UI) { + return static_cast<const char*>(UI.getLanguageSpecificData()) + - reinterpret_cast<const char*>(&UI); +} + +static uint32_t getLargeSlotValue(ArrayRef<UnwindCode> UC) { + if (UC.size() < 3) + return 0; + return UC[1].FrameOffset + (static_cast<uint32_t>(UC[2].FrameOffset) << 16); +} + +// Returns the name of the unwind code. +static StringRef getUnwindCodeTypeName(uint8_t Code) { + switch (Code) { + default: llvm_unreachable("Invalid unwind code"); + case UOP_PushNonVol: return "PUSH_NONVOL"; + case UOP_AllocLarge: return "ALLOC_LARGE"; + case UOP_AllocSmall: return "ALLOC_SMALL"; + case UOP_SetFPReg: return "SET_FPREG"; + case UOP_SaveNonVol: return "SAVE_NONVOL"; + case UOP_SaveNonVolBig: return "SAVE_NONVOL_FAR"; + case UOP_SaveXMM128: return "SAVE_XMM128"; + case UOP_SaveXMM128Big: return "SAVE_XMM128_FAR"; + case UOP_PushMachFrame: return "PUSH_MACHFRAME"; + } +} + +// Returns the name of a referenced register. +static StringRef getUnwindRegisterName(uint8_t Reg) { + switch (Reg) { + default: llvm_unreachable("Invalid register"); + case 0: return "RAX"; + case 1: return "RCX"; + case 2: return "RDX"; + case 3: return "RBX"; + case 4: return "RSP"; + case 5: return "RBP"; + case 6: return "RSI"; + case 7: return "RDI"; + case 8: return "R8"; + case 9: return "R9"; + case 10: return "R10"; + case 11: return "R11"; + case 12: return "R12"; + case 13: return "R13"; + case 14: return "R14"; + case 15: return "R15"; + } +} + +// Calculates the number of array slots required for the unwind code. +static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) { + switch (UnwindCode.getUnwindOp()) { + default: llvm_unreachable("Invalid unwind code"); + case UOP_PushNonVol: + case UOP_AllocSmall: + case UOP_SetFPReg: + case UOP_PushMachFrame: + return 1; + case UOP_SaveNonVol: + case UOP_SaveXMM128: + return 2; + case UOP_SaveNonVolBig: + case UOP_SaveXMM128Big: + return 3; + case UOP_AllocLarge: + return (UnwindCode.getOpInfo() == 0) ? 2 : 3; + } +} + +static std::string formatSymbol(const Dumper::Context &Ctx, + const coff_section *Section, uint64_t Offset, + uint32_t Displacement) { + std::string Buffer; + raw_string_ostream OS(Buffer); + + SymbolRef Symbol; + if (!Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) { + Expected<StringRef> Name = Symbol.getName(); + if (Name) { + OS << *Name; + if (Displacement > 0) + OS << format(" +0x%X (0x%" PRIX64 ")", Displacement, Offset); + else + OS << format(" (0x%" PRIX64 ")", Offset); + return OS.str(); + } else { + // TODO: Actually report errors helpfully. + consumeError(Name.takeError()); + } + } + + OS << format(" (0x%" PRIX64 ")", Offset); + return OS.str(); +} + +static std::error_code resolveRelocation(const Dumper::Context &Ctx, + const coff_section *Section, + uint64_t Offset, + const coff_section *&ResolvedSection, + uint64_t &ResolvedAddress) { + SymbolRef Symbol; + if (std::error_code EC = + Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) + return EC; + + Expected<uint64_t> ResolvedAddressOrErr = Symbol.getAddress(); + if (!ResolvedAddressOrErr) + return errorToErrorCode(ResolvedAddressOrErr.takeError()); + ResolvedAddress = *ResolvedAddressOrErr; + + Expected<section_iterator> SI = Symbol.getSection(); + if (!SI) + return errorToErrorCode(SI.takeError()); + ResolvedSection = Ctx.COFF.getCOFFSection(**SI); + return std::error_code(); +} + +namespace llvm { +namespace Win64EH { +void Dumper::printRuntimeFunctionEntry(const Context &Ctx, + const coff_section *Section, + uint64_t Offset, + const RuntimeFunction &RF) { + SW.printString("StartAddress", + formatSymbol(Ctx, Section, Offset + 0, RF.StartAddress)); + SW.printString("EndAddress", + formatSymbol(Ctx, Section, Offset + 4, RF.EndAddress)); + SW.printString("UnwindInfoAddress", + formatSymbol(Ctx, Section, Offset + 8, RF.UnwindInfoOffset)); +} + +// Prints one unwind code. Because an unwind code can occupy up to 3 slots in +// the unwind codes array, this function requires that the correct number of +// slots is provided. +void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) { + assert(UC.size() >= getNumUsedSlots(UC[0])); + + SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset)) + << getUnwindCodeTypeName(UC[0].getUnwindOp()); + + switch (UC[0].getUnwindOp()) { + case UOP_PushNonVol: + OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo()); + break; + + case UOP_AllocLarge: + OS << " size=" + << ((UC[0].getOpInfo() == 0) ? UC[1].FrameOffset * 8 + : getLargeSlotValue(UC)); + break; + + case UOP_AllocSmall: + OS << " size=" << (UC[0].getOpInfo() + 1) * 8; + break; + + case UOP_SetFPReg: + if (UI.getFrameRegister() == 0) + OS << " reg=<invalid>"; + else + OS << " reg=" << getUnwindRegisterName(UI.getFrameRegister()) + << format(", offset=0x%X", UI.getFrameOffset() * 16); + break; + + case UOP_SaveNonVol: + OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo()) + << format(", offset=0x%X", UC[1].FrameOffset * 8); + break; + + case UOP_SaveNonVolBig: + OS << " reg=" << getUnwindRegisterName(UC[0].getOpInfo()) + << format(", offset=0x%X", getLargeSlotValue(UC)); + break; + + case UOP_SaveXMM128: + OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo()) + << format(", offset=0x%X", UC[1].FrameOffset * 16); + break; + + case UOP_SaveXMM128Big: + OS << " reg=XMM" << static_cast<uint32_t>(UC[0].getOpInfo()) + << format(", offset=0x%X", getLargeSlotValue(UC)); + break; + + case UOP_PushMachFrame: + OS << " errcode=" << (UC[0].getOpInfo() == 0 ? "no" : "yes"); + break; + } + + OS << "\n"; +} + +void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section, + off_t Offset, const UnwindInfo &UI) { + DictScope UIS(SW, "UnwindInfo"); + SW.printNumber("Version", UI.getVersion()); + SW.printFlags("Flags", UI.getFlags(), makeArrayRef(UnwindFlags)); + SW.printNumber("PrologSize", UI.PrologSize); + if (UI.getFrameRegister()) { + SW.printEnum("FrameRegister", UI.getFrameRegister(), + makeArrayRef(UnwindOpInfo)); + SW.printHex("FrameOffset", UI.getFrameOffset()); + } else { + SW.printString("FrameRegister", StringRef("-")); + SW.printString("FrameOffset", StringRef("-")); + } + + SW.printNumber("UnwindCodeCount", UI.NumCodes); + { + ListScope UCS(SW, "UnwindCodes"); + ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes); + for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) { + unsigned UsedSlots = getNumUsedSlots(*UCI); + if (UsedSlots > UC.size()) { + errs() << "corrupt unwind data"; + return; + } + + printUnwindCode(UI, makeArrayRef(UCI, UCE)); + UCI = UCI + UsedSlots - 1; + } + } + + uint64_t LSDAOffset = Offset + getOffsetOfLSDA(UI); + if (UI.getFlags() & (UNW_ExceptionHandler | UNW_TerminateHandler)) { + SW.printString("Handler", + formatSymbol(Ctx, Section, LSDAOffset, + UI.getLanguageSpecificHandlerOffset())); + } else if (UI.getFlags() & UNW_ChainInfo) { + if (const RuntimeFunction *Chained = UI.getChainedFunctionEntry()) { + DictScope CS(SW, "Chained"); + printRuntimeFunctionEntry(Ctx, Section, LSDAOffset, *Chained); + } + } +} + +void Dumper::printRuntimeFunction(const Context &Ctx, + const coff_section *Section, + uint64_t SectionOffset, + const RuntimeFunction &RF) { + DictScope RFS(SW, "RuntimeFunction"); + printRuntimeFunctionEntry(Ctx, Section, SectionOffset, RF); + + const coff_section *XData; + uint64_t Offset; + resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset); + + ArrayRef<uint8_t> Contents; + if (Error E = Ctx.COFF.getSectionContents(XData, Contents)) + reportError(std::move(E), Ctx.COFF.getFileName()); + + if (Contents.empty()) + return; + + Offset = Offset + RF.UnwindInfoOffset; + if (Offset > Contents.size()) + return; + + const auto UI = reinterpret_cast<const UnwindInfo*>(Contents.data() + Offset); + printUnwindInfo(Ctx, XData, Offset, *UI); +} + +void Dumper::printData(const Context &Ctx) { + for (const auto &Section : Ctx.COFF.sections()) { + StringRef Name; + if (Expected<StringRef> NameOrErr = Section.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name != ".pdata" && !Name.startswith(".pdata$")) + continue; + + const coff_section *PData = Ctx.COFF.getCOFFSection(Section); + ArrayRef<uint8_t> Contents; + + if (Error E = Ctx.COFF.getSectionContents(PData, Contents)) + reportError(std::move(E), Ctx.COFF.getFileName()); + if (Contents.empty()) + continue; + + const RuntimeFunction *Entries = + reinterpret_cast<const RuntimeFunction *>(Contents.data()); + const size_t Count = Contents.size() / sizeof(RuntimeFunction); + ArrayRef<RuntimeFunction> RuntimeFunctions(Entries, Count); + + size_t Index = 0; + for (const auto &RF : RuntimeFunctions) { + printRuntimeFunction(Ctx, Ctx.COFF.getCOFFSection(Section), + Index * sizeof(RuntimeFunction), RF); + ++Index; + } + } +} +} +} + diff --git a/llvm/tools/llvm-readobj/Win64EHDumper.h b/llvm/tools/llvm-readobj/Win64EHDumper.h new file mode 100644 index 000000000000..97458c916bec --- /dev/null +++ b/llvm/tools/llvm-readobj/Win64EHDumper.h @@ -0,0 +1,62 @@ +//===- Win64EHDumper.h - Win64 EH Printing ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H +#define LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H + +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Win64EH.h" + +namespace llvm { +namespace object { +class COFFObjectFile; +class SymbolRef; +struct coff_section; +} + +namespace Win64EH { +class Dumper { + ScopedPrinter &SW; + raw_ostream &OS; + +public: + typedef std::error_code (*SymbolResolver)(const object::coff_section *, + uint64_t, object::SymbolRef &, + void *); + + struct Context { + const object::COFFObjectFile &COFF; + SymbolResolver ResolveSymbol; + void *UserData; + + Context(const object::COFFObjectFile &COFF, SymbolResolver Resolver, + void *UserData) + : COFF(COFF), ResolveSymbol(Resolver), UserData(UserData) {} + }; + +private: + void printRuntimeFunctionEntry(const Context &Ctx, + const object::coff_section *Section, + uint64_t SectionOffset, + const RuntimeFunction &RF); + void printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC); + void printUnwindInfo(const Context &Ctx, const object::coff_section *Section, + off_t Offset, const UnwindInfo &UI); + void printRuntimeFunction(const Context &Ctx, + const object::coff_section *Section, + uint64_t SectionOffset, const RuntimeFunction &RF); + +public: + Dumper(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {} + + void printData(const Context &Ctx); +}; +} +} + +#endif diff --git a/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp b/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp new file mode 100644 index 000000000000..a2fb6aac3f93 --- /dev/null +++ b/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp @@ -0,0 +1,85 @@ +//===-- WindowsResourceDumper.cpp - Windows Resource printer --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Windows resource (.res) dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "WindowsResourceDumper.h" +#include "Error.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ScopedPrinter.h" + +namespace llvm { +namespace object { +namespace WindowsRes { + +std::string stripUTF16(const ArrayRef<UTF16> &UTF16Str) { + std::string Result; + Result.reserve(UTF16Str.size()); + + for (UTF16 Ch : UTF16Str) { + // UTF16Str will have swapped byte order in case of big-endian machines. + // Swap it back in such a case. + uint16_t ChValue = support::endian::byte_swap(Ch, support::little); + if (ChValue <= 0xFF) + Result += ChValue; + else + Result += '?'; + } + return Result; +} + +Error Dumper::printData() { + auto EntryPtrOrErr = WinRes->getHeadEntry(); + if (!EntryPtrOrErr) + return EntryPtrOrErr.takeError(); + auto EntryPtr = *EntryPtrOrErr; + + bool IsEnd = false; + while (!IsEnd) { + printEntry(EntryPtr); + + if (auto Err = EntryPtr.moveNext(IsEnd)) + return Err; + } + return Error::success(); +} + +void Dumper::printEntry(const ResourceEntryRef &Ref) { + if (Ref.checkTypeString()) { + auto NarrowStr = stripUTF16(Ref.getTypeString()); + SW.printString("Resource type (string)", NarrowStr); + } else { + SmallString<20> IDStr; + raw_svector_ostream OS(IDStr); + printResourceTypeName(Ref.getTypeID(), OS); + SW.printString("Resource type (int)", IDStr); + } + + if (Ref.checkNameString()) { + auto NarrowStr = stripUTF16(Ref.getNameString()); + SW.printString("Resource name (string)", NarrowStr); + } else + SW.printNumber("Resource name (int)", Ref.getNameID()); + + SW.printNumber("Data version", Ref.getDataVersion()); + SW.printHex("Memory flags", Ref.getMemoryFlags()); + SW.printNumber("Language ID", Ref.getLanguage()); + SW.printNumber("Version (major)", Ref.getMajorVersion()); + SW.printNumber("Version (minor)", Ref.getMinorVersion()); + SW.printNumber("Characteristics", Ref.getCharacteristics()); + SW.printNumber("Data size", (uint64_t)Ref.getData().size()); + SW.printBinary("Data:", Ref.getData()); + SW.startLine() << "\n"; +} + +} // namespace WindowsRes +} // namespace object +} // namespace llvm diff --git a/llvm/tools/llvm-readobj/WindowsResourceDumper.h b/llvm/tools/llvm-readobj/WindowsResourceDumper.h new file mode 100644 index 000000000000..6a5878804eb1 --- /dev/null +++ b/llvm/tools/llvm-readobj/WindowsResourceDumper.h @@ -0,0 +1,36 @@ +//===- WindowsResourceDumper.h - Windows Resource printer -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_WINDOWSRESOURCEDUMPER_H +#define LLVM_TOOLS_LLVM_READOBJ_WINDOWSRESOURCEDUMPER_H + +#include "llvm/Object/WindowsResource.h" +#include "llvm/Support/ScopedPrinter.h" + +namespace llvm { +namespace object { +namespace WindowsRes { + +class Dumper { +public: + Dumper(WindowsResource *Res, ScopedPrinter &SW) : SW(SW), WinRes(Res) {} + + Error printData(); + +private: + ScopedPrinter &SW; + WindowsResource *WinRes; + + void printEntry(const ResourceEntryRef &Ref); +}; + +} // namespace WindowsRes +} // namespace object +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp new file mode 100644 index 000000000000..fe95b6d1b494 --- /dev/null +++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -0,0 +1,544 @@ +//===-- XCOFFDumper.cpp - XCOFF dumping utility -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements an XCOFF specific dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace object; + +namespace { + +class XCOFFDumper : public ObjDumper { + enum { + SymbolTypeMask = 0x07, + SymbolAlignmentMask = 0xF8, + SymbolAlignmentBitOffset = 3 + }; + +public: + XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj) {} + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printSymbols() override; + void printDynamicSymbols() override; + void printUnwindInfo() override; + void printStackMap() const override; + void printNeededLibraries() override; + +private: + template <typename T> void printSectionHeaders(ArrayRef<T> Sections); + template <typename T> void printGenericSectionHeader(T &Sec) const; + template <typename T> void printOverflowSectionHeader(T &Sec) const; + void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr); + void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr); + void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr); + void printSymbol(const SymbolRef &); + + // Least significant 3 bits are reserved. + static constexpr unsigned SectionFlagsReservedMask = 0x7; + + // The low order 16 bits of section flags denotes the section type. + static constexpr unsigned SectionFlagsTypeMask = 0xffffu; + + void printRelocations(ArrayRef<XCOFFSectionHeader32> Sections); + const XCOFFObjectFile &Obj; +}; +} // anonymous namespace + +void XCOFFDumper::printFileHeaders() { + DictScope DS(W, "FileHeader"); + W.printHex("Magic", Obj.getMagic()); + W.printNumber("NumberOfSections", Obj.getNumberOfSections()); + + // Negative timestamp values are reserved for future use. + int32_t TimeStamp = Obj.getTimeStamp(); + if (TimeStamp > 0) { + // This handling of the time stamp assumes that the host system's time_t is + // compatible with AIX time_t. If a platform is not compatible, the lit + // tests will let us know. + time_t TimeDate = TimeStamp; + + char FormattedTime[21] = {}; + size_t BytesWritten = + strftime(FormattedTime, 21, "%Y-%m-%dT%H:%M:%SZ", gmtime(&TimeDate)); + if (BytesWritten) + W.printHex("TimeStamp", FormattedTime, TimeStamp); + else + W.printHex("Timestamp", TimeStamp); + } else { + W.printHex("TimeStamp", TimeStamp == 0 ? "None" : "Reserved Value", + TimeStamp); + } + + // The number of symbol table entries is an unsigned value in 64-bit objects + // and a signed value (with negative values being 'reserved') in 32-bit + // objects. + if (Obj.is64Bit()) { + W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset64()); + W.printNumber("SymbolTableEntries", Obj.getNumberOfSymbolTableEntries64()); + } else { + W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset32()); + int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32(); + if (SymTabEntries >= 0) + W.printNumber("SymbolTableEntries", SymTabEntries); + else + W.printHex("SymbolTableEntries", "Reserved Value", SymTabEntries); + } + + W.printHex("OptionalHeaderSize", Obj.getOptionalHeaderSize()); + W.printHex("Flags", Obj.getFlags()); + + // TODO FIXME Add support for the auxiliary header (if any) once + // XCOFFObjectFile has the necessary support. +} + +void XCOFFDumper::printSectionHeaders() { + if (Obj.is64Bit()) + printSectionHeaders(Obj.sections64()); + else + printSectionHeaders(Obj.sections32()); +} + +void XCOFFDumper::printRelocations() { + if (Obj.is64Bit()) + llvm_unreachable("64-bit relocation output not implemented!"); + else + printRelocations(Obj.sections32()); +} + +static const EnumEntry<XCOFF::RelocationType> RelocationTypeNameclass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(R_POS), ECase(R_RL), ECase(R_RLA), ECase(R_NEG), + ECase(R_REL), ECase(R_TOC), ECase(R_TRL), ECase(R_TRLA), + ECase(R_GL), ECase(R_TCL), ECase(R_REF), ECase(R_BA), + ECase(R_BR), ECase(R_RBA), ECase(R_RBR), ECase(R_TLS), + ECase(R_TLS_IE), ECase(R_TLS_LD), ECase(R_TLS_LE), ECase(R_TLSM), + ECase(R_TLSML), ECase(R_TOCU), ECase(R_TOCL) +#undef ECase +}; + +void XCOFFDumper::printRelocations(ArrayRef<XCOFFSectionHeader32> Sections) { + if (!opts::ExpandRelocs) + report_fatal_error("Unexpanded relocation output not implemented."); + + ListScope LS(W, "Relocations"); + uint16_t Index = 0; + for (const auto &Sec : Sections) { + ++Index; + // Only the .text, .data, .tdata, and STYP_DWARF sections have relocation. + if (Sec.Flags != XCOFF::STYP_TEXT && Sec.Flags != XCOFF::STYP_DATA && + Sec.Flags != XCOFF::STYP_TDATA && Sec.Flags != XCOFF::STYP_DWARF) + continue; + auto Relocations = unwrapOrError(Obj.getFileName(), Obj.relocations(Sec)); + if (Relocations.empty()) + continue; + + W.startLine() << "Section (index: " << Index << ") " << Sec.getName() + << " {\n"; + for (auto Reloc : Relocations) { + StringRef SymbolName = unwrapOrError( + Obj.getFileName(), Obj.getSymbolNameByIndex(Reloc.SymbolIndex)); + + DictScope RelocScope(W, "Relocation"); + W.printHex("Virtual Address", Reloc.VirtualAddress); + W.printNumber("Symbol", SymbolName, Reloc.SymbolIndex); + W.printString("IsSigned", Reloc.isRelocationSigned() ? "Yes" : "No"); + W.printNumber("FixupBitValue", Reloc.isFixupIndicated() ? 1 : 0); + W.printNumber("Length", Reloc.getRelocatedLength()); + W.printEnum("Type", (uint8_t)Reloc.Type, + makeArrayRef(RelocationTypeNameclass)); + } + W.unindent(); + W.startLine() << "}\n"; + } +} + +static const EnumEntry<XCOFF::CFileStringType> FileStringType[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(XFT_FN), ECase(XFT_CT), ECase(XFT_CV), ECase(XFT_CD) +#undef ECase +}; + +void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) { + if (Obj.is64Bit()) + report_fatal_error( + "Printing for File Auxiliary Entry in 64-bit is unimplemented."); + StringRef FileName = + unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr)); + DictScope SymDs(W, "File Auxiliary Entry"); + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); + W.printString("Name", FileName); + W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type), + makeArrayRef(FileStringType)); +} + +static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] = + { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), + ECase(XMC_GL), ECase(XMC_XO), ECase(XMC_SV), + ECase(XMC_SV64), ECase(XMC_SV3264), ECase(XMC_TI), + ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), + ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), + ECase(XMC_UA), ECase(XMC_BS), ECase(XMC_UC), + ECase(XMC_TL), ECase(XMC_TE) +#undef ECase +}; + +static const EnumEntry<XCOFF::SymbolType> CsectSymbolTypeClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(XTY_ER), ECase(XTY_SD), ECase(XTY_LD), ECase(XTY_CM) +#undef ECase +}; + +void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) { + assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); + + DictScope SymDs(W, "CSECT Auxiliary Entry"); + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); + if ((AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask) == XCOFF::XTY_LD) + W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength); + else + W.printNumber("SectionLen", AuxEntPtr->SectionOrLength); + W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex); + W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum); + // Print out symbol alignment and type. + W.printNumber("SymbolAlignmentLog2", + (AuxEntPtr->SymbolAlignmentAndType & SymbolAlignmentMask) >> + SymbolAlignmentBitOffset); + W.printEnum("SymbolType", AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask, + makeArrayRef(CsectSymbolTypeClass)); + W.printEnum("StorageMappingClass", + static_cast<uint8_t>(AuxEntPtr->StorageMappingClass), + makeArrayRef(CsectStorageMappingClass)); + W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex); + W.printHex("StabSectNum", AuxEntPtr->StabSectNum); +} + +void XCOFFDumper::printSectAuxEntForStat( + const XCOFFSectAuxEntForStat *AuxEntPtr) { + assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); + + DictScope SymDs(W, "Sect Auxiliary Entry For Stat"); + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); + W.printNumber("SectionLength", AuxEntPtr->SectionLength); + + // Unlike the corresponding fields in the section header, NumberOfRelocEnt + // and NumberOfLineNum do not handle values greater than 65535. + W.printNumber("NumberOfRelocEnt", AuxEntPtr->NumberOfRelocEnt); + W.printNumber("NumberOfLineNum", AuxEntPtr->NumberOfLineNum); +} + +static const EnumEntry<XCOFF::StorageClass> SymStorageClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(C_NULL), ECase(C_AUTO), ECase(C_EXT), ECase(C_STAT), + ECase(C_REG), ECase(C_EXTDEF), ECase(C_LABEL), ECase(C_ULABEL), + ECase(C_MOS), ECase(C_ARG), ECase(C_STRTAG), ECase(C_MOU), + ECase(C_UNTAG), ECase(C_TPDEF), ECase(C_USTATIC), ECase(C_ENTAG), + ECase(C_MOE), ECase(C_REGPARM), ECase(C_FIELD), ECase(C_BLOCK), + ECase(C_FCN), ECase(C_EOS), ECase(C_FILE), ECase(C_LINE), + ECase(C_ALIAS), ECase(C_HIDDEN), ECase(C_HIDEXT), ECase(C_BINCL), + ECase(C_EINCL), ECase(C_INFO), ECase(C_WEAKEXT), ECase(C_DWARF), + ECase(C_GSYM), ECase(C_LSYM), ECase(C_PSYM), ECase(C_RSYM), + ECase(C_RPSYM), ECase(C_STSYM), ECase(C_TCSYM), ECase(C_BCOMM), + ECase(C_ECOML), ECase(C_ECOMM), ECase(C_DECL), ECase(C_ENTRY), + ECase(C_FUN), ECase(C_BSTAT), ECase(C_ESTAT), ECase(C_GTLS), + ECase(C_STTLS), ECase(C_EFCN) +#undef ECase +}; + +static StringRef GetSymbolValueName(XCOFF::StorageClass SC) { + switch (SC) { + case XCOFF::C_EXT: + case XCOFF::C_WEAKEXT: + case XCOFF::C_HIDEXT: + case XCOFF::C_STAT: + return "Value (RelocatableAddress)"; + case XCOFF::C_FILE: + return "Value (SymbolTableIndex)"; + case XCOFF::C_FCN: + case XCOFF::C_BLOCK: + case XCOFF::C_FUN: + case XCOFF::C_STSYM: + case XCOFF::C_BINCL: + case XCOFF::C_EINCL: + case XCOFF::C_INFO: + case XCOFF::C_BSTAT: + case XCOFF::C_LSYM: + case XCOFF::C_PSYM: + case XCOFF::C_RPSYM: + case XCOFF::C_RSYM: + case XCOFF::C_ECOML: + case XCOFF::C_DWARF: + assert(false && "This StorageClass for the symbol is not yet implemented."); + return ""; + default: + return "Value"; + } +} + +static const EnumEntry<XCOFF::CFileLangId> CFileLangIdClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(TB_C), ECase(TB_CPLUSPLUS) +#undef ECase +}; + +static const EnumEntry<XCOFF::CFileCpuId> CFileCpuIdClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(TCPU_PPC64), ECase(TCPU_COM), ECase(TCPU_970) +#undef ECase +}; + +void XCOFFDumper::printSymbol(const SymbolRef &S) { + if (Obj.is64Bit()) + report_fatal_error("64-bit support is unimplemented."); + + DataRefImpl SymbolDRI = S.getRawDataRefImpl(); + const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI); + + XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj); + uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries(); + + DictScope SymDs(W, "Symbol"); + + StringRef SymbolName = + unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI)); + + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(SymbolEntPtr))); + W.printString("Name", SymbolName); + W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass), + SymbolEntPtr->Value); + + StringRef SectionName = + unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr)); + + W.printString("Section", SectionName); + if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) { + W.printEnum("Source Language ID", + SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId, + makeArrayRef(CFileLangIdClass)); + W.printEnum("CPU Version ID", + SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId, + makeArrayRef(CFileCpuIdClass)); + } else + W.printHex("Type", SymbolEntPtr->SymbolType); + + W.printEnum("StorageClass", static_cast<uint8_t>(SymbolEntPtr->StorageClass), + makeArrayRef(SymStorageClass)); + W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries); + + if (NumberOfAuxEntries == 0) + return; + + switch (XCOFFSymRef.getStorageClass()) { + case XCOFF::C_FILE: + // If the symbol is C_FILE and has auxiliary entries... + for (int i = 1; i <= NumberOfAuxEntries; i++) { + const XCOFFFileAuxEnt *FileAuxEntPtr = + reinterpret_cast<const XCOFFFileAuxEnt *>(SymbolEntPtr + i); +#ifndef NDEBUG + Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(FileAuxEntPtr)); +#endif + printFileAuxEnt(FileAuxEntPtr); + } + break; + case XCOFF::C_EXT: + case XCOFF::C_WEAKEXT: + case XCOFF::C_HIDEXT: + // If the symbol is for a function, and it has more than 1 auxiliary entry, + // then one of them must be function auxiliary entry which we do not + // support yet. + if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2) + report_fatal_error("Function auxiliary entry printing is unimplemented."); + + // If there is more than 1 auxiliary entry, instead of printing out + // error information, print out the raw Auxiliary entry from 1st till + // the last - 1. The last one must be a CSECT Auxiliary Entry. + for (int i = 1; i < NumberOfAuxEntries; i++) { + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; + W.startLine() << format_bytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + XCOFF::SymbolTableEntrySize)); + } + + // The symbol's last auxiliary entry is a CSECT Auxiliary Entry. + printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32()); + break; + case XCOFF::C_STAT: + if (NumberOfAuxEntries > 1) + report_fatal_error( + "C_STAT symbol should not have more than 1 auxiliary entry."); + + const XCOFFSectAuxEntForStat *StatAuxEntPtr; + StatAuxEntPtr = + reinterpret_cast<const XCOFFSectAuxEntForStat *>(SymbolEntPtr + 1); +#ifndef NDEBUG + Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(StatAuxEntPtr)); +#endif + printSectAuxEntForStat(StatAuxEntPtr); + break; + case XCOFF::C_DWARF: + case XCOFF::C_BLOCK: + case XCOFF::C_FCN: + report_fatal_error("Symbol table entry printing for this storage class " + "type is unimplemented."); + break; + default: + for (int i = 1; i <= NumberOfAuxEntries; i++) { + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; + W.startLine() << format_bytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + XCOFF::SymbolTableEntrySize)); + } + break; + } +} + +void XCOFFDumper::printSymbols() { + ListScope Group(W, "Symbols"); + for (const SymbolRef &S : Obj.symbols()) + printSymbol(S); +} + +void XCOFFDumper::printDynamicSymbols() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printUnwindInfo() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printStackMap() const { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printNeededLibraries() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +static const EnumEntry<XCOFF::SectionTypeFlags> SectionTypeFlagsNames[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(STYP_PAD), ECase(STYP_DWARF), ECase(STYP_TEXT), + ECase(STYP_DATA), ECase(STYP_BSS), ECase(STYP_EXCEPT), + ECase(STYP_INFO), ECase(STYP_TDATA), ECase(STYP_TBSS), + ECase(STYP_LOADER), ECase(STYP_DEBUG), ECase(STYP_TYPCHK), + ECase(STYP_OVRFLO) +#undef ECase +}; + +template <typename T> +void XCOFFDumper::printOverflowSectionHeader(T &Sec) const { + if (Obj.is64Bit()) { + reportWarning(make_error<StringError>("An 64-bit XCOFF object file may not " + "contain an overflow section header.", + object_error::parse_failed), + Obj.getFileName()); + } + + W.printString("Name", Sec.getName()); + W.printNumber("NumberOfRelocations", Sec.PhysicalAddress); + W.printNumber("NumberOfLineNumbers", Sec.VirtualAddress); + W.printHex("Size", Sec.SectionSize); + W.printHex("RawDataOffset", Sec.FileOffsetToRawData); + W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); + W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); + W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfRelocations); + W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfLineNumbers); +} + +template <typename T> +void XCOFFDumper::printGenericSectionHeader(T &Sec) const { + W.printString("Name", Sec.getName()); + W.printHex("PhysicalAddress", Sec.PhysicalAddress); + W.printHex("VirtualAddress", Sec.VirtualAddress); + W.printHex("Size", Sec.SectionSize); + W.printHex("RawDataOffset", Sec.FileOffsetToRawData); + W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); + W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); + W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations); + W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers); +} + +template <typename T> +void XCOFFDumper::printSectionHeaders(ArrayRef<T> Sections) { + ListScope Group(W, "Sections"); + + uint16_t Index = 1; + for (const T &Sec : Sections) { + DictScope SecDS(W, "Section"); + + W.printNumber("Index", Index++); + + uint16_t SectionType = Sec.Flags & SectionFlagsTypeMask; + switch (SectionType) { + case XCOFF::STYP_OVRFLO: + printOverflowSectionHeader(Sec); + break; + case XCOFF::STYP_LOADER: + case XCOFF::STYP_EXCEPT: + case XCOFF::STYP_TYPCHK: + // TODO The interpretation of loader, exception and type check section + // headers are different from that of generic section headers. We will + // implement them later. We interpret them as generic section headers for + // now. + default: + printGenericSectionHeader(Sec); + break; + } + // For now we just dump the section type portion of the flags. + if (SectionType & SectionFlagsReservedMask) + W.printHex("Flags", "Reserved", SectionType); + else + W.printEnum("Type", SectionType, makeArrayRef(SectionTypeFlagsNames)); + } + + if (opts::SectionRelocations) + report_fatal_error("Dumping section relocations is unimplemented"); + + if (opts::SectionSymbols) + report_fatal_error("Dumping symbols is unimplemented"); + + if (opts::SectionData) + report_fatal_error("Dumping section data is unimplemented"); +} + +namespace llvm { +std::error_code createXCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(Obj); + if (!XObj) + return readobj_error::unsupported_obj_file_format; + + Result.reset(new XCOFFDumper(*XObj, Writer)); + return readobj_error::success; +} +} // namespace llvm diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp new file mode 100644 index 000000000000..4db13897879d --- /dev/null +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -0,0 +1,724 @@ +//===- llvm-readobj.cpp - Dump contents of an Object File -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a tool similar to readelf, except it works on multiple object file +// formats. The main purpose of this tool is to provide detailed output suitable +// for FileCheck. +// +// Flags should be similar to readelf where supported, but the output format +// does not need to be identical. The point is to not make users learn yet +// another set of flags. +// +// Output should be specialized for each format where appropriate. +// +//===----------------------------------------------------------------------===// + +#include "llvm-readobj.h" +#include "Error.h" +#include "ObjDumper.h" +#include "WindowsResourceDumper.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace llvm::object; + +namespace opts { + cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input object files>"), + cl::ZeroOrMore); + + // --all, -a + cl::opt<bool> + All("all", + cl::desc("Equivalent to setting: --file-headers, --program-headers, " + "--section-headers, --symbols, --relocations, " + "--dynamic-table, --notes, --version-info, --unwind, " + "--section-groups and --elf-hash-histogram.")); + cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All)); + + // --headers -e + cl::opt<bool> + Headers("headers", + cl::desc("Equivalent to setting: --file-headers, --program-headers, " + "--section-headers")); + cl::alias HeadersShort("e", cl::desc("Alias for --headers"), + cl::aliasopt(Headers)); + + // --wide, -W + cl::opt<bool> + WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"), + cl::Hidden); + cl::alias WideOutputShort("W", + cl::desc("Alias for --wide"), + cl::aliasopt(WideOutput)); + + // --file-headers, --file-header, -h + cl::opt<bool> FileHeaders("file-headers", + cl::desc("Display file headers ")); + cl::alias FileHeadersShort("h", cl::desc("Alias for --file-headers"), + cl::aliasopt(FileHeaders), cl::NotHidden); + cl::alias FileHeadersSingular("file-header", + cl::desc("Alias for --file-headers"), + cl::aliasopt(FileHeaders)); + + // --section-headers, --sections, -S + // Also -s in llvm-readobj mode. + cl::opt<bool> SectionHeaders("section-headers", + cl::desc("Display all section headers.")); + cl::alias SectionsShortUpper("S", cl::desc("Alias for --section-headers"), + cl::aliasopt(SectionHeaders), cl::NotHidden); + cl::alias SectionHeadersAlias("sections", + cl::desc("Alias for --section-headers"), + cl::aliasopt(SectionHeaders), cl::NotHidden); + + // --section-relocations + // Also --sr in llvm-readobj mode. + cl::opt<bool> SectionRelocations("section-relocations", + cl::desc("Display relocations for each section shown.")); + + // --section-symbols + // Also --st in llvm-readobj mode. + cl::opt<bool> SectionSymbols("section-symbols", + cl::desc("Display symbols for each section shown.")); + + // --section-data + // Also --sd in llvm-readobj mode. + cl::opt<bool> SectionData("section-data", + cl::desc("Display section data for each section shown.")); + + // --section-mapping + cl::opt<cl::boolOrDefault> + SectionMapping("section-mapping", + cl::desc("Display the section to segment mapping.")); + + // --relocations, --relocs, -r + cl::opt<bool> Relocations("relocations", + cl::desc("Display the relocation entries in the file")); + cl::alias RelocationsShort("r", cl::desc("Alias for --relocations"), + cl::aliasopt(Relocations), cl::NotHidden); + cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"), + cl::aliasopt(Relocations)); + + // --notes, -n + cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file")); + cl::alias NotesShort("n", cl::desc("Alias for --notes"), cl::aliasopt(Notes)); + + // --dyn-relocations + cl::opt<bool> DynRelocs("dyn-relocations", + cl::desc("Display the dynamic relocation entries in the file")); + + // --symbols + // Also -s in llvm-readelf mode, or -t in llvm-readobj mode. + cl::opt<bool> + Symbols("symbols", + cl::desc("Display the symbol table. Also display the dynamic " + "symbol table when using GNU output style for ELF")); + cl::alias SymbolsGNU("syms", cl::desc("Alias for --symbols"), + cl::aliasopt(Symbols)); + + // --dyn-symbols, --dyn-syms + // Also --dt in llvm-readobj mode. + cl::opt<bool> DynamicSymbols("dyn-symbols", + cl::desc("Display the dynamic symbol table")); + cl::alias DynSymsGNU("dyn-syms", cl::desc("Alias for --dyn-symbols"), + cl::aliasopt(DynamicSymbols)); + + // --hash-symbols + cl::opt<bool> HashSymbols( + "hash-symbols", + cl::desc("Display the dynamic symbols derived from the hash section")); + + // --unwind, -u + cl::opt<bool> UnwindInfo("unwind", + cl::desc("Display unwind information")); + cl::alias UnwindInfoShort("u", + cl::desc("Alias for --unwind"), + cl::aliasopt(UnwindInfo)); + + // --dynamic-table, --dynamic, -d + cl::opt<bool> DynamicTable("dynamic-table", + cl::desc("Display the ELF .dynamic section table")); + cl::alias DynamicTableShort("d", cl::desc("Alias for --dynamic-table"), + cl::aliasopt(DynamicTable), cl::NotHidden); + cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"), + cl::aliasopt(DynamicTable)); + + // --needed-libs + cl::opt<bool> NeededLibraries("needed-libs", + cl::desc("Display the needed libraries")); + + // --program-headers, --segments, -l + cl::opt<bool> ProgramHeaders("program-headers", + cl::desc("Display ELF program headers")); + cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"), + cl::aliasopt(ProgramHeaders), cl::NotHidden); + cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"), + cl::aliasopt(ProgramHeaders)); + + // --string-dump, -p + cl::list<std::string> StringDump("string-dump", cl::desc("<number|name>"), + cl::ZeroOrMore); + cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"), + cl::aliasopt(StringDump), cl::Prefix); + + // --hex-dump, -x + cl::list<std::string> HexDump("hex-dump", cl::desc("<number|name>"), + cl::ZeroOrMore); + cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"), + cl::aliasopt(HexDump), cl::Prefix); + + // --demangle, -C + cl::opt<bool> Demangle("demangle", + cl::desc("Demangle symbol names in output")); + cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), + cl::aliasopt(Demangle), cl::NotHidden); + + // --hash-table + cl::opt<bool> HashTable("hash-table", + cl::desc("Display ELF hash table")); + + // --gnu-hash-table + cl::opt<bool> GnuHashTable("gnu-hash-table", + cl::desc("Display ELF .gnu.hash section")); + + // --expand-relocs + cl::opt<bool> ExpandRelocs("expand-relocs", + cl::desc("Expand each shown relocation to multiple lines")); + + // --raw-relr + cl::opt<bool> RawRelr("raw-relr", + cl::desc("Do not decode relocations in SHT_RELR section, display raw contents")); + + // --codeview + cl::opt<bool> CodeView("codeview", + cl::desc("Display CodeView debug information")); + + // --codeview-merged-types + cl::opt<bool> + CodeViewMergedTypes("codeview-merged-types", + cl::desc("Display the merged CodeView type stream")); + + // --codeview-ghash + cl::opt<bool> CodeViewEnableGHash( + "codeview-ghash", + cl::desc( + "Enable global hashing for CodeView type stream de-duplication")); + + // --codeview-subsection-bytes + cl::opt<bool> CodeViewSubsectionBytes( + "codeview-subsection-bytes", + cl::desc("Dump raw contents of codeview debug sections and records")); + + // --arch-specific + cl::opt<bool> ArchSpecificInfo("arch-specific", + cl::desc("Displays architecture-specific information, if there is any.")); + cl::alias ArchSpecifcInfoShort("A", cl::desc("Alias for --arch-specific"), + cl::aliasopt(ArchSpecificInfo), cl::NotHidden); + + // --coff-imports + cl::opt<bool> + COFFImports("coff-imports", cl::desc("Display the PE/COFF import table")); + + // --coff-exports + cl::opt<bool> + COFFExports("coff-exports", cl::desc("Display the PE/COFF export table")); + + // --coff-directives + cl::opt<bool> + COFFDirectives("coff-directives", + cl::desc("Display the PE/COFF .drectve section")); + + // --coff-basereloc + cl::opt<bool> + COFFBaseRelocs("coff-basereloc", + cl::desc("Display the PE/COFF .reloc section")); + + // --coff-debug-directory + cl::opt<bool> + COFFDebugDirectory("coff-debug-directory", + cl::desc("Display the PE/COFF debug directory")); + + // --coff-resources + cl::opt<bool> COFFResources("coff-resources", + cl::desc("Display the PE/COFF .rsrc section")); + + // --coff-load-config + cl::opt<bool> + COFFLoadConfig("coff-load-config", + cl::desc("Display the PE/COFF load config")); + + // --elf-linker-options + cl::opt<bool> + ELFLinkerOptions("elf-linker-options", + cl::desc("Display the ELF .linker-options section")); + + // --macho-data-in-code + cl::opt<bool> + MachODataInCode("macho-data-in-code", + cl::desc("Display MachO Data in Code command")); + + // --macho-indirect-symbols + cl::opt<bool> + MachOIndirectSymbols("macho-indirect-symbols", + cl::desc("Display MachO indirect symbols")); + + // --macho-linker-options + cl::opt<bool> + MachOLinkerOptions("macho-linker-options", + cl::desc("Display MachO linker options")); + + // --macho-segment + cl::opt<bool> + MachOSegment("macho-segment", + cl::desc("Display MachO Segment command")); + + // --macho-version-min + cl::opt<bool> + MachOVersionMin("macho-version-min", + cl::desc("Display MachO version min command")); + + // --macho-dysymtab + cl::opt<bool> + MachODysymtab("macho-dysymtab", + cl::desc("Display MachO Dysymtab command")); + + // --stackmap + cl::opt<bool> + PrintStackMap("stackmap", + cl::desc("Display contents of stackmap section")); + + // --stack-sizes + cl::opt<bool> + PrintStackSizes("stack-sizes", + cl::desc("Display contents of all stack sizes sections")); + + // --version-info, -V + cl::opt<bool> + VersionInfo("version-info", + cl::desc("Display ELF version sections (if present)")); + cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"), + cl::aliasopt(VersionInfo)); + + // --elf-section-groups, --section-groups, -g + cl::opt<bool> SectionGroups("elf-section-groups", + cl::desc("Display ELF section group contents")); + cl::alias SectionGroupsAlias("section-groups", + cl::desc("Alias for -elf-sections-groups"), + cl::aliasopt(SectionGroups)); + cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"), + cl::aliasopt(SectionGroups)); + + // --elf-hash-histogram, --histogram, -I + cl::opt<bool> HashHistogram( + "elf-hash-histogram", + cl::desc("Display bucket list histogram for hash sections")); + cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"), + cl::aliasopt(HashHistogram)); + cl::alias HistogramAlias("histogram", + cl::desc("Alias for --elf-hash-histogram"), + cl::aliasopt(HashHistogram)); + + // --elf-cg-profile + cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); + + // -addrsig + cl::opt<bool> Addrsig("addrsig", + cl::desc("Display address-significance table")); + + // -elf-output-style + cl::opt<OutputStyleTy> + Output("elf-output-style", cl::desc("Specify ELF dump style"), + cl::values(clEnumVal(LLVM, "LLVM default style"), + clEnumVal(GNU, "GNU readelf style")), + cl::init(LLVM)); + + cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +} // namespace opts + +static StringRef ToolName; + +namespace llvm { + +LLVM_ATTRIBUTE_NORETURN static void error(Twine Msg) { + // Flush the standard output to print the error at a + // proper place. + fouts().flush(); + errs() << "\n"; + WithColor::error(errs(), ToolName) << Msg << "\n"; + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN void reportError(Error Err, StringRef Input) { + assert(Err); + if (Input == "-") + Input = "<stdin>"; + handleAllErrors(createFileError(Input, std::move(Err)), + [&](const ErrorInfoBase &EI) { error(EI.message()); }); + llvm_unreachable("error() call should never return"); +} + +void reportWarning(Error Err, StringRef Input) { + assert(Err); + if (Input == "-") + Input = "<stdin>"; + + // Flush the standard output to print the warning at a + // proper place. + fouts().flush(); + handleAllErrors( + createFileError(Input, std::move(Err)), [&](const ErrorInfoBase &EI) { + errs() << "\n"; + WithColor::warning(errs(), ToolName) << EI.message() << "\n"; + }); +} + +} // namespace llvm + +namespace { +struct ReadObjTypeTableBuilder { + ReadObjTypeTableBuilder() + : Allocator(), IDTable(Allocator), TypeTable(Allocator), + GlobalIDTable(Allocator), GlobalTypeTable(Allocator) {} + + llvm::BumpPtrAllocator Allocator; + llvm::codeview::MergingTypeTableBuilder IDTable; + llvm::codeview::MergingTypeTableBuilder TypeTable; + llvm::codeview::GlobalTypeTableBuilder GlobalIDTable; + llvm::codeview::GlobalTypeTableBuilder GlobalTypeTable; + std::vector<OwningBinary<Binary>> Binaries; +}; +} // namespace +static ReadObjTypeTableBuilder CVTypes; + +/// Creates an format-specific object file dumper. +static std::error_code createDumper(const ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + if (!Obj) + return readobj_error::unsupported_file_format; + + if (Obj->isCOFF()) + return createCOFFDumper(Obj, Writer, Result); + if (Obj->isELF()) + return createELFDumper(Obj, Writer, Result); + if (Obj->isMachO()) + return createMachODumper(Obj, Writer, Result); + if (Obj->isWasm()) + return createWasmDumper(Obj, Writer, Result); + if (Obj->isXCOFF()) + return createXCOFFDumper(Obj, Writer, Result); + + return readobj_error::unsupported_obj_file_format; +} + +/// Dumps the specified object file. +static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, + const Archive *A = nullptr) { + std::string FileStr = + A ? Twine(A->getFileName() + "(" + Obj->getFileName() + ")").str() + : Obj->getFileName().str(); + + std::unique_ptr<ObjDumper> Dumper; + if (std::error_code EC = createDumper(Obj, Writer, Dumper)) + reportError(errorCodeToError(EC), FileStr); + + if (opts::Output == opts::LLVM || opts::InputFilenames.size() > 1 || A) { + Writer.startLine() << "\n"; + Writer.printString("File", FileStr); + } + if (opts::Output == opts::LLVM) { + Writer.printString("Format", Obj->getFileFormatName()); + Writer.printString("Arch", Triple::getArchTypeName( + (llvm::Triple::ArchType)Obj->getArch())); + Writer.printString("AddressSize", + formatv("{0}bit", 8 * Obj->getBytesInAddress())); + Dumper->printLoadName(); + } + + if (opts::FileHeaders) + Dumper->printFileHeaders(); + if (opts::SectionHeaders) + Dumper->printSectionHeaders(); + if (opts::Relocations) + Dumper->printRelocations(); + if (opts::DynRelocs) + Dumper->printDynamicRelocations(); + if (opts::Symbols || opts::DynamicSymbols) + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); + if (opts::HashSymbols) + Dumper->printHashSymbols(); + if (opts::UnwindInfo) + Dumper->printUnwindInfo(); + if (opts::DynamicTable) + Dumper->printDynamicTable(); + if (opts::NeededLibraries) + Dumper->printNeededLibraries(); + if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) + Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping); + if (!opts::StringDump.empty()) + Dumper->printSectionsAsString(Obj, opts::StringDump); + if (!opts::HexDump.empty()) + Dumper->printSectionsAsHex(Obj, opts::HexDump); + if (opts::HashTable) + Dumper->printHashTable(); + if (opts::GnuHashTable) + Dumper->printGnuHashTable(); + if (opts::VersionInfo) + Dumper->printVersionInfo(); + if (Obj->isELF()) { + if (opts::ELFLinkerOptions) + Dumper->printELFLinkerOptions(); + if (opts::ArchSpecificInfo) + Dumper->printArchSpecificInfo(); + if (opts::SectionGroups) + Dumper->printGroupSections(); + if (opts::HashHistogram) + Dumper->printHashHistogram(); + if (opts::CGProfile) + Dumper->printCGProfile(); + if (opts::Addrsig) + Dumper->printAddrsig(); + if (opts::Notes) + Dumper->printNotes(); + } + if (Obj->isCOFF()) { + if (opts::COFFImports) + Dumper->printCOFFImports(); + if (opts::COFFExports) + Dumper->printCOFFExports(); + if (opts::COFFDirectives) + Dumper->printCOFFDirectives(); + if (opts::COFFBaseRelocs) + Dumper->printCOFFBaseReloc(); + if (opts::COFFDebugDirectory) + Dumper->printCOFFDebugDirectory(); + if (opts::COFFResources) + Dumper->printCOFFResources(); + if (opts::COFFLoadConfig) + Dumper->printCOFFLoadConfig(); + if (opts::Addrsig) + Dumper->printAddrsig(); + if (opts::CodeView) + Dumper->printCodeViewDebugInfo(); + if (opts::CodeViewMergedTypes) + Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable, + CVTypes.GlobalIDTable, CVTypes.GlobalTypeTable, + opts::CodeViewEnableGHash); + } + if (Obj->isMachO()) { + if (opts::MachODataInCode) + Dumper->printMachODataInCode(); + if (opts::MachOIndirectSymbols) + Dumper->printMachOIndirectSymbols(); + if (opts::MachOLinkerOptions) + Dumper->printMachOLinkerOptions(); + if (opts::MachOSegment) + Dumper->printMachOSegment(); + if (opts::MachOVersionMin) + Dumper->printMachOVersionMin(); + if (opts::MachODysymtab) + Dumper->printMachODysymtab(); + } + if (opts::PrintStackMap) + Dumper->printStackMap(); + if (opts::PrintStackSizes) + Dumper->printStackSizes(); +} + +/// Dumps each object file in \a Arc; +static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { + Error Err = Error::success(); + for (auto &Child : Arc->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + reportError(std::move(E), Arc->getFileName()); + continue; + } + if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get())) + dumpObject(Obj, Writer, Arc); + else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(&*ChildOrErr.get())) + dumpCOFFImportFile(Imp, Writer); + else + reportError(errorCodeToError(readobj_error::unrecognized_file_format), + Arc->getFileName()); + } + if (Err) + reportError(std::move(Err), Arc->getFileName()); +} + +/// Dumps each object file in \a MachO Universal Binary; +static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary, + ScopedPrinter &Writer) { + for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) { + Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); + if (ObjOrErr) + dumpObject(&*ObjOrErr.get(), Writer); + else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) + reportError(ObjOrErr.takeError(), UBinary->getFileName()); + else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive()) + dumpArchive(&*AOrErr.get(), Writer); + } +} + +/// Dumps \a WinRes, Windows Resource (.res) file; +static void dumpWindowsResourceFile(WindowsResource *WinRes, + ScopedPrinter &Printer) { + WindowsRes::Dumper Dumper(WinRes, Printer); + if (auto Err = Dumper.printData()) + reportError(std::move(Err), WinRes->getFileName()); +} + + +/// Opens \a File and dumps it. +static void dumpInput(StringRef File, ScopedPrinter &Writer) { + // Attempt to open the binary. + Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File); + if (!BinaryOrErr) + reportError(BinaryOrErr.takeError(), File); + Binary &Binary = *BinaryOrErr.get().getBinary(); + + if (Archive *Arc = dyn_cast<Archive>(&Binary)) + dumpArchive(Arc, Writer); + else if (MachOUniversalBinary *UBinary = + dyn_cast<MachOUniversalBinary>(&Binary)) + dumpMachOUniversalBinary(UBinary, Writer); + else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) + dumpObject(Obj, Writer); + else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary)) + dumpCOFFImportFile(Import, Writer); + else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary)) + dumpWindowsResourceFile(WinRes, Writer); + else + reportError(errorCodeToError(readobj_error::unrecognized_file_format), + File); + + CVTypes.Binaries.push_back(std::move(*BinaryOrErr)); +} + +/// Registers aliases that should only be allowed by readobj. +static void registerReadobjAliases() { + // -s has meant --sections for a very long time in llvm-readobj despite + // meaning --symbols in readelf. + static cl::alias SectionsShort("s", cl::desc("Alias for --section-headers"), + cl::aliasopt(opts::SectionHeaders), + cl::NotHidden); + + // Only register -t in llvm-readobj, as readelf reserves it for + // --section-details (not implemented yet). + static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"), + cl::aliasopt(opts::Symbols), cl::NotHidden); + + // The following two-letter aliases are only provided for readobj, as readelf + // allows single-letter args to be grouped together. + static cl::alias SectionRelocationsShort( + "sr", cl::desc("Alias for --section-relocations"), + cl::aliasopt(opts::SectionRelocations)); + static cl::alias SectionDataShort("sd", cl::desc("Alias for --section-data"), + cl::aliasopt(opts::SectionData)); + static cl::alias SectionSymbolsShort("st", + cl::desc("Alias for --section-symbols"), + cl::aliasopt(opts::SectionSymbols)); + static cl::alias DynamicSymbolsShort("dt", + cl::desc("Alias for --dyn-symbols"), + cl::aliasopt(opts::DynamicSymbols)); +} + +/// Registers aliases that should only be allowed by readelf. +static void registerReadelfAliases() { + // -s is here because for readobj it means --sections. + static cl::alias SymbolsShort("s", cl::desc("Alias for --symbols"), + cl::aliasopt(opts::Symbols), cl::NotHidden, + cl::Grouping); + + // Allow all single letter flags to be grouped together. + for (auto &OptEntry : cl::getRegisteredOptions()) { + StringRef ArgName = OptEntry.getKey(); + cl::Option *Option = OptEntry.getValue(); + if (ArgName.size() == 1) + apply(Option, cl::Grouping); + } +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + ToolName = argv[0]; + + // Register the target printer for --version. + cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + + if (sys::path::stem(argv[0]).contains("readelf")) { + opts::Output = opts::GNU; + registerReadelfAliases(); + } else { + registerReadobjAliases(); + } + + cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n"); + + if (opts::All) { + opts::FileHeaders = true; + opts::ProgramHeaders = true; + opts::SectionHeaders = true; + opts::Symbols = true; + opts::Relocations = true; + opts::DynamicTable = true; + opts::Notes = true; + opts::VersionInfo = true; + opts::UnwindInfo = true; + opts::SectionGroups = true; + opts::HashHistogram = true; + if (opts::Output == opts::LLVM) { + opts::Addrsig = true; + opts::PrintStackSizes = true; + } + } + + if (opts::Headers) { + opts::FileHeaders = true; + opts::ProgramHeaders = true; + opts::SectionHeaders = true; + } + + // Default to stdin if no filename is specified. + if (opts::InputFilenames.empty()) + opts::InputFilenames.push_back("-"); + + ScopedPrinter Writer(fouts()); + for (const std::string &I : opts::InputFilenames) + dumpInput(I, Writer); + + if (opts::CodeViewMergedTypes) { + if (opts::CodeViewEnableGHash) + dumpCodeViewMergedTypes(Writer, CVTypes.GlobalIDTable.records(), + CVTypes.GlobalTypeTable.records()); + else + dumpCodeViewMergedTypes(Writer, CVTypes.IDTable.records(), + CVTypes.TypeTable.records()); + } + + return 0; +} diff --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h new file mode 100644 index 000000000000..d9813f5dea62 --- /dev/null +++ b/llvm/tools/llvm-readobj/llvm-readobj.h @@ -0,0 +1,52 @@ +//===-- llvm-readobj.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H +#define LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Error.h" +#include <string> + +namespace llvm { + namespace object { + class RelocationRef; + } + + // Various helper functions. + LLVM_ATTRIBUTE_NORETURN void reportError(Error Err, StringRef Input); + void reportWarning(Error Err, StringRef Input); + + template <class T> T unwrapOrError(StringRef Input, Expected<T> EO) { + if (EO) + return *EO; + reportError(EO.takeError(), Input); + } +} // namespace llvm + +namespace opts { + extern llvm::cl::opt<bool> SectionRelocations; + extern llvm::cl::opt<bool> SectionSymbols; + extern llvm::cl::opt<bool> SectionData; + extern llvm::cl::opt<bool> ExpandRelocs; + extern llvm::cl::opt<bool> RawRelr; + extern llvm::cl::opt<bool> CodeViewSubsectionBytes; + extern llvm::cl::opt<bool> Demangle; + enum OutputStyleTy { LLVM, GNU }; + extern llvm::cl::opt<OutputStyleTy> Output; +} // namespace opts + +#define LLVM_READOBJ_ENUM_ENT(ns, enum) \ + { #enum, ns::enum } + +#define LLVM_READOBJ_ENUM_CLASS_ENT(enum_class, enum) \ + { #enum, std::underlying_type<enum_class>::type(enum_class::enum) } + +#endif |