Index: test/tools/llvm-readobj/mips-got.test =================================================================== --- /dev/null +++ test/tools/llvm-readobj/mips-got.test @@ -0,0 +1,306 @@ +RUN: llvm-readobj -mips-plt-got %p/Inputs/relocs.obj.elf-mips | \ +RUN: FileCheck %s -check-prefix GOT-OBJ +RUN: llvm-readobj -mips-plt-got %p/Inputs/dynamic-table-exe.mips | \ +RUN: FileCheck %s -check-prefix GOT-EXE +RUN: llvm-readobj -mips-plt-got %p/Inputs/dynamic-table-so.mips | \ +RUN: FileCheck %s -check-prefix GOT-SO +RUN: llvm-readobj -mips-plt-got %p/Inputs/got-tls.so.elf-mips64el | \ +RUN: FileCheck %s -check-prefix GOT-TLS + +GOT-OBJ: Cannot find PLTGOT dynamic table tag. + +GOT-EXE: Primary GOT { +GOT-EXE-NEXT: Canonical gp value: 0x418880 +GOT-EXE-NEXT: Reserved entries [ +GOT-EXE-NEXT: Entry { +GOT-EXE-NEXT: Address: 0x410890 +GOT-EXE-NEXT: Access: -32752 +GOT-EXE-NEXT: Initial: 0x0 +GOT-EXE-NEXT: Purpose: Lazy resolver +GOT-EXE-NEXT: } +GOT-EXE-NEXT: Entry { +GOT-EXE-NEXT: Address: 0x410894 +GOT-EXE-NEXT: Access: -32748 +GOT-EXE-NEXT: Initial: 0x80000000 +GOT-EXE-NEXT: Purpose: Module pointer (GNU extension) +GOT-EXE-NEXT: } +GOT-EXE-NEXT: ] +GOT-EXE-NEXT: Local entries [ +GOT-EXE-NEXT: Entry { +GOT-EXE-NEXT: Address: 0x410898 +GOT-EXE-NEXT: Access: -32744 +GOT-EXE-NEXT: Initial: 0x400418 +GOT-EXE-NEXT: } +GOT-EXE-NEXT: Entry { +GOT-EXE-NEXT: Address: 0x41089C +GOT-EXE-NEXT: Access: -32740 +GOT-EXE-NEXT: Initial: 0x410840 +GOT-EXE-NEXT: } +GOT-EXE-NEXT: Entry { +GOT-EXE-NEXT: Address: 0x4108A0 +GOT-EXE-NEXT: Access: -32736 +GOT-EXE-NEXT: Initial: 0x0 +GOT-EXE-NEXT: } +GOT-EXE-NEXT: ] +GOT-EXE-NEXT: Global entries [ +GOT-EXE-NEXT: Entry { +GOT-EXE-NEXT: Address: 0x4108A4 +GOT-EXE-NEXT: Access: -32732 +GOT-EXE-NEXT: Initial: 0x0 +GOT-EXE-NEXT: Value: 0x0 +GOT-EXE-NEXT: Type: Function (0x2) +GOT-EXE-NEXT: Section: Undefined (0x0) +GOT-EXE-NEXT: Name: __gmon_start__@ (1) +GOT-EXE-NEXT: } +GOT-EXE-NEXT: ] +GOT-EXE-NEXT: Number of TLS and multi-GOT entries: 0 +GOT-EXE-NEXT: } + +GOT-SO: Primary GOT { +GOT-SO-NEXT: Canonical gp value: 0x188D0 +GOT-SO-NEXT: Reserved entries [ +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108E0 +GOT-SO-NEXT: Access: -32752 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: Purpose: Lazy resolver +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108E4 +GOT-SO-NEXT: Access: -32748 +GOT-SO-NEXT: Initial: 0x80000000 +GOT-SO-NEXT: Purpose: Module pointer (GNU extension) +GOT-SO-NEXT: } +GOT-SO-NEXT: ] +GOT-SO-NEXT: Local entries [ +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108E8 +GOT-SO-NEXT: Access: -32744 +GOT-SO-NEXT: Initial: 0x108E0 +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108EC +GOT-SO-NEXT: Access: -32740 +GOT-SO-NEXT: Initial: 0x10000 +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108F0 +GOT-SO-NEXT: Access: -32736 +GOT-SO-NEXT: Initial: 0x10920 +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108F4 +GOT-SO-NEXT: Access: -32732 +GOT-SO-NEXT: Initial: 0x108CC +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108F8 +GOT-SO-NEXT: Access: -32728 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x108FC +GOT-SO-NEXT: Access: -32724 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x10900 +GOT-SO-NEXT: Access: -32720 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x10904 +GOT-SO-NEXT: Access: -32716 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: } +GOT-SO-NEXT: ] +GOT-SO-NEXT: Global entries [ +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x10908 +GOT-SO-NEXT: Access: -32712 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: Value: 0x0 +GOT-SO-NEXT: Type: None (0x0) +GOT-SO-NEXT: Section: Undefined (0x0) +GOT-SO-NEXT: Name: _ITM_registerTMCloneTable@ (87) +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x1090C +GOT-SO-NEXT: Access: -32708 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: Value: 0x0 +GOT-SO-NEXT: Type: None (0x0) +GOT-SO-NEXT: Section: Undefined (0x0) +GOT-SO-NEXT: Name: _Jv_RegisterClasses@ (128) +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x10910 +GOT-SO-NEXT: Access: -32704 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: Value: 0x0 +GOT-SO-NEXT: Type: Function (0x2) +GOT-SO-NEXT: Section: Undefined (0x0) +GOT-SO-NEXT: Name: __gmon_start__@ (23) +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x10914 +GOT-SO-NEXT: Access: -32700 +GOT-SO-NEXT: Initial: 0x840 +GOT-SO-NEXT: Value: 0x840 +GOT-SO-NEXT: Type: Function (0x2) +GOT-SO-NEXT: Section: Undefined (0x0) +GOT-SO-NEXT: Name: puts@GLIBC_2.0 (162) +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x10918 +GOT-SO-NEXT: Access: -32696 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: Value: 0x0 +GOT-SO-NEXT: Type: None (0x0) +GOT-SO-NEXT: Section: Undefined (0x0) +GOT-SO-NEXT: Name: _ITM_deregisterTMCloneTable@ (59) +GOT-SO-NEXT: } +GOT-SO-NEXT: Entry { +GOT-SO-NEXT: Address: 0x1091C +GOT-SO-NEXT: Access: -32692 +GOT-SO-NEXT: Initial: 0x0 +GOT-SO-NEXT: Value: 0x0 +GOT-SO-NEXT: Type: Function (0x2) +GOT-SO-NEXT: Section: Undefined (0x0) +GOT-SO-NEXT: Name: __cxa_finalize@GLIBC_2.2 (113) +GOT-SO-NEXT: } +GOT-SO-NEXT: ] +GOT-SO-NEXT: Number of TLS and multi-GOT entries: 0 +GOT-SO-NEXT: } + +GOT-TLS: Primary GOT { +GOT-TLS-NEXT: Canonical gp value: 0x18BF0 +GOT-TLS-NEXT: Reserved entries [ +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C00 +GOT-TLS-NEXT: Access: -32752 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: Purpose: Lazy resolver +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C08 +GOT-TLS-NEXT: Access: -32744 +GOT-TLS-NEXT: Initial: 0x8000000000000000 +GOT-TLS-NEXT: Purpose: Module pointer (GNU extension) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: ] +GOT-TLS-NEXT: Local entries [ +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C10 +GOT-TLS-NEXT: Access: -32736 +GOT-TLS-NEXT: Initial: 0x10000 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C18 +GOT-TLS-NEXT: Access: -32728 +GOT-TLS-NEXT: Initial: 0x10C00 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C20 +GOT-TLS-NEXT: Access: -32720 +GOT-TLS-NEXT: Initial: 0x10CB8 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C28 +GOT-TLS-NEXT: Access: -32712 +GOT-TLS-NEXT: Initial: 0x10BF0 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C30 +GOT-TLS-NEXT: Access: -32704 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C38 +GOT-TLS-NEXT: Access: -32696 +GOT-TLS-NEXT: Initial: 0x948 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C40 +GOT-TLS-NEXT: Access: -32688 +GOT-TLS-NEXT: Initial: 0xA20 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C48 +GOT-TLS-NEXT: Access: -32680 +GOT-TLS-NEXT: Initial: 0xAF0 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C50 +GOT-TLS-NEXT: Access: -32672 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C58 +GOT-TLS-NEXT: Access: -32664 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C60 +GOT-TLS-NEXT: Access: -32656 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: } +GOT-TLS-NEXT: ] +GOT-TLS-NEXT: Global entries [ +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C68 +GOT-TLS-NEXT: Access: -32648 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: Value: 0x0 +GOT-TLS-NEXT: Type: None (0x0) +GOT-TLS-NEXT: Section: Undefined (0x0) +GOT-TLS-NEXT: Name: _ITM_registerTMCloneTable@ (78) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C70 +GOT-TLS-NEXT: Access: -32640 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: Value: 0x0 +GOT-TLS-NEXT: Type: None (0x0) +GOT-TLS-NEXT: Section: Undefined (0x0) +GOT-TLS-NEXT: Name: _Jv_RegisterClasses@ (119) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C78 +GOT-TLS-NEXT: Access: -32632 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: Value: 0x0 +GOT-TLS-NEXT: Type: Function (0x2) +GOT-TLS-NEXT: Section: Undefined (0x0) +GOT-TLS-NEXT: Name: __gmon_start__@ (23) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C80 +GOT-TLS-NEXT: Access: -32624 +GOT-TLS-NEXT: Initial: 0xB60 +GOT-TLS-NEXT: Value: 0xB60 +GOT-TLS-NEXT: Type: Function (0x2) +GOT-TLS-NEXT: Section: Undefined (0x0) +GOT-TLS-NEXT: Name: __tls_get_addr@GLIBC_2.3 (150) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C88 +GOT-TLS-NEXT: Access: -32616 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: Value: 0x0 +GOT-TLS-NEXT: Type: None (0x0) +GOT-TLS-NEXT: Section: Undefined (0x0) +GOT-TLS-NEXT: Name: _ITM_deregisterTMCloneTable@ (50) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: Entry { +GOT-TLS-NEXT: Address: 0x10C90 +GOT-TLS-NEXT: Access: -32608 +GOT-TLS-NEXT: Initial: 0x0 +GOT-TLS-NEXT: Value: 0x0 +GOT-TLS-NEXT: Type: Function (0x2) +GOT-TLS-NEXT: Section: Undefined (0x0) +GOT-TLS-NEXT: Name: __cxa_finalize@GLIBC_2.2 (104) +GOT-TLS-NEXT: } +GOT-TLS-NEXT: ] +GOT-TLS-NEXT: Number of TLS and multi-GOT entries: 4 +GOT-TLS-NEXT: } Index: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ tools/llvm-readobj/ELFDumper.cpp @@ -18,6 +18,7 @@ #include "Error.h" #include "ObjDumper.h" #include "StreamWriter.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Object/ELFObjectFile.h" @@ -54,6 +55,7 @@ void printProgramHeaders() override; void printAttributes() override; + void printMipsPLTGOT() override; private: typedef ELFFile ELFO; @@ -158,6 +160,15 @@ } } +template +static const typename ELFFile::Elf_Shdr * +findSectionByAddress(const ELFFile *Obj, uint64_t Addr) { + for (const auto &Shdr : Obj->sections()) + if (Shdr.sh_addr == Addr) + return &Shdr; + return nullptr; +} + static const EnumEntry ElfClass[] = { { "None", ELF::ELFCLASSNONE }, { "32-bit", ELF::ELFCLASS32 }, @@ -1020,3 +1031,205 @@ } } +namespace { +template class MipsGOTParser { +public: + typedef object::ELFFile ObjectFile; + typedef typename ObjectFile::Elf_Shdr Elf_Shdr; + + MipsGOTParser(const ObjectFile *Obj, StreamWriter &W) : Obj(Obj), W(W) {} + + void ParseGOT(const Elf_Shdr &GOTShdr); + +private: + typedef typename ObjectFile::Elf_Sym_Iter Elf_Sym_Iter; + typedef typename ObjectFile::Elf_Addr GOTEntry; + typedef typename ObjectFile::template ELFEntityIterator + GOTIter; + + const ObjectFile *Obj; + StreamWriter &W; + + std::size_t GetGOTTotal(ArrayRef GOT) const; + GOTIter MakeGOTIter(ArrayRef 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, + Elf_Sym_Iter Sym); +}; +} + +template +void MipsGOTParser::ParseGOT(const Elf_Shdr &GOTShdr) { + ErrorOr> GOT = Obj->getSectionContents(&GOTShdr); + if (!GOT) { + W.startLine() << "The .got section is empty.\n"; + return; + } + + uint64_t DtLocalGotNum; + uint64_t DtGotSym; + if (!GetGOTTags(DtLocalGotNum, DtGotSym)) + return; + + if (DtLocalGotNum > GetGOTTotal(*GOT)) { + W.startLine() << "MIPS_LOCAL_GOTNO exceeds a number of GOT entries.\n"; + return; + } + + Elf_Sym_Iter DynSymBegin = Obj->begin_dynamic_symbols(); + Elf_Sym_Iter DynSymEnd = Obj->end_dynamic_symbols(); + std::size_t DynSymTotal = std::size_t(std::distance(DynSymBegin, DynSymEnd)); + + if (DtGotSym + 1 > DynSymTotal) { + W.startLine() << "MIPS_GOTSYM exceeds a number of dynamic symbols.\n"; + return; + } + + std::size_t GlobalGotNum = DynSymTotal - DtGotSym; + + 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 It = GotBegin; + + DictScope GS(W, "Primary GOT"); + + W.printHex("Canonical gp value", GOTShdr.sh_addr + 0x7ff0); + { + ListScope RS(W, "Reserved entries"); + + { + DictScope D(W, "Entry"); + 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++); + W.printString("Purpose", StringRef("Module pointer (GNU extension)")); + } + } + { + ListScope LS(W, "Local entries"); + for (; It != GotLocalEnd; ++It) { + DictScope D(W, "Entry"); + printGotEntry(GOTShdr.sh_addr, GotBegin, It); + } + } + { + ListScope GS(W, "Global entries"); + + GOTIter GotGlobalEnd = MakeGOTIter(*GOT, DtLocalGotNum + GlobalGotNum); + Elf_Sym_Iter GotDynSym = DynSymBegin + DtGotSym; + for (; It != GotGlobalEnd; ++It) { + DictScope D(W, "Entry"); + printGlobalGotEntry(GOTShdr.sh_addr, GotBegin, It, GotDynSym++); + } + } + + std::size_t SpecGotNum = GetGOTTotal(*GOT) - DtLocalGotNum - GlobalGotNum; + W.printNumber("Number of TLS and multi-GOT entries", SpecGotNum); +} + +template +std::size_t MipsGOTParser::GetGOTTotal(ArrayRef GOT) const { + return GOT.size() / sizeof(GOTEntry); +} + +template +typename MipsGOTParser::GOTIter +MipsGOTParser::MakeGOTIter(ArrayRef GOT, std::size_t EntryNum) { + const char *Data = reinterpret_cast(GOT.data()); + return GOTIter(sizeof(GOTEntry), Data + EntryNum * sizeof(GOTEntry)); +} + +template +bool MipsGOTParser::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 +void MipsGOTParser::printGotEntry(uint64_t GotAddr, GOTIter BeginIt, + GOTIter It) { + int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); + W.printHex("Address", GotAddr + Offset); + W.printNumber("Access", Offset - 0x7ff0); + W.printHex("Initial", *It); +} + +template +void MipsGOTParser::printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, + GOTIter It, Elf_Sym_Iter Sym) { + printGotEntry(GotAddr, BeginIt, It); + + W.printHex("Value", Sym->st_value); + W.printEnum("Type", Sym->getType(), makeArrayRef(ElfSymbolTypes)); + + unsigned SectionIndex = 0; + StringRef SectionName; + getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex); + W.printHex("Section", SectionName, SectionIndex); + + std::string FullSymbolName = getFullSymbolName(*Obj, Sym); + W.printNumber("Name", FullSymbolName, Sym->st_name); +} + +template void ELFDumper::printMipsPLTGOT() { + if (Obj->getHeader()->e_machine != EM_MIPS) { + W.startLine() << "MIPS PLT GOT is available for MIPS targets only.\n"; + return; + } + + llvm::Optional DtPltGot; + for (const auto &Entry : Obj->dynamic_table()) { + if (Entry.getTag() == ELF::DT_PLTGOT) { + DtPltGot = Entry.getVal(); + break; + } + } + + if (!DtPltGot) { + W.startLine() << "Cannot find PLTGOT dynamic table tag.\n"; + return; + } + + const Elf_Shdr *GotShdr = findSectionByAddress(Obj, *DtPltGot); + if (!GotShdr) { + W.startLine() << "There is no .got section in the file.\n"; + return; + } + + MipsGOTParser(Obj, W).ParseGOT(*GotShdr); +} Index: tools/llvm-readobj/ObjDumper.h =================================================================== --- tools/llvm-readobj/ObjDumper.h +++ tools/llvm-readobj/ObjDumper.h @@ -42,6 +42,9 @@ // Only implemented for ARM ELF at this time. virtual void printAttributes() { } + // Only implemented for MIPS ELF at this time. + virtual void printMipsPLTGOT() { } + protected: StreamWriter& W; }; Index: tools/llvm-readobj/llvm-readobj.h =================================================================== --- tools/llvm-readobj/llvm-readobj.h +++ tools/llvm-readobj/llvm-readobj.h @@ -40,6 +40,7 @@ extern llvm::cl::opt ExpandRelocs; extern llvm::cl::opt CodeViewLineTables; extern llvm::cl::opt ARMAttributes; + extern llvm::cl::opt MipsPLTGOT; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \ Index: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -135,6 +135,11 @@ cl::desc("Display the ARM attributes section")); cl::alias ARMAttributesShort("-a", cl::desc("Alias for --arm-attributes"), cl::aliasopt(ARMAttributes)); + + // -mips-plt-got + cl::opt + MipsPLTGOT("mips-plt-got", + cl::desc("Display the MIPS GOT and PLT GOT sections")); } // namespace opts static int ReturnValue = EXIT_SUCCESS; @@ -178,6 +183,18 @@ ReturnValue = EXIT_FAILURE; } +static bool isMipsArch(unsigned Arch) { + switch (Arch) { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + return true; + default: + return false; + } +} + /// @brief Creates an format-specific object file dumper. static error_code createDumper(const ObjectFile *Obj, StreamWriter &Writer, std::unique_ptr &Result) { @@ -235,6 +252,9 @@ if (Obj->getArch() == llvm::Triple::arm && Obj->isELF()) if (opts::ARMAttributes) Dumper->printAttributes(); + if (isMipsArch(Obj->getArch()) && Obj->isELF()) + if (opts::MipsPLTGOT) + Dumper->printMipsPLTGOT(); }