diff options
Diffstat (limited to 'tools/llvm-readobj')
-rw-r--r-- | tools/llvm-readobj/ARMWinEHPrinter.cpp | 20 | ||||
-rw-r--r-- | tools/llvm-readobj/COFFDumper.cpp | 29 | ||||
-rw-r--r-- | tools/llvm-readobj/ELFDumper.cpp | 250 | ||||
-rw-r--r-- | tools/llvm-readobj/ObjDumper.h | 1 | ||||
-rw-r--r-- | tools/llvm-readobj/StreamWriter.h | 4 | ||||
-rw-r--r-- | tools/llvm-readobj/Win64EHDumper.cpp | 4 | ||||
-rw-r--r-- | tools/llvm-readobj/llvm-readobj.cpp | 10 |
7 files changed, 228 insertions, 90 deletions
diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp index a1ea79f3688ea..bf5ff8e1d0310 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -201,10 +201,10 @@ ErrorOr<object::SymbolRef> Decoder::getSymbol(const COFFObjectFile &COFF, if (FunctionOnly && Symbol.getType() != SymbolRef::ST_Function) continue; - uint64_t Address; - if (std::error_code EC = Symbol.getAddress(Address)) + ErrorOr<uint64_t> Address = Symbol.getAddress(); + if (std::error_code EC = Address.getError()) return EC; - if (Address == VA) + if (*Address == VA) return Symbol; } return readobj_error::unknown_symbol; @@ -605,7 +605,10 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, if (std::error_code EC = FunctionNameOrErr.getError()) report_fatal_error(EC.message()); FunctionName = *FunctionNameOrErr; - Function->getAddress(FunctionAddress); + ErrorOr<uint64_t> FunctionAddressOrErr = Function->getAddress(); + if (std::error_code EC = FunctionAddressOrErr.getError()) + report_fatal_error(EC.message()); + FunctionAddress = *FunctionAddressOrErr; } else { const pe32_header *PEHeader; if (COFF.getPE32Header(PEHeader)) @@ -620,8 +623,10 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, if (std::error_code EC = Name.getError()) report_fatal_error(EC.message()); - uint64_t Address; - XDataRecord->getAddress(Address); + ErrorOr<uint64_t> AddressOrErr = XDataRecord->getAddress(); + if (std::error_code EC = AddressOrErr.getError()) + report_fatal_error(EC.message()); + uint64_t Address = *AddressOrErr; SW.printString("ExceptionRecord", formatSymbol(*Name, Address)); @@ -666,7 +671,8 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, if (std::error_code EC = FunctionNameOrErr.getError()) report_fatal_error(EC.message()); FunctionName = *FunctionNameOrErr; - Function->getAddress(FunctionAddress); + ErrorOr<uint64_t> FunctionAddressOrErr = Function->getAddress(); + FunctionAddress = *FunctionAddressOrErr; } else { const pe32_header *PEHeader; if (COFF.getPE32Header(PEHeader)) diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index f5effe2924419..cf897d7cb4840 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -48,7 +48,6 @@ public: COFFDumper(const llvm::object::COFFObjectFile *Obj, StreamWriter& Writer) : ObjDumper(Writer) , Obj(Obj) { - cacheRelocations(); } void printFileHeaders() override; @@ -92,6 +91,7 @@ private: typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy; const llvm::object::COFFObjectFile *Obj; + bool RelocCached = false; RelocMapTy RelocMap; StringRef CVFileIndexToStringOffsetTable; StringRef CVStringTable; @@ -119,6 +119,7 @@ std::error_code createCOFFDumper(const object::ObjectFile *Obj, // 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]; for (const auto &Relocation : Relocations) { uint64_t RelocationOffset = Relocation.getOffset(); @@ -339,6 +340,10 @@ static std::error_code getSymbolAuxData(const COFFObjectFile *Obj, } void COFFDumper::cacheRelocations() { + if (RelocCached) + return; + RelocCached = true; + for (const SectionRef &S : Obj->sections()) { const coff_section *Section = Obj->getCOFFSection(S); @@ -580,7 +585,11 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { W.printString("FunctionName", Name); DataExtractor DE(FunctionLineTables[Name], true, 4); - uint32_t Offset = 8; // Skip relocations. + uint32_t Offset = 6; // Skip relocations. + uint16_t Flags = DE.getU16(&Offset); + W.printHex("Flags", Flags); + bool HasColumnInformation = + Flags & COFF::DEBUG_LINE_TABLES_HAVE_COLUMN_RECORDS; uint32_t FunctionSize = DE.getU32(&Offset); W.printHex("CodeSize", FunctionSize); while (DE.isValidOffset(Offset)) { @@ -588,9 +597,12 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { // in the line table. The filename string is accessed using double // indirection to the string table subsection using the index subsection. uint32_t OffsetInIndex = DE.getU32(&Offset), - SegmentLength = DE.getU32(&Offset), + SegmentLength = DE.getU32(&Offset), FullSegmentSize = DE.getU32(&Offset); - if (FullSegmentSize != 12 + 8 * SegmentLength) { + + if (FullSegmentSize != + 12 + 8 * SegmentLength + + (HasColumnInformation ? 4 * SegmentLength : 0)) { error(object_error::parse_failed); return; } @@ -631,6 +643,15 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { format("+0x%X", PC).snprint(Buffer, 32); W.printNumber(Buffer, LineNumber); } + if (HasColumnInformation) { + for (unsigned J = 0; J != SegmentLength && DE.isValidOffset(Offset); + ++J) { + uint16_t ColStart = DE.getU16(&Offset); + W.printNumber("ColStart", ColStart); + uint16_t ColEnd = DE.getU16(&Offset); + W.printNumber("ColEnd", ColEnd); + } + } } } } diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index a4b25efeb9ba1..1cdf5529c0809 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -56,6 +56,7 @@ public: void printDynamicTable() override; void printNeededLibraries() override; void printProgramHeaders() override; + void printHashTable() override; void printAttributes() override; void printMipsPLTGOT() override; @@ -1119,6 +1120,18 @@ void ELFDumper<ELFT>::printProgramHeaders() { } } +template <typename ELFT> +void ELFDumper<ELFT>::printHashTable() { + DictScope D(W, "HashTable"); + auto HT = Obj->getHashTable(); + if (!HT) + return; + W.printNumber("Num Buckets", HT->nbucket); + W.printNumber("Num Chains", HT->nchain); + W.printList("Buckets", HT->buckets()); + W.printList("Chains", HT->chains()); +} + template <class ELFT> void ELFDumper<ELFT>::printAttributes() { W.startLine() << "Attributes not implemented.\n"; @@ -1162,9 +1175,10 @@ public: typedef typename ObjectFile::Elf_Shdr Elf_Shdr; typedef typename ObjectFile::Elf_Sym Elf_Sym; - MipsGOTParser(const ObjectFile *Obj, StreamWriter &W) : Obj(Obj), W(W) {} + MipsGOTParser(const ObjectFile *Obj, StreamWriter &W); - void parseGOT(const Elf_Shdr &GOTShdr); + void parseGOT(); + void parsePLT(); private: typedef typename ObjectFile::Elf_Addr GOTEntry; @@ -1173,35 +1187,79 @@ private: const ObjectFile *Obj; StreamWriter &W; + llvm::Optional<uint64_t> DtPltGot; + llvm::Optional<uint64_t> DtLocalGotNum; + llvm::Optional<uint64_t> DtGotSym; + llvm::Optional<uint64_t> DtMipsPltGot; + llvm::Optional<uint64_t> DtJmpRel; std::size_t getGOTTotal(ArrayRef<uint8_t> GOT) const; GOTIter makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum); - bool getGOTTags(uint64_t &LocalGotNum, uint64_t &GotSym); void printGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It); void printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It, const Elf_Sym *Sym, bool IsDynamic); + void printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, GOTIter It, + StringRef Purpose); + void printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, GOTIter It, + const Elf_Sym *Sym); }; } template <class ELFT> -void MipsGOTParser<ELFT>::parseGOT(const Elf_Shdr &GOTShdr) { +MipsGOTParser<ELFT>::MipsGOTParser(const ObjectFile *Obj, StreamWriter &W) + : Obj(Obj), W(W) { + for (const auto &Entry : Obj->dynamic_table()) { + switch (Entry.getTag()) { + case ELF::DT_PLTGOT: + DtPltGot = Entry.getVal(); + break; + case ELF::DT_MIPS_LOCAL_GOTNO: + DtLocalGotNum = Entry.getVal(); + break; + case ELF::DT_MIPS_GOTSYM: + DtGotSym = Entry.getVal(); + break; + case ELF::DT_MIPS_PLTGOT: + DtMipsPltGot = Entry.getVal(); + break; + case ELF::DT_JMPREL: + DtJmpRel = Entry.getVal(); + break; + } + } +} + +template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() { // 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 + if (!DtPltGot) { + W.startLine() << "Cannot find PLTGOT dynamic table tag.\n"; + return; + } + if (!DtLocalGotNum) { + W.startLine() << "Cannot find MIPS_LOCAL_GOTNO dynamic table tag.\n"; + return; + } + if (!DtGotSym) { + W.startLine() << "Cannot find MIPS_GOTSYM dynamic table tag.\n"; + return; + } - ErrorOr<ArrayRef<uint8_t>> GOT = Obj->getSectionContents(&GOTShdr); - if (!GOT) { - W.startLine() << "The .got section is empty.\n"; + const Elf_Shdr *GOTShdr = findSectionByAddress(Obj, *DtPltGot); + if (!GOTShdr) { + W.startLine() << "There is no .got section in the file.\n"; return; } - uint64_t DtLocalGotNum; - uint64_t DtGotSym; - if (!getGOTTags(DtLocalGotNum, DtGotSym)) + ErrorOr<ArrayRef<uint8_t>> GOT = Obj->getSectionContents(GOTShdr); + if (!GOT) { + W.startLine() << "The .got section is empty.\n"; return; + } - if (DtLocalGotNum > getGOTTotal(*GOT)) { + if (*DtLocalGotNum > getGOTTotal(*GOT)) { W.startLine() << "MIPS_LOCAL_GOTNO exceeds a number of GOT entries.\n"; return; } @@ -1210,37 +1268,37 @@ void MipsGOTParser<ELFT>::parseGOT(const Elf_Shdr &GOTShdr) { const Elf_Sym *DynSymEnd = Obj->dynamic_symbol_end(); std::size_t DynSymTotal = std::size_t(std::distance(DynSymBegin, DynSymEnd)); - if (DtGotSym > DynSymTotal) { + if (*DtGotSym > DynSymTotal) { W.startLine() << "MIPS_GOTSYM exceeds a number of dynamic symbols.\n"; return; } - std::size_t GlobalGotNum = DynSymTotal - DtGotSym; + std::size_t GlobalGotNum = DynSymTotal - *DtGotSym; - if (DtLocalGotNum + GlobalGotNum > getGOTTotal(*GOT)) { + if (*DtLocalGotNum + GlobalGotNum > getGOTTotal(*GOT)) { W.startLine() << "Number of global GOT entries exceeds the size of GOT.\n"; return; } GOTIter GotBegin = makeGOTIter(*GOT, 0); - GOTIter GotLocalEnd = makeGOTIter(*GOT, DtLocalGotNum); + GOTIter GotLocalEnd = makeGOTIter(*GOT, *DtLocalGotNum); GOTIter It = GotBegin; DictScope GS(W, "Primary GOT"); - W.printHex("Canonical gp value", GOTShdr.sh_addr + 0x7ff0); + W.printHex("Canonical gp value", GOTShdr->sh_addr + 0x7ff0); { ListScope RS(W, "Reserved entries"); { DictScope D(W, "Entry"); - printGotEntry(GOTShdr.sh_addr, GotBegin, It++); + printGotEntry(GOTShdr->sh_addr, GotBegin, It++); W.printString("Purpose", StringRef("Lazy resolver")); } if (It != GotLocalEnd && (*It >> (sizeof(GOTEntry) * 8 - 1)) != 0) { DictScope D(W, "Entry"); - printGotEntry(GOTShdr.sh_addr, GotBegin, It++); + printGotEntry(GOTShdr->sh_addr, GotBegin, It++); W.printString("Purpose", StringRef("Module pointer (GNU extension)")); } } @@ -1248,24 +1306,88 @@ void MipsGOTParser<ELFT>::parseGOT(const Elf_Shdr &GOTShdr) { ListScope LS(W, "Local entries"); for (; It != GotLocalEnd; ++It) { DictScope D(W, "Entry"); - printGotEntry(GOTShdr.sh_addr, GotBegin, It); + printGotEntry(GOTShdr->sh_addr, GotBegin, It); } } { ListScope GS(W, "Global entries"); - GOTIter GotGlobalEnd = makeGOTIter(*GOT, DtLocalGotNum + GlobalGotNum); - const Elf_Sym *GotDynSym = DynSymBegin + DtGotSym; + GOTIter GotGlobalEnd = makeGOTIter(*GOT, *DtLocalGotNum + GlobalGotNum); + const Elf_Sym *GotDynSym = DynSymBegin + *DtGotSym; for (; It != GotGlobalEnd; ++It) { DictScope D(W, "Entry"); - printGlobalGotEntry(GOTShdr.sh_addr, GotBegin, It, GotDynSym++, true); + printGlobalGotEntry(GOTShdr->sh_addr, GotBegin, It, GotDynSym++, true); } } - std::size_t SpecGotNum = getGOTTotal(*GOT) - DtLocalGotNum - GlobalGotNum; + std::size_t SpecGotNum = getGOTTotal(*GOT) - *DtLocalGotNum - GlobalGotNum; W.printNumber("Number of TLS and multi-GOT entries", uint64_t(SpecGotNum)); } +template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() { + if (!DtMipsPltGot) { + W.startLine() << "Cannot find MIPS_PLTGOT dynamic table tag.\n"; + return; + } + if (!DtJmpRel) { + W.startLine() << "Cannot find JMPREL dynamic table tag.\n"; + return; + } + + const Elf_Shdr *PLTShdr = findSectionByAddress(Obj, *DtMipsPltGot); + if (!PLTShdr) { + W.startLine() << "There is no .got.plt section in the file.\n"; + return; + } + ErrorOr<ArrayRef<uint8_t>> PLT = Obj->getSectionContents(PLTShdr); + if (!PLT) { + W.startLine() << "The .got.plt section is empty.\n"; + return; + } + + const Elf_Shdr *PLTRelShdr = findSectionByAddress(Obj, *DtJmpRel); + if (!PLTShdr) { + W.startLine() << "There is no .rel.plt section in the file.\n"; + return; + } + + GOTIter PLTBegin = makeGOTIter(*PLT, 0); + GOTIter PLTEnd = makeGOTIter(*PLT, getGOTTotal(*PLT)); + GOTIter It = PLTBegin; + + DictScope GS(W, "PLT GOT"); + { + ListScope RS(W, "Reserved entries"); + printPLTEntry(PLTShdr->sh_addr, PLTBegin, It++, "PLT lazy resolver"); + if (It != PLTEnd) + printPLTEntry(PLTShdr->sh_addr, PLTBegin, It++, "Module pointer"); + } + { + ListScope GS(W, "Entries"); + + switch (PLTRelShdr->sh_type) { + case ELF::SHT_REL: + for (typename ObjectFile::Elf_Rel_Iter RI = Obj->rel_begin(PLTRelShdr), + RE = Obj->rel_end(PLTRelShdr); + RI != RE && It != PLTEnd; ++RI, ++It) { + const Elf_Sym *Sym = + Obj->getRelocationSymbol(&*PLTRelShdr, &*RI).second; + printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, Sym); + } + break; + case ELF::SHT_RELA: + for (typename ObjectFile::Elf_Rela_Iter RI = Obj->rela_begin(PLTRelShdr), + RE = Obj->rela_end(PLTRelShdr); + RI != RE && It != PLTEnd; ++RI, ++It) { + const Elf_Sym *Sym = + Obj->getRelocationSymbol(&*PLTRelShdr, &*RI).second; + printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, Sym); + } + break; + } + } +} + template <class ELFT> std::size_t MipsGOTParser<ELFT>::getGOTTotal(ArrayRef<uint8_t> GOT) const { return GOT.size() / sizeof(GOTEntry); @@ -1279,36 +1401,6 @@ MipsGOTParser<ELFT>::makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum) { } template <class ELFT> -bool MipsGOTParser<ELFT>::getGOTTags(uint64_t &LocalGotNum, uint64_t &GotSym) { - bool FoundLocalGotNum = false; - bool FoundGotSym = false; - for (const auto &Entry : Obj->dynamic_table()) { - switch (Entry.getTag()) { - case ELF::DT_MIPS_LOCAL_GOTNO: - LocalGotNum = Entry.getVal(); - FoundLocalGotNum = true; - break; - case ELF::DT_MIPS_GOTSYM: - GotSym = Entry.getVal(); - FoundGotSym = true; - break; - } - } - - if (!FoundLocalGotNum) { - W.startLine() << "Cannot find MIPS_LOCAL_GOTNO dynamic table tag.\n"; - return false; - } - - if (!FoundGotSym) { - W.startLine() << "Cannot find MIPS_GOTSYM dynamic table tag.\n"; - return false; - } - - return true; -} - -template <class ELFT> void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It) { int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); @@ -1335,32 +1427,44 @@ void MipsGOTParser<ELFT>::printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, W.printNumber("Name", FullSymbolName, Sym->st_name); } -template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() { - if (Obj->getHeader()->e_machine != EM_MIPS) { - W.startLine() << "MIPS PLT GOT is available for MIPS targets only.\n"; - return; - } +template <class ELFT> +void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, + GOTIter It, StringRef Purpose) { + DictScope D(W, "Entry"); + int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); + W.printHex("Address", PLTAddr + Offset); + W.printHex("Initial", *It); + W.printString("Purpose", Purpose); +} - llvm::Optional<uint64_t> DtPltGot; - for (const auto &Entry : Obj->dynamic_table()) { - if (Entry.getTag() == ELF::DT_PLTGOT) { - DtPltGot = Entry.getVal(); - break; - } - } +template <class ELFT> +void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, + GOTIter It, const Elf_Sym *Sym) { + DictScope D(W, "Entry"); + int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); + W.printHex("Address", PLTAddr + Offset); + W.printHex("Initial", *It); + W.printHex("Value", Sym->st_value); + W.printEnum("Type", Sym->getType(), makeArrayRef(ElfSymbolTypes)); - if (!DtPltGot) { - W.startLine() << "Cannot find PLTGOT dynamic table tag.\n"; - return; - } + unsigned SectionIndex = 0; + StringRef SectionName; + getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex); + W.printHex("Section", SectionName, SectionIndex); - const Elf_Shdr *GotShdr = findSectionByAddress(Obj, *DtPltGot); - if (!GotShdr) { - W.startLine() << "There is no .got section in the file.\n"; + std::string FullSymbolName = getFullSymbolName(*Obj, Sym, true); + W.printNumber("Name", FullSymbolName, Sym->st_name); +} + +template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() { + if (Obj->getHeader()->e_machine != EM_MIPS) { + W.startLine() << "MIPS PLT GOT is available for MIPS targets only.\n"; return; } - MipsGOTParser<ELFT>(Obj, W).parseGOT(*GotShdr); + MipsGOTParser<ELFT> GOTParser(Obj, W); + GOTParser.parseGOT(); + GOTParser.parsePLT(); } static const EnumEntry<unsigned> ElfMipsISAExtType[] = { diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 27e15b256cc57..5ecf0ec3d6fa0 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -37,6 +37,7 @@ public: virtual void printDynamicTable() { } virtual void printNeededLibraries() { } virtual void printProgramHeaders() { } + virtual void printHashTable() { } // Only implemented for ARM ELF at this time. virtual void printAttributes() { } diff --git a/tools/llvm-readobj/StreamWriter.h b/tools/llvm-readobj/StreamWriter.h index 245588ba06006..f3cc57ef940e7 100644 --- a/tools/llvm-readobj/StreamWriter.h +++ b/tools/llvm-readobj/StreamWriter.h @@ -181,8 +181,8 @@ public: startLine() << Label << ": " << (Value ? "Yes" : "No") << '\n'; } - template <typename T_> - void printList(StringRef Label, const SmallVectorImpl<T_> &List) { + template <typename T> + void printList(StringRef Label, const T &List) { startLine() << Label << ": ["; bool Comma = false; for (const auto &Item : List) { diff --git a/tools/llvm-readobj/Win64EHDumper.cpp b/tools/llvm-readobj/Win64EHDumper.cpp index 5a8af4135bd75..f57eea20e2d9c 100644 --- a/tools/llvm-readobj/Win64EHDumper.cpp +++ b/tools/llvm-readobj/Win64EHDumper.cpp @@ -144,8 +144,10 @@ static std::error_code resolveRelocation(const Dumper::Context &Ctx, Ctx.ResolveSymbol(Section, Offset, Symbol, Ctx.UserData)) return EC; - if (std::error_code EC = Symbol.getAddress(ResolvedAddress)) + ErrorOr<uint64_t> ResolvedAddressOrErr = Symbol.getAddress(); + if (std::error_code EC = ResolvedAddressOrErr.getError()) return EC; + ResolvedAddress = *ResolvedAddressOrErr; section_iterator SI = Ctx.COFF.section_begin(); if (std::error_code EC = Symbol.getSection(SI)) diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index c5bccf9796094..12afacb0a858e 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -127,6 +127,10 @@ namespace opts { cl::opt<bool> ProgramHeaders("program-headers", cl::desc("Display ELF program headers")); + // -hash-table + cl::opt<bool> HashTable("hash-table", + cl::desc("Display ELF hash table")); + // -expand-relocs cl::opt<bool> ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); @@ -199,9 +203,7 @@ bool error(std::error_code EC) { } bool relocAddressLess(RelocationRef a, RelocationRef b) { - uint64_t a_addr = a.getOffset(); - uint64_t b_addr = b.getOffset(); - return a_addr < b_addr; + return a.getOffset() < b.getOffset(); } } // namespace llvm @@ -302,6 +304,8 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printNeededLibraries(); if (opts::ProgramHeaders) Dumper->printProgramHeaders(); + if (opts::HashTable) + Dumper->printHashTable(); if (Obj->getArch() == llvm::Triple::arm && Obj->isELF()) if (opts::ARMAttributes) Dumper->printAttributes(); |