From 367cf70f27e2e3261df2b27fc89fafe5b0a1dc22 Mon Sep 17 00:00:00 2001 From: Nick Kledzik Date: Tue, 16 Sep 2014 01:41:51 +0000 Subject: [PATCH] [llvm-objdump] for mach-o add -bind, -lazy-bind, and -weak-bind options This finishes the ability of llvm-objdump to print out all information from the LC_DYLD_INFO load command. The -bind option prints out symbolic references that dyld must resolve immediately. The -lazy-bind option prints out symbolc reference that are lazily resolved on first use. The -weak-bind option prints out information about symbols which dyld must try to coalesce across images. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@217853 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Object/MachO.h | 66 ++++ lib/Object/MachOObjectFile.cpp | 282 +++++++++++++++++- .../llvm-objdump/Inputs/bind.macho-x86_64 | Bin 0 -> 8776 bytes .../Inputs/lazy-bind.macho-x86_64 | Bin 0 -> 8592 bytes .../Inputs/weak-bind.macho-x86_64 | Bin 0 -> 8856 bytes test/tools/llvm-objdump/macho-bind.test | 10 + test/tools/llvm-objdump/macho-lazy-bind.test | 7 + test/tools/llvm-objdump/macho-weak-bind.test | 10 + tools/llvm-objdump/MachODump.cpp | 117 +++++++- tools/llvm-objdump/llvm-objdump.cpp | 52 +++- tools/llvm-objdump/llvm-objdump.h | 3 + 11 files changed, 544 insertions(+), 3 deletions(-) create mode 100755 test/tools/llvm-objdump/Inputs/bind.macho-x86_64 create mode 100755 test/tools/llvm-objdump/Inputs/lazy-bind.macho-x86_64 create mode 100755 test/tools/llvm-objdump/Inputs/weak-bind.macho-x86_64 create mode 100644 test/tools/llvm-objdump/macho-bind.test create mode 100644 test/tools/llvm-objdump/macho-lazy-bind.test create mode 100644 test/tools/llvm-objdump/macho-weak-bind.test diff --git a/include/llvm/Object/MachO.h b/include/llvm/Object/MachO.h index 29f737571fb..a65679060dd 100644 --- a/include/llvm/Object/MachO.h +++ b/include/llvm/Object/MachO.h @@ -136,6 +136,54 @@ private: }; typedef content_iterator rebase_iterator; +/// MachOBindEntry encapsulates the current state in the decompression of +/// binding opcodes. This allows you to iterate through the compressed table of +/// bindings using: +/// for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable()) { +/// } +class MachOBindEntry { +public: + enum class Kind { Regular, Lazy, Weak }; + + MachOBindEntry(ArrayRef Opcodes, bool is64Bit, MachOBindEntry::Kind); + + uint32_t segmentIndex() const; + uint64_t segmentOffset() const; + StringRef typeName() const; + StringRef symbolName() const; + uint32_t flags() const; + int64_t addend() const; + int ordinal() const; + + bool operator==(const MachOBindEntry &) const; + + void moveNext(); + +private: + friend class MachOObjectFile; + void moveToFirst(); + void moveToEnd(); + uint64_t readULEB128(); + int64_t readSLEB128(); + + ArrayRef Opcodes; + const uint8_t *Ptr; + uint64_t SegmentOffset; + uint32_t SegmentIndex; + StringRef SymbolName; + int Ordinal; + uint32_t Flags; + int64_t Addend; + uint64_t RemainingLoopCount; + uint64_t AdvanceAmount; + uint8_t BindType; + uint8_t PointerSize; + Kind TableKind; + bool Malformed; + bool Done; +}; +typedef content_iterator bind_iterator; + class MachOObjectFile : public ObjectFile { public: struct LoadCommandInfo { @@ -245,6 +293,21 @@ public: static iterator_range rebaseTable(ArrayRef Opcodes, bool is64); + /// For use iterating over all bind table entries. + iterator_range bindTable() const; + + /// For use iterating over all lazy bind table entries. + iterator_range lazyBindTable() const; + + /// For use iterating over all lazy bind table entries. + iterator_range weakBindTable() const; + + /// For use examining bind opcodes not in a MachOObjectFile. + static iterator_range bindTable(ArrayRef Opcodes, + bool is64, + MachOBindEntry::Kind); + + // In a MachO file, sections have a segment name. This is used in the .o // files. They have a single segment, but this field specifies which segment // a section should be put in in the final object. @@ -342,6 +405,8 @@ public: bool isRelocatableObject() const override; + bool hasPageZeroSegment() const { return HasPageZeroSegment; } + static bool classof(const Binary *v) { return v->isMachO(); } @@ -357,6 +422,7 @@ private: const char *DysymtabLoadCmd; const char *DataInCodeLoadCmd; const char *DyldInfoLoadCmd; + bool HasPageZeroSegment; }; /// DiceRef diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index 7e7e8c49315..bb973b41db2 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -58,6 +58,17 @@ getSegmentLoadCommandNumSections(const MachOObjectFile *O, return S.nsects; } +static bool isPageZeroSegment(const MachOObjectFile *O, + const MachOObjectFile::LoadCommandInfo &L) { + if (O->is64Bit()) { + MachO::segment_command_64 S = O->getSegment64LoadCommand(L); + return StringRef("__PAGEZERO").equals(S.segname); + } + MachO::segment_command S = O->getSegmentLoadCommand(L); + return StringRef("__PAGEZERO").equals(S.segname); +} + + static const char * getSectionPtr(const MachOObjectFile *O, MachOObjectFile::LoadCommandInfo L, unsigned Sec) { @@ -229,7 +240,8 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, bool Is64bits, std::error_code &EC) : ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object), SymtabLoadCmd(nullptr), DysymtabLoadCmd(nullptr), - DataInCodeLoadCmd(nullptr), DyldInfoLoadCmd(nullptr) { + DataInCodeLoadCmd(nullptr), DyldInfoLoadCmd(nullptr), + HasPageZeroSegment(false) { uint32_t LoadCommandCount = this->getHeader().ncmds; MachO::LoadCommandType SegmentLoadType = is64Bit() ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT; @@ -255,6 +267,8 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, const char *Sec = getSectionPtr(this, Load, J); Sections.push_back(Sec); } + if (isPageZeroSegment(this, Load)) + HasPageZeroSegment = true; } else if (Load.C.cmd == MachO::LC_LOAD_DYLIB || Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || @@ -1860,6 +1874,272 @@ iterator_range MachOObjectFile::rebaseTable() const { return rebaseTable(getDyldInfoRebaseOpcodes(), is64Bit()); } + +MachOBindEntry::MachOBindEntry(ArrayRef Bytes, bool is64Bit, + Kind BK) + : Opcodes(Bytes), Ptr(Bytes.begin()), SegmentOffset(0), SegmentIndex(0), + Ordinal(0), Flags(0), Addend(0), RemainingLoopCount(0), AdvanceAmount(0), + BindType(0), PointerSize(is64Bit ? 8 : 4), + TableKind(BK), Malformed(false), Done(false) {} + +void MachOBindEntry::moveToFirst() { + Ptr = Opcodes.begin(); + moveNext(); +} + +void MachOBindEntry::moveToEnd() { + Ptr = Opcodes.end(); + RemainingLoopCount = 0; + Done = true; +} + +void MachOBindEntry::moveNext() { + // If in the middle of some loop, move to next binding in loop. + SegmentOffset += AdvanceAmount; + if (RemainingLoopCount) { + --RemainingLoopCount; + return; + } + if (Ptr == Opcodes.end()) { + Done = true; + return; + } + bool More = true; + while (More && !Malformed) { + // Parse next opcode and set up next loop. + uint8_t Byte = *Ptr++; + uint8_t ImmValue = Byte & MachO::BIND_IMMEDIATE_MASK; + uint8_t Opcode = Byte & MachO::BIND_OPCODE_MASK; + int8_t SignExtended; + const uint8_t *SymStart; + switch (Opcode) { + case MachO::BIND_OPCODE_DONE: + if (TableKind == Kind::Lazy) { + // Lazying bindings have a DONE opcode between entries. Need to ignore + // it to advance to next entry. But need not if this is last entry. + bool NotLastEntry = false; + for (const uint8_t *P = Ptr; P < Opcodes.end(); ++P) { + if (*P) { + NotLastEntry = true; + } + } + if (NotLastEntry) + break; + } + More = false; + Done = true; + moveToEnd(); + DEBUG_WITH_TYPE("mach-o-bind", llvm::dbgs() << "BIND_OPCODE_DONE\n"); + break; + case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + Ordinal = ImmValue; + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: " + << "Ordinal=" << Ordinal << "\n"); + break; + case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + Ordinal = readULEB128(); + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: " + << "Ordinal=" << Ordinal << "\n"); + break; + case MachO::BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + if (ImmValue) { + SignExtended = MachO::BIND_OPCODE_MASK | ImmValue; + Ordinal = SignExtended; + } else + Ordinal = 0; + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: " + << "Ordinal=" << Ordinal << "\n"); + break; + case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + Flags = ImmValue; + SymStart = Ptr; + while (*Ptr) { + ++Ptr; + } + ++Ptr; + SymbolName = StringRef(reinterpret_cast(SymStart), + Ptr-SymStart); + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: " + << "SymbolName=" << SymbolName << "\n"); + if (TableKind == Kind::Weak) { + if (ImmValue & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) + return; + } + break; + case MachO::BIND_OPCODE_SET_TYPE_IMM: + BindType = ImmValue; + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_TYPE_IMM: " + << "BindType=" << (int)BindType << "\n"); + break; + case MachO::BIND_OPCODE_SET_ADDEND_SLEB: + Addend = readSLEB128(); + if (TableKind == Kind::Lazy) + Malformed = true; + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_ADDEND_SLEB: " + << "Addend=" << Addend << "\n"); + break; + case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + SegmentIndex = ImmValue; + SegmentOffset = readULEB128(); + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: " + << "SegmentIndex=" << SegmentIndex << ", " + << format("SegmentOffset=0x%06X", SegmentOffset) + << "\n"); + break; + case MachO::BIND_OPCODE_ADD_ADDR_ULEB: + SegmentOffset += readULEB128(); + DEBUG_WITH_TYPE("mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_ADD_ADDR_ULEB: " + << format("SegmentOffset=0x%06X", + SegmentOffset) << "\n"); + break; + case MachO::BIND_OPCODE_DO_BIND: + AdvanceAmount = PointerSize; + RemainingLoopCount = 0; + DEBUG_WITH_TYPE("mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_DO_BIND: " + << format("SegmentOffset=0x%06X", + SegmentOffset) << "\n"); + return; + case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + AdvanceAmount = readULEB128(); + RemainingLoopCount = 0; + if (TableKind == Kind::Lazy) + Malformed = true; + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_DO_BIND_IMM_TIMES: " + << format("SegmentOffset=0x%06X", SegmentOffset) + << ", AdvanceAmount=" << AdvanceAmount + << ", RemainingLoopCount=" << RemainingLoopCount + << "\n"); + return; + case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + AdvanceAmount = ImmValue * PointerSize; + RemainingLoopCount = 0; + if (TableKind == Kind::Lazy) + Malformed = true; + DEBUG_WITH_TYPE("mach-o-bind", + llvm::dbgs() + << "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: " + << format("SegmentOffset=0x%06X", + SegmentOffset) << "\n"); + return; + case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + RemainingLoopCount = readULEB128() - 1; + AdvanceAmount = readULEB128() + PointerSize; + if (TableKind == Kind::Lazy) + Malformed = true; + DEBUG_WITH_TYPE( + "mach-o-bind", + llvm::dbgs() << "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: " + << format("SegmentOffset=0x%06X", SegmentOffset) + << ", AdvanceAmount=" << AdvanceAmount + << ", RemainingLoopCount=" << RemainingLoopCount + << "\n"); + return; + default: + Malformed = true; + } + } +} + +uint64_t MachOBindEntry::readULEB128() { + unsigned Count; + uint64_t Result = decodeULEB128(Ptr, &Count); + Ptr += Count; + if (Ptr > Opcodes.end()) { + Ptr = Opcodes.end(); + Malformed = true; + } + return Result; +} + +int64_t MachOBindEntry::readSLEB128() { + unsigned Count; + int64_t Result = decodeSLEB128(Ptr, &Count); + Ptr += Count; + if (Ptr > Opcodes.end()) { + Ptr = Opcodes.end(); + Malformed = true; + } + return Result; +} + + +uint32_t MachOBindEntry::segmentIndex() const { return SegmentIndex; } + +uint64_t MachOBindEntry::segmentOffset() const { return SegmentOffset; } + +StringRef MachOBindEntry::typeName() const { + switch (BindType) { + case MachO::BIND_TYPE_POINTER: + return "pointer"; + case MachO::BIND_TYPE_TEXT_ABSOLUTE32: + return "text abs32"; + case MachO::BIND_TYPE_TEXT_PCREL32: + return "text rel32"; + } + return "unknown"; +} + +StringRef MachOBindEntry::symbolName() const { return SymbolName; } + +int64_t MachOBindEntry::addend() const { return Addend; } + +uint32_t MachOBindEntry::flags() const { return Flags; } + +int MachOBindEntry::ordinal() const { return Ordinal; } + +bool MachOBindEntry::operator==(const MachOBindEntry &Other) const { + assert(Opcodes == Other.Opcodes && "compare iterators of different files"); + return (Ptr == Other.Ptr) && + (RemainingLoopCount == Other.RemainingLoopCount) && + (Done == Other.Done); +} + +iterator_range +MachOObjectFile::bindTable(ArrayRef Opcodes, bool is64, + MachOBindEntry::Kind BKind) { + MachOBindEntry Start(Opcodes, is64, BKind); + Start.moveToFirst(); + + MachOBindEntry Finish(Opcodes, is64, BKind); + Finish.moveToEnd(); + + return iterator_range(bind_iterator(Start), + bind_iterator(Finish)); +} + +iterator_range MachOObjectFile::bindTable() const { + return bindTable(getDyldInfoBindOpcodes(), is64Bit(), + MachOBindEntry::Kind::Regular); +} + +iterator_range MachOObjectFile::lazyBindTable() const { + return bindTable(getDyldInfoLazyBindOpcodes(), is64Bit(), + MachOBindEntry::Kind::Lazy); +} + +iterator_range MachOObjectFile::weakBindTable() const { + return bindTable(getDyldInfoWeakBindOpcodes(), is64Bit(), + MachOBindEntry::Kind::Weak); +} + StringRef MachOObjectFile::getSectionFinalSegmentName(DataRefImpl Sec) const { ArrayRef Raw = getSectionRawFinalSegmentName(Sec); diff --git a/test/tools/llvm-objdump/Inputs/bind.macho-x86_64 b/test/tools/llvm-objdump/Inputs/bind.macho-x86_64 new file mode 100755 index 0000000000000000000000000000000000000000..51a58a7342e92418e3a38d57f6c7415b014ed934 GIT binary patch literal 8776 zcmeHNJxo+V5S|ApD5sog6n~-*Vibh%4H(Y`}Y0Z|9j9#=?s+XvM5zZZWess$CdRwo)~i0H^jI> zd~F)29E3_0Gp|=O3ALI@Wf7=e-?r47l|hW_f(5F4rIIgIDj!QH{XliSU8&b5;~1OY zR#i$v=4~6Q)bwO@GLT{Ts$rQouIsbU;}fw$?09^wdi^E^bx&FsbE4Gz+>^&M({pA| zT`6GJC3sdy<8_%eab^cm`Djk=aEX>-mSIf^K*|9>h2=8Lo!d@y0d@~|3AP!^@u2Wr ztmat=#!m8YB7x`m5?_4vqwC?`*2i`=`m68lUG(X7ojXqiePA#wO9N(D5JkAdF6*uyIsKvTi+9)K`y;H}di0)wwuk)+x{~x;q ze+OpihH{)S^Jm2}U>UFsSOzQumI2FvWxz6E892QRJfHpW=fyYAbMO;x$#-OU|Y2^SLjCGe#IH;#@J#n`h)~hMjPzu}K#<88xgqkJkv>Zp(mWz%pPNunbrR zECZGS%YbFTGGH073|IzE5d)7RDwZkH!g{AS5y1s`Ha(fkQF-rB#PRwgDqTtyGb2qB6 z3hn^LbO$h6?xO`Z>!%)#%e-za184yb1=6^W_1d{_stUVgc2ngBNaY5hL(pO92y~S6 f^PqQ9@9@zxVKx<4;R&@-D8^MB-(Mv2JPXl3o+H9e literal 0 HcmV?d00001 diff --git a/test/tools/llvm-objdump/Inputs/lazy-bind.macho-x86_64 b/test/tools/llvm-objdump/Inputs/lazy-bind.macho-x86_64 new file mode 100755 index 0000000000000000000000000000000000000000..02a4d1216f2107a9b9c33542479a01d2e1f9e938 GIT binary patch literal 8592 zcmeHMOK1~O6unccRxNh?Aoa^s8o$uGaG`>bh`|(#pMFF{eI)Ia1oA0Kimj_?aZy2t z;KF5bt8zFzqDbG3-I zLZs3nA{)W>N|9UQKoi*tPJop<7dsWd6dyQCZzEZa=5H<5`G7#BhT<288biE%yU{0s zjkyV?<1r(YD!Y?qYCbjBb2_jX3>^=~X}1QtCt9TvHkB%sCsL(FS986qK|SI^(zb7j zv<3B61rEQSO1s%{w+LTzz44%4PY_R=xG&PO`Ay`nW%8pclOHP}sJY&3P>*{=qTNu> zcQ0O*a?@(8n9RAptGQk+sMi@}(8jazN8-NLz)GEr4aGuX*uRE-)%zRTSJu^YRVtrV zrKw!1kX7U5;vehjGJHP5^CJFXTd8dFZ}n{CbAEcAw8Q!Oxzj6JdJp>h&YX^)?9aKP>0-icDNH=9ZIj!tDqVUxClIgewTr!Q|CIXe8{{kz$l z7w*64h7Zt*tXb>v0d3O`)iZ;G0cLW>B02#InZITW9Oe^@F=ANSv)IY zGS5&T8Rk(JuCO`e5&MBK7U#k~ec0vzli6eC#6i)p&q3#UIR?NA7ghZ+xBGQEte?sK5>dR<}Jdfg{C^bF)USc+J{sZ>5n$rpo zMdaCmqf*Jlg=DLXo*!-P6N3?(ARJxjp;ReTDY4~QJKy%xxET!HGULQYOj}n+q#A@u zt(401D~+ypzCy&~d(wFL*q;zKeEzt{rxr8WLZ%3(cD{1)h_9T#vXoD& zrToG&oZ9)mFnru28gUDs*WI`(m045^#Z)fib+z;D8oq8bgBZ`ozZ&;-Oz-Q&cyc`8 zhW%^VSATy4`$}G)Td90jt*qwem$Rx+D&js5=ksa!eE4&@D)rav5x|tnrXI%^#eDjQ zwYLaykdMz@5G$2Vl~TqTReOBhhR@fHI5@vK(`z-~tFx2upGizi`tL-nO*dfL4YR3? zD|nx$@!k&6A?Dpn81)6fIn@3$0Vo^5i>OJ%82NfXg?b1C>_3h&jS@7j)viNuob4Fq zx4d7*0k0iHl1`Mt@=9?qyEH$TUd^U~X)mHMj$f{Ree?2jW8d7G`}Tgv%{NZHf<8zm zatzU-yzXQ6!TJz7=f&|{294`b2afR`MJgyxb8W1DslR_C$ATu@6C0Se>8?+0xV5QG z;D_$_eyA63FGNm#s*&cetv%FT6CxU4`y=Z9$ggSIR^l&Kdna)Dfh{gsyX#)N6TNu5 zzTf*L`{eBPo*Jt0nf=~X)OG!>`1?IX>V4n8L?>9V=65ZB&%TM8g}+5kSv*wOM~|e*ZOlY|AQO6|f3e1*`&A0jq#jz$#!BunJfO ztO8bn|GNTRr=#b14_$GkW|NaMrQwm~a!D;OsA4MrNk+~UyX*Bj2lJ_%RCjrnLUZaT z;!Nl1^tqZxh8pSa>Yg{qVI$0AX=RWb-60Kb^!`$*_DhA|H>+Tu5%j z(D@ZF%#h-IrDu-mJ9+eLC;xW9V;L`<=pDuI~#GUqNlMI!e9$H4z%L>wKGCh9fg%zu)Y`Nl{X`9`=94;maZc+}uN zgWm#nq)Jz9Q@93kQsc%w>m27X&oz!^Bqsizne9>P;4Ub=N2)L^sxX2w%1MVjT%^EL Kb>KO|I{yHEO<2?b literal 0 HcmV?d00001 diff --git a/test/tools/llvm-objdump/macho-bind.test b/test/tools/llvm-objdump/macho-bind.test new file mode 100644 index 00000000000..0db316fb6fe --- /dev/null +++ b/test/tools/llvm-objdump/macho-bind.test @@ -0,0 +1,10 @@ +# RUN: llvm-objdump -macho -bind -arch x86_64 \ +# RUN: %p/Inputs/bind.macho-x86_64 | FileCheck %s + + +# CHECK:__DATA __data 0x00001028 pointer 0 flat-namespace _any +# CHECK:__DATA __data 0x00001020 pointer 0 main-executable _fromApp +# CHECK:__DATA __data 0x00001018 pointer 0 this-image _myfunc +# CHECK:__DATA __data 0x00001000 pointer 0 libfoo.dylib _foo +# CHECK:__DATA __data 0x00001008 pointer 0 libbar.dylib _bar +# CHECK:__DATA __data 0x00001010 pointer 0 libSystem.B.dylib _malloc diff --git a/test/tools/llvm-objdump/macho-lazy-bind.test b/test/tools/llvm-objdump/macho-lazy-bind.test new file mode 100644 index 00000000000..f4a5216af49 --- /dev/null +++ b/test/tools/llvm-objdump/macho-lazy-bind.test @@ -0,0 +1,7 @@ +# RUN: llvm-objdump -macho -lazy-bind -arch x86_64 \ +# RUN: %p/Inputs/lazy-bind.macho-x86_64 | FileCheck %s + + +# CHECK: __DATA __la_symbol_ptr 0x100001010 libfoo.dylib _foo +# CHECK: __DATA __la_symbol_ptr 0x100001018 libbar.dylib _bar +# CHECK: __DATA __la_symbol_ptr 0x100001020 libSystem.B.dylib _malloc diff --git a/test/tools/llvm-objdump/macho-weak-bind.test b/test/tools/llvm-objdump/macho-weak-bind.test new file mode 100644 index 00000000000..9f1aa33773e --- /dev/null +++ b/test/tools/llvm-objdump/macho-weak-bind.test @@ -0,0 +1,10 @@ +# RUN: llvm-objdump -macho -weak-bind -arch x86_64 \ +# RUN: %p/Inputs/weak-bind.macho-x86_64 | FileCheck %s + + +# CHECK: __DATA __data 0x100001018 pointer 0 __ZTISt12out_of_range +# CHECK: __DATA __data 0x100001020 pointer 0 __ZTISt12out_of_range +# CHECK: __DATA __data 0x100001028 pointer 0 __ZTISt12out_of_range +# CHECK: strong __ZdlPv +# CHECK: __DATA __data 0x100001018 pointer 0 __Znam +# CHECK: strong __Znwm diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 4d751099792..821cf82f418 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -2178,7 +2178,7 @@ private: SegInfo::SegInfo(const object::MachOObjectFile *Obj) { // Build table of sections so segIndex/offset pairs can be translated. - uint32_t CurSegIndex = 0; + uint32_t CurSegIndex = Obj->hasPageZeroSegment() ? 1 : 0; StringRef CurSegName; uint64_t CurSegAddress; for (const SectionRef &Section : Obj->sections()) { @@ -2253,3 +2253,118 @@ void llvm::printMachORebaseTable(const object::MachOObjectFile *Obj) { Entry.typeName().str().c_str()); } } + +static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { + StringRef DylibName; + switch (Ordinal) { + case MachO::BIND_SPECIAL_DYLIB_SELF: + return "this-image"; + case MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: + return "main-executable"; + case MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP: + return "flat-namespace"; + default: + Obj->getLibraryShortNameByIndex(Ordinal-1, DylibName); + return DylibName; + } +} + +//===----------------------------------------------------------------------===// +// bind table dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOBindTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address type " + "addend dylib symbol\n"; + for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable()) { + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: + // __DATA __got 0x00012010 pointer 0 libSystem ___stack_chk_guard + outs() << format("%-8s %-18s 0x%08" PRIX64 " %-8s %-8" PRId64 " %-20s", + SegmentName.str().c_str(), + SectionName.str().c_str(), + Address, + Entry.typeName().str().c_str(), + Entry.addend(), + ordinalName(Obj, Entry.ordinal())) + << Entry.symbolName(); + if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT) + outs() << " (weak_import)\n"; + else + outs() << "\n"; + } +} + +//===----------------------------------------------------------------------===// +// lazy bind table dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOLazyBindTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address " + "dylib symbol\n"; + for (const llvm::object::MachOBindEntry &Entry : Obj->lazyBindTable()) { + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: + // __DATA __got 0x00012010 libSystem ___stack_chk_guard + outs() << format("%-8s %-18s 0x%08" PRIX64 " %-20s", + SegmentName.str().c_str(), + SectionName.str().c_str(), + Address, + ordinalName(Obj, Entry.ordinal())) + << Entry.symbolName() << "\n"; + } +} + + +//===----------------------------------------------------------------------===// +// weak bind table dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOWeakBindTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address " + "type addend symbol\n"; + for (const llvm::object::MachOBindEntry &Entry : Obj->weakBindTable()) { + // Strong symbols don't have a location to update. + if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) { + outs() << " strong " + << Entry.symbolName() << "\n"; + continue; + } + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: + // __DATA __data 0x00001000 pointer 0 _foo + outs() << format("%-8s %-18s 0x%08" PRIX64 " %-8s %-8" PRId64 " ", + SegmentName.str().c_str(), + SectionName.str().c_str(), + Address, + Entry.typeName().str().c_str(), + Entry.addend()) + << Entry.symbolName() << "\n"; + } +} + + diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 790d08a1fc2..12cf1f72b09 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -84,6 +84,15 @@ ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols")); static cl::opt Rebase("rebase", cl::desc("Display mach-o rebasing info")); +static cl::opt +Bind("bind", cl::desc("Display mach-o binding info")); + +static cl::opt +LazyBind("lazy-bind", cl::desc("Display mach-o lazy binding info")); + +static cl::opt +WeakBind("weak-bind", cl::desc("Display mach-o weak binding info")); + static cl::opt MachOOpt("macho", cl::desc("Use MachO specific object file parser")); static cl::alias @@ -736,6 +745,38 @@ static void printRebaseTable(const ObjectFile *o) { } } +static void printBindTable(const ObjectFile *o) { + outs() << "Bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printLazyBindTable(const ObjectFile *o) { + outs() << "Lazy bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOLazyBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printWeakBindTable(const ObjectFile *o) { + outs() << "Weak bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOWeakBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} static void printPrivateFileHeader(const ObjectFile *o) { if (o->isELF()) { @@ -770,6 +811,12 @@ static void DumpObject(const ObjectFile *o) { printExportsTrie(o); if (Rebase) printRebaseTable(o); + if (Bind) + printBindTable(o); + if (LazyBind) + printLazyBindTable(o); + if (WeakBind) + printWeakBindTable(o); } /// @brief Dump each object file in \a a; @@ -853,7 +900,10 @@ int main(int argc, char **argv) { && !UnwindInfo && !PrivateHeaders && !ExportsTrie - && !Rebase) { + && !Rebase + && !Bind + && !LazyBind + && !WeakBind) { cl::PrintHelpMessage(); return 2; } diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index a328e635ee0..cae10354a91 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -37,6 +37,9 @@ void printCOFFUnwindInfo(const object::COFFObjectFile* o); void printMachOUnwindInfo(const object::MachOObjectFile* o); void printMachOExportsTrie(const object::MachOObjectFile* o); void printMachORebaseTable(const object::MachOObjectFile* o); +void printMachOBindTable(const object::MachOObjectFile* o); +void printMachOLazyBindTable(const object::MachOObjectFile* o); +void printMachOWeakBindTable(const object::MachOObjectFile* o); void printELFFileHeader(const object::ObjectFile *o); void printCOFFFileHeader(const object::ObjectFile *o); void printMachOFileHeader(const object::ObjectFile *o); -- 2.34.1