Loading...
--- /dev/null
+++ dyld/dyld-1122.1/common/MetadataVisitor.cpp
@@ -0,0 +1,696 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 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 <TargetConditionals.h>
+
+#if !TARGET_OS_EXCLAVEKIT
+
+#include "MetadataVisitor.h"
+
+#if SUPPORT_VM_LAYOUT
+#include "MachOAnalyzer.h"
+#endif
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+#include "ASLRTracker.h"
+#endif
+
+#if POINTERS_ARE_UNSLID
+#include "DyldSharedCache.h"
+#endif
+
+using namespace metadata_visitor;
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+typedef cache_builder::Fixup::Cache32 Cache32;
+typedef cache_builder::Fixup::Cache64 Cache64;
+#endif
+
+//
+// MARK: --- ResolvedValue methods ---
+//
+
+#if SUPPORT_VM_LAYOUT
+
+ResolvedValue::ResolvedValue(const void* targetValue, VMAddress vmAddr)
+ : targetValue(targetValue), vmAddr(vmAddr)
+{
+}
+
+#else
+
+ResolvedValue::ResolvedValue(const Segment& cacheSegment, VMOffset segmentVMOffset)
+ : cacheSegment(cacheSegment), segmentVMOffset(segmentVMOffset)
+{
+}
+ResolvedValue::ResolvedValue(const ResolvedValue& parentValue, const void* childLocation)
+ : cacheSegment(parentValue.cacheSegment)
+{
+ this->segmentVMOffset = VMOffset((uint64_t)((uint8_t*)childLocation - parentValue.cacheSegment.bufferStart));
+}
+
+std::optional<uint16_t> ResolvedValue::chainedPointerFormat() const
+{
+ return this->cacheSegment.onDiskDylibChainedPointerFormat;
+}
+
+uint32_t ResolvedValue::segmentIndex() const
+{
+ return this->cacheSegment.segIndex;
+}
+
+#endif // SUPPORT_VM_LAYOUT
+
+void* ResolvedValue::value() const
+{
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+ return this->cacheSegment.bufferStart + this->segmentVMOffset.rawValue();
+#else
+ return (void*)this->targetValue;
+#endif
+}
+
+VMAddress ResolvedValue::vmAddress() const
+{
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+ return this->cacheSegment.startVMAddr + this->segmentVMOffset;
+#else
+ return this->vmAddr;
+#endif
+}
+
+//
+// MARK: --- Visitor methods ---
+//
+
+#if POINTERS_ARE_UNSLID
+
+Visitor::Visitor(const DyldSharedCache* dyldCache, const dyld3::MachOAnalyzer* dylibMA,
+ std::optional<VMAddress> selectorStringsBaseAddress)
+ : dylibMA(dylibMA), dylibBaseAddress(dylibMA->preferredLoadAddress()),
+ selectorStringsBaseAddress(selectorStringsBaseAddress)
+{
+ pointerSize = dylibMA->pointerSize();
+
+ this->onDiskDylibChainedPointerBaseAddress = dylibBaseAddress;
+
+ if ( dylibMA->inDyldCache() ) {
+ dyldCache->forEachCache(^(const DyldSharedCache *cache, bool& stopCache) {
+ cache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
+ if ( slideInfoHeader->version == 1 ) {
+ this->sharedCacheChainedPointerFormat = SharedCacheFormat::v1;
+ this->onDiskDylibChainedPointerBaseAddress = VMAddress(dyldCache->unslidLoadAddress());
+ } else if ( slideInfoHeader->version == 2 ) {
+ const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader);
+ assert(slideInfo->delta_mask == 0x00FFFF0000000000);
+ this->sharedCacheChainedPointerFormat = SharedCacheFormat::v2_x86_64_tbi;
+ this->onDiskDylibChainedPointerBaseAddress = VMAddress(slideInfo->value_add);
+ } else if ( slideInfoHeader->version == 3 ) {
+ this->sharedCacheChainedPointerFormat = SharedCacheFormat::v3;
+ this->onDiskDylibChainedPointerBaseAddress = VMAddress(dyldCache->unslidLoadAddress());
+ } else if ( slideInfoHeader->version == 4 ) {
+ const dyld_cache_slide_info4* slideInfo = (dyld_cache_slide_info4*)(slideInfoHeader);
+ assert(slideInfo->delta_mask == 0x00000000C0000000);
+ this->sharedCacheChainedPointerFormat = SharedCacheFormat::v4;
+ this->onDiskDylibChainedPointerBaseAddress = VMAddress(slideInfo->value_add);
+ } else {
+ assert(false);
+ }
+ });
+ });
+ } else {
+ if ( dylibMA->hasChainedFixups() )
+ this->chainedPointerFormat = dylibMA->chainedPointerFormat();
+ }
+}
+
+#elif SUPPORT_VM_LAYOUT
+
+Visitor::Visitor(const dyld3::MachOAnalyzer* dylibMA)
+ : dylibMA(dylibMA), dylibBaseAddress(dylibMA->preferredLoadAddress())
+{
+ pointerSize = dylibMA->pointerSize();
+}
+
+#else
+
+// Cache builder dylib
+Visitor::Visitor(CacheVMAddress cacheBaseAddress, const dyld3::MachOFile* dylibMF,
+ std::vector<Segment>&& segments, std::optional<VMAddress> selectorStringsBaseAddress,
+ std::vector<uint64_t>&& bindTargets)
+ : isOnDiskDylib(false), dylibMF(dylibMF), sharedCacheBaseAddress(cacheBaseAddress),
+ segments(std::move(segments)), bindTargets(std::move(bindTargets)),
+ selectorStringsBaseAddress(selectorStringsBaseAddress)
+{
+ pointerSize = dylibMF->pointerSize();
+
+ // Cache dylibs should never have a chain value set, as they always use the in-cache builder
+ // representation of values
+ for ( const Segment& segment : this->segments ) {
+ assert(!segment.onDiskDylibChainedPointerFormat.has_value());
+ }
+}
+
+// On disk dylib/executable
+Visitor::Visitor(VMAddress chainedPointerBaseAddress, const dyld3::MachOFile* dylibMF,
+ std::vector<Segment>&& segments, std::optional<VMAddress> selectorStringsBaseAddress,
+ std::vector<uint64_t>&& bindTargets)
+ : isOnDiskDylib(true), dylibMF(dylibMF), onDiskDylibChainedPointerBaseAddress(chainedPointerBaseAddress),
+ segments(std::move(segments)), bindTargets(std::move(bindTargets)),
+ selectorStringsBaseAddress(selectorStringsBaseAddress)
+{
+ pointerSize = dylibMF->pointerSize();
+
+ // On-disk dylibs should have a chain value set, even if its a 0 for opcode fixup dylibs
+ for ( const Segment& segment : this->segments ) {
+ assert(segment.onDiskDylibChainedPointerFormat.has_value());
+ }
+}
+
+#endif
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS || POINTERS_ARE_UNSLID
+VMAddress Visitor::sharedCacheSelectorStringsBaseAddress() const
+{
+ return this->selectorStringsBaseAddress.value();
+}
+#endif
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+VMAddress Visitor::getOnDiskDylibChainedPointerBaseAddress() const
+{
+ assert(this->isOnDiskDylib);
+ return this->onDiskDylibChainedPointerBaseAddress;
+}
+
+const dyld3::MachOFile* Visitor::mf() const
+{
+ return this->dylibMF;
+}
+
+bool Visitor::isOnDiskBinary() const
+{
+ return this->isOnDiskDylib;
+}
+#endif
+
+ResolvedValue Visitor::getField(const ResolvedValue& parent, const void* fieldPos) const
+{
+#if SUPPORT_VM_LAYOUT
+ // In dyld, we just use raw pointers for everything, and don't need to indirect via segment+offset like
+ // in the cache builder
+ VMOffset offsetInDylib((uint64_t)fieldPos - (uint64_t)this->dylibMA);
+ VMAddress fieldVMAddr(this->dylibBaseAddress + offsetInDylib);
+ return ResolvedValue(fieldPos, fieldVMAddr);
+#else
+ // In the cache builder, everything is an offset in a segment, as we don't know where the segments
+ // will be in memory when running
+ return ResolvedValue(parent, fieldPos);
+#endif
+}
+
+ResolvedValue Visitor::getValueFor(VMAddress vmAddr) const
+{
+#if SUPPORT_VM_LAYOUT
+ // In dyld, we just use raw pointers for everything, and don't need to indirect via segment+offset like
+ // in the cache builder
+ VMOffset offsetInDylib = vmAddr - this->dylibBaseAddress;
+ const void* valueInDylib = (const uint8_t*)this->dylibMA + offsetInDylib.rawValue();
+ return ResolvedValue(valueInDylib, vmAddr);
+#else
+ // Find the segment containing the target address
+ for ( const Segment& cacheSegment : segments ) {
+ if ( (vmAddr >= cacheSegment.startVMAddr) && (vmAddr < cacheSegment.endVMAddr) ) {
+ // Skip segments which don't contribute to the cache. This is a hack to account
+ // for LINKEDIT, which doesn't really get its own buffer. We don't want to match
+ // an address to LINKEDIT, when we actually wanted to find it in the selector strings
+ // "segment" we also track here
+ if ( cacheSegment.bufferStart == nullptr )
+ continue;
+
+ VMOffset segmentVMOffset = vmAddr - cacheSegment.startVMAddr;
+ return ResolvedValue(cacheSegment, segmentVMOffset);
+ }
+ }
+
+ assert(0);
+#endif
+}
+
+// Dereferences the given value. It must not resolve to nullptr
+ResolvedValue Visitor::resolveRebase(const ResolvedValue& value) const
+{
+#if POINTERS_ARE_UNSLID
+ uint64_t runtimeOffset = 0;
+
+ if ( this->sharedCacheChainedPointerFormat != SharedCacheFormat::none ) {
+ // Crack the shared cache slide format
+ switch ( this->sharedCacheChainedPointerFormat ) {
+ case SharedCacheFormat::none:
+ assert(false);
+ case SharedCacheFormat::v1: {
+ runtimeOffset = *(uint32_t*)value.value() - onDiskDylibChainedPointerBaseAddress.rawValue();
+ break;
+ }
+ case SharedCacheFormat::v2_x86_64_tbi: {
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint64_t rawValue = fixup->raw64;
+
+ const uint64_t deltaMask = 0x00FFFF0000000000;
+ const uint64_t valueMask = ~deltaMask;
+ rawValue = (rawValue & valueMask);
+ // Already a runtime offset, so no need to do anything with valueAdd
+ runtimeOffset = rawValue;
+ break;
+ }
+ case SharedCacheFormat::v3: {
+ // Just use the chained pointer format for arm64e
+ auto* chainedValue = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E,
+ onDiskDylibChainedPointerBaseAddress.rawValue(),
+ runtimeOffset);
+ break;
+ }
+ case SharedCacheFormat::v4: {
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint64_t rawValue = fixup->raw32;
+
+ const uint64_t deltaMask = 0x00000000C0000000;
+ const uint64_t valueMask = ~deltaMask;
+ rawValue = (rawValue & valueMask);
+ // Already a runtime offset, so no need to do anything with valueAdd
+ runtimeOffset = rawValue;
+ break;
+ }
+ }
+ } else {
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ if ( this->chainedPointerFormat == 0 ) {
+ // HACK: 32-bit cache dylibs don't have enough bits to have real chains, so we pretend they
+ // have no chains, just raw VMAddr's
+ assert(dylibMA->hasOpcodeFixups());
+
+ // HACK: This is a binary without chained fixups. Is it safe to assume this is a rebase?
+ uint64_t rebaseVMAddr = (pointerSize == 8) ? fixup->raw64 : fixup->raw32;
+ runtimeOffset = rebaseVMAddr - this->onDiskDylibChainedPointerBaseAddress.rawValue();
+ } else {
+ bool isRebase = fixup->isRebase(this->chainedPointerFormat,
+ onDiskDylibChainedPointerBaseAddress.rawValue(),
+ runtimeOffset);
+ assert(isRebase);
+ }
+ }
+
+ VMAddress targetVMAddress = onDiskDylibChainedPointerBaseAddress + VMOffset(runtimeOffset);
+ return this->getValueFor(targetVMAddress);
+#elif SUPPORT_VM_LAYOUT
+ // In dyld, we just use raw pointers for everything, and don't need to indirect via segment+offset like
+ // in the cache builder
+ const void* targetValue = (const void*)*(uintptr_t*)value.value();
+
+ // FIXME: We didn't expect a null here. Should we find a way to error out, or just let the parser
+ // crash with a nullptr dereference.
+ if ( targetValue == nullptr )
+ return ResolvedValue(nullptr, VMAddress());
+
+ // The value may have been signed. Strip the signature if that is the case
+#if __has_feature(ptrauth_calls)
+ targetValue = __builtin_ptrauth_strip(targetValue, ptrauth_key_asia);
+#endif
+
+ VMOffset offsetInDylib((uint64_t)targetValue - (uint64_t)this->dylibMA);
+ return ResolvedValue(targetValue, this->dylibBaseAddress + offsetInDylib);
+#else
+ // In on-disk dylibs, we crack the chained fixups or other fixups
+ if ( this->isOnDiskBinary() ) {
+ uint64_t runtimeOffset = 0;
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint16_t chainedPointerFormat = value.chainedPointerFormat().value();
+ if ( chainedPointerFormat == 0 ) {
+ // HACK: 32-bit cache dylibs don't have enough bits to have real chains, so we pretend they
+ // have no chains, just raw VMAddr's
+ assert(dylibMF->hasOpcodeFixups() || (dylibMF->inDyldCache() && !dylibMF->is64()));
+
+ // HACK: This is a binary without chained fixups. Is it safe to assume this is a rebase?
+ uint64_t rebaseVMAddr = (pointerSize == 8) ? fixup->raw64 : fixup->raw32;
+ runtimeOffset = rebaseVMAddr - this->onDiskDylibChainedPointerBaseAddress.rawValue();
+ } else {
+ bool isRebase = fixup->isRebase(chainedPointerFormat, onDiskDylibChainedPointerBaseAddress.rawValue(), runtimeOffset);
+ assert(isRebase);
+ }
+
+ VMAddress targetVMAddress = onDiskDylibChainedPointerBaseAddress + VMOffset(runtimeOffset);
+ return this->getValueFor(targetVMAddress);
+ } else {
+ // Cache builder dylib. These use values in a packed format
+ if ( this->pointerSize == 4 ) {
+ const void* fixupLocation = value.value();
+ assert(!Cache32::isNull(fixupLocation));
+
+ CacheVMAddress targetCacheVMAddress = Cache32::getCacheVMAddressFromLocation(sharedCacheBaseAddress, fixupLocation);
+ VMAddress targetVMAddress(targetCacheVMAddress.rawValue());
+ return this->getValueFor(targetVMAddress);
+ } else {
+ const void* fixupLocation = value.value();
+ assert(!Cache64::isNull(fixupLocation));
+
+ CacheVMAddress targetCacheVMAddress = Cache64::getCacheVMAddressFromLocation(sharedCacheBaseAddress, fixupLocation);
+ VMAddress targetVMAddress(targetCacheVMAddress.rawValue());
+ return this->getValueFor(targetVMAddress);
+ }
+ }
+#endif
+}
+
+// Dereferences the given value. It may be either a bind or a rebase. It must not resolve to nullptr
+ResolvedValue Visitor::resolveBindOrRebase(const ResolvedValue& value, bool& wasBind) const
+{
+#if SUPPORT_VM_LAYOUT
+ // dyld will never see a bind or a rebase, just live values. Use resolveRebase for this as it already
+ // handles this case
+ wasBind = false;
+ return this->resolveRebase(value);
+#else
+ // In on-disk dylibs, we crack the chained fixups or other fixups
+ if ( this->isOnDiskBinary() ) {
+ // Check if this is a bind
+ {
+ const auto* fixupLoc = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint16_t chainedPointerFormat = value.chainedPointerFormat().value();
+
+ // Follow the class reference to get to the actual class
+ // With objc patching, this might be a bind to self
+ uint32_t bindOrdinal;
+ int64_t bindAddend;
+ if ( (chainedPointerFormat != 0) && fixupLoc->isBind(chainedPointerFormat, bindOrdinal, bindAddend) ) {
+ wasBind = true;
+ VMAddress targetVMAddress(this->bindTargets[bindOrdinal] + bindAddend);
+ return this->getValueFor(targetVMAddress);
+ }
+ }
+
+ // Fall back to resolveRebase() which can handle rebases
+ wasBind = false;
+ return this->resolveRebase(value);
+ } else {
+ // Cache builder dylibs don't have binds, so fall back to resolveRebase()
+ wasBind = false;
+ return this->resolveRebase(value);
+ }
+#endif
+}
+
+std::optional<ResolvedValue> Visitor::resolveOptionalRebase(const ResolvedValue& value) const
+{
+#if POINTERS_ARE_UNSLID
+ uint64_t runtimeOffset = 0;
+
+ if ( this->sharedCacheChainedPointerFormat != SharedCacheFormat::none ) {
+ // Crack the shared cache slide format
+ switch ( this->sharedCacheChainedPointerFormat ) {
+ case SharedCacheFormat::none:
+ assert(false);
+ case SharedCacheFormat::v1: {
+ uint64_t rawvalue = *(uint32_t*)value.value();
+ if ( rawvalue == 0 )
+ return { };
+ runtimeOffset = rawvalue - onDiskDylibChainedPointerBaseAddress.rawValue();
+ break;
+ }
+ case SharedCacheFormat::v2_x86_64_tbi: {
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint64_t rawValue = fixup->raw64;
+ if ( rawValue == 0 )
+ return { };
+
+ const uint64_t deltaMask = 0x00FFFF0000000000;
+ const uint64_t valueMask = ~deltaMask;
+ rawValue = (rawValue & valueMask);
+ // Already a runtime offset, so no need to do anything with valueAdd
+ runtimeOffset = rawValue;
+ break;
+ }
+ case SharedCacheFormat::v3: {
+ // Just use the chained pointer format for arm64e
+ auto* chainedValue = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ if ( chainedValue->raw64 == 0 )
+ return { };
+
+ chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E,
+ onDiskDylibChainedPointerBaseAddress.rawValue(),
+ runtimeOffset);
+ break;
+ }
+ case SharedCacheFormat::v4: {
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint64_t rawValue = fixup->raw32;
+ if ( rawValue == 0 )
+ return { };
+
+ const uint64_t deltaMask = 0x00000000C0000000;
+ const uint64_t valueMask = ~deltaMask;
+ rawValue = (rawValue & valueMask);
+ // Already a runtime offset, so no need to do anything with valueAdd
+ runtimeOffset = rawValue;
+ break;
+ }
+ }
+ } else {
+ const auto* fixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ if ( this->chainedPointerFormat == 0 ) {
+ // HACK: 32-bit cache dylibs don't have enough bits to have real chains, so we pretend they
+ // have no chains, just raw VMAddr's
+ assert(dylibMA->hasOpcodeFixups());
+
+ // HACK: This is a binary without chained fixups. Is it safe to assume this is a rebase?
+ uint64_t rebaseVMAddr = (pointerSize == 8) ? fixup->raw64 : fixup->raw32;
+ if ( rebaseVMAddr == 0 )
+ return { };
+
+ runtimeOffset = rebaseVMAddr - this->onDiskDylibChainedPointerBaseAddress.rawValue();
+ } else {
+ if ( pointerSize == 8 ) {
+ if ( fixup->raw64 == 0 )
+ return { };
+ } else {
+ if ( fixup->raw32 == 0 )
+ return { };
+ }
+
+ bool isRebase = fixup->isRebase(this->chainedPointerFormat,
+ onDiskDylibChainedPointerBaseAddress.rawValue(),
+ runtimeOffset);
+ assert(isRebase);
+ }
+ }
+
+ VMAddress targetVMAddress = onDiskDylibChainedPointerBaseAddress + VMOffset(runtimeOffset);
+ return this->getValueFor(targetVMAddress);
+#elif SUPPORT_VM_LAYOUT
+ // In dyld, we just use raw pointers for everything, and don't need to indirect via segment+offset like
+ // in the cache builder
+ const void* targetValue = (const void*)*(uintptr_t*)value.value();
+
+ // FIXME: We didn't expect a null here. Should we find a way to error out, or just let the parser
+ // crash with a nullptr dereference.
+ if ( targetValue == nullptr )
+ return std::nullopt;
+
+ // The value may have been signed. Strip the signature if that is the case
+#if __has_feature(ptrauth_calls)
+ targetValue = __builtin_ptrauth_strip(targetValue, ptrauth_key_asia);
+#endif
+
+ VMOffset offsetInDylib((uint64_t)targetValue - (uint64_t)this->dylibMA);
+ return ResolvedValue(targetValue, this->dylibBaseAddress + offsetInDylib);
+#else
+ // In on-disk dylibs, we crack the chained fixups or other fixups
+ if ( this->isOnDiskBinary() ) {
+ uint64_t runtimeOffset = 0;
+ const auto* fixupLoc = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint16_t chainedPointerFormat = value.chainedPointerFormat().value();
+ if ( chainedPointerFormat == 0 ) {
+ assert(dylibMF->hasOpcodeFixups() || (dylibMF->inDyldCache() && !dylibMF->is64()));
+
+ // HACK: This is a binary without chained fixups. Is it safe to assume this is a rebase?
+ uint64_t rebaseVMAddr = (pointerSize == 8) ? fixupLoc->raw64 : fixupLoc->raw32;
+
+ // Assume null VMAddr's means there's no value here
+ if ( rebaseVMAddr == 0 )
+ return { };
+
+ runtimeOffset = rebaseVMAddr - this->onDiskDylibChainedPointerBaseAddress.rawValue();
+ } else {
+ bool isRebase = fixupLoc->isRebase(chainedPointerFormat, onDiskDylibChainedPointerBaseAddress.rawValue(), runtimeOffset);
+ assert(isRebase);
+
+ if ( pointerSize == 8 ) {
+ if ( fixupLoc->raw64 == 0 )
+ return { };
+ } else {
+ if ( fixupLoc->raw32 == 0 )
+ return { };
+ }
+
+ // Assume an offset of 0 means its null. There's no good reason for an objc class to have an offset of 0 from the cache.
+ if ( runtimeOffset == 0 )
+ return { };
+ }
+
+ VMAddress targetVMAddress = this->onDiskDylibChainedPointerBaseAddress + VMOffset(runtimeOffset);
+ return this->getValueFor(targetVMAddress);
+ } else {
+ // Cache builder dylib. These use values in a packed format
+ if ( this->pointerSize == 4 ) {
+ const void* fixupLocation = value.value();
+ if ( Cache32::isNull(fixupLocation) )
+ return { };
+
+ CacheVMAddress targetCacheVMAddress = Cache32::getCacheVMAddressFromLocation(sharedCacheBaseAddress, fixupLocation);
+ VMAddress targetVMAddress(targetCacheVMAddress.rawValue());
+ return this->getValueFor(targetVMAddress);
+ } else {
+ const void* fixupLocation = value.value();
+ if ( Cache64::isNull(fixupLocation) )
+ return { };
+
+ CacheVMAddress targetCacheVMAddress = Cache64::getCacheVMAddressFromLocation(sharedCacheBaseAddress, fixupLocation);
+ VMAddress targetVMAddress(targetCacheVMAddress.rawValue());
+ return this->getValueFor(targetVMAddress);
+ }
+ }
+#endif
+}
+
+std::optional<VMAddress> Visitor::resolveOptionalRebaseToVMAddress(const ResolvedValue& value) const
+{
+#if SUPPORT_VM_LAYOUT
+ // In dyld, we just use raw pointers for everything, and don't need to indirect via segment+offset like
+ // in the cache builder
+ const void* targetValue = (const void*)*(uintptr_t*)value.value();
+
+ // FIXME: We didn't expect a null here. Should we find a way to error out, or just let the parser
+ // crash with a nullptr dereference.
+ if ( targetValue == nullptr )
+ return std::nullopt;
+
+ // The value may have been signed. Strip the signature if that is the case
+#if __has_feature(ptrauth_calls)
+ targetValue = __builtin_ptrauth_strip(targetValue, ptrauth_key_asia);
+#endif
+
+ VMOffset offsetInDylib((uint64_t)targetValue - (uint64_t)this->dylibMA);
+ return ResolvedValue(targetValue, this->dylibBaseAddress + offsetInDylib).vmAddress();
+#else
+ // In on-disk dylibs, we crack the chained fixups or other fixups
+ if ( this->isOnDiskBinary() ) {
+ uint64_t runtimeOffset = 0;
+ const auto* fixupLoc = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
+ uint16_t chainedPointerFormat = value.chainedPointerFormat().value();
+ if ( chainedPointerFormat == 0 ) {
+ assert(dylibMF->hasOpcodeFixups() || (dylibMF->inDyldCache() && !dylibMF->is64()));
+
+ // HACK: This is a binary without chained fixups. Is it safe to assume this is a rebase?
+ uint64_t rebaseVMAddr = (pointerSize == 8) ? fixupLoc->raw64 : fixupLoc->raw32;
+
+ // Assume null VMAddr's means there's no value here
+ if ( rebaseVMAddr == 0 )
+ return { };
+
+ runtimeOffset = rebaseVMAddr - this->onDiskDylibChainedPointerBaseAddress.rawValue();
+ } else {
+ bool isRebase = fixupLoc->isRebase(chainedPointerFormat, onDiskDylibChainedPointerBaseAddress.rawValue(), runtimeOffset);
+ assert(isRebase);
+
+ if ( pointerSize == 8 ) {
+ if ( fixupLoc->raw64 == 0 )
+ return { };
+ } else {
+ if ( fixupLoc->raw32 == 0 )
+ return { };
+ }
+
+ // Assume an offset of 0 means its null. There's no good reason for an objc class to have an offset of 0 from the cache.
+ if ( runtimeOffset == 0 )
+ return { };
+ }
+
+ VMAddress targetVMAddress = this->onDiskDylibChainedPointerBaseAddress + VMOffset(runtimeOffset);
+ return targetVMAddress;
+ } else {
+ // Cache builder dylib. These use values in a packed format
+ if ( this->pointerSize == 4 ) {
+ const void* fixupLocation = value.value();
+ if ( Cache32::isNull(fixupLocation) )
+ return { };
+
+ CacheVMAddress targetCacheVMAddress = Cache32::getCacheVMAddressFromLocation(sharedCacheBaseAddress, fixupLocation);
+ VMAddress targetVMAddress(targetCacheVMAddress.rawValue());
+ return targetVMAddress;
+ } else {
+ const void* fixupLocation = value.value();
+ if ( Cache64::isNull(fixupLocation) )
+ return { };
+
+ CacheVMAddress targetCacheVMAddress = Cache64::getCacheVMAddressFromLocation(sharedCacheBaseAddress, fixupLocation);
+ VMAddress targetVMAddress(targetCacheVMAddress.rawValue());
+ return targetVMAddress;
+ }
+ }
+#endif
+}
+
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+void Visitor::setTargetVMAddress(ResolvedValue& value, CacheVMAddress vmAddr, const dyld3::MachOFile::PointerMetaData& PMD) const
+{
+ assert(!isOnDiskDylib);
+
+ void* fixupLocation = value.value();
+ if ( this->pointerSize == 4 ) {
+ Cache32::setLocation(this->sharedCacheBaseAddress, fixupLocation, vmAddr);
+ } else {
+ uint8_t high8 = 0;
+ Cache64::setLocation(this->sharedCacheBaseAddress, fixupLocation, vmAddr, high8,
+ PMD.diversity, PMD.usesAddrDiversity, PMD.key,
+ PMD.authenticated);
+ }
+}
+
+// Update just the target address in a given location. Doesn't change any of the other fields
+// such as high8 or PointerMetadata
+void Visitor::updateTargetVMAddress(ResolvedValue& value, CacheVMAddress vmAddr) const
+{
+ assert(!isOnDiskDylib);
+
+ void* fixupLocation = value.value();
+ if ( this->pointerSize == 4 ) {
+ Cache32::updateLocationToCacheVMAddress(this->sharedCacheBaseAddress, fixupLocation, vmAddr);
+ } else {
+ Cache64::updateLocationToCacheVMAddress(this->sharedCacheBaseAddress, fixupLocation, vmAddr);
+ }
+}
+#endif
+
+#endif // !TARGET_OS_EXCLAVEKIT