diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/libs/llvm12/tools/llvm-readobj | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'contrib/libs/llvm12/tools/llvm-readobj')
19 files changed, 14724 insertions, 0 deletions
diff --git a/contrib/libs/llvm12/tools/llvm-readobj/ARMEHABIPrinter.h b/contrib/libs/llvm12/tools/llvm-readobj/ARMEHABIPrinter.h new file mode 100644 index 0000000000..3d8acbf48f --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/ARMEHABIPrinter.h @@ -0,0 +1,647 @@ +//===--- 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 "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 & 0x40000000) + Location |= (uint64_t) ~0x7fffffff; + return Location + Place; + } + + ErrorOr<StringRef> FunctionAtAddress(uint64_t Address, + Optional<unsigned> SectionIndex) 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 &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(uint64_t Address, + Optional<unsigned> SectionIndex) const { + if (!Symtab) + return inconvertibleErrorCode(); + 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 (SectionIndex && *SectionIndex != Sym.st_shndx) + continue; + + if (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 inconvertibleErrorCode(); + } + return *NameOrErr; + } + } + + return inconvertibleErrorCode(); +} + +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> +static const typename ET::Shdr * +findSectionContainingAddress(const object::ELFFile<ET> &Obj, StringRef FileName, + uint64_t Address) { + for (const typename ET::Shdr &Sec : unwrapOrError(FileName, Obj.sections())) + if (Address >= Sec.sh_addr && Address < Sec.sh_addr + Sec.sh_size) + return &Sec; + return nullptr; +} + +template <typename ET> +void PrinterContext<ET>::PrintExceptionTable(const Elf_Shdr &EHT, + uint64_t TableEntryOffset) const { + // TODO: handle failure. + 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")); + const bool IsRelocatable = ELF.getHeader().e_type == ELF::ET_REL; + uint64_t Address = IsRelocatable + ? PREL31(Word, EHT.sh_addr) + : PREL31(Word, EHT.sh_addr + TableEntryOffset); + SW.printHex("PersonalityRoutineAddress", Address); + Optional<unsigned> SecIndex = + IsRelocatable ? Optional<unsigned>(EHT.sh_link) : None; + if (ErrorOr<StringRef> Name = FunctionAtAddress(Address, SecIndex)) + 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 { + // TODO: handle failure. + 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; + const bool IsRelocatable = ELF.getHeader().e_type == ELF::ET_REL; + + 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; + } + + // FIXME: For a relocatable object ideally we might want to: + // 1) Find a relocation for the offset of Word0. + // 2) Verify this relocation is of an expected type (R_ARM_PREL31) and + // verify the symbol index. + // 3) Resolve the relocation using it's symbol value, addend etc. + // Currently the code assumes that Word0 contains an addend of a + // R_ARM_PREL31 REL relocation that references a section symbol. RELA + // relocations are not supported and it works because addresses of sections + // are nulls in relocatable objects. + // + // For a non-relocatable object, Word0 contains a place-relative signed + // offset to the referenced entity. + const uint64_t Address = + IsRelocatable + ? PREL31(Word0, IT->sh_addr) + : PREL31(Word0, IT->sh_addr + Entry * IndexTableEntrySize); + SW.printHex("FunctionAddress", Address); + + // In a relocatable output we might have many .ARM.exidx sections linked to + // their code sections via the sh_link field. For a non-relocatable ELF file + // the sh_link field is not reliable, because we have one .ARM.exidx section + // normally, but might have many code sections. + Optional<unsigned> SecIndex = + IsRelocatable ? Optional<unsigned>(IT->sh_link) : None; + if (ErrorOr<StringRef> Name = FunctionAtAddress(Address, SecIndex)) + 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; + uint64_t TableEntryAddress; + if (IsRelocatable) { + TableEntryAddress = PREL31(Word1, IT->sh_addr); + EHT = FindExceptionTable(SectionIndex, Entry * IndexTableEntrySize + 4); + } else { + TableEntryAddress = + PREL31(Word1, IT->sh_addr + Entry * IndexTableEntrySize + 4); + EHT = findSectionContainingAddress(ELF, FileName, TableEntryAddress); + } + + if (EHT) + // TODO: handle failure. + if (Expected<StringRef> Name = ELF.getSectionName(*EHT)) + SW.printString("ExceptionHandlingTable", *Name); + + SW.printHex(IsRelocatable ? "TableEntryOffset" : "TableEntryAddress", + TableEntryAddress); + if (EHT) { + if (IsRelocatable) + PrintExceptionTable(*EHT, TableEntryAddress); + else + PrintExceptionTable(*EHT, TableEntryAddress - EHT->sh_addr); + } + } + } +} + +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); + // TODO: handle failure. + if (Expected<StringRef> SectionName = ELF.getSectionName(Sec)) + SW.printString("SectionName", *SectionName); + SW.printHex("SectionOffset", Sec.sh_offset); + + PrintIndexTable(SectionIndex, &Sec); + } + ++SectionIndex; + } +} +} +} +} + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-readobj/ARMWinEHPrinter.cpp b/contrib/libs/llvm12/tools/llvm-readobj/ARMWinEHPrinter.cpp new file mode 100644 index 0000000000..5995a09514 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -0,0 +1,1301 @@ +//===-- 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 "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 }, + { 0xff, 0xe6, 1, &Decoder::opcode_save_next }, + { 0xff, 0xe8, 1, &Decoder::opcode_trap_frame }, + { 0xff, 0xe9, 1, &Decoder::opcode_machine_frame }, + { 0xff, 0xea, 1, &Decoder::opcode_context }, + { 0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call }, +}; + +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 inconvertibleErrorCode(); +} + +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 inconvertibleErrorCode(); +} + +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 inconvertibleErrorCode(); +} + +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 %s, %s\n", OC[Offset], + static_cast<const char *>(Prologue ? "fp" : "sp"), + static_cast<const char *>(Prologue ? "sp" : "fp")); + ++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 ; %s %s, %s, #%u\n", OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "add" : "sub"), + static_cast<const char *>(Prologue ? "fp" : "sp"), + static_cast<const char *>(Prologue ? "sp" : "fp"), 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; +} + +bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + if (Prologue) + SW.startLine() << format("0x%02x ; save next\n", OC[Offset]); + else + SW.startLine() << format("0x%02x ; restore next\n", + OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_trap_frame(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; trap frame\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_machine_frame(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; machine frame\n", + OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_context(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; context\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_clear_unwound_to_call(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + SW.startLine() << format("0x%02x ; clear unwound to call\n", + OC[Offset]); + ++Offset; + return false; +} + +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 uint64_t Address = COFF.getImageBase() + 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.printHex("Routine", Address); + SW.printHex("Parameter", Parameter); + 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, COFF.getImageBase() + 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::dumpPackedARM64Entry(const object::COFFObjectFile &COFF, + const SectionRef Section, uint64_t Offset, + unsigned Index, + const RuntimeFunctionARM64 &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.getPE32PlusHeader()->ImageBase + RF.BeginAddress; + } + + SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printBoolean("Fragment", + RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); + SW.printNumber("FunctionLength", RF.FunctionLength()); + SW.printNumber("RegF", RF.RegF()); + SW.printNumber("RegI", RF.RegI()); + SW.printBoolean("HomedParameters", RF.H()); + SW.printNumber("CR", RF.CR()); + SW.printNumber("FrameSize", RF.FrameSize() << 4); + ListScope PS(SW, "Prologue"); + + // Synthesize the equivalent prologue according to the documentation + // at https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling, + // printed in reverse order compared to the docs, to match how prologues + // are printed for the non-packed case. + int IntSZ = 8 * RF.RegI(); + if (RF.CR() == 1) + IntSZ += 8; + int FpSZ = 8 * RF.RegF(); + if (RF.RegF()) + FpSZ += 8; + int SavSZ = (IntSZ + FpSZ + 8 * 8 * RF.H() + 0xf) & ~0xf; + int LocSZ = (RF.FrameSize() << 4) - SavSZ; + + if (RF.CR() == 3) { + SW.startLine() << "mov x29, sp\n"; + if (LocSZ <= 512) { + SW.startLine() << format("stp x29, lr, [sp, #-%d]!\n", LocSZ); + } else { + SW.startLine() << "stp x29, lr, [sp, #0]\n"; + } + } + if (LocSZ > 4080) { + SW.startLine() << format("sub sp, sp, #%d\n", LocSZ - 4080); + SW.startLine() << "sub sp, sp, #4080\n"; + } else if ((RF.CR() != 3 && LocSZ > 0) || LocSZ > 512) { + SW.startLine() << format("sub sp, sp, #%d\n", LocSZ); + } + if (RF.H()) { + SW.startLine() << format("stp x6, x7, [sp, #%d]\n", IntSZ + FpSZ + 48); + SW.startLine() << format("stp x4, x5, [sp, #%d]\n", IntSZ + FpSZ + 32); + SW.startLine() << format("stp x2, x3, [sp, #%d]\n", IntSZ + FpSZ + 16); + if (RF.RegI() > 0 || RF.RegF() > 0 || RF.CR() == 1) { + SW.startLine() << format("stp x0, x1, [sp, #%d]\n", IntSZ + FpSZ); + } else { + // This case isn't documented; if neither RegI nor RegF nor CR=1 + // have decremented the stack pointer by SavSZ, we need to do it here + // (as the final stack adjustment of LocSZ excludes SavSZ). + SW.startLine() << format("stp x0, x1, [sp, #-%d]!\n", SavSZ); + } + } + int FloatRegs = RF.RegF() > 0 ? RF.RegF() + 1 : 0; + for (int I = (FloatRegs + 1) / 2 - 1; I >= 0; I--) { + if (I == (FloatRegs + 1) / 2 - 1 && FloatRegs % 2 == 1) { + // The last register, an odd register without a pair + SW.startLine() << format("str d%d, [sp, #%d]\n", 8 + 2 * I, + IntSZ + 16 * I); + } else if (I == 0 && RF.RegI() == 0 && RF.CR() != 1) { + SW.startLine() << format("stp d%d, d%d, [sp, #-%d]!\n", 8 + 2 * I, + 8 + 2 * I + 1, SavSZ); + } else { + SW.startLine() << format("stp d%d, d%d, [sp, #%d]\n", 8 + 2 * I, + 8 + 2 * I + 1, IntSZ + 16 * I); + } + } + if (RF.CR() == 1 && (RF.RegI() % 2) == 0) { + if (RF.RegI() == 0) + SW.startLine() << format("str lr, [sp, #-%d]!\n", SavSZ); + else + SW.startLine() << format("str lr, [sp, #%d]\n", IntSZ - 8); + } + for (int I = (RF.RegI() + 1) / 2 - 1; I >= 0; I--) { + if (I == (RF.RegI() + 1) / 2 - 1 && RF.RegI() % 2 == 1) { + // The last register, an odd register without a pair + if (RF.CR() == 1) { + if (I == 0) { // If this is the only register pair + // CR=1 combined with RegI=1 doesn't map to a documented case; + // it doesn't map to any regular unwind info opcode, and the + // actual unwinder doesn't support it. + SW.startLine() << "INVALID!\n"; + } else + SW.startLine() << format("stp x%d, lr, [sp, #%d]\n", 19 + 2 * I, + 16 * I); + } else { + if (I == 0) + SW.startLine() << format("str x%d, [sp, #-%d]!\n", 19 + 2 * I, SavSZ); + else + SW.startLine() << format("str x%d, [sp, #%d]\n", 19 + 2 * I, 16 * I); + } + } else if (I == 0) { + // The first register pair + SW.startLine() << format("stp x19, x20, [sp, #-%d]!\n", SavSZ); + } else { + SW.startLine() << format("stp x%d, x%d, [sp, #%d]\n", 19 + 2 * I, + 19 + 2 * I + 1, 16 * I); + } + } + SW.startLine() << "end\n"; + + 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) { + const RuntimeFunctionARM64 EntryARM64(Data); + return dumpPackedARM64Entry(COFF, Section, Offset, Index, EntryARM64); + } + 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/contrib/libs/llvm12/tools/llvm-readobj/ARMWinEHPrinter.h b/contrib/libs/llvm12/tools/llvm-readobj/ARMWinEHPrinter.h new file mode 100644 index 0000000000..3263841a26 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/ARMWinEHPrinter.h @@ -0,0 +1,177 @@ +//===--- 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 RuntimeFunctionARM64; + +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); + bool opcode_trap_frame(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_machine_frame(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_context(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_clear_unwound_to_call(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 dumpPackedARM64Entry(const object::COFFObjectFile &COFF, + const object::SectionRef Section, uint64_t Offset, + unsigned Index, const RuntimeFunctionARM64 &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/contrib/libs/llvm12/tools/llvm-readobj/COFFDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/COFFDumper.cpp new file mode 100644 index 0000000000..684967f933 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/COFFDumper.cpp @@ -0,0 +1,2057 @@ +//===-- 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 "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; + +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 GuardIatTableVA = 0; + uint64_t GuardIatTableCount = 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->getFileName()), 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 printCOFFTLSDirectory() 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; + void printCGProfile() override; + +private: + StringRef getSymbolName(uint32_t Index); + 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); + template <typename IntTy> + void printCOFFTLSDirectory(const coff_tls_directory<IntTy> *TlsTable); + 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 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::unique_ptr<ObjDumper> createCOFFDumper(const object::COFFObjectFile &Obj, + ScopedPrinter &Writer) { + return std::make_unique<COFFDumper>(&Obj, Writer); +} + +} // 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 inconvertibleErrorCode(); + Sym = *SymI; + return std::error_code(); +} + +// 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::ExtendedDLLCharacteristics> + PEExtendedDLLCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT), +}; + +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}, + {"ExtendedDLLCharacteristics", + COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS}, +}; + +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 std::error_code(); +} + +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 = Obj->getDataDirectory(Index); + if (!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("StringTableSize", Obj->getStringTableSize()); + 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); + // Ideally, if D.AddressOfRawData == 0, we should try to load the payload + // using D.PointerToRawData instead. + if (D.AddressOfRawData == 0) + continue; + if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) { + const codeview::DebugInfo *DebugInfo; + StringRef PDBFileName; + if (Error E = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName)) + reportError(std::move(E), 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: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and + // IMAGE_DEBUG_TYPE_POGO? + ArrayRef<uint8_t> RawData; + if (Error E = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, + D.SizeOfData, RawData)) + reportError(std::move(E), Obj->getFileName()); + if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) { + // FIXME right now the only possible value would fit in 8 bits, + // but that might change in the future + uint16_t Characteristics = RawData[0]; + W.printFlags("ExtendedCharacteristics", Characteristics, + makeArrayRef(PEExtendedDLLCharacteristics)); + } + W.printBinaryBlock("RawData", RawData); + } + } +} + +void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count, + uint64_t EntrySize, PrintExtraCB PrintExtra) { + uintptr_t TableStart, TableEnd; + if (Error E = Obj->getVaPtr(TableVA, TableStart)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = + Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd)) + reportError(std::move(E), 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.GuardIatTableVA) { + ListScope LS(W, "GuardIatTable"); + printRVATable(Tables.GuardIatTableVA, Tables.GuardIatTableCount, 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.GuardIatTableVA = Conf->GuardAddressTakenIatEntryTable; + Tables.GuardIatTableCount = Conf->GuardAddressTakenIatEntryCount; + + 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 = std::string(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); + Expected<const coff_section *> SecOrErr = + Obj->getSection(Symbol.getSectionNumber()); + if (!SecOrErr) { + W.startLine() << "Invalid section number: " << Symbol.getSectionNumber() + << "\n"; + W.flush(); + consumeError(SecOrErr.takeError()); + return; + } + const coff_section *Section = *SecOrErr; + + StringRef SymbolName; + if (Expected<StringRef> SymNameOrErr = Obj->getSymbolName(Symbol)) + SymbolName = *SymNameOrErr; + + StringRef SectionName; + if (Expected<StringRef> SecNameOrErr = + getSectionName(Obj, Symbol.getSectionNumber(), Section)) + SectionName = *SecNameOrErr; + + 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()); + + DictScope AS(W, "AuxWeakExternal"); + W.printNumber("Linked", getSymbolName(Aux->TagIndex), 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) { + Expected<const coff_section *> Assoc = Obj->getSection(AuxNumber); + if (!Assoc) + reportError(Assoc.takeError(), Obj->getFileName()); + Expected<StringRef> AssocName = getSectionName(Obj, AuxNumber, *Assoc); + if (!AssocName) + reportError(AssocName.takeError(), Obj->getFileName()); + + 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()); + + DictScope AS(W, "AuxCLRToken"); + W.printNumber("AuxType", Aux->AuxType); + W.printNumber("Reserved", Aux->Reserved); + W.printNumber("SymbolTableIndex", getSymbolName(Aux->SymbolTableIndex), + 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 (Error E = I.getSymbolName(Sym)) + reportError(std::move(E), Obj->getFileName()); + uint16_t Ordinal; + if (Error E = I.getOrdinal(Ordinal)) + reportError(std::move(E), 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 (Error E = S.getSymbolName(Sym)) + reportError(std::move(E), Obj->getFileName()); + + uint16_t Ordinal; + if (Error E = S.getOrdinal(Ordinal)) + reportError(std::move(E), Obj->getFileName()); + W.printNumber("Symbol", Sym, Ordinal); + + uint64_t Addr; + if (Error E = I.getImportAddress(Index++, Addr)) + reportError(std::move(E), 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 (Error E = I.getName(Name)) + reportError(std::move(E), Obj->getFileName()); + W.printString("Name", Name); + uint32_t ILTAddr; + if (Error E = I.getImportLookupTableRVA(ILTAddr)) + reportError(std::move(E), Obj->getFileName()); + W.printHex("ImportLookupTableRVA", ILTAddr); + uint32_t IATAddr; + if (Error E = I.getImportAddressTableRVA(IATAddr)) + reportError(std::move(E), 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 (Error E = I.getName(Name)) + reportError(std::move(E), Obj->getFileName()); + W.printString("Name", Name); + const delay_import_directory_table_entry *Table; + if (Error E = I.getDelayImportTable(Table)) + reportError(std::move(E), 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 &Exp : Obj->export_directories()) { + DictScope Export(W, "Export"); + + StringRef Name; + uint32_t Ordinal, RVA; + + if (Error E = Exp.getSymbolName(Name)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Exp.getOrdinal(Ordinal)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Exp.getExportRVA(RVA)) + reportError(std::move(E), 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 (Error E = I.getRVA(RVA)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = I.getType(Type)) + reportError(std::move(E), 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 { + 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 == 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() { + 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 == 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()); + + W.printNumber("Sym", getSymbolName(SymIndex), SymIndex); + Cur += Size; + } +} + +void COFFDumper::printCGProfile() { + SectionRef CGProfileSection; + for (SectionRef Sec : Obj->sections()) { + StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName()); + if (Name == ".llvm.call-graph-profile") { + CGProfileSection = Sec; + break; + } + } + + if (CGProfileSection == SectionRef()) + return; + + StringRef CGProfileContents = + unwrapOrError(Obj->getFileName(), CGProfileSection.getContents()); + BinaryStreamReader Reader(CGProfileContents, llvm::support::little); + + ListScope L(W, "CGProfile"); + while (!Reader.empty()) { + uint32_t FromIndex, ToIndex; + uint64_t Count; + if (Error Err = Reader.readInteger(FromIndex)) + reportError(std::move(Err), Obj->getFileName()); + if (Error Err = Reader.readInteger(ToIndex)) + reportError(std::move(Err), Obj->getFileName()); + if (Error Err = Reader.readInteger(Count)) + reportError(std::move(Err), Obj->getFileName()); + + DictScope D(W, "CGProfileEntry"); + W.printNumber("From", getSymbolName(FromIndex), FromIndex); + W.printNumber("To", getSymbolName(ToIndex), ToIndex); + W.printNumber("Weight", Count); + } +} + +StringRef COFFDumper::getSymbolName(uint32_t Index) { + Expected<COFFSymbolRef> Sym = Obj->getSymbol(Index); + if (!Sym) + reportError(Sym.takeError(), Obj->getFileName()); + + Expected<StringRef> SymName = Obj->getSymbolName(*Sym); + if (!SymName) + reportError(SymName.takeError(), Obj->getFileName()); + + return *SymName; +} + +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(); + } +} + +void COFFDumper::printCOFFTLSDirectory() { + if (Obj->is64()) + printCOFFTLSDirectory(Obj->getTLSDirectory64()); + else + printCOFFTLSDirectory(Obj->getTLSDirectory32()); +} + +template <typename IntTy> +void COFFDumper::printCOFFTLSDirectory( + const coff_tls_directory<IntTy> *TlsTable) { + DictScope D(W, "TLSDirectory"); + if (!TlsTable) + return; + + W.printHex("StartAddressOfRawData", TlsTable->StartAddressOfRawData); + W.printHex("EndAddressOfRawData", TlsTable->EndAddressOfRawData); + W.printHex("AddressOfIndex", TlsTable->AddressOfIndex); + W.printHex("AddressOfCallBacks", TlsTable->AddressOfCallBacks); + W.printHex("SizeOfZeroFill", TlsTable->SizeOfZeroFill); + W.printFlags("Characteristics", TlsTable->Characteristics, + makeArrayRef(ImageSectionCharacteristics), + COFF::SectionCharacteristics(COFF::IMAGE_SCN_ALIGN_MASK)); +} diff --git a/contrib/libs/llvm12/tools/llvm-readobj/COFFImportDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/COFFImportDumper.cpp new file mode 100644 index 0000000000..c9d5e82263 --- /dev/null +++ b/contrib/libs/llvm12/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/contrib/libs/llvm12/tools/llvm-readobj/DwarfCFIEHPrinter.h b/contrib/libs/llvm12/tools/llvm-readobj/DwarfCFIEHPrinter.h new file mode 100644 index 0000000000..2dfe21684a --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -0,0 +1,240 @@ +//===--- 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 "llvm-readobj.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Casting.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 DwarfCFIEH { + +template <typename ELFT> class PrinterContext { + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Phdr = typename ELFT::Phdr; + + ScopedPrinter &W; + const object::ELFObjectFile<ELFT> &ObjF; + + void printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const; + void printEHFrame(const Elf_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 ELFT::Shdr * +findSectionByAddress(const object::ELFObjectFile<ELFT> &ObjF, uint64_t Addr) { + Expected<typename ELFT::ShdrRange> SectionsOrErr = + ObjF.getELFFile().sections(); + if (!SectionsOrErr) + reportError(SectionsOrErr.takeError(), ObjF.getFileName()); + + for (const typename ELFT::Shdr &Shdr : *SectionsOrErr) + if (Shdr.sh_addr == Addr) + return &Shdr; + return nullptr; +} + +template <typename ELFT> +void PrinterContext<ELFT>::printUnwindInformation() const { + const object::ELFFile<ELFT> &Obj = ObjF.getELFFile(); + + Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj.program_headers(); + if (!PhdrsOrErr) + reportError(PhdrsOrErr.takeError(), ObjF.getFileName()); + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + if (Phdr.p_type != ELF::PT_GNU_EH_FRAME) + continue; + + if (Phdr.p_memsz != Phdr.p_filesz) + reportError(object::createError( + "p_memsz does not match p_filesz for GNU_EH_FRAME"), + ObjF.getFileName()); + printEHFrameHdr(&Phdr); + break; + } + + Expected<typename ELFT::ShdrRange> SectionsOrErr = Obj.sections(); + if (!SectionsOrErr) + reportError(SectionsOrErr.takeError(), ObjF.getFileName()); + + for (const Elf_Shdr &Shdr : *SectionsOrErr) { + Expected<StringRef> NameOrErr = Obj.getSectionName(Shdr); + if (!NameOrErr) + reportError(NameOrErr.takeError(), ObjF.getFileName()); + if (*NameOrErr == ".eh_frame") + printEHFrame(&Shdr); + } +} + +template <typename ELFT> +void PrinterContext<ELFT>::printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const { + DictScope L(W, "EHFrameHeader"); + uint64_t EHFrameHdrAddress = EHFramePHdr->p_vaddr; + W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress); + W.startLine() << format("Offset: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_offset); + W.startLine() << format("Size: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_memsz); + + const object::ELFFile<ELFT> &Obj = ObjF.getELFFile(); + if (const Elf_Shdr *EHFrameHdr = + findSectionByAddress(ObjF, EHFramePHdr->p_vaddr)) { + Expected<StringRef> NameOrErr = Obj.getSectionName(*EHFrameHdr); + if (!NameOrErr) + reportError(NameOrErr.takeError(), ObjF.getFileName()); + W.printString("Corresponding Section", *NameOrErr); + } + + Expected<ArrayRef<uint8_t>> Content = Obj.getSegmentContents(*EHFramePHdr); + if (!Content) + reportError(Content.takeError(), ObjF.getFileName()); + + DataExtractor DE(*Content, + 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 <= EHFramePHdr->p_memsz && 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 Elf_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(); + + Expected<ArrayRef<uint8_t>> DataOrErr = + ObjF.getELFFile().getSectionContents(*EHFrameShdr); + if (!DataOrErr) + reportError(DataOrErr.takeError(), ObjF.getFileName()); + + // Construct DWARFDataExtractor to handle relocations ("PC Begin" fields). + std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(ObjF, nullptr); + DWARFDataExtractor DE(DICtx->getDWARFObj(), + DICtx->getDWARFObj().getEHFrameSection(), + ELFT::TargetEndianness == support::endianness::little, + ELFT::Is64Bits ? 8 : 4); + DWARFDebugFrame EHFrame(Triple::ArchType(ObjF.getArch()), /*IsEH=*/true, + /*EHFrameAddress=*/Address); + if (Error E = EHFrame.parse(DE)) + reportError(std::move(E), ObjF.getFileName()); + + for (const dwarf::FrameEntry &Entry : EHFrame) { + if (const dwarf::CIE *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()); + } else { + const dwarf::FDE *FDE = 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(); + Entry.cfis().dump(W.getOStream(), DIDumpOptions(), nullptr, + W.getIndentLevel()); + W.unindent(); + W.unindent(); + W.getOStream() << "\n"; + } + + W.unindent(); +} +} // namespace DwarfCFIEH +} // namespace llvm + +#endif diff --git a/contrib/libs/llvm12/tools/llvm-readobj/ELFDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/ELFDumper.cpp new file mode 100644 index 0000000000..0f508f8dc0 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/ELFDumper.cpp @@ -0,0 +1,6763 @@ +//===- 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 "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/RISCVAttributeParser.h" +#include "llvm/Support/RISCVAttributes.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 <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 } + +namespace { + +template <class ELFT> struct RelSymbol { + RelSymbol(const typename ELFT::Sym *S, StringRef N) + : Sym(S), Name(N.str()) {} + const typename ELFT::Sym *Sym; + std::string Name; +}; + +/// 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(const Binary &Owner, const ObjDumper &D) + : Obj(&Owner), Dumper(&D) {} + DynRegionInfo(const Binary &Owner, const ObjDumper &D, const uint8_t *A, + uint64_t S, uint64_t ES) + : Addr(A), Size(S), EntSize(ES), Obj(&Owner), Dumper(&D) {} + + /// Address in current address space. + const uint8_t *Addr = nullptr; + /// Size in bytes of the region. + uint64_t Size = 0; + /// Size of each entity in the region. + uint64_t EntSize = 0; + + /// Owner object. Used for error reporting. + const Binary *Obj; + /// Dumper used for error reporting. + const ObjDumper *Dumper; + /// Error prefix. Used for error reporting to provide more information. + std::string Context; + /// Region size name. Used for error reporting. + StringRef SizePrintName = "size"; + /// Entry size name. Used for error reporting. If this field is empty, errors + /// will not mention the entry size. + StringRef EntSizePrintName = "entry size"; + + template <typename Type> ArrayRef<Type> getAsArrayRef() const { + const Type *Start = reinterpret_cast<const Type *>(Addr); + if (!Start) + return {Start, Start}; + + const uint64_t Offset = + Addr - (const uint8_t *)Obj->getMemoryBufferRef().getBufferStart(); + const uint64_t ObjSize = Obj->getMemoryBufferRef().getBufferSize(); + + if (Size > ObjSize - Offset) { + Dumper->reportUniqueWarning( + "unable to read data at 0x" + Twine::utohexstr(Offset) + + " of size 0x" + Twine::utohexstr(Size) + " (" + SizePrintName + + "): it goes past the end of the file of size 0x" + + Twine::utohexstr(ObjSize)); + return {Start, Start}; + } + + if (EntSize == sizeof(Type) && (Size % EntSize == 0)) + return {Start, Start + (Size / EntSize)}; + + std::string Msg; + if (!Context.empty()) + Msg += Context + " has "; + + Msg += ("invalid " + SizePrintName + " (0x" + Twine::utohexstr(Size) + ")") + .str(); + if (!EntSizePrintName.empty()) + Msg += + (" or " + EntSizePrintName + " (0x" + Twine::utohexstr(EntSize) + ")") + .str(); + + Dumper->reportUniqueWarning(Msg); + return {Start, Start}; + } +}; + +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; +}; + +namespace { + +struct NoteType { + uint32_t ID; + StringRef Name; +}; + +} // namespace + +template <class ELFT> class Relocation { +public: + Relocation(const typename ELFT::Rel &R, bool IsMips64EL) + : Type(R.getType(IsMips64EL)), Symbol(R.getSymbol(IsMips64EL)), + Offset(R.r_offset), Info(R.r_info) {} + + Relocation(const typename ELFT::Rela &R, bool IsMips64EL) + : Relocation((const typename ELFT::Rel &)R, IsMips64EL) { + Addend = R.r_addend; + } + + uint32_t Type; + uint32_t Symbol; + typename ELFT::uint Offset; + typename ELFT::uint Info; + Optional<int64_t> Addend; +}; + +template <class ELFT> class MipsGOTParser; + +template <typename ELFT> class ELFDumper : public ObjDumper { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + +public: + ELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer); + + void printUnwindInfo() override; + void printNeededLibraries() override; + void printHashTable() override; + void printGnuHashTable() override; + void printLoadName() override; + void printVersionInfo() override; + void printArchSpecificInfo() override; + void printStackMap() const override; + + const object::ELFObjectFile<ELFT> &getElfObject() const { return ObjF; }; + + std::string describe(const Elf_Shdr &Sec) const; + + unsigned getHashTableEntSize() const { + // EM_S390 and ELF::EM_ALPHA platforms use 8-bytes entries in SHT_HASH + // sections. This violates the ELF specification. + if (Obj.getHeader().e_machine == ELF::EM_S390 || + Obj.getHeader().e_machine == ELF::EM_ALPHA) + return 8; + return 4; + } + + 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.template 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 { + if (!DynSymRegion) + return Elf_Sym_Range(); + return DynSymRegion->template getAsArrayRef<Elf_Sym>(); + } + + const Elf_Shdr *findSectionByName(StringRef Name) const; + + StringRef getDynamicStringTable() const { return DynamicStringTable; } + +protected: + virtual void printVersionSymbolSection(const Elf_Shdr *Sec) = 0; + virtual void printVersionDefinitionSection(const Elf_Shdr *Sec) = 0; + virtual void printVersionDependencySection(const Elf_Shdr *Sec) = 0; + + void + printDependentLibsHelper(function_ref<void(const Elf_Shdr &)> OnSectionStart, + function_ref<void(StringRef, uint64_t)> OnLibEntry); + + virtual void printRelRelaReloc(const Relocation<ELFT> &R, + const RelSymbol<ELFT> &RelSym) = 0; + virtual void printRelrReloc(const Elf_Relr &R) = 0; + virtual void printDynamicRelocHeader(unsigned Type, StringRef Name, + const DynRegionInfo &Reg) {} + void printReloc(const Relocation<ELFT> &R, unsigned RelIndex, + const Elf_Shdr &Sec, const Elf_Shdr *SymTab); + void printDynamicReloc(const Relocation<ELFT> &R); + void printDynamicRelocationsHelper(); + void printRelocationsHelper(const Elf_Shdr &Sec); + void forEachRelocationDo( + const Elf_Shdr &Sec, bool RawRelr, + llvm::function_ref<void(const Relocation<ELFT> &, unsigned, + const Elf_Shdr &, const Elf_Shdr *)> + RelRelaFn, + llvm::function_ref<void(const Elf_Relr &)> RelrFn); + + virtual void printSymtabMessage(const Elf_Shdr *Symtab, size_t Offset, + bool NonVisibilityBitsUsed) const {}; + virtual void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, bool IsDynamic, + bool NonVisibilityBitsUsed) const = 0; + + virtual void printMipsABIFlags() = 0; + virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0; + virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0; + + Expected<ArrayRef<Elf_Versym>> + getVersionTable(const Elf_Shdr &Sec, ArrayRef<Elf_Sym> *SymTab, + StringRef *StrTab, const Elf_Shdr **SymTabSec) const; + StringRef getPrintableSectionName(const Elf_Shdr &Sec) const; + + std::vector<GroupSection> getGroups(); + + bool printFunctionStackSize(uint64_t SymValue, + Optional<const Elf_Shdr *> FunctionSec, + const Elf_Shdr &StackSizeSec, DataExtractor Data, + uint64_t *Offset); + void printStackSize(const Relocation<ELFT> &R, const Elf_Shdr &RelocSec, + unsigned Ndx, const Elf_Shdr *SymTab, + const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec, + const RelocationResolver &Resolver, DataExtractor Data); + virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0; + + void printRelocatableStackSizes(std::function<void()> PrintHeader); + void printNonRelocatableStackSizes(std::function<void()> PrintHeader); + + const object::ELFObjectFile<ELFT> &ObjF; + const ELFFile<ELFT> &Obj; + StringRef FileName; + + Expected<DynRegionInfo> createDRI(uint64_t Offset, uint64_t Size, + uint64_t EntSize) { + if (Offset + Size < Offset || Offset + Size > Obj.getBufSize()) + return createError("offset (0x" + Twine::utohexstr(Offset) + + ") + size (0x" + Twine::utohexstr(Size) + + ") is greater than the file size (0x" + + Twine::utohexstr(Obj.getBufSize()) + ")"); + return DynRegionInfo(ObjF, *this, Obj.base() + Offset, Size, EntSize); + } + + void printAttributes(); + void printMipsReginfo(); + void printMipsOptions(); + + std::pair<const Elf_Phdr *, const Elf_Shdr *> findDynamic(); + void loadDynamicTable(); + void parseDynamicTable(); + + Expected<StringRef> getSymbolVersion(const Elf_Sym &Sym, + bool &IsDefault) const; + Expected<SmallVector<Optional<VersionEntry>, 0> *> getVersionMap() const; + + DynRegionInfo DynRelRegion; + DynRegionInfo DynRelaRegion; + DynRegionInfo DynRelrRegion; + DynRegionInfo DynPLTRelRegion; + Optional<DynRegionInfo> DynSymRegion; + DynRegionInfo DynSymTabShndxRegion; + DynRegionInfo DynamicTable; + StringRef DynamicStringTable; + const Elf_Hash *HashTable = nullptr; + const Elf_GnuHash *GnuHashTable = nullptr; + const Elf_Shdr *DotSymtabSec = nullptr; + const Elf_Shdr *DotDynsymSec = nullptr; + const Elf_Shdr *DotCGProfileSec = nullptr; + const Elf_Shdr *DotAddrsigSec = nullptr; + DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables; + Optional<uint64_t> SONameOffset; + + const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version + const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r + const Elf_Shdr *SymbolVersionDefSection = nullptr; // .gnu.version_d + + std::string getFullSymbolName(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, + bool IsDynamic) const; + Expected<unsigned> + getSymbolSectionIndex(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable) const; + Expected<StringRef> getSymbolSectionName(const Elf_Sym &Symbol, + unsigned SectionIndex) const; + std::string getStaticSymbolName(uint32_t Index) const; + StringRef getDynamicString(uint64_t Value) const; + + void printSymbolsHelper(bool IsDynamic) const; + std::string getDynamicEntry(uint64_t Type, uint64_t Value) const; + + Expected<RelSymbol<ELFT>> getRelocationTarget(const Relocation<ELFT> &R, + const Elf_Shdr *SymTab) const; + + ArrayRef<Elf_Word> getShndxTable(const Elf_Shdr *Symtab) const; + +private: + mutable SmallVector<Optional<VersionEntry>, 0> VersionMap; +}; + +template <class ELFT> +std::string ELFDumper<ELFT>::describe(const Elf_Shdr &Sec) const { + return ::describe(Obj, Sec); +} + +namespace { + +template <class ELFT> struct SymtabLink { + typename ELFT::SymRange Symbols; + StringRef StringTable; + const typename ELFT::Shdr *SymTab; +}; + +// Returns the linked symbol table, symbols and associated string table for a +// given section. +template <class ELFT> +Expected<SymtabLink<ELFT>> getLinkAsSymtab(const ELFFile<ELFT> &Obj, + const typename ELFT::Shdr &Sec, + unsigned ExpectedType) { + Expected<const typename ELFT::Shdr *> SymtabOrErr = + Obj.getSection(Sec.sh_link); + if (!SymtabOrErr) + return createError("invalid section linked to " + describe(Obj, Sec) + + ": " + toString(SymtabOrErr.takeError())); + + if ((*SymtabOrErr)->sh_type != ExpectedType) + return createError( + "invalid section linked to " + describe(Obj, Sec) + ": expected " + + object::getELFSectionTypeName(Obj.getHeader().e_machine, ExpectedType) + + ", but got " + + object::getELFSectionTypeName(Obj.getHeader().e_machine, + (*SymtabOrErr)->sh_type)); + + Expected<StringRef> StrTabOrErr = Obj.getLinkAsStrtab(**SymtabOrErr); + if (!StrTabOrErr) + return createError( + "can't get a string table for the symbol table linked to " + + describe(Obj, Sec) + ": " + toString(StrTabOrErr.takeError())); + + Expected<typename ELFT::SymRange> SymsOrErr = Obj.symbols(*SymtabOrErr); + if (!SymsOrErr) + return createError("unable to read symbols from the " + describe(Obj, Sec) + + ": " + toString(SymsOrErr.takeError())); + + return SymtabLink<ELFT>{*SymsOrErr, *StrTabOrErr, *SymtabOrErr}; +} + +} // namespace + +template <class ELFT> +Expected<ArrayRef<typename ELFT::Versym>> +ELFDumper<ELFT>::getVersionTable(const Elf_Shdr &Sec, ArrayRef<Elf_Sym> *SymTab, + StringRef *StrTab, + const Elf_Shdr **SymTabSec) const { + assert((!SymTab && !StrTab && !SymTabSec) || (SymTab && StrTab && SymTabSec)); + if (reinterpret_cast<uintptr_t>(Obj.base() + Sec.sh_offset) % + sizeof(uint16_t) != + 0) + return createError("the " + describe(Sec) + " is misaligned"); + + Expected<ArrayRef<Elf_Versym>> VersionsOrErr = + Obj.template getSectionContentsAsArray<Elf_Versym>(Sec); + if (!VersionsOrErr) + return createError("cannot read content of " + describe(Sec) + ": " + + toString(VersionsOrErr.takeError())); + + Expected<SymtabLink<ELFT>> SymTabOrErr = + getLinkAsSymtab(Obj, Sec, SHT_DYNSYM); + if (!SymTabOrErr) { + reportUniqueWarning(SymTabOrErr.takeError()); + return *VersionsOrErr; + } + + if (SymTabOrErr->Symbols.size() != VersionsOrErr->size()) + reportUniqueWarning(describe(Sec) + ": the number of entries (" + + Twine(VersionsOrErr->size()) + + ") does not match the number of symbols (" + + Twine(SymTabOrErr->Symbols.size()) + + ") in the symbol table with index " + + Twine(Sec.sh_link)); + + if (SymTab) { + *SymTab = SymTabOrErr->Symbols; + *StrTab = SymTabOrErr->StringTable; + *SymTabSec = SymTabOrErr->SymTab; + } + return *VersionsOrErr; +} + +template <class ELFT> +void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const { + Optional<StringRef> StrTable; + size_t Entries = 0; + Elf_Sym_Range Syms(nullptr, nullptr); + const Elf_Shdr *SymtabSec = IsDynamic ? DotDynsymSec : DotSymtabSec; + + if (IsDynamic) { + StrTable = DynamicStringTable; + Syms = dynamic_symbols(); + Entries = Syms.size(); + } else if (DotSymtabSec) { + if (Expected<StringRef> StrTableOrErr = + Obj.getStringTableForSymtab(*DotSymtabSec)) + StrTable = *StrTableOrErr; + else + reportUniqueWarning( + "unable to get the string table for the SHT_SYMTAB section: " + + toString(StrTableOrErr.takeError())); + + if (Expected<Elf_Sym_Range> SymsOrErr = Obj.symbols(DotSymtabSec)) + Syms = *SymsOrErr; + else + reportUniqueWarning( + "unable to read symbols from the SHT_SYMTAB section: " + + toString(SymsOrErr.takeError())); + Entries = DotSymtabSec->getEntityCount(); + } + if (Syms.empty()) + 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::any_of(Syms, [](const Elf_Sym &S) { return S.st_other & ~0x3; }); + + DataRegion<Elf_Word> ShndxTable = + IsDynamic ? DataRegion<Elf_Word>( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, + this->getElfObject().getELFFile().end()) + : DataRegion<Elf_Word>(this->getShndxTable(SymtabSec)); + + printSymtabMessage(SymtabSec, Entries, NonVisibilityBitsUsed); + for (const Elf_Sym &Sym : Syms) + printSymbol(Sym, &Sym - Syms.begin(), ShndxTable, StrTable, IsDynamic, + NonVisibilityBitsUsed); +} + +template <typename ELFT> class GNUELFDumper : public ELFDumper<ELFT> { + formatted_raw_ostream &OS; + +public: + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + + GNUELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer) + : ELFDumper<ELFT>(ObjF, Writer), + OS(static_cast<formatted_raw_ostream &>(Writer.getOStream())) { + assert(&this->W.getOStream() == &llvm::fouts()); + } + + void printFileHeaders() override; + void printGroupSections() override; + void printRelocations() override; + void printSectionHeaders() override; + void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; + void printHashSymbols() override; + void printSectionDetails() override; + void printDependentLibs() override; + void printDynamicTable() override; + void printDynamicRelocations() override; + void printSymtabMessage(const Elf_Shdr *Symtab, size_t Offset, + bool NonVisibilityBitsUsed) const override; + void printProgramHeaders(bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const Elf_Shdr *Sec) override; + void printVersionDependencySection(const Elf_Shdr *Sec) override; + void printHashHistograms() override; + void printCGProfile() override; + void printAddrsig() override; + void printNotes() override; + void printELFLinkerOptions() override; + void printStackSizes() override; + +private: + void printHashHistogram(const Elf_Hash &HashTable); + void printGnuHashHistogram(const Elf_GnuHash &GnuHashTable); + void printHashTableSymbols(const Elf_Hash &HashTable); + void printGnuHashTableSymbols(const Elf_GnuHash &GnuHashTable); + + struct Field { + std::string Str; + unsigned Column; + + Field(StringRef S, unsigned Col) : Str(std::string(S)), Column(Col) {} + Field(unsigned Col) : Column(Col) {} + }; + + template <typename T, typename TEnum> + std::string printEnum(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) const { + for (const EnumEntry<TEnum> &EnumItem : EnumValues) + if (EnumItem.Value == Value) + return std::string(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 = {}) const { + std::string Str; + for (const EnumEntry<TEnum> &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) const { + if (F.Column != 0) + OS.PadToColumn(F.Column); + OS << F.Str; + OS.flush(); + return OS; + } + void printHashedSymbol(const Elf_Sym *Sym, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, StringRef StrTable, + uint32_t Bucket); + void printRelrReloc(const Elf_Relr &R) override; + void printRelRelaReloc(const Relocation<ELFT> &R, + const RelSymbol<ELFT> &RelSym) override; + void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, bool IsDynamic, + bool NonVisibilityBitsUsed) const override; + void printDynamicRelocHeader(unsigned Type, StringRef Name, + const DynRegionInfo &Reg) override; + + std::string getSymbolSectionNdx(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable) const; + void printProgramHeaders() override; + void printSectionMapping() override; + void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec, + const Twine &Label, unsigned EntriesNum); + + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + + void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsABIFlags() override; +}; + +template <typename ELFT> class LLVMELFDumper : public ELFDumper<ELFT> { +public: + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + + LLVMELFDumper(const object::ELFObjectFile<ELFT> &ObjF, ScopedPrinter &Writer) + : ELFDumper<ELFT>(ObjF, Writer), W(Writer) {} + + void printFileHeaders() override; + void printGroupSections() override; + void printRelocations() override; + void printSectionHeaders() override; + void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; + void printDependentLibs() override; + void printDynamicTable() override; + void printDynamicRelocations() override; + void printProgramHeaders(bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const Elf_Shdr *Sec) override; + void printVersionDependencySection(const Elf_Shdr *Sec) override; + void printHashHistograms() override; + void printCGProfile() override; + void printAddrsig() override; + void printNotes() override; + void printELFLinkerOptions() override; + void printStackSizes() override; + +private: + void printRelrReloc(const Elf_Relr &R) override; + void printRelRelaReloc(const Relocation<ELFT> &R, + const RelSymbol<ELFT> &RelSym) override; + + void printSymbolSection(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable) const; + void printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, bool IsDynamic, + bool /*NonVisibilityBitsUsed*/) const override; + void printProgramHeaders() override; + void printSectionMapping() 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() override; + + ScopedPrinter &W; +}; + +} // end anonymous namespace + +namespace llvm { + +template <class ELFT> +static std::unique_ptr<ObjDumper> +createELFDumper(const ELFObjectFile<ELFT> &Obj, ScopedPrinter &Writer) { + if (opts::Output == opts::GNU) + return std::make_unique<GNUELFDumper<ELFT>>(Obj, Writer); + return std::make_unique<LLVMELFDumper<ELFT>>(Obj, Writer); +} + +std::unique_ptr<ObjDumper> createELFDumper(const object::ELFObjectFileBase &Obj, + ScopedPrinter &Writer) { + // Little-endian 32-bit + if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(&Obj)) + return createELFDumper(*ELFObj, Writer); + + // Big-endian 32-bit + if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(&Obj)) + return createELFDumper(*ELFObj, Writer); + + // Little-endian 64-bit + if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(&Obj)) + return createELFDumper(*ELFObj, Writer); + + // Big-endian 64-bit + return createELFDumper(*cast<ELF64BEObjectFile>(&Obj), Writer); +} + +} // end namespace llvm + +template <class ELFT> +Expected<SmallVector<Optional<VersionEntry>, 0> *> +ELFDumper<ELFT>::getVersionMap() const { + // If the VersionMap has already been loaded or if there is no dynamic symtab + // or version table, there is nothing to do. + if (!VersionMap.empty() || !DynSymRegion || !SymbolVersionSection) + return &VersionMap; + + Expected<SmallVector<Optional<VersionEntry>, 0>> MapOrErr = + Obj.loadVersionMap(SymbolVersionNeedSection, SymbolVersionDefSection); + if (MapOrErr) + VersionMap = *MapOrErr; + else + return MapOrErr.takeError(); + + return &VersionMap; +} + +template <typename ELFT> +Expected<StringRef> ELFDumper<ELFT>::getSymbolVersion(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 ""; + } + + assert(DynSymRegion && "DynSymRegion has not been initialised"); + // 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. + Expected<const Elf_Versym *> EntryOrErr = + Obj.template getEntry<Elf_Versym>(*SymbolVersionSection, EntryIndex); + if (!EntryOrErr) + return EntryOrErr.takeError(); + + unsigned Version = (*EntryOrErr)->vs_index; + if (Version == VER_NDX_LOCAL || Version == VER_NDX_GLOBAL) { + IsDefault = false; + return ""; + } + + Expected<SmallVector<Optional<VersionEntry>, 0> *> MapOrErr = + getVersionMap(); + if (!MapOrErr) + return MapOrErr.takeError(); + + return Obj.getSymbolVersionByIndex(Version, IsDefault, **MapOrErr, + Sym.st_shndx == ELF::SHN_UNDEF); +} + +template <typename ELFT> +Expected<RelSymbol<ELFT>> +ELFDumper<ELFT>::getRelocationTarget(const Relocation<ELFT> &R, + const Elf_Shdr *SymTab) const { + if (R.Symbol == 0) + return RelSymbol<ELFT>(nullptr, ""); + + Expected<const Elf_Sym *> SymOrErr = + Obj.template getEntry<Elf_Sym>(*SymTab, R.Symbol); + if (!SymOrErr) + return createError("unable to read an entry with index " + Twine(R.Symbol) + + " from " + describe(*SymTab) + ": " + + toString(SymOrErr.takeError())); + const Elf_Sym *Sym = *SymOrErr; + if (!Sym) + return RelSymbol<ELFT>(nullptr, ""); + + Expected<StringRef> StrTableOrErr = Obj.getStringTableForSymtab(*SymTab); + if (!StrTableOrErr) + return StrTableOrErr.takeError(); + + const Elf_Sym *FirstSym = + cantFail(Obj.template getEntry<Elf_Sym>(*SymTab, 0)); + std::string SymbolName = + getFullSymbolName(*Sym, Sym - FirstSym, getShndxTable(SymTab), + *StrTableOrErr, SymTab->sh_type == SHT_DYNSYM); + return RelSymbol<ELFT>(Sym, SymbolName); +} + +template <typename ELFT> +ArrayRef<typename ELFT::Word> +ELFDumper<ELFT>::getShndxTable(const Elf_Shdr *Symtab) const { + if (Symtab) { + auto It = ShndxTables.find(Symtab); + if (It != ShndxTables.end()) + return It->second; + } + return {}; +} + +static std::string maybeDemangle(StringRef Name) { + return opts::Demangle ? demangle(std::string(Name)) : Name.str(); +} + +template <typename ELFT> +std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { + auto Warn = [&](Error E) -> std::string { + reportUniqueWarning("unable to read the name of symbol with index " + + Twine(Index) + ": " + toString(std::move(E))); + return "<?>"; + }; + + Expected<const typename ELFT::Sym *> SymOrErr = + Obj.getSymbol(DotSymtabSec, Index); + if (!SymOrErr) + return Warn(SymOrErr.takeError()); + + Expected<StringRef> StrTabOrErr = Obj.getStringTableForSymtab(*DotSymtabSec); + if (!StrTabOrErr) + return Warn(StrTabOrErr.takeError()); + + Expected<StringRef> NameOrErr = (*SymOrErr)->getName(*StrTabOrErr); + if (!NameOrErr) + return Warn(NameOrErr.takeError()); + return maybeDemangle(*NameOrErr); +} + +template <typename ELFT> +std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym &Symbol, + unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, + bool IsDynamic) const { + if (!StrTable) + return "<?>"; + + std::string SymbolName; + if (Expected<StringRef> NameOrErr = Symbol.getName(*StrTable)) { + SymbolName = maybeDemangle(*NameOrErr); + } else { + reportUniqueWarning(NameOrErr.takeError()); + return "<?>"; + } + + if (SymbolName.empty() && Symbol.getType() == ELF::STT_SECTION) { + Expected<unsigned> SectionIndex = + getSymbolSectionIndex(Symbol, SymIndex, ShndxTable); + if (!SectionIndex) { + reportUniqueWarning(SectionIndex.takeError()); + return "<?>"; + } + Expected<StringRef> NameOrErr = getSymbolSectionName(Symbol, *SectionIndex); + if (!NameOrErr) { + reportUniqueWarning(NameOrErr.takeError()); + return ("<section " + Twine(*SectionIndex) + ">").str(); + } + return std::string(*NameOrErr); + } + + if (!IsDynamic) + return SymbolName; + + bool IsDefault; + Expected<StringRef> VersionOrErr = getSymbolVersion(Symbol, IsDefault); + if (!VersionOrErr) { + reportUniqueWarning(VersionOrErr.takeError()); + return SymbolName + "@<corrupt>"; + } + + if (!VersionOrErr->empty()) { + SymbolName += (IsDefault ? "@@" : "@"); + SymbolName += *VersionOrErr; + } + return SymbolName; +} + +template <typename ELFT> +Expected<unsigned> +ELFDumper<ELFT>::getSymbolSectionIndex(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable) const { + unsigned Ndx = Symbol.st_shndx; + if (Ndx == SHN_XINDEX) + return object::getExtendedSymbolTableIndex<ELFT>(Symbol, SymIndex, + ShndxTable); + if (Ndx != SHN_UNDEF && Ndx < SHN_LORESERVE) + return Ndx; + + auto CreateErr = [&](const Twine &Name, Optional<unsigned> Offset = None) { + std::string Desc; + if (Offset) + Desc = (Name + "+0x" + Twine::utohexstr(*Offset)).str(); + else + Desc = Name.str(); + return createError( + "unable to get section index for symbol with st_shndx = 0x" + + Twine::utohexstr(Ndx) + " (" + Desc + ")"); + }; + + if (Ndx >= ELF::SHN_LOPROC && Ndx <= ELF::SHN_HIPROC) + return CreateErr("SHN_LOPROC", Ndx - ELF::SHN_LOPROC); + if (Ndx >= ELF::SHN_LOOS && Ndx <= ELF::SHN_HIOS) + return CreateErr("SHN_LOOS", Ndx - ELF::SHN_LOOS); + if (Ndx == ELF::SHN_UNDEF) + return CreateErr("SHN_UNDEF"); + if (Ndx == ELF::SHN_ABS) + return CreateErr("SHN_ABS"); + if (Ndx == ELF::SHN_COMMON) + return CreateErr("SHN_COMMON"); + return CreateErr("SHN_LORESERVE", Ndx - SHN_LORESERVE); +} + +template <typename ELFT> +Expected<StringRef> +ELFDumper<ELFT>::getSymbolSectionName(const Elf_Sym &Symbol, + unsigned SectionIndex) const { + Expected<const Elf_Shdr *> SecOrErr = Obj.getSection(SectionIndex); + if (!SecOrErr) + return SecOrErr.takeError(); + return Obj.getSectionName(**SecOrErr); +} + +template <class ELFO> +static const typename ELFO::Elf_Shdr * +findNotEmptySectionByAddress(const ELFO &Obj, StringRef FileName, + uint64_t Addr) { + for (const typename ELFO::Elf_Shdr &Shdr : cantFail(Obj.sections())) + if (Shdr.sh_addr == Addr && Shdr.sh_size > 0) + 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> 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"), + // FIXME: Following EM_ECOG1X definitions is dead code since EM_ECOG1X has + // an identical number to EM_ECOG1. + 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"), + ENUM_ENT(EM_VE, "NEC SX-Aurora Vector Engine"), +}; + +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_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_COMPRESSED, "C"), + ENUM_ENT(SHF_EXCLUDE, "E"), +}; + +static const EnumEntry<unsigned> ElfXCoreSectionFlags[] = { + ENUM_ENT(XCORE_SHF_CP_SECTION, ""), + ENUM_ENT(XCORE_SHF_DP_SECTION, "") +}; + +static const EnumEntry<unsigned> ElfARMSectionFlags[] = { + ENUM_ENT(SHF_ARM_PURECODE, "y") +}; + +static const EnumEntry<unsigned> ElfHexagonSectionFlags[] = { + ENUM_ENT(SHF_HEX_GPREL, "") +}; + +static const EnumEntry<unsigned> ElfMipsSectionFlags[] = { + ENUM_ENT(SHF_MIPS_NODUPES, ""), + ENUM_ENT(SHF_MIPS_NAMES, ""), + ENUM_ENT(SHF_MIPS_LOCAL, ""), + ENUM_ENT(SHF_MIPS_NOSTRIP, ""), + ENUM_ENT(SHF_MIPS_GPREL, ""), + ENUM_ENT(SHF_MIPS_MERGE, ""), + ENUM_ENT(SHF_MIPS_ADDR, ""), + ENUM_ENT(SHF_MIPS_STRING, "") +}; + +static const EnumEntry<unsigned> ElfX86_64SectionFlags[] = { + ENUM_ENT(SHF_X86_64_LARGE, "l") +}; + +static std::vector<EnumEntry<unsigned>> +getSectionFlagsForTarget(unsigned EMachine) { + std::vector<EnumEntry<unsigned>> Ret(std::begin(ElfSectionFlags), + std::end(ElfSectionFlags)); + switch (EMachine) { + case EM_ARM: + Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags), + std::end(ElfARMSectionFlags)); + break; + case EM_HEXAGON: + Ret.insert(Ret.end(), std::begin(ElfHexagonSectionFlags), + std::end(ElfHexagonSectionFlags)); + break; + case EM_MIPS: + Ret.insert(Ret.end(), std::begin(ElfMipsSectionFlags), + std::end(ElfMipsSectionFlags)); + break; + case EM_X86_64: + Ret.insert(Ret.end(), std::begin(ElfX86_64SectionFlags), + std::end(ElfX86_64SectionFlags)); + break; + case EM_XCORE: + Ret.insert(Ret.end(), std::begin(ElfXCoreSectionFlags), + std::end(ElfXCoreSectionFlags)); + break; + default: + break; + } + return Ret; +} + +static std::string getGNUFlags(unsigned EMachine, uint64_t Flags) { + // Here we are trying to build the flags string in the same way as GNU does. + // It is not that straightforward. Imagine we have sh_flags == 0x90000000. + // SHF_EXCLUDE ("E") has a value of 0x80000000 and SHF_MASKPROC is 0xf0000000. + // GNU readelf will not print "E" or "Ep" in this case, but will print just + // "p". It only will print "E" when no other processor flag is set. + std::string Str; + bool HasUnknownFlag = false; + bool HasOSFlag = false; + bool HasProcFlag = false; + std::vector<EnumEntry<unsigned>> FlagsList = + getSectionFlagsForTarget(EMachine); + while (Flags) { + // Take the least significant bit as a flag. + uint64_t Flag = Flags & -Flags; + Flags -= Flag; + + // Find the flag in the known flags list. + auto I = llvm::find_if(FlagsList, [=](const EnumEntry<unsigned> &E) { + // Flags with empty names are not printed in GNU style output. + return E.Value == Flag && !E.AltName.empty(); + }); + if (I != FlagsList.end()) { + Str += I->AltName; + continue; + } + + // If we did not find a matching regular flag, then we deal with an OS + // specific flag, processor specific flag or an unknown flag. + if (Flag & ELF::SHF_MASKOS) { + HasOSFlag = true; + Flags &= ~ELF::SHF_MASKOS; + } else if (Flag & ELF::SHF_MASKPROC) { + HasProcFlag = true; + // Mask off all the processor-specific bits. This removes the SHF_EXCLUDE + // bit if set so that it doesn't also get printed. + Flags &= ~ELF::SHF_MASKPROC; + } else { + HasUnknownFlag = true; + } + } + + // "o", "p" and "x" are printed last. + if (HasOSFlag) + Str += "o"; + if (HasProcFlag) + Str += "p"; + if (HasUnknownFlag) + Str += "x"; + return Str; +} + +static StringRef segmentTypeToString(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_GNU_PROPERTY); + + 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 getGNUPtType(unsigned Arch, unsigned Type) { + StringRef Seg = segmentTypeToString(Arch, Type); + if (Seg.empty()) + return std::string("<unknown>: ") + to_string(format_hex(Type, 1)); + + // E.g. "PT_ARM_EXIDX" -> "EXIDX". + if (Seg.startswith("PT_ARM_")) + return Seg.drop_front(7).str(); + + // E.g. "PT_MIPS_REGINFO" -> "REGINFO". + if (Seg.startswith("PT_MIPS_")) + return Seg.drop_front(8).str(); + + // E.g. "PT_LOAD" -> "LOAD". + assert(Seg.startswith("PT_")); + return Seg.drop_front(3).str(); +} + +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_GFX602), + 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_GFX705), + 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_GFX805), + 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_GFX90C), + 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_MACH_AMDGCN_GFX1030), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033), + 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> ElfAArch64SymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_AARCH64_VARIANT_PCS) +}; + +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() { + // Try to locate the PT_DYNAMIC header. + const Elf_Phdr *DynamicPhdr = nullptr; + if (Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj.program_headers()) { + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + if (Phdr.p_type != ELF::PT_DYNAMIC) + continue; + DynamicPhdr = &Phdr; + break; + } + } else { + reportUniqueWarning( + "unable to read program headers to locate the PT_DYNAMIC segment: " + + toString(PhdrsOrErr.takeError())); + } + + // Try to locate the .dynamic section in the sections header table. + const Elf_Shdr *DynamicSec = nullptr; + for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { + if (Sec.sh_type != ELF::SHT_DYNAMIC) + continue; + DynamicSec = &Sec; + break; + } + + if (DynamicPhdr && ((DynamicPhdr->p_offset + DynamicPhdr->p_filesz > + ObjF.getMemoryBufferRef().getBufferSize()) || + (DynamicPhdr->p_offset + DynamicPhdr->p_filesz < + DynamicPhdr->p_offset))) { + reportUniqueWarning( + "PT_DYNAMIC segment offset (0x" + + Twine::utohexstr(DynamicPhdr->p_offset) + ") + file size (0x" + + Twine::utohexstr(DynamicPhdr->p_filesz) + + ") exceeds the size of the file (0x" + + Twine::utohexstr(ObjF.getMemoryBufferRef().getBufferSize()) + ")"); + // Don't use the broken dynamic header. + DynamicPhdr = nullptr; + } + + if (DynamicPhdr && DynamicSec) { + if (DynamicSec->sh_addr + DynamicSec->sh_size > + DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz || + DynamicSec->sh_addr < DynamicPhdr->p_vaddr) + reportUniqueWarning(describe(*DynamicSec) + + " is not contained within the " + "PT_DYNAMIC segment"); + + if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr) + reportUniqueWarning(describe(*DynamicSec) + " is not at the start of " + "PT_DYNAMIC segment"); + } + + return std::make_pair(DynamicPhdr, DynamicSec); +} + +template <typename ELFT> +void ELFDumper<ELFT>::loadDynamicTable() { + const Elf_Phdr *DynamicPhdr; + const Elf_Shdr *DynamicSec; + std::tie(DynamicPhdr, DynamicSec) = findDynamic(); + if (!DynamicPhdr && !DynamicSec) + return; + + DynRegionInfo FromPhdr(ObjF, *this); + bool IsPhdrTableValid = false; + if (DynamicPhdr) { + // Use cantFail(), because p_offset/p_filesz fields of a PT_DYNAMIC are + // validated in findDynamic() and so createDRI() is not expected to fail. + FromPhdr = cantFail(createDRI(DynamicPhdr->p_offset, DynamicPhdr->p_filesz, + sizeof(Elf_Dyn))); + FromPhdr.SizePrintName = "PT_DYNAMIC size"; + FromPhdr.EntSizePrintName = ""; + IsPhdrTableValid = !FromPhdr.template 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, *this); + bool IsSecTableValid = false; + if (DynamicSec) { + Expected<DynRegionInfo> RegOrErr = + createDRI(DynamicSec->sh_offset, DynamicSec->sh_size, sizeof(Elf_Dyn)); + if (RegOrErr) { + FromSec = *RegOrErr; + FromSec.Context = describe(*DynamicSec); + FromSec.EntSizePrintName = ""; + IsSecTableValid = !FromSec.template getAsArrayRef<Elf_Dyn>().empty(); + } else { + reportUniqueWarning("unable to read the dynamic table from " + + describe(*DynamicSec) + ": " + + toString(RegOrErr.takeError())); + } + } + + // 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 { + reportUniqueWarning("no valid dynamic table was found"); + } + 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) + reportUniqueWarning("SHT_DYNAMIC section header and PT_DYNAMIC " + "program header disagree about " + "the location of the dynamic table"); + + if (!IsPhdrTableValid && !IsSecTableValid) { + reportUniqueWarning("no valid dynamic table was found"); + return; + } + + // Information in the PT_DYNAMIC program header has priority over the + // information in a section header. + if (IsPhdrTableValid) { + if (!IsSecTableValid) + reportUniqueWarning( + "SHT_DYNAMIC dynamic table is invalid: PT_DYNAMIC will be used"); + DynamicTable = FromPhdr; + } else { + reportUniqueWarning( + "PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used"); + DynamicTable = FromSec; + } + + parseDynamicTable(); +} + +template <typename ELFT> +ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> &O, + ScopedPrinter &Writer) + : ObjDumper(Writer, O.getFileName()), ObjF(O), Obj(O.getELFFile()), + FileName(O.getFileName()), DynRelRegion(O, *this), + DynRelaRegion(O, *this), DynRelrRegion(O, *this), + DynPLTRelRegion(O, *this), DynSymTabShndxRegion(O, *this), + DynamicTable(O, *this) { + if (!O.IsContentValid()) + return; + + typename ELFT::ShdrRange Sections = cantFail(Obj.sections()); + for (const Elf_Shdr &Sec : Sections) { + switch (Sec.sh_type) { + case ELF::SHT_SYMTAB: + if (!DotSymtabSec) + DotSymtabSec = &Sec; + break; + case ELF::SHT_DYNSYM: + if (!DotDynsymSec) + DotDynsymSec = &Sec; + + if (!DynSymRegion) { + Expected<DynRegionInfo> RegOrErr = + createDRI(Sec.sh_offset, Sec.sh_size, Sec.sh_entsize); + if (RegOrErr) { + DynSymRegion = *RegOrErr; + DynSymRegion->Context = describe(Sec); + + if (Expected<StringRef> E = Obj.getStringTableForSymtab(Sec)) + DynamicStringTable = *E; + else + reportUniqueWarning("unable to get the string table for the " + + describe(Sec) + ": " + toString(E.takeError())); + } else { + reportUniqueWarning("unable to read dynamic symbols from " + + describe(Sec) + ": " + + toString(RegOrErr.takeError())); + } + } + break; + case ELF::SHT_SYMTAB_SHNDX: { + uint32_t SymtabNdx = Sec.sh_link; + if (SymtabNdx >= Sections.size()) { + reportUniqueWarning( + "unable to get the associated symbol table for " + describe(Sec) + + ": sh_link (" + Twine(SymtabNdx) + + ") is greater than or equal to the total number of sections (" + + Twine(Sections.size()) + ")"); + continue; + } + + if (Expected<ArrayRef<Elf_Word>> ShndxTableOrErr = + Obj.getSHNDXTable(Sec)) { + if (!ShndxTables.insert({&Sections[SymtabNdx], *ShndxTableOrErr}) + .second) + reportUniqueWarning( + "multiple SHT_SYMTAB_SHNDX sections are linked to " + + describe(Sec)); + } else { + reportUniqueWarning(ShndxTableOrErr.takeError()); + } + 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(); +} + +template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { + auto toMappedAddr = [&](uint64_t Tag, uint64_t VAddr) -> const uint8_t * { + auto MappedAddrOrError = Obj.toMappedAddr(VAddr, [&](const Twine &Msg) { + this->reportUniqueWarning(Msg); + return Error::success(); + }); + if (!MappedAddrOrError) { + this->reportUniqueWarning("unable to parse DT_" + + Obj.getDynamicTagAsString(Tag) + ": " + + llvm::toString(MappedAddrOrError.takeError())); + return nullptr; + } + return MappedAddrOrError.get(); + }; + + const char *StringTableBegin = nullptr; + uint64_t StringTableSize = 0; + Optional<DynRegionInfo> DynSymFromTable; + 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: { + // 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())) { + DynSymFromTable.emplace(ObjF, *this); + DynSymFromTable->Addr = VA; + DynSymFromTable->EntSize = sizeof(Elf_Sym); + DynSymFromTable->EntSizePrintName = ""; + } + break; + } + case ELF::DT_SYMENT: { + uint64_t Val = Dyn.getVal(); + if (Val != sizeof(Elf_Sym)) + this->reportUniqueWarning("DT_SYMENT value of 0x" + + Twine::utohexstr(Val) + + " is not the size of a symbol (0x" + + Twine::utohexstr(sizeof(Elf_Sym)) + ")"); + break; + } + case ELF::DT_RELA: + DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + break; + case ELF::DT_RELASZ: + DynRelaRegion.Size = Dyn.getVal(); + DynRelaRegion.SizePrintName = "DT_RELASZ value"; + break; + case ELF::DT_RELAENT: + DynRelaRegion.EntSize = Dyn.getVal(); + DynRelaRegion.EntSizePrintName = "DT_RELAENT value"; + 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(); + DynRelRegion.SizePrintName = "DT_RELSZ value"; + break; + case ELF::DT_RELENT: + DynRelRegion.EntSize = Dyn.getVal(); + DynRelRegion.EntSizePrintName = "DT_RELENT value"; + 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(); + DynRelrRegion.SizePrintName = Dyn.d_tag == ELF::DT_RELRSZ + ? "DT_RELRSZ value" + : "DT_ANDROID_RELRSZ value"; + break; + case ELF::DT_RELRENT: + case ELF::DT_ANDROID_RELRENT: + DynRelrRegion.EntSize = Dyn.getVal(); + DynRelrRegion.EntSizePrintName = Dyn.d_tag == ELF::DT_RELRENT + ? "DT_RELRENT value" + : "DT_ANDROID_RELRENT value"; + 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 + reportUniqueWarning(Twine("unknown DT_PLTREL value of ") + + Twine((uint64_t)Dyn.getVal())); + DynPLTRelRegion.EntSizePrintName = "PLTREL entry size"; + break; + case ELF::DT_JMPREL: + DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + break; + case ELF::DT_PLTRELSZ: + DynPLTRelRegion.Size = Dyn.getVal(); + DynPLTRelRegion.SizePrintName = "DT_PLTRELSZ value"; + break; + case ELF::DT_SYMTAB_SHNDX: + DynSymTabShndxRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); + DynSymTabShndxRegion.EntSize = sizeof(Elf_Word); + break; + } + } + + if (StringTableBegin) { + const uint64_t FileSize = Obj.getBufSize(); + const uint64_t Offset = (const uint8_t *)StringTableBegin - Obj.base(); + if (StringTableSize > FileSize - Offset) + reportUniqueWarning( + "the dynamic string table at 0x" + Twine::utohexstr(Offset) + + " goes past the end of the file (0x" + Twine::utohexstr(FileSize) + + ") with DT_STRSZ = 0x" + Twine::utohexstr(StringTableSize)); + else + DynamicStringTable = StringRef(StringTableBegin, StringTableSize); + } + + const bool IsHashTableSupported = getHashTableEntSize() == 4; + if (DynSymRegion) { + // 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 (DynSymFromTable && DynSymFromTable->Addr != DynSymRegion->Addr) + reportUniqueWarning( + createError("SHT_DYNSYM section header and DT_SYMTAB disagree about " + "the location of the dynamic symbol table")); + + // According to the ELF gABI: "The number of symbol table entries should + // equal nchain". Check to see if the DT_HASH hash table nchain value + // conflicts with the number of symbols in the dynamic symbol table + // according to the section header. + if (HashTable && IsHashTableSupported) { + if (DynSymRegion->EntSize == 0) + reportUniqueWarning("SHT_DYNSYM section has sh_entsize == 0"); + else if (HashTable->nchain != DynSymRegion->Size / DynSymRegion->EntSize) + reportUniqueWarning( + "hash table nchain (" + Twine(HashTable->nchain) + + ") differs from symbol count derived from SHT_DYNSYM section " + "header (" + + Twine(DynSymRegion->Size / DynSymRegion->EntSize) + ")"); + } + } + + // Delay the creation of the actual dynamic symbol table until now, so that + // checks can always be made against the section header-based properties, + // without worrying about tag order. + if (DynSymFromTable) { + if (!DynSymRegion) { + DynSymRegion = DynSymFromTable; + } else { + DynSymRegion->Addr = DynSymFromTable->Addr; + DynSymRegion->EntSize = DynSymFromTable->EntSize; + DynSymRegion->EntSizePrintName = DynSymFromTable->EntSizePrintName; + } + } + + // Derive the dynamic symbol table size from the DT_HASH hash table, if + // present. + if (HashTable && IsHashTableSupported && DynSymRegion) { + const uint64_t FileSize = Obj.getBufSize(); + const uint64_t DerivedSize = + (uint64_t)HashTable->nchain * DynSymRegion->EntSize; + const uint64_t Offset = (const uint8_t *)DynSymRegion->Addr - Obj.base(); + if (DerivedSize > FileSize - Offset) + reportUniqueWarning( + "the size (0x" + Twine::utohexstr(DerivedSize) + + ") of the dynamic symbol table at 0x" + Twine::utohexstr(Offset) + + ", derived from the hash table, goes past the end of the file (0x" + + Twine::utohexstr(FileSize) + ") and will be ignored"); + else + DynSymRegion->Size = HashTable->nchain * DynSymRegion->EntSize; + } +} + +template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() { + // Dump version symbol section. + printVersionSymbolSection(SymbolVersionSection); + + // Dump version definition section. + printVersionDefinitionSection(SymbolVersionDefSection); + + // Dump version dependency section. + printVersionDependencySection(SymbolVersionNeedSection); +} + +#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), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, PIE), +}; + +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) { + SmallVector<EnumEntry<TFlag>, 10> SetFlags; + for (const EnumEntry<TFlag> &Flag : Flags) + if (Flag.Value != 0 && (Value & Flag.Value) == Flag.Value) + SetFlags.push_back(Flag); + + for (const EnumEntry<TFlag> &Flag : SetFlags) + OS << Flag.Name << " "; +} + +template <class ELFT> +const typename ELFT::Shdr * +ELFDumper<ELFT>::findSectionByName(StringRef Name) const { + for (const Elf_Shdr &Shdr : cantFail(Obj.sections())) { + if (Expected<StringRef> NameOrErr = Obj.getSectionName(Shdr)) { + if (*NameOrErr == Name) + return &Shdr; + } else { + reportUniqueWarning("unable to read the name of " + describe(Shdr) + + ": " + toString(NameOrErr.takeError())); + } + } + return nullptr; +} + +template <class ELFT> +std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type, + uint64_t Value) const { + auto FormatHexValue = [](uint64_t V) { + std::string Str; + raw_string_ostream OS(Str); + const char *ConvChar = + (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64; + OS << format(ConvChar, V); + return OS.str(); + }; + + auto FormatFlags = [](uint64_t V, + llvm::ArrayRef<llvm::EnumEntry<unsigned int>> Array) { + std::string Str; + raw_string_ostream OS(Str); + printFlags(V, Array, OS); + return OS.str(); + }; + + // Handle custom printing of architecture specific tags + switch (Obj.getHeader().e_machine) { + case EM_AARCH64: + switch (Type) { + case DT_AARCH64_BTI_PLT: + case DT_AARCH64_PAC_PLT: + case DT_AARCH64_VARIANT_PCS: + return std::to_string(Value); + default: + break; + } + break; + case EM_HEXAGON: + switch (Type) { + case DT_HEXAGON_VER: + return std::to_string(Value); + case DT_HEXAGON_SYMSZ: + case DT_HEXAGON_PLT: + return FormatHexValue(Value); + 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: + return std::to_string(Value); + 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: + return FormatHexValue(Value); + case DT_MIPS_FLAGS: + return FormatFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags)); + default: + break; + } + break; + default: + break; + } + + switch (Type) { + case DT_PLTREL: + if (Value == DT_REL) + return "REL"; + if (Value == DT_RELA) + return "RELA"; + 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: + return FormatHexValue(Value); + case DT_RELACOUNT: + case DT_RELCOUNT: + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + return std::to_string(Value); + 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: + return std::to_string(Value) + " (bytes)"; + 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"}, + }; + + return (Twine(TagNames.at(Type)) + ": [" + getDynamicString(Value) + "]") + .str(); + } + case DT_FLAGS: + return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags)); + case DT_FLAGS_1: + return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags1)); + default: + return FormatHexValue(Value); + } +} + +template <class ELFT> +StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const { + if (DynamicStringTable.empty() && !DynamicStringTable.data()) { + reportUniqueWarning("string table was not found"); + return "<?>"; + } + + auto WarnAndReturn = [this](const Twine &Msg, uint64_t Offset) { + reportUniqueWarning("string table at offset 0x" + Twine::utohexstr(Offset) + + Msg); + return "<?>"; + }; + + const uint64_t FileSize = Obj.getBufSize(); + const uint64_t Offset = + (const uint8_t *)DynamicStringTable.data() - Obj.base(); + if (DynamicStringTable.size() > FileSize - Offset) + return WarnAndReturn(" with size 0x" + + Twine::utohexstr(DynamicStringTable.size()) + + " goes past the end of the file (0x" + + Twine::utohexstr(FileSize) + ")", + Offset); + + if (Value >= DynamicStringTable.size()) + return WarnAndReturn( + ": unable to read the string at 0x" + Twine::utohexstr(Offset + Value) + + ": it goes past the end of the table (0x" + + Twine::utohexstr(Offset + DynamicStringTable.size()) + ")", + Offset); + + if (DynamicStringTable.back() != '\0') + return WarnAndReturn(": unable to read the string at 0x" + + Twine::utohexstr(Offset + Value) + + ": the string table is not null-terminated", + Offset); + + return DynamicStringTable.data() + Value; +} + +template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() { + DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF); + Ctx.printUnwindInformation(); +} + +// The namespace is needed to fix the compilation with GCC older than 7.0+. +namespace { +template <> void ELFDumper<ELF32LE>::printUnwindInfo() { + if (Obj.getHeader().e_machine == EM_ARM) { + ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, ObjF.getFileName(), + DotSymtabSec); + Ctx.PrintUnwindInformation(); + } + DwarfCFIEH::PrinterContext<ELF32LE> Ctx(W, ObjF); + Ctx.printUnwindInformation(); +} +} // namespace + +template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() { + ListScope D(W, "NeededLibraries"); + + std::vector<StringRef> Libs; + for (const auto &Entry : dynamic_table()) + if (Entry.d_tag == ELF::DT_NEEDED) + Libs.push_back(getDynamicString(Entry.d_un.d_val)); + + llvm::sort(Libs); + + for (StringRef L : Libs) + W.startLine() << L << "\n"; +} + +template <class ELFT> +static Error checkHashTable(const ELFDumper<ELFT> &Dumper, + const typename ELFT::Hash *H, + bool *IsHeaderValid = nullptr) { + const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile(); + const uint64_t SecOffset = (const uint8_t *)H - Obj.base(); + if (Dumper.getHashTableEntSize() == 8) { + auto It = llvm::find_if(ElfMachineType, [&](const EnumEntry<unsigned> &E) { + return E.Value == Obj.getHeader().e_machine; + }); + if (IsHeaderValid) + *IsHeaderValid = false; + return createError("the hash table at 0x" + Twine::utohexstr(SecOffset) + + " is not supported: it contains non-standard 8 " + "byte entries on " + + It->AltName + " platform"); + } + + auto MakeError = [&](const Twine &Msg = "") { + return createError("the hash table at offset 0x" + + Twine::utohexstr(SecOffset) + + " goes past the end of the file (0x" + + Twine::utohexstr(Obj.getBufSize()) + ")" + Msg); + }; + + // Each SHT_HASH section starts from two 32-bit fields: nbucket and nchain. + const unsigned HeaderSize = 2 * sizeof(typename ELFT::Word); + + if (IsHeaderValid) + *IsHeaderValid = Obj.getBufSize() - SecOffset >= HeaderSize; + + if (Obj.getBufSize() - SecOffset < HeaderSize) + return MakeError(); + + if (Obj.getBufSize() - SecOffset - HeaderSize < + ((uint64_t)H->nbucket + H->nchain) * sizeof(typename ELFT::Word)) + return MakeError(", nbucket = " + Twine(H->nbucket) + + ", nchain = " + Twine(H->nchain)); + return Error::success(); +} + +template <class ELFT> +static Error checkGNUHashTable(const ELFFile<ELFT> &Obj, + const typename ELFT::GnuHash *GnuHashTable, + bool *IsHeaderValid = nullptr) { + const uint8_t *TableData = reinterpret_cast<const uint8_t *>(GnuHashTable); + assert(TableData >= Obj.base() && TableData < Obj.base() + Obj.getBufSize() && + "GnuHashTable must always point to a location inside the file"); + + uint64_t TableOffset = TableData - Obj.base(); + if (IsHeaderValid) + *IsHeaderValid = TableOffset + /*Header size:*/ 16 < Obj.getBufSize(); + if (TableOffset + 16 + (uint64_t)GnuHashTable->nbuckets * 4 + + (uint64_t)GnuHashTable->maskwords * sizeof(typename ELFT::Off) >= + Obj.getBufSize()) + return createError("unable to dump the SHT_GNU_HASH " + "section at 0x" + + Twine::utohexstr(TableOffset) + + ": it goes past the end of the file"); + return Error::success(); +} + +template <typename ELFT> void ELFDumper<ELFT>::printHashTable() { + DictScope D(W, "HashTable"); + if (!HashTable) + return; + + bool IsHeaderValid; + Error Err = checkHashTable(*this, HashTable, &IsHeaderValid); + if (IsHeaderValid) { + W.printNumber("Num Buckets", HashTable->nbucket); + W.printNumber("Num Chains", HashTable->nchain); + } + + if (Err) { + reportUniqueWarning(std::move(Err)); + return; + } + + W.printList("Buckets", HashTable->buckets()); + W.printList("Chains", HashTable->chains()); +} + +template <class ELFT> +static Expected<ArrayRef<typename ELFT::Word>> +getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion, + const typename ELFT::GnuHash *GnuHashTable) { + if (!DynSymRegion) + return createError("no dynamic symbol table found"); + + ArrayRef<typename ELFT::Sym> DynSymTable = + DynSymRegion->template getAsArrayRef<typename ELFT::Sym>(); + size_t NumSyms = DynSymTable.size(); + if (!NumSyms) + return createError("the dynamic symbol table is empty"); + + if (GnuHashTable->symndx < NumSyms) + return GnuHashTable->values(NumSyms); + + // A normal empty GNU hash table section produced by linker might have + // symndx set to the number of dynamic symbols + 1 (for the zero symbol) + // and have dummy null values in the Bloom filter and in the buckets + // vector (or no values at all). It happens because the value of symndx is not + // important for dynamic loaders when the GNU hash table is empty. They just + // skip the whole object during symbol lookup. In such cases, the symndx value + // is irrelevant and we should not report a warning. + ArrayRef<typename ELFT::Word> Buckets = GnuHashTable->buckets(); + if (!llvm::all_of(Buckets, [](typename ELFT::Word V) { return V == 0; })) + return createError( + "the first hashed symbol index (" + Twine(GnuHashTable->symndx) + + ") is greater than or equal to the number of dynamic symbols (" + + Twine(NumSyms) + ")"); + // There is no way to represent an array of (dynamic symbols count - symndx) + // length. + return ArrayRef<typename ELFT::Word>(); +} + +template <typename ELFT> +void ELFDumper<ELFT>::printGnuHashTable() { + DictScope D(W, "GnuHashTable"); + if (!GnuHashTable) + return; + + bool IsHeaderValid; + Error Err = checkGNUHashTable<ELFT>(Obj, GnuHashTable, &IsHeaderValid); + if (IsHeaderValid) { + 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); + } + + if (Err) { + reportUniqueWarning(std::move(Err)); + return; + } + + ArrayRef<typename ELFT::Off> BloomFilter = GnuHashTable->filter(); + W.printHexList("Bloom Filter", BloomFilter); + + ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets(); + W.printList("Buckets", Buckets); + + Expected<ArrayRef<Elf_Word>> Chains = + getGnuHashTableChains<ELFT>(DynSymRegion, GnuHashTable); + if (!Chains) { + reportUniqueWarning("unable to dump 'Values' for the SHT_GNU_HASH " + "section: " + + toString(Chains.takeError())); + return; + } + + W.printHexList("Values", *Chains); +} + +template <typename ELFT> void ELFDumper<ELFT>::printLoadName() { + StringRef SOName = "<Not found>"; + if (SONameOffset) + SOName = getDynamicString(*SONameOffset); + W.printString("LoadName", SOName); +} + +template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() { + switch (Obj.getHeader().e_machine) { + case EM_ARM: + case EM_RISCV: + printAttributes(); + break; + case EM_MIPS: { + printMipsABIFlags(); + printMipsOptions(); + printMipsReginfo(); + MipsGOTParser<ELFT> Parser(*this); + if (Error E = Parser.findGOT(dynamic_table(), dynamic_symbols())) + reportUniqueWarning(std::move(E)); + else if (!Parser.isGotEmpty()) + printMipsGOT(Parser); + + if (Error E = Parser.findPLT(dynamic_table())) + reportUniqueWarning(std::move(E)); + else if (!Parser.isPltEmpty()) + printMipsPLT(Parser); + break; + } + default: + break; + } +} + +template <class ELFT> void ELFDumper<ELFT>::printAttributes() { + if (!Obj.isLE()) { + W.startLine() << "Attributes not implemented.\n"; + return; + } + + const unsigned Machine = Obj.getHeader().e_machine; + assert((Machine == EM_ARM || Machine == EM_RISCV) && + "Attributes not implemented."); + + DictScope BA(W, "BuildAttributes"); + for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { + if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES && + Sec.sh_type != ELF::SHT_RISCV_ATTRIBUTES) + continue; + + ArrayRef<uint8_t> Contents; + if (Expected<ArrayRef<uint8_t>> ContentOrErr = + Obj.getSectionContents(Sec)) { + Contents = *ContentOrErr; + if (Contents.empty()) { + reportUniqueWarning("the " + describe(Sec) + " is empty"); + continue; + } + } else { + reportUniqueWarning("unable to read the content of the " + describe(Sec) + + ": " + toString(ContentOrErr.takeError())); + continue; + } + + W.printHex("FormatVersion", Contents[0]); + + auto ParseAttrubutes = [&]() { + if (Machine == EM_ARM) + return ARMAttributeParser(&W).parse(Contents, support::little); + return RISCVAttributeParser(&W).parse(Contents, support::little); + }; + + if (Error E = ParseAttrubutes()) + reportUniqueWarning("unable to dump attributes from the " + + describe(Sec) + ": " + toString(std::move(E))); + } +} + +namespace { + +template <class ELFT> class MipsGOTParser { +public: + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + using Entry = typename ELFT::Addr; + using Entries = ArrayRef<Entry>; + + const bool IsStatic; + const ELFFile<ELFT> &Obj; + const ELFDumper<ELFT> &Dumper; + + MipsGOTParser(const ELFDumper<ELFT> &D); + Error findGOT(Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms); + Error findPLT(Elf_Dyn_Range DynTable); + + bool isGotEmpty() const { return GotEntries.empty(); } + bool isPltEmpty() 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; } + const Elf_Shdr *getPltSymTable() const { return PltSymTable; } + +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 ELFDumper<ELFT> &D) + : IsStatic(D.dynamic_table().empty()), Obj(D.getElfObject().getELFFile()), + Dumper(D), GotSec(nullptr), LocalNum(0), GlobalNum(0), PltSec(nullptr), + PltRelSec(nullptr), PltSymTable(nullptr), + FileName(D.getElfObject().getFileName()) {} + +template <class ELFT> +Error MipsGOTParser<ELFT>::findGOT(Elf_Dyn_Range DynTable, + Elf_Sym_Range DynSyms) { + // 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 = Dumper.findSectionByName(".got"); + if (!GotSec) + return Error::success(); + + 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 Error::success(); + } + + // Lookup dynamic table tags which define the GOT layout. + Optional<uint64_t> DtPltGot; + Optional<uint64_t> DtLocalGotNum; + Optional<uint64_t> DtGotSym; + 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; + } + } + + if (!DtPltGot && !DtLocalGotNum && !DtGotSym) + return Error::success(); + + if (!DtPltGot) + return createError("cannot find PLTGOT dynamic tag"); + if (!DtLocalGotNum) + return createError("cannot find MIPS_LOCAL_GOTNO dynamic tag"); + if (!DtGotSym) + return createError("cannot find MIPS_GOTSYM dynamic tag"); + + size_t DynSymTotal = DynSyms.size(); + if (*DtGotSym > DynSymTotal) + return createError("DT_MIPS_GOTSYM value (" + Twine(*DtGotSym) + + ") exceeds the number of dynamic symbols (" + + Twine(DynSymTotal) + ")"); + + GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot); + if (!GotSec) + return createError("there is no non-empty GOT section at 0x" + + Twine::utohexstr(*DtPltGot)); + + 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); + + return Error::success(); +} + +template <class ELFT> +Error MipsGOTParser<ELFT>::findPLT(Elf_Dyn_Range DynTable) { + // Lookup dynamic table tags which define the PLT layout. + Optional<uint64_t> DtMipsPltGot; + Optional<uint64_t> DtJmpRel; + for (const auto &Entry : DynTable) { + switch (Entry.getTag()) { + case ELF::DT_MIPS_PLTGOT: + DtMipsPltGot = Entry.getVal(); + break; + case ELF::DT_JMPREL: + DtJmpRel = Entry.getVal(); + break; + } + } + + if (!DtMipsPltGot && !DtJmpRel) + return Error::success(); + + // Find PLT section. + if (!DtMipsPltGot) + return createError("cannot find MIPS_PLTGOT dynamic tag"); + if (!DtJmpRel) + return createError("cannot find JMPREL dynamic tag"); + + PltSec = findNotEmptySectionByAddress(Obj, FileName, *DtMipsPltGot); + if (!PltSec) + return createError("there is no non-empty PLTGOT section at 0x" + + Twine::utohexstr(*DtMipsPltGot)); + + PltRelSec = findNotEmptySectionByAddress(Obj, FileName, *DtJmpRel); + if (!PltRelSec) + return createError("there is no non-empty RELPLT section at 0x" + + Twine::utohexstr(*DtJmpRel)); + + if (Expected<ArrayRef<uint8_t>> PltContentOrErr = + Obj.getSectionContents(*PltSec)) + PltEntries = + Entries(reinterpret_cast<const Entry *>(PltContentOrErr->data()), + PltContentOrErr->size() / sizeof(Entry)); + else + return createError("unable to read PLTGOT section content: " + + toString(PltContentOrErr.takeError())); + + if (Expected<const Elf_Shdr *> PltSymTableOrErr = + Obj.getSection(PltRelSec->sh_link)) + PltSymTable = *PltSymTableOrErr; + else + return createError("unable to get a symbol table linked to the " + + describe(Obj, *PltRelSec) + ": " + + toString(PltSymTableOrErr.takeError())); + + if (Expected<StringRef> StrTabOrErr = + Obj.getStringTableForSymtab(*PltSymTable)) + PltStrTable = *StrTabOrErr; + else + return createError("unable to get a string table for the " + + describe(Obj, *PltSymTable) + ": " + + toString(StrTabOrErr.takeError())); + + return Error::success(); +} + +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 Elf_Shdr *RegInfoSec = findSectionByName(".reginfo"); + if (!RegInfoSec) { + W.startLine() << "There is no .reginfo section in the file.\n"; + return; + } + + Expected<ArrayRef<uint8_t>> ContentsOrErr = + Obj.getSectionContents(*RegInfoSec); + if (!ContentsOrErr) { + this->reportUniqueWarning( + "unable to read the content of the .reginfo section (" + + describe(*RegInfoSec) + "): " + toString(ContentsOrErr.takeError())); + return; + } + + if (ContentsOrErr->size() < sizeof(Elf_Mips_RegInfo<ELFT>)) { + this->reportUniqueWarning("the .reginfo section has an invalid size (0x" + + Twine::utohexstr(ContentsOrErr->size()) + ")"); + return; + } + + DictScope GS(W, "MIPS RegInfo"); + printMipsReginfoData(W, *reinterpret_cast<const Elf_Mips_RegInfo<ELFT> *>( + ContentsOrErr->data())); +} + +template <class ELFT> +static Expected<const Elf_Mips_Options<ELFT> *> +readMipsOptions(const uint8_t *SecBegin, ArrayRef<uint8_t> &SecData, + bool &IsSupported) { + if (SecData.size() < sizeof(Elf_Mips_Options<ELFT>)) + return createError("the .MIPS.options section has an invalid size (0x" + + Twine::utohexstr(SecData.size()) + ")"); + + const Elf_Mips_Options<ELFT> *O = + reinterpret_cast<const Elf_Mips_Options<ELFT> *>(SecData.data()); + const uint8_t Size = O->size; + if (Size > SecData.size()) { + const uint64_t Offset = SecData.data() - SecBegin; + const uint64_t SecSize = Offset + SecData.size(); + return createError("a descriptor of size 0x" + Twine::utohexstr(Size) + + " at offset 0x" + Twine::utohexstr(Offset) + + " goes past the end of the .MIPS.options " + "section of size 0x" + + Twine::utohexstr(SecSize)); + } + + IsSupported = O->kind == ODK_REGINFO; + const size_t ExpectedSize = + sizeof(Elf_Mips_Options<ELFT>) + sizeof(Elf_Mips_RegInfo<ELFT>); + + if (IsSupported) + if (Size < ExpectedSize) + return createError( + "a .MIPS.options entry of kind " + + Twine(getElfMipsOptionsOdkType(O->kind)) + + " has an invalid size (0x" + Twine::utohexstr(Size) + + "), the expected size is 0x" + Twine::utohexstr(ExpectedSize)); + + SecData = SecData.drop_front(Size); + return O; +} + +template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { + const Elf_Shdr *MipsOpts = findSectionByName(".MIPS.options"); + if (!MipsOpts) { + W.startLine() << "There is no .MIPS.options section in the file.\n"; + return; + } + + DictScope GS(W, "MIPS Options"); + + ArrayRef<uint8_t> Data = + unwrapOrError(ObjF.getFileName(), Obj.getSectionContents(*MipsOpts)); + const uint8_t *const SecBegin = Data.begin(); + while (!Data.empty()) { + bool IsSupported; + Expected<const Elf_Mips_Options<ELFT> *> OptsOrErr = + readMipsOptions<ELFT>(SecBegin, Data, IsSupported); + if (!OptsOrErr) { + reportUniqueWarning(OptsOrErr.takeError()); + break; + } + + unsigned Kind = (*OptsOrErr)->kind; + const char *Type = getElfMipsOptionsOdkType(Kind); + if (!IsSupported) { + W.startLine() << "Unsupported MIPS options tag: " << Type << " (" << Kind + << ")\n"; + continue; + } + + DictScope GS(W, Type); + if (Kind == ODK_REGINFO) + printMipsReginfoData(W, (*OptsOrErr)->getRegInfo()); + else + llvm_unreachable("unexpected .MIPS.options section descriptor kind"); + } +} + +template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { + const Elf_Shdr *StackMapSection = findSectionByName(".llvm_stackmaps"); + if (!StackMapSection) + return; + + auto Warn = [&](Error &&E) { + this->reportUniqueWarning("unable to read the stack map from " + + describe(*StackMapSection) + ": " + + toString(std::move(E))); + }; + + Expected<ArrayRef<uint8_t>> ContentOrErr = + Obj.getSectionContents(*StackMapSection); + if (!ContentOrErr) { + Warn(ContentOrErr.takeError()); + return; + } + + if (Error E = StackMapParser<ELFT::TargetEndianness>::validateHeader( + *ContentOrErr)) { + Warn(std::move(E)); + return; + } + + prettyPrintStackMap(W, StackMapParser<ELFT::TargetEndianness>(*ContentOrErr)); +} + +template <class ELFT> +void ELFDumper<ELFT>::printReloc(const Relocation<ELFT> &R, unsigned RelIndex, + const Elf_Shdr &Sec, const Elf_Shdr *SymTab) { + Expected<RelSymbol<ELFT>> Target = getRelocationTarget(R, SymTab); + if (!Target) + reportUniqueWarning("unable to print relocation " + Twine(RelIndex) + + " in " + describe(Sec) + ": " + + toString(Target.takeError())); + else + printRelRelaReloc(R, *Target); +} + +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); + + Expected<ArrayRef<typename ELFT::Shdr>> ArrOrErr = Obj.sections(); + if (!ArrOrErr) { + // In this case we can ignore an error, because we have already reported a + // warning about the broken section header table earlier. + consumeError(ArrOrErr.takeError()); + return "<?>"; + } + + if (ArrOrErr->empty()) + return "0"; + return "0 (" + to_string((*ArrOrErr)[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); + + Expected<ArrayRef<typename ELFT::Shdr>> ArrOrErr = Obj.sections(); + if (!ArrOrErr) { + // In this case we can ignore an error, because we have already reported a + // warning about the broken section header table earlier. + consumeError(ArrOrErr.takeError()); + return "<?>"; + } + + if (ArrOrErr->empty()) + return "65535 (corrupt: out of range)"; + return to_string(ElfHeader.e_shstrndx) + " (" + + to_string((*ArrOrErr)[0].sh_link) + ")"; +} + +static const EnumEntry<unsigned> *getObjectFileEnumEntry(unsigned Type) { + auto It = llvm::find_if(ElfObjectFileType, [&](const EnumEntry<unsigned> &E) { + return E.Value == Type; + }); + if (It != makeArrayRef(ElfObjectFileType).end()) + return It; + return nullptr; +} + +template <class ELFT> void GNUELFDumper<ELFT>::printFileHeaders() { + const Elf_Ehdr &e = this->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); + printFields(OS, + "ABI Version:", std::to_string(e.e_ident[ELF::EI_ABIVERSION])); + + if (const EnumEntry<unsigned> *E = getObjectFileEnumEntry(e.e_type)) { + Str = E->AltName.str(); + } else { + if (e.e_type >= ET_LOPROC) + Str = "Processor Specific: (" + to_hexString(e.e_type, false) + ")"; + else if (e.e_type >= ET_LOOS) + Str = "OS Specific: (" + to_hexString(e.e_type, false) + ")"; + else + Str = "<unknown>: " + to_hexString(e.e_type, false); + } + 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(this->Obj, this->FileName); + printFields(OS, "Number of section headers:", Str); + Str = getSectionHeaderTableIndexString(this->Obj, this->FileName); + printFields(OS, "Section header string table index:", Str); +} + +template <class ELFT> std::vector<GroupSection> ELFDumper<ELFT>::getGroups() { + auto GetSignature = [&](const Elf_Sym &Sym, unsigned SymNdx, + const Elf_Shdr &Symtab) -> StringRef { + Expected<StringRef> StrTableOrErr = Obj.getStringTableForSymtab(Symtab); + if (!StrTableOrErr) { + reportUniqueWarning("unable to get the string table for " + + describe(Symtab) + ": " + + toString(StrTableOrErr.takeError())); + return "<?>"; + } + + StringRef Strings = *StrTableOrErr; + if (Sym.st_name >= Strings.size()) { + reportUniqueWarning("unable to get the name of the symbol with index " + + Twine(SymNdx) + ": st_name (0x" + + Twine::utohexstr(Sym.st_name) + + ") is past the end of the string table of size 0x" + + Twine::utohexstr(Strings.size())); + return "<?>"; + } + + return StrTableOrErr->data() + Sym.st_name; + }; + + std::vector<GroupSection> Ret; + uint64_t I = 0; + for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { + ++I; + if (Sec.sh_type != ELF::SHT_GROUP) + continue; + + StringRef Signature = "<?>"; + if (Expected<const Elf_Shdr *> SymtabOrErr = Obj.getSection(Sec.sh_link)) { + if (Expected<const Elf_Sym *> SymOrErr = + Obj.template getEntry<Elf_Sym>(**SymtabOrErr, Sec.sh_info)) + Signature = GetSignature(**SymOrErr, Sec.sh_info, **SymtabOrErr); + else + reportUniqueWarning("unable to get the signature symbol for " + + describe(Sec) + ": " + + toString(SymOrErr.takeError())); + } else { + reportUniqueWarning("unable to get the symbol table for " + + describe(Sec) + ": " + + toString(SymtabOrErr.takeError())); + } + + ArrayRef<Elf_Word> Data; + if (Expected<ArrayRef<Elf_Word>> ContentsOrErr = + Obj.template getSectionContentsAsArray<Elf_Word>(Sec)) { + if (ContentsOrErr->empty()) + reportUniqueWarning("unable to read the section group flag from the " + + describe(Sec) + ": the section is empty"); + else + Data = *ContentsOrErr; + } else { + reportUniqueWarning("unable to get the content of the " + describe(Sec) + + ": " + toString(ContentsOrErr.takeError())); + } + + Ret.push_back({getPrintableSectionName(Sec), + maybeDemangle(Signature), + Sec.sh_name, + I - 1, + Sec.sh_link, + Sec.sh_info, + Data.empty() ? Elf_Word(0) : Data[0], + {}}); + + if (Data.empty()) + continue; + + std::vector<GroupMember> &GM = Ret.back().Members; + for (uint32_t Ndx : Data.slice(1)) { + if (Expected<const Elf_Shdr *> SecOrErr = Obj.getSection(Ndx)) { + GM.push_back({getPrintableSectionName(**SecOrErr), Ndx}); + } else { + reportUniqueWarning("unable to get the section with index " + + Twine(Ndx) + " when dumping the " + describe(Sec) + + ": " + toString(SecOrErr.takeError())); + GM.push_back({"<?>", Ndx}); + } + } + } + return Ret; +} + +static 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; +} + +template <class ELFT> void GNUELFDumper<ELFT>::printGroupSections() { + std::vector<GroupSection> V = this->getGroups(); + 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) + this->reportUniqueWarning( + "section with index " + Twine(GM.Index) + + ", included in the group section with index " + + Twine(MainGroup->Index) + + ", was also found in the group section with index " + + Twine(G.Index)); + 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 GNUELFDumper<ELFT>::printRelrReloc(const Elf_Relr &R) { + OS << to_string(format_hex_no_prefix(R, ELFT::Is64Bits ? 16 : 8)) << "\n"; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printRelRelaReloc(const Relocation<ELFT> &R, + const RelSymbol<ELFT> &RelSym) { + // 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.Offset, Width)); + Fields[1].Str = to_string(format_hex_no_prefix(R.Info, Width)); + + SmallString<32> RelocName; + this->Obj.getRelocationTypeName(R.Type, RelocName); + Fields[2].Str = RelocName.c_str(); + + if (RelSym.Sym) + Fields[3].Str = + to_string(format_hex_no_prefix(RelSym.Sym->getValue(), Width)); + + Fields[4].Str = std::string(RelSym.Name); + for (const Field &F : Fields) + printField(F); + + std::string Addend; + if (Optional<int64_t> A = R.Addend) { + int64_t RelAddend = *A; + if (!RelSym.Name.empty()) { + if (RelAddend < 0) { + Addend = " - "; + RelAddend = std::abs(RelAddend); + } else { + Addend = " + "; + } + } + Addend += to_hexString(RelAddend, false); + } + OS << Addend << "\n"; +} + +template <class ELFT> +static void printRelocHeaderFields(formatted_raw_ostream &OS, 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 GNUELFDumper<ELFT>::printDynamicRelocHeader(unsigned Type, StringRef Name, + const DynRegionInfo &Reg) { + uint64_t Offset = Reg.Addr - this->Obj.base(); + OS << "\n'" << Name.str().c_str() << "' relocation section at offset 0x" + << to_hexString(Offset, false) << " contains " << Reg.Size << " bytes:\n"; + printRelocHeaderFields<ELFT>(OS, Type); +} + +template <class ELFT> +static bool isRelocationSec(const typename ELFT::Shdr &Sec) { + return 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; +} + +template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() { + auto GetEntriesNum = [&](const Elf_Shdr &Sec) -> Expected<size_t> { + // Android's packed relocation section needs to be unpacked first + // to get the actual number of entries. + if (Sec.sh_type == ELF::SHT_ANDROID_REL || + Sec.sh_type == ELF::SHT_ANDROID_RELA) { + Expected<std::vector<typename ELFT::Rela>> RelasOrErr = + this->Obj.android_relas(Sec); + if (!RelasOrErr) + return RelasOrErr.takeError(); + return RelasOrErr->size(); + } + + if (!opts::RawRelr && (Sec.sh_type == ELF::SHT_RELR || + Sec.sh_type == ELF::SHT_ANDROID_RELR)) { + Expected<Elf_Relr_Range> RelrsOrErr = this->Obj.relrs(Sec); + if (!RelrsOrErr) + return RelrsOrErr.takeError(); + return this->Obj.decode_relrs(*RelrsOrErr).size(); + } + + return Sec.getEntityCount(); + }; + + bool HasRelocSections = false; + for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { + if (!isRelocationSec<ELFT>(Sec)) + continue; + HasRelocSections = true; + + std::string EntriesNum = "<?>"; + if (Expected<size_t> NumOrErr = GetEntriesNum(Sec)) + EntriesNum = std::to_string(*NumOrErr); + else + this->reportUniqueWarning("unable to get the number of relocations in " + + this->describe(Sec) + ": " + + toString(NumOrErr.takeError())); + + uintX_t Offset = Sec.sh_offset; + StringRef Name = this->getPrintableSectionName(Sec); + OS << "\nRelocation section '" << Name << "' at offset 0x" + << to_hexString(Offset, false) << " contains " << EntriesNum + << " entries:\n"; + printRelocHeaderFields<ELFT>(OS, Sec.sh_type); + this->printRelocationsHelper(Sec); + } + 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 Machine, unsigned Type) { + StringRef Name = getELFSectionTypeName(Machine, Type); + + // Handle SHT_GNU_* type names. + if (Name.startswith("SHT_GNU_")) { + if (Name == "SHT_GNU_HASH") + return "GNU_HASH"; + // E.g. SHT_GNU_verneed -> VERNEED. + return Name.drop_front(8).upper(); + } + + if (Name == "SHT_SYMTAB_SHNDX") + return "SYMTAB SECTION INDICES"; + + if (Name.startswith("SHT_")) + return Name.drop_front(4).str(); + return getSectionTypeOffsetString(Type); +} + +static void printSectionDescription(formatted_raw_ostream &OS, + unsigned EMachine) { + OS << "Key to Flags:\n"; + OS << " W (write), A (alloc), X (execute), M (merge), S (strings), I " + "(info),\n"; + OS << " L (link order), O (extra OS processing required), G (group), T " + "(TLS),\n"; + OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n"; + + if (EMachine == EM_X86_64) + OS << " l (large), "; + else if (EMachine == EM_ARM) + OS << " y (purecode), "; + else + OS << " "; + + OS << "p (processor specific)\n"; +} + +template <class ELFT> void GNUELFDumper<ELFT>::printSectionHeaders() { + unsigned Bias = ELFT::Is64Bits ? 0 : 8; + ArrayRef<Elf_Shdr> Sections = cantFail(this->Obj.sections()); + OS << "There are " << to_string(Sections.size()) + << " section headers, starting at offset " + << "0x" << to_hexString(this->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 (const Field &F : Fields) + printField(F); + OS << "\n"; + + StringRef SecStrTable; + if (Expected<StringRef> SecStrTableOrErr = + this->Obj.getSectionStringTable(Sections, this->WarningHandler)) + SecStrTable = *SecStrTableOrErr; + else + this->reportUniqueWarning(SecStrTableOrErr.takeError()); + + size_t SectionIndex = 0; + for (const Elf_Shdr &Sec : Sections) { + Fields[0].Str = to_string(SectionIndex); + if (SecStrTable.empty()) + Fields[1].Str = "<no-strings>"; + else + Fields[1].Str = std::string(unwrapOrError<StringRef>( + this->FileName, this->Obj.getSectionName(Sec, SecStrTable))); + Fields[2].Str = + getSectionTypeString(this->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(this->Obj.getHeader().e_machine, 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; + } + printSectionDescription(OS, this->Obj.getHeader().e_machine); +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printSymtabMessage(const Elf_Shdr *Symtab, + size_t Entries, + bool NonVisibilityBitsUsed) const { + StringRef Name; + if (Symtab) + Name = this->getPrintableSectionName(*Symtab); + if (!Name.empty()) + OS << "\nSymbol table '" << Name << "'"; + else + OS << "\nSymbol table for image"; + OS << " contains " << Entries << " entries:\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 +GNUELFDumper<ELFT>::getSymbolSectionNdx(const Elf_Sym &Symbol, + unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable) const { + 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: { + Expected<uint32_t> IndexOrErr = + object::getExtendedSymbolTableIndex<ELFT>(Symbol, SymIndex, ShndxTable); + if (!IndexOrErr) { + assert(Symbol.st_shndx == SHN_XINDEX && + "getExtendedSymbolTableIndex should only fail due to an invalid " + "SHT_SYMTAB_SHNDX table/reference"); + this->reportUniqueWarning(IndexOrErr.takeError()); + return "RSV[0xffff]"; + } + return to_string(format_decimal(*IndexOrErr, 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 GNUELFDumper<ELFT>::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, + bool IsDynamic, + bool NonVisibilityBitsUsed) const { + 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(SymIndex, 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 (this->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) { + if (this->Obj.getHeader().e_machine == ELF::EM_AARCH64) { + uint8_t Other = Symbol.st_other & ~0x3; + if (Other & STO_AARCH64_VARIANT_PCS) { + Other &= ~STO_AARCH64_VARIANT_PCS; + Fields[5].Str += " [VARIANT_PCS"; + if (Other != 0) + Fields[5].Str.append(" | " + to_hexString(Other, false)); + Fields[5].Str.append("]"); + } + } else { + Fields[5].Str += + " [<other: " + to_string(format_hex(Symbol.st_other, 2)) + ">]"; + } + } + + Fields[6].Column += NonVisibilityBitsUsed ? 13 : 0; + Fields[6].Str = getSymbolSectionNdx(Symbol, SymIndex, ShndxTable); + + Fields[7].Str = this->getFullSymbolName(Symbol, SymIndex, ShndxTable, + StrTable, IsDynamic); + for (const Field &Entry : Fields) + printField(Entry); + OS << "\n"; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printHashedSymbol(const Elf_Sym *Symbol, + unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + 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(SymIndex, 5)); + Fields[1].Str = to_string(format_decimal(Bucket, 3)) + ":"; + + 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 (this->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(*Symbol, SymIndex, ShndxTable); + Fields[8].Str = + this->getFullSymbolName(*Symbol, SymIndex, ShndxTable, StrTable, true); + + for (const Field &Entry : Fields) + printField(Entry); + OS << "\n"; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printSymbols(bool PrintSymbols, + bool PrintDynamicSymbols) { + if (!PrintSymbols && !PrintDynamicSymbols) + return; + // GNU readelf prints both the .dynsym and .symtab with --symbols. + this->printSymbolsHelper(true); + if (PrintSymbols) + this->printSymbolsHelper(false); +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printHashTableSymbols(const Elf_Hash &SysVHash) { + if (this->DynamicStringTable.empty()) + return; + + 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"; + + Elf_Sym_Range DynSyms = this->dynamic_symbols(); + const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + if (!FirstSym) { + this->reportUniqueWarning( + Twine("unable to print symbols for the .hash table: the " + "dynamic symbol table ") + + (this->DynSymRegion ? "is empty" : "was not found")); + return; + } + + DataRegion<Elf_Word> ShndxTable( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end()); + 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]) { + this->reportUniqueWarning(".hash section is invalid: bucket " + + Twine(Ch) + + ": a cycle was detected in the linked chain"); + break; + } + + printHashedSymbol(FirstSym + Ch, Ch, ShndxTable, this->DynamicStringTable, + Buc); + Visited[Ch] = true; + } + } +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printGnuHashTableSymbols(const Elf_GnuHash &GnuHash) { + if (this->DynamicStringTable.empty()) + return; + + Elf_Sym_Range DynSyms = this->dynamic_symbols(); + const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + if (!FirstSym) { + this->reportUniqueWarning( + Twine("unable to print symbols for the .gnu.hash table: the " + "dynamic symbol table ") + + (this->DynSymRegion ? "is empty" : "was not found")); + return; + } + + auto GetSymbol = [&](uint64_t SymIndex, + uint64_t SymsTotal) -> const Elf_Sym * { + if (SymIndex >= SymsTotal) { + this->reportUniqueWarning( + "unable to print hashed symbol with index " + Twine(SymIndex) + + ", which is greater than or equal to the number of dynamic symbols " + "(" + + Twine::utohexstr(SymsTotal) + ")"); + return nullptr; + } + return FirstSym + SymIndex; + }; + + Expected<ArrayRef<Elf_Word>> ValuesOrErr = + getGnuHashTableChains<ELFT>(this->DynSymRegion, &GnuHash); + ArrayRef<Elf_Word> Values; + if (!ValuesOrErr) + this->reportUniqueWarning("unable to get hash values for the SHT_GNU_HASH " + "section: " + + toString(ValuesOrErr.takeError())); + else + Values = *ValuesOrErr; + + DataRegion<Elf_Word> ShndxTable( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end()); + ArrayRef<Elf_Word> Buckets = GnuHash.buckets(); + for (uint32_t Buc = 0; Buc < GnuHash.nbuckets; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + uint32_t Index = Buckets[Buc]; + // Print whole chain. + while (true) { + uint32_t SymIndex = Index++; + if (const Elf_Sym *Sym = GetSymbol(SymIndex, DynSyms.size())) + printHashedSymbol(Sym, SymIndex, ShndxTable, this->DynamicStringTable, + Buc); + else + break; + + if (SymIndex < GnuHash.symndx) { + this->reportUniqueWarning( + "unable to read the hash value for symbol with index " + + Twine(SymIndex) + + ", which is less than the index of the first hashed symbol (" + + Twine(GnuHash.symndx) + ")"); + break; + } + + // Chain ends at symbol with stopper bit. + if ((Values[SymIndex - GnuHash.symndx] & 1) == 1) + break; + } + } +} + +template <class ELFT> void GNUELFDumper<ELFT>::printHashSymbols() { + if (this->HashTable) { + OS << "\n Symbol table of .hash for image:\n"; + if (Error E = checkHashTable<ELFT>(*this, this->HashTable)) + this->reportUniqueWarning(std::move(E)); + else + printHashTableSymbols(*this->HashTable); + } + + // Try printing the .gnu.hash table. + if (this->GnuHashTable) { + 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"; + + if (Error E = checkGNUHashTable<ELFT>(this->Obj, this->GnuHashTable)) + this->reportUniqueWarning(std::move(E)); + else + printGnuHashTableSymbols(*this->GnuHashTable); + } +} + +template <class ELFT> void GNUELFDumper<ELFT>::printSectionDetails() { + ArrayRef<Elf_Shdr> Sections = cantFail(this->Obj.sections()); + OS << "There are " << to_string(Sections.size()) + << " section headers, starting at offset " + << "0x" << to_hexString(this->Obj.getHeader().e_shoff, false) << ":\n\n"; + + OS << "Section Headers:\n"; + + auto PrintFields = [&](ArrayRef<Field> V) { + for (const Field &F : V) + printField(F); + OS << "\n"; + }; + + PrintFields({{"[Nr]", 2}, {"Name", 7}}); + + constexpr bool Is64 = ELFT::Is64Bits; + PrintFields({{"Type", 7}, + {Is64 ? "Address" : "Addr", 23}, + {"Off", Is64 ? 40 : 32}, + {"Size", Is64 ? 47 : 39}, + {"ES", Is64 ? 54 : 46}, + {"Lk", Is64 ? 59 : 51}, + {"Inf", Is64 ? 62 : 54}, + {"Al", Is64 ? 66 : 57}}); + PrintFields({{"Flags", 7}}); + + StringRef SecStrTable; + if (Expected<StringRef> SecStrTableOrErr = + this->Obj.getSectionStringTable(Sections, this->WarningHandler)) + SecStrTable = *SecStrTableOrErr; + else + this->reportUniqueWarning(SecStrTableOrErr.takeError()); + + size_t SectionIndex = 0; + const unsigned AddrSize = Is64 ? 16 : 8; + for (const Elf_Shdr &S : Sections) { + StringRef Name = "<?>"; + if (Expected<StringRef> NameOrErr = + this->Obj.getSectionName(S, SecStrTable)) + Name = *NameOrErr; + else + this->reportUniqueWarning(NameOrErr.takeError()); + + OS.PadToColumn(2); + OS << "[" << right_justify(to_string(SectionIndex), 2) << "]"; + PrintFields({{Name, 7}}); + PrintFields( + {{getSectionTypeString(this->Obj.getHeader().e_machine, S.sh_type), 7}, + {to_string(format_hex_no_prefix(S.sh_addr, AddrSize)), 23}, + {to_string(format_hex_no_prefix(S.sh_offset, 6)), Is64 ? 39 : 32}, + {to_string(format_hex_no_prefix(S.sh_size, 6)), Is64 ? 47 : 39}, + {to_string(format_hex_no_prefix(S.sh_entsize, 2)), Is64 ? 54 : 46}, + {to_string(S.sh_link), Is64 ? 59 : 51}, + {to_string(S.sh_info), Is64 ? 63 : 55}, + {to_string(S.sh_addralign), Is64 ? 66 : 58}}); + + OS.PadToColumn(7); + OS << "[" << to_string(format_hex_no_prefix(S.sh_flags, AddrSize)) << "]: "; + + DenseMap<unsigned, StringRef> FlagToName = { + {SHF_WRITE, "WRITE"}, {SHF_ALLOC, "ALLOC"}, + {SHF_EXECINSTR, "EXEC"}, {SHF_MERGE, "MERGE"}, + {SHF_STRINGS, "STRINGS"}, {SHF_INFO_LINK, "INFO LINK"}, + {SHF_LINK_ORDER, "LINK ORDER"}, {SHF_OS_NONCONFORMING, "OS NONCONF"}, + {SHF_GROUP, "GROUP"}, {SHF_TLS, "TLS"}, + {SHF_COMPRESSED, "COMPRESSED"}, {SHF_EXCLUDE, "EXCLUDE"}}; + + uint64_t Flags = S.sh_flags; + uint64_t UnknownFlags = 0; + bool NeedsComma = false; + while (Flags) { + // Take the least significant bit as a flag. + uint64_t Flag = Flags & -Flags; + Flags -= Flag; + + auto It = FlagToName.find(Flag); + if (It != FlagToName.end()) { + if (NeedsComma) + OS << ", "; + NeedsComma = true; + OS << It->second; + } else { + UnknownFlags |= Flag; + } + } + + auto PrintUnknownFlags = [&](uint64_t Mask, StringRef Name) { + uint64_t FlagsToPrint = UnknownFlags & Mask; + if (!FlagsToPrint) + return; + + if (NeedsComma) + OS << ", "; + OS << Name << " (" + << to_string(format_hex_no_prefix(FlagsToPrint, AddrSize)) << ")"; + UnknownFlags &= ~Mask; + NeedsComma = true; + }; + + PrintUnknownFlags(SHF_MASKOS, "OS"); + PrintUnknownFlags(SHF_MASKPROC, "PROC"); + PrintUnknownFlags(uint64_t(-1), "UNKNOWN"); + + OS << "\n"; + ++SectionIndex; + } +} + +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; +} + +template <class ELFT> +static bool checkTLSSections(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + if (Sec.sh_flags & ELF::SHF_TLS) { + // .tbss must only be shown in the PT_TLS segment. + if (Sec.sh_type == ELF::SHT_NOBITS) + return Phdr.p_type == ELF::PT_TLS; + + // SHF_TLS sections are only shown in PT_TLS, PT_LOAD or PT_GNU_RELRO + // segments. + return (Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) || + (Phdr.p_type == ELF::PT_GNU_RELRO); + } + + // PT_TLS must only have SHF_TLS sections. + return Phdr.p_type != ELF::PT_TLS; +} + +template <class ELFT> +static bool checkOffsets(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + // SHT_NOBITS sections don't need to have an offset inside the segment. + if (Sec.sh_type == ELF::SHT_NOBITS) + return true; + + if (Sec.sh_offset < Phdr.p_offset) + return false; + + // Only non-empty sections can be at the end of a segment. + if (Sec.sh_size == 0) + return (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz); + return Sec.sh_offset + Sec.sh_size <= Phdr.p_offset + Phdr.p_filesz; +} + +// Check that an allocatable section belongs to a virtual address +// space of a segment. +template <class ELFT> +static bool checkVMA(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + if (!(Sec.sh_flags & ELF::SHF_ALLOC)) + return true; + + if (Sec.sh_addr < Phdr.p_vaddr) + return false; + + bool IsTbss = + (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. + bool IsTbssInNonTLS = IsTbss && Phdr.p_type != ELF::PT_TLS; + // Only non-empty sections can be at the end of a segment. + if (Sec.sh_size == 0 || IsTbssInNonTLS) + return Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz; + return Sec.sh_addr + Sec.sh_size <= Phdr.p_vaddr + Phdr.p_memsz; +} + +template <class ELFT> +static bool checkPTDynamic(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + if (Phdr.p_type != ELF::PT_DYNAMIC || Phdr.p_memsz == 0 || Sec.sh_size != 0) + return true; + + // We get here when we have an empty section. Only non-empty sections can be + // at the start or at the end of PT_DYNAMIC. + // Is section within the phdr both based on offset and VMA? + bool CheckOffset = (Sec.sh_type == ELF::SHT_NOBITS) || + (Sec.sh_offset > Phdr.p_offset && + Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz); + bool CheckVA = !(Sec.sh_flags & ELF::SHF_ALLOC) || + (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz); + return CheckOffset && CheckVA; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printProgramHeaders( + bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(); + + // Display the section mapping along with the program headers, unless + // -section-mapping is explicitly set to false. + if (PrintSectionMapping != cl::BOU_FALSE) + printSectionMapping(); +} + +template <class ELFT> void GNUELFDumper<ELFT>::printProgramHeaders() { + unsigned Bias = ELFT::Is64Bits ? 8 : 0; + const Elf_Ehdr &Header = this->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; + + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = this->Obj.program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning("unable to dump program headers: " + + toString(PhdrsOrErr.takeError())); + return; + } + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + Fields[0].Str = getGNUPtType(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 (const Field &F : Fields) + printField(F); + if (Phdr.p_type == ELF::PT_INTERP) { + OS << "\n"; + auto ReportBadInterp = [&](const Twine &Msg) { + this->reportUniqueWarning( + "unable to read program interpreter name at offset 0x" + + Twine::utohexstr(Phdr.p_offset) + ": " + Msg); + }; + + if (Phdr.p_offset >= this->Obj.getBufSize()) { + ReportBadInterp("it goes past the end of the file (0x" + + Twine::utohexstr(this->Obj.getBufSize()) + ")"); + continue; + } + + const char *Data = + reinterpret_cast<const char *>(this->Obj.base()) + Phdr.p_offset; + size_t MaxSize = this->Obj.getBufSize() - Phdr.p_offset; + size_t Len = strnlen(Data, MaxSize); + if (Len == MaxSize) { + ReportBadInterp("it is not null-terminated"); + continue; + } + + OS << " [Requesting program interpreter: "; + OS << StringRef(Data, Len) << "]"; + } + OS << "\n"; + } +} + +template <class ELFT> void GNUELFDumper<ELFT>::printSectionMapping() { + OS << "\n Section to Segment mapping:\n Segment Sections...\n"; + DenseSet<const Elf_Shdr *> BelongsToSegment; + int Phnum = 0; + + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = this->Obj.program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning( + "can't read program headers to build section to segment mapping: " + + toString(PhdrsOrErr.takeError())); + return; + } + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + std::string Sections; + OS << format(" %2.2d ", Phnum++); + // Check if each section is in a segment and then print mapping. + for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { + if (Sec.sh_type == ELF::SHT_NULL) + continue; + + // 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. + if (checkTLSSections<ELFT>(Phdr, Sec) && checkOffsets<ELFT>(Phdr, Sec) && + checkVMA<ELFT>(Phdr, Sec) && checkPTDynamic<ELFT>(Phdr, Sec)) { + Sections += + unwrapOrError(this->FileName, this->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 : cantFail(this->Obj.sections())) { + if (BelongsToSegment.find(&Sec) == BelongsToSegment.end()) + Sections += + unwrapOrError(this->FileName, this->Obj.getSectionName(Sec)).str() + + ' '; + } + if (!Sections.empty()) { + OS << " None " << Sections << '\n'; + OS.flush(); + } +} + +namespace { + +template <class ELFT> +RelSymbol<ELFT> getSymbolForReloc(const ELFDumper<ELFT> &Dumper, + const Relocation<ELFT> &Reloc) { + using Elf_Sym = typename ELFT::Sym; + auto WarnAndReturn = [&](const Elf_Sym *Sym, + const Twine &Reason) -> RelSymbol<ELFT> { + Dumper.reportUniqueWarning( + "unable to get name of the dynamic symbol with index " + + Twine(Reloc.Symbol) + ": " + Reason); + return {Sym, "<corrupt>"}; + }; + + ArrayRef<Elf_Sym> Symbols = Dumper.dynamic_symbols(); + const Elf_Sym *FirstSym = Symbols.begin(); + if (!FirstSym) + return WarnAndReturn(nullptr, "no dynamic symbol table found"); + + // We might have an object without a section header. In this case the size of + // Symbols is zero, because there is no way to know the size of the dynamic + // table. We should allow this case and not print a warning. + if (!Symbols.empty() && Reloc.Symbol >= Symbols.size()) + return WarnAndReturn( + nullptr, + "index is greater than or equal to the number of dynamic symbols (" + + Twine(Symbols.size()) + ")"); + + const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile(); + const uint64_t FileSize = Obj.getBufSize(); + const uint64_t SymOffset = ((const uint8_t *)FirstSym - Obj.base()) + + (uint64_t)Reloc.Symbol * sizeof(Elf_Sym); + if (SymOffset + sizeof(Elf_Sym) > FileSize) + return WarnAndReturn(nullptr, "symbol at 0x" + Twine::utohexstr(SymOffset) + + " goes past the end of the file (0x" + + Twine::utohexstr(FileSize) + ")"); + + const Elf_Sym *Sym = FirstSym + Reloc.Symbol; + Expected<StringRef> ErrOrName = Sym->getName(Dumper.getDynamicStringTable()); + if (!ErrOrName) + return WarnAndReturn(Sym, toString(ErrOrName.takeError())); + + return {Sym == FirstSym ? nullptr : Sym, maybeDemangle(*ErrOrName)}; +} +} // namespace + +template <class ELFT> +static size_t getMaxDynamicTagSize(const ELFFile<ELFT> &Obj, + typename ELFT::DynRange Tags) { + size_t Max = 0; + for (const typename ELFT::Dyn &Dyn : Tags) + Max = std::max(Max, Obj.getDynamicTagAsString(Dyn.d_tag).size()); + return Max; +} + +template <class ELFT> void GNUELFDumper<ELFT>::printDynamicTable() { + Elf_Dyn_Range Table = this->dynamic_table(); + if (Table.empty()) + return; + + OS << "Dynamic section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(this->DynamicTable.Addr) - + this->Obj.base(), + 1) + << " contains " << Table.size() << " entries:\n"; + + // The type name is surrounded with round brackets, hence add 2. + size_t MaxTagSize = getMaxDynamicTagSize(this->Obj, Table) + 2; + // The "Name/Value" column should be indented from the "Type" column by N + // spaces, where N = MaxTagSize - length of "Type" (4) + trailing + // space (1) = 3. + OS << " Tag" + std::string(ELFT::Is64Bits ? 16 : 8, ' ') + "Type" + << std::string(MaxTagSize - 3, ' ') << "Name/Value\n"; + + std::string ValueFmt = " %-" + std::to_string(MaxTagSize) + "s "; + for (auto Entry : Table) { + uintX_t Tag = Entry.getTag(); + std::string Type = + std::string("(") + this->Obj.getDynamicTagAsString(Tag).c_str() + ")"; + std::string Value = this->getDynamicEntry(Tag, Entry.getVal()); + OS << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10) + << format(ValueFmt.c_str(), Type.c_str()) << Value << "\n"; + } +} + +template <class ELFT> void GNUELFDumper<ELFT>::printDynamicRelocations() { + this->printDynamicRelocationsHelper(); +} + +template <class ELFT> +void ELFDumper<ELFT>::printDynamicReloc(const Relocation<ELFT> &R) { + printRelRelaReloc(R, getSymbolForReloc(*this, R)); +} + +template <class ELFT> +void ELFDumper<ELFT>::printRelocationsHelper(const Elf_Shdr &Sec) { + this->forEachRelocationDo( + Sec, opts::RawRelr, + [&](const Relocation<ELFT> &R, unsigned Ndx, const Elf_Shdr &Sec, + const Elf_Shdr *SymTab) { printReloc(R, Ndx, Sec, SymTab); }, + [&](const Elf_Relr &R) { printRelrReloc(R); }); +} + +template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocationsHelper() { + const bool IsMips64EL = this->Obj.isMips64EL(); + if (this->DynRelaRegion.Size > 0) { + printDynamicRelocHeader(ELF::SHT_RELA, "RELA", this->DynRelaRegion); + for (const Elf_Rela &Rela : + this->DynRelaRegion.template getAsArrayRef<Elf_Rela>()) + printDynamicReloc(Relocation<ELFT>(Rela, IsMips64EL)); + } + + if (this->DynRelRegion.Size > 0) { + printDynamicRelocHeader(ELF::SHT_REL, "REL", this->DynRelRegion); + for (const Elf_Rel &Rel : + this->DynRelRegion.template getAsArrayRef<Elf_Rel>()) + printDynamicReloc(Relocation<ELFT>(Rel, IsMips64EL)); + } + + if (this->DynRelrRegion.Size > 0) { + printDynamicRelocHeader(ELF::SHT_REL, "RELR", this->DynRelrRegion); + Elf_Relr_Range Relrs = + this->DynRelrRegion.template getAsArrayRef<Elf_Relr>(); + for (const Elf_Rel &Rel : Obj.decode_relrs(Relrs)) + printDynamicReloc(Relocation<ELFT>(Rel, IsMips64EL)); + } + + if (this->DynPLTRelRegion.Size) { + if (this->DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) { + printDynamicRelocHeader(ELF::SHT_RELA, "PLT", this->DynPLTRelRegion); + for (const Elf_Rela &Rela : + this->DynPLTRelRegion.template getAsArrayRef<Elf_Rela>()) + printDynamicReloc(Relocation<ELFT>(Rela, IsMips64EL)); + } else { + printDynamicRelocHeader(ELF::SHT_REL, "PLT", this->DynPLTRelRegion); + for (const Elf_Rel &Rel : + this->DynPLTRelRegion.template getAsArrayRef<Elf_Rel>()) + printDynamicReloc(Relocation<ELFT>(Rel, IsMips64EL)); + } + } +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printGNUVersionSectionProlog( + const typename ELFT::Shdr &Sec, const Twine &Label, unsigned EntriesNum) { + // Don't inline the SecName, because it might report a warning to stderr and + // corrupt the output. + StringRef SecName = this->getPrintableSectionName(Sec); + OS << Label << " section '" << SecName << "' " + << "contains " << EntriesNum << " entries:\n"; + + StringRef LinkedSecName = "<corrupt>"; + if (Expected<const typename ELFT::Shdr *> LinkedSecOrErr = + this->Obj.getSection(Sec.sh_link)) + LinkedSecName = this->getPrintableSectionName(**LinkedSecOrErr); + else + this->reportUniqueWarning("invalid section linked to " + + this->describe(Sec) + ": " + + toString(LinkedSecOrErr.takeError())); + + OS << " Addr: " << format_hex_no_prefix(Sec.sh_addr, 16) + << " Offset: " << format_hex(Sec.sh_offset, 8) + << " Link: " << Sec.sh_link << " (" << LinkedSecName << ")\n"; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printVersionSymbolSection(const Elf_Shdr *Sec) { + if (!Sec) + return; + + printGNUVersionSectionProlog(*Sec, "Version symbols", + Sec->sh_size / sizeof(Elf_Versym)); + Expected<ArrayRef<Elf_Versym>> VerTableOrErr = + this->getVersionTable(*Sec, /*SymTab=*/nullptr, + /*StrTab=*/nullptr, /*SymTabSec=*/nullptr); + if (!VerTableOrErr) { + this->reportUniqueWarning(VerTableOrErr.takeError()); + return; + } + + SmallVector<Optional<VersionEntry>, 0> *VersionMap = nullptr; + if (Expected<SmallVector<Optional<VersionEntry>, 0> *> MapOrErr = + this->getVersionMap()) + VersionMap = *MapOrErr; + else + this->reportUniqueWarning(MapOrErr.takeError()); + + ArrayRef<Elf_Versym> VerTable = *VerTableOrErr; + std::vector<StringRef> Versions; + for (size_t I = 0, E = VerTable.size(); I < E; ++I) { + unsigned Ndx = VerTable[I].vs_index; + if (Ndx == VER_NDX_LOCAL || Ndx == VER_NDX_GLOBAL) { + Versions.emplace_back(Ndx == VER_NDX_LOCAL ? "*local*" : "*global*"); + continue; + } + + if (!VersionMap) { + Versions.emplace_back("<corrupt>"); + continue; + } + + bool IsDefault; + Expected<StringRef> NameOrErr = this->Obj.getSymbolVersionByIndex( + Ndx, IsDefault, *VersionMap, /*IsSymHidden=*/None); + if (!NameOrErr) { + this->reportUniqueWarning("unable to get a version for entry " + + Twine(I) + " of " + this->describe(*Sec) + + ": " + toString(NameOrErr.takeError())); + Versions.emplace_back("<corrupt>"); + continue; + } + Versions.emplace_back(*NameOrErr); + } + + // readelf prints 4 entries per line. + uint64_t Entries = VerTable.size(); + for (uint64_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) { + OS << " " << format_hex_no_prefix(VersymRow, 3) << ":"; + for (uint64_t I = 0; (I < 4) && (I + VersymRow) < Entries; ++I) { + unsigned Ndx = VerTable[VersymRow + I].vs_index; + OS << format("%4x%c", Ndx & VERSYM_VERSION, + Ndx & VERSYM_HIDDEN ? 'h' : ' '); + OS << left_justify("(" + std::string(Versions[VersymRow + I]) + ")", 13); + } + 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 GNUELFDumper<ELFT>::printVersionDefinitionSection(const Elf_Shdr *Sec) { + if (!Sec) + return; + + printGNUVersionSectionProlog(*Sec, "Version definition", Sec->sh_info); + + Expected<std::vector<VerDef>> V = this->Obj.getVersionDefinitions(*Sec); + if (!V) { + this->reportUniqueWarning(V.takeError()); + return; + } + + for (const VerDef &Def : *V) { + OS << format(" 0x%04x: Rev: %u Flags: %s Index: %u Cnt: %u Name: %s\n", + Def.Offset, Def.Version, + versionFlagToString(Def.Flags).c_str(), Def.Ndx, Def.Cnt, + Def.Name.data()); + unsigned I = 0; + for (const VerdAux &Aux : Def.AuxV) + OS << format(" 0x%04x: Parent %u: %s\n", Aux.Offset, ++I, + Aux.Name.data()); + } + + OS << '\n'; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printVersionDependencySection(const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned VerneedNum = Sec->sh_info; + printGNUVersionSectionProlog(*Sec, "Version needs", VerneedNum); + + Expected<std::vector<VerNeed>> V = + this->Obj.getVersionDependencies(*Sec, this->WarningHandler); + if (!V) { + this->reportUniqueWarning(V.takeError()); + return; + } + + for (const VerNeed &VN : *V) { + OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", VN.Offset, + VN.Version, VN.File.data(), VN.Cnt); + for (const VernAux &Aux : VN.AuxV) + OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", Aux.Offset, + Aux.Name.data(), versionFlagToString(Aux.Flags).c_str(), + Aux.Other); + } + OS << '\n'; +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printHashHistogram(const Elf_Hash &HashTable) { + 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]) { + this->reportUniqueWarning(".hash section is invalid: bucket " + + Twine(C) + + ": a cycle was detected in the linked chain"); + 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); + } +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printGnuHashHistogram( + const Elf_GnuHash &GnuHashTable) { + Expected<ArrayRef<Elf_Word>> ChainsOrErr = + getGnuHashTableChains<ELFT>(this->DynSymRegion, &GnuHashTable); + if (!ChainsOrErr) { + this->reportUniqueWarning("unable to print the GNU hash table histogram: " + + toString(ChainsOrErr.takeError())); + return; + } + + ArrayRef<Elf_Word> Chains = *ChainsOrErr; + size_t Symndx = GnuHashTable.symndx; + size_t TotalSyms = 0; + size_t MaxChain = 1; + size_t CumulativeNonZero = 0; + + size_t NBucket = GnuHashTable.nbuckets; + if (Chains.empty() || NBucket == 0) + return; + + ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets(); + 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); + } +} + +// Hash histogram shows statistics of how efficient the hash was for the +// dynamic symbol table. The table shows the number of hash buckets for +// different lengths of chains as an absolute number and percentage of the total +// buckets, and the cumulative coverage of symbols for each set of buckets. +template <class ELFT> void GNUELFDumper<ELFT>::printHashHistograms() { + // Print histogram for the .hash section. + if (this->HashTable) { + if (Error E = checkHashTable<ELFT>(*this, this->HashTable)) + this->reportUniqueWarning(std::move(E)); + else + printHashHistogram(*this->HashTable); + } + + // Print histogram for the .gnu.hash section. + if (this->GnuHashTable) { + if (Error E = checkGNUHashTable<ELFT>(this->Obj, this->GnuHashTable)) + this->reportUniqueWarning(std::move(E)); + else + printGnuHashHistogram(*this->GnuHashTable); + } +} + +template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() { + OS << "GNUStyle::printCGProfile not implemented\n"; +} + +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> +static Expected<std::vector<uint64_t>> +decodeAddrsigSection(const ELFFile<ELFT> &Obj, const typename ELFT::Shdr &Sec) { + Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj.getSectionContents(Sec); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + + if (Expected<std::vector<uint64_t>> SymsOrErr = + toULEB128Array(*ContentsOrErr)) + return *SymsOrErr; + else + return createError("unable to decode " + describe(Obj, Sec) + ": " + + toString(SymsOrErr.takeError())); +} + +template <class ELFT> void GNUELFDumper<ELFT>::printAddrsig() { + if (!this->DotAddrsigSec) + return; + + Expected<std::vector<uint64_t>> SymsOrErr = + decodeAddrsigSection(this->Obj, *this->DotAddrsigSec); + if (!SymsOrErr) { + this->reportUniqueWarning(SymsOrErr.takeError()); + return; + } + + StringRef Name = this->getPrintableSectionName(*this->DotAddrsigSec); + OS << "\nAddress-significant symbols section '" << Name << "'" + << " contains " << SymsOrErr->size() << " entries:\n"; + OS << " Num: Name\n"; + + Field Fields[2] = {0, 8}; + size_t SymIndex = 0; + for (uint64_t Sym : *SymsOrErr) { + Fields[0].Str = to_string(format_decimal(++SymIndex, 6)) + ":"; + Fields[1].Str = this->getStaticSymbolName(Sym); + for (const Field &Entry : Fields) + printField(Entry); + OS << "\n"; + } +} + +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 {std::string(OSName), ABI.str(), /*IsValid=*/true}; +} + +static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) { + std::string str; + raw_string_ostream OS(str); + for (uint8_t 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 std::string &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: { + StringRef 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); + std::string HSAMetadataString; + if (!Verifier.verify(MsgPackDoc.getRoot())) + HSAMetadataString = "Invalid AMDGPU Metadata\n"; + + 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 createError("the note of size 0x" + Twine::utohexstr(Desc.size()) + + " is too short, expected at least 0x" + + Twine::utohexstr(Bytes * 2)); + if (Desc.getData().back() != 0) + return createError("the note is 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 createError("unable to read file mappings (found " + + Twine(FileCount) + "): the note of size 0x" + + Twine::utohexstr(Desc.size()) + " is too short"); + + uint64_t FilenamesOffset = 0; + DataExtractor Filenames( + Desc.getData().drop_front(DescOffset + 3 * FileCount * Bytes), + Desc.isLittleEndian(), Desc.getAddressSize()); + + Ret.Mappings.resize(FileCount); + size_t I = 0; + for (CoreFileMapping &Mapping : Ret.Mappings) { + ++I; + if (!Filenames.isValidOffsetForDataOfSize(FilenamesOffset, 1)) + return createError( + "unable to read the file name for the mapping with index " + + Twine(I) + ": the note of size 0x" + Twine::utohexstr(Desc.size()) + + " is truncated"); + 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'; + } +} + +static const NoteType GenericNoteTypes[] = { + {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"}, +}; + +static const NoteType GNUNoteTypes[] = { + {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)"}, +}; + +static const NoteType FreeBSDNoteTypes[] = { + {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)"}, +}; + +static const NoteType AMDNoteTypes[] = { + {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)"}, +}; + +static const NoteType AMDGPUNoteTypes[] = { + {ELF::NT_AMDGPU_METADATA, "NT_AMDGPU_METADATA (AMDGPU Metadata)"}, +}; + +static const NoteType CoreNoteTypes[] = { + {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)"}, +}; + +template <class ELFT> +const StringRef getNoteTypeName(const typename ELFT::Note &Note, + unsigned ELFType) { + uint32_t Type = Note.getType(); + auto FindNote = [&](ArrayRef<NoteType> V) -> StringRef { + for (const NoteType &N : V) + if (N.ID == Type) + return N.Name; + return ""; + }; + + StringRef Name = Note.getName(); + if (Name == "GNU") + return FindNote(GNUNoteTypes); + if (Name == "FreeBSD") + return FindNote(FreeBSDNoteTypes); + if (Name == "AMD") + return FindNote(AMDNoteTypes); + if (Name == "AMDGPU") + return FindNote(AMDGPUNoteTypes); + + if (ELFType == ELF::ET_CORE) + return FindNote(CoreNoteTypes); + return FindNote(GenericNoteTypes); +} + +template <class ELFT> +static void printNotesHelper( + const ELFDumper<ELFT> &Dumper, + llvm::function_ref<void(Optional<StringRef>, typename ELFT::Off, + typename ELFT::Addr)> + StartNotesFn, + llvm::function_ref<Error(const typename ELFT::Note &)> ProcessNoteFn, + llvm::function_ref<void()> FinishNotesFn) { + const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile(); + + ArrayRef<typename ELFT::Shdr> Sections = cantFail(Obj.sections()); + if (Obj.getHeader().e_type != ELF::ET_CORE && !Sections.empty()) { + for (const typename ELFT::Shdr &S : Sections) { + if (S.sh_type != SHT_NOTE) + continue; + StartNotesFn(expectedToOptional(Obj.getSectionName(S)), S.sh_offset, + S.sh_size); + Error Err = Error::success(); + size_t I = 0; + for (const typename ELFT::Note Note : Obj.notes(S, Err)) { + if (Error E = ProcessNoteFn(Note)) + Dumper.reportUniqueWarning( + "unable to read note with index " + Twine(I) + " from the " + + describe(Obj, S) + ": " + toString(std::move(E))); + ++I; + } + if (Err) + Dumper.reportUniqueWarning("unable to read notes from the " + + describe(Obj, S) + ": " + + toString(std::move(Err))); + FinishNotesFn(); + } + return; + } + + Expected<ArrayRef<typename ELFT::Phdr>> PhdrsOrErr = Obj.program_headers(); + if (!PhdrsOrErr) { + Dumper.reportUniqueWarning( + "unable to read program headers to locate the PT_NOTE segment: " + + toString(PhdrsOrErr.takeError())); + return; + } + + for (size_t I = 0, E = (*PhdrsOrErr).size(); I != E; ++I) { + const typename ELFT::Phdr &P = (*PhdrsOrErr)[I]; + if (P.p_type != PT_NOTE) + continue; + StartNotesFn(/*SecName=*/None, P.p_offset, P.p_filesz); + Error Err = Error::success(); + size_t Index = 0; + for (const typename ELFT::Note Note : Obj.notes(P, Err)) { + if (Error E = ProcessNoteFn(Note)) + Dumper.reportUniqueWarning("unable to read note with index " + + Twine(Index) + + " from the PT_NOTE segment with index " + + Twine(I) + ": " + toString(std::move(E))); + ++Index; + } + if (Err) + Dumper.reportUniqueWarning( + "unable to read notes from the PT_NOTE segment with index " + + Twine(I) + ": " + toString(std::move(Err))); + FinishNotesFn(); + } +} + +template <class ELFT> void GNUELFDumper<ELFT>::printNotes() { + auto PrintHeader = [&](Optional<StringRef> SecName, + const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { + OS << "Displaying notes found "; + + if (SecName) + OS << "in: " << *SecName << "\n"; + else + OS << "at file offset " << format_hex(Offset, 10) << " with length " + << format_hex(Size, 10) << ":\n"; + + OS << " Owner Data size \tDescription\n"; + }; + + auto ProcessNote = [&](const Elf_Note &Note) -> Error { + 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'; + + StringRef NoteType = + getNoteTypeName<ELFT>(Note, this->Obj.getHeader().e_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)); + if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) + printCoreNote<ELFT>(OS, *NoteOrErr); + else + return NoteOrErr.takeError(); + } + } else if (!Descriptor.empty()) { + OS << " description data:"; + for (uint8_t B : Descriptor) + OS << " " << format("%02x", B); + OS << '\n'; + } + return Error::success(); + }; + + printNotesHelper(*this, PrintHeader, ProcessNote, []() {}); +} + +template <class ELFT> void GNUELFDumper<ELFT>::printELFLinkerOptions() { + OS << "printELFLinkerOptions not implemented!\n"; +} + +template <class ELFT> +void ELFDumper<ELFT>::printDependentLibsHelper( + function_ref<void(const Elf_Shdr &)> OnSectionStart, + function_ref<void(StringRef, uint64_t)> OnLibEntry) { + auto Warn = [this](unsigned SecNdx, StringRef Msg) { + this->reportUniqueWarning("SHT_LLVM_DEPENDENT_LIBRARIES section at index " + + Twine(SecNdx) + " is broken: " + Msg); + }; + + unsigned I = -1; + for (const Elf_Shdr &Shdr : cantFail(Obj.sections())) { + ++I; + if (Shdr.sh_type != ELF::SHT_LLVM_DEPENDENT_LIBRARIES) + continue; + + OnSectionStart(Shdr); + + Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj.getSectionContents(Shdr); + if (!ContentsOrErr) { + Warn(I, toString(ContentsOrErr.takeError())); + continue; + } + + ArrayRef<uint8_t> Contents = *ContentsOrErr; + if (!Contents.empty() && Contents.back() != 0) { + Warn(I, "the content is not null-terminated"); + continue; + } + + for (const uint8_t *I = Contents.begin(), *E = Contents.end(); I < E;) { + StringRef Lib((const char *)I); + OnLibEntry(Lib, I - Contents.begin()); + I += Lib.size() + 1; + } + } +} + +template <class ELFT> +void ELFDumper<ELFT>::forEachRelocationDo( + const Elf_Shdr &Sec, bool RawRelr, + llvm::function_ref<void(const Relocation<ELFT> &, unsigned, + const Elf_Shdr &, const Elf_Shdr *)> + RelRelaFn, + llvm::function_ref<void(const Elf_Relr &)> RelrFn) { + auto Warn = [&](Error &&E, + const Twine &Prefix = "unable to read relocations from") { + this->reportUniqueWarning(Prefix + " " + describe(Sec) + ": " + + toString(std::move(E))); + }; + + // SHT_RELR/SHT_ANDROID_RELR sections do not have an associated symbol table. + // For them we should not treat the value of the sh_link field as an index of + // a symbol table. + const Elf_Shdr *SymTab; + if (Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_RELR) { + Expected<const Elf_Shdr *> SymTabOrErr = Obj.getSection(Sec.sh_link); + if (!SymTabOrErr) { + Warn(SymTabOrErr.takeError(), "unable to locate a symbol table for"); + return; + } + SymTab = *SymTabOrErr; + } + + unsigned RelNdx = 0; + const bool IsMips64EL = this->Obj.isMips64EL(); + switch (Sec.sh_type) { + case ELF::SHT_REL: + if (Expected<Elf_Rel_Range> RangeOrErr = Obj.rels(Sec)) { + for (const Elf_Rel &R : *RangeOrErr) + RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, SymTab); + } else { + Warn(RangeOrErr.takeError()); + } + break; + case ELF::SHT_RELA: + if (Expected<Elf_Rela_Range> RangeOrErr = Obj.relas(Sec)) { + for (const Elf_Rela &R : *RangeOrErr) + RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, SymTab); + } else { + Warn(RangeOrErr.takeError()); + } + break; + case ELF::SHT_RELR: + case ELF::SHT_ANDROID_RELR: { + Expected<Elf_Relr_Range> RangeOrErr = Obj.relrs(Sec); + if (!RangeOrErr) { + Warn(RangeOrErr.takeError()); + break; + } + if (RawRelr) { + for (const Elf_Relr &R : *RangeOrErr) + RelrFn(R); + break; + } + + for (const Elf_Rel &R : Obj.decode_relrs(*RangeOrErr)) + RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, + /*SymTab=*/nullptr); + break; + } + case ELF::SHT_ANDROID_REL: + case ELF::SHT_ANDROID_RELA: + if (Expected<std::vector<Elf_Rela>> RelasOrErr = Obj.android_relas(Sec)) { + for (const Elf_Rela &R : *RelasOrErr) + RelRelaFn(Relocation<ELFT>(R, IsMips64EL), RelNdx++, Sec, SymTab); + } else { + Warn(RelasOrErr.takeError()); + } + break; + } +} + +template <class ELFT> +StringRef ELFDumper<ELFT>::getPrintableSectionName(const Elf_Shdr &Sec) const { + StringRef Name = "<?>"; + if (Expected<StringRef> SecNameOrErr = + Obj.getSectionName(Sec, this->WarningHandler)) + Name = *SecNameOrErr; + else + this->reportUniqueWarning("unable to get the name of " + describe(Sec) + + ": " + toString(SecNameOrErr.takeError())); + return Name; +} + +template <class ELFT> void GNUELFDumper<ELFT>::printDependentLibs() { + bool SectionStarted = false; + struct NameOffset { + StringRef Name; + uint64_t Offset; + }; + std::vector<NameOffset> SecEntries; + NameOffset Current; + auto PrintSection = [&]() { + OS << "Dependent libraries section " << Current.Name << " at offset " + << format_hex(Current.Offset, 1) << " contains " << SecEntries.size() + << " entries:\n"; + for (NameOffset Entry : SecEntries) + OS << " [" << format("%6" PRIx64, Entry.Offset) << "] " << Entry.Name + << "\n"; + OS << "\n"; + SecEntries.clear(); + }; + + auto OnSectionStart = [&](const Elf_Shdr &Shdr) { + if (SectionStarted) + PrintSection(); + SectionStarted = true; + Current.Offset = Shdr.sh_offset; + Current.Name = this->getPrintableSectionName(Shdr); + }; + auto OnLibEntry = [&](StringRef Lib, uint64_t Offset) { + SecEntries.push_back(NameOffset{Lib, Offset}); + }; + + this->printDependentLibsHelper(OnSectionStart, OnLibEntry); + if (SectionStarted) + PrintSection(); +} + +template <class ELFT> +bool ELFDumper<ELFT>::printFunctionStackSize( + uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, + const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) { + uint32_t FuncSymIndex = 0; + if (this->DotSymtabSec) { + if (Expected<Elf_Sym_Range> SymsOrError = Obj.symbols(this->DotSymtabSec)) { + uint32_t Index = (uint32_t)-1; + for (const Elf_Sym &Sym : *SymsOrError) { + ++Index; + + if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC) + continue; + + if (Expected<uint64_t> SymAddrOrErr = + ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress()) { + if (SymValue != *SymAddrOrErr) + continue; + } else { + std::string Name = this->getStaticSymbolName(Index); + reportUniqueWarning("unable to get address of symbol '" + Name + + "': " + toString(SymAddrOrErr.takeError())); + break; + } + + // Check if the symbol is in the right section. FunctionSec == None + // means "any section". + if (FunctionSec) { + if (Expected<const Elf_Shdr *> SecOrErr = + Obj.getSection(Sym, this->DotSymtabSec, + this->getShndxTable(this->DotSymtabSec))) { + if (*FunctionSec != *SecOrErr) + continue; + } else { + std::string Name = this->getStaticSymbolName(Index); + // Note: it is impossible to trigger this error currently, it is + // untested. + reportUniqueWarning("unable to get section of symbol '" + Name + + "': " + toString(SecOrErr.takeError())); + break; + } + } + + FuncSymIndex = Index; + break; + } + } else { + reportUniqueWarning("unable to read the symbol table: " + + toString(SymsOrError.takeError())); + } + } + + std::string FuncName = "?"; + if (!FuncSymIndex) + reportUniqueWarning( + "could not identify function symbol for stack size entry in " + + describe(StackSizeSec)); + else + FuncName = this->getStaticSymbolName(FuncSymIndex); + + // Extract the size. The expectation is that Offset is pointing to the right + // place, i.e. past the function address. + Error Err = Error::success(); + uint64_t StackSize = Data.getULEB128(Offset, &Err); + if (Err) { + reportUniqueWarning("could not extract a valid stack size from " + + describe(StackSizeSec) + ": " + + toString(std::move(Err))); + return false; + } + printStackSizeEntry(StackSize, FuncName); + return true; +} + +template <class ELFT> +void GNUELFDumper<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 ELFDumper<ELFT>::printStackSize(const Relocation<ELFT> &R, + const Elf_Shdr &RelocSec, unsigned Ndx, + const Elf_Shdr *SymTab, + const Elf_Shdr *FunctionSec, + const Elf_Shdr &StackSizeSec, + const RelocationResolver &Resolver, + DataExtractor Data) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + const Elf_Sym *Sym = nullptr; + Expected<RelSymbol<ELFT>> TargetOrErr = this->getRelocationTarget(R, SymTab); + if (!TargetOrErr) + reportUniqueWarning("unable to get the target of relocation with index " + + Twine(Ndx) + " in " + describe(RelocSec) + ": " + + toString(TargetOrErr.takeError())); + else + Sym = TargetOrErr->Sym; + + uint64_t RelocSymValue = 0; + if (Sym) { + Expected<const Elf_Shdr *> SectionOrErr = + this->Obj.getSection(*Sym, SymTab, this->getShndxTable(SymTab)); + if (!SectionOrErr) { + reportUniqueWarning( + "cannot identify the section for relocation symbol '" + + (*TargetOrErr).Name + "': " + toString(SectionOrErr.takeError())); + } else if (*SectionOrErr != FunctionSec) { + reportUniqueWarning("relocation symbol '" + (*TargetOrErr).Name + + "' is not in the expected section"); + // Pretend that the symbol is in the correct section and report its + // stack size anyway. + FunctionSec = *SectionOrErr; + } + + RelocSymValue = Sym->st_value; + } + + uint64_t Offset = R.Offset; + if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) { + reportUniqueWarning("found invalid relocation offset (0x" + + Twine::utohexstr(Offset) + ") into " + + describe(StackSizeSec) + + " while trying to extract a stack size entry"); + return; + } + + uint64_t SymValue = + Resolver(R.Type, Offset, RelocSymValue, Data.getAddress(&Offset), + R.Addend.getValueOr(0)); + this->printFunctionStackSize(SymValue, FunctionSec, StackSizeSec, Data, + &Offset); +} + +template <class ELFT> +void ELFDumper<ELFT>::printNonRelocatableStackSizes( + std::function<void()> PrintHeader) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { + if (this->getPrintableSectionName(Sec) != ".stack_sizes") + continue; + PrintHeader(); + ArrayRef<uint8_t> Contents = + unwrapOrError(this->FileName, Obj.getSectionContents(Sec)); + DataExtractor Data(Contents, Obj.isLE(), sizeof(Elf_Addr)); + 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)) { + reportUniqueWarning( + describe(Sec) + + " ended while trying to extract a stack size entry"); + break; + } + uint64_t SymValue = Data.getAddress(&Offset); + if (!printFunctionStackSize(SymValue, /*FunctionSec=*/None, Sec, Data, + &Offset)) + break; + } + } +} + +template <class ELFT> +void ELFDumper<ELFT>::printRelocatableStackSizes( + std::function<void()> PrintHeader) { + // Build a map between stack size sections and their corresponding relocation + // sections. + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap; + for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec)) + 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 + .insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) + .second) + continue; + + // Check relocation sections if they are relocating contents of a + // stack sizes section. + if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL) + continue; + + Expected<const Elf_Shdr *> RelSecOrErr = Obj.getSection(Sec.sh_info); + if (!RelSecOrErr) { + reportUniqueWarning(describe(Sec) + + ": failed to get a relocated section: " + + toString(RelSecOrErr.takeError())); + continue; + } + + const Elf_Shdr *ContentsSec = *RelSecOrErr; + if (this->getPrintableSectionName(**RelSecOrErr) != ".stack_sizes") + continue; + + // Insert a mapping from the stack sizes section to its relocation section. + StackSizeRelocMap[ContentsSec] = &Sec; + } + + for (const auto &StackSizeMapEntry : StackSizeRelocMap) { + PrintHeader(); + const Elf_Shdr *StackSizesELFSec = StackSizeMapEntry.first; + const Elf_Shdr *RelocSec = StackSizeMapEntry.second; + + // Warn about stack size sections without a relocation section. + if (!RelocSec) { + reportWarning(createError(".stack_sizes (" + describe(*StackSizesELFSec) + + ") does not have a corresponding " + "relocation section"), + FileName); + 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 *FunctionSec = unwrapOrError( + this->FileName, Obj.getSection(StackSizesELFSec->sh_link)); + + SupportsRelocation IsSupportedFn; + RelocationResolver Resolver; + std::tie(IsSupportedFn, Resolver) = getRelocationResolver(this->ObjF); + ArrayRef<uint8_t> Contents = + unwrapOrError(this->FileName, Obj.getSectionContents(*StackSizesELFSec)); + DataExtractor Data(Contents, Obj.isLE(), sizeof(Elf_Addr)); + + forEachRelocationDo( + *RelocSec, /*RawRelr=*/false, + [&](const Relocation<ELFT> &R, unsigned Ndx, const Elf_Shdr &Sec, + const Elf_Shdr *SymTab) { + if (!IsSupportedFn || !IsSupportedFn(R.Type)) { + reportUniqueWarning( + describe(*RelocSec) + + " contains an unsupported relocation with index " + Twine(Ndx) + + ": " + Obj.getRelocationTypeName(R.Type)); + return; + } + + this->printStackSize(R, *RelocSec, Ndx, SymTab, FunctionSec, + *StackSizesELFSec, Resolver, Data); + }, + [](const Elf_Relr &) { + llvm_unreachable("can't get here, because we only support " + "SHT_REL/SHT_RELA sections"); + }); + } +} + +template <class ELFT> +void GNUELFDumper<ELFT>::printStackSizes() { + 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 (this->Obj.getHeader().e_type == ELF::ET_REL) + this->printRelocatableStackSizes(PrintHeader); + else + this->printNonRelocatableStackSizes(PrintHeader); +} + +template <class ELFT> +void GNUELFDumper<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"; + + DataRegion<Elf_Word> ShndxTable( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end()); + for (auto &E : Parser.getGlobalEntries()) { + const Elf_Sym &Sym = *Parser.getGotSym(&E); + const Elf_Sym &FirstSym = this->dynamic_symbols()[0]; + std::string SymName = this->getFullSymbolName( + Sym, &Sym - &FirstSym, ShndxTable, this->DynamicStringTable, 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(Sym, &Sym - this->dynamic_symbols().begin(), + ShndxTable); + 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 GNUELFDumper<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"; + DataRegion<Elf_Word> ShndxTable( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end()); + for (auto &E : Parser.getPltEntries()) { + const Elf_Sym &Sym = *Parser.getPltSym(&E); + const Elf_Sym &FirstSym = *cantFail( + this->Obj.template getEntry<Elf_Sym>(*Parser.getPltSymTable(), 0)); + std::string SymName = this->getFullSymbolName( + Sym, &Sym - &FirstSym, ShndxTable, this->DynamicStringTable, 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(Sym, &Sym - this->dynamic_symbols().begin(), + ShndxTable); + OS.PadToColumn(41 + 3 * Bias); + OS << SymName << "\n"; + } + } +} + +template <class ELFT> +Expected<const Elf_Mips_ABIFlags<ELFT> *> +getMipsAbiFlagsSection(const ELFDumper<ELFT> &Dumper) { + const typename ELFT::Shdr *Sec = Dumper.findSectionByName(".MIPS.abiflags"); + if (Sec == nullptr) + return nullptr; + + constexpr StringRef ErrPrefix = "unable to read the .MIPS.abiflags section: "; + Expected<ArrayRef<uint8_t>> DataOrErr = + Dumper.getElfObject().getELFFile().getSectionContents(*Sec); + if (!DataOrErr) + return createError(ErrPrefix + toString(DataOrErr.takeError())); + + if (DataOrErr->size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) + return createError(ErrPrefix + "it has a wrong size (" + + Twine(DataOrErr->size()) + ")"); + return reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(DataOrErr->data()); +} + +template <class ELFT> void GNUELFDumper<ELFT>::printMipsABIFlags() { + const Elf_Mips_ABIFlags<ELFT> *Flags = nullptr; + if (Expected<const Elf_Mips_ABIFlags<ELFT> *> SecOrErr = + getMipsAbiFlagsSection(*this)) + Flags = *SecOrErr; + else + this->reportUniqueWarning(SecOrErr.takeError()); + if (!Flags) + return; + + 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 LLVMELFDumper<ELFT>::printFileHeaders() { + const Elf_Ehdr &E = this->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)); + } + + std::string TypeStr; + if (const EnumEntry<unsigned> *Ent = getObjectFileEnumEntry(E.e_type)) { + TypeStr = Ent->Name.str(); + } else { + if (E.e_type >= ET_LOPROC) + TypeStr = "Processor Specific"; + else if (E.e_type >= ET_LOOS) + TypeStr = "OS Specific"; + else + TypeStr = "Unknown"; + } + W.printString("Type", TypeStr + " (0x" + to_hexString(E.e_type) + ")"); + + 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(this->Obj, this->FileName)); + W.printString("StringTableSectionIndex", + getSectionHeaderTableIndexString(this->Obj, this->FileName)); + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printGroupSections() { + DictScope Lists(W, "Groups"); + std::vector<GroupSection> V = this->getGroups(); + 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) + this->reportUniqueWarning( + "section with index " + Twine(GM.Index) + + ", included in the group section with index " + + Twine(MainGroup->Index) + + ", was also found in the group section with index " + + Twine(G.Index)); + 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 LLVMELFDumper<ELFT>::printRelocations() { + ListScope D(W, "Relocations"); + + for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { + if (!isRelocationSec<ELFT>(Sec)) + continue; + + StringRef Name = this->getPrintableSectionName(Sec); + unsigned SecNdx = &Sec - &cantFail(this->Obj.sections()).front(); + W.startLine() << "Section (" << SecNdx << ") " << Name << " {\n"; + W.indent(); + this->printRelocationsHelper(Sec); + W.unindent(); + W.startLine() << "}\n"; + } +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printRelrReloc(const Elf_Relr &R) { + W.startLine() << W.hex(R) << "\n"; +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printRelRelaReloc(const Relocation<ELFT> &R, + const RelSymbol<ELFT> &RelSym) { + StringRef SymbolName = RelSym.Name; + SmallString<32> RelocName; + this->Obj.getRelocationTypeName(R.Type, RelocName); + + if (opts::ExpandRelocs) { + DictScope Group(W, "Relocation"); + W.printHex("Offset", R.Offset); + W.printNumber("Type", RelocName, R.Type); + W.printNumber("Symbol", !SymbolName.empty() ? SymbolName : "-", R.Symbol); + if (R.Addend) + W.printHex("Addend", (uintX_t)*R.Addend); + } else { + raw_ostream &OS = W.startLine(); + OS << W.hex(R.Offset) << " " << RelocName << " " + << (!SymbolName.empty() ? SymbolName : "-"); + if (R.Addend) + OS << " " << W.hex((uintX_t)*R.Addend); + OS << "\n"; + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printSectionHeaders() { + ListScope SectionsD(W, "Sections"); + + int SectionIndex = -1; + std::vector<EnumEntry<unsigned>> FlagsList = + getSectionFlagsForTarget(this->Obj.getHeader().e_machine); + for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { + DictScope SectionD(W, "Section"); + W.printNumber("Index", ++SectionIndex); + W.printNumber("Name", this->getPrintableSectionName(Sec), Sec.sh_name); + W.printHex("Type", + object::getELFSectionTypeName(this->Obj.getHeader().e_machine, + Sec.sh_type), + Sec.sh_type); + W.printFlags("Flags", Sec.sh_flags, makeArrayRef(FlagsList)); + 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"); + this->printRelocationsHelper(Sec); + } + + if (opts::SectionSymbols) { + ListScope D(W, "Symbols"); + if (this->DotSymtabSec) { + StringRef StrTable = unwrapOrError( + this->FileName, + this->Obj.getStringTableForSymtab(*this->DotSymtabSec)); + ArrayRef<Elf_Word> ShndxTable = this->getShndxTable(this->DotSymtabSec); + + typename ELFT::SymRange Symbols = unwrapOrError( + this->FileName, this->Obj.symbols(this->DotSymtabSec)); + for (const Elf_Sym &Sym : Symbols) { + const Elf_Shdr *SymSec = unwrapOrError( + this->FileName, + this->Obj.getSection(Sym, this->DotSymtabSec, ShndxTable)); + if (SymSec == &Sec) + printSymbol(Sym, &Sym - &Symbols[0], ShndxTable, StrTable, false, + false); + } + } + } + + if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) { + ArrayRef<uint8_t> Data = + unwrapOrError(this->FileName, this->Obj.getSectionContents(Sec)); + W.printBinaryBlock( + "SectionData", + StringRef(reinterpret_cast<const char *>(Data.data()), Data.size())); + } + } +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printSymbolSection( + const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable) const { + auto GetSectionSpecialType = [&]() -> Optional<StringRef> { + if (Symbol.isUndefined()) + return StringRef("Undefined"); + if (Symbol.isProcessorSpecific()) + return StringRef("Processor Specific"); + if (Symbol.isOSSpecific()) + return StringRef("Operating System Specific"); + if (Symbol.isAbsolute()) + return StringRef("Absolute"); + if (Symbol.isCommon()) + return StringRef("Common"); + if (Symbol.isReserved() && Symbol.st_shndx != SHN_XINDEX) + return StringRef("Reserved"); + return None; + }; + + if (Optional<StringRef> Type = GetSectionSpecialType()) { + W.printHex("Section", *Type, Symbol.st_shndx); + return; + } + + Expected<unsigned> SectionIndex = + this->getSymbolSectionIndex(Symbol, SymIndex, ShndxTable); + if (!SectionIndex) { + assert(Symbol.st_shndx == SHN_XINDEX && + "getSymbolSectionIndex should only fail due to an invalid " + "SHT_SYMTAB_SHNDX table/reference"); + this->reportUniqueWarning(SectionIndex.takeError()); + W.printHex("Section", "Reserved", SHN_XINDEX); + return; + } + + Expected<StringRef> SectionName = + this->getSymbolSectionName(Symbol, *SectionIndex); + if (!SectionName) { + // Don't report an invalid section name if the section headers are missing. + // In such situations, all sections will be "invalid". + if (!this->ObjF.sections().empty()) + this->reportUniqueWarning(SectionName.takeError()); + else + consumeError(SectionName.takeError()); + W.printHex("Section", "<?>", *SectionIndex); + } else { + W.printHex("Section", *SectionName, *SectionIndex); + } +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printSymbol(const Elf_Sym &Symbol, unsigned SymIndex, + DataRegion<Elf_Word> ShndxTable, + Optional<StringRef> StrTable, + bool IsDynamic, + bool /*NonVisibilityBitsUsed*/) const { + std::string FullSymbolName = this->getFullSymbolName( + Symbol, SymIndex, ShndxTable, 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 (this->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 (this->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)); + } else if (this->Obj.getHeader().e_machine == EM_AARCH64) { + SymOtherFlags.insert(SymOtherFlags.end(), + std::begin(ElfAArch64SymOtherFlags), + std::end(ElfAArch64SymOtherFlags)); + } + W.printFlags("Other", Symbol.st_other, makeArrayRef(SymOtherFlags), 0x3u); + } + printSymbolSection(Symbol, SymIndex, ShndxTable); +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printSymbols(bool PrintSymbols, + bool PrintDynamicSymbols) { + if (PrintSymbols) { + ListScope Group(W, "Symbols"); + this->printSymbolsHelper(false); + } + if (PrintDynamicSymbols) { + ListScope Group(W, "DynamicSymbols"); + this->printSymbolsHelper(true); + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printDynamicTable() { + Elf_Dyn_Range Table = this->dynamic_table(); + if (Table.empty()) + return; + + W.startLine() << "DynamicSection [ (" << Table.size() << " entries)\n"; + + size_t MaxTagSize = getMaxDynamicTagSize(this->Obj, Table); + // The "Name/Value" column should be indented from the "Type" column by N + // spaces, where N = MaxTagSize - length of "Type" (4) + trailing + // space (1) = -3. + W.startLine() << " Tag" << std::string(ELFT::Is64Bits ? 16 : 8, ' ') + << "Type" << std::string(MaxTagSize - 3, ' ') << "Name/Value\n"; + + std::string ValueFmt = "%-" + std::to_string(MaxTagSize) + "s "; + for (auto Entry : Table) { + uintX_t Tag = Entry.getTag(); + std::string Value = this->getDynamicEntry(Tag, Entry.getVal()); + W.startLine() << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10, true) + << " " + << format(ValueFmt.c_str(), + this->Obj.getDynamicTagAsString(Tag).c_str()) + << Value << "\n"; + } + W.startLine() << "]\n"; +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printDynamicRelocations() { + W.startLine() << "Dynamic Relocations {\n"; + W.indent(); + this->printDynamicRelocationsHelper(); + W.unindent(); + W.startLine() << "}\n"; +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printProgramHeaders( + bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(); + if (PrintSectionMapping == cl::BOU_TRUE) + printSectionMapping(); +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printProgramHeaders() { + ListScope L(W, "ProgramHeaders"); + + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = this->Obj.program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning("unable to dump program headers: " + + toString(PhdrsOrErr.takeError())); + return; + } + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + DictScope P(W, "ProgramHeader"); + StringRef Type = + segmentTypeToString(this->Obj.getHeader().e_machine, Phdr.p_type); + + W.printHex("Type", Type.empty() ? "Unknown" : 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 LLVMELFDumper<ELFT>::printVersionSymbolSection(const Elf_Shdr *Sec) { + ListScope SS(W, "VersionSymbols"); + if (!Sec) + return; + + StringRef StrTable; + ArrayRef<Elf_Sym> Syms; + const Elf_Shdr *SymTabSec; + Expected<ArrayRef<Elf_Versym>> VerTableOrErr = + this->getVersionTable(*Sec, &Syms, &StrTable, &SymTabSec); + if (!VerTableOrErr) { + this->reportUniqueWarning(VerTableOrErr.takeError()); + return; + } + + if (StrTable.empty() || Syms.empty() || Syms.size() != VerTableOrErr->size()) + return; + + ArrayRef<Elf_Word> ShNdxTable = this->getShndxTable(SymTabSec); + for (size_t I = 0, E = Syms.size(); I < E; ++I) { + DictScope S(W, "Symbol"); + W.printNumber("Version", (*VerTableOrErr)[I].vs_index & VERSYM_VERSION); + W.printString("Name", + this->getFullSymbolName(Syms[I], I, ShNdxTable, StrTable, + /*IsDynamic=*/true)); + } +} + +static const EnumEntry<unsigned> SymVersionFlags[] = { + {"Base", "BASE", VER_FLG_BASE}, + {"Weak", "WEAK", VER_FLG_WEAK}, + {"Info", "INFO", VER_FLG_INFO}}; + +template <class ELFT> +void LLVMELFDumper<ELFT>::printVersionDefinitionSection(const Elf_Shdr *Sec) { + ListScope SD(W, "VersionDefinitions"); + if (!Sec) + return; + + Expected<std::vector<VerDef>> V = this->Obj.getVersionDefinitions(*Sec); + if (!V) { + this->reportUniqueWarning(V.takeError()); + return; + } + + for (const VerDef &D : *V) { + DictScope Def(W, "Definition"); + W.printNumber("Version", D.Version); + W.printFlags("Flags", D.Flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", D.Ndx); + W.printNumber("Hash", D.Hash); + W.printString("Name", D.Name.c_str()); + W.printList( + "Predecessors", D.AuxV, + [](raw_ostream &OS, const VerdAux &Aux) { OS << Aux.Name.c_str(); }); + } +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printVersionDependencySection(const Elf_Shdr *Sec) { + ListScope SD(W, "VersionRequirements"); + if (!Sec) + return; + + Expected<std::vector<VerNeed>> V = + this->Obj.getVersionDependencies(*Sec, this->WarningHandler); + if (!V) { + this->reportUniqueWarning(V.takeError()); + return; + } + + for (const VerNeed &VN : *V) { + DictScope Entry(W, "Dependency"); + W.printNumber("Version", VN.Version); + W.printNumber("Count", VN.Cnt); + W.printString("FileName", VN.File.c_str()); + + ListScope L(W, "Entries"); + for (const VernAux &Aux : VN.AuxV) { + DictScope Entry(W, "Entry"); + W.printNumber("Hash", Aux.Hash); + W.printFlags("Flags", Aux.Flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Aux.Other); + W.printString("Name", Aux.Name.c_str()); + } + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printHashHistograms() { + W.startLine() << "Hash Histogram not implemented!\n"; +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() { + ListScope L(W, "CGProfile"); + if (!this->DotCGProfileSec) + return; + + Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr = + this->Obj.template getSectionContentsAsArray<Elf_CGProfile>( + *this->DotCGProfileSec); + if (!CGProfileOrErr) { + this->reportUniqueWarning( + "unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileOrErr.takeError())); + return; + } + + for (const Elf_CGProfile &CGPE : *CGProfileOrErr) { + DictScope D(W, "CGProfileEntry"); + W.printNumber("From", this->getStaticSymbolName(CGPE.cgp_from), + CGPE.cgp_from); + W.printNumber("To", this->getStaticSymbolName(CGPE.cgp_to), + CGPE.cgp_to); + W.printNumber("Weight", CGPE.cgp_weight); + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() { + ListScope L(W, "Addrsig"); + if (!this->DotAddrsigSec) + return; + + Expected<std::vector<uint64_t>> SymsOrErr = + decodeAddrsigSection(this->Obj, *this->DotAddrsigSec); + if (!SymsOrErr) { + this->reportUniqueWarning(SymsOrErr.takeError()); + return; + } + + for (uint64_t Sym : *SymsOrErr) + W.printNumber("Sym", this->getStaticSymbolName(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 std::string &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 LLVMELFDumper<ELFT>::printNotes() { + ListScope L(W, "Notes"); + + std::unique_ptr<DictScope> NoteScope; + auto StartNotes = [&](Optional<StringRef> SecName, + const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { + NoteScope = std::make_unique<DictScope>(W, "NoteSection"); + W.printString("Name", SecName ? *SecName : "<?>"); + W.printHex("Offset", Offset); + W.printHex("Size", Size); + }; + + auto EndNotes = [&] { NoteScope.reset(); }; + + auto ProcessNote = [&](const Elf_Note &Note) -> Error { + 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()); + + StringRef NoteType = + getNoteTypeName<ELFT>(Note, this->Obj.getHeader().e_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)); + if (Expected<CoreNote> Note = readCoreNote(DescExtractor)) + printCoreNoteLLVMStyle(*Note, W); + else + return Note.takeError(); + } + } else if (!Descriptor.empty()) { + W.printBinaryBlock("Description data", Descriptor); + } + return Error::success(); + }; + + printNotesHelper(*this, StartNotes, ProcessNote, EndNotes); +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printELFLinkerOptions() { + ListScope L(W, "LinkerOptions"); + + unsigned I = -1; + for (const Elf_Shdr &Shdr : cantFail(this->Obj.sections())) { + ++I; + if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS) + continue; + + Expected<ArrayRef<uint8_t>> ContentsOrErr = + this->Obj.getSectionContents(Shdr); + if (!ContentsOrErr) { + this->reportUniqueWarning("unable to read the content of the " + "SHT_LLVM_LINKER_OPTIONS section: " + + toString(ContentsOrErr.takeError())); + continue; + } + if (ContentsOrErr->empty()) + continue; + + if (ContentsOrErr->back() != 0) { + this->reportUniqueWarning("SHT_LLVM_LINKER_OPTIONS section at index " + + Twine(I) + + " is broken: the " + "content is not null-terminated"); + continue; + } + + SmallVector<StringRef, 16> Strings; + toStringRef(ContentsOrErr->drop_back()).split(Strings, '\0'); + if (Strings.size() % 2 != 0) { + this->reportUniqueWarning( + "SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) + + " is broken: an incomplete " + "key-value pair was found. The last possible key was: \"" + + Strings.back() + "\""); + continue; + } + + for (size_t I = 0; I < Strings.size(); I += 2) + W.printString(Strings[I], Strings[I + 1]); + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printDependentLibs() { + ListScope L(W, "DependentLibs"); + this->printDependentLibsHelper( + [](const Elf_Shdr &) {}, + [this](StringRef Lib, uint64_t) { W.printString(Lib); }); +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printStackSizes() { + ListScope L(W, "StackSizes"); + if (this->Obj.getHeader().e_type == ELF::ET_REL) + this->printRelocatableStackSizes([]() {}); + else + this->printNonRelocatableStackSizes([]() {}); +} + +template <class ELFT> +void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + DictScope D(W, "Entry"); + W.printString("Function", FuncName); + W.printHex("Size", Size); +} + +template <class ELFT> +void LLVMELFDumper<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)); + + const unsigned SymIndex = &Sym - this->dynamic_symbols().begin(); + DataRegion<Elf_Word> ShndxTable( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end()); + printSymbolSection(Sym, SymIndex, ShndxTable); + + std::string SymName = this->getFullSymbolName( + Sym, SymIndex, ShndxTable, this->DynamicStringTable, 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 LLVMELFDumper<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"); + DataRegion<Elf_Word> ShndxTable( + (const Elf_Word *)this->DynSymTabShndxRegion.Addr, this->Obj.end()); + 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)); + printSymbolSection(Sym, &Sym - this->dynamic_symbols().begin(), + ShndxTable); + + const Elf_Sym *FirstSym = cantFail( + this->Obj.template getEntry<Elf_Sym>(*Parser.getPltSymTable(), 0)); + std::string SymName = this->getFullSymbolName( + Sym, &Sym - FirstSym, ShndxTable, Parser.getPltStrTable(), true); + W.printNumber("Name", SymName, Sym.st_name); + } + } +} + +template <class ELFT> void LLVMELFDumper<ELFT>::printMipsABIFlags() { + const Elf_Mips_ABIFlags<ELFT> *Flags; + if (Expected<const Elf_Mips_ABIFlags<ELFT> *> SecOrErr = + getMipsAbiFlagsSection(*this)) { + Flags = *SecOrErr; + if (!Flags) { + W.startLine() << "There is no .MIPS.abiflags section in the file.\n"; + return; + } + } else { + this->reportUniqueWarning(SecOrErr.takeError()); + return; + } + + 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/contrib/libs/llvm12/tools/llvm-readobj/MachODumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/MachODumper.cpp new file mode 100644 index 0000000000..c13b1f3bf2 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/MachODumper.cpp @@ -0,0 +1,890 @@ +//===- 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 "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->getFileName()), 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::unique_ptr<ObjDumper> createMachODumper(const object::MachOObjectFile &Obj, + ScopedPrinter &Writer) { + return std::make_unique<MachODumper>(&Obj, Writer); +} + +} // 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_ARM64_V8), + 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 = ""; + // Don't ask a Mach-O STABS symbol for its section unless we know that + // STAB symbol's section field refers to a valid section index. Otherwise + // the symbol may error trying to load a section that does not exist. + // TODO: Add a whitelist of STABS symbol types that contain valid section + // indices. + if (!(MOSymbol.Type & MachO::N_STAB)) { + 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/contrib/libs/llvm12/tools/llvm-readobj/ObjDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/ObjDumper.cpp new file mode 100644 index 0000000000..fc91d81f07 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/ObjDumper.cpp @@ -0,0 +1,186 @@ +//===-- 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 "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, StringRef ObjName) : W(Writer) { + // Dumper reports all non-critical errors as warnings. + // It does not print the same warning more than once. + WarningHandler = [=](const Twine &Msg) { + if (Warnings.insert(Msg.str()).second) + reportWarning(createError(Msg), ObjName); + return Error::success(); + }; +} + +ObjDumper::~ObjDumper() {} + +void ObjDumper::reportUniqueWarning(Error Err) const { + reportUniqueWarning(toString(std::move(Err))); +} + +void ObjDumper::reportUniqueWarning(const Twine &Msg) const { + cantFail(WarningHandler(Msg), + "WarningHandler should always return ErrorSuccess"); +} + +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(std::string(Section), false); + } + + SecIndex = Obj.isELF() ? 0 : 1; + for (object::SectionRef SecRef : Obj.sections()) { + StringRef SecName = unwrapOrError(Obj.getFileName(), SecRef.getName()); + auto NameIt = SecNames.find(std::string(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<const 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), ' '); + if (k < 4) + W.startLine() << format("%*c", 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/contrib/libs/llvm12/tools/llvm-readobj/ObjDumper.h b/contrib/libs/llvm12/tools/llvm-readobj/ObjDumper.h new file mode 100644 index 0000000000..d4e166b504 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/ObjDumper.h @@ -0,0 +1,154 @@ +//===-- 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" + +#include <unordered_set> + +namespace llvm { +namespace object { +class COFFImportFile; +class ObjectFile; +class XCOFFObjectFile; +class ELFObjectFileBase; +} +namespace codeview { +class GlobalTypeTableBuilder; +class MergingTypeTableBuilder; +} // namespace codeview + +class ScopedPrinter; + +class ObjDumper { +public: + ObjDumper(ScopedPrinter &Writer, StringRef ObjName); + virtual ~ObjDumper(); + + virtual bool canDumpContent() { return true; } + + 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 printDependentLibs() {} + 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 printHashHistograms() {} + virtual void printCGProfile() {} + virtual void printAddrsig() {} + virtual void printNotes() {} + virtual void printELFLinkerOptions() {} + virtual void printStackSizes() {} + virtual void printSectionDetails() {} + 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 printCOFFTLSDirectory() {} + 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); + + std::function<Error(const Twine &Msg)> WarningHandler; + void reportUniqueWarning(Error Err) const; + void reportUniqueWarning(const Twine &Msg) const; + +protected: + ScopedPrinter &W; + +private: + virtual void printSymbols() {} + virtual void printDynamicSymbols() {} + virtual void printProgramHeaders() {} + virtual void printSectionMapping() {} + + std::unordered_set<std::string> Warnings; +}; + +std::unique_ptr<ObjDumper> createCOFFDumper(const object::COFFObjectFile &Obj, + ScopedPrinter &Writer); + +std::unique_ptr<ObjDumper> createELFDumper(const object::ELFObjectFileBase &Obj, + ScopedPrinter &Writer); + +std::unique_ptr<ObjDumper> createMachODumper(const object::MachOObjectFile &Obj, + ScopedPrinter &Writer); + +std::unique_ptr<ObjDumper> createWasmDumper(const object::WasmObjectFile &Obj, + ScopedPrinter &Writer); + +std::unique_ptr<ObjDumper> createXCOFFDumper(const object::XCOFFObjectFile &Obj, + ScopedPrinter &Writer); + +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/contrib/libs/llvm12/tools/llvm-readobj/StackMapPrinter.h b/contrib/libs/llvm12/tools/llvm-readobj/StackMapPrinter.h new file mode 100644 index 0000000000..ef75756402 --- /dev/null +++ b/contrib/libs/llvm12/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/contrib/libs/llvm12/tools/llvm-readobj/WasmDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/WasmDumper.cpp new file mode 100644 index 0000000000..fb7134d20a --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/WasmDumper.cpp @@ -0,0 +1,248 @@ +//===-- 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 "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), ENUM_ENTRY(TABLE), +#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->getFileName()), 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 = wasm::relocTypeHasAddend(static_cast<uint32_t>(RelocType)); + + 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); + else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST) + W.printNumber("Offset", Seg.Offset.Value.Int64); + else + llvm_unreachable("unknown init expr opcode"); + } + 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) { + if (Symbol.Info.ImportName) { + W.printString("ImportName", *Symbol.Info.ImportName); + } + if (Symbol.Info.ImportModule) { + 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::unique_ptr<ObjDumper> createWasmDumper(const object::WasmObjectFile &Obj, + ScopedPrinter &Writer) { + return std::make_unique<WasmDumper>(&Obj, Writer); +} + +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-readobj/Win64EHDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/Win64EHDumper.cpp new file mode 100644 index 0000000000..7e84c1bca3 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/Win64EHDumper.cpp @@ -0,0 +1,387 @@ +//===- 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::error_code getSymbol(const COFFObjectFile &COFF, uint64_t VA, + object::SymbolRef &Sym) { + for (const auto &Symbol : COFF.symbols()) { + Expected<uint64_t> Address = Symbol.getAddress(); + if (!Address) + return errorToErrorCode(Address.takeError()); + if (*Address == VA) { + Sym = Symbol; + return std::error_code(); + } + } + return inconvertibleErrorCode(); +} + +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()); + } + } else if (!getSymbol(Ctx.COFF, Ctx.COFF.getImageBase() + Displacement, + Symbol)) { + Expected<StringRef> Name = Symbol.getName(); + if (Name) { + OS << *Name; + OS << format(" (0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement); + return OS.str(); + } else { + consumeError(Name.takeError()); + } + } + + if (Displacement > 0) + OS << format("(0x%" PRIX64 ")", Ctx.COFF.getImageBase() + Displacement); + else + 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(); +} + +static const object::coff_section * +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 COFF.getCOFFSection(Section); + } + return nullptr; +} + +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 = nullptr; + uint64_t Offset; + resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset); + Offset = Offset + RF.UnwindInfoOffset; + + if (!XData) { + uint64_t Address = Ctx.COFF.getImageBase() + RF.UnwindInfoOffset; + XData = getSectionContaining(Ctx.COFF, Address); + if (!XData) + return; + Offset = RF.UnwindInfoOffset - XData->VirtualAddress; + } + + ArrayRef<uint8_t> Contents; + if (Error E = Ctx.COFF.getSectionContents(XData, Contents)) + reportError(std::move(E), Ctx.COFF.getFileName()); + + if (Contents.empty()) + return; + + 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/contrib/libs/llvm12/tools/llvm-readobj/Win64EHDumper.h b/contrib/libs/llvm12/tools/llvm-readobj/Win64EHDumper.h new file mode 100644 index 0000000000..97458c916b --- /dev/null +++ b/contrib/libs/llvm12/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/contrib/libs/llvm12/tools/llvm-readobj/WindowsResourceDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/WindowsResourceDumper.cpp new file mode 100644 index 0000000000..fb085ecaa7 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/WindowsResourceDumper.cpp @@ -0,0 +1,84 @@ +//===-- 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 "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/contrib/libs/llvm12/tools/llvm-readobj/WindowsResourceDumper.h b/contrib/libs/llvm12/tools/llvm-readobj/WindowsResourceDumper.h new file mode 100644 index 0000000000..6a5878804e --- /dev/null +++ b/contrib/libs/llvm12/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/contrib/libs/llvm12/tools/llvm-readobj/XCOFFDumper.cpp b/contrib/libs/llvm12/tools/llvm-readobj/XCOFFDumper.cpp new file mode 100644 index 0000000000..8f0f18cedc --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/XCOFFDumper.cpp @@ -0,0 +1,521 @@ +//===-- 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 "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 { + +public: + XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) + : ObjDumper(Writer, Obj.getFileName()), 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 &); + 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->isLabel()) + 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->getAlignmentLog2()); + W.printEnum("SymbolType", AuxEntPtr->getSymbolType(), + 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.getSectionType(); + 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; + } + if (Sec.isReservedSectionType()) + 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::unique_ptr<ObjDumper> +createXCOFFDumper(const object::XCOFFObjectFile &XObj, ScopedPrinter &Writer) { + return std::make_unique<XCOFFDumper>(XObj, Writer); +} +} // namespace llvm diff --git a/contrib/libs/llvm12/tools/llvm-readobj/llvm-readobj.cpp b/contrib/libs/llvm12/tools/llvm-readobj/llvm-readobj.cpp new file mode 100644 index 0000000000..41cd4414d0 --- /dev/null +++ b/contrib/libs/llvm12/tools/llvm-readobj/llvm-readobj.cpp @@ -0,0 +1,780 @@ +//===- 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 "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/ELFObjectFile.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Object/WindowsResource.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.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)); + + // --dependent-libraries + cl::opt<bool> + DependentLibraries("dependent-libraries", + cl::desc("Display the dependent libraries section")); + + // --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")); + + // --section-details + // Also -t in llvm-readelf mode. + cl::opt<bool> SectionDetails("section-details", + cl::desc("Display the section details")); + + // --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::value_desc("number|name"), + cl::desc("Display the specified section(s) as a list of strings"), + 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::value_desc("number|name"), + cl::desc("Display the specified section(s) as hexadecimal bytes"), + 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-tls-directory + cl::opt<bool> COFFTLSDirectory("coff-tls-directory", + cl::desc("Display the PE/COFF TLS 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)); + + // --cg-profile + cl::opt<bool> CGProfile("cg-profile", + cl::desc("Display callgraph profile section")); + cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"), + cl::aliasopt(CGProfile)); + + // -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(); + 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) { + 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 Expected<std::unique_ptr<ObjDumper>> +createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) { + if (const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(&Obj)) + return createCOFFDumper(*COFFObj, Writer); + + if (const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) + return createELFDumper(*ELFObj, Writer); + + if (const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(&Obj)) + return createMachODumper(*MachOObj, Writer); + + if (const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(&Obj)) + return createWasmDumper(*WasmObj, Writer); + + if (const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(&Obj)) + return createXCOFFDumper(*XObj, Writer); + + return createStringError(errc::invalid_argument, + "unsupported object file format"); +} + +/// Dumps the specified object file. +static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, + const Archive *A = nullptr) { + std::string FileStr = + A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")").str() + : Obj.getFileName().str(); + + std::string ContentErrString; + if (Error ContentErr = Obj.initContent()) + ContentErrString = "unable to continue dumping, the file is corrupt: " + + toString(std::move(ContentErr)); + + ObjDumper *Dumper; + Expected<std::unique_ptr<ObjDumper>> DumperOrErr = createDumper(Obj, Writer); + if (!DumperOrErr) + reportError(DumperOrErr.takeError(), FileStr); + Dumper = (*DumperOrErr).get(); + + 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(Obj.getArch())); + Writer.printString( + "AddressSize", + std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress()))); + Dumper->printLoadName(); + } + + if (opts::FileHeaders) + Dumper->printFileHeaders(); + + // This is only used for ELF currently. In some cases, when an object is + // corrupt (e.g. truncated), we can't dump anything except the file header. + if (!ContentErrString.empty()) + reportError(createError(ContentErrString), FileStr); + + if (opts::SectionDetails || opts::SectionHeaders) { + if (opts::Output == opts::GNU && opts::SectionDetails) + Dumper->printSectionDetails(); + else + Dumper->printSectionHeaders(); + } + + if (opts::HashSymbols) + Dumper->printHashSymbols(); + if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) + Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping); + if (opts::DynamicTable) + Dumper->printDynamicTable(); + if (opts::NeededLibraries) + Dumper->printNeededLibraries(); + if (opts::Relocations) + Dumper->printRelocations(); + if (opts::DynRelocs) + Dumper->printDynamicRelocations(); + if (opts::UnwindInfo) + Dumper->printUnwindInfo(); + if (opts::Symbols || opts::DynamicSymbols) + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); + 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::DependentLibraries) + Dumper->printDependentLibs(); + if (opts::ELFLinkerOptions) + Dumper->printELFLinkerOptions(); + if (opts::ArchSpecificInfo) + Dumper->printArchSpecificInfo(); + if (opts::SectionGroups) + Dumper->printGroupSections(); + if (opts::HashHistogram) + Dumper->printHashHistograms(); + 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::COFFTLSDirectory) + Dumper->printCOFFTLSDirectory(); + if (opts::COFFResources) + Dumper->printCOFFResources(); + if (opts::COFFLoadConfig) + Dumper->printCOFFLoadConfig(); + if (opts::CGProfile) + Dumper->printCGProfile(); + 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; + } + + Binary *Bin = ChildOrErr->get(); + if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin)) + dumpObject(*Obj, Writer, Arc); + else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(Bin)) + dumpCOFFImportFile(Imp, Writer); + else + reportWarning(createStringError(errc::invalid_argument, + Bin->getFileName() + + " has an unsupported file type"), + 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, /*Context=*/nullptr, /*InitContent=*/false); + 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 + llvm_unreachable("unrecognized file type"); + + 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); + + // llvm-readelf reserves it for --section-details. + 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); + + // -t is here because for readobj it is an alias for --symbols. + static cl::alias SectionDetailsShort( + "t", cl::desc("Alias for --section-details"), + cl::aliasopt(opts::SectionDetails), cl::NotHidden); + + // 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"); + + // Default to print error if no filename is specified. + if (opts::InputFilenames.empty()) { + error("no input files specified"); + } + + 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; + } + + 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/contrib/libs/llvm12/tools/llvm-readobj/llvm-readobj.h b/contrib/libs/llvm12/tools/llvm-readobj/llvm-readobj.h new file mode 100644 index 0000000000..d9813f5dea --- /dev/null +++ b/contrib/libs/llvm12/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 |