Loading...
--- dyld/dyld-1340/common/MachOLayout.cpp
+++ /dev/null
@@ -1,2395 +0,0 @@
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include "Array.h"
-#include "Header.h"
-#include "MachOLayout.h"
-#include "MachOFile.h"
-
-#include <TargetConditionals.h>
-#include "Defines.h"
-#if SUPPORT_CLASSIC_RELOCS
- #include <mach-o/reloc.h>
- #include <mach-o/x86_64/reloc.h>
-#endif
-#include <mach-o/nlist.h>
-
-// FIXME: We should get this from cctools
-#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F
-
-namespace mach_o
-{
-
-// MARK: --- Layout methods ---
-
-Layout::Layout(MachOFileRef mf, std::span<SegmentLayout> segments, const LinkeditLayout& linkedit)
- : mf(std::move(mf)), segments(segments), linkedit(linkedit)
-{
-}
-
-uint64_t Layout::textUnslidVMAddr() const
-{
- for ( const SegmentLayout& segment : this->segments ) {
- if ( segment.kind == SegmentLayout::Kind::text )
- return segment.vmAddr;
- }
-
- // MachOFile::preferredLoadAddress seems to return 0 if we didn't find __TEXT, so match it
- return 0;
-}
-
-bool Layout::isSwiftLibrary() const
-{
- if ( std::optional<uint32_t> flags = this->getObjcInfoFlags(); flags.has_value() ) {
- uint32_t swiftVersion = ((flags.value() >> 8) & 0xFF);
- return (swiftVersion != 0);
- }
- return false;
-}
-
-std::optional<uint32_t> Layout::getObjcInfoFlags() const
-{
- struct objc_image_info {
- int32_t version;
- uint32_t flags;
- };
-
- __block std::optional<uint32_t> flags;
- ((const Header*)this->mf)->forEachSection(^(const Header::SegmentInfo& segInfo, const Header::SectionInfo& sectInfo, bool& stop) {
- if ( (sectInfo.sectionName.starts_with("__objc_imageinfo")) && sectInfo.segmentName.starts_with("__DATA") ) {
- uint64_t segmentOffset = sectInfo.fileOffset - segInfo.fileOffset;
- objc_image_info* info = (objc_image_info*)(this->segments[sectInfo.segIndex].buffer + segmentOffset);
- flags = info->flags;
- stop = true;
- }
- });
- return flags;
-}
-
-bool Layout::hasSection(std::string_view segmentName, std::string_view sectionName) const
-{
- __block bool result = false;
- ((const Header*)this->mf)->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
- if ( (sectInfo.segmentName == segmentName) && (sectInfo.sectionName == sectionName) ) {
- result = true;
- stop = true;
- }
- });
- return result;
-}
-
-namespace {
- struct LinkEditContentChunk
- {
- const char* name;
- uint32_t alignment;
- uint32_t fileOffsetStart;
- uint32_t size;
-
- // only have a few chunks, so bubble sort is ok. Don't use libc's qsort because it may call malloc
- static void sort(LinkEditContentChunk array[], unsigned long count)
- {
- for (unsigned i=0; i < count-1; ++i) {
- bool done = true;
- for (unsigned j=0; j < count-i-1; ++j) {
- if ( array[j].fileOffsetStart > array[j+1].fileOffsetStart ) {
- LinkEditContentChunk temp = array[j];
- array[j] = array[j+1];
- array[j+1] = temp;
- done = false;
- }
- }
- if ( done )
- break;
- }
- }
- };
-} // anonymous namespace
-
-bool Layout::isValidLinkeditLayout(Diagnostics &diag, const char *path) const
-{
- typedef dyld3::MachOFile::Malformed Malformed;
-
- const uint32_t ptrSize = this->mf->pointerSize();
-
- // build vector of all blobs in LINKEDIT
- LinkEditContentChunk blobs[32];
- LinkEditContentChunk* bp = blobs;
- if ( const Linkedit& blob = this->linkedit.rebaseOpcodes; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"rebase opcodes", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.regularBindOpcodes; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"bind opcodes", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.weakBindOpcodes; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"weak bind opcodes", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.lazyBindOpcodes; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"lazy bind opcodes", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.exportsTrie; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"exports trie", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.chainedFixups; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"chained fixups", ptrSize, blob.fileOffset, blob.bufferSize };
- }
-
-#if SUPPORT_CLASSIC_RELOCS
- if ( const Linkedit& blob = this->linkedit.localRelocs; blob.hasValue() ) {
- if ( blob.entryCount != 0 ) {
- uint32_t bufferSize = (uint32_t)(blob.entryCount * sizeof(relocation_info));
- *bp++ = {"local relocations", ptrSize, blob.fileOffset, bufferSize };
- }
- }
- if ( const Linkedit& blob = this->linkedit.externRelocs; blob.hasValue() ) {
- if ( blob.entryCount != 0 ) {
- uint32_t bufferSize = (uint32_t)(blob.entryCount * sizeof(relocation_info));
- *bp++ = {"external relocations", ptrSize, blob.fileOffset, bufferSize };
- }
- }
-#endif
- if ( const Linkedit& blob = this->linkedit.indirectSymbolTable; blob.hasValue() ) {
- if ( blob.entryCount != 0 ) {
- uint32_t bufferSize = (uint32_t)(blob.entryCount * sizeof(uint32_t));
- *bp++ = {"indirect symbol table", 4, blob.fileOffset, bufferSize };
- }
- }
-
- if ( const Linkedit& blob = this->linkedit.splitSegInfo; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"shared cache info", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.functionStarts; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"function starts", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.dataInCode; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"data in code", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.symbolTable; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"symbol table", ptrSize, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.symbolStrings; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"symbol table strings", 1, blob.fileOffset, blob.bufferSize };
- }
- if ( const Linkedit& blob = this->linkedit.codeSignature; blob.hasValue() ) {
- if ( blob.bufferSize != 0 )
- *bp++ = {"code signature", ptrSize, blob.fileOffset, blob.bufferSize };
- }
-
- // check for bad combinations
- if ( (this->linkedit.dyldInfoCmd == LC_DYLD_INFO_ONLY) ) {
- if ( (this->linkedit.localRelocs.entryCount != 0) && this->mf->enforceFormat(Malformed::dyldInfoAndlocalRelocs) ) {
- diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations", path);
- return false;
- }
- if ( this->linkedit.externRelocs.entryCount != 0 ) {
- diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations", path);
- return false;
- }
- }
-
- bool checkMissingDyldInfo = true;
-#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
- checkMissingDyldInfo = this->mf->isDyldManaged() && !this->mf->isStaticExecutable();
-#endif
- if ( (this->linkedit.dyldInfoCmd == 0 ) && !this->linkedit.hasDynSymTab && checkMissingDyldInfo ) {
- diag.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path);
- return false;
- }
-
- // FIXME: Remove this hack
-#if BUILDING_APP_CACHE_UTIL
- if ( this->mf->isFileSet() )
- return true;
-#endif
-
- const unsigned long blobCount = bp - blobs;
- if ( blobCount == 0 ) {
- diag.error("in '%s' malformed mach-o missing LINKEDIT", path);
- return false;
- }
-
- // Find the linkedit
- uint32_t linkeditFileOffset = ~0U;
- uint32_t linkeditFileSize = ~0U;
- for ( const SegmentLayout& segment : this->segments ) {
- if ( segment.kind == SegmentLayout::Kind::linkedit ) {
- linkeditFileOffset = (uint32_t)segment.fileOffset;
- linkeditFileSize = (uint32_t)segment.fileSize;
- break;
- }
- }
-
- uint32_t linkeditFileEnd = linkeditFileOffset + linkeditFileSize;
-
-
- // sort blobs by file-offset and error on overlaps
- LinkEditContentChunk::sort(blobs, blobCount);
- uint32_t prevEnd = linkeditFileOffset;
- const char* prevName = "start of LINKEDIT";
- for (unsigned long i=0; i < blobCount; ++i) {
- const LinkEditContentChunk& blob = blobs[i];
- if ( blob.fileOffsetStart < prevEnd ) {
- diag.error("in '%s' LINKEDIT overlap of %s and %s", path, prevName, blob.name);
- return false;
- }
- if (dyld3::greaterThanAddOrOverflow(blob.fileOffsetStart, blob.size, linkeditFileEnd)) {
- diag.error("in '%s' LINKEDIT content '%s' extends beyond end of segment", path, blob.name);
- return false;
- }
- if ( (blob.fileOffsetStart & (blob.alignment-1)) != 0 ) {
- // <rdar://problem/51115705> relax code sig alignment for pre iOS13
- Malformed kind = (strcmp(blob.name, "code signature") == 0) ? Malformed::codeSigAlignment : Malformed::linkeditAlignment;
- if ( this->mf->enforceFormat(kind) )
- diag.error("in '%s' mis-aligned LINKEDIT content '%s'", path, blob.name);
- }
- prevEnd = blob.fileOffsetStart + blob.size;
- prevName = blob.name;
- }
-
- // Check for invalid symbol table sizes
- if ( this->linkedit.hasSymTab ) {
- const Linkedit& symbolTable = this->linkedit.symbolTable;
- if ( symbolTable.entryCount > 0x10000000 ) {
- diag.error("in '%s' malformed mach-o image: symbol table too large", path);
- return false;
- }
- if ( this->linkedit.hasDynSymTab ) {
- // validate indirect symbol table
- const Linkedit& localSymbolTable = this->linkedit.localSymbolTable;
- const Linkedit& globalSymbolTable = this->linkedit.globalSymbolTable;
- const Linkedit& undefSymbolTable = this->linkedit.undefSymbolTable;
- const Linkedit& indirectSymbolTable = this->linkedit.indirectSymbolTable;
- if ( indirectSymbolTable.entryCount != 0 ) {
- if ( indirectSymbolTable.entryCount > 0x10000000 ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table too large", path);
- return false;
- }
- }
- if ( (localSymbolTable.entryCount > symbolTable.entryCount) || (localSymbolTable.entryIndex > symbolTable.entryCount) ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count exceeds total symbols", path);
- return false;
- }
- if ( (localSymbolTable.entryIndex + localSymbolTable.entryCount) < localSymbolTable.entryIndex ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count wraps", path);
- return false;
- }
- if ( (globalSymbolTable.entryCount > symbolTable.entryCount)
- || (globalSymbolTable.entryIndex > symbolTable.entryCount) ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols", path);
- return false;
- }
- if ( (globalSymbolTable.entryIndex + globalSymbolTable.entryCount) < globalSymbolTable.entryIndex ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count wraps", path);
- return false;
- }
- if ( (undefSymbolTable.entryCount > symbolTable.entryCount) || (undefSymbolTable.entryIndex > symbolTable.entryCount) ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols", path);
- return false;
- }
- if ( (undefSymbolTable.entryIndex + undefSymbolTable.entryCount) < undefSymbolTable.entryIndex ) {
- diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count wraps", path);
- return false;
- }
- }
- }
-
- return true;
-}
-
-bool Layout::findExportedSymbol(Diagnostics& diag, const char* symbolName, bool weakImport,
- FoundSymbol& foundInfo) const
-{
- if ( this->linkedit.exportsTrie.hasValue() ) {
- // FIXME: Move all this to the ExportTrie class
- const uint8_t* trieStart = this->linkedit.exportsTrie.buffer;
- const uint8_t* trieEnd = trieStart + this->linkedit.exportsTrie.bufferSize;
- const uint8_t* node = dyld3::MachOFile::trieWalk(diag, trieStart, trieEnd, symbolName);
- if ( node == nullptr ) {
- // symbol not exported from this image. Seach any re-exported dylibs
- // FIXME: Implement this
-#if 0
- __block unsigned depIndex = 0;
- __block bool foundInReExportedDylib = false;
- forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if ( isReExport && findDependent ) {
- if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
- if ( depMH->findExportedSymbol(diag, symbolName, weakImport, foundInfo, findDependent) ) {
- stop = true;
- foundInReExportedDylib = true;
- }
- }
- }
- ++depIndex;
- });
- return foundInReExportedDylib;
-#endif
- return false;
- }
- const uint8_t* p = node;
- const uint64_t flags = dyld3::MachOFile::read_uleb128(diag, p, trieEnd);
- if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
- // FIXME: Implement this
-#if 0
- if ( !findDependent )
- return false;
- // re-export from another dylib, lookup there
- const uint64_t ordinal = dyld3::MachOFile::read_uleb128(diag, p, trieEnd);
- const char* importedName = (char*)p;
- if ( importedName[0] == '\0' )
- importedName = symbolName;
- if ( (ordinal == 0) || (ordinal > dependentDylibCount()) ) {
- diag.error("re-export ordinal %lld out of range for %s", ordinal, symbolName);
- return false;
- }
- uint32_t depIndex = (uint32_t)(ordinal-1);
- if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
- return depMH->findExportedSymbol(diag, importedName, weakImport, foundInfo, findDependent);
- }
- else if (weakImport) {
- return false;
- }
- else {
- diag.error("dependent dylib %lld not found for re-exported symbol %s", ordinal, symbolName);
- return false;
- }
-#endif
- return false;
- }
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- foundInfo.isThreadLocal = false;
- foundInfo.isWeakDef = false;
- foundInfo.foundInDylib = this->mf;
- foundInfo.value = dyld3::MachOFile::read_uleb128(diag, p, trieEnd);
- foundInfo.resolverFuncOffset = 0;
- foundInfo.foundSymbolName = symbolName;
- if ( diag.hasError() )
- return false;
- switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
- case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
- if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- foundInfo.resolverFuncOffset = (uint32_t)dyld3::MachOFile::read_uleb128(diag, p, trieEnd);
- }
- else {
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- }
- if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
- foundInfo.isWeakDef = true;
- break;
- case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
- foundInfo.isThreadLocal = true;
- break;
- case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
- foundInfo.kind = FoundSymbol::Kind::absolute;
- break;
- default:
- diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-trieStart));
- return false;
- }
- return true;
- }
- else {
- // this is an old binary (before macOS 10.6), scan the symbol table
- foundInfo.foundInDylib.reset();
-
- SymbolTable symbolTable(*this);
- symbolTable.forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type,
- uint8_t n_sect, uint16_t n_desc, bool& stop) {
- if ( strcmp(aSymbolName, symbolName) == 0 ) {
- foundInfo.kind = FoundSymbol::Kind::headerOffset;
- foundInfo.isThreadLocal = false;
- foundInfo.foundInDylib = this->mf;
- foundInfo.value = n_value - this->textUnslidVMAddr();
- foundInfo.resolverFuncOffset = 0;
- foundInfo.foundSymbolName = symbolName;
- stop = true;
- }
- });
-
- // FIXME: Implement this
-#if 0
- if ( !foundInfo.foundInDylib.has_value() ) {
- // symbol not exported from this image. Search any re-exported dylibs
- __block unsigned depIndex = 0;
- forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if ( isReExport && findDependent ) {
- if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
- if ( depMH->findExportedSymbol(diag, symbolName, weakImport, foundInfo, findDependent) ) {
- stop = true;
- }
- }
- }
- ++depIndex;
- });
- }
-#endif
- return foundInfo.foundInDylib.has_value();
- }
-}
-
-// MARK: --- Fixups methods ---
-
-Fixups::Fixups(const Layout& layout)
- : layout(layout)
-{
-}
-
-void Fixups::forEachBindTarget(Diagnostics& diag, bool allowLazyBinds, intptr_t slide,
- void (^handler)(const BindTargetInfo& info, bool& stop),
- void (^overrideHandler)(const BindTargetInfo& info, bool& stop)) const
-{
- if ( this->layout.mf->isPreload() )
- return;
- if ( this->layout.mf->hasChainedFixups() )
- this->forEachBindTarget_ChainedFixups(diag, handler);
- else if ( this->layout.mf->hasOpcodeFixups() )
- this->forEachBindTarget_Opcodes(diag, allowLazyBinds, handler, overrideHandler);
-#if SUPPORT_CLASSIC_RELOCS
- else
- this->forEachBindTarget_Relocations(diag, slide, handler);
-#endif
-}
-
-void Fixups::forEachBindTarget_ChainedFixups(Diagnostics& diag, void (^handler)(const BindTargetInfo& info, bool& stop)) const
-{
- __block unsigned targetIndex = 0;
- this->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
- BindTargetInfo info;
- info.targetIndex = targetIndex;
- info.libOrdinal = libOrdinal;
- info.symbolName = symbolName;
- info.addend = addend;
- info.weakImport = weakImport;
- info.lazyBind = false;
- handler(info, stop);
- ++targetIndex;
- });
-
- // The C++ spec says main executables can define non-weak functions which override weak-defs in dylibs
- // This happens automatically for anything bound at launch, but the dyld cache is pre-bound so we need
- // to patch any binds that are overridden by this non-weak in the main executable.
- if ( diag.noError() && this->layout.mf->isMainExecutable() && this->layout.mf->hasWeakDefs() ) {
- dyld3::MachOFile::forEachTreatAsWeakDef(^(const char* symbolName) {
- BindTargetInfo info;
- info.targetIndex = targetIndex;
- info.libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
- info.symbolName = symbolName;
- info.addend = 0;
- info.weakImport = false;
- info.lazyBind = false;
- bool stop = false;
- handler(info, stop);
- ++targetIndex;
- });
- }
-}
-
-void Fixups::parseOrgArm64eChainedFixups(Diagnostics& diag,
- void (^targetCount)(uint32_t totalTargets, bool& stop),
- void (^addTarget)(bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
- void (^addChainStart)(uint32_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop)) const
-{
- bool stop = false;
-
- const uint32_t dylibCount = this->layout.mf->dependentDylibCount();
-
- if ( this->layout.linkedit.regularBindOpcodes.hasValue() ) {
- // process bind opcodes
- const uint8_t* p = this->layout.linkedit.regularBindOpcodes.buffer;
- const uint8_t* end = p + this->layout.linkedit.regularBindOpcodes.bufferSize;
- uint8_t type = 0;
- uint64_t segmentOffset = 0;
- uint8_t segmentIndex = 0;
- const char* symbolName = NULL;
- int libraryOrdinal = 0;
- bool segIndexSet = false;
- bool libraryOrdinalSet = false;
- uint64_t targetTableCount;
- uint64_t addend = 0;
- bool weakImport = false;
- while ( !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- stop = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- libraryOrdinal = immediate;
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- libraryOrdinal = (int)dyld3::MachOFile::read_uleb128(diag, p, end);
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- // the special ordinals are negative numbers
- if ( immediate == 0 )
- libraryOrdinal = 0;
- else {
- int8_t signExtended = BIND_OPCODE_MASK | immediate;
- libraryOrdinal = signExtended;
- }
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = dyld3::MachOFile::read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = dyld3::MachOFile::read_sleb128(diag, p, end);
- break;
- case BIND_OPCODE_DO_BIND:
- if ( addTarget )
- addTarget(libraryOrdinalSet, dylibCount, libraryOrdinal, type, symbolName, addend, weakImport, stop);
- break;
- case BIND_OPCODE_THREADED:
- switch (immediate) {
- case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
- targetTableCount = dyld3::MachOFile::read_uleb128(diag, p, end);
- if ( targetTableCount > 65535 ) {
- diag.error("BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB size too large");
- stop = true;
- }
- else {
- if ( targetCount )
- targetCount((uint32_t)targetTableCount, stop);
- }
- break;
- case BIND_SUBOPCODE_THREADED_APPLY:
- if ( addChainStart )
- addChainStart(segmentIndex, segIndexSet, segmentOffset, DYLD_CHAINED_PTR_ARM64E, stop);
- break;
- default:
- diag.error("bad BIND_OPCODE_THREADED sub-opcode 0x%02X", immediate);
- }
- break;
- default:
- diag.error("bad bind opcode 0x%02X", immediate);
- }
- }
- if ( diag.hasError() )
- return;
- }
-}
-
-void Fixups::forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const
-{
- if ( this->layout.linkedit.regularBindOpcodes.hasValue() ) {
- parseOrgArm64eChainedFixups(diag, nullptr, ^(bool libraryOrdinalSet, uint32_t dylibCount,
- int libOrdinal, uint8_t type, const char* symbolName, uint64_t fixAddend, bool weakImport, bool& stopChain) {
- callback(libOrdinal, symbolName, fixAddend, weakImport, stopChain);
- }, nullptr);
- }
- else if ( this->layout.linkedit.chainedFixups.hasValue() ) {
- const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)this->layout.linkedit.chainedFixups.buffer;
- dyld3::MachOFile::forEachChainedFixupTarget(diag, header, this->layout.linkedit.chainedFixups.cmd, callback);
- }
-}
-
-#if (BUILDING_DYLD || BUILDING_LIBDYLD) && !__arm64e__
- #define SUPPORT_OLD_ARM64E_FORMAT 0
-#else
- #define SUPPORT_OLD_ARM64E_FORMAT 1
-#endif
-
-// find dyld_chained_starts_in_image* in image
-// if old arm64e binary, synthesize dyld_chained_starts_in_image*
-void Fixups::withThreadedRebaseAsChainStarts(Diagnostics& diag, void (^callback)(const dyld_chained_fixups_header* header, uint64_t fixupsSize)) const
-{
-#if SUPPORT_OLD_ARM64E_FORMAT
- // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up
- // old arm64e binary, create a dyld_chained_starts_in_image for caller
- uint64_t baseAddress = ((const Header*)this->layout.mf)->preferredLoadAddress();
- uint64_t imagePageCount = this->layout.mf->mappedSize()/0x4000;
- size_t bufferSize = this->layout.linkedit.regularBindOpcodes.bufferSize + (size_t)imagePageCount*sizeof(uint16_t) + 512;
- BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer, bufferSize);
- uint8_t* bufferEnd = &buffer[bufferSize];
- dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)buffer;
- header->fixups_version = 0;
- header->starts_offset = sizeof(dyld_chained_fixups_header);
- header->imports_offset = 0;
- header->symbols_offset = 0;
- header->imports_count = 0;
- header->imports_format = 0;
- header->symbols_format = 0;
- dyld_chained_starts_in_image* starts = (dyld_chained_starts_in_image*)(dyld_chained_starts_in_image*)((uint8_t*)header + header->starts_offset);
- starts->seg_count = (uint32_t)this->layout.segments.size();
- for (uint32_t i=0; i < starts->seg_count; ++i)
- starts->seg_info_offset[i] = 0;
- __block uint8_t curSegIndex = 0;
- __block dyld_chained_starts_in_segment* curSeg = (dyld_chained_starts_in_segment*)(&(starts->seg_info_offset[starts->seg_count]));
- parseOrgArm64eChainedFixups(diag, nullptr, nullptr, ^(uint32_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop) {
- uint32_t pageIndex = (uint32_t)(segmentOffset/0x1000);
- if ( segmentIndex != curSegIndex ) {
- if ( curSegIndex == 0 ) {
- starts->seg_info_offset[segmentIndex] = (uint32_t)((uint8_t*)curSeg - (uint8_t*)starts);
- }
- else {
- starts->seg_info_offset[segmentIndex] = (uint32_t)((uint8_t*)(&curSeg->page_start[curSeg->page_count]) - (uint8_t*)starts);
- curSeg = (dyld_chained_starts_in_segment*)((uint8_t*)starts+starts->seg_info_offset[segmentIndex]);
- assert((uint8_t*)curSeg < bufferEnd);
- }
- curSeg->page_count = 0;
- curSegIndex = segmentIndex;
- }
- while ( curSeg->page_count != pageIndex ) {
- assert((uint8_t*)(&curSeg->page_start[curSeg->page_count]) < bufferEnd);
- curSeg->page_start[curSeg->page_count] = 0xFFFF;
- curSeg->page_count++;
- }
- curSeg->size = (uint32_t)((uint8_t*)(&curSeg->page_start[pageIndex]) - (uint8_t*)curSeg);
- curSeg->page_size = 0x1000; // old arm64e encoding used 4KB pages
- curSeg->pointer_format = DYLD_CHAINED_PTR_ARM64E;
- curSeg->segment_offset = this->layout.segments[segmentIndex].vmAddr - baseAddress;
- curSeg->max_valid_pointer = 0;
- curSeg->page_count = pageIndex+1;
- assert((uint8_t*)(&curSeg->page_start[pageIndex]) < bufferEnd);
- curSeg->page_start[pageIndex] = segmentOffset & 0xFFF;
- //fprintf(stderr, "segment_offset=0x%llX, vmAddr=0x%llX\n", curSeg->segment_offset, segments[segmentIndex].vmAddr );
- //printf("segIndex=%d, segOffset=0x%08llX, page_start[%d]=0x%04X, page_start[%d]=0x%04X\n",
- // segmentIndex, segmentOffset, pageIndex, curSeg->page_start[pageIndex], pageIndex-1, pageIndex ? curSeg->page_start[pageIndex-1] : 0);
- });
- callback(header, (uint64_t)bufferSize);
-#endif
-}
-
-const dyld_chained_fixups_header* Fixups::chainedFixupsHeader() const {
- if ( this->layout.linkedit.chainedFixups.hasValue() ) {
- // find dyld_chained_starts_in_image from dyld_chained_fixups_header
- return (dyld_chained_fixups_header*)this->layout.linkedit.chainedFixups.buffer;
- }
- return nullptr;
-}
-
-// find dyld_chained_starts_in_image* in image
-// if old arm64e binary, synthesize dyld_chained_starts_in_image*
-void Fixups::withChainStarts(Diagnostics& diag, void (^callback)(const dyld_chained_starts_in_image*)) const
-{
- if ( const dyld_chained_fixups_header* chainHeader = this->chainedFixupsHeader() ) {
- // find dyld_chained_starts_in_image from dyld_chained_fixups_header
- callback((dyld_chained_starts_in_image*)((uint8_t*)chainHeader + chainHeader->starts_offset));
- }
-#if SUPPORT_OLD_ARM64E_FORMAT
- // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up
- else if ( this->layout.linkedit.regularBindOpcodes.hasValue() && (this->layout.mf->cputype == CPU_TYPE_ARM64) && (this->layout.mf->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
- // old arm64e binary, create a dyld_chained_starts_in_image for caller
- this->withThreadedRebaseAsChainStarts(diag, ^(const dyld_chained_fixups_header* header, uint64_t fixupsSize) {
- callback((dyld_chained_starts_in_image*)((uint8_t*)header + header->starts_offset));
- });
- }
-#endif
- else {
- diag.error("image does not use chained fixups");
- }
-}
-
-void Fixups::forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers,
- void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, uint64_t fixupSegmentOffset,
- const dyld_chained_starts_in_segment* segInfo, bool& stop)) const
-{
-
- bool stopped = false;
- for (uint32_t segIndex=0; segIndex < starts->seg_count && !stopped; ++segIndex) {
- if ( starts->seg_info_offset[segIndex] == 0 )
- continue;
- const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]);
- auto adaptor = ^(ChainedFixupPointerOnDisk* fixupLocation, uint64_t fixupSegmentOffset, bool& stop) {
- handler(fixupLocation, fixupSegmentOffset, segInfo, stop);
- };
- forEachFixupInSegmentChains(diag, segInfo, segIndex, notifyNonPointers, adaptor);
- }
-}
-
-void Fixups::forEachFixupInSegmentChains(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool notifyNonPointers,
- void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, uint64_t fixupSegmentOffset, bool& stop)) const
-{
- const uint8_t* segmentBuffer = this->layout.segments[segIndex].buffer;
- auto adaptor = ^(ChainedFixupPointerOnDisk* fixupLocation, bool& stop) {
- uint64_t fixupSegmentOffset = (uint64_t)fixupLocation - (uint64_t)segmentBuffer;
- handler(fixupLocation, fixupSegmentOffset, stop);
- };
- bool stopped = false;
- for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) {
- uint16_t offsetInPage = segInfo->page_start[pageIndex];
- if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
- continue;
- const uint8_t* pageContentStart = segmentBuffer + (pageIndex * segInfo->page_size);
- if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
- // 32-bit chains which may need multiple starts per page
- uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
- bool chainEnd = false;
- while (!stopped && !chainEnd) {
- chainEnd = (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST);
- offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
- ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
-
- stopped = dyld3::MachOFile::walkChain(diag, chain, segInfo->pointer_format, notifyNonPointers, segInfo->max_valid_pointer, adaptor);
- ++overflowIndex;
- }
- }
- else {
- // one chain per page
- ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
- stopped = dyld3::MachOFile::walkChain(diag, chain, segInfo->pointer_format, notifyNonPointers, segInfo->max_valid_pointer, adaptor);
- }
- }
-}
-
-void Fixups::forEachFixupChainSegment(Diagnostics& diag, const dyld_chained_starts_in_image* starts,
- void (^handler)(const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool& stop))
-{
- bool stopped = false;
- for (uint32_t segIndex=0; segIndex < starts->seg_count && !stopped; ++segIndex) {
- if ( starts->seg_info_offset[segIndex] == 0 )
- continue;
- const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]);
- handler(segInfo, segIndex, stopped);
- }
-}
-
-uint16_t Fixups::chainedPointerFormat() const
-{
- if ( const dyld_chained_fixups_header* chainHeader = this->chainedFixupsHeader() ) {
- // get pointer format from chain info struct in LINKEDIT
- return dyld3::MachOFile::chainedPointerFormat(chainHeader);
- }
- assert(this->layout.mf->cputype == CPU_TYPE_ARM64
- && (this->layout.mf->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E)
- && "chainedPointerFormat() called on non-chained binary");
- return DYLD_CHAINED_PTR_ARM64E;
-}
-
-// walk through all binds, unifying weak, lazy, and regular binds
-void Fixups::forEachBindUnified_Opcodes(Diagnostics& diag, bool allowLazyBinds,
- void (^handler)(uint64_t runtimeOffset, uint32_t segmentIndex, const BindTargetInfo& targetInfo, bool& stop),
- void (^overrideHandler)(uint64_t runtimeOffset, uint32_t segmentIndex, const BindTargetInfo& targetInfo, bool& stop)) const
-{
- {
- __block unsigned targetIndex = 0;
- __block BindTargetInfo targetInfo;
- BindDetailedHandler binder = ^(const char* opcodeName,
- bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
- uint32_t pointerSize, uint32_t segmentIndex, uint64_t segmentOffset,
- uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
- uint64_t addend, bool targetOrAddendChanged, bool& stop) {
- uint64_t bindVmOffset = this->layout.segments[segmentIndex].vmAddr + segmentOffset;
- uint64_t runtimeOffset = bindVmOffset - this->layout.textUnslidVMAddr();
- if ( targetOrAddendChanged ) {
- targetInfo.targetIndex = targetIndex++;
- targetInfo.libOrdinal = libOrdinal;
- targetInfo.symbolName = symbolName;
- targetInfo.addend = addend;
- targetInfo.weakImport = weakImport;
- targetInfo.lazyBind = lazyBind && allowLazyBinds;
- }
- handler(runtimeOffset, segmentIndex, targetInfo, stop);
- };
- bool stopped = this->forEachBind_OpcodesRegular(diag, binder);
- if ( stopped )
- return;
- stopped = this->forEachBind_OpcodesLazy(diag, binder);
- if ( stopped )
- return;
- }
-
- // Opcode based weak-binds effectively override other binds/rebases. Process them last
- // To match dyld2, they are allowed to fail to find a target, in which case the normal rebase/bind will
- // not be overridden.
- {
- __block unsigned weakTargetIndex = 0;
- __block BindTargetInfo weakTargetInfo;
- BindDetailedHandler weakBinder = ^(const char* opcodeName,
- bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
- uint32_t pointerSize, uint32_t segmentIndex, uint64_t segmentOffset,
- uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
- uint64_t addend, bool targetOrAddendChanged, bool& stop) {
-
- uint64_t bindVmOffset = this->layout.segments[segmentIndex].vmAddr + segmentOffset;
- uint64_t runtimeOffset = bindVmOffset - this->layout.textUnslidVMAddr();
- if ( (symbolName != weakTargetInfo.symbolName) || (strcmp(symbolName, weakTargetInfo.symbolName) != 0) || (weakTargetInfo.addend != addend) ) {
- weakTargetInfo.targetIndex = weakTargetIndex++;
- weakTargetInfo.libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
- weakTargetInfo.symbolName = symbolName;
- weakTargetInfo.addend = addend;
- weakTargetInfo.weakImport = false;
- weakTargetInfo.lazyBind = false;
- }
- overrideHandler(runtimeOffset, segmentIndex, weakTargetInfo, stop);
- };
- auto strongHandler = ^(const char* strongName) { };
- this->forEachBind_OpcodesWeak(diag, weakBinder, strongHandler);
- }
-}
-
-void Fixups::forEachBindTarget_Opcodes(Diagnostics& diag, bool allowLazyBinds,
- void (^handler)(const BindTargetInfo& info, bool& stop),
- void (^overrideHandler)(const BindTargetInfo& info, bool& stop)) const
-{
- __block unsigned lastTargetIndex = -1;
- __block unsigned lastWeakBindTargetIndex = -1;
- this->forEachBindUnified_Opcodes(diag, allowLazyBinds,
- ^(uint64_t runtimeOffset, uint32_t segmentIndex, const BindTargetInfo& targetInfo, bool& stop) {
- // Regular/lazy binds
- if ( lastTargetIndex != targetInfo.targetIndex) {
- handler(targetInfo, stop);
- lastTargetIndex = targetInfo.targetIndex;
- }
- }, ^(uint64_t runtimeOffset, uint32_t segmentIndex, const BindTargetInfo& targetInfo, bool& stop) {
- // Weak binds
- if ( lastWeakBindTargetIndex != targetInfo.targetIndex) {
- overrideHandler(targetInfo, stop);
- lastWeakBindTargetIndex = targetInfo.targetIndex;
- }
- });
-}
-
-bool Fixups::forEachBind_OpcodesLazy(Diagnostics& diag, BindDetailedHandler handler) const
-{
- if ( !this->layout.linkedit.lazyBindOpcodes.hasValue() )
- return false;
-
- uint32_t lazyDoneCount = 0;
- uint32_t lazyBindCount = 0;
- const uint32_t ptrSize = this->layout.mf->pointerSize();
- bool stop = false;
- const uint32_t dylibCount = this->layout.mf->dependentDylibCount();
- const uint8_t* p = this->layout.linkedit.lazyBindOpcodes.buffer;
- const uint8_t* end = p + this->layout.linkedit.lazyBindOpcodes.bufferSize;
- uint8_t type = BIND_TYPE_POINTER;
- uint64_t segmentOffset = 0;
- uint8_t segmentIndex = 0;
- const char* symbolName = NULL;
- int libraryOrdinal = 0;
- bool segIndexSet = false;
- bool libraryOrdinalSet = false;
- int64_t addend = 0;
- bool weakImport = false;
- while ( !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- // this opcode marks the end of each lazy pointer binding
- ++lazyDoneCount;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- libraryOrdinal = immediate;
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- libraryOrdinal = (int)dyld3::MachOFile::read_uleb128(diag, p, end);
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- // the special ordinals are negative numbers
- if ( immediate == 0 )
- libraryOrdinal = 0;
- else {
- int8_t signExtended = BIND_OPCODE_MASK | immediate;
- libraryOrdinal = signExtended;
- }
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = dyld3::MachOFile::read_sleb128(diag, p, end);
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = dyld3::MachOFile::read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_DO_BIND:
- handler("BIND_OPCODE_DO_BIND", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, true, addend, true, stop);
- segmentOffset += ptrSize;
- ++lazyBindCount;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- case BIND_OPCODE_ADD_ADDR_ULEB:
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- default:
- diag.error("bad lazy bind opcode 0x%02X", opcode);
- break;
- }
- }
- if ( lazyDoneCount > lazyBindCount+7 ) {
- // diag.error("lazy bind opcodes missing binds");
- }
- return stop;
-}
-
-
-
-bool Fixups::forEachBind_OpcodesWeak(Diagnostics& diag, BindDetailedHandler handler, void (^strongHandler)(const char* symbolName)) const
-{
- if ( !this->layout.linkedit.weakBindOpcodes.hasValue() )
- return false;
-
- const uint32_t ptrSize = this->layout.mf->pointerSize();
- bool stop = false;
- const uint32_t dylibCount = this->layout.mf->dependentDylibCount();
- const uint8_t* p = this->layout.linkedit.weakBindOpcodes.buffer;
- const uint8_t* end = p + this->layout.linkedit.weakBindOpcodes.bufferSize;
- uint8_t type = BIND_TYPE_POINTER;
- uint64_t segmentOffset = 0;
- uint8_t segmentIndex = 0;
- const char* symbolName = NULL;
- int libraryOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
- bool segIndexSet = false;
- bool libraryOrdinalSet = true;
- int64_t addend = 0;
- bool weakImport = false;
- bool targetOrAddendChanged = true;
- bool done = false;
- uint64_t count;
- uint64_t skip;
- while ( !stop && diag.noError() && (p < end) && !done ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- done = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- diag.error("unexpected dylib ordinal in weak_bind");
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- if ( immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION ) {
- strongHandler(symbolName);
- }
- targetOrAddendChanged = true;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = dyld3::MachOFile::read_sleb128(diag, p, end);
- targetOrAddendChanged = true;
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = dyld3::MachOFile::read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_ADD_ADDR_ULEB:
- segmentOffset += dyld3::MachOFile::read_uleb128(diag, p, end);
- break;
- case BIND_OPCODE_DO_BIND:
- handler("BIND_OPCODE_DO_BIND", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += ptrSize;
- targetOrAddendChanged = false;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += dyld3::MachOFile::read_uleb128(diag, p, end) + ptrSize;
- targetOrAddendChanged = false;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += immediate*ptrSize + ptrSize;
- targetOrAddendChanged = false;
- break;
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- count = dyld3::MachOFile::read_uleb128(diag, p, end);
- skip = dyld3::MachOFile::read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += skip + ptrSize;
- targetOrAddendChanged = false;
- if ( stop )
- break;
- }
- break;
- default:
- diag.error("bad bind opcode 0x%02X", *p);
- }
- }
- return stop;
-}
-
-bool Fixups::forEachBind_OpcodesRegular(Diagnostics& diag, BindDetailedHandler handler) const
-{
- if ( !this->layout.linkedit.regularBindOpcodes.hasValue() )
- return false;
-
- const uint32_t ptrSize = this->layout.mf->pointerSize();
- bool stop = false;
- const uint32_t dylibCount = this->layout.mf->dependentDylibCount();
- const uint8_t* p = this->layout.linkedit.regularBindOpcodes.buffer;
- const uint8_t* end = p + this->layout.linkedit.regularBindOpcodes.bufferSize;
- uint8_t type = 0;
- uint64_t segmentOffset = 0;
- uint8_t segmentIndex = 0;
- const char* symbolName = NULL;
- int libraryOrdinal = 0;
- bool segIndexSet = false;
- bool libraryOrdinalSet = false;
- bool targetOrAddendChanged = false;
- bool done = false;
- int64_t addend = 0;
- uint64_t count;
- uint64_t skip;
- bool weakImport = false;
- while ( !stop && diag.noError() && (p < end) && !done ) {
- uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
- uint8_t opcode = *p & BIND_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case BIND_OPCODE_DONE:
- done = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
- libraryOrdinal = immediate;
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
- libraryOrdinal = (int)dyld3::MachOFile::read_uleb128(diag, p, end);
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
- // the special ordinals are negative numbers
- if ( immediate == 0 )
- libraryOrdinal = 0;
- else {
- int8_t signExtended = BIND_OPCODE_MASK | immediate;
- libraryOrdinal = signExtended;
- }
- libraryOrdinalSet = true;
- break;
- case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
- weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
- symbolName = (char*)p;
- while (*p != '\0')
- ++p;
- ++p;
- targetOrAddendChanged = true;
- break;
- case BIND_OPCODE_SET_TYPE_IMM:
- type = immediate;
- break;
- case BIND_OPCODE_SET_ADDEND_SLEB:
- addend = dyld3::MachOFile::read_sleb128(diag, p, end);
- targetOrAddendChanged = true;
- break;
- case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segmentIndex = immediate;
- segmentOffset = dyld3::MachOFile::read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case BIND_OPCODE_ADD_ADDR_ULEB:
- segmentOffset += dyld3::MachOFile::read_uleb128(diag, p, end);
- break;
- case BIND_OPCODE_DO_BIND:
- handler("BIND_OPCODE_DO_BIND", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += ptrSize;
- targetOrAddendChanged = false;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += dyld3::MachOFile::read_uleb128(diag, p, end) + ptrSize;
- targetOrAddendChanged = false;
- break;
- case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += immediate*ptrSize + ptrSize;
- targetOrAddendChanged = false;
- break;
- case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
- count = dyld3::MachOFile::read_uleb128(diag, p, end);
- skip = dyld3::MachOFile::read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
- ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- segmentOffset += skip + ptrSize;
- targetOrAddendChanged = false;
- if ( stop )
- break;
- }
- break;
- default:
- diag.error("bad bind opcode 0x%02X", *p);
- }
- }
- return stop;
-}
-
-void Fixups::forEachBindLocation_Opcodes(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned targetIndex, bool& stop),
- void (^overrideHandler)(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned overrideBindTargetIndex, bool& stop)) const
-{
- this->forEachBindUnified_Opcodes(diag, false,
- ^(uint64_t runtimeOffset, uint32_t segmentIndex, const BindTargetInfo& targetInfo, bool& stop) {
- handler(runtimeOffset, segmentIndex, targetInfo.targetIndex, stop);
- }, ^(uint64_t runtimeOffset, uint32_t segmentIndex, const BindTargetInfo& weakTargetInfo, bool& stop) {
- overrideHandler(runtimeOffset, segmentIndex, weakTargetInfo.targetIndex, stop);
- });
-}
-
-void Fixups::forEachBindLocation_Relocations(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, unsigned targetIndex,
- bool& stop)) const
-{
- // As we don't need the private externs workaround, we also don't need a slide here
- bool supportPrivateExternsWorkaround = false;
- intptr_t unusedSlide = 0;
-
- __block int targetIndex = -1;
- this->forEachBind_Relocations(diag, supportPrivateExternsWorkaround, unusedSlide,
- ^(const char* opcodeName,
- bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
- uint32_t pointerSize, uint32_t segmentIndex, uint64_t segmentOffset,
- uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
- uint64_t addend, bool targetOrAddendChanged, bool& stop) {
- if ( targetOrAddendChanged )
- ++targetIndex;
- uint64_t bindVMAddr = this->layout.segments[segmentIndex].vmAddr + segmentOffset;
- uint64_t runtimeOffset = bindVMAddr - this->layout.textUnslidVMAddr();
- handler(runtimeOffset, targetIndex, stop);
- });
-}
-
-// old binary, walk external relocations and indirect symbol table
-void Fixups::forEachBindTarget_Relocations(Diagnostics& diag, intptr_t slide,
- void (^handler)(const BindTargetInfo& info, bool& stop)) const
-{
- __block unsigned targetIndex = 0;
- this->forEachBind_Relocations(diag, true, slide,
- ^(const char* opcodeName,
- bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
- uint32_t pointerSize, uint32_t segmentIndex, uint64_t segmentOffset,
- uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
- uint64_t addend, bool targetOrAddendChanged, bool& stop) {
- if ( targetOrAddendChanged ) {
- BindTargetInfo info;
- info.targetIndex = targetIndex;
- info.libOrdinal = libOrdinal;
- info.symbolName = symbolName;
- info.addend = addend;
- info.weakImport = weakImport;
- info.lazyBind = lazyBind;
- handler(info, stop);
- ++targetIndex;
- }
- });
-}
-
-bool Fixups::forEachRebaseLocation_Opcodes(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, uint32_t segmentIndex, bool& stop)) const
-{
- return this->forEachRebase_Opcodes(diag,
- ^(const char* opcodeName, bool segIndexSet, uint32_t pointerSize,
- uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind,
- bool& stop) {
- uint64_t rebaseVMAddr = this->layout.segments[segmentIndex].vmAddr + segmentOffset;
- uint64_t runtimeOffset = rebaseVMAddr - this->layout.textUnslidVMAddr();
- handler(runtimeOffset, segmentIndex, stop);
- });
-}
-
-void Fixups::forEachRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, uint64_t rebasedValue, bool& stop)) const
-{
- if ( !this->layout.linkedit.rebaseOpcodes.hasValue() )
- return;
-
- const bool is64 = this->layout.mf->is64();
- this->forEachRebase_Opcodes(diag, ^(const char* opcodeName, bool segIndexSet, uint32_t pointerSize,
- uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind,
- bool& stop) {
- uint64_t rebaseVMAddr = this->layout.segments[segmentIndex].vmAddr + segmentOffset;
- uint64_t runtimeOffset = rebaseVMAddr - this->layout.textUnslidVMAddr();
- const uint8_t* fixupLoc = this->layout.segments[segmentIndex].buffer + segmentOffset;
- uint64_t targetVMAddr = 0;
- if ( is64 ) {
- targetVMAddr = *(uint64_t*)fixupLoc;
- } else {
- targetVMAddr = *(uint32_t*)fixupLoc;
- }
- callback(runtimeOffset, targetVMAddr, stop);
- });
-}
-
-bool Fixups::forEachRebase_Opcodes(Diagnostics& diag, RebaseDetailHandler handler) const
-{
- const bool is64 = this->layout.mf->is64();
- const Rebase pointerRebaseKind = is64 ? Rebase::pointer64 : Rebase::pointer32;
- assert(this->layout.linkedit.rebaseOpcodes.hasValue());
-
- const uint8_t* const start = this->layout.linkedit.rebaseOpcodes.buffer;
- const uint8_t* const end = start + this->layout.linkedit.rebaseOpcodes.bufferSize;
- const uint8_t* p = start;
- const uint32_t ptrSize = this->layout.mf->pointerSize();
- Rebase kind = Rebase::unknown;
- int segIndex = 0;
- uint64_t segOffset = 0;
- uint64_t count;
- uint64_t skip;
- bool segIndexSet = false;
- bool stop = false;
- while ( !stop && diag.noError() && (p < end) ) {
- uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
- uint8_t opcode = *p & REBASE_OPCODE_MASK;
- ++p;
- switch (opcode) {
- case REBASE_OPCODE_DONE:
- // Allow some padding, in case rebases were somehow aligned to 16-bytes in size
- if ( (end - p) > 15 )
- diag.error("rebase opcodes terminated early at offset %d of %d", (int)(p-start), (int)(end-start));
- stop = true;
- break;
- case REBASE_OPCODE_SET_TYPE_IMM:
- switch ( immediate ) {
- case REBASE_TYPE_POINTER:
- kind = pointerRebaseKind;
- break;
- case REBASE_TYPE_TEXT_ABSOLUTE32:
- kind = Rebase::textAbsolute32;
- break;
- case REBASE_TYPE_TEXT_PCREL32:
- kind = Rebase::textPCrel32;
- break;
- default:
- kind = Rebase::unknown;
- break;
- }
- break;
- case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
- segIndex = immediate;
- segOffset = dyld3::MachOFile::read_uleb128(diag, p, end);
- segIndexSet = true;
- break;
- case REBASE_OPCODE_ADD_ADDR_ULEB:
- segOffset += dyld3::MachOFile::read_uleb128(diag, p, end);
- break;
- case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
- segOffset += immediate*ptrSize;
- break;
- case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
- for (int i=0; i < immediate; ++i) {
- handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
- segOffset += ptrSize;
- if ( stop )
- break;
- }
- break;
- case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
- count = dyld3::MachOFile::read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
- segOffset += ptrSize;
- if ( stop )
- break;
- }
- break;
- case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
- segOffset += dyld3::MachOFile::read_uleb128(diag, p, end) + ptrSize;
- break;
- case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
- count = dyld3::MachOFile::read_uleb128(diag, p, end);
- if ( diag.hasError() )
- break;
- skip = dyld3::MachOFile::read_uleb128(diag, p, end);
- for (uint32_t i=0; i < count; ++i) {
- handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
- segOffset += skip + ptrSize;
- if ( stop )
- break;
- }
- break;
- default:
- diag.error("unknown rebase opcode 0x%02X", opcode);
- }
- }
- return stop;
-}
-
-bool Fixups::forEachRebaseLocation_Relocations(Diagnostics& diag,
- void (^handler)(uint64_t runtimeOffset, uint32_t segmentIndex,
- bool& stop)) const
-{
- return this->forEachRebase_Relocations(diag, ^(const char* opcodeName, bool segIndexSet,
- uint32_t pointerSize, uint8_t segmentIndex,
- uint64_t segmentOffset, Rebase kind, bool& stop) {
- uint64_t rebaseVmOffset = this->layout.segments[segmentIndex].vmAddr + segmentOffset;
- uint64_t runtimeOffset = rebaseVmOffset - this->layout.textUnslidVMAddr();
- handler(runtimeOffset, segmentIndex, stop);
- });
-}
-
-#if SUPPORT_CLASSIC_RELOCS
-// relocs are normally sorted, we don't want to use qsort because it may switch to mergesort which uses malloc
-static void sortRelocations(dyld3::Array<relocation_info>& relocs)
-{
- // The kernel linker has malloc, and old-style relocations are extremely common. So use qsort
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- ::qsort(&relocs[0], (size_t)relocs.count(), sizeof(relocation_info),
- [](const void* l, const void* r) -> int {
- if ( ((relocation_info*)l)->r_address < ((relocation_info*)r)->r_address )
- return -1;
- else
- return 1;
- });
-#else
- uint64_t count = relocs.count();
- for (uint64_t i=0; i < count-1; ++i) {
- bool done = true;
- for (uint64_t j=0; j < count-i-1; ++j) {
- if ( relocs[j].r_address > relocs[j+1].r_address ) {
- relocation_info temp = relocs[j];
- relocs[j] = relocs[j+1];
- relocs[j+1] = temp;
- done = false;
- }
- }
- if ( done )
- break;
- }
-#endif
-}
-
-bool Fixups::forEachRebase_Relocations(Diagnostics& diag, RebaseDetailHandler handler) const
-{
- // old binary, walk relocations
- bool is64Bit = this->layout.mf->is64();
- const uint8_t ptrSize = this->layout.mf->pointerSize();
- const uint64_t relocsStartAddress = localRelocBaseAddress();
- const relocation_info* const relocsStart = (const relocation_info*)this->layout.linkedit.localRelocs.buffer;
- const relocation_info* const relocsEnd = &relocsStart[this->layout.linkedit.localRelocs.entryCount];
- const uint8_t relocSize = (is64Bit ? 3 : 2);
- bool stop = false;
- STACK_ALLOC_OVERFLOW_SAFE_ARRAY(relocation_info, relocs, 2048);
- for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
- if ( reloc->r_length != relocSize ) {
- bool shouldEmitError = true;
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- if ( this->layout.mf->usesClassicRelocationsInKernelCollection() && (reloc->r_length == 2) && (relocSize == 3) )
- shouldEmitError = false;
-#endif
- if ( shouldEmitError ) {
- diag.error("local relocation has wrong r_length");
- break;
- }
- }
- if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
- diag.error("local relocation has wrong r_type");
- break;
- }
- relocs.push_back(*reloc);
- }
- if ( !relocs.empty() ) {
- sortRelocations(relocs);
- for (relocation_info reloc : relocs) {
- uint32_t addrOff = reloc.r_address;
- uint32_t segIndex = 0;
- uint64_t segOffset = 0;
- uint64_t addr = 0;
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
- // negative
- if ( this->layout.mf->isStaticExecutable() || this->layout.mf->isFileSet() ) {
- addr = relocsStartAddress + (int32_t)addrOff;
- } else {
- addr = relocsStartAddress + addrOff;
- }
-#else
- addr = relocsStartAddress + addrOff;
-#endif
- if ( segIndexAndOffsetForAddress(addr, segIndex, segOffset) ) {
- Rebase kind = (reloc.r_length == 2) ? Rebase::pointer32 : Rebase::pointer64;
- if ( this->layout.mf->cputype == CPU_TYPE_I386 ) {
- if ( this->layout.segments[segIndex].executable() )
- kind = Rebase::textAbsolute32;
- }
- handler("local relocation", true, ptrSize, segIndex, segOffset, kind, stop);
- }
- else {
- diag.error("local relocation has out of range r_address");
- break;
- }
- }
- }
- // then process indirect symbols
- const Rebase pointerRebaseKind = is64Bit ? Rebase::pointer64 : Rebase::pointer32;
- intptr_t unusedSlide = 0;
- forEachIndirectPointer(diag, false, unusedSlide,
- ^(uint64_t address, bool bind, int bindLibOrdinal,
- const char* bindSymbolName, bool bindWeakImport, bool bindLazy,
- bool selfModifyingStub, bool& indStop) {
- if ( bind )
- return;
- uint32_t segIndex = 0;
- uint64_t segOffset = 0;
- if ( segIndexAndOffsetForAddress(address, segIndex, segOffset) ) {
- handler("local relocation", true, ptrSize, segIndex, segOffset, pointerRebaseKind, indStop);
- }
- else {
- diag.error("local relocation has out of range r_address");
- indStop = true;
- }
- });
-
- return stop;
-}
-
-
-bool Fixups::forEachBind_Relocations(Diagnostics& diag, bool supportPrivateExternsWorkaround,
- intptr_t slide, BindDetailedHandler handler) const
-{
- // Firmare binaries won't have a dynSymTab
- if ( !this->layout.linkedit.externRelocs.hasValue() )
- return false;
-
- const uint64_t relocsStartAddress = externalRelocBaseAddress();
- const relocation_info* const relocsStart = (const relocation_info*)this->layout.linkedit.externRelocs.buffer;
- const relocation_info* const relocsEnd = &relocsStart[this->layout.linkedit.externRelocs.entryCount];
- bool is64Bit = this->layout.mf->is64() ;
- const uint32_t ptrSize = this->layout.mf->pointerSize();
- const uint32_t dylibCount = this->layout.mf->dependentDylibCount();
- const uint8_t relocSize = (is64Bit ? 3 : 2);
- const void* symbolTable = this->layout.linkedit.symbolTable.buffer;
- const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
- const struct nlist* symbols32 = (struct nlist*)symbolTable;
- const char* stringPool = (char*)this->layout.linkedit.symbolStrings.buffer;
- uint32_t symCount = this->layout.linkedit.symbolTable.entryCount;
- uint32_t poolSize = this->layout.linkedit.symbolStrings.bufferSize;
- uint32_t lastSymIndx = -1;
- uint64_t lastAddend = 0;
- bool stop = false;
- for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
- bool isBranch = false;
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- if ( this->layout.mf->isKextBundle() ) {
- // kext's may have other kinds of relocations, eg, branch relocs. Skip them
- if ( this->layout.mf->isArch("x86_64") || this->layout.mf->isArch("x86_64h") ) {
- if ( reloc->r_type == X86_64_RELOC_BRANCH ) {
- if ( reloc->r_length != 2 ) {
- diag.error("external relocation has wrong r_length");
- break;
- }
- if ( reloc->r_pcrel != true ) {
- diag.error("external relocation should be pcrel");
- break;
- }
- isBranch = true;
- }
- }
- }
-#endif
- if ( !isBranch ) {
- if ( reloc->r_length != relocSize ) {
- diag.error("external relocation has wrong r_length");
- break;
- }
- if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
- diag.error("external relocation has wrong r_type");
- break;
- }
- }
- uint32_t segIndex = 0;
- uint64_t segOffset = 0;
- if ( segIndexAndOffsetForAddress(relocsStartAddress+reloc->r_address, segIndex, segOffset) ) {
- uint32_t symbolIndex = reloc->r_symbolnum;
- if ( symbolIndex > symCount ) {
- diag.error("external relocation has out of range r_symbolnum");
- break;
- }
- else {
- uint32_t strOffset = is64Bit ? symbols64[symbolIndex].n_un.n_strx : symbols32[symbolIndex].n_un.n_strx;
- uint16_t n_desc = is64Bit ? symbols64[symbolIndex].n_desc : symbols32[symbolIndex].n_desc;
- uint8_t n_type = is64Bit ? symbols64[symbolIndex].n_type : symbols32[symbolIndex].n_type;
- uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
- if ( strOffset >= poolSize ) {
- diag.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex);
- break;
- }
- else {
- const char* symbolName = stringPool + strOffset;
- bool weakImport = (n_desc & N_WEAK_REF);
- const uint8_t* content = this->layout.segments[segIndex].buffer + segOffset;
- uint64_t addend = (reloc->r_length == 3) ? *((uint64_t*)content) : *((uint32_t*)content);
- // Handle defined weak def symbols which need to get a special ordinal
- if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
- libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
- uint8_t type = isBranch ? BIND_TYPE_TEXT_PCREL32 : BIND_TYPE_POINTER;
- bool targetOrAddendChanged = (lastSymIndx != symbolIndex) || (lastAddend != addend);
- handler("external relocation", true, true, dylibCount, libOrdinal,
- ptrSize, segIndex, segOffset, type, symbolName, weakImport, false, addend, targetOrAddendChanged, stop);
- lastSymIndx = symbolIndex;
- lastAddend = addend;
- }
- }
- }
- else {
- diag.error("local relocation has out of range r_address");
- break;
- }
- }
- // then process indirect symbols
- forEachIndirectPointer(diag, supportPrivateExternsWorkaround, slide,
- ^(uint64_t address, bool bind, int bindLibOrdinal,
- const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
- if ( !bind )
- return;
- uint32_t segIndex = 0;
- uint64_t segOffset = 0;
- if ( segIndexAndOffsetForAddress(address, segIndex, segOffset) ) {
- handler("indirect symbol", true, true, dylibCount, bindLibOrdinal,
- ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, bindSymbolName, bindWeakImport, bindLazy, 0, true, indStop);
- }
- else {
- diag.error("indirect symbol has out of range address");
- indStop = true;
- }
- });
-
- return false;
-}
-#endif // SUPPORT_CLASSIC_RELOCS
-
-void Fixups::forEachIndirectPointer(Diagnostics& diag, bool supportPrivateExternsWorkaround, intptr_t slide,
- void (^handler)(uint64_t pointerAddress, bool bind, int bindLibOrdinal, const char* bindSymbolName,
- bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const
-{
- // find lazy and non-lazy pointer sections
- const bool is64Bit = this->layout.mf->is64();
- const uint32_t* const indirectSymbolTable = (uint32_t*)this->layout.linkedit.indirectSymbolTable.buffer;
- const uint32_t indirectSymbolTableCount = this->layout.linkedit.indirectSymbolTable.entryCount;
- const uint32_t ptrSize = this->layout.mf->pointerSize();
- const void* symbolTable = this->layout.linkedit.symbolTable.buffer;
- const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
- const struct nlist* symbols32 = (struct nlist*)symbolTable;
- const char* stringPool = (char*)this->layout.linkedit.symbolStrings.buffer;
- uint32_t symCount = this->layout.linkedit.symbolTable.entryCount;
- uint32_t poolSize = this->layout.linkedit.symbolStrings.bufferSize;
- __block bool stop = false;
-
- // Old kexts put S_LAZY_SYMBOL_POINTERS on the __got section, even if they didn't have indirect symbols to prcess.
- // In that case, skip the loop as there shouldn't be anything to process
- if ( (indirectSymbolTableCount == 0) && this->layout.mf->isKextBundle() )
- return;
-
- ((const Header*)this->layout.mf)->forEachSection(^(const Header::SectionInfo& sectInfo, bool& sectionStop) {
- uint8_t sectionType = (sectInfo.flags & SECTION_TYPE);
- bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (sectInfo.flags & S_ATTR_SELF_MODIFYING_CODE) && (sectInfo.reserved2 == 5) && (this->layout.mf->cputype == CPU_TYPE_I386);
- if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && !selfModifyingStub )
- return;
- if ( (sectInfo.flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) {
- diag.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries");
- sectionStop = true;
- return;
- }
- uint32_t elementSize = selfModifyingStub ? sectInfo.reserved2 : ptrSize;
- uint32_t elementCount = (uint32_t)(sectInfo.size/elementSize);
- if ( dyld3::greaterThanAddOrOverflow(sectInfo.reserved1, elementCount, indirectSymbolTableCount) ) {
- diag.error("section %.*s overflows indirect symbol table", (int)sectInfo.sectionName.size(), sectInfo.sectionName.data());
- sectionStop = true;
- return;
- }
-
- for (uint32_t i=0; (i < elementCount) && !stop; ++i) {
- uint32_t symNum = indirectSymbolTable[sectInfo.reserved1 + i];
- if ( symNum == INDIRECT_SYMBOL_ABS )
- continue;
- if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
- handler(sectInfo.address+i*elementSize, false, 0, "", false, false, false, stop);
- continue;
- }
- if ( symNum > symCount ) {
- diag.error("indirect symbol[%d] = %d which is invalid symbol index", sectInfo.reserved1 + i, symNum);
- sectionStop = true;
- return;
- }
- uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc;
- uint8_t n_type = is64Bit ? symbols64[symNum].n_type : symbols32[symNum].n_type;
- uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
- uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
- if ( strOffset > poolSize ) {
- diag.error("symbol[%d] string offset out of range", sectInfo.reserved1 + i);
- sectionStop = true;
- return;
- }
- const char* symbolName = stringPool + strOffset;
- bool weakImport = (n_desc & N_WEAK_REF);
- bool lazy = (sectionType == S_LAZY_SYMBOL_POINTERS);
-#if SUPPORT_PRIVATE_EXTERNS_WORKAROUND
- if ( lazy && ((n_type & N_PEXT) != 0) ) {
- // don't know why the static linker did not eliminate the internal reference to a private extern definition
- // As this is private extern, we know the symbol lookup will fail. We also know that this is a lazy-bind, and so
- // there is a corresponding rebase. The rebase will be run later, and will slide whatever value is in here.
- // So lets change the value in this slot, and let the existing rebase slide it for us
- // Note we only want to change the value in memory once, before rebases are applied. We don't want to accidentally
- // change it again later.
- if ( supportPrivateExternsWorkaround ) {
- uintptr_t* ptr = (uintptr_t*)((uint8_t*)(sectInfo.address+i*elementSize) + slide);
- uint64_t n_value = is64Bit ? symbols64[symNum].n_value : symbols32[symNum].n_value;
- *ptr = (uintptr_t)n_value;
- }
- continue;
- }
-#endif
- // Handle defined weak def symbols which need to get a special ordinal
- if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
- libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
- handler(sectInfo.address+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
- }
- sectionStop = stop;
- });
-}
-
-uint64_t Fixups::localRelocBaseAddress() const
-{
- if ( this->layout.mf->isArch("x86_64") || this->layout.mf->isArch("x86_64h") ) {
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- if ( this->layout.mf->isKextBundle() ) {
- // for kext bundles the reloc base address starts at __TEXT segment
- return this->layout.segments[0].vmAddr;
- }
-#endif
- // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
- for ( const SegmentLayout& segment : this->layout.segments ) {
- if ( segment.writable() )
- return segment.vmAddr;
- }
- }
- return this->layout.segments[0].vmAddr;
-}
-
-uint64_t Fixups::externalRelocBaseAddress() const
-{
- // Dyld caches are too large for a raw r_address, so everything is an offset from the base address
- if ( this->layout.mf->inDyldCache() ) {
- return ((const Header*)this->layout.mf)->preferredLoadAddress();
- }
-
-#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
- if ( this->layout.mf->isKextBundle() ) {
- // for kext bundles the reloc base address starts at __TEXT segment
- return ((const Header*)this->layout.mf)->preferredLoadAddress();
- }
-#endif
-
- if ( this->layout.mf->isArch("x86_64") || this->layout.mf->isArch("x86_64h") ) {
- // for x86_64 reloc base address starts at first writable segment (usually __DATA)
- for ( const SegmentLayout& segment : this->layout.segments ) {
- if ( segment.writable() )
- return segment.vmAddr;
- }
- }
- // For everyone else we start at 0
- return 0;
-}
-
-bool Fixups::segIndexAndOffsetForAddress(uint64_t addr, uint32_t& segIndex, uint64_t& segOffset) const
-{
- for (uint32_t i=0; i < this->layout.segments.size(); ++i) {
- const SegmentLayout& segment = this->layout.segments[i];
- if ( (segment.vmAddr <= addr) && (addr < (segment.vmAddr + segment.vmSize)) ) {
- segIndex = i;
- segOffset = addr - segment.vmAddr;
- return true;
- }
- }
- return false;
-}
-
-int Fixups::libOrdinalFromDesc(uint16_t n_desc) const
-{
- // -flat_namespace is always flat lookup
- if ( (this->layout.mf->flags & MH_TWOLEVEL) == 0 )
- return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
-
- // extract byte from undefined symbol entry
- int libIndex = GET_LIBRARY_ORDINAL(n_desc);
- switch ( libIndex ) {
- case SELF_LIBRARY_ORDINAL:
- return BIND_SPECIAL_DYLIB_SELF;
-
- case DYNAMIC_LOOKUP_ORDINAL:
- return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
-
- case EXECUTABLE_ORDINAL:
- return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
- }
-
- return libIndex;
-}
-
-// MARK: --- SplitSeg methods ---
-
-SplitSeg::SplitSeg(const Layout& layout)
- : layout(layout)
-{
-}
-
-bool SplitSeg::hasMarker() const
-{
- if ( !this->layout.linkedit.splitSegInfo.hasValue() )
- return false;
-
- return this->layout.linkedit.splitSegInfo.bufferSize == 0;
-}
-
-bool SplitSeg::isV1() const
-{
- if ( !this->layout.linkedit.splitSegInfo.hasValue() )
- return false;
-
- const void* splitSegStart = this->layout.linkedit.splitSegInfo.buffer;
- return (*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT;
-}
-
-bool SplitSeg::isV2() const
-{
- if ( !this->layout.linkedit.splitSegInfo.hasValue() )
- return false;
-
- const void* splitSegStart = this->layout.linkedit.splitSegInfo.buffer;
- return (*(const uint8_t*)splitSegStart) == DYLD_CACHE_ADJ_V2_FORMAT;
-}
-
-bool SplitSeg::hasValue() const
-{
- return this->layout.linkedit.splitSegInfo.hasValue();
-}
-
-void SplitSeg::forEachReferenceV2(Diagnostics& diag, ReferenceCallbackV2 callback) const
-{
- const uint8_t* infoStart = layout.linkedit.splitSegInfo.buffer;
- const uint8_t* infoEnd = infoStart + layout.linkedit.splitSegInfo.bufferSize;
-
- if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
- return;
- }
-
- // Whole :== <count> FromToSection+
- // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
- // ToOffset :== <to-sect-offset-delta> <count> FromOffset+
- // FromOffset :== <kind> <count> <from-sect-offset-delta>
- const uint8_t* p = infoStart;
- uint64_t sectionCount = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- for (uint64_t i=0; i < sectionCount; ++i) {
- uint64_t fromSectionIndex = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- uint64_t toSectionIndex = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- uint64_t toOffsetCount = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- uint64_t toSectionOffset = 0;
- for (uint64_t j=0; j < toOffsetCount; ++j) {
- uint64_t toSectionDelta = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- uint64_t fromOffsetCount = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- toSectionOffset += toSectionDelta;
- for (uint64_t k=0; k < fromOffsetCount; ++k) {
- uint64_t kind = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- if ( kind > 13 ) {
- diag.error("bad kind (%llu) value in %s\n", kind, ((const Header*)this->layout.mf)->installName());
- }
- uint64_t fromSectDeltaCount = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- uint64_t fromSectionOffset = 0;
- for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
- uint64_t delta = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
- fromSectionOffset += delta;
- bool stop = false;
- callback(fromSectionIndex, fromSectionOffset, toSectionIndex, toSectionOffset, stop);
- if ( stop )
- return;
- }
- }
- }
- }
-}
-
-
-void SplitSeg::forEachSplitSegSection(void (^callback)(std::string_view segmentName,
- std::string_view sectionName,
- uint64_t sectionVMAddr)) const
-{
- callback("mach header", "", 0);
- ((const Header*)this->layout.mf)->forEachSection(^(const Header::SectionInfo §Info, bool &stop) {
- callback(sectInfo.segmentName, sectInfo.sectionName, sectInfo.address);
- });
-}
-
-// MARK: --- ExportTrie methods ---
-
-ExportTrie::ExportTrie(const Layout& layout)
- : layout(layout)
-{
-}
-
-static void recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
- dyld3::OverflowSafeArray<char>& cummulativeString, int curStrOffset, bool& stop,
- ExportTrie::ExportsCallback callback)
-{
- if ( p >= end ) {
- diag.error("malformed trie, node past end");
- return;
- }
- const uint64_t terminalSize = dyld3::MachOFile::read_uleb128(diag, p, end);
- const uint8_t* children = p + terminalSize;
- if ( terminalSize != 0 ) {
- uint64_t imageOffset = 0;
- uint64_t flags = dyld3::MachOFile::read_uleb128(diag, p, end);
- uint64_t other = 0;
- const char* importName = nullptr;
- if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
- other = dyld3::MachOFile::read_uleb128(diag, p, end); // dylib ordinal
- importName = (char*)p;
- }
- else {
- imageOffset = dyld3::MachOFile::read_uleb128(diag, p, end);
- if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
- other = dyld3::MachOFile::read_uleb128(diag, p, end);
- else
- other = 0;
- }
- if ( diag.hasError() )
- return;
- callback(cummulativeString.begin(), imageOffset, flags, other, importName, stop);
- if ( stop )
- return;
- }
- if ( children > end ) {
- diag.error("malformed trie, terminalSize extends beyond trie data");
- return;
- }
- const uint8_t childrenCount = *children++;
- const uint8_t* s = children;
- for (uint8_t i=0; i < childrenCount; ++i) {
- int edgeStrLen = 0;
- while (*s != '\0') {
- cummulativeString.resize(curStrOffset+edgeStrLen + 1);
- cummulativeString[curStrOffset+edgeStrLen] = *s++;
- ++edgeStrLen;
- if ( s > end ) {
- diag.error("malformed trie node, child node extends past end of trie\n");
- return;
- }
- }
- cummulativeString.resize(curStrOffset+edgeStrLen + 1);
- cummulativeString[curStrOffset+edgeStrLen] = *s++;
- uint64_t childNodeOffset = dyld3::MachOFile::read_uleb128(diag, s, end);
- if (childNodeOffset == 0) {
- diag.error("malformed trie, childNodeOffset==0");
- return;
- }
- recurseTrie(diag, start, start+childNodeOffset, end, cummulativeString, curStrOffset+edgeStrLen, stop, callback);
- if ( diag.hasError() || stop )
- return;
- }
-}
-
-void ExportTrie::forEachExportedSymbol(Diagnostics& diag, ExportsCallback callback) const
-{
- if ( layout.linkedit.exportsTrie.hasValue() ) {
- const uint8_t* trieStart = layout.linkedit.exportsTrie.buffer;
- const uint8_t* trieEnd = trieStart + layout.linkedit.exportsTrie.bufferSize;
-
- // We still emit empty export trie load commands just as a placeholder to show we have
- // no exports. In that case, don't start recursing as we'll immediately think we ran
- // of the end of the buffer
- if ( trieStart == trieEnd )
- return;
-
- bool stop = false;
- STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString, 4096);
- recurseTrie(diag, trieStart, trieStart, trieEnd, cummulativeString, 0, stop, callback);
- }
-}
-
-// MARK: --- ChainedFixupPointerOnDisk methods ---
-
-uint64_t ChainedFixupPointerOnDisk::Arm64e::unpackTarget() const
-{
- assert(this->authBind.bind == 0);
- assert(this->authBind.auth == 0);
- return ((uint64_t)(this->rebase.high8) << 56) | (this->rebase.target);
-}
-
-uint64_t ChainedFixupPointerOnDisk::Arm64e::signExtendedAddend() const
-{
- assert(this->authBind.bind == 1);
- assert(this->authBind.auth == 0);
- uint64_t addend19 = this->bind.addend;
- if ( addend19 & 0x40000 )
- return addend19 | 0xFFFFFFFFFFFC0000ULL;
- else
- return addend19;
-}
-
-const char* ChainedFixupPointerOnDisk::Arm64e::keyName(uint8_t keyBits)
-{
- static const char* const names[] = {
- "IA", "IB", "DA", "DB"
- };
- assert(keyBits < 4);
- return names[keyBits];
-}
-const char* ChainedFixupPointerOnDisk::Arm64e::keyName() const
-{
- assert(this->authBind.auth == 1);
- return keyName(this->authBind.key);
-}
-
-uint64_t ChainedFixupPointerOnDisk::Arm64e::signPointer(uint64_t unsignedAddr, void* loc, bool addrDiv, uint16_t diversity, uint8_t key)
-{
- // don't sign NULL
- if ( unsignedAddr == 0 )
- return 0;
-
-#if __has_feature(ptrauth_calls)
- uint64_t extendedDiscriminator = diversity;
- if ( addrDiv )
- extendedDiscriminator = __builtin_ptrauth_blend_discriminator(loc, extendedDiscriminator);
- switch ( key ) {
- case 0: // IA
- return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 0, extendedDiscriminator);
- case 1: // IB
- return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 1, extendedDiscriminator);
- case 2: // DA
- return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 2, extendedDiscriminator);
- case 3: // DB
- return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 3, extendedDiscriminator);
- }
- assert(0 && "invalid signing key");
-#else
- assert(0 && "arm64e signing only arm64e");
-#endif
-}
-
-
-uint64_t ChainedFixupPointerOnDisk::Arm64e::signPointer(void* loc, uint64_t target) const
-{
- assert(this->authBind.auth == 1);
- return signPointer(target, loc, authBind.addrDiv, authBind.diversity, authBind.key);
-}
-
-uint64_t ChainedFixupPointerOnDisk::Generic64::unpackedTarget() const
-{
- return (((uint64_t)this->rebase.high8) << 56) | (uint64_t)(this->rebase.target);
-}
-
-uint64_t ChainedFixupPointerOnDisk::Generic64::signExtendedAddend() const
-{
- uint64_t addend27 = this->bind.addend;
- uint64_t top8Bits = addend27 & 0x00007F80000ULL;
- uint64_t bottom19Bits = addend27 & 0x0000007FFFFULL;
- uint64_t newValue = (top8Bits << 13) | (((uint64_t)(bottom19Bits << 37) >> 37) & 0x00FFFFFFFFFFFFFF);
- return newValue;
-}
-
-const char* ChainedFixupPointerOnDisk::Kernel64::keyName() const
-{
- static const char* names[] = {
- "IA", "IB", "DA", "DB"
- };
- assert(this->isAuth == 1);
- uint8_t keyBits = this->key;
- assert(keyBits < 4);
- return names[keyBits];
-}
-
-uint64_t ChainedFixupPointerOnDisk::Cache64e::high8() const
-{
- assert(this->regular.auth == 0);
- return ((uint64_t)(this->regular.high8) << 56);
-}
-
-const char* ChainedFixupPointerOnDisk::Cache64e::keyName() const
-{
- assert(this->auth.auth == 1);
- static const char* const names[] = {
- "IA", "DA"
- };
- assert(this->auth.keyIsData < 2);
- return names[this->auth.keyIsData];
-}
-
-uint64_t ChainedFixupPointerOnDisk::Cache64e::signPointer(uint64_t unsignedAddr, void* loc, bool addrDiv, uint16_t diversity, uint8_t keyIsData)
-{
- // don't sign NULL
- if ( unsignedAddr == 0 )
- return 0;
-
-#if __has_feature(ptrauth_calls)
- uint64_t extendedDiscriminator = diversity;
- if ( addrDiv )
- extendedDiscriminator = __builtin_ptrauth_blend_discriminator(loc, extendedDiscriminator);
- switch ( keyIsData ) {
- case 0: // IA
- return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 0, extendedDiscriminator);
- case 1: // DA
- return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 2, extendedDiscriminator);
- }
- assert(0 && "invalid signing key");
-#else
- assert(0 && "arm64e signing only arm64e");
-#endif
-}
-
-
-uint64_t ChainedFixupPointerOnDisk::Cache64e::signPointer(void* loc, uint64_t target) const
-{
- assert(this->auth.auth == 1);
- return signPointer(target, loc, auth.addrDiv, auth.diversity, auth.keyIsData);
-}
-
-bool ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, uint64_t preferedLoadAddress, uint64_t& targetRuntimeOffset) const
-{
- switch (pointerFormat) {
- case DYLD_CHAINED_PTR_ARM64E:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
- case DYLD_CHAINED_PTR_ARM64E_KERNEL:
- case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
- if ( this->arm64e.bind.bind )
- return false;
- if ( this->arm64e.authRebase.auth ) {
- targetRuntimeOffset = this->arm64e.authRebase.target;
- return true;
- }
- else {
- targetRuntimeOffset = this->arm64e.unpackTarget();
- if ( (pointerFormat == DYLD_CHAINED_PTR_ARM64E) || (pointerFormat == DYLD_CHAINED_PTR_ARM64E_FIRMWARE) ) {
- targetRuntimeOffset -= preferedLoadAddress;
- }
- return true;
- }
- break;
- case DYLD_CHAINED_PTR_64:
- case DYLD_CHAINED_PTR_64_OFFSET:
- if ( this->generic64.bind.bind )
- return false;
- targetRuntimeOffset = this->generic64.unpackedTarget();
- if ( pointerFormat == DYLD_CHAINED_PTR_64 )
- targetRuntimeOffset -= preferedLoadAddress;
- return true;
- break;
- case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
- case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
- targetRuntimeOffset = this->kernel64.target;
- return true;
- break;
- case DYLD_CHAINED_PTR_32:
- if ( this->generic32.bind.bind )
- return false;
- targetRuntimeOffset = this->generic32.rebase.target - preferedLoadAddress;
- return true;
- break;
- case DYLD_CHAINED_PTR_32_FIRMWARE:
- targetRuntimeOffset = this->firmware32.target - preferedLoadAddress;
- return true;
- break;
- case DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE:
- if ( this->cache64e.regular.auth ) {
- targetRuntimeOffset = this->cache64e.auth.runtimeOffset;
- return true;
- }
- else {
- targetRuntimeOffset = this->cache64e.regular.runtimeOffset;
- return true;
- }
- break;
- default:
- break;
- }
- assert(0 && "unsupported pointer chain format");
-}
-
-bool ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint32_t& bindOrdinal, int64_t& addend) const
-{
- addend = 0;
- switch (pointerFormat) {
- case DYLD_CHAINED_PTR_ARM64E:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
- case DYLD_CHAINED_PTR_ARM64E_KERNEL:
- case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
- if ( !this->arm64e.authBind.bind )
- return false;
- if ( this->arm64e.authBind.auth ) {
- if ( pointerFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24 )
- bindOrdinal = this->arm64e.authBind24.ordinal;
- else
- bindOrdinal = this->arm64e.authBind.ordinal;
- return true;
- }
- else {
- if ( pointerFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24 )
- bindOrdinal = this->arm64e.bind24.ordinal;
- else
- bindOrdinal = this->arm64e.bind.ordinal;
- addend = this->arm64e.signExtendedAddend();
- return true;
- }
- break;
- case DYLD_CHAINED_PTR_64:
- case DYLD_CHAINED_PTR_64_OFFSET:
- if ( !this->generic64.bind.bind )
- return false;
- bindOrdinal = this->generic64.bind.ordinal;
- addend = this->generic64.bind.addend;
- return true;
- break;
- case DYLD_CHAINED_PTR_32:
- if ( !this->generic32.bind.bind )
- return false;
- bindOrdinal = this->generic32.bind.ordinal;
- addend = this->generic32.bind.addend;
- return true;
- break;
- case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
- case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
- return false;
- default:
- break;
- }
- assert(0 && "unsupported pointer chain format");
-}
-
-unsigned ChainedFixupPointerOnDisk::strideSize(uint16_t pointerFormat)
-{
- switch (pointerFormat) {
- case DYLD_CHAINED_PTR_ARM64E:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND:
- case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
- case DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE:
- return 8;
- case DYLD_CHAINED_PTR_ARM64E_KERNEL:
- case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
- case DYLD_CHAINED_PTR_32_FIRMWARE:
- case DYLD_CHAINED_PTR_64:
- case DYLD_CHAINED_PTR_64_OFFSET:
- case DYLD_CHAINED_PTR_32:
- case DYLD_CHAINED_PTR_32_CACHE:
- case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
- return 4;
- case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
- return 1;
- }
- assert(0 && "unsupported pointer chain format");
-}
-
-// MARK: --- SymbolTable methods ---
-
-SymbolTable::SymbolTable(const Layout& layout)
- : layout(layout)
-{
-}
-
-void SymbolTable::forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
-{
- const bool is64Bit = this->layout.mf->is64();
- if ( this->layout.linkedit.symbolTable.hasValue() ) {
- uint32_t localsStartIndex = 0;
- uint32_t localsCount = this->layout.linkedit.symbolTable.entryCount;
- if ( this->layout.linkedit.localSymbolTable.hasValue() ) {
- localsStartIndex = this->layout.linkedit.localSymbolTable.entryIndex;
- localsCount = this->layout.linkedit.localSymbolTable.entryCount;
- }
- uint32_t maxStringOffset = this->layout.linkedit.symbolStrings.bufferSize;
- const char* stringPool = (char*)this->layout.linkedit.symbolStrings.buffer;
- const struct nlist* symbols = (struct nlist*)this->layout.linkedit.symbolTable.buffer;
- const struct nlist_64* symbols64 = (struct nlist_64*)this->layout.linkedit.symbolTable.buffer;
- bool stop = false;
- for (uint32_t i=0; (i < localsCount) && !stop; ++i) {
- if ( is64Bit ) {
- const struct nlist_64& sym = symbols64[localsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- else {
- const struct nlist& sym = symbols[localsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- }
- }
-}
-
-
-void SymbolTable::forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
-{
- const bool is64Bit = this->layout.mf->is64();
- if ( this->layout.linkedit.symbolTable.hasValue() ) {
- uint32_t globalsStartIndex = 0;
- uint32_t globalsCount = this->layout.linkedit.symbolTable.entryCount;
- if ( this->layout.linkedit.globalSymbolTable.hasValue() ) {
- globalsStartIndex = this->layout.linkedit.globalSymbolTable.entryIndex;
- globalsCount = this->layout.linkedit.globalSymbolTable.entryCount;
- }
- uint32_t maxStringOffset = this->layout.linkedit.symbolStrings.bufferSize;
- const char* stringPool = (char*)this->layout.linkedit.symbolStrings.buffer;
- const struct nlist* symbols = (struct nlist*)this->layout.linkedit.symbolTable.buffer;
- const struct nlist_64* symbols64 = (struct nlist_64*)this->layout.linkedit.symbolTable.buffer;
- bool stop = false;
- for (uint32_t i=0; (i < globalsCount) && !stop; ++i) {
- if ( is64Bit ) {
- const struct nlist_64& sym = symbols64[globalsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- else {
- const struct nlist& sym = symbols[globalsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- }
- }
-}
-
-
-void SymbolTable::forEachImportedSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const
-{
- const bool is64Bit = this->layout.mf->is64();
- if ( this->layout.linkedit.symbolTable.hasValue() ) {
- uint32_t undefsStartIndex = 0;
- uint32_t undefsCount = this->layout.linkedit.symbolTable.entryCount;
- if ( this->layout.linkedit.undefSymbolTable.hasValue() ) {
- undefsStartIndex = this->layout.linkedit.undefSymbolTable.entryIndex;
- undefsCount = this->layout.linkedit.undefSymbolTable.entryCount;
- }
- uint32_t maxStringOffset = this->layout.linkedit.symbolStrings.bufferSize;
- const char* stringPool = (char*)this->layout.linkedit.symbolStrings.buffer;
- const struct nlist* symbols = (struct nlist*)this->layout.linkedit.symbolTable.buffer;
- const struct nlist_64* symbols64 = (struct nlist_64*)this->layout.linkedit.symbolTable.buffer;
- bool stop = false;
- for (uint32_t i=0; (i < undefsCount) && !stop; ++i) {
- if ( is64Bit ) {
- const struct nlist_64& sym = symbols64[undefsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( (sym.n_type & N_TYPE) == N_UNDF )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- else {
- const struct nlist& sym = symbols[undefsStartIndex+i];
- if ( sym.n_un.n_strx > maxStringOffset )
- continue;
- if ( (sym.n_type & N_TYPE) == N_UNDF )
- callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop);
- }
- }
- }
-}
-
-void SymbolTable::forEachIndirectSymbol(Diagnostics& diag,
- void (^callback)(const char* symbolName, uint32_t symNum)) const
-{
- // find lazy and non-lazy pointer sections
- const bool is64Bit = this->layout.mf->is64();
- const uint32_t* const indirectSymbolTable = (uint32_t*)this->layout.linkedit.indirectSymbolTable.buffer;
- const uint32_t indirectSymbolTableCount = this->layout.linkedit.indirectSymbolTable.entryCount;
- const void* symbolTable = this->layout.linkedit.symbolTable.buffer;
- const struct nlist_64* symbols64 = (nlist_64*)symbolTable;
- const struct nlist* symbols32 = (struct nlist*)symbolTable;
- const char* stringPool = (char*)this->layout.linkedit.symbolStrings.buffer;
- uint32_t symCount = this->layout.linkedit.symbolTable.entryCount;
- uint32_t poolSize = this->layout.linkedit.symbolStrings.bufferSize;
-
- if ( indirectSymbolTableCount == 0 )
- return;
-
- for (uint32_t i = 0; i != indirectSymbolTableCount; ++i ) {
- uint32_t symNum = indirectSymbolTable[i];
- if ( symNum == INDIRECT_SYMBOL_ABS ) {
- // FIXME: The client wants to know about all the entries, so what should we pass here?
- callback(nullptr, symNum);
- continue;
- }
- if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
- callback("", symNum);
- continue;
- }
- if ( symNum == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS) ) {
- // FIXME: We are using the "local" callback. Should we use the "abs" one instead
- callback("", symNum);
- continue;
- }
- if ( symNum > symCount ) {
- diag.error("indirect symbol[%d] = %d which is invalid symbol index", i, symNum);
- return;
- }
- uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
- if ( strOffset > poolSize ) {
- diag.error("symbol[%d] string offset out of range", i);
- return;
- }
- const char* symbolName = stringPool + strOffset;
- callback(symbolName, symNum);
- }
-}
-
-} // namespace mach_o
-