Loading...
--- dyld/dyld-1340/common/MetadataVisitor.cpp
+++ /dev/null
@@ -1,828 +0,0 @@
-/* -*- 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
-
-using mach_o::Header;
-
-//
-// 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(((const Header*)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 if ( slideInfoHeader->version == 5 ) {
- this->sharedCacheChainedPointerFormat = SharedCacheFormat::v5;
- this->onDiskDylibChainedPointerBaseAddress = VMAddress(dyldCache->unslidLoadAddress());
- } else {
- assert(false);
- }
- });
- });
- } else {
- if ( dylibMA->hasChainedFixups() )
- this->chainedPointerFormat = dylibMA->chainedPointerFormat();
- }
-}
-
-#elif SUPPORT_VM_LAYOUT
-
-Visitor::Visitor(const dyld3::MachOAnalyzer* dylibMA)
- : dylibMA(dylibMA), dylibBaseAddress(((const Header*)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;
-}
-
-const Header* Visitor::hdr() const
-{
- return (const Header*)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;
- }
- case SharedCacheFormat::v5: {
- // Just use the chained pointer format for arm64/arm64e shared caches
- auto* chainedValue = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
- chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE,
- onDiskDylibChainedPointerBaseAddress.rawValue(),
- runtimeOffset);
- 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;
- }
- case SharedCacheFormat::v5: {
- // Just use the chained pointer format for arm64/arm64e shared caches
- auto* chainedValue = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
- if ( chainedValue->raw64 == 0 )
- return { };
-
- chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE,
- onDiskDylibChainedPointerBaseAddress.rawValue(),
- runtimeOffset);
- 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 POINTERS_ARE_UNSLID
- 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;
-
- 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;
- }
- case SharedCacheFormat::v5: {
- // Just use the chained pointer format for arm64/arm64e shared caches
- auto* chainedValue = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)value.value();
- if ( chainedValue->raw64 == 0 )
- return { };
-
- chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE,
- onDiskDylibChainedPointerBaseAddress.rawValue(),
- runtimeOffset);
- 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 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).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