X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FObject%2FMachOObjectFile.cpp;h=260e7557f433547c19f10fa4698fae19b16d7608;hb=8175be535acfffc3977231f58382a07e554d8d6b;hp=da968b58f21fbbac319eaf3ae3864a8f0525e50e;hpb=3512034554649610c9727daecc29925d34bb3d2a;p=oota-llvm.git diff --git a/lib/Object/MachOObjectFile.cpp b/lib/Object/MachOObjectFile.cpp index da968b58f21..260e7557f43 100644 --- a/lib/Object/MachOObjectFile.cpp +++ b/lib/Object/MachOObjectFile.cpp @@ -17,8 +17,11 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" #include "llvm/Support/Host.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MachO.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include @@ -55,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) { @@ -130,11 +144,9 @@ static void printRelocationTargetName(const MachOObjectFile *O, // to find a section beginning instead. for (const SectionRef &Section : O->sections()) { std::error_code ec; - uint64_t Addr; - StringRef Name; - if ((ec = Section.getAddress(Addr))) - report_fatal_error(ec.message()); + StringRef Name; + uint64_t Addr = Section.getAddress(); if (Addr != Val) continue; if ((ec = Section.getName(Name))) @@ -226,7 +238,8 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, bool Is64bits, std::error_code &EC) : ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object), SymtabLoadCmd(nullptr), DysymtabLoadCmd(nullptr), - DataInCodeLoadCmd(nullptr) { + DataInCodeLoadCmd(nullptr), DyldInfoLoadCmd(nullptr), + HasPageZeroSegment(false) { uint32_t LoadCommandCount = this->getHeader().ncmds; MachO::LoadCommandType SegmentLoadType = is64Bit() ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT; @@ -242,12 +255,18 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, } else if (Load.C.cmd == MachO::LC_DATA_IN_CODE) { assert(!DataInCodeLoadCmd && "Multiple data in code tables"); DataInCodeLoadCmd = Load.Ptr; + } else if (Load.C.cmd == MachO::LC_DYLD_INFO || + Load.C.cmd == MachO::LC_DYLD_INFO_ONLY) { + assert(!DyldInfoLoadCmd && "Multiple dyldinfo load commands"); + DyldInfoLoadCmd = Load.Ptr; } else if (Load.C.cmd == SegmentLoadType) { uint32_t NumSections = getSegmentLoadCommandNumSections(this, Load); for (unsigned J = 0; J < NumSections; ++J) { 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 || @@ -373,11 +392,10 @@ std::error_code MachOObjectFile::getSymbolSize(DataRefImpl DRI, EndOffset = Value; } if (!EndOffset) { - uint64_t Size; DataRefImpl Sec; Sec.d.a = SectionIndex-1; - getSectionSize(Sec, Size); - getSectionAddress(Sec, EndOffset); + uint64_t Size = getSectionSize(Sec); + EndOffset = getSectionAddress(Sec); EndOffset += Size; } Result = EndOffset - BeginOffset; @@ -474,29 +492,16 @@ std::error_code MachOObjectFile::getSectionName(DataRefImpl Sec, return object_error::success; } -std::error_code MachOObjectFile::getSectionAddress(DataRefImpl Sec, - uint64_t &Res) const { - if (is64Bit()) { - MachO::section_64 Sect = getSection64(Sec); - Res = Sect.addr; - } else { - MachO::section Sect = getSection(Sec); - Res = Sect.addr; - } - return object_error::success; +uint64_t MachOObjectFile::getSectionAddress(DataRefImpl Sec) const { + if (is64Bit()) + return getSection64(Sec).addr; + return getSection(Sec).addr; } -std::error_code MachOObjectFile::getSectionSize(DataRefImpl Sec, - uint64_t &Res) const { - if (is64Bit()) { - MachO::section_64 Sect = getSection64(Sec); - Res = Sect.size; - } else { - MachO::section Sect = getSection(Sec); - Res = Sect.size; - } - - return object_error::success; +uint64_t MachOObjectFile::getSectionSize(DataRefImpl Sec) const { + if (is64Bit()) + return getSection64(Sec).size; + return getSection(Sec).size; } std::error_code MachOObjectFile::getSectionContents(DataRefImpl Sec, @@ -518,8 +523,7 @@ std::error_code MachOObjectFile::getSectionContents(DataRefImpl Sec, return object_error::success; } -std::error_code MachOObjectFile::getSectionAlignment(DataRefImpl Sec, - uint64_t &Res) const { +uint64_t MachOObjectFile::getSectionAlignment(DataRefImpl Sec) const { uint32_t Align; if (is64Bit()) { MachO::section_64 Sect = getSection64(Sec); @@ -529,92 +533,70 @@ std::error_code MachOObjectFile::getSectionAlignment(DataRefImpl Sec, Align = Sect.align; } - Res = uint64_t(1) << Align; - return object_error::success; + return uint64_t(1) << Align; } -std::error_code MachOObjectFile::isSectionText(DataRefImpl Sec, - bool &Res) const { +bool MachOObjectFile::isSectionText(DataRefImpl Sec) const { uint32_t Flags = getSectionFlags(this, Sec); - Res = Flags & MachO::S_ATTR_PURE_INSTRUCTIONS; - return object_error::success; + return Flags & MachO::S_ATTR_PURE_INSTRUCTIONS; } -std::error_code MachOObjectFile::isSectionData(DataRefImpl Sec, - bool &Result) const { +bool MachOObjectFile::isSectionData(DataRefImpl Sec) const { uint32_t Flags = getSectionFlags(this, Sec); unsigned SectionType = Flags & MachO::SECTION_TYPE; - Result = !(Flags & MachO::S_ATTR_PURE_INSTRUCTIONS) && - !(SectionType == MachO::S_ZEROFILL || - SectionType == MachO::S_GB_ZEROFILL); - return object_error::success; + return !(Flags & MachO::S_ATTR_PURE_INSTRUCTIONS) && + !(SectionType == MachO::S_ZEROFILL || + SectionType == MachO::S_GB_ZEROFILL); } -std::error_code MachOObjectFile::isSectionBSS(DataRefImpl Sec, - bool &Result) const { +bool MachOObjectFile::isSectionBSS(DataRefImpl Sec) const { uint32_t Flags = getSectionFlags(this, Sec); unsigned SectionType = Flags & MachO::SECTION_TYPE; - Result = !(Flags & MachO::S_ATTR_PURE_INSTRUCTIONS) && - (SectionType == MachO::S_ZEROFILL || - SectionType == MachO::S_GB_ZEROFILL); - return object_error::success; + return !(Flags & MachO::S_ATTR_PURE_INSTRUCTIONS) && + (SectionType == MachO::S_ZEROFILL || + SectionType == MachO::S_GB_ZEROFILL); } -std::error_code -MachOObjectFile::isSectionRequiredForExecution(DataRefImpl Sec, - bool &Result) const { +bool MachOObjectFile::isSectionRequiredForExecution(DataRefImpl Sect) const { // FIXME: Unimplemented. - Result = true; - return object_error::success; + return true; } -std::error_code MachOObjectFile::isSectionVirtual(DataRefImpl Sec, - bool &Result) const { +bool MachOObjectFile::isSectionVirtual(DataRefImpl Sec) const { // FIXME: Unimplemented. - Result = false; - return object_error::success; + return false; } -std::error_code MachOObjectFile::isSectionZeroInit(DataRefImpl Sec, - bool &Res) const { +bool MachOObjectFile::isSectionZeroInit(DataRefImpl Sec) const { uint32_t Flags = getSectionFlags(this, Sec); unsigned SectionType = Flags & MachO::SECTION_TYPE; - Res = SectionType == MachO::S_ZEROFILL || - SectionType == MachO::S_GB_ZEROFILL; - return object_error::success; + return SectionType == MachO::S_ZEROFILL || + SectionType == MachO::S_GB_ZEROFILL; } -std::error_code MachOObjectFile::isSectionReadOnlyData(DataRefImpl Sec, - bool &Result) const { +bool MachOObjectFile::isSectionReadOnlyData(DataRefImpl Sec) const { // Consider using the code from isSectionText to look for __const sections. // Alternately, emit S_ATTR_PURE_INSTRUCTIONS and/or S_ATTR_SOME_INSTRUCTIONS // to use section attributes to distinguish code from data. // FIXME: Unimplemented. - Result = false; - return object_error::success; + return false; } -std::error_code MachOObjectFile::sectionContainsSymbol(DataRefImpl Sec, - DataRefImpl Symb, - bool &Result) const { +bool MachOObjectFile::sectionContainsSymbol(DataRefImpl Sec, + DataRefImpl Symb) const { SymbolRef::Type ST; this->getSymbolType(Symb, ST); - if (ST == SymbolRef::ST_Unknown) { - Result = false; - return object_error::success; - } + if (ST == SymbolRef::ST_Unknown) + return false; - uint64_t SectBegin, SectEnd; - getSectionAddress(Sec, SectBegin); - getSectionSize(Sec, SectEnd); + uint64_t SectBegin = getSectionAddress(Sec); + uint64_t SectEnd = getSectionSize(Sec); SectEnd += SectBegin; uint64_t SymAddr; getSymbolAddress(Symb, SymAddr); - Result = (SymAddr >= SectBegin) && (SymAddr < SectEnd); - - return object_error::success; + return (SymAddr >= SectBegin) && (SymAddr < SectEnd); } relocation_iterator MachOObjectFile::section_rel_begin(DataRefImpl Sec) const { @@ -652,8 +634,7 @@ std::error_code MachOObjectFile::getRelocationAddress(DataRefImpl Rel, DataRefImpl Sec; Sec.d.a = Rel.d.a; - uint64_t SecAddress; - getSectionAddress(Sec, SecAddress); + uint64_t SecAddress = getSectionAddress(Sec); Res = SecAddress + Offset; return object_error::success; } @@ -1159,31 +1140,26 @@ guess_qtx: // It is passed the index (0 - based) of the library as translated from // GET_LIBRARY_ORDINAL (1 - based). std::error_code MachOObjectFile::getLibraryShortNameByIndex(unsigned Index, - StringRef &Res) { + StringRef &Res) const { if (Index >= Libraries.size()) return object_error::parse_failed; - MachO::dylib_command D = - getStruct(this, Libraries[Index]); - if (D.dylib.name >= D.cmdsize) - return object_error::parse_failed; - // If the cache of LibrariesShortNames is not built up do that first for // all the Libraries. if (LibrariesShortNames.size() == 0) { for (unsigned i = 0; i < Libraries.size(); i++) { MachO::dylib_command D = getStruct(this, Libraries[i]); - if (D.dylib.name >= D.cmdsize) { - LibrariesShortNames.push_back(StringRef()); - continue; - } + if (D.dylib.name >= D.cmdsize) + return object_error::parse_failed; const char *P = (const char *)(Libraries[i]) + D.dylib.name; StringRef Name = StringRef(P); + if (D.dylib.name+Name.size() >= D.cmdsize) + return object_error::parse_failed; StringRef Suffix; bool isFramework; StringRef shortName = guessLibraryShortName(Name, isFramework, Suffix); - if (shortName == StringRef()) + if (shortName.empty()) LibrariesShortNames.push_back(Name); else LibrariesShortNames.push_back(shortName); @@ -1505,6 +1481,620 @@ dice_iterator MachOObjectFile::end_dices() const { return dice_iterator(DiceRef(DRI, this)); } +ExportEntry::ExportEntry(ArrayRef T) + : Trie(T), Malformed(false), Done(false) { } + +void ExportEntry::moveToFirst() { + pushNode(0); + pushDownUntilBottom(); +} + +void ExportEntry::moveToEnd() { + Stack.clear(); + Done = true; +} + +bool ExportEntry::operator==(const ExportEntry &Other) const { + // Common case, one at end, other iterating from begin. + if (Done || Other.Done) + return (Done == Other.Done); + // Not equal if different stack sizes. + if (Stack.size() != Other.Stack.size()) + return false; + // Not equal if different cumulative strings. + if (!CumulativeString.str().equals(Other.CumulativeString.str())) + return false; + // Equal if all nodes in both stacks match. + for (unsigned i=0; i < Stack.size(); ++i) { + if (Stack[i].Start != Other.Stack[i].Start) + return false; + } + return true; +} + +uint64_t ExportEntry::readULEB128(const uint8_t *&Ptr) { + unsigned Count; + uint64_t Result = decodeULEB128(Ptr, &Count); + Ptr += Count; + if (Ptr > Trie.end()) { + Ptr = Trie.end(); + Malformed = true; + } + return Result; +} + +StringRef ExportEntry::name() const { + return CumulativeString.str(); +} + +uint64_t ExportEntry::flags() const { + return Stack.back().Flags; +} + +uint64_t ExportEntry::address() const { + return Stack.back().Address; +} + +uint64_t ExportEntry::other() const { + return Stack.back().Other; +} + +StringRef ExportEntry::otherName() const { + const char* ImportName = Stack.back().ImportName; + if (ImportName) + return StringRef(ImportName); + return StringRef(); +} + +uint32_t ExportEntry::nodeOffset() const { + return Stack.back().Start - Trie.begin(); +} + +ExportEntry::NodeState::NodeState(const uint8_t *Ptr) + : Start(Ptr), Current(Ptr), Flags(0), Address(0), Other(0), + ImportName(nullptr), ChildCount(0), NextChildIndex(0), + ParentStringLength(0), IsExportNode(false) { +} + +void ExportEntry::pushNode(uint64_t offset) { + const uint8_t *Ptr = Trie.begin() + offset; + NodeState State(Ptr); + uint64_t ExportInfoSize = readULEB128(State.Current); + State.IsExportNode = (ExportInfoSize != 0); + const uint8_t* Children = State.Current + ExportInfoSize; + if (State.IsExportNode) { + State.Flags = readULEB128(State.Current); + if (State.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { + State.Address = 0; + State.Other = readULEB128(State.Current); // dylib ordinal + State.ImportName = reinterpret_cast(State.Current); + } else { + State.Address = readULEB128(State.Current); + if (State.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) + State.Other = readULEB128(State.Current); + } + } + State.ChildCount = *Children; + State.Current = Children + 1; + State.NextChildIndex = 0; + State.ParentStringLength = CumulativeString.size(); + Stack.push_back(State); +} + +void ExportEntry::pushDownUntilBottom() { + while (Stack.back().NextChildIndex < Stack.back().ChildCount) { + NodeState &Top = Stack.back(); + CumulativeString.resize(Top.ParentStringLength); + for (;*Top.Current != 0; Top.Current++) { + char C = *Top.Current; + CumulativeString.push_back(C); + } + Top.Current += 1; + uint64_t childNodeIndex = readULEB128(Top.Current); + Top.NextChildIndex += 1; + pushNode(childNodeIndex); + } + if (!Stack.back().IsExportNode) { + Malformed = true; + moveToEnd(); + } +} + +// We have a trie data structure and need a way to walk it that is compatible +// with the C++ iterator model. The solution is a non-recursive depth first +// traversal where the iterator contains a stack of parent nodes along with a +// string that is the accumulation of all edge strings along the parent chain +// to this point. +// +// There is one “export” node for each exported symbol. But because some +// symbols may be a prefix of another symbol (e.g. _dup and _dup2), an export +// node may have child nodes too. +// +// The algorithm for moveNext() is to keep moving down the leftmost unvisited +// child until hitting a node with no children (which is an export node or +// else the trie is malformed). On the way down, each node is pushed on the +// stack ivar. If there is no more ways down, it pops up one and tries to go +// down a sibling path until a childless node is reached. +void ExportEntry::moveNext() { + if (Stack.empty() || !Stack.back().IsExportNode) { + Malformed = true; + moveToEnd(); + return; + } + + Stack.pop_back(); + while (!Stack.empty()) { + NodeState &Top = Stack.back(); + if (Top.NextChildIndex < Top.ChildCount) { + pushDownUntilBottom(); + // Now at the next export node. + return; + } else { + if (Top.IsExportNode) { + // This node has no children but is itself an export node. + CumulativeString.resize(Top.ParentStringLength); + return; + } + Stack.pop_back(); + } + } + Done = true; +} + +iterator_range +MachOObjectFile::exports(ArrayRef Trie) { + ExportEntry Start(Trie); + Start.moveToFirst(); + + ExportEntry Finish(Trie); + Finish.moveToEnd(); + + return iterator_range(export_iterator(Start), + export_iterator(Finish)); +} + +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), + PointerSize(is64Bit ? 8 : 4), Malformed(false), Done(false) {} + +void MachORebaseEntry::moveToFirst() { + Ptr = Opcodes.begin(); + moveNext(); +} + +void MachORebaseEntry::moveToEnd() { + Ptr = Opcodes.end(); + RemainingLoopCount = 0; + Done = true; +} + +void MachORebaseEntry::moveNext() { + // If in the middle of some loop, move to next rebasing 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::REBASE_IMMEDIATE_MASK; + uint8_t Opcode = Byte & MachO::REBASE_OPCODE_MASK; + switch (Opcode) { + case MachO::REBASE_OPCODE_DONE: + More = false; + Done = true; + moveToEnd(); + DEBUG_WITH_TYPE("mach-o-rebase", llvm::dbgs() << "REBASE_OPCODE_DONE\n"); + break; + case MachO::REBASE_OPCODE_SET_TYPE_IMM: + RebaseType = ImmValue; + DEBUG_WITH_TYPE( + "mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_SET_TYPE_IMM: " + << "RebaseType=" << (int) RebaseType << "\n"); + break; + case MachO::REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + SegmentIndex = ImmValue; + SegmentOffset = readULEB128(); + DEBUG_WITH_TYPE( + "mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: " + << "SegmentIndex=" << SegmentIndex << ", " + << format("SegmentOffset=0x%06X", SegmentOffset) + << "\n"); + break; + case MachO::REBASE_OPCODE_ADD_ADDR_ULEB: + SegmentOffset += readULEB128(); + DEBUG_WITH_TYPE("mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_ADD_ADDR_ULEB: " + << format("SegmentOffset=0x%06X", + SegmentOffset) << "\n"); + break; + case MachO::REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + SegmentOffset += ImmValue * PointerSize; + DEBUG_WITH_TYPE("mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_ADD_ADDR_IMM_SCALED: " + << format("SegmentOffset=0x%06X", + SegmentOffset) << "\n"); + break; + case MachO::REBASE_OPCODE_DO_REBASE_IMM_TIMES: + AdvanceAmount = PointerSize; + RemainingLoopCount = ImmValue - 1; + DEBUG_WITH_TYPE( + "mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_DO_REBASE_IMM_TIMES: " + << format("SegmentOffset=0x%06X", SegmentOffset) + << ", AdvanceAmount=" << AdvanceAmount + << ", RemainingLoopCount=" << RemainingLoopCount + << "\n"); + return; + case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + AdvanceAmount = PointerSize; + RemainingLoopCount = readULEB128() - 1; + DEBUG_WITH_TYPE( + "mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_DO_REBASE_ULEB_TIMES: " + << format("SegmentOffset=0x%06X", SegmentOffset) + << ", AdvanceAmount=" << AdvanceAmount + << ", RemainingLoopCount=" << RemainingLoopCount + << "\n"); + return; + case MachO::REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + AdvanceAmount = readULEB128() + PointerSize; + RemainingLoopCount = 0; + DEBUG_WITH_TYPE( + "mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: " + << format("SegmentOffset=0x%06X", SegmentOffset) + << ", AdvanceAmount=" << AdvanceAmount + << ", RemainingLoopCount=" << RemainingLoopCount + << "\n"); + return; + case MachO::REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + RemainingLoopCount = readULEB128() - 1; + AdvanceAmount = readULEB128() + PointerSize; + DEBUG_WITH_TYPE( + "mach-o-rebase", + llvm::dbgs() << "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: " + << format("SegmentOffset=0x%06X", SegmentOffset) + << ", AdvanceAmount=" << AdvanceAmount + << ", RemainingLoopCount=" << RemainingLoopCount + << "\n"); + return; + default: + Malformed = true; + } + } +} + +uint64_t MachORebaseEntry::readULEB128() { + unsigned Count; + uint64_t Result = decodeULEB128(Ptr, &Count); + Ptr += Count; + if (Ptr > Opcodes.end()) { + Ptr = Opcodes.end(); + Malformed = true; + } + return Result; +} + +uint32_t MachORebaseEntry::segmentIndex() const { return SegmentIndex; } + +uint64_t MachORebaseEntry::segmentOffset() const { return SegmentOffset; } + +StringRef MachORebaseEntry::typeName() const { + switch (RebaseType) { + case MachO::REBASE_TYPE_POINTER: + return "pointer"; + case MachO::REBASE_TYPE_TEXT_ABSOLUTE32: + return "text abs32"; + case MachO::REBASE_TYPE_TEXT_PCREL32: + return "text rel32"; + } + return "unknown"; +} + +bool MachORebaseEntry::operator==(const MachORebaseEntry &Other) const { + assert(Opcodes == Other.Opcodes && "compare iterators of different files"); + return (Ptr == Other.Ptr) && + (RemainingLoopCount == Other.RemainingLoopCount) && + (Done == Other.Done); +} + +iterator_range +MachOObjectFile::rebaseTable(ArrayRef Opcodes, bool is64) { + MachORebaseEntry Start(Opcodes, is64); + Start.moveToFirst(); + + MachORebaseEntry Finish(Opcodes, is64); + Finish.moveToEnd(); + + return iterator_range(rebase_iterator(Start), + rebase_iterator(Finish)); +} + +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; + } + SymbolName = StringRef(reinterpret_cast(SymStart), + Ptr-SymStart); + ++Ptr; + 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); @@ -1678,6 +2268,31 @@ MachOObjectFile::getDylibIDLoadCommand(const LoadCommandInfo &L) const { return getStruct(this, L.Ptr); } +MachO::dyld_info_command +MachOObjectFile::getDyldInfoLoadCommand(const LoadCommandInfo &L) const { + return getStruct(this, L.Ptr); +} + +MachO::dylinker_command +MachOObjectFile::getDylinkerCommand(const LoadCommandInfo &L) const { + return getStruct(this, L.Ptr); +} + +MachO::uuid_command +MachOObjectFile::getUuidCommand(const LoadCommandInfo &L) const { + return getStruct(this, L.Ptr); +} + +MachO::source_version_command +MachOObjectFile::getSourceVersionCommand(const LoadCommandInfo &L) const { + return getStruct(this, L.Ptr); +} + +MachO::entry_point_command +MachOObjectFile::getEntryPointCommand(const LoadCommandInfo &L) const { + return getStruct(this, L.Ptr); +} + MachO::any_relocation_info MachOObjectFile::getRelocation(DataRefImpl Rel) const { @@ -1748,6 +2363,62 @@ MachOObjectFile::getDataInCodeLoadCommand() const { return Cmd; } +ArrayRef MachOObjectFile::getDyldInfoRebaseOpcodes() const { + if (!DyldInfoLoadCmd) + return ArrayRef(); + + MachO::dyld_info_command DyldInfo + = getStruct(this, DyldInfoLoadCmd); + const uint8_t *Ptr = reinterpret_cast( + getPtr(this, DyldInfo.rebase_off)); + return ArrayRef(Ptr, DyldInfo.rebase_size); +} + +ArrayRef MachOObjectFile::getDyldInfoBindOpcodes() const { + if (!DyldInfoLoadCmd) + return ArrayRef(); + + MachO::dyld_info_command DyldInfo + = getStruct(this, DyldInfoLoadCmd); + const uint8_t *Ptr = reinterpret_cast( + getPtr(this, DyldInfo.bind_off)); + return ArrayRef(Ptr, DyldInfo.bind_size); +} + +ArrayRef MachOObjectFile::getDyldInfoWeakBindOpcodes() const { + if (!DyldInfoLoadCmd) + return ArrayRef(); + + MachO::dyld_info_command DyldInfo + = getStruct(this, DyldInfoLoadCmd); + const uint8_t *Ptr = reinterpret_cast( + getPtr(this, DyldInfo.weak_bind_off)); + return ArrayRef(Ptr, DyldInfo.weak_bind_size); +} + +ArrayRef MachOObjectFile::getDyldInfoLazyBindOpcodes() const { + if (!DyldInfoLoadCmd) + return ArrayRef(); + + MachO::dyld_info_command DyldInfo + = getStruct(this, DyldInfoLoadCmd); + const uint8_t *Ptr = reinterpret_cast( + getPtr(this, DyldInfo.lazy_bind_off)); + return ArrayRef(Ptr, DyldInfo.lazy_bind_size); +} + +ArrayRef MachOObjectFile::getDyldInfoExportsTrie() const { + if (!DyldInfoLoadCmd) + return ArrayRef(); + + MachO::dyld_info_command DyldInfo + = getStruct(this, DyldInfoLoadCmd); + const uint8_t *Ptr = reinterpret_cast( + getPtr(this, DyldInfo.export_off)); + return ArrayRef(Ptr, DyldInfo.export_size); +} + + StringRef MachOObjectFile::getStringTableData() const { MachO::symtab_command S = getSymtabLoadCommand(); return getData().substr(S.stroff, S.strsize);