From 42469f61f5ea6ce6dafcabfe0dffdd19dc4ab527 Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Wed, 18 Jun 2014 08:47:09 +0000 Subject: [PATCH] [llvm-readobj][ELF] New `-mips-plt-got` command line option to output MIPS GOT section. Patch reviewed by Rafael Espindola. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@211150 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Inputs/got-tls.so.elf-mips64el | Bin 0 -> 7398 bytes test/tools/llvm-readobj/mips-got.test | 306 ++++++++++++++++++ tools/llvm-readobj/ELFDumper.cpp | 217 +++++++++++++ tools/llvm-readobj/ObjDumper.h | 3 + tools/llvm-readobj/llvm-readobj.cpp | 20 ++ tools/llvm-readobj/llvm-readobj.h | 1 + 6 files changed, 547 insertions(+) create mode 100755 test/tools/llvm-readobj/Inputs/got-tls.so.elf-mips64el create mode 100644 test/tools/llvm-readobj/mips-got.test diff --git a/test/tools/llvm-readobj/Inputs/got-tls.so.elf-mips64el b/test/tools/llvm-readobj/Inputs/got-tls.so.elf-mips64el new file mode 100755 index 0000000000000000000000000000000000000000..3afc567f85d588c8f34e29a305d40cd231f259e9 GIT binary patch literal 7398 zcmcH-TWlOx_3Vz5xN+L7lK_TRbQDG4i0;&JX-H5$Hr^ynBIiNuO6>IWvgQq@wUmP!#I#VtsUL`kdyRS*@j_(~-!Ys3fnL2{9*u2jfy&b{ZX zXYYe57kpfvRFMq~+nsY}Noa8+zIL(`ok(lG|GsS^X2SY99xL+Ny&{CfUGP9VvC zoF@8-UagIG-iWia`hpHrrU9I0xSe5& zA!~D|sBHA%9WHZ-z-;nA5CqTgmT$ zB}4vQrkj4>RP@cweU;6qb=VE>ho;|K>)81m(znIr;RU3BvN@A_{>aBk{I{uNv~MDA z8#F>ch5tKa=s!Ukt%0-hL9$FgLYwkJ$k5?oclhAI;9(bB zw>VyzaBF_9>br=RCQ3fIqX@t~HhjV@cvY`hs`*}Z_(Zl`nec{lBV|wX;yG@9I!6X_ z<t2DPzV)hobgrGoDLa?LGzzMCr)s!%SC;IV`{_bOW=IB|7jk5jY-9&y_LQ( z8~?YLO88k?@HgyJBkk7^$~U-8@sscm#clt&(z-r-ueIRYtV@jZ{?>cB72mxY{W|?$J>))ZRwx>?eF%m5a8xN%W-h;;MJ80+kXydrO!nJCdg>v-8Cs_*G~JKdkp+&K9?fTui|mVJEyE?t~0( z!3*JK&S4+I%gr1fTDF6W^?kt)>UJu zDuX%+9CxD!W9`Y}kutMP^MJB!4D(`)mcXR%izBgaAgY(G05jMJJm+>B$3edI%I>+R{(RE(=lR0dl-&aFO z(L7R~C?7Ul4|_0Q>D9|>ozU6}&^E3cT-8tUDsnJgO^zDCzduY4FyU7jA9j(>1+}XTlQ<$6d^om7#-Oy7Y`Dioq zOL?6P=8NVxayro8(NVNl;5n51M!yj{Yv~{F8LEjd6BoJxgXYbM_W{{2R{4G|@<-_) z$7b`nxXMsd@Bi04PyU-`N1#U{aXppIe%bCFs!Ub$UUkOKRtnyLH<2H+2TQ(Z_xJ7T zcl!3H_wV0rf6@7p(=X@vQUlG1uRiR27XKf`|ATZnDNam5f^It<32(UnH@Lx$kF~F8cDW z5%?kUqfN#ww4>O;rC1UDF}`FA>}5fTSL6gfjeL4z2rlmoff?4997+642HKG5Qx<)B zM+rR5g5qEFCI92dr!mnSQnOh=-~~-?{)!zT&>D>DOWmOfB`{VHzoI{5=*xa0&=x_4 z62Fk2$4=CL!1r5$gCfY#jGyk^q%UE<%lZOMc@zIJ>s!o_cZ}32%y>7{2G~2 zeK{AdvA)QO4HN$jGKePmlXu%9>x)0pH}OSekpDgrU?}<~on)yA{9~lA82q!!{_Q9c z`{w-r8EGGI4IwI;;tRbQulN;Ndh5&Bg+|b3eR}6co2gH@to^9mA{801_3*~<8SGOo zB>%F1(%uv2{}w4=d&n?u`qxtNzZPa$&Y`G;g%R6^UE*8#C+9}1d`GiB)k@z0vd_2D zH-hZnt@KUJI#4UU6J)<_rLR}-Xs;uXo&{ZP?{iIWuXkwrgCP49;nL#jDa5nzbtU_Y z$ZC2DWZ!6|cZ2K)t#qs;$afxLSXi%-?>9n5zdPFMIK;Q$6H&2d-@-~rT(eYlqm@JJ zse|e-Mx)O!5i?#|mI-`)o8#?O^!D*SiS#5^+2lM!RJG&yOZ0|(5KR9n)8&56WYFj2 zS{~#(m_>mE=I18oQ_Rh=9l4*29Xc=K#`_A=H)8!r_7{Qc<>wbDPx)Cf-UYN@C-6pS zuB&$xy*)pFK|19j?)?xK=N%>AzRo{T_ETWKQ<6AapV0H(&Orze^qwY*0b3E`#N?K$@seGCZxyJJt1H9 z@$ob|>g2JymmNNJ#vMF1G)%Qc_fQLVJlkZgRT*7>+|5&+u?A9JjF4QnP;rap%1Ey4 zs>hd`o0 ELFO; @@ -159,6 +161,15 @@ getSectionNameIndex(const ELFO &Obj, typename ELFO::Elf_Sym_Iter Symbol, } } +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 }, @@ -1021,3 +1032,209 @@ void ELFDumper >::printAttributes() { } } +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) { + // 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 + + 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); +} diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 795193327bc..f80a28b25cf 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -40,6 +40,9 @@ public: // 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; }; diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 52db0ebbef3..8d2a997a231 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -135,6 +135,11 @@ namespace opts { 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; @@ -177,6 +182,18 @@ static void reportError(StringRef Input, StringRef Message) { 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 std::error_code createDumper(const ObjectFile *Obj, StreamWriter &Writer, std::unique_ptr &Result) { @@ -234,6 +251,9 @@ static void dumpObject(const ObjectFile *Obj) { if (Obj->getArch() == llvm::Triple::arm && Obj->isELF()) if (opts::ARMAttributes) Dumper->printAttributes(); + if (isMipsArch(Obj->getArch()) && Obj->isELF()) + if (opts::MipsPLTGOT) + Dumper->printMipsPLTGOT(); } diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 033195c1d49..04139480a6d 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -38,6 +38,7 @@ namespace opts { 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) \