diff --git a/include/llvm/Object/MachO.h b/include/llvm/Object/MachO.h index 4ae28a529cc..019f777ed65 100644 --- a/include/llvm/Object/MachO.h +++ b/include/llvm/Object/MachO.h @@ -100,18 +100,58 @@ private: }; typedef content_iterator export_iterator; +// Segment info so SegIndex/SegOffset pairs in a Mach-O Bind or Rebase entry +// can be checked and translated. Only the SegIndex/SegOffset pairs from +// checked entries are to be used with the segmentName(), sectionName() and +// address() methods below. +class BindRebaseSegInfo { +public: + BindRebaseSegInfo(const object::MachOObjectFile *Obj); + + // Used to check a Mach-O Bind or Rebase entry for errors when iterating. + const char *checkSegAndOffset(int32_t SegIndex, uint64_t SegOffset, + bool endInvalid); + const char *checkCountAndSkip(uint32_t Count, uint32_t Skip, + uint8_t PointerSize, int32_t SegIndex, + uint64_t SegOffset); + // Used with valid SegIndex/SegOffset values from checked entries. + StringRef segmentName(int32_t SegIndex); + StringRef sectionName(int32_t SegIndex, uint64_t SegOffset); + uint64_t address(uint32_t SegIndex, uint64_t SegOffset); + +private: + struct SectionInfo { + uint64_t Address; + uint64_t Size; + StringRef SectionName; + StringRef SegmentName; + uint64_t OffsetInSegment; + uint64_t SegmentStartAddress; + int32_t SegmentIndex; + }; + const SectionInfo &findSection(int32_t SegIndex, uint64_t SegOffset); + SmallVector Sections; + int32_t MaxSegIndex; +}; + /// MachORebaseEntry encapsulates the current state in the decompression of /// rebasing opcodes. This allows you to iterate through the compressed table of /// rebasing using: -/// for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable()) { +/// Error Err; +/// for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable(&Err)) { /// } +/// if (Err) { report error ... class MachORebaseEntry { public: - MachORebaseEntry(ArrayRef opcodes, bool is64Bit); + MachORebaseEntry(Error *Err, const MachOObjectFile *O, + ArrayRef opcodes, bool is64Bit); uint32_t segmentIndex() const; uint64_t segmentOffset() const; StringRef typeName() const; + StringRef segmentName() const; + StringRef sectionName() const; + uint64_t address() const; bool operator==(const MachORebaseEntry &) const; @@ -123,6 +163,8 @@ private: void moveToEnd(); uint64_t readULEB128(); + Error *E; + const MachOObjectFile *O; ArrayRef Opcodes; const uint8_t *Ptr; uint64_t SegmentOffset; @@ -150,7 +192,7 @@ public: MachOBindEntry(Error *Err, const MachOObjectFile *O, ArrayRef Opcodes, bool is64Bit, MachOBindEntry::Kind); - uint32_t segmentIndex() const; + int32_t segmentIndex() const; uint64_t segmentOffset() const; StringRef typeName() const; StringRef symbolName() const; @@ -158,6 +200,10 @@ public: int64_t addend() const; int ordinal() const; + StringRef segmentName() const; + StringRef sectionName() const; + uint64_t address() const; + bool operator==(const MachOBindEntry &) const; void moveNext(); @@ -166,16 +212,17 @@ private: friend class MachOObjectFile; void moveToFirst(); void moveToEnd(); - uint64_t readULEB128(); - int64_t readSLEB128(); + uint64_t readULEB128(const char **error); + int64_t readSLEB128(const char **error); Error *E; const MachOObjectFile *O; ArrayRef Opcodes; const uint8_t *Ptr; uint64_t SegmentOffset; - uint32_t SegmentIndex; + int32_t SegmentIndex; StringRef SymbolName; + bool LibraryOrdinalSet; int Ordinal; uint32_t Flags; int64_t Addend; @@ -291,28 +338,63 @@ public: static iterator_range exports(ArrayRef Trie); /// For use iterating over all rebase table entries. - iterator_range rebaseTable() const; + iterator_range rebaseTable(Error &Err); - /// For use examining rebase opcodes not in a MachOObjectFile. - static iterator_range rebaseTable(ArrayRef Opcodes, + /// For use examining rebase opcodes in a MachOObjectFile. + static iterator_range rebaseTable(Error &Err, + MachOObjectFile *O, + ArrayRef Opcodes, bool is64); /// For use iterating over all bind table entries. - iterator_range bindTable(Error &Err) const; + iterator_range bindTable(Error &Err); /// For use iterating over all lazy bind table entries. - iterator_range lazyBindTable(Error &Err) const; + iterator_range lazyBindTable(Error &Err); /// For use iterating over all weak bind table entries. - iterator_range weakBindTable(Error &Err) const; + iterator_range weakBindTable(Error &Err); - /// For use examining bind opcodes not in a MachOObjectFile. + /// For use examining bind opcodes in a MachOObjectFile. static iterator_range bindTable(Error &Err, - const MachOObjectFile *O, + MachOObjectFile *O, ArrayRef Opcodes, bool is64, MachOBindEntry::Kind); + /// For use with a SegIndex,SegOffset pair in MachOBindEntry::moveNext() to + /// validate a MachOBindEntry. + const char *BindEntryCheckSegAndOffset(int32_t SegIndex, uint64_t SegOffset, + bool endInvalid) const { + return BindRebaseSectionTable->checkSegAndOffset(SegIndex, SegOffset, + endInvalid); + } + /// For use in MachOBindEntry::moveNext() to validate a MachOBindEntry for + /// the BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB opcode. + const char *BindEntryCheckCountAndSkip(uint32_t Count, uint32_t Skip, + uint8_t PointerSize, int32_t SegIndex, + uint64_t SegOffset) const { + return BindRebaseSectionTable->checkCountAndSkip(Count, Skip, PointerSize, + SegIndex, SegOffset); + } + + /// For use with the SegIndex of a checked Mach-O Bind or Rebase entry to + /// get the segment name. + StringRef BindRebaseSegmentName(int32_t SegIndex) const { + return BindRebaseSectionTable->segmentName(SegIndex); + } + + /// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or + /// Rebase entry to get the section name. + StringRef BindRebaseSectionName(uint32_t SegIndex, uint64_t SegOffset) const { + return BindRebaseSectionTable->sectionName(SegIndex, SegOffset); + } + + /// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or + /// Rebase entry to get the address. + uint64_t BindRebaseAddress(uint32_t SegIndex, uint64_t SegOffset) const { + return BindRebaseSectionTable->address(SegIndex, SegOffset); + } // 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 @@ -519,6 +601,7 @@ private: using BuildToolList = SmallVector; BuildToolList BuildTools; mutable LibraryShortName LibrariesShortNames; + std::unique_ptr BindRebaseSectionTable; const char *SymtabLoadCmd; const char *DysymtabLoadCmd; const char *DataInCodeLoadCmd; diff --git a/include/llvm/Support/LEB128.h b/include/llvm/Support/LEB128.h index edb923bb659..ff775f3b7b3 100644 --- a/include/llvm/Support/LEB128.h +++ b/include/llvm/Support/LEB128.h @@ -113,11 +113,30 @@ inline unsigned encodeULEB128(uint64_t Value, uint8_t *p, /// Utility function to decode a ULEB128 value. -inline uint64_t decodeULEB128(const uint8_t *p, unsigned *n = nullptr) { +inline uint64_t decodeULEB128(const uint8_t *p, unsigned *n = nullptr, + const uint8_t *end = nullptr, + const char **error = nullptr) { const uint8_t *orig_p = p; uint64_t Value = 0; unsigned Shift = 0; + if(error) + *error = nullptr; do { + if(end && p == end){ + if(error) + *error = "malformed uleb128, extends past end"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } + uint64_t Slice = *p & 0x7f; + if(Shift >= 64 || Slice << Shift >> Shift != Slice){ + if(error) + *error = "uleb128 too big for uint64"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } Value += uint64_t(*p & 0x7f) << Shift; Shift += 7; } while (*p++ >= 128); @@ -127,12 +146,21 @@ inline uint64_t decodeULEB128(const uint8_t *p, unsigned *n = nullptr) { } /// Utility function to decode a SLEB128 value. -inline int64_t decodeSLEB128(const uint8_t *p, unsigned *n = nullptr) { +inline int64_t decodeSLEB128(const uint8_t *p, unsigned *n = nullptr, + const uint8_t *end = nullptr, + const char **error = nullptr) { const uint8_t *orig_p = p; int64_t Value = 0; unsigned Shift = 0; uint8_t Byte; do { + if(end && p == end){ + if(error) + *error = "malformed sleb128, extends past end"; + if (n) + *n = (unsigned)(p - orig_p); + return 0; + } Byte = *p++; Value |= ((Byte & 0x7f) << Shift); Shift += 7; diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index 25f9a3d93dc..0ca1fea6179 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -2755,9 +2755,10 @@ iterator_range MachOObjectFile::exports() const { return exports(getDyldInfoExportsTrie()); } -MachORebaseEntry::MachORebaseEntry(ArrayRef Bytes, bool is64Bit) - : Opcodes(Bytes), Ptr(Bytes.begin()), SegmentOffset(0), SegmentIndex(0), - RemainingLoopCount(0), AdvanceAmount(0), RebaseType(0), +MachORebaseEntry::MachORebaseEntry(Error *E, const MachOObjectFile *O, + ArrayRef Bytes, bool is64Bit) + : E(E), O(O), Opcodes(Bytes), Ptr(Bytes.begin()), SegmentOffset(0), + SegmentIndex(0), RemainingLoopCount(0), AdvanceAmount(0), RebaseType(0), PointerSize(is64Bit ? 8 : 4), Malformed(false), Done(false) {} void MachORebaseEntry::moveToFirst() { @@ -2903,6 +2904,24 @@ StringRef MachORebaseEntry::typeName() const { return "unknown"; } +// For use with the SegIndex of a checked Mach-O Rebase entry +// to get the segment name. +StringRef MachORebaseEntry::segmentName() const { + return O->BindRebaseSegmentName(SegmentIndex); +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Rebase entry +// to get the section name. +StringRef MachORebaseEntry::sectionName() const { + return O->BindRebaseSectionName(SegmentIndex, SegmentOffset); +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Rebase entry +// to get the address. +uint64_t MachORebaseEntry::address() const { + return O->BindRebaseAddress(SegmentIndex, SegmentOffset); +} + bool MachORebaseEntry::operator==(const MachORebaseEntry &Other) const { #ifdef EXPENSIVE_CHECKS assert(Opcodes == Other.Opcodes && "compare iterators of different files"); @@ -2915,26 +2934,30 @@ bool MachORebaseEntry::operator==(const MachORebaseEntry &Other) const { } iterator_range -MachOObjectFile::rebaseTable(ArrayRef Opcodes, bool is64) { - MachORebaseEntry Start(Opcodes, is64); +MachOObjectFile::rebaseTable(Error &Err, MachOObjectFile *O, + ArrayRef Opcodes, bool is64) { + if (O->BindRebaseSectionTable == nullptr) + O->BindRebaseSectionTable = llvm::make_unique(O); + MachORebaseEntry Start(&Err, O, Opcodes, is64); Start.moveToFirst(); - MachORebaseEntry Finish(Opcodes, is64); + MachORebaseEntry Finish(&Err, O, Opcodes, is64); Finish.moveToEnd(); return make_range(rebase_iterator(Start), rebase_iterator(Finish)); } -iterator_range MachOObjectFile::rebaseTable() const { - return rebaseTable(getDyldInfoRebaseOpcodes(), is64Bit()); +iterator_range MachOObjectFile::rebaseTable(Error &Err) { + return rebaseTable(Err, this, getDyldInfoRebaseOpcodes(), is64Bit()); } MachOBindEntry::MachOBindEntry(Error *E, const MachOObjectFile *O, ArrayRef Bytes, bool is64Bit, Kind BK) - : E(E), O(O), 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) {} + : E(E), O(O), Opcodes(Bytes), Ptr(Bytes.begin()), SegmentOffset(0), + SegmentIndex(-1), LibraryOrdinalSet(false), 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(); @@ -2968,6 +2991,8 @@ void MachOBindEntry::moveNext() { uint8_t Opcode = Byte & MachO::BIND_OPCODE_MASK; int8_t SignExtended; const uint8_t *SymStart; + uint32_t Count, Skip; + const char *error = nullptr; switch (Opcode) { case MachO::BIND_OPCODE_DONE: if (TableKind == Kind::Lazy) { @@ -2989,6 +3014,7 @@ void MachOBindEntry::moveNext() { break; case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: Ordinal = ImmValue; + LibraryOrdinalSet = true; if (ImmValue > O->getLibraryCount()) { *E = malformedError("for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB bad " "library ordinal: " + Twine((int)ImmValue) + " (max " + @@ -3003,7 +3029,23 @@ void MachOBindEntry::moveNext() { << "Ordinal=" << Ordinal << "\n"); break; case MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - Ordinal = readULEB128(); + Ordinal = readULEB128(&error); + LibraryOrdinalSet = true; + if (error) { + *E = malformedError("for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (Ordinal > (int)O->getLibraryCount()) { + *E = malformedError("for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB bad " + "library ordinal: " + Twine((int)Ordinal) + " (max " + + Twine((int)O->getLibraryCount()) + ") for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } DEBUG_WITH_TYPE( "mach-o-bind", llvm::dbgs() << "BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: " @@ -3013,6 +3055,14 @@ void MachOBindEntry::moveNext() { if (ImmValue) { SignExtended = MachO::BIND_OPCODE_MASK | ImmValue; Ordinal = SignExtended; + LibraryOrdinalSet = true; + if (Ordinal < MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP) { + *E = malformedError("for BIND_OPCODE_SET_DYLIB_SPECIAL_IMM unknown " + "special ordinal: " + Twine((int)Ordinal) + " for opcode at: " + "0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } } else Ordinal = 0; DEBUG_WITH_TYPE( @@ -3023,9 +3073,16 @@ void MachOBindEntry::moveNext() { case MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: Flags = ImmValue; SymStart = Ptr; - while (*Ptr) { + while (*Ptr && (Ptr < Opcodes.end())) { ++Ptr; } + if (Ptr == Opcodes.end()) { + *E = malformedError("for BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM " + "symbol name extends past opcodes for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } SymbolName = StringRef(reinterpret_cast(SymStart), Ptr-SymStart); ++Ptr; @@ -3040,13 +3097,27 @@ void MachOBindEntry::moveNext() { break; case MachO::BIND_OPCODE_SET_TYPE_IMM: BindType = ImmValue; + if (ImmValue > MachO::BIND_TYPE_TEXT_PCREL32) { + *E = malformedError("for BIND_OPCODE_SET_TYPE_IMM bad bind type: " + + Twine((int)ImmValue) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } 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(); + Addend = readSLEB128(&error); + if (error) { + *E = malformedError("for BIND_OPCODE_SET_ADDEND_SLEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } if (TableKind == Kind::Lazy) Malformed = true; DEBUG_WITH_TYPE( @@ -3056,7 +3127,22 @@ void MachOBindEntry::moveNext() { break; case MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: SegmentIndex = ImmValue; - SegmentOffset = readULEB128(); + SegmentOffset = readULEB128(&error); + if (error) { + *E = malformedError("for BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset, true); + if (error) { + *E = malformedError("for BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } DEBUG_WITH_TYPE( "mach-o-bind", llvm::dbgs() << "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: " @@ -3065,7 +3151,22 @@ void MachOBindEntry::moveNext() { << "\n"); break; case MachO::BIND_OPCODE_ADD_ADDR_ULEB: - SegmentOffset += readULEB128(); + SegmentOffset += readULEB128(&error); + if (error) { + *E = malformedError("for BIND_OPCODE_ADD_ADDR_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset, true); + if (error) { + *E = malformedError("for BIND_OPCODE_ADD_ADDR_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } DEBUG_WITH_TYPE("mach-o-bind", llvm::dbgs() << "BIND_OPCODE_ADD_ADDR_ULEB: " << format("SegmentOffset=0x%06X", @@ -3074,13 +3175,75 @@ void MachOBindEntry::moveNext() { case MachO::BIND_OPCODE_DO_BIND: AdvanceAmount = PointerSize; RemainingLoopCount = 0; + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset, true); + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND " + Twine(error) + + " for opcode at: 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (SymbolName == StringRef()) { + *E = malformedError("for BIND_OPCODE_DO_BIND missing preceding " + "BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (!LibraryOrdinalSet && TableKind != Kind::Weak) { + *E = malformedError("for BIND_OPCODE_DO_BIND missing preceding " + "BIND_OPCODE_SET_DYLIB_ORDINAL_* for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } 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() + PointerSize; + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset, true); + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (SymbolName == StringRef()) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing " + "preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM for opcode " + "at: 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (!LibraryOrdinalSet && TableKind != Kind::Weak) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing " + "preceding BIND_OPCODE_SET_DYLIB_ORDINAL_* for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + AdvanceAmount = readULEB128(&error) + PointerSize; + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + // Note, this is not really an error until the next bind but make no sense + // for a BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB to not be followed by another + // bind operation. + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset + + AdvanceAmount, false); + if (error) { + *E = malformedError("for BIND_OPCODE_ADD_ADDR_ULEB (after adding " + "ULEB) " + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } RemainingLoopCount = 0; if (TableKind == Kind::Lazy) Malformed = true; @@ -3093,8 +3256,40 @@ void MachOBindEntry::moveNext() { << "\n"); return; case MachO::BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset, true); + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (SymbolName == StringRef()) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED " + "missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM for " + "opcode at: 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (!LibraryOrdinalSet && TableKind != Kind::Weak) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED " + "missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL_* for opcode " + "at: 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } AdvanceAmount = ImmValue * PointerSize + PointerSize; RemainingLoopCount = 0; + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset + + AdvanceAmount, false); + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED " + " (after adding immediate times the pointer size) " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } if (TableKind == Kind::Lazy) Malformed = true; DEBUG_WITH_TYPE("mach-o-bind", @@ -3104,8 +3299,58 @@ void MachOBindEntry::moveNext() { SegmentOffset) << "\n"); return; case MachO::BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - RemainingLoopCount = readULEB128() - 1; - AdvanceAmount = readULEB128() + PointerSize; + Count = readULEB128(&error); + if (Count != 0) + RemainingLoopCount = Count - 1; + else + RemainingLoopCount = 0; + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB " + " (count value) " + Twine(error) + " for opcode at" + ": 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + Skip = readULEB128(&error); + AdvanceAmount = Skip + PointerSize; + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB " + " (skip value) " + Twine(error) + " for opcode at" + ": 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + error = O->BindEntryCheckSegAndOffset(SegmentIndex, SegmentOffset, true); + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (SymbolName == StringRef()) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB " + "missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM for " + "opcode at: 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + if (!LibraryOrdinalSet && TableKind != Kind::Weak) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB " + "missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL_* for opcode " + "at: 0x" + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } + error = O->BindEntryCheckCountAndSkip(Count, Skip, PointerSize, + SegmentIndex, SegmentOffset); + if (error) { + *E = malformedError("for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB " + + Twine(error) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; + } if (TableKind == Kind::Lazy) Malformed = true; DEBUG_WITH_TYPE( @@ -3118,13 +3363,18 @@ void MachOBindEntry::moveNext() { return; default: Malformed = true; + *E = malformedError("bad bind info (bad opcode value 0x" + + utohexstr(Opcode) + " for opcode at: 0x" + + utohexstr(OpcodeStart - Opcodes.begin())); + moveToEnd(); + return; } } } -uint64_t MachOBindEntry::readULEB128() { +uint64_t MachOBindEntry::readULEB128(const char **error) { unsigned Count; - uint64_t Result = decodeULEB128(Ptr, &Count); + uint64_t Result = decodeULEB128(Ptr, &Count, Opcodes.end(), error); Ptr += Count; if (Ptr > Opcodes.end()) { Ptr = Opcodes.end(); @@ -3133,9 +3383,9 @@ uint64_t MachOBindEntry::readULEB128() { return Result; } -int64_t MachOBindEntry::readSLEB128() { +int64_t MachOBindEntry::readSLEB128(const char **error) { unsigned Count; - int64_t Result = decodeSLEB128(Ptr, &Count); + int64_t Result = decodeSLEB128(Ptr, &Count, Opcodes.end(), error); Ptr += Count; if (Ptr > Opcodes.end()) { Ptr = Opcodes.end(); @@ -3144,7 +3394,7 @@ int64_t MachOBindEntry::readSLEB128() { return Result; } -uint32_t MachOBindEntry::segmentIndex() const { return SegmentIndex; } +int32_t MachOBindEntry::segmentIndex() const { return SegmentIndex; } uint64_t MachOBindEntry::segmentOffset() const { return SegmentOffset; } @@ -3168,6 +3418,24 @@ uint32_t MachOBindEntry::flags() const { return Flags; } int MachOBindEntry::ordinal() const { return Ordinal; } +// For use with the SegIndex of a checked Mach-O Bind entry +// to get the segment name. +StringRef MachOBindEntry::segmentName() const { + return O->BindRebaseSegmentName(SegmentIndex); +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind entry +// to get the section name. +StringRef MachOBindEntry::sectionName() const { + return O->BindRebaseSectionName(SegmentIndex, SegmentOffset); +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind entry +// to get the address. +uint64_t MachOBindEntry::address() const { + return O->BindRebaseAddress(SegmentIndex, SegmentOffset); +} + bool MachOBindEntry::operator==(const MachOBindEntry &Other) const { #ifdef EXPENSIVE_CHECKS assert(Opcodes == Other.Opcodes && "compare iterators of different files"); @@ -3179,10 +3447,119 @@ bool MachOBindEntry::operator==(const MachOBindEntry &Other) const { (Done == Other.Done); } +// Build table of sections so SegIndex/SegOffset pairs can be translated. +BindRebaseSegInfo::BindRebaseSegInfo(const object::MachOObjectFile *Obj) { + uint32_t CurSegIndex = Obj->hasPageZeroSegment() ? 1 : 0; + StringRef CurSegName; + uint64_t CurSegAddress; + for (const SectionRef &Section : Obj->sections()) { + SectionInfo Info; + Section.getName(Info.SectionName); + Info.Address = Section.getAddress(); + Info.Size = Section.getSize(); + Info.SegmentName = + Obj->getSectionFinalSegmentName(Section.getRawDataRefImpl()); + if (!Info.SegmentName.equals(CurSegName)) { + ++CurSegIndex; + CurSegName = Info.SegmentName; + CurSegAddress = Info.Address; + } + Info.SegmentIndex = CurSegIndex - 1; + Info.OffsetInSegment = Info.Address - CurSegAddress; + Info.SegmentStartAddress = CurSegAddress; + Sections.push_back(Info); + } + MaxSegIndex = CurSegIndex; +} + +// For use with a SegIndex,SegOffset pair in MachOBindEntry::moveNext() to +// validate a MachOBindEntry. +const char * BindRebaseSegInfo::checkSegAndOffset(int32_t SegIndex, + uint64_t SegOffset, + bool endInvalid) { + if (SegIndex == -1) + return "missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"; + if (SegIndex >= MaxSegIndex) + return "bad segIndex (too large)"; + for (const SectionInfo &SI : Sections) { + if (SI.SegmentIndex != SegIndex) + continue; + if (SI.OffsetInSegment > SegOffset) + continue; + if (SegOffset > (SI.OffsetInSegment + SI.Size)) + continue; + if (endInvalid && SegOffset >= (SI.OffsetInSegment + SI.Size)) + continue; + return nullptr; + } + return "bad segOffset, too large"; +} + +// For use in MachOBindEntry::moveNext() to validate a MachOBindEntry for +// the BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB opcode. The SegIndex +// and SegOffset must have been already checked. +const char * BindRebaseSegInfo::checkCountAndSkip(uint32_t Count, uint32_t Skip, + uint8_t PointerSize, + int32_t SegIndex, + uint64_t SegOffset) { + const SectionInfo &SI = findSection(SegIndex, SegOffset); + uint64_t addr = SI.SegmentStartAddress + SegOffset; + if (addr >= SI.Address + SI.Size) + return "bad segOffset, too large"; + uint64_t i = 0; + if (Count > 1) + i = (Skip + PointerSize) * (Count - 1); + if (addr + i >= SI.Address + SI.Size) + return "bad count and skip, too large"; + return nullptr; +} + +// For use with the SegIndex of a checked Mach-O Bind or Rebase entry +// to get the segment name. +StringRef BindRebaseSegInfo::segmentName(int32_t SegIndex) { + for (const SectionInfo &SI : Sections) { + if (SI.SegmentIndex == SegIndex) + return SI.SegmentName; + } + llvm_unreachable("invalid SegIndex"); +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or Rebase +// to get the SectionInfo. +const BindRebaseSegInfo::SectionInfo &BindRebaseSegInfo::findSection( + int32_t SegIndex, uint64_t SegOffset) { + for (const SectionInfo &SI : Sections) { + if (SI.SegmentIndex != SegIndex) + continue; + if (SI.OffsetInSegment > SegOffset) + continue; + if (SegOffset >= (SI.OffsetInSegment + SI.Size)) + continue; + return SI; + } + llvm_unreachable("SegIndex and SegOffset not in any section"); +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or Rebase +// entry to get the section name. +StringRef BindRebaseSegInfo::sectionName(int32_t SegIndex, + uint64_t SegOffset) { + return findSection(SegIndex, SegOffset).SectionName; +} + +// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or Rebase +// entry to get the address. +uint64_t BindRebaseSegInfo::address(uint32_t SegIndex, uint64_t OffsetInSeg) { + const SectionInfo &SI = findSection(SegIndex, OffsetInSeg); + return SI.SegmentStartAddress + OffsetInSeg; +} + iterator_range -MachOObjectFile::bindTable(Error &Err, const MachOObjectFile *O, +MachOObjectFile::bindTable(Error &Err, MachOObjectFile *O, ArrayRef Opcodes, bool is64, MachOBindEntry::Kind BKind) { + if (O->BindRebaseSectionTable == nullptr) + O->BindRebaseSectionTable = llvm::make_unique(O); MachOBindEntry Start(&Err, O, Opcodes, is64, BKind); Start.moveToFirst(); @@ -3192,17 +3569,17 @@ MachOObjectFile::bindTable(Error &Err, const MachOObjectFile *O, return make_range(bind_iterator(Start), bind_iterator(Finish)); } -iterator_range MachOObjectFile::bindTable(Error &Err) const { +iterator_range MachOObjectFile::bindTable(Error &Err) { return bindTable(Err, this, getDyldInfoBindOpcodes(), is64Bit(), MachOBindEntry::Kind::Regular); } -iterator_range MachOObjectFile::lazyBindTable(Error &Err) const { +iterator_range MachOObjectFile::lazyBindTable(Error &Err) { return bindTable(Err, this, getDyldInfoLazyBindOpcodes(), is64Bit(), MachOBindEntry::Kind::Lazy); } -iterator_range MachOObjectFile::weakBindTable(Error &Err) const { +iterator_range MachOObjectFile::weakBindTable(Error &Err) { return bindTable(Err, this, getDyldInfoWeakBindOpcodes(), is64Bit(), MachOBindEntry::Kind::Weak); } diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-add-addr-imm-scaled b/test/tools/llvm-objdump/Inputs/macho-bind-add-addr-imm-scaled new file mode 100755 index 00000000000..2180437408c Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-add-addr-imm-scaled differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-add_addr_uleb b/test/tools/llvm-objdump/Inputs/macho-bind-add_addr_uleb new file mode 100755 index 00000000000..fc950db155a Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-add_addr_uleb differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-bad-opcode-value b/test/tools/llvm-objdump/Inputs/macho-bind-bad-opcode-value new file mode 100755 index 00000000000..c9195314c8e Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-bad-opcode-value differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-bind-add-addr-uleb b/test/tools/llvm-objdump/Inputs/macho-bind-bind-add-addr-uleb new file mode 100755 index 00000000000..11abd6246b8 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-bind-add-addr-uleb differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-do-bind-no-segIndex b/test/tools/llvm-objdump/Inputs/macho-bind-do-bind-no-segIndex new file mode 100755 index 00000000000..cc4f09708c4 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-do-bind-no-segIndex differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb new file mode 100755 index 00000000000..7769195d44c Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb-malformed-uleb128 b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb-malformed-uleb128 new file mode 100755 index 00000000000..0d5410e976e Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb-malformed-uleb128 differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb-too-big b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb-too-big new file mode 100755 index 00000000000..40564b5a262 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-ordinal-uleb-too-big differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-dylib-special-imm b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-special-imm new file mode 100755 index 00000000000..09bf10ded89 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-dylib-special-imm differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-seg-too-big b/test/tools/llvm-objdump/Inputs/macho-bind-seg-too-big new file mode 100755 index 00000000000..20be9957919 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-seg-too-big differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-segoff-too-big b/test/tools/llvm-objdump/Inputs/macho-bind-segoff-too-big new file mode 100755 index 00000000000..3f8e5ee8384 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-segoff-too-big differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-set-addend-sleb b/test/tools/llvm-objdump/Inputs/macho-bind-set-addend-sleb new file mode 100755 index 00000000000..726b96d3de3 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-set-addend-sleb differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-set-symbol b/test/tools/llvm-objdump/Inputs/macho-bind-set-symbol new file mode 100755 index 00000000000..b8201c3ad19 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-set-symbol differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-set-type-imm b/test/tools/llvm-objdump/Inputs/macho-bind-set-type-imm new file mode 100755 index 00000000000..002057e6b86 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-set-type-imm differ diff --git a/test/tools/llvm-objdump/Inputs/macho-bind-uleb-times-skipping-uleb b/test/tools/llvm-objdump/Inputs/macho-bind-uleb-times-skipping-uleb new file mode 100755 index 00000000000..81ab8130f66 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-bind-uleb-times-skipping-uleb differ diff --git a/test/tools/llvm-objdump/Inputs/macho-do-bind-no-dylib-ordinal b/test/tools/llvm-objdump/Inputs/macho-do-bind-no-dylib-ordinal new file mode 100755 index 00000000000..77daede7868 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-do-bind-no-dylib-ordinal differ diff --git a/test/tools/llvm-objdump/Inputs/macho-do-bind-no-symbol b/test/tools/llvm-objdump/Inputs/macho-do-bind-no-symbol new file mode 100755 index 00000000000..0592b9bfe40 Binary files /dev/null and b/test/tools/llvm-objdump/Inputs/macho-do-bind-no-symbol differ diff --git a/test/tools/llvm-objdump/X86/malformed-machos.test b/test/tools/llvm-objdump/X86/malformed-machos.test index 0a9ac02f0eb..292666a3725 100644 --- a/test/tools/llvm-objdump/X86/malformed-machos.test +++ b/test/tools/llvm-objdump/X86/malformed-machos.test @@ -64,5 +64,5 @@ INVALID-SYMBOL-STRX-UNIVERSAL: macho-invalid-symbol-strx-universal' (for archite RUN: not llvm-objdump -macho -disassemble %p/Inputs/macho-invalid-symbol-lib_ordinal 2>&1 | FileCheck -check-prefix INVALID-SYMBOL-LIB_ORDINAL %s INVALID-SYMBOL-LIB_ORDINAL: macho-invalid-symbol-lib_ordinal': truncated or malformed object (bad library ordinal: 7 for symbol at index 2) -RUN: llvm-objdump -macho -objc-meta-data %p/Inputs/macho-invalid-bind-entry | FileCheck -check-prefix INVALID-BIND-ENTRY %s -INVALID-BIND-ENTRY: 0000000100020c90 0x2d0409887202e473 +RUN: not llvm-objdump -macho -objc-meta-data %p/Inputs/macho-invalid-bind-entry 2>&1 | FileCheck -check-prefix INVALID-BIND-ENTRY %s +INVALID-BIND-ENTRY: macho-invalid-bind-entry': truncated or malformed object (for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB bad library ordinal: 83 (max 0) for opcode at: 0x0) diff --git a/test/tools/llvm-objdump/macho-bad-bind.test b/test/tools/llvm-objdump/macho-bad-bind.test new file mode 100644 index 00000000000..dea86bb6514 --- /dev/null +++ b/test/tools/llvm-objdump/macho-bad-bind.test @@ -0,0 +1,50 @@ +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-dylib-ordinal-uleb 2>&1 | FileCheck -check-prefix DYLIB-ORDINAL-ULEB %s +DYLIB-ORDINAL-ULEB: macho-bind-dylib-ordinal-uleb': truncated or malformed object (for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB bad library ordinal: 355 (max 1) for opcode at: 0x0) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-dylib-ordinal-uleb-malformed-uleb128 2>&1 | FileCheck -check-prefix DYLIB-ORDINAL-ULEB-MALFORMED-ULEB128 %s +DYLIB-ORDINAL-ULEB-MALFORMED-ULEB128: macho-bind-dylib-ordinal-uleb-malformed-uleb128': truncated or malformed object (for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB malformed uleb128, extends past end for opcode at: 0x0) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-dylib-ordinal-uleb-too-big 2>&1 | FileCheck -check-prefix DYLIB-ORDINAL-ULEB-TOO-BIG %s +DYLIB-ORDINAL-ULEB-TOO-BIG: macho-bind-dylib-ordinal-uleb-too-big': truncated or malformed object (for BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB uleb128 too big for uint64 for opcode at: 0x0) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-dylib-special-imm 2>&1 | FileCheck -check-prefix DYLIB-SPECIAL-IMM %s +DYLIB-SPECIAL-IMM: macho-bind-dylib-special-imm': truncated or malformed object (for BIND_OPCODE_SET_DYLIB_SPECIAL_IMM unknown special ordinal: -5 for opcode at: 0x0) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-set-symbol 2>&1 | FileCheck -check-prefix BIND-SET-SYMBOL %s +BIND-SET-SYMBOL: macho-bind-set-symbol': truncated or malformed object (for BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM symbol name extends past opcodes for opcode at: 0x2) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-set-type-imm 2>&1 | FileCheck -check-prefix SET-TYPE-IMM %s +SET-TYPE-IMM: macho-bind-set-type-imm': truncated or malformed object (for BIND_OPCODE_SET_TYPE_IMM bad bind type: 5 for opcode at: 0x14) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-set-addend-sleb 2>&1 | FileCheck -check-prefix SET-ADDEND-SLEB %s +SET-ADDEND-SLEB: macho-bind-set-addend-sleb': truncated or malformed object (for BIND_OPCODE_SET_ADDEND_SLEB malformed sleb128, extends past end for opcode at: 0x14) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-seg-too-big 2>&1 | FileCheck -check-prefix SEG-TOO-BIG %s +SEG-TOO-BIG: macho-bind-seg-too-big': truncated or malformed object (for BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB bad segIndex (too large) for opcode at: 0x15) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-segoff-too-big 2>&1 | FileCheck -check-prefix SEGOFF-TOO-BIG %s +SEGOFF-TOO-BIG: macho-bind-segoff-too-big': truncated or malformed object (for BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB bad segOffset, too large for opcode at: 0x15) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-add_addr_uleb 2>&1 | FileCheck -check-prefix ADD_ADDR_ULEB %s +ADD_ADDR_ULEB: macho-bind-add_addr_uleb': truncated or malformed object (for BIND_OPCODE_ADD_ADDR_ULEB bad segOffset, too large for opcode at: 0x17) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-do-bind-no-segIndex 2>&1 | FileCheck -check-prefix BIND-NO-SEGINDEX %s +BIND-NO-SEGINDEX: macho-bind-do-bind-no-segIndex': truncated or malformed object (for BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB for opcode at: 0x15) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-bind-add-addr-uleb 2>&1 | FileCheck -check-prefix ADD-ADDR-ULEB %s +ADD-ADDR-ULEB: macho-bind-bind-add-addr-uleb': truncated or malformed object (for BIND_OPCODE_ADD_ADDR_ULEB (after adding ULEB) bad segOffset, too large for opcode at: 0x18) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-add-addr-imm-scaled 2>&1 | FileCheck -check-prefix ADD-ADDR-IMM-SCALED %s +ADD-ADDR-IMM-SCALED: macho-bind-add-addr-imm-scaled': truncated or malformed object (for BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED (after adding immediate times the pointer size) bad segOffset, too large for opcode at: 0x17) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-uleb-times-skipping-uleb 2>&1 | FileCheck -check-prefix ULEB-TIMES-SKIPPING-ULEB %s +ULEB-TIMES-SKIPPING-ULEB: macho-bind-uleb-times-skipping-uleb': truncated or malformed object (for BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB bad count and skip, too large for opcode at: 0x17) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-do-bind-no-symbol 2>&1 | FileCheck -check-prefix DO-BIND-NO-SYMBOL %s +DO-BIND-NO-SYMBOL: macho-do-bind-no-symbol': truncated or malformed object (for BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM for opcode at: 0x5) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-do-bind-no-dylib-ordinal 2>&1 | FileCheck -check-prefix DO-BIND-NO-DYLIB-ORDINAL %s +DO-BIND-NO-DYLIB-ORDINAL: macho-do-bind-no-dylib-ordinal': truncated or malformed object (for BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL_* for opcode at: 0x15) + +RUN: not llvm-objdump -macho -bind %p/Inputs/macho-bind-bad-opcode-value 2>&1 | FileCheck -check-prefix BAD-OPCODE-VALUE %s +BAD-OPCODE-VALUE: macho-bind-bad-opcode-value': truncated or malformed object (bad bind info (bad opcode value 0xD0 for opcode at: 0x18) diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 7fe811b3b8f..9e02951a4a9 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -9391,117 +9391,21 @@ void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { // rebase table dumping //===----------------------------------------------------------------------===// -namespace { -class SegInfo { -public: - SegInfo(const object::MachOObjectFile *Obj); - - StringRef segmentName(uint32_t SegIndex); - StringRef sectionName(uint32_t SegIndex, uint64_t SegOffset); - uint64_t address(uint32_t SegIndex, uint64_t SegOffset); - bool isValidSegIndexAndOffset(uint32_t SegIndex, uint64_t SegOffset); - -private: - struct SectionInfo { - uint64_t Address; - uint64_t Size; - StringRef SectionName; - StringRef SegmentName; - uint64_t OffsetInSegment; - uint64_t SegmentStartAddress; - uint32_t SegmentIndex; - }; - const SectionInfo &findSection(uint32_t SegIndex, uint64_t SegOffset); - SmallVector Sections; -}; -} - -SegInfo::SegInfo(const object::MachOObjectFile *Obj) { - // Build table of sections so segIndex/offset pairs can be translated. - uint32_t CurSegIndex = Obj->hasPageZeroSegment() ? 1 : 0; - StringRef CurSegName; - uint64_t CurSegAddress; - for (const SectionRef &Section : Obj->sections()) { - SectionInfo Info; - error(Section.getName(Info.SectionName)); - Info.Address = Section.getAddress(); - Info.Size = Section.getSize(); - Info.SegmentName = - Obj->getSectionFinalSegmentName(Section.getRawDataRefImpl()); - if (!Info.SegmentName.equals(CurSegName)) { - ++CurSegIndex; - CurSegName = Info.SegmentName; - CurSegAddress = Info.Address; - } - Info.SegmentIndex = CurSegIndex - 1; - Info.OffsetInSegment = Info.Address - CurSegAddress; - Info.SegmentStartAddress = CurSegAddress; - Sections.push_back(Info); - } -} - -StringRef SegInfo::segmentName(uint32_t SegIndex) { - for (const SectionInfo &SI : Sections) { - if (SI.SegmentIndex == SegIndex) - return SI.SegmentName; - } - llvm_unreachable("invalid segIndex"); -} - -bool SegInfo::isValidSegIndexAndOffset(uint32_t SegIndex, - uint64_t OffsetInSeg) { - for (const SectionInfo &SI : Sections) { - if (SI.SegmentIndex != SegIndex) - continue; - if (SI.OffsetInSegment > OffsetInSeg) - continue; - if (OffsetInSeg >= (SI.OffsetInSegment + SI.Size)) - continue; - return true; - } - return false; -} - -const SegInfo::SectionInfo &SegInfo::findSection(uint32_t SegIndex, - uint64_t OffsetInSeg) { - for (const SectionInfo &SI : Sections) { - if (SI.SegmentIndex != SegIndex) - continue; - if (SI.OffsetInSegment > OffsetInSeg) - continue; - if (OffsetInSeg >= (SI.OffsetInSegment + SI.Size)) - continue; - return SI; - } - llvm_unreachable("segIndex and offset not in any section"); -} - -StringRef SegInfo::sectionName(uint32_t SegIndex, uint64_t OffsetInSeg) { - return findSection(SegIndex, OffsetInSeg).SectionName; -} - -uint64_t SegInfo::address(uint32_t SegIndex, uint64_t OffsetInSeg) { - const SectionInfo &SI = findSection(SegIndex, OffsetInSeg); - return SI.SegmentStartAddress + OffsetInSeg; -} - -void llvm::printMachORebaseTable(const object::MachOObjectFile *Obj) { - // Build table of sections so names can used in final output. - SegInfo sectionTable(Obj); - +void llvm::printMachORebaseTable(object::MachOObjectFile *Obj) { outs() << "segment section address type\n"; - for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable()) { - 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); + Error Err = Error::success(); + for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) { + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: __DATA __nl_symbol_ptr 0x0000F00C pointer outs() << format("%-8s %-18s 0x%08" PRIX64 " %s\n", SegmentName.str().c_str(), SectionName.str().c_str(), Address, Entry.typeName().str().c_str()); } + if (Err) + report_error(Obj->getFileName(), std::move(Err)); } static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { @@ -9529,19 +9433,15 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { // bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOBindTable(const object::MachOObjectFile *Obj) { +void llvm::printMachOBindTable(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"; Error Err = Error::success(); for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable(Err)) { - 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); + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: // __DATA __got 0x00012010 pointer 0 libSystem ___stack_chk_guard @@ -9564,19 +9464,14 @@ void llvm::printMachOBindTable(const object::MachOObjectFile *Obj) { // 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); - +void llvm::printMachOLazyBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "dylib symbol\n"; Error Err = Error::success(); for (const llvm::object::MachOBindEntry &Entry : Obj->lazyBindTable(Err)) { - 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); + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: // __DATA __got 0x00012010 libSystem ___stack_chk_guard @@ -9594,10 +9489,7 @@ void llvm::printMachOLazyBindTable(const object::MachOObjectFile *Obj) { // 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); - +void llvm::printMachOWeakBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "type addend symbol\n"; Error Err = Error::success(); @@ -9608,11 +9500,9 @@ void llvm::printMachOWeakBindTable(const object::MachOObjectFile *Obj) { << 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); + StringRef SegmentName = Entry.segmentName(); + StringRef SectionName = Entry.sectionName(); + uint64_t Address = Entry.address(); // Table lines look like: // __DATA __data 0x00001000 pointer 0 _foo @@ -9635,17 +9525,9 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info) { if (info->bindtable == nullptr) { info->bindtable = llvm::make_unique(); - SegInfo sectionTable(info->O); Error Err = Error::success(); for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable(Err)) { - uint32_t SegIndex = Entry.segmentIndex(); - uint64_t OffsetInSeg = Entry.segmentOffset(); - if (!sectionTable.isValidSegIndexAndOffset(SegIndex, OffsetInSeg)) { - if (Err) - report_error(info->O->getFileName(), std::move(Err)); - return nullptr; - } - uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + uint64_t Address = Entry.address(); StringRef name = Entry.symbolName(); if (!name.empty()) (*info->bindtable)[Address] = name; diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 96932eb0a47..613d0643b43 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -1885,9 +1885,9 @@ void llvm::printExportsTrie(const ObjectFile *o) { } } -void llvm::printRebaseTable(const ObjectFile *o) { +void llvm::printRebaseTable(ObjectFile *o) { outs() << "Rebase table:\n"; - if (const MachOObjectFile *MachO = dyn_cast(o)) + if (MachOObjectFile *MachO = dyn_cast(o)) printMachORebaseTable(MachO); else { errs() << "This operation is only currently supported " @@ -1896,9 +1896,9 @@ void llvm::printRebaseTable(const ObjectFile *o) { } } -void llvm::printBindTable(const ObjectFile *o) { +void llvm::printBindTable(ObjectFile *o) { outs() << "Bind table:\n"; - if (const MachOObjectFile *MachO = dyn_cast(o)) + if (MachOObjectFile *MachO = dyn_cast(o)) printMachOBindTable(MachO); else { errs() << "This operation is only currently supported " @@ -1907,9 +1907,9 @@ void llvm::printBindTable(const ObjectFile *o) { } } -void llvm::printLazyBindTable(const ObjectFile *o) { +void llvm::printLazyBindTable(ObjectFile *o) { outs() << "Lazy bind table:\n"; - if (const MachOObjectFile *MachO = dyn_cast(o)) + if (MachOObjectFile *MachO = dyn_cast(o)) printMachOLazyBindTable(MachO); else { errs() << "This operation is only currently supported " @@ -1918,9 +1918,9 @@ void llvm::printLazyBindTable(const ObjectFile *o) { } } -void llvm::printWeakBindTable(const ObjectFile *o) { +void llvm::printWeakBindTable(ObjectFile *o) { outs() << "Weak bind table:\n"; - if (const MachOObjectFile *MachO = dyn_cast(o)) + if (MachOObjectFile *MachO = dyn_cast(o)) printMachOWeakBindTable(MachO); else { errs() << "This operation is only currently supported " @@ -2018,7 +2018,7 @@ static void printPrivateFileHeaders(const ObjectFile *o, bool onlyFirst) { report_error(o->getFileName(), "Invalid/Unsupported object file format"); } -static void DumpObject(const ObjectFile *o, const Archive *a = nullptr) { +static void DumpObject(ObjectFile *o, const Archive *a = nullptr) { StringRef ArchiveName = a != nullptr ? a->getFileName() : ""; // Avoid other output when using a raw option. if (!RawClangAST) { diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index 191660b9f4d..2fcd506884b 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -70,10 +70,10 @@ void ParseInputMachO(StringRef Filename); 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 printMachORebaseTable(object::MachOObjectFile* o); +void printMachOBindTable(object::MachOObjectFile* o); +void printMachOLazyBindTable(object::MachOObjectFile* o); +void printMachOWeakBindTable(object::MachOObjectFile* o); void printELFFileHeader(const object::ObjectFile *o); void printCOFFFileHeader(const object::ObjectFile *o); void printCOFFSymbolTable(const object::COFFImportFile *i); @@ -82,10 +82,10 @@ void printMachOFileHeader(const object::ObjectFile *o); void printMachOLoadCommands(const object::ObjectFile *o); void printWasmFileHeader(const object::ObjectFile *o); void printExportsTrie(const object::ObjectFile *o); -void printRebaseTable(const object::ObjectFile *o); -void printBindTable(const object::ObjectFile *o); -void printLazyBindTable(const object::ObjectFile *o); -void printWeakBindTable(const object::ObjectFile *o); +void printRebaseTable(object::ObjectFile *o); +void printBindTable(object::ObjectFile *o); +void printLazyBindTable(object::ObjectFile *o); +void printWeakBindTable(object::ObjectFile *o); void printRawClangAST(const object::ObjectFile *o); void PrintRelocations(const object::ObjectFile *o); void PrintSectionHeaders(const object::ObjectFile *o);