2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
27 #include <uuid/uuid.h>
30 #include <mach-o/dyld_priv.h>
32 #include <sys/sysctl.h>
33 #include <System/machine/cpu_capabilities.h>
37 #include <corecrypto/ccdigest.h>
38 #include <corecrypto/ccsha2.h>
42 #include "MachOFile.h"
43 #include "MachOLoaded.h"
44 #include "StringUtils.h"
46 #include "objc-shared-cache.h"
53 //////////////////////////// TypedBytes ////////////////////////////////////////
55 const void* TypedBytes::payload() const
57 return (uint8_t*)this + sizeof(TypedBytes
);
60 void* TypedBytes::payload()
62 return (uint8_t*)this + sizeof(TypedBytes
);
66 //////////////////////////// ContainerTypedBytes ////////////////////////////////////////
68 const TypedBytes
* ContainerTypedBytes::first() const
70 return (TypedBytes
*)payload();
73 const TypedBytes
* ContainerTypedBytes::next(const TypedBytes
* p
) const
75 assert((p
->payloadLength
& 0x3) == 0);
76 return (TypedBytes
*)((uint8_t*)(p
->payload()) + p
->payloadLength
);
79 void ContainerTypedBytes::forEachAttribute(void (^handler
)(const TypedBytes
* typedBytes
, bool& stop
)) const
81 assert(((long)this & 0x3) == 0);
82 const TypedBytes
* end
= next(this);
84 for (const TypedBytes
* p
= first(); p
< end
&& !stop
; p
= next(p
)) {
89 void ContainerTypedBytes::forEachAttributePayload(Type requestedType
, void (^handler
)(const void* payload
, uint32_t size
, bool& stop
)) const
91 forEachAttribute(^(const TypedBytes
* typedBytes
, bool& stop
) {
92 if ( (Type
)(typedBytes
->type
) != requestedType
)
94 handler(typedBytes
->payload(), typedBytes
->payloadLength
, stop
);
98 const void* ContainerTypedBytes::findAttributePayload(Type requestedType
, uint32_t* payloadSize
) const
100 assert(((long)this & 0x3) == 0);
101 if ( payloadSize
!= nullptr )
103 const TypedBytes
* end
= next(this);
105 for (const TypedBytes
* p
= first(); p
< end
&& !stop
; p
= next(p
)) {
106 if ( (Type
)(p
->type
) == requestedType
) {
107 if ( payloadSize
!= nullptr )
108 *payloadSize
= p
->payloadLength
;
116 //////////////////////////// Image ////////////////////////////////////////
118 const Image::Flags
& Image::getFlags() const
120 return *(Flags
*)((uint8_t*)this + 2*sizeof(TypedBytes
));
123 bool Image::isInvalid() const
125 return getFlags().isInvalid
;
128 size_t Image::size() const
130 return sizeof(TypedBytes
) + this->payloadLength
;
133 ImageNum
Image::imageNum() const
135 return getFlags().imageNum
;
138 // returns true iff 'num' is this image's ImageNum, or this image overrides that imageNum (in dyld cache)
139 bool Image::representsImageNum(ImageNum num
) const
141 const Flags
& flags
= getFlags();
142 if ( flags
.imageNum
== num
)
144 if ( !flags
.isDylib
)
146 if ( !flags
.hasOverrideImageNum
)
148 ImageNum cacheImageNum
;
149 if ( isOverrideOfDyldCacheImage(cacheImageNum
) )
150 return (cacheImageNum
== num
);
154 uint32_t Image::maxLoadCount() const
156 return getFlags().maxLoadCount
;
159 bool Image::isBundle() const
161 return getFlags().isBundle
;
164 bool Image::isDylib() const
166 return getFlags().isDylib
;
169 bool Image::isExecutable() const
171 return getFlags().isExecutable
;
174 bool Image::hasObjC() const
176 return getFlags().hasObjC
;
179 bool Image::is64() const
181 return getFlags().is64
;
184 bool Image::hasWeakDefs() const
186 return getFlags().hasWeakDefs
;
189 bool Image::mayHavePlusLoads() const
191 return getFlags().mayHavePlusLoads
;
194 bool Image::neverUnload() const
196 return getFlags().neverUnload
;
199 bool Image::overridableDylib() const
201 return getFlags().overridableDylib
;
204 bool Image::inDyldCache() const
206 return getFlags().inDyldCache
;
209 const char* Image::path() const
211 // might be multiple pathWithHash enties, first is canonical name
212 const PathAndHash
* result
= (PathAndHash
*)findAttributePayload(Type::pathWithHash
);
213 assert(result
&& "Image missing pathWithHash");
217 const char* Image::leafName() const
220 // might be multiple pathWithHash enties, first is canonical name
221 const PathAndHash
* result
= (PathAndHash
*)findAttributePayload(Type::pathWithHash
, &size
);
222 assert(result
&& "Image missing pathWithHash");
223 for (const char* p
=(char*)result
+ size
; p
> result
->path
; --p
) {
230 bool Image::hasFileModTimeAndInode(uint64_t& inode
, uint64_t& mTime
) const
233 const FileInfo
* info
= (FileInfo
*)(findAttributePayload(Type::fileInodeAndTime
, &size
));
234 if ( info
!= nullptr ) {
235 assert(size
== sizeof(FileInfo
));
237 mTime
= info
->modTime
;
243 void Image::forEachCDHash(void (^handler
)(const uint8_t cdHash
[20], bool& stop
)) const
245 forEachAttribute(^(const TypedBytes
* typedBytes
, bool& stopLoop
) {
246 if ( (Type
)(typedBytes
->type
) != Type::cdHash
)
248 assert(typedBytes
->payloadLength
== 20);
249 const uint8_t* bytes
= (const uint8_t*)typedBytes
->payload();
250 handler(bytes
, stopLoop
);
254 bool Image::getUuid(uuid_t uuid
) const
257 const uint8_t* bytes
= (uint8_t*)(findAttributePayload(Type::uuid
, &size
));
258 if ( bytes
== nullptr )
261 memcpy(uuid
, bytes
, 16);
265 bool Image::hasCodeSignature(uint32_t& sigFileOffset
, uint32_t& sigSize
) const
268 const Image::CodeSignatureLocation
* sigInfo
= (Image::CodeSignatureLocation
*)(findAttributePayload(Type::codeSignLoc
, &sz
));
269 if ( sigInfo
!= nullptr ) {
270 assert(sz
== sizeof(Image::CodeSignatureLocation
));
271 sigFileOffset
= sigInfo
->fileOffset
;
272 sigSize
= sigInfo
->fileSize
;
278 bool Image::isFairPlayEncrypted(uint32_t& textOffset
, uint32_t& size
) const
281 const Image::FairPlayRange
* fpInfo
= (Image::FairPlayRange
*)(findAttributePayload(Type::fairPlayLoc
, &sz
));
282 if ( fpInfo
!= nullptr ) {
283 assert(sz
== sizeof(Image::FairPlayRange
));
284 textOffset
= fpInfo
->rangeStart
;
285 size
= fpInfo
->rangeLength
;
291 const Array
<Image::LinkedImage
> Image::dependentsArray() const
294 LinkedImage
* dependents
= (LinkedImage
*)findAttributePayload(Type::dependents
, &size
);
295 assert((size
% sizeof(LinkedImage
)) == 0);
296 uintptr_t count
= size
/ sizeof(LinkedImage
);
297 return Array
<Image::LinkedImage
>(dependents
, count
, count
);
300 void Image::forEachDependentImage(void (^handler
)(uint32_t dependentIndex
, LinkKind kind
, ImageNum imageNum
, bool& stop
)) const
303 const LinkedImage
* dependents
= (LinkedImage
*)findAttributePayload(Type::dependents
, &size
);
304 assert((size
% sizeof(LinkedImage
)) == 0);
305 const uint32_t count
= size
/ sizeof(LinkedImage
);
307 for (uint32_t i
=0; (i
< count
) && !stop
; ++i
) {
308 LinkKind kind
= dependents
[i
].kind();
309 ImageNum imageNum
= dependents
[i
].imageNum();
310 // ignore missing weak links
311 if ( (imageNum
== kMissingWeakLinkedImage
) && (kind
== LinkKind::weak
) )
313 handler(i
, kind
, imageNum
, stop
);
317 ImageNum
Image::dependentImageNum(uint32_t depIndex
) const
320 const LinkedImage
* dependents
= (LinkedImage
*)findAttributePayload(Type::dependents
, &size
);
321 assert((size
% sizeof(LinkedImage
)) == 0);
322 const uint32_t count
= size
/ sizeof(LinkedImage
);
323 assert(depIndex
< count
);
324 return dependents
[depIndex
].imageNum();
328 uint32_t Image::hashFunction(const char* str
)
331 for (const char* s
=str
; *s
!= '\0'; ++s
)
336 void Image::forEachAlias(void (^handler
)(const char* aliasPath
, bool& stop
)) const
338 __block
bool foundFirst
= false;
339 forEachAttribute(^(const TypedBytes
* typedBytes
, bool& stopLoop
) {
340 if ( (Type
)(typedBytes
->type
) != Type::pathWithHash
)
343 const PathAndHash
* aliasInfo
= (PathAndHash
*)typedBytes
->payload();
344 handler(aliasInfo
->path
, stopLoop
);
352 bool Image::hasPathWithHash(const char* path
, uint32_t hash
) const
354 __block
bool found
= false;
355 forEachAttribute(^(const TypedBytes
* typedBytes
, bool& stop
) {
356 if ( (Type
)(typedBytes
->type
) != Type::pathWithHash
)
358 const PathAndHash
* pathInfo
= (PathAndHash
*)typedBytes
->payload();
359 if ( (pathInfo
->hash
== hash
) && (strcmp(path
, pathInfo
->path
) == 0) ) {
367 void Image::forEachDiskSegment(void (^handler
)(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
,
368 uint8_t permissions
, bool laterReadOnly
, bool& stop
)) const
371 const DiskSegment
* segments
= (DiskSegment
*)findAttributePayload(Type::diskSegment
, &size
);
372 assert(segments
!= nullptr);
373 assert((size
% sizeof(DiskSegment
)) == 0);
374 const uint32_t count
= size
/ sizeof(DiskSegment
);
375 const uint32_t pageSz
= pageSize();
376 uint32_t segIndex
= 0;
377 uint32_t fileOffset
= 0;
378 int64_t vmOffset
= 0;
379 // decrement vmOffset by all segments before TEXT (e.g. PAGEZERO)
380 for (uint32_t i
=0; i
< count
; ++i
) {
381 const DiskSegment
* seg
= &segments
[i
];
382 if ( seg
->filePageCount
!= 0 ) {
385 vmOffset
-= (uint64_t)seg
->vmPageCount
* pageSz
;
387 // walk each segment and call handler
389 for (uint32_t i
=0; i
< count
&& !stop
; ++i
) {
390 const DiskSegment
* seg
= &segments
[i
];
391 uint64_t vmSize
= (uint64_t)seg
->vmPageCount
* pageSz
;
392 uint32_t fileSize
= seg
->filePageCount
* pageSz
;
393 if ( !seg
->paddingNotSeg
) {
394 uint8_t perms
= seg
->permissions
;
395 bool laterRO
= false;
396 // read-only data segments are encoded as .w. , initially make them r/w
397 if ( perms
== Image::DiskSegment::kReadOnlyDataPermissions
) {
398 perms
= VM_PROT_READ
|VM_PROT_WRITE
;
401 handler(segIndex
, ( fileSize
== 0) ? 0 : fileOffset
, fileSize
, vmOffset
, vmSize
, perms
, laterRO
, stop
);
405 fileOffset
+= fileSize
;
409 uint32_t Image::pageSize() const
411 if ( getFlags().has16KBpages
)
417 uint32_t Image::cacheOffset() const
420 const DyldCacheSegment
* segments
= (DyldCacheSegment
*)findAttributePayload(Type::cacheSegment
, &size
);
421 assert(segments
!= nullptr);
422 assert((size
% sizeof(DyldCacheSegment
)) == 0);
423 return segments
[0].cacheOffset
;
426 void Image::forEachCacheSegment(void (^handler
)(uint32_t segIndex
, uint64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool& stop
)) const
429 const DyldCacheSegment
* segments
= (DyldCacheSegment
*)findAttributePayload(Type::cacheSegment
, &size
);
430 assert(segments
!= nullptr);
431 assert((size
% sizeof(DyldCacheSegment
)) == 0);
432 const uint32_t count
= size
/ sizeof(DyldCacheSegment
);
434 for (uint32_t i
=0; i
< count
; ++i
) {
435 uint64_t vmOffset
= segments
[i
].cacheOffset
- segments
[0].cacheOffset
;
436 uint64_t vmSize
= segments
[i
].size
;
437 uint8_t permissions
= segments
[i
].permissions
;
438 handler(i
, vmOffset
, vmSize
, permissions
, stop
);
444 uint64_t Image::textSize() const
446 __block
uint64_t result
= 0;
447 if ( inDyldCache() ) {
448 forEachCacheSegment(^(uint32_t segIndex
, uint64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool& stop
) {
454 forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool laterReadOnly
, bool& stop
) {
455 if ( permissions
!= 0) {
464 bool Image::containsAddress(const void* addr
, const void* imageLoadAddress
, uint8_t* permsResult
) const
466 __block
bool result
= false;
467 uint64_t targetAddr
= (uint64_t)addr
;
468 uint64_t imageStart
= (uint64_t)imageLoadAddress
;
469 if ( inDyldCache() ) {
470 forEachCacheSegment(^(uint32_t segIndex
, uint64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool& stop
) {
471 if ( (targetAddr
>= imageStart
+vmOffset
) && (targetAddr
< imageStart
+vmOffset
+vmSize
) ) {
474 *permsResult
= permissions
;
480 forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool laterReadOnly
, bool& stop
) {
481 if ( (targetAddr
>= imageStart
+vmOffset
) && (targetAddr
< imageStart
+vmOffset
+vmSize
) ) {
484 *permsResult
= permissions
;
492 uint64_t Image::vmSizeToMap() const
495 const Image::MappingInfo
* info
= (Image::MappingInfo
*)(findAttributePayload(Type::mappingInfo
, &size
));
496 assert(info
!= nullptr);
497 assert(size
== sizeof(Image::MappingInfo
));
498 return info
->totalVmPages
* pageSize();
501 uint64_t Image::sliceOffsetInFile() const
504 const Image::MappingInfo
* info
= (Image::MappingInfo
*)(findAttributePayload(Type::mappingInfo
, &size
));
505 assert(info
!= nullptr);
506 assert(size
== sizeof(Image::MappingInfo
));
507 return info
->sliceOffsetIn4K
* 0x1000;
510 void Image::forEachInitializer(const void* imageLoadAddress
, void (^handler
)(const void* initializer
)) const
513 const uint32_t* inits
= (uint32_t*)findAttributePayload(Type::initOffsets
, &size
);
514 if ( inits
!= nullptr ) {
515 assert((size
% sizeof(uint32_t)) == 0);
516 const uint32_t count
= size
/ sizeof(uint32_t);
517 for (uint32_t i
=0; i
< count
; ++i
) {
518 uint32_t offset
= inits
[i
];
519 const void* init
= (void*)((uint8_t*)imageLoadAddress
+ offset
);
524 const Image::InitializerSectionRange
* range
= (Image::InitializerSectionRange
*)findAttributePayload(Type::initsSection
, &size
);
525 if ( range
!= nullptr ) {
526 const uint32_t pointerSize
= is64() ? 8 : 4;
527 const uint32_t* start32
= (uint32_t*)((uint8_t*)imageLoadAddress
+ range
->sectionOffset
);
528 const uint64_t* start64
= (uint64_t*)((uint8_t*)imageLoadAddress
+ range
->sectionOffset
);
529 const uint32_t count
= range
->sectionSize
/ pointerSize
;
530 for (uint32_t i
=0; i
< count
; ++i
) {
531 if ( pointerSize
== 8 )
532 handler((void*)(long)(start64
[i
]));
534 handler((void*)(long)(start32
[i
]));
539 bool Image::forEachInitializerSection(void (^handler
)(uint32_t sectionOffset
, uint32_t sectionSize
)) const
541 __block
bool result
= false;
542 forEachAttributePayload(Type::initsSection
, ^(const void* payload
, uint32_t size
, bool& stop
) {
543 const Image::InitializerSectionRange
* range
= (Image::InitializerSectionRange
*)payload
;
544 assert((size
% sizeof(Image::InitializerSectionRange
)) == 0);
545 handler(range
->sectionOffset
, range
->sectionSize
);
551 bool Image::hasInitializers() const
554 return ( findAttributePayload(Type::initOffsets
, &size
) != nullptr );
557 bool Image::hasTerminators() const
559 return getFlags().hasTerminators
;
562 bool Image::hasReadOnlyData() const
564 return getFlags().hasReadOnlyData
;
567 bool Image::hasChainedFixups() const
569 return getFlags().hasChainedFixups
;
572 bool Image::hasPrecomputedObjC() const
574 return getFlags().hasPrecomputedObjC
;
577 bool Image::fixupsNotEncoded() const
579 return getFlags().fixupsNotEncoded
;
582 bool Image::rebasesNotEncoded() const
584 return getFlags().rebasesNotEncoded
;
587 void Image::forEachTerminator(const void* imageLoadAddress
, void (^handler
)(const void* terminator
)) const
590 const uint32_t* terms
= (uint32_t*)findAttributePayload(Type::termOffsets
, &size
);
591 if ( terms
!= nullptr ) {
592 assert((size
% sizeof(uint32_t)) == 0);
593 const uint32_t count
= size
/ sizeof(uint32_t);
594 for (uint32_t i
=0; i
< count
; ++i
) {
595 uint32_t offset
= terms
[i
];
596 const void* term
= (void*)((uint8_t*)imageLoadAddress
+ offset
);
602 void Image::forEachDOF(const void* imageLoadAddress
, void (^handler
)(const void* dofSection
)) const
605 const uint32_t* dofs
= (uint32_t*)findAttributePayload(Type::dofOffsets
, &size
);
606 if ( dofs
!= nullptr ) {
607 assert((size
% sizeof(uint32_t)) == 0);
608 const uint32_t count
= size
/ sizeof(uint32_t);
609 for (uint32_t i
=0; i
< count
; ++i
) {
610 uint32_t offset
= dofs
[i
];
611 const void* sect
= (void*)((uint8_t*)imageLoadAddress
+ offset
);
617 void Image::forEachFixup(void (^rebase
)(uint64_t imageOffsetToRebase
, bool& stop
),
618 void (^bind
)(uint64_t imageOffsetToBind
, ResolvedSymbolTarget bindTarget
, bool& stop
),
619 void (^chainedFixups
)(uint64_t imageOffsetToStarts
, const Array
<ResolvedSymbolTarget
>& targets
, bool& stop
),
620 void (^fixupObjCImageInfo
)(uint64_t imageOffsetToFixup
),
621 void (^fixupObjCProtocol
)(uint64_t imageOffsetToBind
, ResolvedSymbolTarget bindTarget
, bool& stop
),
622 void (^fixupObjCSelRef
)(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool& stop
),
623 void (^fixupObjCStableSwift
)(uint64_t imageOffsetToFixup
, bool& stop
),
624 void (^fixupObjCMethodList
)(uint64_t imageOffsetToFixup
, bool& stop
)) const
626 const uint32_t pointerSize
= is64() ? 8 : 4;
627 uint64_t curRebaseOffset
= 0;
629 for (const Image::RebasePattern
& rebasePat
: rebaseFixups()) {
630 //fprintf(stderr, " repeat=0x%04X, contig=%d, skip=%d\n", rebasePat.repeatCount, rebasePat.contigCount, rebasePat.skipCount);
631 if ( rebasePat
.contigCount
== 0 ) {
632 // note: contigCount==0 means this just advances location
633 if ( (rebasePat
.repeatCount
== 0) && (rebasePat
.skipCount
== 0) ) {
634 // all zeros is special pattern that means reset to rebase offset to zero
638 curRebaseOffset
+= rebasePat
.repeatCount
* rebasePat
.skipCount
;
642 for (int r
=0; r
< rebasePat
.repeatCount
&& !stop
; ++r
) {
643 for (int i
=0; i
< rebasePat
.contigCount
&& !stop
; ++i
) {
644 //fprintf(stderr, " 0x%08llX\n", curRebaseOffset);
645 rebase(curRebaseOffset
, stop
);
646 curRebaseOffset
+= pointerSize
;
648 curRebaseOffset
+= pointerSize
* rebasePat
.skipCount
;
657 stop
= this->forEachBind(bind
);
662 if (hasChainedFixups())
663 chainedFixups(chainedStartsOffset(), chainedTargets(), stop
);
665 if ( hasPrecomputedObjC() ) {
666 ResolvedSymbolTarget objcProtocolClassTarget
;
667 uint64_t objcImageInfoVMOffset
= 0;
668 Array
<ProtocolISAFixup
> protocolISAFixups
;
669 Array
<Image::SelectorReferenceFixup
> selRefFixupEntries
;
670 Array
<Image::ClassStableSwiftFixup
> classStableSwiftFixups
;
671 Array
<Image::MethodListFixup
> methodListFixups
;
672 objcFixups(objcProtocolClassTarget
, objcImageInfoVMOffset
, protocolISAFixups
,
673 selRefFixupEntries
, classStableSwiftFixups
, methodListFixups
);
675 // Set the objc image info bit to tell libobjc we are optimized
676 fixupObjCImageInfo(objcImageInfoVMOffset
);
679 // First bind all the protocols to the same Protocol class in libobjc
680 for (const Image::ProtocolISAFixup
& bindPat
: protocolISAFixups
) {
681 uint64_t curBindOffset
= bindPat
.startVmOffset
;
682 for (uint16_t i
=0; i
< bindPat
.repeatCount
; ++i
) {
683 fixupObjCProtocol(curBindOffset
, objcProtocolClassTarget
, stop
);
684 curBindOffset
+= (pointerSize
* (1 + bindPat
.skipCount
));
692 for (uintptr_t i
= 0, e
= selRefFixupEntries
.count(); i
!= e
; ++i
) {
693 Image::SelectorReferenceFixup fixupEntry
= selRefFixupEntries
[i
];
695 uint64_t curFixupOffset
= fixupEntry
.chainStartVMOffset
;
696 // Now walk the chain until we get a 'next' of 0
698 fixupEntry
= selRefFixupEntries
[++i
];
699 fixupObjCSelRef(curFixupOffset
, fixupEntry
.chainEntry
.index
, fixupEntry
.chainEntry
.inSharedCache
, stop
);
702 if ( fixupEntry
.chainEntry
.next
== 0 )
704 curFixupOffset
+= (4 * fixupEntry
.chainEntry
.next
);
708 // Set classes to have stable Swift
709 for (const Image::ClassStableSwiftFixup
& bindPat
: classStableSwiftFixups
) {
710 uint64_t curBindOffset
= bindPat
.startVmOffset
;
711 for (uint16_t i
=0; i
< bindPat
.repeatCount
; ++i
) {
712 fixupObjCStableSwift(curBindOffset
, stop
);
713 curBindOffset
+= (pointerSize
* (1 + bindPat
.skipCount
));
721 // Set method lists to be fixed up
722 for (const Image::MethodListFixup
& bindPat
: methodListFixups
) {
723 uint64_t curBindOffset
= bindPat
.startVmOffset
;
724 for (uint16_t i
=0; i
< bindPat
.repeatCount
; ++i
) {
725 fixupObjCMethodList(curBindOffset
, stop
);
726 curBindOffset
+= (pointerSize
* (1 + bindPat
.skipCount
));
736 bool Image::forEachBind(void (^bind
)(uint64_t imageOffsetToBind
, ResolvedSymbolTarget bindTarget
, bool& stop
)) const
738 const uint32_t pointerSize
= is64() ? 8 : 4;
740 for (const Image::BindPattern
& bindPat
: bindFixups()) {
741 uint64_t curBindOffset
= bindPat
.startVmOffset
;
742 for (uint16_t i
=0; i
< bindPat
.repeatCount
; ++i
) {
743 bind(curBindOffset
, bindPat
.target
, stop
);
744 curBindOffset
+= (pointerSize
* (1 + bindPat
.skipCount
));
754 void Image::forEachTextReloc(void (^rebase
)(uint32_t imageOffsetToRebase
, bool& stop
),
755 void (^bind
)(uint32_t imageOffsetToBind
, ResolvedSymbolTarget bindTarget
, bool& stop
)) const
758 const Array
<Image::TextFixupPattern
> f
= textFixups();
759 for (const Image::TextFixupPattern
& pat
: f
) {
760 uint32_t curOffset
= pat
.startVmOffset
;
761 for (uint16_t i
=0; i
< pat
.repeatCount
; ++i
) {
762 if ( pat
.target
.raw
== 0 )
763 rebase(curOffset
, stop
);
765 bind(curOffset
, pat
.target
, stop
);
766 curOffset
+= pat
.skipCount
;
771 const Array
<Image::RebasePattern
> Image::rebaseFixups() const
773 uint32_t rebaseFixupsSize
;
774 Image::RebasePattern
* rebaseFixupsContent
= (RebasePattern
*)findAttributePayload(Type::rebaseFixups
, &rebaseFixupsSize
);
775 uint32_t rebaseCount
= rebaseFixupsSize
/sizeof(RebasePattern
);
776 return Array
<RebasePattern
>(rebaseFixupsContent
, rebaseCount
, rebaseCount
);
779 const Array
<Image::BindPattern
> Image::bindFixups() const
781 uint32_t bindFixupsSize
;
782 BindPattern
* bindFixupsContent
= (BindPattern
*)findAttributePayload(Type::bindFixups
, &bindFixupsSize
);
783 uint32_t bindCount
= bindFixupsSize
/sizeof(BindPattern
);
784 return Array
<BindPattern
>(bindFixupsContent
, bindCount
, bindCount
);
787 uint64_t Image::chainedStartsOffset() const
790 uint64_t* startsOffset
= (uint64_t*)findAttributePayload(Type::chainedStartsOffset
, &size
);
791 if ( startsOffset
== nullptr )
792 return 0; // means no pre-computed offset to starts table
793 assert(size
== sizeof(uint64_t));
794 return *startsOffset
;
797 void Image::objcFixups(ResolvedSymbolTarget
& objcProtocolClassTarget
,
798 uint64_t& objcImageInfoVMOffset
,
799 Array
<ProtocolISAFixup
>& protocolISAFixups
,
800 Array
<SelectorReferenceFixup
>& selRefFixups
,
801 Array
<ClassStableSwiftFixup
>& classStableSwiftFixups
,
802 Array
<MethodListFixup
>& methodListFixups
) const
804 // The layout here is:
805 // ResolvedSymbolTarget
806 // uint64_t vmOffset to objc_imageinfo
807 // uint32_t protocol count
808 // uint32_t selector reference count
809 // array of ProtocolISAFixup
810 // array of SelectorReferenceFixup
811 // optional uint32_t stable swift fixup count
812 // optional uint32_t method list fixup count
813 // optional array of ClassStableSwiftFixup
814 // optional array of MethodListFixup
816 if (!hasPrecomputedObjC())
819 uint32_t contentSize
;
820 const uint8_t* fixupsContent
= (uint8_t*)findAttributePayload(Type::objcFixups
, &contentSize
);
821 const uint8_t* fixupsContentEnd
= fixupsContent
+ contentSize
;
823 // Get the statically sized data
824 uint32_t protocolFixupCount
= 0;
825 uint32_t selRefFixupCount
= 0;
826 memcpy(&objcProtocolClassTarget
, fixupsContent
, sizeof(ResolvedSymbolTarget
));
827 fixupsContent
+= sizeof(ResolvedSymbolTarget
);
828 memcpy(&objcImageInfoVMOffset
, fixupsContent
, sizeof(uint64_t));
829 fixupsContent
+= sizeof(uint64_t);
830 memcpy(&protocolFixupCount
, fixupsContent
, sizeof(uint32_t));
831 fixupsContent
+= sizeof(uint32_t);
832 memcpy(&selRefFixupCount
, fixupsContent
, sizeof(uint32_t));
833 fixupsContent
+= sizeof(uint32_t);
835 // Get the protocol fixups
836 if ( protocolFixupCount
!= 0) {
837 protocolISAFixups
= Array
<ProtocolISAFixup
>((ProtocolISAFixup
*)fixupsContent
, protocolFixupCount
, protocolFixupCount
);
838 fixupsContent
+= (sizeof(ProtocolISAFixup
) * protocolFixupCount
);
841 // Get the selector reference fixups
842 if ( selRefFixupCount
!= 0) {
843 selRefFixups
= Array
<SelectorReferenceFixup
>((SelectorReferenceFixup
*)fixupsContent
, selRefFixupCount
, selRefFixupCount
);
844 fixupsContent
+= (sizeof(SelectorReferenceFixup
) * selRefFixupCount
);
847 // Old closures end here, but newer ones might have additional fixups
848 if (fixupsContent
== fixupsContentEnd
)
851 uint32_t stableSwiftFixupCount
= 0;
852 uint32_t methodListFixupCount
= 0;
853 memcpy(&stableSwiftFixupCount
, fixupsContent
, sizeof(uint32_t));
854 fixupsContent
+= sizeof(uint32_t);
855 memcpy(&methodListFixupCount
, fixupsContent
, sizeof(uint32_t));
856 fixupsContent
+= sizeof(uint32_t);
858 // Get the stable swift fixups
859 if ( stableSwiftFixupCount
!= 0) {
860 classStableSwiftFixups
= Array
<ClassStableSwiftFixup
>((ClassStableSwiftFixup
*)fixupsContent
, stableSwiftFixupCount
, stableSwiftFixupCount
);
861 fixupsContent
+= (sizeof(ClassStableSwiftFixup
) * stableSwiftFixupCount
);
864 // Get the method list fixups
865 if ( methodListFixupCount
!= 0) {
866 methodListFixups
= Array
<MethodListFixup
>((MethodListFixup
*)fixupsContent
, methodListFixupCount
, methodListFixupCount
);
867 fixupsContent
+= (sizeof(MethodListFixup
) * methodListFixupCount
);
871 const Array
<Image::ResolvedSymbolTarget
> Image::chainedTargets() const
874 ResolvedSymbolTarget
* targetsContent
= (ResolvedSymbolTarget
*)findAttributePayload(Type::chainedFixupsTargets
, &size
);
875 uint32_t count
= size
/sizeof(ResolvedSymbolTarget
);
876 return Array
<ResolvedSymbolTarget
>(targetsContent
, count
, count
);
879 const Array
<Image::TextFixupPattern
> Image::textFixups() const
882 TextFixupPattern
* fixupsContent
= (TextFixupPattern
*)findAttributePayload(Type::textFixups
, &fixupsSize
);
883 uint32_t count
= fixupsSize
/sizeof(TextFixupPattern
);
884 return Array
<TextFixupPattern
>(fixupsContent
, count
, count
);
887 bool Image::isOverrideOfDyldCacheImage(ImageNum
& imageNum
) const
890 const uint32_t* content
= (uint32_t*)findAttributePayload(Type::imageOverride
, &size
);
891 if ( content
!= nullptr ) {
892 assert(size
== sizeof(uint32_t));
899 void Image::forEachImageToInitBefore(void (^handler
)(ImageNum imageToInit
, bool& stop
)) const
902 const ImageNum
* initBefores
= (ImageNum
*)findAttributePayload(Type::initBefores
, &size
);
903 if ( initBefores
!= nullptr ) {
904 assert((size
% sizeof(ImageNum
)) == 0);
905 const uint32_t count
= size
/ sizeof(ImageNum
);
907 for (uint32_t i
=0; (i
< count
) && !stop
; ++i
) {
908 handler(initBefores
[i
], stop
);
913 const char* Image::variantString() const
915 return (this->fixupsNotEncoded() ? "minimal" : "full");
918 //////////////////////////// ImageArray ////////////////////////////////////////
920 size_t ImageArray::size() const
922 return sizeof(TypedBytes
) + this->payloadLength
;
925 size_t ImageArray::startImageNum() const
927 return firstImageNum
;
930 uint32_t ImageArray::imageCount() const
935 void ImageArray::forEachImage(void (^callback
)(const Image
* image
, bool& stop
)) const
938 for (uint32_t i
=0; i
< count
&& !stop
; ++i
) {
939 const Image
* image
= (Image
*)((uint8_t*)payload() + offsets
[i
]);
940 callback(image
, stop
);
946 bool ImageArray::hasPath(const char* path
, ImageNum
& num
) const
948 const uint32_t hash
= Image::hashFunction(path
);
949 __block
bool found
= false;
950 forEachImage(^(const Image
* image
, bool& stop
) {
951 if ( image
->hasPathWithHash(path
, hash
) ) {
952 num
= image
->imageNum();
960 const Image
* ImageArray::imageForNum(ImageNum num
) const
963 __block
const Image
* foundImage
= nullptr;
964 forEachImage(^(const Image
*image
, bool &stop
) {
965 if (image
->imageNum() == num
) {
972 if ( num
< firstImageNum
)
975 uint32_t index
= num
- firstImageNum
;
976 if ( index
>= count
)
979 return (Image
*)((uint8_t*)payload() + offsets
[index
]);
982 const Image
* ImageArray::findImage(const Array
<const ImageArray
*> imagesArrays
, ImageNum imageNum
)
984 // Search image arrays backwards as the main closure, or dlopen closures, may rebuild closures
985 // for shared cache images which are not roots, but whose initialisers must rebuild as they depend
987 for (uintptr_t index
= imagesArrays
.count(); index
> 0; --index
) {
988 const ImageArray
* ia
= imagesArrays
[index
- 1];
989 if ( const Image
* result
= ia
->imageForNum(imageNum
) )
995 void ImageArray::deallocate() const
997 ::vm_deallocate(mach_task_self(), (long)this, size());
1000 //////////////////////////// Closure ////////////////////////////////////////
1002 size_t Closure::size() const
1004 return sizeof(TypedBytes
) + this->payloadLength
;
1007 const ImageArray
* Closure::images() const
1009 __block
const TypedBytes
* result
= nullptr;
1010 forEachAttribute(^(const TypedBytes
* typedBytes
, bool& stop
) {
1011 if ( (Type
)(typedBytes
->type
) == Type::imageArray
) {
1012 result
= typedBytes
;
1017 return (ImageArray
*)result
;
1020 ImageNum
Closure::topImageNum() const
1023 const ImageNum
* top
= (ImageNum
*)findAttributePayload(Type::topImage
, &size
);
1024 assert(top
!= nullptr);
1025 assert(size
== sizeof(ImageNum
));
1029 const Image
* Closure::topImage() const
1031 ImageNum imageNum
= this->topImageNum();
1032 const dyld3::closure::ImageArray
* closureImages
= this->images();
1033 return closureImages
->imageForNum(imageNum
);
1036 void Closure::forEachPatchEntry(void (^handler
)(const PatchEntry
& entry
)) const
1038 forEachAttributePayload(Type::cacheOverrides
, ^(const void* payload
, uint32_t size
, bool& stop
) {
1039 assert((size
% sizeof(Closure::PatchEntry
)) == 0);
1040 const PatchEntry
* patches
= (PatchEntry
*)payload
;
1041 const PatchEntry
* patchesEnd
= (PatchEntry
*)((char*)payload
+ size
);
1042 for (const PatchEntry
* p
=patches
; p
< patchesEnd
; ++p
)
1047 void Closure::forEachWarning(Closure::Warning::Type type
, void (^handler
)(const char* warning
, bool& stop
)) const
1049 forEachAttributePayload(Type::warning
, ^(const void* payload
, uint32_t size
, bool& stop
) {
1050 const Closure::Warning
* warning
= (const Closure::Warning
*)payload
;
1051 if ( warning
->type
!= type
)
1053 handler(warning
->message
, stop
);
1057 void Closure::deallocate() const
1059 ::vm_deallocate(mach_task_self(), (long)this, size());
1063 //////////////////////////// LaunchClosure ////////////////////////////////////////
1065 void LaunchClosure::forEachMustBeMissingFile(void (^handler
)(const char* path
, bool& stop
)) const
1068 const char* paths
= (const char*)findAttributePayload(Type::missingFiles
, &size
);
1070 for (const char* s
=paths
; s
< &paths
[size
]; ++s
) {
1079 void LaunchClosure::forEachSkipIfExistsFile(void (^handler
)(const SkippedFile
& file
, bool& stop
)) const
1082 const uint64_t* files
= (const uint64_t*)findAttributePayload(Type::existingFiles
, &size
);
1083 if (files
== nullptr)
1086 // The first entry is the length of the array
1087 uint64_t fileCount
= *files
++;
1089 // Followed by count number of mod times and inodes
1090 const char* paths
= (const char*)(files
+ (2 * fileCount
));
1092 for (const char* s
=paths
; s
< &paths
[size
]; ++s
) {
1094 uint64_t inode
= *files
++;
1095 uint64_t mtime
= *files
++;
1096 SkippedFile skippedFile
= { s
, inode
, mtime
};
1097 handler(skippedFile
, stop
);
1105 bool LaunchClosure::builtAgainstDyldCache(uuid_t cacheUUID
) const
1108 const uint8_t* uuidBytes
= (uint8_t*)findAttributePayload(Type::dyldCacheUUID
, &size
);
1109 if ( uuidBytes
== nullptr )
1111 assert(size
== sizeof(uuid_t
));
1112 memcpy(cacheUUID
, uuidBytes
, sizeof(uuid_t
));
1116 void LaunchClosure::forEachEnvVar(void (^handler
)(const char* keyEqualValue
, bool& stop
)) const
1118 forEachAttributePayload(Type::envVar
, ^(const void* payload
, uint32_t size
, bool& stop
) {
1119 handler((char*)payload
, stop
);
1123 ImageNum
LaunchClosure::libSystemImageNum() const
1126 const ImageNum
* num
= (ImageNum
*)findAttributePayload(Type::libSystemNum
, &size
);
1127 assert(num
!= nullptr);
1128 assert(size
== sizeof(ImageNum
));
1132 void LaunchClosure::libDyldEntry(Image::ResolvedSymbolTarget
& loc
) const
1135 const Image::ResolvedSymbolTarget
* data
= (Image::ResolvedSymbolTarget
*)findAttributePayload(Type::libDyldEntry
, &size
);
1136 assert(data
!= nullptr);
1137 assert(size
== sizeof(Image::ResolvedSymbolTarget
));
1141 bool LaunchClosure::mainEntry(Image::ResolvedSymbolTarget
& mainLoc
) const
1144 const Image::ResolvedSymbolTarget
* data
= (Image::ResolvedSymbolTarget
*)findAttributePayload(Type::mainEntry
, &size
);
1145 if ( data
== nullptr )
1147 assert(size
== sizeof(Image::ResolvedSymbolTarget
));
1152 bool LaunchClosure::startEntry(Image::ResolvedSymbolTarget
& startLoc
) const
1155 const Image::ResolvedSymbolTarget
* data
= (Image::ResolvedSymbolTarget
*)findAttributePayload(Type::startEntry
, &size
);
1156 if ( data
== nullptr )
1158 assert(size
== sizeof(Image::ResolvedSymbolTarget
));
1163 const LaunchClosure::Flags
& LaunchClosure::getFlags() const
1166 const Flags
* flags
= (Flags
*)findAttributePayload(Type::closureFlags
, &size
);
1167 assert(flags
!= nullptr && "Closure missing Flags");
1171 uint32_t LaunchClosure::initialLoadCount() const
1173 return getFlags().initImageCount
;
1176 bool LaunchClosure::usedAtPaths() const
1178 return getFlags().usedAtPaths
;
1181 bool LaunchClosure::usedFallbackPaths() const
1183 return getFlags().usedFallbackPaths
;
1186 bool LaunchClosure::hasInsertedLibraries() const
1188 return getFlags().hasInsertedLibraries
;
1191 bool LaunchClosure::hasProgramVars(uint32_t& runtimeOffset
) const
1193 if ( !getFlags().hasProgVars
)
1195 uint32_t payloadSize
= 0;
1196 const uint8_t* buffer
= (const uint8_t*)findAttributePayload(Type::progVars
, &payloadSize
);
1197 if (buffer
== nullptr)
1199 runtimeOffset
= *((uint32_t*)buffer
);
1203 bool LaunchClosure::usedInterposing() const
1205 return getFlags().usedInterposing
;
1208 bool LaunchClosure::hasInterposings() const
1210 __block
bool result
= false;
1212 forEachInterposingTuple(^(const InterposingTuple
&, bool& stop
) {
1220 void LaunchClosure::forEachInterposingTuple(void (^handler
)(const InterposingTuple
& tuple
, bool& stop
)) const
1222 forEachAttributePayload(Type::interposeTuples
, ^(const void* payload
, uint32_t size
, bool& stop
) {
1223 assert((size
% sizeof(InterposingTuple
)) == 0);
1224 uintptr_t count
= size
/ sizeof(InterposingTuple
);
1225 const InterposingTuple
* tuples
= (InterposingTuple
*)payload
;
1226 for (uint32_t i
=0; i
< count
&& !stop
; ++i
) {
1227 handler(tuples
[i
], stop
);
1231 bool LaunchClosure::selectorHashTable(Array
<Image::ObjCSelectorImage
>& imageNums
,
1232 const closure::ObjCSelectorOpt
*& hashTable
) const {
1233 uint32_t payloadSize
= 0;
1234 const uint8_t* buffer
= (const uint8_t*)findAttributePayload(Type::selectorTable
, &payloadSize
);
1235 if (buffer
== nullptr)
1240 memcpy(&count
, buffer
, sizeof(uint32_t));
1241 buffer
+= sizeof(uint32_t);
1244 imageNums
= Array
<Image::ObjCSelectorImage
>((Image::ObjCSelectorImage
*)buffer
, count
, count
);
1245 buffer
+= sizeof(Image::ObjCSelectorImage
) * count
;
1248 hashTable
= (const closure::ObjCSelectorOpt
*)buffer
;
1253 bool LaunchClosure::classAndProtocolHashTables(Array
<Image::ObjCClassImage
>& imageNums
,
1254 const ObjCClassOpt
*& classHashTable
,
1255 const ObjCClassOpt
*& protocolHashTable
) const {
1256 // The layout here is:
1257 // uint32_t offset to class table (note this is 0 if there are no classes)
1258 // uint32_t offset to protocol table (note this is 0 if there are no protocols)
1259 // uint32_t num images
1260 // ObjCClassImage[num images]
1262 // [ padding to 4-byte alignment if needed
1263 // protocol hash table
1264 // [ padding to 4-byte alignment if needed
1266 uint32_t payloadSize
= 0;
1267 const uint8_t* buffer
= (const uint8_t*)findAttributePayload(Type::classTable
, &payloadSize
);
1268 if (buffer
== nullptr)
1271 uint32_t headerSize
= sizeof(uint32_t) * 3;
1273 uint32_t offsetToClassTable
= 0;
1274 uint32_t offsetToProtocolTable
= 0;
1275 uint32_t numImages
= 0;
1278 memcpy(&offsetToClassTable
, buffer
+ 0, sizeof(uint32_t));
1279 memcpy(&offsetToProtocolTable
, buffer
+ 4, sizeof(uint32_t));
1280 memcpy(&numImages
, buffer
+ 8, sizeof(uint32_t));
1282 // Get the image nums
1283 imageNums
= Array
<Image::ObjCClassImage
>((Image::ObjCClassImage
*)(buffer
+ headerSize
), numImages
, numImages
);
1285 // Get the class hash table if there is one
1286 if ( offsetToClassTable
!= 0 )
1287 classHashTable
= (const ObjCClassOpt
*)(buffer
+ offsetToClassTable
);
1289 // Write out out the protocol hash table if there is one
1290 if ( offsetToProtocolTable
!= 0 )
1291 protocolHashTable
= (const ObjCClassOpt
*)(buffer
+ offsetToProtocolTable
);
1296 void LaunchClosure::duplicateClassesHashTable(const ObjCClassDuplicatesOpt
*& duplicateClassesHashTable
) const {
1297 uint32_t payloadSize
= 0;
1298 const uint8_t* buffer
= (const uint8_t*)findAttributePayload(Type::duplicateClassesTable
, &payloadSize
);
1299 if (buffer
== nullptr)
1302 duplicateClassesHashTable
= (const ObjCClassDuplicatesOpt
*)buffer
;
1306 static bool getContainerLibraryCachesDir(const char* envp
[], char libCacheDir
[])
1308 // $HOME is root of writable data container
1309 const char* homeDir
= _simple_getenv(envp
, "HOME");
1310 if ( homeDir
== nullptr )
1313 // Use realpath to block malicious values like HOME=/tmp/../usr/bin
1314 char realHomePath
[PATH_MAX
];
1315 if ( realpath(homeDir
, realHomePath
) != nullptr )
1316 homeDir
= realHomePath
;
1318 // <rdar://problem/66593232> iOS apps on Apple Silicon macOS have a different data container location
1319 if ( strstr(homeDir
, "/Library/Containers/") == nullptr )
1322 // <rdar://problem/47688842> dyld3 should only save closures to disk for containerized apps
1323 if ( strncmp(homeDir
, "/private/var/mobile/Containers/Data/", 36) != 0 )
1327 // return $HOME/Library/Caches/
1328 strlcpy(libCacheDir
, homeDir
, PATH_MAX
);
1329 strlcat(libCacheDir
, "/Library/Caches", PATH_MAX
);
1333 bool LaunchClosure::buildClosureCachePath(const char* mainExecutablePath
, const char* envp
[],
1334 bool makeDirsIfMissing
, char closurePath
[])
1336 // get path to data container's Library/Caches/ dir
1337 if ( !getContainerLibraryCachesDir(envp
, closurePath
) )
1340 // make sure XXX/Library/Caches/ exists
1341 struct stat statbuf
;
1342 if ( dyld3::stat(closurePath
, &statbuf
) != 0 )
1346 strlcat(closurePath
, "/com.apple.dyld", PATH_MAX
);
1347 if ( makeDirsIfMissing
) {
1348 if ( dyld3::stat(closurePath
, &statbuf
) != 0 ) {
1349 if ( ::mkdir(closurePath
, S_IRWXU
) != 0 )
1354 // add <prog-name> + ".closure"
1355 const char* leafName
= strrchr(mainExecutablePath
, '/');
1356 if ( leafName
== nullptr )
1357 leafName
= mainExecutablePath
;
1360 strlcat(closurePath
, "/", PATH_MAX
);
1361 strlcat(closurePath
, leafName
, PATH_MAX
);
1363 strlcat(closurePath
, ".closure", PATH_MAX
);
1368 //////////////////////////// ObjCStringTable ////////////////////////////////////////
1370 uint32_t ObjCStringTable::hash(const char *key
, size_t keylen
) const
1372 uint64_t val
= objc_opt::lookup8((uint8_t*)key
, keylen
, salt
);
1373 uint32_t index
= (uint32_t)((shift
== 64) ? 0 : (val
>>shift
)) ^ scramble
[tab
[val
&mask
]];
1377 const char* ObjCStringTable::getString(const char* selName
, const Array
<uintptr_t>& baseAddresses
) const {
1378 StringTarget target
= getPotentialTarget(selName
);
1379 if (target
== sentinelTarget
)
1382 dyld3::closure::Image::ObjCImageOffset imageAndOffset
;
1383 imageAndOffset
.raw
= target
;
1385 uintptr_t sectionBaseAddress
= baseAddresses
[imageAndOffset
.imageIndex
];
1387 const char* value
= (const char*)(sectionBaseAddress
+ imageAndOffset
.imageOffset
);
1388 if (!strcmp(selName
, value
))
1393 //////////////////////////// ObjCSelectorOpt ////////////////////////////////////////
1394 bool ObjCSelectorOpt::getStringLocation(uint32_t index
, const Array
<closure::Image::ObjCSelectorImage
>& selImages
,
1395 ImageNum
& imageNum
, uint64_t& vmOffset
) const {
1396 if ( index
>= capacity
)
1399 StringTarget target
= targets()[index
];
1400 if ( target
== indexNotFound
)
1403 dyld3::closure::Image::ObjCImageOffset imageAndOffset
;
1404 imageAndOffset
.raw
= target
;
1406 imageNum
= selImages
[imageAndOffset
.imageIndex
].imageNum
;
1407 vmOffset
= selImages
[imageAndOffset
.imageIndex
].offset
+ imageAndOffset
.imageOffset
;
1411 void ObjCSelectorOpt::forEachString(const Array
<Image::ObjCSelectorImage
>& selectorImages
,
1412 void (^callback
)(uint64_t selVMOffset
, ImageNum imageNum
)) const {
1413 dyld3::Array
<StringTarget
> stringTargets
= targets();
1414 for (unsigned i
= 0; i
!= capacity
; ++i
) {
1415 dyld3::closure::Image::ObjCImageOffset imageAndOffset
;
1416 imageAndOffset
.raw
= stringTargets
[i
];
1418 if (imageAndOffset
.raw
== sentinelTarget
)
1421 callback(selectorImages
[imageAndOffset
.imageIndex
].offset
+ imageAndOffset
.imageOffset
,
1422 selectorImages
[imageAndOffset
.imageIndex
].imageNum
);
1426 //////////////////////////// ObjCClassOpt ////////////////////////////////////////
1428 void ObjCClassOpt::forEachClass(const char* className
, const Array
<std::pair
<uintptr_t, uintptr_t>>& nameAndDataBaseAddresses
,
1429 void (^callback
)(void* classPtr
, bool isLoaded
, bool* stop
)) const {
1430 uint32_t index
= getIndex(className
);
1431 if ( index
== closure::ObjCStringTable::indexNotFound
)
1434 StringTarget target
= targets()[index
];
1435 if ( target
== sentinelTarget
)
1438 // We have a potential target. First check if the name is an exact match given the hash matched
1439 closure::Image::ObjCImageOffset classNameImageAndOffset
;
1440 classNameImageAndOffset
.raw
= target
;
1442 uintptr_t nameBaseAddress
= 0;
1443 uintptr_t dataBaseAddress
= 0;
1444 std::tie(nameBaseAddress
, dataBaseAddress
) = nameAndDataBaseAddresses
[classNameImageAndOffset
.imageIndex
];
1446 const char* value
= (const char*)(nameBaseAddress
+ classNameImageAndOffset
.imageOffset
);
1447 if ( strcmp(className
, value
) != 0 )
1450 // The name matched so now call the handler on all the classes for this name
1451 Array
<closure::ObjCClassOpt::ClassTarget
> classOffsetsArray
= classOffsets();
1452 Array
<closure::ObjCClassOpt::ClassTarget
> duplicatesArray
= duplicateOffsets(duplicateCount());
1454 const closure::ObjCClassOpt::ClassTarget
& classOffset
= classOffsetsArray
[index
];
1455 if (classOffset
.classData
.isDuplicate
== 0) {
1456 // This class has a single implementation
1457 void* classImpl
= (void*)(dataBaseAddress
+ classOffset
.classData
.imageOffset
);
1459 callback(classImpl
, true, &stop
);
1461 // This class has mulitple implementations
1462 uint32_t duplicateCount
= classOffset
.duplicateData
.count
;
1463 uint32_t duplicateStartIndex
= classOffset
.duplicateData
.index
;
1464 for (uint32_t dupeIndex
= 0; dupeIndex
!= duplicateCount
; ++dupeIndex
) {
1465 closure::ObjCClassOpt::ClassTarget
& duplicateClass
= duplicatesArray
[duplicateStartIndex
+ dupeIndex
];
1467 std::tie(nameBaseAddress
, dataBaseAddress
) = nameAndDataBaseAddresses
[duplicateClass
.classData
.imageIndex
];
1468 void* classImpl
= (void*)(dataBaseAddress
+ duplicateClass
.classData
.imageOffset
);
1470 callback(classImpl
, true, &stop
);
1477 void ObjCClassOpt::forEachClass(const Array
<Image::ObjCClassImage
>& classImages
,
1478 void (^nameCallback
)(uint64_t classNameVMOffset
, ImageNum imageNum
),
1479 void (^implCallback
)(uint64_t classVMOffset
, ImageNum imageNum
)) const {
1481 dyld3::Array
<StringTarget
> stringTargets
= targets();
1482 dyld3::Array
<ObjCClassOpt::ClassTarget
> classOffsetsArray
= classOffsets();
1483 dyld3::Array
<ObjCClassOpt::ClassTarget
> duplicatesArray
= duplicateOffsets(duplicateCount());
1484 for (unsigned i
= 0; i
!= capacity
; ++i
) {
1485 dyld3::closure::Image::ObjCImageOffset classNameImageAndOffset
;
1486 classNameImageAndOffset
.raw
= stringTargets
[i
];
1488 if (classNameImageAndOffset
.raw
== sentinelTarget
)
1491 nameCallback(classImages
[classNameImageAndOffset
.imageIndex
].offsetOfClassNames
+ classNameImageAndOffset
.imageOffset
,
1492 classImages
[classNameImageAndOffset
.imageIndex
].imageNum
);
1494 // Walk each class for this key
1495 const ObjCClassOpt::ClassTarget
& classOffset
= classOffsetsArray
[i
];
1496 if (classOffset
.classData
.isDuplicate
== 0) {
1497 // This class has a single implementation
1498 implCallback(classImages
[classOffset
.classData
.imageIndex
].offsetOfClasses
+ classOffset
.classData
.imageOffset
,
1499 classImages
[classOffset
.classData
.imageIndex
].imageNum
);
1501 // This class has mulitple implementations
1502 uint32_t duplicateCount
= classOffset
.duplicateData
.count
;
1503 uint32_t duplicateStartIndex
= classOffset
.duplicateData
.index
;
1504 for (uint32_t dupeIndex
= 0; dupeIndex
!= duplicateCount
; ++dupeIndex
) {
1505 ObjCClassOpt::ClassTarget
& duplicateClass
= duplicatesArray
[duplicateStartIndex
+ dupeIndex
];
1506 implCallback(classImages
[duplicateClass
.classData
.imageIndex
].offsetOfClasses
+ duplicateClass
.classData
.imageOffset
,
1507 classImages
[duplicateClass
.classData
.imageIndex
].imageNum
);
1513 //////////////////////////// ObjCClassDuplicatesOpt ////////////////////////////////////////
1515 bool ObjCClassDuplicatesOpt::getClassLocation(const char* className
, const objc_opt::objc_opt_t
* objCOpt
, void*& classImpl
) const {
1516 uint32_t potentialTarget
= getPotentialTarget(className
);
1517 if (potentialTarget
== sentinelTarget
)
1520 objc_opt::objc_clsopt_t
* clsOpt
= objCOpt
->clsopt();
1522 Image::ObjCDuplicateClass duplicateClass
;
1523 duplicateClass
.raw
= potentialTarget
;
1525 const char* sharedCacheClassName
= clsOpt
->getClassNameForIndex(duplicateClass
.sharedCacheClassOptIndex
);
1526 if (strcmp(className
, sharedCacheClassName
) != 0)
1529 classImpl
= clsOpt
->getClassForIndex(duplicateClass
.sharedCacheClassOptIndex
, duplicateClass
.sharedCacheClassDuplicateIndex
);
1533 void ObjCClassDuplicatesOpt::forEachClass(void (^callback
)(Image::ObjCDuplicateClass duplicateClass
)) const {
1534 dyld3::Array
<StringTarget
> stringTargets
= targets();
1535 for (unsigned i
= 0; i
!= capacity
; ++i
) {
1536 StringTarget target
= stringTargets
[i
];
1537 if ( target
== sentinelTarget
)
1539 Image::ObjCDuplicateClass duplicateClass
;
1540 duplicateClass
.raw
= (uint32_t)target
;
1541 callback(duplicateClass
);
1546 } // namespace closure
1547 } // namespace dyld3