diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-04-14 21:41:27 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-06-22 18:20:56 +0000 |
commit | bdd1243df58e60e85101c09001d9812a789b6bc4 (patch) | |
tree | a1ce621c7301dd47ba2ddc3b8eaa63b441389481 /contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp | |
parent | 781624ca2d054430052c828ba8d2c2eaf2d733e7 (diff) | |
parent | e3b557809604d036af6e00c60f012c2025b59a5e (diff) |
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp | 508 |
1 files changed, 457 insertions, 51 deletions
diff --git a/contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp b/contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp index 2f463a1bd458..9c0b85cf7416 100644 --- a/contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp +++ b/contrib/llvm-project/llvm/lib/Object/MachOObjectFile.cpp @@ -12,13 +12,13 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" +#include "llvm/ADT/bit.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/Swift.h" #include "llvm/Object/Error.h" @@ -1385,6 +1385,11 @@ MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, *this, Load, I, &DyldChainedFixupsLoadCmd, "LC_DYLD_CHAINED_FIXUPS", Elements, "chained fixups"))) return; + } else if (Load.C.cmd == MachO::LC_DYLD_EXPORTS_TRIE) { + if ((Err = checkLinkeditDataCommand( + *this, Load, I, &DyldExportsTrieLoadCmd, "LC_DYLD_EXPORTS_TRIE", + Elements, "exports trie"))) + return; } else if (Load.C.cmd == MachO::LC_UUID) { if (Load.C.cmdsize != sizeof(MachO::uuid_command)) { Err = malformedError("LC_UUID command " + Twine(I) + " has incorrect " @@ -1875,9 +1880,13 @@ Expected<uint32_t> MachOObjectFile::getSymbolFlags(DataRefImpl DRI) const { Result |= SymbolRef::SF_Undefined; } - if (!(MachOType & MachO::N_PEXT)) + if (MachOType & MachO::N_PEXT) + Result |= SymbolRef::SF_Hidden; + else Result |= SymbolRef::SF_Exported; - } + + } else if (MachOType & MachO::N_PEXT) + Result |= SymbolRef::SF_Hidden; if (MachOFlags & (MachO::N_WEAK_REF | MachO::N_WEAK_DEF)) Result |= SymbolRef::SF_Weak; @@ -2072,6 +2081,19 @@ ArrayRef<uint8_t> getSegmentContents(const MachOObjectFile &Obj, Segment.fileoff, Segment.fileoff + Segment.filesize)); return {}; } + +template <typename LoadCommandType> +ArrayRef<uint8_t> getSegmentContents(const MachOObjectFile &Obj, + MachOObjectFile::LoadCommandInfo LoadCmd) { + auto SegmentOrErr = getStructOrErr<LoadCommandType>(Obj, LoadCmd.Ptr); + if (!SegmentOrErr) { + consumeError(SegmentOrErr.takeError()); + return {}; + } + auto &Segment = SegmentOrErr.get(); + return arrayRefFromStringRef( + Obj.getData().slice(Segment.fileoff, Segment.fileoff + Segment.filesize)); +} } // namespace ArrayRef<uint8_t> @@ -2096,6 +2118,28 @@ MachOObjectFile::getSegmentContents(StringRef SegmentName) const { return {}; } +ArrayRef<uint8_t> +MachOObjectFile::getSegmentContents(size_t SegmentIndex) const { + size_t Idx = 0; + for (auto LoadCmd : load_commands()) { + switch (LoadCmd.C.cmd) { + case MachO::LC_SEGMENT: + if (Idx == SegmentIndex) + return ::getSegmentContents<MachO::segment_command>(*this, LoadCmd); + ++Idx; + break; + case MachO::LC_SEGMENT_64: + if (Idx == SegmentIndex) + return ::getSegmentContents<MachO::segment_command_64>(*this, LoadCmd); + ++Idx; + break; + default: + continue; + } + } + return {}; +} + unsigned MachOObjectFile::getSectionID(SectionRef Sec) const { return Sec.getRawDataRefImpl().d.a; } @@ -2293,7 +2337,7 @@ void MachOObjectFile::getRelocationTypeName( "ARM64_RELOC_ADDEND" }; - if (RType >= array_lengthof(Table)) + if (RType >= std::size(Table)) res = "Unknown"; else res = Table[RType]; @@ -3055,7 +3099,7 @@ void ExportEntry::pushNode(uint64_t offset) { } if(ExportStart + ExportInfoSize != State.Current) { *E = malformedError( - "inconsistant export info size: 0x" + + "inconsistent export info size: 0x" + Twine::utohexstr(ExportInfoSize) + " where actual size was: 0x" + Twine::utohexstr(State.Current - ExportStart) + " in export trie data at node: 0x" + Twine::utohexstr(offset)); @@ -3189,7 +3233,13 @@ MachOObjectFile::exports(Error &E, ArrayRef<uint8_t> Trie, } iterator_range<export_iterator> MachOObjectFile::exports(Error &Err) const { - return exports(Err, getDyldInfoExportsTrie(), this); + ArrayRef<uint8_t> Trie; + if (DyldInfoLoadCmd) + Trie = getDyldInfoExportsTrie(); + else if (DyldExportsTrieLoadCmd) + Trie = getDyldExportsTrie(); + + return exports(Err, Trie, this); } MachOAbstractFixupEntry::MachOAbstractFixupEntry(Error *E, @@ -3256,6 +3306,8 @@ void MachOAbstractFixupEntry::moveToFirst() { void MachOAbstractFixupEntry::moveToEnd() { Done = true; } +void MachOAbstractFixupEntry::moveNext() {} + MachOChainedFixupEntry::MachOChainedFixupEntry(Error *E, const MachOObjectFile *O, bool Parse) @@ -3263,17 +3315,54 @@ MachOChainedFixupEntry::MachOChainedFixupEntry(Error *E, ErrorAsOutParameter e(E); if (!Parse) return; - if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets()) + + if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets()) { FixupTargets = *FixupTargetsOrErr; - else { + } else { *E = FixupTargetsOrErr.takeError(); return; } + + if (auto SegmentsOrErr = O->getChainedFixupsSegments()) { + Segments = std::move(SegmentsOrErr->second); + } else { + *E = SegmentsOrErr.takeError(); + return; + } +} + +void MachOChainedFixupEntry::findNextPageWithFixups() { + auto FindInSegment = [this]() { + const ChainedFixupsSegment &SegInfo = Segments[InfoSegIndex]; + while (PageIndex < SegInfo.PageStarts.size() && + SegInfo.PageStarts[PageIndex] == MachO::DYLD_CHAINED_PTR_START_NONE) + ++PageIndex; + return PageIndex < SegInfo.PageStarts.size(); + }; + + while (InfoSegIndex < Segments.size()) { + if (FindInSegment()) { + PageOffset = Segments[InfoSegIndex].PageStarts[PageIndex]; + SegmentData = O->getSegmentContents(Segments[InfoSegIndex].SegIdx); + return; + } + + InfoSegIndex++; + PageIndex = 0; + } } void MachOChainedFixupEntry::moveToFirst() { MachOAbstractFixupEntry::moveToFirst(); - FixupIndex = 0; + if (Segments.empty()) { + Done = true; + return; + } + + InfoSegIndex = 0; + PageIndex = 0; + + findNextPageWithFixups(); moveNext(); } @@ -3281,15 +3370,104 @@ void MachOChainedFixupEntry::moveToEnd() { MachOAbstractFixupEntry::moveToEnd(); } -void MachOChainedFixupEntry::moveNext() { Done = true; } +void MachOChainedFixupEntry::moveNext() { + ErrorAsOutParameter ErrAsOutParam(E); + + if (InfoSegIndex == Segments.size()) { + Done = true; + return; + } + + const ChainedFixupsSegment &SegInfo = Segments[InfoSegIndex]; + SegmentIndex = SegInfo.SegIdx; + SegmentOffset = SegInfo.Header.page_size * PageIndex + PageOffset; + + // FIXME: Handle other pointer formats. + uint16_t PointerFormat = SegInfo.Header.pointer_format; + if (PointerFormat != MachO::DYLD_CHAINED_PTR_64 && + PointerFormat != MachO::DYLD_CHAINED_PTR_64_OFFSET) { + *E = createError("segment " + Twine(SegmentIndex) + + " has unsupported chained fixup pointer_format " + + Twine(PointerFormat)); + moveToEnd(); + return; + } + + Ordinal = 0; + Flags = 0; + Addend = 0; + PointerValue = 0; + SymbolName = {}; + + if (SegmentOffset + sizeof(RawValue) > SegmentData.size()) { + *E = malformedError("fixup in segment " + Twine(SegmentIndex) + + " at offset " + Twine(SegmentOffset) + + " extends past segment's end"); + moveToEnd(); + return; + } + + static_assert(sizeof(RawValue) == sizeof(MachO::dyld_chained_import_addend)); + memcpy(&RawValue, SegmentData.data() + SegmentOffset, sizeof(RawValue)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(RawValue); + + // The bit extraction below assumes little-endian fixup entries. + assert(O->isLittleEndian() && "big-endian object should have been rejected " + "by getDyldChainedFixupTargets()"); + auto Field = [this](uint8_t Right, uint8_t Count) { + return (RawValue >> Right) & ((1ULL << Count) - 1); + }; + + // The `bind` field (most significant bit) of the encoded fixup determines + // whether it is dyld_chained_ptr_64_bind or dyld_chained_ptr_64_rebase. + bool IsBind = Field(63, 1); + Kind = IsBind ? FixupKind::Bind : FixupKind::Rebase; + uint32_t Next = Field(51, 12); + if (IsBind) { + uint32_t ImportOrdinal = Field(0, 24); + uint8_t InlineAddend = Field(24, 8); + + if (ImportOrdinal >= FixupTargets.size()) { + *E = malformedError("fixup in segment " + Twine(SegmentIndex) + + " at offset " + Twine(SegmentOffset) + + " has out-of range import ordinal " + + Twine(ImportOrdinal)); + moveToEnd(); + return; + } + + ChainedFixupTarget &Target = FixupTargets[ImportOrdinal]; + Ordinal = Target.libOrdinal(); + Addend = InlineAddend ? InlineAddend : Target.addend(); + Flags = Target.weakImport() ? MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0; + SymbolName = Target.symbolName(); + } else { + uint64_t Target = Field(0, 36); + uint64_t High8 = Field(36, 8); + + PointerValue = Target | (High8 << 56); + if (PointerFormat == MachO::DYLD_CHAINED_PTR_64_OFFSET) + PointerValue += textAddress(); + } + + // The stride is 4 bytes for DYLD_CHAINED_PTR_64(_OFFSET). + if (Next != 0) { + PageOffset += 4 * Next; + } else { + ++PageIndex; + findNextPageWithFixups(); + } +} bool MachOChainedFixupEntry::operator==( const MachOChainedFixupEntry &Other) const { - if (Done == Other.Done) - return true; - if ((FixupIndex == Other.FixupIndex)) + if (Done && Other.Done) return true; - return false; + if (Done != Other.Done) + return false; + return InfoSegIndex == Other.InfoSegIndex && PageIndex == Other.PageIndex && + PageOffset == Other.PageOffset; } MachORebaseEntry::MachORebaseEntry(Error *E, const MachOObjectFile *O, @@ -4301,6 +4479,9 @@ iterator_range<bind_iterator> MachOObjectFile::weakBindTable(Error &Err) { } iterator_range<fixup_iterator> MachOObjectFile::fixupTable(Error &Err) { + if (BindRebaseSectionTable == nullptr) + BindRebaseSectionTable = std::make_unique<BindRebaseSegInfo>(this); + MachOChainedFixupEntry Start(&Err, this, true); Start.moveToFirst(); @@ -4336,7 +4517,7 @@ MachOObjectFile::getSectionRawName(DataRefImpl Sec) const { assert(Sec.d.a < Sections.size() && "Should have detected this earlier"); const section_base *Base = reinterpret_cast<const section_base *>(Sections[Sec.d.a]); - return makeArrayRef(Base->sectname); + return ArrayRef(Base->sectname); } ArrayRef<char> @@ -4344,7 +4525,7 @@ MachOObjectFile::getSectionRawFinalSegmentName(DataRefImpl Sec) const { assert(Sec.d.a < Sections.size() && "Should have detected this earlier"); const section_base *Base = reinterpret_cast<const section_base *>(Sections[Sec.d.a]); - return makeArrayRef(Base->segname); + return ArrayRef(Base->segname); } bool @@ -4711,76 +4892,105 @@ MachOObjectFile::getLinkOptHintsLoadCommand() const { ArrayRef<uint8_t> MachOObjectFile::getDyldInfoRebaseOpcodes() const { if (!DyldInfoLoadCmd) - return None; + return std::nullopt; auto DyldInfoOrErr = getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); if (!DyldInfoOrErr) - return None; + return std::nullopt; MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); const uint8_t *Ptr = reinterpret_cast<const uint8_t *>(getPtr(*this, DyldInfo.rebase_off)); - return makeArrayRef(Ptr, DyldInfo.rebase_size); + return ArrayRef(Ptr, DyldInfo.rebase_size); } ArrayRef<uint8_t> MachOObjectFile::getDyldInfoBindOpcodes() const { if (!DyldInfoLoadCmd) - return None; + return std::nullopt; auto DyldInfoOrErr = getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); if (!DyldInfoOrErr) - return None; + return std::nullopt; MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); const uint8_t *Ptr = reinterpret_cast<const uint8_t *>(getPtr(*this, DyldInfo.bind_off)); - return makeArrayRef(Ptr, DyldInfo.bind_size); + return ArrayRef(Ptr, DyldInfo.bind_size); } ArrayRef<uint8_t> MachOObjectFile::getDyldInfoWeakBindOpcodes() const { if (!DyldInfoLoadCmd) - return None; + return std::nullopt; auto DyldInfoOrErr = getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); if (!DyldInfoOrErr) - return None; + return std::nullopt; MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); const uint8_t *Ptr = reinterpret_cast<const uint8_t *>(getPtr(*this, DyldInfo.weak_bind_off)); - return makeArrayRef(Ptr, DyldInfo.weak_bind_size); + return ArrayRef(Ptr, DyldInfo.weak_bind_size); } ArrayRef<uint8_t> MachOObjectFile::getDyldInfoLazyBindOpcodes() const { if (!DyldInfoLoadCmd) - return None; + return std::nullopt; auto DyldInfoOrErr = - getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); + getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); if (!DyldInfoOrErr) - return None; + return std::nullopt; MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); const uint8_t *Ptr = reinterpret_cast<const uint8_t *>(getPtr(*this, DyldInfo.lazy_bind_off)); - return makeArrayRef(Ptr, DyldInfo.lazy_bind_size); + return ArrayRef(Ptr, DyldInfo.lazy_bind_size); } -Expected<Optional<MachO::dyld_chained_fixups_header>> -MachOObjectFile::getChainedFixupsHeader() const { +ArrayRef<uint8_t> MachOObjectFile::getDyldInfoExportsTrie() const { + if (!DyldInfoLoadCmd) + return std::nullopt; + + auto DyldInfoOrErr = + getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); + if (!DyldInfoOrErr) + return std::nullopt; + MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); + const uint8_t *Ptr = + reinterpret_cast<const uint8_t *>(getPtr(*this, DyldInfo.export_off)); + return ArrayRef(Ptr, DyldInfo.export_size); +} + +Expected<std::optional<MachO::linkedit_data_command>> +MachOObjectFile::getChainedFixupsLoadCommand() const { // Load the dyld chained fixups load command. if (!DyldChainedFixupsLoadCmd) - return llvm::None; + return std::nullopt; auto DyldChainedFixupsOrErr = getStructOrErr<MachO::linkedit_data_command>( *this, DyldChainedFixupsLoadCmd); if (!DyldChainedFixupsOrErr) return DyldChainedFixupsOrErr.takeError(); - MachO::linkedit_data_command DyldChainedFixups = DyldChainedFixupsOrErr.get(); + const MachO::linkedit_data_command &DyldChainedFixups = + *DyldChainedFixupsOrErr; // If the load command is present but the data offset has been zeroed out, - // as is the case for dylib stubs, return None (no error). + // as is the case for dylib stubs, return std::nullopt (no error). + if (!DyldChainedFixups.dataoff) + return std::nullopt; + return DyldChainedFixups; +} + +Expected<std::optional<MachO::dyld_chained_fixups_header>> +MachOObjectFile::getChainedFixupsHeader() const { + auto CFOrErr = getChainedFixupsLoadCommand(); + if (!CFOrErr) + return CFOrErr.takeError(); + if (!CFOrErr->has_value()) + return std::nullopt; + + const MachO::linkedit_data_command &DyldChainedFixups = **CFOrErr; + uint64_t CFHeaderOffset = DyldChainedFixups.dataoff; - if (CFHeaderOffset == 0) - return DyldChainedFixupsOrErr.takeError(); + uint64_t CFSize = DyldChainedFixups.datasize; // Load the dyld chained fixups header. const char *CFHeaderPtr = getPtr(*this, CFHeaderOffset); @@ -4808,7 +5018,7 @@ MachOObjectFile::getChainedFixupsHeader() const { Twine(CFHeader.starts_offset) + " overlaps with chained fixups header"); } - uint32_t EndOffset = DyldChainedFixups.dataoff + DyldChainedFixups.datasize; + uint32_t EndOffset = CFHeaderOffset + CFSize; if (CFImageStartsOffset + sizeof(MachO::dyld_chained_starts_in_image) > EndOffset) { return malformedError(Twine("bad chained fixups: image starts end ") + @@ -4820,29 +5030,225 @@ MachOObjectFile::getChainedFixupsHeader() const { return CFHeader; } +Expected<std::pair<size_t, std::vector<ChainedFixupsSegment>>> +MachOObjectFile::getChainedFixupsSegments() const { + auto CFOrErr = getChainedFixupsLoadCommand(); + if (!CFOrErr) + return CFOrErr.takeError(); + + std::vector<ChainedFixupsSegment> Segments; + if (!CFOrErr->has_value()) + return std::make_pair(0, Segments); + + const MachO::linkedit_data_command &DyldChainedFixups = **CFOrErr; + + auto HeaderOrErr = getChainedFixupsHeader(); + if (!HeaderOrErr) + return HeaderOrErr.takeError(); + if (!HeaderOrErr->has_value()) + return std::make_pair(0, Segments); + const MachO::dyld_chained_fixups_header &Header = **HeaderOrErr; + + const char *Contents = getPtr(*this, DyldChainedFixups.dataoff); + + auto ImageStartsOrErr = getStructOrErr<MachO::dyld_chained_starts_in_image>( + *this, Contents + Header.starts_offset); + if (!ImageStartsOrErr) + return ImageStartsOrErr.takeError(); + const MachO::dyld_chained_starts_in_image &ImageStarts = *ImageStartsOrErr; + + const char *SegOffsPtr = + Contents + Header.starts_offset + + offsetof(MachO::dyld_chained_starts_in_image, seg_info_offset); + const char *SegOffsEnd = + SegOffsPtr + ImageStarts.seg_count * sizeof(uint32_t); + if (SegOffsEnd > Contents + DyldChainedFixups.datasize) + return malformedError( + "bad chained fixups: seg_info_offset extends past end"); + + const char *LastSegEnd = nullptr; + for (size_t I = 0, N = ImageStarts.seg_count; I < N; ++I) { + auto OffOrErr = + getStructOrErr<uint32_t>(*this, SegOffsPtr + I * sizeof(uint32_t)); + if (!OffOrErr) + return OffOrErr.takeError(); + // seg_info_offset == 0 means there is no associated starts_in_segment + // entry. + if (!*OffOrErr) + continue; + + auto Fail = [&](Twine Message) { + return malformedError("bad chained fixups: segment info" + Twine(I) + + " at offset " + Twine(*OffOrErr) + Message); + }; + + const char *SegPtr = Contents + Header.starts_offset + *OffOrErr; + if (LastSegEnd && SegPtr < LastSegEnd) + return Fail(" overlaps with previous segment info"); + + auto SegOrErr = + getStructOrErr<MachO::dyld_chained_starts_in_segment>(*this, SegPtr); + if (!SegOrErr) + return SegOrErr.takeError(); + const MachO::dyld_chained_starts_in_segment &Seg = *SegOrErr; + + LastSegEnd = SegPtr + Seg.size; + if (Seg.pointer_format < 1 || Seg.pointer_format > 12) + return Fail(" has unknown pointer format: " + Twine(Seg.pointer_format)); + + const char *PageStart = + SegPtr + offsetof(MachO::dyld_chained_starts_in_segment, page_start); + const char *PageEnd = PageStart + Seg.page_count * sizeof(uint16_t); + if (PageEnd > SegPtr + Seg.size) + return Fail(" : page_starts extend past seg_info size"); + + // FIXME: This does not account for multiple offsets on a single page + // (DYLD_CHAINED_PTR_START_MULTI; 32-bit only). + std::vector<uint16_t> PageStarts; + for (size_t PageIdx = 0; PageIdx < Seg.page_count; ++PageIdx) { + uint16_t Start; + memcpy(&Start, PageStart + PageIdx * sizeof(uint16_t), sizeof(uint16_t)); + if (isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(Start); + PageStarts.push_back(Start); + } + + Segments.emplace_back(I, *OffOrErr, Seg, std::move(PageStarts)); + } + + return std::make_pair(ImageStarts.seg_count, Segments); +} + +// The special library ordinals have a negative value, but they are encoded in +// an unsigned bitfield, so we need to sign extend the value. +template <typename T> static int getEncodedOrdinal(T Value) { + if (Value == static_cast<T>(MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) || + Value == static_cast<T>(MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP) || + Value == static_cast<T>(MachO::BIND_SPECIAL_DYLIB_WEAK_LOOKUP)) + return SignExtend32<sizeof(T) * CHAR_BIT>(Value); + return Value; +} + +template <typename T, unsigned N> +static std::array<T, N> getArray(const MachOObjectFile &O, const void *Ptr) { + std::array<T, N> RawValue; + memcpy(RawValue.data(), Ptr, N * sizeof(T)); + if (O.isLittleEndian() != sys::IsLittleEndianHost) + for (auto &Element : RawValue) + sys::swapByteOrder(Element); + return RawValue; +} + Expected<std::vector<ChainedFixupTarget>> MachOObjectFile::getDyldChainedFixupTargets() const { + auto CFOrErr = getChainedFixupsLoadCommand(); + if (!CFOrErr) + return CFOrErr.takeError(); + + std::vector<ChainedFixupTarget> Targets; + if (!CFOrErr->has_value()) + return Targets; + + const MachO::linkedit_data_command &DyldChainedFixups = **CFOrErr; + auto CFHeaderOrErr = getChainedFixupsHeader(); if (!CFHeaderOrErr) return CFHeaderOrErr.takeError(); - std::vector<ChainedFixupTarget> Targets; if (!(*CFHeaderOrErr)) return Targets; - return Targets; + const MachO::dyld_chained_fixups_header &Header = **CFHeaderOrErr; + + size_t ImportSize = 0; + if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT) + ImportSize = sizeof(MachO::dyld_chained_import); + else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND) + ImportSize = sizeof(MachO::dyld_chained_import_addend); + else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND64) + ImportSize = sizeof(MachO::dyld_chained_import_addend64); + else + return malformedError("bad chained fixups: unknown imports format: " + + Twine(Header.imports_format)); + + const char *Contents = getPtr(*this, DyldChainedFixups.dataoff); + const char *Imports = Contents + Header.imports_offset; + size_t ImportsEndOffset = + Header.imports_offset + ImportSize * Header.imports_count; + const char *ImportsEnd = Contents + ImportsEndOffset; + const char *Symbols = Contents + Header.symbols_offset; + const char *SymbolsEnd = Contents + DyldChainedFixups.datasize; + + if (ImportsEnd > Symbols) + return malformedError("bad chained fixups: imports end " + + Twine(ImportsEndOffset) + " extends past end " + + Twine(DyldChainedFixups.datasize)); + + if (ImportsEnd > Symbols) + return malformedError("bad chained fixups: imports end " + + Twine(ImportsEndOffset) + " overlaps with symbols"); + + // We use bit manipulation to extract data from the bitfields. This is correct + // for both LE and BE hosts, but we assume that the object is little-endian. + if (!isLittleEndian()) + return createError("parsing big-endian chained fixups is not implemented"); + for (const char *ImportPtr = Imports; ImportPtr < ImportsEnd; + ImportPtr += ImportSize) { + int LibOrdinal; + bool WeakImport; + uint32_t NameOffset; + uint64_t Addend; + if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT) { + static_assert(sizeof(uint32_t) == sizeof(MachO::dyld_chained_import)); + auto RawValue = getArray<uint32_t, 1>(*this, ImportPtr); + + LibOrdinal = getEncodedOrdinal<uint8_t>(RawValue[0] & 0xFF); + WeakImport = (RawValue[0] >> 8) & 1; + NameOffset = RawValue[0] >> 9; + Addend = 0; + } else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND) { + static_assert(sizeof(uint64_t) == + sizeof(MachO::dyld_chained_import_addend)); + auto RawValue = getArray<uint32_t, 2>(*this, ImportPtr); + + LibOrdinal = getEncodedOrdinal<uint8_t>(RawValue[0] & 0xFF); + WeakImport = (RawValue[0] >> 8) & 1; + NameOffset = RawValue[0] >> 9; + Addend = bit_cast<int32_t>(RawValue[1]); + } else if (Header.imports_format == MachO::DYLD_CHAINED_IMPORT_ADDEND64) { + static_assert(2 * sizeof(uint64_t) == + sizeof(MachO::dyld_chained_import_addend64)); + auto RawValue = getArray<uint64_t, 2>(*this, ImportPtr); + + LibOrdinal = getEncodedOrdinal<uint16_t>(RawValue[0] & 0xFFFF); + NameOffset = (RawValue[0] >> 16) & 1; + WeakImport = RawValue[0] >> 17; + Addend = RawValue[1]; + } else { + llvm_unreachable("Import format should have been checked"); + } + + const char *Str = Symbols + NameOffset; + if (Str >= SymbolsEnd) + return malformedError("bad chained fixups: symbol offset " + + Twine(NameOffset) + " extends past end " + + Twine(DyldChainedFixups.datasize)); + Targets.emplace_back(LibOrdinal, NameOffset, Str, Addend, WeakImport); + } + + return std::move(Targets); } -ArrayRef<uint8_t> MachOObjectFile::getDyldInfoExportsTrie() const { - if (!DyldInfoLoadCmd) - return None; +ArrayRef<uint8_t> MachOObjectFile::getDyldExportsTrie() const { + if (!DyldExportsTrieLoadCmd) + return std::nullopt; - auto DyldInfoOrErr = - getStructOrErr<MachO::dyld_info_command>(*this, DyldInfoLoadCmd); - if (!DyldInfoOrErr) - return None; - MachO::dyld_info_command DyldInfo = DyldInfoOrErr.get(); + auto DyldExportsTrieOrError = getStructOrErr<MachO::linkedit_data_command>( + *this, DyldExportsTrieLoadCmd); + if (!DyldExportsTrieOrError) + return std::nullopt; + MachO::linkedit_data_command DyldExportsTrie = DyldExportsTrieOrError.get(); const uint8_t *Ptr = - reinterpret_cast<const uint8_t *>(getPtr(*this, DyldInfo.export_off)); - return makeArrayRef(Ptr, DyldInfo.export_size); + reinterpret_cast<const uint8_t *>(getPtr(*this, DyldExportsTrie.dataoff)); + return ArrayRef(Ptr, DyldExportsTrie.datasize); } SmallVector<uint64_t> MachOObjectFile::getFunctionStarts() const { @@ -4862,10 +5268,10 @@ SmallVector<uint64_t> MachOObjectFile::getFunctionStarts() const { ArrayRef<uint8_t> MachOObjectFile::getUuid() const { if (!UuidLoadCmd) - return None; + return std::nullopt; // Returning a pointer is fine as uuid doesn't need endian swapping. const char *Ptr = UuidLoadCmd + offsetof(MachO::uuid_command, uuid); - return makeArrayRef(reinterpret_cast<const uint8_t *>(Ptr), 16); + return ArrayRef(reinterpret_cast<const uint8_t *>(Ptr), 16); } StringRef MachOObjectFile::getStringTableData() const { |