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@
24 #include <sys/types.h>
26 #include <sys/errno.h>
29 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/sysctl.h>
37 #include <mach-o/dyld_priv.h>
39 #include "ClosureWriter.h"
40 #include "ClosureBuilder.h"
41 #include "MachOAnalyzer.h"
42 #include "libdyldEntryVector.h"
45 #define CLOSURE_SELOPT_WRITE
46 #include "objc-shared-cache.h"
52 const DlopenClosure
* ClosureBuilder::sRetryDlopenClosure
= (const DlopenClosure
*)(-1);
54 ClosureBuilder::ClosureBuilder(uint32_t startImageNum
, const FileSystem
& fileSystem
, const DyldSharedCache
* dyldCache
, bool dyldCacheIsLive
,
55 const GradedArchs
& archs
, const PathOverrides
& pathOverrides
, AtPath atPathHandling
, bool allowRelativePaths
,
56 LaunchErrorInfo
* errorInfo
, Platform platform
, const CacheDylibsBindingHandlers
* handlers
)
57 : _fileSystem(fileSystem
), _dyldCache(dyldCache
), _pathOverrides(pathOverrides
), _archs(archs
), _platform(platform
), _startImageNum(startImageNum
),
58 _handlers(handlers
), _atPathHandling(atPathHandling
), _launchErrorInfo(errorInfo
), _dyldCacheIsLive(dyldCacheIsLive
), _allowRelativePaths(allowRelativePaths
)
60 if ( dyldCache
!= nullptr ) {
61 _dyldImageArray
= dyldCache
->cachedDylibsImageArray();
62 if ( (dyldCache
->header
.otherImageArrayAddr
!= 0) && (dyldCache
->header
.progClosuresSize
== 0) )
63 _makingClosuresInCache
= true;
68 ClosureBuilder::~ClosureBuilder() {
69 if ( _tempPaths
!= nullptr )
70 PathPool::deallocate(_tempPaths
);
71 if ( _mustBeMissingPaths
!= nullptr )
72 PathPool::deallocate(_mustBeMissingPaths
);
73 if ( _objcDuplicateClassWarnings
!= nullptr )
74 PathPool::deallocate(_objcDuplicateClassWarnings
);
77 bool ClosureBuilder::findImage(const char* loadPath
, const LoadedImageChain
& forImageChain
, BuilderLoadedImage
*& foundImage
, LinkageType linkageType
,
78 uint32_t compatVersion
, bool canUseSharedCacheClosure
)
80 // There shouldn't be an error here as the callers should stop trying to find more images if they get an error for an image
81 _diag
.assertNoError();
83 __block
bool result
= false;
85 // record if this is a non-overridable path
86 bool pathIsInDyldCacheWhichCannotBeOverridden
= false;
87 bool dylibsExpectedOnDisk
= true;
88 if ( _dyldCache
!= nullptr ) {
89 pathIsInDyldCacheWhichCannotBeOverridden
= _dyldCache
->hasNonOverridablePath(loadPath
);
90 dylibsExpectedOnDisk
= _dyldCache
->header
.dylibsExpectedOnDisk
;
93 _pathOverrides
.forEachPathVariant(loadPath
, pathIsInDyldCacheWhichCannotBeOverridden
, ^(const char* possibleVariantPath
, bool isFallbackPath
, bool& stopPathVariant
) {
95 // This check is within forEachPathVariant() to let DYLD_LIBRARY_PATH override LC_RPATH
96 bool isRPath
= (strncmp(possibleVariantPath
, "@rpath/", 7) == 0);
98 // passing a leaf name to dlopen() allows rpath searching for it
99 // FIXME: Does this apply to DYLD_INSERT_LIBRARIES too?
100 bool implictRPath
= (linkageType
== LinkageType::kDynamic
) && (loadPath
[0] != '/') && (loadPath
== possibleVariantPath
) && (_atPathHandling
!= AtPath::none
);
103 forEachResolvedPathVar(possibleVariantPath
, forImageChain
, implictRPath
, linkageType
,
104 ^(const char* possiblePath
, bool& stop
) {
105 if ( possibleVariantPath
!= possiblePath
)
108 // look at already loaded images
109 const char* leafName
= strrchr(possiblePath
, '/');
110 for (BuilderLoadedImage
& li
: _loadedImages
) {
111 if ( strcmp(li
.path(), possiblePath
) == 0 ) {
117 else if ( isRPath
) {
118 // Special case @rpath/ because name in li.fileInfo.path is full path.
119 // Getting installName is expensive, so first see if an already loaded image
120 // has same leaf name and if so see if its installName matches request @rpath
121 if (const char* aLeaf
= strrchr(li
.path(), '/')) {
122 if ( strcmp(aLeaf
, leafName
) == 0 ) {
123 if ( li
.loadAddress()->isDylib() && (strcmp(loadPath
, li
.loadAddress()->installName()) == 0) ) {
134 // look to see if image already loaded via a different symlink
135 bool fileFound
= false;
136 uint64_t fileFoundINode
= 0;
137 uint64_t fileFoundMTime
= 0;
138 bool inodesMatchRuntime
= false;
139 // Note, we only do this check if we even expect to find this on-disk
140 // We can also use the pathIsInDyldCacheWhichCannotBeOverridden result if we are still trying the same path
141 // it was computed from
142 if ( dylibsExpectedOnDisk
|| !pathIsInDyldCacheWhichCannotBeOverridden
|| (loadPath
!= possiblePath
) ) {
143 if ( _fileSystem
.fileExists(possiblePath
, &fileFoundINode
, &fileFoundMTime
, nullptr, &inodesMatchRuntime
) ) {
145 for (BuilderLoadedImage
& li
: _loadedImages
) {
146 if ( (li
.loadedFileInfo
.inode
== fileFoundINode
) && (li
.loadedFileInfo
.mtime
== fileFoundMTime
) ) {
156 bool unmapWhenDone
= false;
157 bool contentRebased
= false;
158 bool hasInits
= false;
159 bool markNeverUnload
= false;
160 bool mustBuildClosure
= _dyldCacheInvalidFormatVersion
;
161 ImageNum overrideImageNum
= 0;
162 ImageNum foundImageNum
= 0;
163 const MachOAnalyzer
* mh
= nullptr;
164 const char* filePath
= nullptr;
165 LoadedFileInfo loadedFileInfo
;
167 // look in dyld cache
168 filePath
= possiblePath
;
169 char realPath
[MAXPATHLEN
];
170 if ( _dyldImageArray
!= nullptr ) {
171 uint32_t dyldCacheImageIndex
;
172 bool foundInCache
= _dyldCache
->hasImagePath(possiblePath
, dyldCacheImageIndex
);
173 if ( !foundInCache
&& fileFound
) {
174 // see if this is an OS dylib/bundle with a pre-built dlopen closure
175 // We can only use the pre-built closure if we are dynamic linkage (a dlopen) and
176 // there are no roots
177 if ( canUseSharedCacheClosure
&& (linkageType
== LinkageType::kDynamic
) ) {
178 if (const dyld3::closure::Image
* otherImage
= _dyldCache
->findDlopenOtherImage(possiblePath
) ) {
179 uint64_t expectedInode
;
180 uint64_t expectedModTime
;
181 if ( !otherImage
->isInvalid() ) {
182 bool hasInodeInfo
= otherImage
->hasFileModTimeAndInode(expectedInode
, expectedModTime
);
183 // use pre-built Image if it does not have mtime/inode or it does and it has matches current file info
184 if ( !hasInodeInfo
|| ((expectedInode
== fileFoundINode
) && (expectedModTime
== fileFoundMTime
)) ) {
185 loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, possiblePath
, _archs
, _platform
, realPath
);
186 if ( _diag
.noError() ) {
187 mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
188 foundImageNum
= otherImage
->imageNum();
189 unmapWhenDone
= true;
190 contentRebased
= false;
191 hasInits
= otherImage
->hasInitializers() || otherImage
->mayHavePlusLoads();
192 // Use the realpath in the case where we loaded a symlink
193 // The closure must have recordered an alias path
194 if (realPath
[0] != '\0')
201 // if not found in cache, may be a symlink to something in cache
202 if ( mh
== nullptr ) {
203 if ( _fileSystem
.getRealPath(possiblePath
, realPath
) ) {
204 foundInCache
= _dyldCache
->hasImagePath(realPath
, dyldCacheImageIndex
);
205 if ( foundInCache
) {
208 // handle case where OS dylib was updated after this process launched
209 if ( foundInCache
) {
210 for (BuilderLoadedImage
& li
: _loadedImages
) {
211 if ( strcmp(li
.path(), realPath
) == 0 ) {
225 // if using a cached dylib, look to see if there is an override
226 if ( foundInCache
) {
227 ImageNum dyldCacheImageNum
= dyldCacheImageIndex
+ 1;
228 bool useCache
= true;
229 markNeverUnload
= true; // dylibs in cache, or dylibs that override cache should not be unloaded at runtime
230 const Image
* image
= _dyldImageArray
->imageForNum(dyldCacheImageNum
);
231 if ( image
->overridableDylib() ) {
233 uint64_t expectedInode
;
234 uint64_t expectedModTime
;
235 if ( image
->hasFileModTimeAndInode(expectedInode
, expectedModTime
) ) {
236 // macOS where dylibs remain on disk. only use cache if mtime and inode have not changed
237 useCache
= ( (fileFoundINode
== expectedInode
) && (fileFoundMTime
== expectedModTime
) );
239 else if ( _makingClosuresInCache
) {
240 // during iOS cache build, don't look at files on disk, use ones in cache
244 // iOS internal build. Any disk on cache overrides cache
249 overrideImageNum
= dyldCacheImageNum
;
250 _foundDyldCacheRoots
= true;
254 foundImageNum
= dyldCacheImageNum
;
255 mh
= (MachOAnalyzer
*)_dyldCache
->getIndexedImageEntry(foundImageNum
-1, loadedFileInfo
.mtime
, loadedFileInfo
.inode
);
256 unmapWhenDone
= false;
257 // if we are building ImageArray in dyld cache, content is not rebased
258 contentRebased
= !_makingDyldCacheImages
&& _dyldCacheIsLive
;
259 hasInits
= image
->hasInitializers() || image
->mayHavePlusLoads();
260 // If the cache format is different from dyld/libdyld then we can't use this closure.
261 if ( (_dyldCache
->header
.formatVersion
!= dyld3::closure::kFormatVersion
) || !canUseSharedCacheClosure
) {
262 mustBuildClosure
= true;
263 _foundDyldCacheRoots
= true;
269 // If we are building the cache, and don't find an image, then it might be weak so just return
270 if (_makingDyldCacheImages
) {
271 addMustBeMissingPath(possiblePath
);
275 // if not found yet, mmap file
276 if ( mh
== nullptr ) {
277 loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, filePath
, _archs
, _platform
, realPath
);
278 mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
279 if ( mh
== nullptr ) {
280 // Don't add must be missing paths for dlopen as we don't cache dlopen closures
281 if (_isLaunchClosure
) {
282 // If we found the file then we want to skip it as its not a valid macho for this platform/arch
283 // We can't record skipped file mtime/inode for caches built on a different machine that it runs on.
284 // In that case, we expect the file to be mastered out, as otherwise we couldn't detect if its
285 // changed or not on the device side
286 if (fileFound
&& inodesMatchRuntime
) {
287 addSkippedFile(possiblePath
, fileFoundINode
, fileFoundMTime
);
289 addMustBeMissingPath(possiblePath
);
294 if ( linkageType
!= LinkageType::kDynamic
) {
295 // LC_LOAD_DYLIB can only link with dylibs, and DYLD_INSERT_LIBRARIES can only be dylibs
296 if ( !mh
->isDylib() ) {
297 _diag
.error("found '%s' which is not a dylib. Needed by '%s'", filePath
, forImageChain
.image
.path());
300 // verify this is compatable dylib version
301 const char* installName
;
302 uint32_t foundCompatVers
;
303 uint32_t foundCurrentVers
;
304 mh
->getDylibInstallName(&installName
, &foundCompatVers
, &foundCurrentVers
);
305 if ( (foundCompatVers
< compatVersion
) && mh
->enforceCompatVersion() ) {
307 char requiredStr
[32];
308 MachOFile::packedVersionToString(foundCompatVers
, foundStr
);
309 MachOFile::packedVersionToString(compatVersion
, requiredStr
);
310 _diag
.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'",
311 filePath
, foundStr
, requiredStr
, forImageChain
.image
.path());
315 else if ( mh
->isMainExecutable() ) {
316 // when dlopen()ing a main executable, it must be dynamic Position Independent Executable
317 if ( !mh
->isPIE() || !mh
->isDynamicExecutable() ) {
318 _diag
.error("not PIE");
322 // Use the realpath in the case where we loaded a symlink
323 // The closure must have recordered an alias path
324 if (realPath
[0] != '\0')
326 foundImageNum
= _startImageNum
+ _nextIndex
++;
327 _foundNonCachedImage
= true;
328 mustBuildClosure
= true;
329 unmapWhenDone
= true;
331 loadedFileInfo
.fileContent
= mh
;
334 // if path is not original path, or its an inserted path (as forEachInColonList uses a stack temporary)
335 if ( (filePath
!= loadPath
) || (linkageType
== LinkageType::kInserted
) ) {
336 // possiblePath may be a temporary (stack) string, since we found file at that path, make it permanent
337 filePath
= strdup_temp(filePath
);
338 // check if this overrides what would have been found in cache
339 // This is the case where we didn't find the image with the path in the shared cache, perhaps as it used library paths
340 // but the path we requested had pointed in to the cache
341 // FIXME: What if load path is via an @rpath and we will override the cache?
342 if ( overrideImageNum
== 0 ) {
343 if ( _dyldImageArray
!= nullptr ) {
344 uint32_t dyldCacheImageIndex
;
345 if ( _dyldCache
->hasImagePath(loadPath
, dyldCacheImageIndex
) ) {
346 ImageNum possibleOverrideNum
= dyldCacheImageIndex
+1;
347 if ( possibleOverrideNum
!= foundImageNum
)
348 overrideImageNum
= possibleOverrideNum
;
354 if ( !markNeverUnload
) {
355 switch (linkageType
) {
356 case LinkageType::kStatic
:
357 // Static linkages can only be unloaded if the image loading us can be unloaded
358 markNeverUnload
= forImageChain
.image
.markNeverUnload
;
360 case LinkageType::kDynamic
:
361 markNeverUnload
= false;
363 case LinkageType::kInserted
:
364 // Inserted libraries must never be unloaded
365 markNeverUnload
= true;
370 if ( !markNeverUnload
) {
371 // If the parent didn't force us to be never unload, other conditions still may
372 if ( mh
->hasThreadLocalVariables() ) {
373 markNeverUnload
= true;
374 } else if ( mh
->hasObjC() && mh
->isDylib() ) {
375 markNeverUnload
= true;
377 // record if image has DOF sections
378 __block
bool hasDOFs
= false;
379 mh
->forEachDOFSection(_diag
, ^(uint32_t offset
) {
383 markNeverUnload
= true;
387 // Set the path again just in case it was strdup'ed.
388 loadedFileInfo
.path
= filePath
;
391 BuilderLoadedImage entry
;
392 entry
.loadedFileInfo
= loadedFileInfo
;
393 entry
.imageNum
= foundImageNum
;
394 entry
.unmapWhenDone
= unmapWhenDone
;
395 entry
.contentRebased
= contentRebased
;
396 entry
.hasInits
= hasInits
;
397 entry
.markNeverUnload
= markNeverUnload
;
398 entry
.rtldLocal
= false;
399 entry
.isBadImage
= false;
400 entry
.mustBuildClosure
= mustBuildClosure
;
401 entry
.hasMissingWeakImports
= false;
402 entry
.overrideImageNum
= overrideImageNum
;
403 _loadedImages
.push_back(entry
);
404 foundImage
= &_loadedImages
.back();
405 if ( isFallbackPath
)
406 _fallbackPathUsed
= true;
411 stopPathVariant
= true;
414 // If we found a file, but also had an error, then we must have logged a diagnostic for a file we couldn't use.
415 // Clear that for now.
416 // FIXME: Surface this to the user in case they wanted to see the error
417 if (result
&& _diag
.hasError())
423 bool ClosureBuilder::expandAtLoaderPath(const char* loadPath
, bool fromLCRPATH
, const BuilderLoadedImage
& loadedImage
, char fixedPath
[])
425 switch ( _atPathHandling
) {
428 case AtPath::onlyInRPaths
:
429 if ( !fromLCRPATH
) {
430 // <rdar://42360708> allow @loader_path in LC_LOAD_DYLIB during dlopen()
431 if ( _isLaunchClosure
)
438 if ( strncmp(loadPath
, "@loader_path/", 13) != 0 )
441 strlcpy(fixedPath
, loadedImage
.path(), PATH_MAX
);
442 char* lastSlash
= strrchr(fixedPath
, '/');
443 if ( lastSlash
!= nullptr ) {
444 strcpy(lastSlash
+1, &loadPath
[13]);
450 bool ClosureBuilder::expandAtExecutablePath(const char* loadPath
, bool fromLCRPATH
, char fixedPath
[])
452 switch ( _atPathHandling
) {
455 case AtPath::onlyInRPaths
:
462 if ( strncmp(loadPath
, "@executable_path/", 17) != 0 )
465 if ( _atPathHandling
!= AtPath::all
)
468 strlcpy(fixedPath
, _mainProgLoadPath
, PATH_MAX
);
469 char* lastSlash
= strrchr(fixedPath
, '/');
470 if ( lastSlash
!= nullptr ) {
471 strcpy(lastSlash
+1, &loadPath
[17]);
477 void ClosureBuilder::forEachResolvedPathVar(const char* loadPath
, const LoadedImageChain
& forImageChain
,
478 bool implictRPath
, LinkageType linkageType
,
479 void (^handler
)(const char* possiblePath
, bool& stop
))
481 // don't expand @loader_path or @executable_path if disallowed
482 if ( (_atPathHandling
== AtPath::none
) && (loadPath
[0] == '@') && (loadPath
[1] != 'r') ) {
484 handler(loadPath
, stop
);
488 // quick out if not @ path or not implicit rpath
489 if ( !implictRPath
&& (loadPath
[0] != '@') ) {
491 handler(loadPath
, stop
);
495 // expand @loader_path
496 // Note this isn't supported for DYLD_INSERT_LIBRARIES
497 BLOCK_ACCCESSIBLE_ARRAY(char, tempPath
, PATH_MAX
); // read as: char tempPath[PATH_MAX];
498 if ( (linkageType
!= LinkageType::kInserted
) && expandAtLoaderPath(loadPath
, false, forImageChain
.image
, tempPath
) ) {
500 handler(tempPath
, stop
);
504 // expand @executable_path
505 // Note this is supported for DYLD_INSERT_LIBRARIES
506 if ( expandAtExecutablePath(loadPath
, false, tempPath
) ) {
508 handler(tempPath
, stop
);
513 // Note this isn't supported for DYLD_INSERT_LIBRARIES
514 const char* rpathTail
= nullptr;
515 char implicitRpathBuffer
[PATH_MAX
];
516 if ( linkageType
!= LinkageType::kInserted
) {
517 if ( strncmp(loadPath
, "@rpath/", 7) == 0 ) {
518 // note: rpathTail starts with '/'
519 rpathTail
= &loadPath
[6];
521 else if ( implictRPath
) {
522 // make rpathTail starts with '/'
523 strlcpy(implicitRpathBuffer
, "/", PATH_MAX
);
524 strlcat(implicitRpathBuffer
, loadPath
, PATH_MAX
);
525 rpathTail
= implicitRpathBuffer
;
528 if ( rpathTail
!= nullptr ) {
529 // rpath is expansion is technically a stack of rpath dirs built starting with main executable and pushing
530 // LC_RPATHS from each dylib as they are recursively loaded. Our imageChain represents that stack.
531 __block
bool done
= false;
532 for (const LoadedImageChain
* link
= &forImageChain
; (link
!= nullptr) && !done
; link
= link
->previous
) {
533 link
->image
.loadAddress()->forEachRPath(^(const char* rPath
, bool& stop
) {
534 // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.loadedFileInfo.path);
535 if ( expandAtLoaderPath(rPath
, true, link
->image
, tempPath
) || expandAtExecutablePath(rPath
, true, tempPath
) ) {
536 // @loader_path allowed and expended
537 strlcat(tempPath
, rpathTail
, PATH_MAX
);
538 handler(tempPath
, stop
);
540 else if ( rPath
[0] == '/' ) {
541 // LC_RPATH is an absolute path, not blocked by AtPath::none
542 strlcpy(tempPath
, rPath
, PATH_MAX
);
543 strlcat(tempPath
, rpathTail
, PATH_MAX
);
544 handler(tempPath
, stop
);
549 if ( _fileSystem
.fileExists(tempPath
) ) {
551 result
= strdup_temp(tempPath
);
554 // Don't add must be missing paths for dlopen as we don't cache dlopen closures
555 if (_isLaunchClosure
) {
556 addMustBeMissingPath(tempPath
);
567 handler(loadPath
, stop
);
570 const char* ClosureBuilder::strdup_temp(const char* path
)
572 if ( _tempPaths
== nullptr )
573 _tempPaths
= PathPool::allocate();
574 return _tempPaths
->add(path
);
577 void ClosureBuilder::addMustBeMissingPath(const char* path
)
579 //fprintf(stderr, "must be missing: %s\n", path);
580 if ( _mustBeMissingPaths
== nullptr )
581 _mustBeMissingPaths
= PathPool::allocate();
582 _mustBeMissingPaths
->add(path
);
585 void ClosureBuilder::addSkippedFile(const char* path
, uint64_t inode
, uint64_t mtime
)
587 _skippedFiles
.push_back({ strdup_temp(path
), inode
, mtime
});
590 ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(ImageNum imageNum
)
592 for (BuilderLoadedImage
& li
: _loadedImages
) {
593 if ( li
.imageNum
== imageNum
) {
597 for (BuilderLoadedImage
& li
: _loadedImages
) {
598 if ( li
.overrideImageNum
== imageNum
) {
602 assert(0 && "LoadedImage not found");
605 ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(const MachOAnalyzer
* mh
)
607 for (BuilderLoadedImage
& li
: _loadedImages
) {
608 if ( li
.loadAddress() == mh
) {
612 assert(0 && "LoadedImage not found");
615 const MachOAnalyzer
* ClosureBuilder::machOForImageNum(ImageNum imageNum
)
617 return findLoadedImage(imageNum
).loadAddress();
620 const MachOAnalyzer
* ClosureBuilder::findDependent(const MachOLoaded
* mh
, uint32_t depIndex
)
622 for (const BuilderLoadedImage
& li
: _loadedImages
) {
623 if ( li
.loadAddress() == mh
) {
625 // Bad image duting building group 1 closures, so the dependents array
626 // is potentially incomplete.
629 ImageNum childNum
= li
.dependents
[depIndex
].imageNum();
630 // This is typically something like a missing weak-dylib we are re-exporting a weak-import symbol from
631 if (childNum
== kMissingWeakLinkedImage
)
633 return machOForImageNum(childNum
);
639 ImageNum
ClosureBuilder::imageNumForMachO(const MachOAnalyzer
* mh
)
641 for (const BuilderLoadedImage
& li
: _loadedImages
) {
642 if ( li
.loadAddress() == mh
) {
646 assert(0 && "unknown mach-o");
650 void ClosureBuilder::recursiveLoadDependents(LoadedImageChain
& forImageChain
, bool canUseSharedCacheClosure
)
652 // if dependents is set, then we have already loaded this
653 if ( forImageChain
.image
.dependents
.begin() != nullptr )
656 uintptr_t startDepIndex
= _dependencies
.count();
658 __block
uint32_t depIndex
= 0;
659 forImageChain
.image
.loadAddress()->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
660 Image::LinkKind kind
= Image::LinkKind::regular
;
662 kind
= Image::LinkKind::weak
;
663 else if ( isReExport
)
664 kind
= Image::LinkKind::reExport
;
666 kind
= Image::LinkKind::upward
;
667 BuilderLoadedImage
* foundImage
;
668 if ( findImage(loadPath
, forImageChain
, foundImage
, LinkageType::kStatic
, compatVersion
, canUseSharedCacheClosure
) ) {
669 ImageNum foundImageNum
= foundImage
->imageNum
;
670 if ( _diag
.noError() )
671 _dependencies
.push_back(Image::LinkedImage(kind
, foundImageNum
));
674 _dependencies
.push_back(Image::LinkedImage(Image::LinkKind::weak
, kMissingWeakLinkedImage
));
675 // <rdar://problem/54387345> don't let an error loading weak dylib cause everything to fail
676 // _diag is checked after each dependent load, so if there is an error it was with loading the current dylib.
677 // Since it is a weak load, it is ok to ignore and and go on.
681 BLOCK_ACCCESSIBLE_ARRAY(char, extra
, 4096);
683 const char* targetLeaf
= strrchr(loadPath
, '/');
684 if ( targetLeaf
== nullptr )
685 targetLeaf
= loadPath
;
686 if ( _mustBeMissingPaths
!= nullptr ) {
687 strcpy(extra
, ", tried but didn't find: ");
688 _mustBeMissingPaths
->forEachPath(^(const char* aPath
) {
689 const char* aLeaf
= strrchr(aPath
, '/');
690 if ( aLeaf
== nullptr )
692 if ( strcmp(targetLeaf
, aLeaf
) == 0 ) {
693 strlcat(extra
, "'", 4096);
694 strlcat(extra
, aPath
, 4096);
695 strlcat(extra
, "' ", 4096);
699 if ( !_skippedFiles
.empty() ) {
700 strcpy(extra
, ", tried but invalid: ");
701 for (const SkippedFile
& skippedFile
: _skippedFiles
) {
702 const char* aPath
= skippedFile
.path
;
703 const char* aLeaf
= strrchr(aPath
, '/');
704 if ( aLeaf
== nullptr )
706 if ( strcmp(targetLeaf
, aLeaf
) == 0 ) {
707 strlcat(extra
, "'", 4096);
708 strlcat(extra
, aPath
, 4096);
709 strlcat(extra
, "' ", 4096);
713 if ( _diag
.hasError() ) {
714 #if BUILDING_CACHE_BUILDER
715 std::string errorMessageBuffer
= _diag
.errorMessage();
716 const char* msg
= errorMessageBuffer
.c_str();
718 const char* msg
= _diag
.errorMessage();
720 char msgCopy
[strlen(msg
)+4];
721 strcpy(msgCopy
, msg
);
722 _diag
.error("dependent dylib '%s' not found for '%s'. %s", loadPath
, forImageChain
.image
.path(), msgCopy
);
725 _diag
.error("dependent dylib '%s' not found for '%s'%s", loadPath
, forImageChain
.image
.path(), extra
);
727 if ( _launchErrorInfo
!= nullptr ) {
728 _launchErrorInfo
->kind
= DYLD_EXIT_REASON_DYLIB_MISSING
;
729 _launchErrorInfo
->clientOfDylibPath
= strdup_temp(forImageChain
.image
.path());
730 _launchErrorInfo
->targetDylibPath
= strdup_temp(loadPath
);
731 _launchErrorInfo
->symbol
= nullptr;
735 if ( _diag
.hasError() )
738 if ( _diag
.hasError() )
740 forImageChain
.image
.dependents
= _dependencies
.subArray(startDepIndex
, depIndex
);
742 // breadth first recurse
743 for (Image::LinkedImage dep
: forImageChain
.image
.dependents
) {
744 // don't recurse upwards
745 if ( dep
.kind() == Image::LinkKind::upward
)
747 // don't recurse down missing weak links
748 if ( (dep
.kind() == Image::LinkKind::weak
) && (dep
.imageNum() == kMissingWeakLinkedImage
) )
750 BuilderLoadedImage
& depLoadedImage
= findLoadedImage(dep
.imageNum());
751 LoadedImageChain chain
= { &forImageChain
, depLoadedImage
};
752 recursiveLoadDependents(chain
, canUseSharedCacheClosure
);
753 if ( _diag
.hasError() )
758 void ClosureBuilder::loadDanglingUpwardLinks(bool canUseSharedCacheClosure
)
762 danglingFixed
= false;
763 for (BuilderLoadedImage
& li
: _loadedImages
) {
764 if ( li
.dependents
.begin() == nullptr ) {
765 // this image has not have dependents set (probably a dangling upward link or referenced by upward link)
766 LoadedImageChain chain
= { nullptr, li
};
767 recursiveLoadDependents(chain
, canUseSharedCacheClosure
);
768 danglingFixed
= true;
772 } while (danglingFixed
&& _diag
.noError());
775 bool ClosureBuilder::overridableDylib(const BuilderLoadedImage
& forImage
)
777 // only set on dylibs in the dyld shared cache
778 if ( !_makingDyldCacheImages
)
781 // on macOS dylibs always override cache
782 if ( _platform
== Platform::macOS
)
785 // on embedded platforms with Internal cache, allow overrides
786 if ( !_makingCustomerCache
)
789 // embedded platform customer caches, no overrides
790 return false; // FIXME, allow libdispatch.dylib to be overridden
793 void ClosureBuilder::buildImage(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
795 const MachOAnalyzer
* macho
= forImage
.loadAddress();
797 writer
.setImageNum(forImage
.imageNum
);
800 writer
.setHasWeakDefs(macho
->hasWeakDefs());
801 writer
.setIsBundle(macho
->isBundle());
802 writer
.setIsDylib(macho
->isDylib());
803 writer
.setIs64(macho
->is64());
804 writer
.setIsExecutable(macho
->isMainExecutable());
805 writer
.setUses16KPages(macho
->uses16KPages());
806 writer
.setOverridableDylib(overridableDylib(forImage
));
807 writer
.setInDyldCache(macho
->inDyldCache());
808 if ( macho
->hasObjC() ) {
809 writer
.setHasObjC(true);
810 bool hasPlusLoads
= macho
->hasPlusLoadMethod(_diag
);
811 writer
.setHasPlusLoads(hasPlusLoads
);
813 forImage
.hasInits
= true;
816 writer
.setHasObjC(false);
817 writer
.setHasPlusLoads(false);
820 if ( forImage
.markNeverUnload
) {
821 writer
.setNeverUnload(true);
824 #if BUILDING_DYLD || BUILDING_LIBDYLD
825 if ( _foundDyldCacheRoots
) {
826 // If we had roots, then some images are potentially on-disk while others are
827 // being rebuilt for a new initializer order, but do not exist on disk
828 if ( macho
->inDyldCache() && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
829 // don't add file info for shared cache files mastered out of final file system
832 // file is either not in cache or is in cache but not mastered out
833 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
836 // shared cache not built by dyld or libdyld.dylib, so must be real file
837 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
840 if ( _platform
== Platform::macOS
|| MachOFile::isSimulatorPlatform(_platform
) ) {
841 if ( macho
->inDyldCache() && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
842 // don't add file info for shared cache files mastered out of final file system
845 // file is either not in cache or is in cache but not mastered out
846 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
850 // all other platforms, cache is built off-device, so inodes are not known
854 // add info on how to load image
855 if ( !macho
->inDyldCache() ) {
856 writer
.setMappingInfo(forImage
.loadedFileInfo
.sliceOffset
, macho
->mappedSize());
857 // add code signature, if signed
858 uint32_t codeSigFileOffset
;
859 uint32_t codeSigSize
;
860 if ( macho
->hasCodeSignature(codeSigFileOffset
, codeSigSize
) ) {
861 writer
.setCodeSignatureLocation(codeSigFileOffset
, codeSigSize
);
862 macho
->forEachCDHash(^(const uint8_t *cdHash
) {
863 writer
.addCDHash(cdHash
);
866 // add FairPlay encryption range if encrypted
867 uint32_t fairPlayFileOffset
;
868 uint32_t fairPlaySize
;
869 if ( macho
->isFairPlayEncrypted(fairPlayFileOffset
, fairPlaySize
) ) {
870 writer
.setFairPlayEncryptionRange(fairPlayFileOffset
, fairPlaySize
);
875 writer
.addPath(forImage
.path());
876 if ( _aliases
!= nullptr ) {
877 for (const CachedDylibAlias
& alias
: *_aliases
) {
878 if ( strcmp(alias
.realPath
, forImage
.path()) == 0 )
879 writer
.addPath(alias
.aliasPath
);
883 // set uuid, if has one
885 if ( macho
->getUuid(uuid
) )
886 writer
.setUUID(uuid
);
889 writer
.setDependents(forImage
.dependents
);
892 addSegments(writer
, macho
);
894 // record if this dylib overrides something in the cache
895 if ( forImage
.overrideImageNum
!= 0 ) {
896 writer
.setAsOverrideOf(forImage
.overrideImageNum
);
897 const char* overridePath
= _dyldImageArray
->imageForNum(forImage
.overrideImageNum
)->path();
898 writer
.addPath(overridePath
);
899 if ( strcmp(overridePath
, "/usr/lib/system/libdyld.dylib") == 0 )
900 _libDyldImageNum
= forImage
.imageNum
;
901 else if ( strcmp(overridePath
, "/usr/lib/libSystem.B.dylib") == 0 )
902 _libSystemImageNum
= forImage
.imageNum
;
905 // do fix up info for non-cached, and cached if building cache
906 if ( !macho
->inDyldCache() || _makingDyldCacheImages
) {
907 if ( macho
->hasChainedFixups() ) {
908 addChainedFixupInfo(writer
, forImage
);
911 if ( _handlers
!= nullptr ) {
912 reportRebasesAndBinds(writer
, forImage
);
915 // Note we have to do binds before rebases so that we know if we have missing lazy binds
916 addBindInfo(writer
, forImage
);
917 if ( _diag
.noError() )
918 addRebaseInfo(writer
, macho
);
922 if ( _diag
.hasError() ) {
927 // Don't build iOSMac for now. Just add an invalid placeholder
928 if ( _makingDyldCacheImages
&& strncmp(forImage
.path(), "/System/iOSSupport/", 19) == 0 ) {
934 bool contentRebased
= forImage
.contentRebased
;
935 __block
unsigned initCount
= 0;
936 Diagnostics initializerDiag
;
937 macho
->forEachInitializer(initializerDiag
, contentRebased
, ^(uint32_t offset
) {
940 if ( initializerDiag
.noError() ) {
941 if ( initCount
!= 0 ) {
942 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets
, initCount
);
943 __block
unsigned index
= 0;
944 macho
->forEachInitializer(_diag
, contentRebased
, ^(uint32_t offset
) {
945 initOffsets
[index
++] = offset
;
947 writer
.setInitOffsets(initOffsets
, initCount
);
948 forImage
.hasInits
= true;
952 // mod_init_func section is malformed, might be self modifying pointers
953 macho
->forEachInitializerPointerSection(_diag
, ^(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
) {
954 writer
.setInitSectRange(sectionOffset
, sectionSize
);
955 forImage
.hasInits
= true;
960 // add terminators (except for dylibs in the cache because they are never unloaded)
961 if ( !macho
->inDyldCache() ) {
962 __block
unsigned termCount
= 0;
963 macho
->forEachTerminator(_diag
, contentRebased
, ^(uint32_t offset
) {
966 if ( termCount
!= 0 ) {
967 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, termOffsets
, termCount
);
968 __block
unsigned index
= 0;
969 macho
->forEachTerminator(_diag
, contentRebased
, ^(uint32_t offset
) {
970 termOffsets
[index
++] = offset
;
972 writer
.setTermOffsets(termOffsets
, termCount
);
976 // record if image has DOF sections
977 STACK_ALLOC_ARRAY(uint32_t, dofSectionOffsets
, 256);
978 macho
->forEachDOFSection(_diag
, ^(uint32_t offset
) {
979 dofSectionOffsets
.push_back(offset
);
981 if ( !dofSectionOffsets
.empty() ) {
982 writer
.setDofOffsets(dofSectionOffsets
);
987 void ClosureBuilder::addSegments(ImageWriter
& writer
, const MachOAnalyzer
* mh
)
989 const uint32_t segCount
= mh
->segmentCount();
990 if ( mh
->inDyldCache() ) {
991 uint64_t cacheUnslideBaseAddress
= _dyldCache
->unslidLoadAddress();
992 BLOCK_ACCCESSIBLE_ARRAY(Image::DyldCacheSegment
, segs
, segCount
);
993 mh
->forEachSegment(^(const MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
994 segs
[info
.segIndex
] = { (uint32_t)(info
.vmAddr
-cacheUnslideBaseAddress
), (uint32_t)info
.vmSize
, info
.protections
};
996 writer
.setCachedSegments(segs
, segCount
);
999 const uint32_t pageSize
= (mh
->uses16KPages() ? 0x4000 : 0x1000);
1000 __block
uint32_t diskSegIndex
= 0;
1001 __block
uint32_t totalPageCount
= 0;
1002 __block
uint32_t lastFileOffsetEnd
= 0;
1003 __block
uint64_t lastVmAddrEnd
= 0;
1004 BLOCK_ACCCESSIBLE_ARRAY(Image::DiskSegment
, dsegs
, segCount
*3); // room for padding
1005 mh
->forEachSegment(^(const MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
1006 if ( (info
.fileOffset
!= 0) && (info
.fileOffset
!= lastFileOffsetEnd
) ) {
1007 Image::DiskSegment filePadding
;
1008 filePadding
.filePageCount
= (info
.fileOffset
- lastFileOffsetEnd
)/pageSize
;
1009 filePadding
.vmPageCount
= 0;
1010 filePadding
.permissions
= 0;
1011 filePadding
.paddingNotSeg
= 1;
1012 dsegs
[diskSegIndex
++] = filePadding
;
1014 if ( (lastVmAddrEnd
!= 0) && (info
.vmAddr
!= lastVmAddrEnd
) ) {
1015 Image::DiskSegment vmPadding
;
1016 vmPadding
.filePageCount
= 0;
1017 vmPadding
.vmPageCount
= (info
.vmAddr
- lastVmAddrEnd
)/pageSize
;
1018 vmPadding
.permissions
= 0;
1019 vmPadding
.paddingNotSeg
= 1;
1020 dsegs
[diskSegIndex
++] = vmPadding
;
1021 totalPageCount
+= vmPadding
.vmPageCount
;
1024 Image::DiskSegment segInfo
;
1025 segInfo
.filePageCount
= (info
.fileSize
+pageSize
-1)/pageSize
;
1026 segInfo
.vmPageCount
= (info
.vmSize
+pageSize
-1)/pageSize
;
1027 segInfo
.permissions
= info
.protections
& 7;
1028 segInfo
.paddingNotSeg
= 0;
1029 if ( info
.readOnlyData
)
1030 segInfo
.permissions
= Image::DiskSegment::kReadOnlyDataPermissions
;
1031 dsegs
[diskSegIndex
++] = segInfo
;
1032 totalPageCount
+= segInfo
.vmPageCount
;
1033 if ( info
.fileSize
!= 0 )
1034 lastFileOffsetEnd
= (uint32_t)(info
.fileOffset
+ info
.fileSize
);
1035 if ( info
.vmSize
!= 0 )
1036 lastVmAddrEnd
= info
.vmAddr
+ info
.vmSize
;
1039 writer
.setDiskSegments(dsegs
, diskSegIndex
);
1043 static bool isTupleFixup(uint64_t tupleSectVmStartOffset
, uint64_t tupleSectVmEndOffset
, uint64_t imageOffsetOfFixup
, uint32_t entrySize
, uint32_t& tupleIndex
)
1045 if ( imageOffsetOfFixup
< tupleSectVmStartOffset
)
1047 if ( imageOffsetOfFixup
> tupleSectVmEndOffset
)
1049 uint64_t offsetIntoSection
= imageOffsetOfFixup
- tupleSectVmStartOffset
;
1050 tupleIndex
= (uint32_t)(offsetIntoSection
/entrySize
);
1051 return (tupleIndex
*entrySize
== offsetIntoSection
) || ((tupleIndex
*entrySize
+entrySize
/2) == offsetIntoSection
);
1054 void ClosureBuilder::addInterposingTuples(LaunchClosureWriter
& writer
, const Image
* image
, const MachOAnalyzer
* mh
)
1056 const unsigned pointerSize
= mh
->pointerSize();
1057 const uint64_t baseAddress
= mh
->preferredLoadAddress();
1058 mh
->forEachInterposingSection(_diag
, ^(uint64_t sectVmOffset
, uint64_t sectVmSize
, bool &stop
) {
1059 const uint32_t entrySize
= 2*pointerSize
;
1060 const uint32_t tupleCount
= (uint32_t)(sectVmSize
/entrySize
);
1061 const uint64_t sectVmEndOffset
= sectVmOffset
+ sectVmSize
;
1062 BLOCK_ACCCESSIBLE_ARRAY(InterposingTuple
, resolvedTuples
, tupleCount
);
1063 for (uint32_t i
=0; i
< tupleCount
; ++i
) {
1064 resolvedTuples
[i
].stockImplementation
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1065 resolvedTuples
[i
].stockImplementation
.absolute
.value
= 0;
1066 resolvedTuples
[i
].newImplementation
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1067 resolvedTuples
[i
].newImplementation
.absolute
.value
= 0;
1069 // figure out what the replacement (rebase) and replacement (bind) of the tuple point to
1070 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool& rebaseStop
) {
1071 uint32_t tupleIndex
;
1072 if ( isTupleFixup(sectVmOffset
, sectVmEndOffset
, imageOffsetToRebase
, entrySize
, tupleIndex
) ) {
1073 const void* content
= (uint8_t*)mh
+ imageOffsetToRebase
;
1074 uint64_t unslidTargetAddress
= mh
->is64() ? *(uint64_t*)content
: *(uint32_t*)content
;
1075 resolvedTuples
[tupleIndex
].newImplementation
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1076 resolvedTuples
[tupleIndex
].newImplementation
.image
.imageNum
= image
->imageNum();
1077 resolvedTuples
[tupleIndex
].newImplementation
.image
.offset
= unslidTargetAddress
- mh
->preferredLoadAddress();
1080 ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &bindStop
) {
1081 uint32_t tupleIndex
;
1082 if ( isTupleFixup(sectVmOffset
, sectVmEndOffset
, imageOffsetToBind
, entrySize
, tupleIndex
) ) {
1083 resolvedTuples
[tupleIndex
].stockImplementation
= bindTarget
;
1086 ^(uint64_t imageOffsetToStartsInfo
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& chainStop
) {
1087 mh
->withChainStarts(_diag
, imageOffsetToStartsInfo
, ^(const dyld_chained_starts_in_image
* startsInfo
) {
1088 mh
->forEachFixupInAllChains(_diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
1089 uint64_t fixupOffset
= (uint8_t*)fixupLoc
- (uint8_t*)mh
;
1090 uint32_t tupleIndex
;
1091 if ( !isTupleFixup(sectVmOffset
, sectVmEndOffset
, fixupOffset
, entrySize
, tupleIndex
) )
1093 uint32_t bindOrdinal
;
1094 uint64_t rebaseTargetOffset
;
1095 if ( fixupLoc
->isBind(segInfo
->pointer_format
, bindOrdinal
) ) {
1096 if ( bindOrdinal
< targets
.count() ) {
1097 resolvedTuples
[tupleIndex
].stockImplementation
= targets
[bindOrdinal
];
1100 _diag
.error("out of range bind ordinal %d (max %lu)", bindOrdinal
, targets
.count());
1104 else if ( fixupLoc
->isRebase(segInfo
->pointer_format
, baseAddress
, rebaseTargetOffset
) ) {
1105 resolvedTuples
[tupleIndex
].newImplementation
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1106 resolvedTuples
[tupleIndex
].newImplementation
.image
.imageNum
= image
->imageNum();
1107 resolvedTuples
[tupleIndex
].newImplementation
.image
.offset
= rebaseTargetOffset
;
1112 ^(uint64_t imageOffsetToFixup
) {
1113 // objc optimisation can't be interposed so nothing to do here.
1115 ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &bindStop
) {
1116 // objc protocol optimisation fixups can't be interposed so nothing to do here.
1118 ^(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool &fixupStop
) {
1119 // objc selector optimisation fixups can't be interposed so nothing to do here.
1121 ^(uint64_t imageOffsetToFixup
, bool &fixupStop
) {
1122 // objc stable Swift optimisation fixups can't be interposed so nothing to do here.
1124 ^(uint64_t imageOffsetToFixup
, bool &fixupStop
) {
1125 // objc method list optimisation fixups can't be interposed so nothing to do here.
1128 // remove any tuples in which both sides are not set (or target is weak-import NULL)
1129 STACK_ALLOC_ARRAY(InterposingTuple
, goodTuples
, tupleCount
);
1130 for (uint32_t i
=0; i
< tupleCount
; ++i
) {
1131 if ( (resolvedTuples
[i
].stockImplementation
.image
.kind
!= Image::ResolvedSymbolTarget::kindAbsolute
)
1132 && (resolvedTuples
[i
].newImplementation
.image
.kind
!= Image::ResolvedSymbolTarget::kindAbsolute
) )
1133 goodTuples
.push_back(resolvedTuples
[i
]);
1135 writer
.addInterposingTuples(goodTuples
);
1137 // if the target of the interposing is in the dyld shared cache, add a PatchEntry so the cache is fixed up at launch
1138 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, goodTuples
.count());
1139 for (const InterposingTuple
& aTuple
: goodTuples
) {
1140 if ( aTuple
.stockImplementation
.sharedCache
.kind
== Image::ResolvedSymbolTarget::kindSharedCache
) {
1141 uint32_t imageIndex
;
1142 assert(_dyldCache
->addressInText((uint32_t)aTuple
.stockImplementation
.sharedCache
.offset
, &imageIndex
));
1143 ImageNum imageInCache
= imageIndex
+1;
1144 Closure::PatchEntry patch
;
1145 patch
.exportCacheOffset
= (uint32_t)aTuple
.stockImplementation
.sharedCache
.offset
;
1146 patch
.overriddenDylibInCache
= imageInCache
;
1147 patch
.replacement
= aTuple
.newImplementation
;
1148 patches
.push_back(patch
);
1151 writer
.addCachePatches(patches
);
1155 void ClosureBuilder::addRebaseInfo(ImageWriter
& writer
, const MachOAnalyzer
* mh
)
1157 const uint64_t ptrSize
= mh
->pointerSize();
1158 Image::RebasePattern maxLeapPattern
= { 0xFFFFF, 0, 0xF };
1159 const uint64_t maxLeapCount
= maxLeapPattern
.repeatCount
* maxLeapPattern
.skipCount
;
1160 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern
, rebaseEntries
, 1024);
1161 __block
uint64_t lastLocation
= -ptrSize
;
1162 mh
->forEachRebase(_diag
, !_foundMissingLazyBinds
, ^(uint64_t runtimeOffset
, bool& stop
) {
1163 const uint64_t delta
= runtimeOffset
- lastLocation
;
1164 const bool aligned
= ((delta
% ptrSize
) == 0);
1165 if ( delta
== ptrSize
) {
1166 // this rebase location is contiguous to previous
1167 if ( rebaseEntries
.back().contigCount
< 255 ) {
1168 // just bump previous's contigCount
1169 rebaseEntries
.back().contigCount
++;
1172 // previous contiguous run already has max 255, so start a new run
1173 rebaseEntries
.push_back({ 1, 1, 0 });
1176 else if ( aligned
&& (delta
<= (ptrSize
*15)) ) {
1177 // this rebase is within skip distance of last rebase
1178 rebaseEntries
.back().skipCount
= (uint8_t)((delta
-ptrSize
)/ptrSize
);
1179 int lastIndex
= (int)(rebaseEntries
.count() - 1);
1180 if ( lastIndex
> 1 ) {
1181 if ( (rebaseEntries
[lastIndex
].contigCount
== rebaseEntries
[lastIndex
-1].contigCount
)
1182 && (rebaseEntries
[lastIndex
].skipCount
== rebaseEntries
[lastIndex
-1].skipCount
) ) {
1183 // this entry as same contig and skip as prev, so remove it and bump repeat count of previous
1184 rebaseEntries
.pop_back();
1185 rebaseEntries
.back().repeatCount
+= 1;
1188 rebaseEntries
.push_back({ 1, 1, 0 });
1191 uint64_t advanceCount
= (delta
-ptrSize
);
1192 if ( (runtimeOffset
< lastLocation
) && (lastLocation
!= -ptrSize
) ) {
1193 // out of rebases! handle this be resting rebase offset to zero
1194 rebaseEntries
.push_back({ 0, 0, 0 });
1195 advanceCount
= runtimeOffset
;
1197 // if next rebase is too far to reach with one pattern, use series
1198 while ( advanceCount
> maxLeapCount
) {
1199 rebaseEntries
.push_back(maxLeapPattern
);
1200 advanceCount
-= maxLeapCount
;
1202 // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate
1203 while ( advanceCount
> maxLeapPattern
.repeatCount
) {
1204 uint64_t count
= advanceCount
/ maxLeapPattern
.skipCount
;
1205 rebaseEntries
.push_back({ (uint32_t)count
, 0, maxLeapPattern
.skipCount
});
1206 advanceCount
-= (count
*maxLeapPattern
.skipCount
);
1208 if ( advanceCount
!= 0 )
1209 rebaseEntries
.push_back({ (uint32_t)advanceCount
, 0, 1 });
1210 rebaseEntries
.push_back({ 1, 1, 0 });
1212 lastLocation
= runtimeOffset
;
1214 writer
.setRebaseInfo(rebaseEntries
);
1216 // i386 programs also use text relocs to rebase stubs
1217 if ( mh
->cputype
== CPU_TYPE_I386
) {
1218 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern
, textRebases
, 512);
1219 __block
uint64_t lastOffset
= -4;
1220 mh
->forEachTextRebase(_diag
, ^(uint64_t runtimeOffset
, bool& stop
) {
1221 if ( textRebases
.freeCount() < 2 ) {
1222 _diag
.error("too many text rebase locations (%ld) in %s", textRebases
.maxCount(), writer
.currentImage()->path());
1225 bool mergedIntoPrevious
= false;
1226 if ( (runtimeOffset
> lastOffset
) && !textRebases
.empty() ) {
1227 uint32_t skipAmount
= (uint32_t)(runtimeOffset
- lastOffset
);
1228 if ( (textRebases
.back().repeatCount
== 1) && (textRebases
.back().skipCount
== 0) ) {
1229 textRebases
.back().repeatCount
= 2;
1230 textRebases
.back().skipCount
= skipAmount
;
1231 mergedIntoPrevious
= true;
1233 else if ( textRebases
.back().skipCount
== skipAmount
) {
1234 textRebases
.back().repeatCount
+= 1;
1235 mergedIntoPrevious
= true;
1238 if ( !mergedIntoPrevious
) {
1239 Image::TextFixupPattern pattern
;
1240 pattern
.target
.raw
= 0;
1241 pattern
.startVmOffset
= (uint32_t)runtimeOffset
;
1242 pattern
.repeatCount
= 1;
1243 pattern
.skipCount
= 0;
1244 textRebases
.push_back(pattern
);
1246 lastOffset
= runtimeOffset
;
1248 writer
.setTextRebaseInfo(textRebases
);
1253 void ClosureBuilder::forEachBind(BuilderLoadedImage
& forImage
, void (^handler
)(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
),
1254 void (^strongHandler
)(const char* strongSymbolName
),
1255 void (^missingLazyBindHandler
)())
1257 __block
int lastLibOrdinal
= 256;
1258 __block
const char* lastSymbolName
= nullptr;
1259 __block
uint64_t lastAddend
= 0;
1260 __block
Image::ResolvedSymbolTarget target
;
1261 __block ResolvedTargetInfo targetInfo
;
1262 forImage
.loadAddress()->forEachBind(_diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
1263 if ( (symbolName
== lastSymbolName
) && (libOrdinal
== lastLibOrdinal
) && (addend
== lastAddend
) ) {
1264 // same symbol lookup as last location
1265 handler(runtimeOffset
, target
, targetInfo
, stop
);
1267 else if ( findSymbol(forImage
, libOrdinal
, symbolName
, weakImport
, lazyBind
, addend
, target
, targetInfo
) ) {
1268 if ( !targetInfo
.skippableWeakDef
) {
1269 handler(runtimeOffset
, target
, targetInfo
, stop
);
1270 lastSymbolName
= symbolName
;
1271 lastLibOrdinal
= libOrdinal
;
1272 lastAddend
= addend
;
1278 }, ^(const char* symbolName
) {
1279 strongHandler(symbolName
);
1281 missingLazyBindHandler();
1285 void ClosureBuilder::addBindInfo(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1287 const uint32_t ptrSize
= forImage
.loadAddress()->pointerSize();
1288 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern
, binds
, 512);
1289 __block
uint64_t lastOffset
= -ptrSize
;
1290 __block
Image::ResolvedSymbolTarget lastTarget
= { {0, 0} };
1291 forEachBind(forImage
, ^(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
) {
1292 if ( targetInfo
.weakBindCoalese
) {
1293 // may be previous bind to this location
1294 // if so, update that rather create new BindPattern
1295 for (Image::BindPattern
& aBind
: binds
) {
1296 if ( (aBind
.startVmOffset
== runtimeOffset
) && (aBind
.repeatCount
== 1) && (aBind
.skipCount
== 0) ) {
1297 aBind
.target
= target
;
1302 bool mergedIntoPrevious
= false;
1303 if ( !mergedIntoPrevious
&& (target
== lastTarget
) && (runtimeOffset
> lastOffset
) && !binds
.empty() ) {
1304 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- ptrSize
)/ptrSize
;
1305 if ( skipAmount
*ptrSize
!= (runtimeOffset
- lastOffset
- ptrSize
) ) {
1306 // misaligned pointer means we cannot optimize
1309 if ( (binds
.back().repeatCount
== 1) && (binds
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
1310 binds
.back().repeatCount
= 2;
1311 binds
.back().skipCount
= skipAmount
;
1312 assert(binds
.back().skipCount
== skipAmount
); // check overflow
1313 mergedIntoPrevious
= true;
1315 else if ( (binds
.back().skipCount
== skipAmount
) && (binds
.back().repeatCount
< 0xfff) ) {
1316 uint32_t prevRepeatCount
= binds
.back().repeatCount
;
1317 binds
.back().repeatCount
+= 1;
1318 assert(binds
.back().repeatCount
> prevRepeatCount
); // check overflow
1319 mergedIntoPrevious
= true;
1323 if ( (target
== lastTarget
) && (runtimeOffset
== lastOffset
) && !binds
.empty() ) {
1324 // duplicate bind for same location, ignore this one
1325 mergedIntoPrevious
= true;
1327 if ( !mergedIntoPrevious
) {
1328 Image::BindPattern pattern
;
1329 pattern
.target
= target
;
1330 pattern
.startVmOffset
= runtimeOffset
;
1331 pattern
.repeatCount
= 1;
1332 pattern
.skipCount
= 0;
1333 assert(pattern
.startVmOffset
== runtimeOffset
);
1334 binds
.push_back(pattern
);
1336 lastTarget
= target
;
1337 lastOffset
= runtimeOffset
;
1338 }, ^(const char* strongSymbolName
) {
1339 if ( !_makingDyldCacheImages
) {
1340 // something has a strong symbol definition that may override a weak impl in the dyld cache
1341 Image::ResolvedSymbolTarget strongOverride
;
1342 ResolvedTargetInfo strongTargetInfo
;
1343 if ( findSymbolInImage(forImage
.loadAddress(), strongSymbolName
, 0, false, false, strongOverride
, strongTargetInfo
) ) {
1344 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1345 if ( li
.loadAddress()->inDyldCache() && li
.loadAddress()->hasWeakDefs() ) {
1346 Image::ResolvedSymbolTarget implInCache
;
1347 ResolvedTargetInfo implInCacheInfo
;
1348 if ( findSymbolInImage(li
.loadAddress(), strongSymbolName
, 0, false, false, implInCache
, implInCacheInfo
) ) {
1349 // found another instance in some dylib in dyld cache, will need to patch it
1350 Closure::PatchEntry patch
;
1351 patch
.exportCacheOffset
= (uint32_t)implInCache
.sharedCache
.offset
;
1352 patch
.overriddenDylibInCache
= li
.imageNum
;
1353 patch
.replacement
= strongOverride
;
1354 _weakDefCacheOverrides
.push_back(patch
);
1361 _foundMissingLazyBinds
= true;
1364 // check for __dyld section in main executable to support licenseware
1365 if ( forImage
.loadAddress()->filetype
== MH_EXECUTE
) {
1366 forImage
.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
1367 if ( (strcmp(sectInfo
.sectName
, "__dyld") == 0) && (strcmp(sectInfo
.segInfo
.segName
, "__DATA") == 0) ) {
1368 // find dyld3::compatFuncLookup in libdyld.dylib
1369 assert(_libDyldImageNum
!= 0);
1370 Image::ResolvedSymbolTarget lookupFuncTarget
;
1371 ResolvedTargetInfo lookupFuncInfo
;
1372 if ( findSymbolInImage(findLoadedImage(_libDyldImageNum
).loadAddress(), "__ZN5dyld316compatFuncLookupEPKcPPv", 0, false, false, lookupFuncTarget
, lookupFuncInfo
) ) {
1373 // add bind to set second pointer in __dyld section to be dyld3::compatFuncLookup
1374 uint64_t runtimeOffset
= sectInfo
.sectAddr
- forImage
.loadAddress()->preferredLoadAddress() + forImage
.loadAddress()->pointerSize();
1375 Image::BindPattern compatFuncPattern
;
1376 compatFuncPattern
.target
= lookupFuncTarget
;
1377 compatFuncPattern
.startVmOffset
= runtimeOffset
;
1378 compatFuncPattern
.repeatCount
= 1;
1379 compatFuncPattern
.skipCount
= 0;
1380 assert(compatFuncPattern
.startVmOffset
== runtimeOffset
);
1381 binds
.push_back(compatFuncPattern
);
1384 _diag
.error("libdyld.dylib is dyld3::compatFuncLookup");
1390 writer
.setBindInfo(binds
);
1393 void ClosureBuilder::reportRebasesAndBinds(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1395 // report all rebases
1396 forImage
.loadAddress()->forEachRebase(_diag
, true, ^(uint64_t runtimeOffset
, bool& stop
) {
1397 _handlers
->rebase(forImage
.imageNum
, forImage
.loadAddress(), (uint32_t)runtimeOffset
);
1401 forEachBind(forImage
, ^(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
) {
1402 _handlers
->bind(forImage
.imageNum
, forImage
.loadAddress(), (uint32_t)runtimeOffset
, target
, targetInfo
);
1404 ^(const char* strongSymbolName
) {},
1407 // i386 programs also use text relocs to rebase stubs
1408 if ( forImage
.loadAddress()->cputype
== CPU_TYPE_I386
) {
1413 // These are mangled symbols for all the variants of operator new and delete
1414 // which a main executable can define (non-weak) and override the
1415 // weak-def implementation in the OS.
1416 static const char* const sTreatAsWeak
[] = {
1417 "__Znwm", "__ZnwmRKSt9nothrow_t",
1418 "__Znam", "__ZnamRKSt9nothrow_t",
1419 "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
1420 "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
1421 "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
1422 "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
1423 "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
1424 "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
1428 void ClosureBuilder::addChainedFixupInfo(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1430 // build array of targets
1431 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ResolvedSymbolTarget
, targets
, 1024);
1432 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ResolvedTargetInfo
, targetInfos
, 1024);
1433 forImage
.loadAddress()->forEachChainedFixupTarget(_diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1434 Image::ResolvedSymbolTarget target
;
1435 ResolvedTargetInfo targetInfo
;
1436 if ( !findSymbol(forImage
, libOrdinal
, symbolName
, weakImport
, false, addend
, target
, targetInfo
) ) {
1437 const char* expectedInPath
= forImage
.loadAddress()->dependentDylibLoadPath(libOrdinal
-1);
1438 _diag
.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName
, expectedInPath
, forImage
.path());
1442 if ( libOrdinal
== BIND_SPECIAL_DYLIB_WEAK_LOOKUP
) {
1443 // add if not already in array
1444 bool alreadyInArray
= false;
1445 for (const char* sym
: _weakDefsFromChainedBinds
) {
1446 if ( strcmp(sym
, symbolName
) == 0 ) {
1447 alreadyInArray
= true;
1451 if ( !alreadyInArray
)
1452 _weakDefsFromChainedBinds
.push_back(symbolName
);
1454 targets
.push_back(target
);
1455 targetInfos
.push_back(targetInfo
);
1457 if ( _diag
.hasError() )
1460 uint64_t chainStartsOffset
= forImage
.loadAddress()->chainStartsOffset();
1461 if ( _handlers
!= nullptr ) {
1462 forImage
.loadAddress()->withChainStarts(_diag
, chainStartsOffset
, ^(const dyld_chained_starts_in_image
* starts
) {
1463 _handlers
->chainedBind(forImage
.imageNum
, forImage
.loadAddress(), starts
, targets
, targetInfos
);
1467 writer
.setChainedFixups(chainStartsOffset
, targets
);
1470 // with chained fixups, main executable may define symbol that overrides weak-defs but has no fixup
1471 if ( _isLaunchClosure
&& forImage
.loadAddress()->hasWeakDefs() && forImage
.loadAddress()->isMainExecutable() ) {
1472 for (const char* weakSymbolName
: sTreatAsWeak
) {
1473 Diagnostics exportDiag
;
1474 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
1475 if ( forImage
.loadAddress()->findExportedSymbol(exportDiag
, weakSymbolName
, false, foundInfo
, nullptr) ) {
1476 _weakDefsFromChainedBinds
.push_back(weakSymbolName
);
1483 bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer
* macho
, const char* symbolName
, uint64_t addend
, bool followReExports
,
1484 bool weakImport
, Image::ResolvedSymbolTarget
& target
, ResolvedTargetInfo
& targetInfo
)
1486 targetInfo
.foundInDylib
= nullptr;
1487 targetInfo
.requestedSymbolName
= symbolName
;
1488 targetInfo
.addend
= addend
;
1489 targetInfo
.weakBindCoalese
= false;
1490 targetInfo
.weakBindSameImage
= false;
1491 targetInfo
.isWeakDef
= false;
1492 targetInfo
.skippableWeakDef
= false;
1493 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
1494 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
1496 MachOAnalyzer::DependentToMachOLoaded finder
= nullptr;
1497 if ( followReExports
)
1498 finder
= reexportFinder
;
1500 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
1501 if ( macho
->findExportedSymbol(_diag
, symbolName
, weakImport
, foundInfo
, finder
) ) {
1502 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
1503 targetInfo
.foundInDylib
= foundInfo
.foundInDylib
;
1504 targetInfo
.foundSymbolName
= foundInfo
.foundSymbolName
;
1505 if ( foundInfo
.isWeakDef
)
1506 targetInfo
.isWeakDef
= true;
1507 if ( foundInfo
.kind
== MachOAnalyzer::FoundSymbol::Kind::absolute
) {
1508 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1509 target
.absolute
.value
= foundInfo
.value
+ addend
;
1511 else if ( impDylib
->inDyldCache() ) {
1512 uint64_t offsetValue
= (uint8_t*)impDylib
- (uint8_t*)_dyldCache
+ foundInfo
.value
+ addend
;
1513 target
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
1514 target
.sharedCache
.offset
= offsetValue
;
1515 assert(target
.sharedCache
.offset
== offsetValue
);
1518 uint64_t offsetValue
= foundInfo
.value
+ addend
;
1519 target
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1520 target
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
1521 target
.image
.offset
= offsetValue
;
1522 assert(target
.image
.offset
== offsetValue
);
1529 bool ClosureBuilder::findSymbol(BuilderLoadedImage
& fromImage
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
,
1530 uint64_t addend
, Image::ResolvedSymbolTarget
& target
, ResolvedTargetInfo
& targetInfo
)
1533 targetInfo
.weakBindCoalese
= false;
1534 targetInfo
.weakBindSameImage
= false;
1535 targetInfo
.isWeakDef
= false;
1536 targetInfo
.skippableWeakDef
= false;
1537 targetInfo
.requestedSymbolName
= symbolName
;
1538 targetInfo
.libOrdinal
= libOrdinal
;
1539 if ( libOrdinal
== BIND_SPECIAL_DYLIB_FLAT_LOOKUP
) {
1540 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1541 if ( !li
.rtldLocal
&& findSymbolInImage(li
.loadAddress(), symbolName
, addend
, true, weakImport
, target
, targetInfo
) )
1545 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1546 target
.absolute
.value
= 0;
1547 // Record that we found a missing weak import so that the objc optimizer doens't have to check
1548 fromImage
.hasMissingWeakImports
= true;
1551 // <rdar://problem/44315944> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace
1552 if ( lazyBind
&& _allowMissingLazies
) {
1553 if ( findMissingSymbolHandler(target
, targetInfo
) )
1556 _diag
.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName
, fromImage
.path());
1558 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_WEAK_LOOKUP
) {
1559 // to resolve weakDef coalesing, we need to search all images in order and use first definition
1560 // but, if first found is a weakDef, a later non-weak def overrides that
1561 bool foundWeakDefImpl
= false;
1562 bool foundStrongDefImpl
= false;
1563 bool foundImpl
= false;
1565 if ( _makingDyldCacheImages
) {
1566 // _loadedImages is all dylibs in the dyld cache, it is not load-order, so need alterate weak-def binding algorithm
1567 // look first in /usr/lib/libc++, most will be here
1568 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1569 if ( li
.loadAddress()->hasWeakDefs() && (strncmp(li
.path(), "/usr/lib/libc++", 15) == 0) ) {
1570 if ( findSymbolInImage(li
.loadAddress(), symbolName
, addend
, false, weakImport
, target
, targetInfo
) ) {
1576 // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself
1578 if ( findSymbolInImage(fromImage
.loadAddress(), symbolName
, addend
, false, weakImport
, target
, targetInfo
) ) {
1582 // if still not found, then this is the rare case of a simple use of a weak-def symbol
1584 // look in all direct dependents
1585 for (Image::LinkedImage child
: fromImage
.dependents
) {
1586 if (child
.imageNum() == kMissingWeakLinkedImage
)
1588 BuilderLoadedImage
& childLi
= findLoadedImage(child
.imageNum());
1589 if ( childLi
.loadAddress()->hasWeakDefs() && findSymbolInImage(childLi
.loadAddress(), symbolName
, addend
, false, weakImport
, target
, targetInfo
) ) {
1595 targetInfo
.weakBindCoalese
= true;
1598 // walk images in load-order to find first that implements this symbol
1599 Image::ResolvedSymbolTarget aTarget
;
1600 ResolvedTargetInfo aTargetInfo
;
1601 STACK_ALLOC_ARRAY(const BuilderLoadedImage
*, cachedDylibsUsingSymbol
, 1024);
1602 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1603 // only search images with weak-defs that were not loaded with RTLD_LOCAL
1604 if ( li
.loadAddress()->hasWeakDefs() && !li
.rtldLocal
) {
1605 if ( findSymbolInImage(li
.loadAddress(), symbolName
, addend
, false, weakImport
, aTarget
, aTargetInfo
) ) {
1607 // with non-chained images, weak-defs first have a rebase to their local impl, and a weak-bind which allows earlier impls to override
1608 if ( !li
.loadAddress()->hasChainedFixups() && (aTargetInfo
.foundInDylib
== fromImage
.loadAddress()) )
1609 targetInfo
.weakBindSameImage
= true;
1610 if ( aTargetInfo
.isWeakDef
) {
1611 // found a weakDef impl, if this is first found, set target to this
1612 if ( !foundWeakDefImpl
&& !foundStrongDefImpl
) {
1614 targetInfo
= aTargetInfo
;
1616 foundWeakDefImpl
= true;
1619 // found a non-weak impl, use this (unless early strong found)
1620 if ( !foundStrongDefImpl
) {
1622 targetInfo
= aTargetInfo
;
1624 foundStrongDefImpl
= true;
1627 if ( foundImpl
&& li
.loadAddress()->inDyldCache() )
1628 cachedDylibsUsingSymbol
.push_back(&li
);
1632 // now that final target found, if any dylib in dyld cache uses that symbol name, redirect it to new target
1633 if ( !cachedDylibsUsingSymbol
.empty() ) {
1634 for (const BuilderLoadedImage
* li
: cachedDylibsUsingSymbol
) {
1635 Image::ResolvedSymbolTarget implInCache
;
1636 ResolvedTargetInfo implInCacheInfo
;
1637 if ( findSymbolInImage(li
->loadAddress(), symbolName
, addend
, false, weakImport
, implInCache
, implInCacheInfo
) ) {
1638 if ( implInCache
!= target
) {
1639 // found another instance in some dylib in dyld cache, will need to patch it
1640 Closure::PatchEntry patch
;
1641 patch
.exportCacheOffset
= (uint32_t)implInCache
.sharedCache
.offset
;
1642 patch
.overriddenDylibInCache
= li
->imageNum
;
1643 patch
.replacement
= target
;
1644 _weakDefCacheOverrides
.push_back(patch
);
1649 targetInfo
.weakBindCoalese
= true;
1655 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1656 target
.absolute
.value
= 0;
1659 if ( ! fromImage
.loadAddress()->hasChainedFixups() ) {
1660 // support old binaries where symbols have been stripped and have weak_bind to itself
1661 targetInfo
.skippableWeakDef
= true;
1665 _diag
.error("symbol '%s' not found, expected to be weak-def coalesced by '%s'", symbolName
, fromImage
.path());
1668 const BuilderLoadedImage
* targetLoadedImage
= nullptr;
1669 if ( (libOrdinal
> 0) && (libOrdinal
<= (int)fromImage
.dependents
.count()) ) {
1670 ImageNum childNum
= fromImage
.dependents
[libOrdinal
- 1].imageNum();
1671 if ( childNum
!= kMissingWeakLinkedImage
) {
1672 targetLoadedImage
= &findLoadedImage(childNum
);
1675 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_SELF
) {
1676 targetLoadedImage
= &fromImage
;
1678 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
) {
1679 targetLoadedImage
= &_loadedImages
[_mainProgLoadIndex
];
1682 _diag
.error("unknown special ordinal %d in %s", libOrdinal
, fromImage
.path());
1686 if ( targetLoadedImage
!= nullptr ) {
1687 if ( findSymbolInImage(targetLoadedImage
->loadAddress(), symbolName
, addend
, true, weakImport
, target
, targetInfo
) )
1692 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1693 target
.absolute
.value
= 0;
1694 // Record that we found a missing weak import so that the objc optimizer doens't have to check
1695 fromImage
.hasMissingWeakImports
= true;
1699 // <rdar://problem/43315403> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld
1700 if ( lazyBind
&& _allowMissingLazies
) {
1701 if ( findMissingSymbolHandler(target
, targetInfo
) )
1705 // symbol not found and not weak or lazy so error out
1706 const char* expectedInPath
= targetLoadedImage
? targetLoadedImage
->path() : "unknown";
1707 _diag
.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName
, expectedInPath
, fromImage
.path());
1708 if ( _launchErrorInfo
!= nullptr ) {
1709 _launchErrorInfo
->kind
= DYLD_EXIT_REASON_SYMBOL_MISSING
;
1710 _launchErrorInfo
->clientOfDylibPath
= strdup_temp(fromImage
.path());
1711 _launchErrorInfo
->targetDylibPath
= strdup_temp(expectedInPath
);
1712 _launchErrorInfo
->symbol
= symbolName
;
1719 bool ClosureBuilder::findMissingSymbolHandler(Image::ResolvedSymbolTarget
& target
, ResolvedTargetInfo
& targetInfo
)
1721 for (BuilderLoadedImage
& li
: _loadedImages
) {
1722 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) {
1723 if ( findSymbolInImage(li
.loadAddress(), "__dyld_missing_symbol_abort", 0, false, false, target
, targetInfo
) ) {
1732 void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex
, InitInfo initInfos
[], uint32_t& initOrder
, bool& hasError
)
1734 if ( initInfos
[loadIndex
].visited
)
1736 initInfos
[loadIndex
].visited
= true;
1737 initInfos
[loadIndex
].danglingUpward
= false;
1739 if (_loadedImages
[loadIndex
].isBadImage
) {
1744 for (const Image::LinkedImage
& dep
: _loadedImages
[loadIndex
].dependents
) {
1745 if ( dep
.imageNum() == kMissingWeakLinkedImage
)
1747 ClosureBuilder::BuilderLoadedImage
& depLi
= findLoadedImage(dep
.imageNum());
1748 uint32_t depLoadIndex
= (uint32_t)_loadedImages
.index(depLi
);
1749 if ( dep
.kind() == Image::LinkKind::upward
) {
1750 if ( !initInfos
[depLoadIndex
].visited
)
1751 initInfos
[depLoadIndex
].danglingUpward
= true;
1754 depthFirstRecurseSetInitInfo(depLoadIndex
, initInfos
, initOrder
, hasError
);
1759 initInfos
[loadIndex
].initOrder
= initOrder
++;
1762 void ClosureBuilder::computeInitOrder(ImageWriter
& imageWriter
, uint32_t loadIndex
)
1764 // allocate array to track initializers
1765 InitInfo initInfos
[_loadedImages
.count()];
1766 bzero(initInfos
, sizeof(initInfos
));
1768 // recurse all images and build initializer list from bottom up
1769 uint32_t initOrder
= 1;
1770 bool hasMissingDependent
= false;
1771 depthFirstRecurseSetInitInfo(loadIndex
, initInfos
, initOrder
, hasMissingDependent
);
1772 if (hasMissingDependent
) {
1773 imageWriter
.setInvalid();
1777 // any images not visited yet are are danging, force add them to end of init list
1778 for (uint32_t i
=0; i
< (uint32_t)_loadedImages
.count(); ++i
) {
1779 if ( !initInfos
[i
].visited
&& initInfos
[i
].danglingUpward
) {
1780 depthFirstRecurseSetInitInfo(i
, initInfos
, initOrder
, hasMissingDependent
);
1784 if (hasMissingDependent
) {
1785 imageWriter
.setInvalid();
1789 // build array of just images with initializer
1790 STACK_ALLOC_ARRAY(uint32_t, indexOfImagesWithInits
, _loadedImages
.count());
1792 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1793 if ( initInfos
[index
].visited
&& li
.hasInits
) {
1794 indexOfImagesWithInits
.push_back(index
);
1799 // bubble sort (FIXME)
1800 if ( indexOfImagesWithInits
.count() > 1 ) {
1801 for (uint32_t i
=0; i
< indexOfImagesWithInits
.count()-1; ++i
) {
1802 for (uint32_t j
=0; j
< indexOfImagesWithInits
.count()-i
-1; ++j
) {
1803 if ( initInfos
[indexOfImagesWithInits
[j
]].initOrder
> initInfos
[indexOfImagesWithInits
[j
+1]].initOrder
) {
1804 uint32_t temp
= indexOfImagesWithInits
[j
];
1805 indexOfImagesWithInits
[j
] = indexOfImagesWithInits
[j
+1];
1806 indexOfImagesWithInits
[j
+1] = temp
;
1812 // copy ImageNum of each image with initializers into array
1813 ImageNum initNums
[indexOfImagesWithInits
.count()];
1814 for (uint32_t i
=0; i
< indexOfImagesWithInits
.count(); ++i
) {
1815 initNums
[i
] = _loadedImages
[indexOfImagesWithInits
[i
]].imageNum
;
1818 // add to closure info
1819 imageWriter
.setInitsOrder(initNums
, (uint32_t)indexOfImagesWithInits
.count());
1822 void ClosureBuilder::addClosureInfo(LaunchClosureWriter
& closureWriter
)
1824 // record which is libSystem
1825 assert(_libSystemImageNum
!= 0);
1826 closureWriter
.setLibSystemImageNum(_libSystemImageNum
);
1828 // record which is libdyld
1829 assert(_libDyldImageNum
!= 0);
1830 Image::ResolvedSymbolTarget entryLocation
;
1831 ResolvedTargetInfo entryInfo
;
1832 if ( findSymbolInImage(findLoadedImage(_libDyldImageNum
).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, false, entryLocation
, entryInfo
) ) {
1833 const dyld3::LibDyldEntryVector
* libDyldEntry
= nullptr;
1834 switch ( entryLocation
.image
.kind
) {
1835 case Image::ResolvedSymbolTarget::kindSharedCache
:
1836 libDyldEntry
= (dyld3::LibDyldEntryVector
*)((uint8_t*)_dyldCache
+ entryLocation
.sharedCache
.offset
);
1838 case Image::ResolvedSymbolTarget::kindImage
:
1839 libDyldEntry
= (dyld3::LibDyldEntryVector
*)((uint8_t*)findLoadedImage(entryLocation
.image
.imageNum
).loadAddress() + entryLocation
.image
.offset
);
1842 if ( (libDyldEntry
!= nullptr) && ((libDyldEntry
->binaryFormatVersion
& LibDyldEntryVector::kBinaryFormatVersionMask
) == dyld3::closure::kFormatVersion
) )
1843 closureWriter
.setLibDyldEntry(entryLocation
);
1845 _diag
.error("libdyld.dylib entry vector is incompatible");
1848 _diag
.error("libdyld.dylib is missing entry vector");
1851 // record which is main executable
1852 ImageNum mainProgImageNum
= _loadedImages
[_mainProgLoadIndex
].imageNum
;
1853 closureWriter
.setTopImageNum(mainProgImageNum
);
1856 uint32_t entryOffset
;
1858 if ( _loadedImages
[_mainProgLoadIndex
].loadAddress()->getEntry(entryOffset
, usesCRT
) ) {
1859 Image::ResolvedSymbolTarget location
;
1860 location
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1861 location
.image
.imageNum
= mainProgImageNum
;
1862 location
.image
.offset
= entryOffset
;
1864 closureWriter
.setStartEntry(location
);
1866 closureWriter
.setMainEntry(location
);
1869 // add env vars that must match at launch time
1870 _pathOverrides
.forEachEnvVar(^(const char* envVar
) {
1871 closureWriter
.addEnvVar(envVar
);
1874 // add list of files which must be missing
1875 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const char*, paths
, 8192);
1876 if ( _mustBeMissingPaths
!= nullptr ) {
1877 _mustBeMissingPaths
->forEachPath(^(const char* aPath
) {
1878 paths
.push_back(aPath
);
1881 closureWriter
.setMustBeMissingFiles(paths
);
1883 // add list of files which must be be present with a specific inode/mtime
1884 if (!_skippedFiles
.empty())
1885 closureWriter
.setMustExistFiles(_skippedFiles
);
1887 void ClosureBuilder::invalidateInitializerRoots()
1890 bool madeChange
= false;
1891 for (uintptr_t loadedImageIndex
= _alreadyInitedIndex
; loadedImageIndex
!= _loadedImages
.count(); ++loadedImageIndex
) {
1892 BuilderLoadedImage
& li
= _loadedImages
[loadedImageIndex
];
1893 if ( li
.mustBuildClosure
) {
1894 // Already invalidated
1897 for (Image::LinkedImage depIndex
: li
.dependents
) {
1898 if ( depIndex
.imageNum() == kMissingWeakLinkedImage
)
1900 BuilderLoadedImage
& depImage
= findLoadedImage(depIndex
.imageNum());
1901 // If a dependent is bad, or a new image num, or an override, then we need this image to get a new closure
1902 if ( depImage
.mustBuildClosure
) {
1903 li
.mustBuildClosure
= true; // mark bad
1910 // If we made a change, then we detected an existing image with a dependent which needed to be rebuilt.
1911 // This corresponds to a root of the shared cache where the existing image is a shared cache one and the root is the depImage
1912 _foundDyldCacheRoots
= true;
1916 size_t ClosureBuilder::HashCString::hash(const char* v
) {
1917 // FIXME: Use hash<string_view> when it has the correct visibility markup
1918 return __gnu_cxx::hash
<const char*>{}(v
);
1921 bool ClosureBuilder::EqualCString::equal(const char* s1
, const char* s2
) {
1922 return strcmp(s1
, s2
) == 0;
1927 static size_t hash(const uint64_t& v
) {
1928 return std::hash
<uint64_t>{}(v
);
1932 struct EqualUInt64
{
1933 static bool equal(uint64_t s1
, uint64_t s2
) {
1938 void ClosureBuilder::writeClassOrProtocolHashTable(bool classes
, Array
<ObjCOptimizerImage
>& objcImages
) {
1939 __block MultiMap
<const char*, dyld3::closure::Image::ObjCClassImageOffset
, HashCString
, EqualCString
> seenClassesMap
;
1940 __block Map
<const char*, dyld3::closure::Image::ObjCClassNameImageOffset
, HashCString
, EqualCString
> classNameMap
;
1941 __block OverflowSafeArray
<const char*> classNames
;
1943 // Note we walk the images backwards as we want them in load order to match the order they are registered with objc
1944 for (size_t imageIndex
= 0, reverseIndex
= (objcImages
.count() - 1); imageIndex
!= objcImages
.count(); ++imageIndex
, --reverseIndex
) {
1945 if (objcImages
[reverseIndex
].diag
.hasError())
1947 ObjCOptimizerImage
& image
= objcImages
[reverseIndex
];
1948 const OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenClasses
= classes
? image
.seenClasses
: image
.seenProtocols
;
1950 for (const ObjCOptimizerImage::SeenClass
& seenClass
: seenClasses
) {
1951 closure::Image::ObjCClassNameImageOffset classNameTarget
= seenClass
.first
;
1952 dyld3::closure::Image::ObjCClassImageOffset classDataTarget
= seenClass
.second
;
1953 Image::ObjCClassImage classImage
= _objcClassesHashTableImages
[classNameTarget
.classNameImageIndex
];
1955 const BuilderLoadedImage
& li
= findLoadedImage(classImage
.imageNum
);
1956 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
1958 const char* className
= ((const char*)ma
) + classImage
.offsetOfClassNames
+ classNameTarget
.classNameImageOffset
;
1959 //uint64_t nameVMAddr = ma->preferredLoadAddress() + classImage.offsetOfClassNames + classNameTarget.classNameImageOffset;
1960 //printf("%s: 0x%08llx = '%s'\n", li.path(), nameVMAddr, className);
1961 seenClassesMap
.insert({ className
, classDataTarget
});
1963 // Also track the name
1964 auto itAndInserted
= classNameMap
.insert({ className
, dyld3::closure::Image::ObjCClassNameImageOffset() });
1965 if (itAndInserted
.second
) {
1966 // We inserted the class name so we need to add it to the strings for the closure hash table
1967 classNames
.push_back(className
);
1969 // We already computed a class name target in a previous loop so use that one
1970 itAndInserted
.first
->second
= seenClass
.first
;
1972 // If we are processing protocols, and this is the first one we've seen, then track its ISA to be fixed up
1974 uint64_t protocolVMOffset
= classImage
.offsetOfClasses
+ classDataTarget
.classData
.imageOffset
;
1975 image
.protocolISAFixups
.push_back(protocolVMOffset
);
1981 __block
uint32_t duplicateCount
= 0;
1982 seenClassesMap
.forEachEntry(^(const char *const &key
, const Image::ObjCClassImageOffset
**values
,
1983 uint64_t valuesCount
) {
1984 if (valuesCount
!= 1)
1985 duplicateCount
+= valuesCount
;
1988 // If we have closure class names, we need to make a hash table for them.
1989 OverflowSafeArray
<uint8_t>& hashTable
= classes
? _objcClassesHashTable
: _objcProtocolsHashTable
;
1990 if (!classNames
.empty()) {
1991 objc_opt::perfect_hash phash
;
1992 objc_opt::make_perfect(classNames
, phash
);
1993 size_t size
= ObjCClassOpt::size(phash
, duplicateCount
);
1994 hashTable
.resize(size
);
1995 //printf("Class table size: %lld\n", size);
1996 ObjCClassOpt
* classesHashTable
= (ObjCClassOpt
*)hashTable
.begin();
1997 classesHashTable
->write(phash
, classNameMap
.array(), seenClassesMap
, duplicateCount
);
2001 bool ClosureBuilder::optimizeObjC(Array
<ImageWriter
>& writers
) {
2002 if ( _dyldCache
== nullptr )
2005 // If we have the read only data, make sure it has a valid selector table inside.
2006 const objc_opt::objc_clsopt_t
* objcClassOpt
= nullptr;
2007 const objc_opt::objc_selopt_t
* objcSelOpt
= nullptr;
2008 const objc_opt::objc_protocolopt2_t
* objcProtocolOpt
= nullptr;
2009 if (const objc_opt::objc_opt_t
* optObjCHeader
= _dyldCache
->objcOpt()) {
2010 objcClassOpt
= optObjCHeader
->clsopt();
2011 objcSelOpt
= optObjCHeader
->selopt();
2012 objcProtocolOpt
= optObjCHeader
->protocolopt2();
2015 if ( !objcClassOpt
|| !objcSelOpt
|| !objcProtocolOpt
)
2018 // We have 24 bits of index in SelectorReferenceFixup so we can't handle a
2019 // shared cache selector table larger than that
2020 if ( objcSelOpt
->usedCount() >= (1 << 24) )
2023 // Make sure we have the pointers section with the pointer to the protocol class
2024 const void* objcOptPtrs
= _dyldCache
->objcOptPtrs();
2025 if ( objcOptPtrs
== nullptr )
2028 uint32_t pointerSize
= _loadedImages
.begin()->loadAddress()->pointerSize();
2029 uint64_t classProtocolVMAddr
= (pointerSize
== 8) ? *(uint64_t*)objcOptPtrs
: *(uint32_t*)objcOptPtrs
;
2031 Image::ResolvedSymbolTarget objcProtocolClassTarget
;
2032 objcProtocolClassTarget
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
2033 if ( _dyldCacheIsLive
) {
2034 objcProtocolClassTarget
.sharedCache
.offset
= classProtocolVMAddr
- (uint64_t)_dyldCache
;
2036 objcProtocolClassTarget
.sharedCache
.offset
= classProtocolVMAddr
- _dyldCache
->unslidLoadAddress();
2039 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ObjCOptimizerImage
, objcImages
, 32);
2040 ArrayFinalizer
<ObjCOptimizerImage
> scopedCleanup(objcImages
,
2041 ^(ObjCOptimizerImage
& objcImage
) {
2042 objcImage
.~ObjCOptimizerImage();
2045 // Find all the images with valid objc info
2046 // Also add shared cache images to a map so that we can see them later for looking up classes
2047 Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
> sharedCacheImagesMap
;
2048 for (size_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
2049 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
2051 // Skip shared cache images as even if they need a new closure, the objc runtime can still use
2052 // the optimized shared cache tables.
2053 if ( li
.loadAddress()->inDyldCache() ) {
2054 sharedCacheImagesMap
.insert({ li
.loadAddress(), true });
2055 // Bump the writer index if we have a writer for this image
2056 if ( li
.mustBuildClosure
)
2060 // Images which don't need a closure can be skipped. They are from the shared cache
2061 if ( !li
.mustBuildClosure
)
2064 // If we have a root of libobjc, just give up for now
2065 if ( !strcmp(li
.path(), "/usr/lib/libobjc.A.dylib"))
2068 ImageWriter
& writer
= writers
[writerIndex
];
2071 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2073 // Skip images with chained fixups other than arm64e legacy fixups until we can test them
2074 // FIXME: Handle chained fixups
2075 if ( ma
->hasChainedFixups() ) {
2076 switch ( ma
->chainedPointerFormat() ) {
2077 case DYLD_CHAINED_PTR_ARM64E
:
2078 case DYLD_CHAINED_PTR_64
:
2079 // We've tested the 64-bit chained fixups.
2081 case DYLD_CHAINED_PTR_32
:
2082 case DYLD_CHAINED_PTR_32_CACHE
:
2083 case DYLD_CHAINED_PTR_32_FIRMWARE
:
2084 // FIXME: Test 32-bit chained fixups then enable this.
2089 const MachOAnalyzer::ObjCImageInfo
* objcImageInfo
= ma
->objcImageInfo();
2090 if ( objcImageInfo
== nullptr )
2093 // This image is good so record it for use later.
2094 objcImages
.default_constuct_back();
2095 ObjCOptimizerImage
& image
= objcImages
.back();
2096 image
.loadedImage
= &li
;
2097 image
.writer
= &writer
;
2099 // Find FairPlay encryption range if encrypted
2100 uint32_t fairPlayFileOffset
;
2101 uint32_t fairPlaySize
;
2102 if ( ma
->isFairPlayEncrypted(fairPlayFileOffset
, fairPlaySize
) ) {
2103 image
.fairplayFileOffsetStart
= fairPlayFileOffset
;
2104 image
.fairplayFileOffsetEnd
= fairPlayFileOffset
;
2107 // Set the offset to the objc image info
2108 image
.objcImageInfoVMOffset
= (uint64_t)objcImageInfo
- (uint64_t)ma
;
2111 OverflowSafeArray
<const char*> closureSelectorStrings
;
2112 Map
<const char*, dyld3::closure::Image::ObjCImageOffset
, HashCString
, EqualCString
> closureSelectorMap
;
2113 OverflowSafeArray
<const char*> closureDuplicateSharedCacheClassNames
;
2114 Map
<const char*, dyld3::closure::Image::ObjCDuplicateClass
, HashCString
, EqualCString
> closureDuplicateSharedCacheClassMap
;
2115 for (ObjCOptimizerImage
& image
: objcImages
) {
2116 optimizeObjCClasses(objcClassOpt
, sharedCacheImagesMap
, closureDuplicateSharedCacheClassMap
, image
);
2117 if (image
.diag
.hasError())
2120 optimizeObjCProtocols(objcProtocolOpt
, sharedCacheImagesMap
, image
);
2121 if (image
.diag
.hasError())
2124 optimizeObjCSelectors(objcSelOpt
, closureSelectorMap
, image
);
2125 if (image
.diag
.hasError())
2128 // If this image is still valid, then add its intermediate results to the main tables
2131 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2132 uint64_t nameVMOffset
= nameAndDataVMOffset
.first
;
2133 uint64_t dataVMOffset
= nameAndDataVMOffset
.second
;
2134 _objcClassesHashTableImages
.push_back({ image
.loadedImage
->imageNum
, (uint32_t)nameVMOffset
, (uint32_t)dataVMOffset
});
2136 image
.classesNameAndDataVMOffsets
.clear();
2138 for (const auto& stringAndDuplicate
: image
.classSharedCacheDuplicates
) {
2139 closureDuplicateSharedCacheClassMap
[stringAndDuplicate
.first
] = stringAndDuplicate
.second
;
2140 closureDuplicateSharedCacheClassNames
.push_back(stringAndDuplicate
.first
);
2144 // Note we don't need to add the selector binds here. Its easier just to process them later from each image
2145 for (const auto& stringAndTarget
: image
.selectorMap
) {
2146 closureSelectorMap
[stringAndTarget
.first
] = stringAndTarget
.second
;
2147 closureSelectorStrings
.push_back(stringAndTarget
.first
);
2149 if (image
.methodNameVMOffset
)
2150 _objcSelectorsHashTableImages
.push_back({ image
.loadedImage
->imageNum
, (uint32_t)*image
.methodNameVMOffset
});
2153 // If we successfully analyzed the classes and selectors, we can now emit their data
2154 // Set all the writers to have optimized objc
2155 for (ObjCOptimizerImage
& image
: objcImages
) {
2156 if (image
.diag
.hasError())
2158 image
.writer
->setHasPrecomputedObjC(true);
2161 // Write out the class table
2162 writeClassOrProtocolHashTable(true, objcImages
);
2164 // Write out the protocol table
2165 writeClassOrProtocolHashTable(false, objcImages
);
2167 // If we have closure duplicate classes, we need to make a hash table for them.
2168 closure::ObjCStringTable
* duplicateClassesTable
= nullptr;
2169 if (!closureDuplicateSharedCacheClassNames
.empty()) {
2170 objc_opt::perfect_hash phash
;
2171 objc_opt::make_perfect(closureDuplicateSharedCacheClassNames
, phash
);
2172 size_t size
= ObjCStringTable::size(phash
);
2173 _objcClassesDuplicatesHashTable
.resize(size
);
2174 //printf("Duplicate classes table size: %lld\n", size);
2175 duplicateClassesTable
= (closure::ObjCClassDuplicatesOpt
*)_objcClassesDuplicatesHashTable
.begin();
2176 duplicateClassesTable
->write(phash
, closureDuplicateSharedCacheClassMap
.array());
2179 // If we have closure selectors, we need to make a hash table for them.
2180 closure::ObjCStringTable
* selectorStringTable
= nullptr;
2181 if (!closureSelectorStrings
.empty()) {
2182 objc_opt::perfect_hash phash
;
2183 objc_opt::make_perfect(closureSelectorStrings
, phash
);
2184 size_t size
= ObjCStringTable::size(phash
);
2185 _objcSelectorsHashTable
.resize(size
);
2186 //printf("Selector table size: %lld\n", size);
2187 selectorStringTable
= (closure::ObjCStringTable
*)_objcSelectorsHashTable
.begin();
2188 selectorStringTable
->write(phash
, closureSelectorMap
.array());
2191 // Add fixups for the image info, protocol ISAs, and selector refs
2192 for (ObjCOptimizerImage
& image
: objcImages
) {
2193 if (image
.diag
.hasError())
2196 // Protocol ISA references
2197 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ProtocolISAFixup
, protocolFixups
, 512);
2198 if ( !image
.protocolISAFixups
.empty() ) {
2200 __block
uint64_t lastOffset
= -pointerSize
;
2201 for (uint64_t runtimeOffset
: image
.protocolISAFixups
) {
2202 bool mergedIntoPrevious
= false;
2203 if ( (runtimeOffset
> lastOffset
) && !protocolFixups
.empty() ) {
2204 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2205 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2206 // misaligned pointer means we cannot optimize
2209 if ( (protocolFixups
.back().repeatCount
== 1) && (protocolFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2210 protocolFixups
.back().repeatCount
= 2;
2211 protocolFixups
.back().skipCount
= skipAmount
;
2212 assert(protocolFixups
.back().skipCount
== skipAmount
); // check overflow
2213 mergedIntoPrevious
= true;
2215 else if ( (protocolFixups
.back().skipCount
== skipAmount
) && (protocolFixups
.back().repeatCount
< 0xfff) ) {
2216 uint32_t prevRepeatCount
= protocolFixups
.back().repeatCount
;
2217 protocolFixups
.back().repeatCount
+= 1;
2218 assert(protocolFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2219 mergedIntoPrevious
= true;
2223 if ( !mergedIntoPrevious
) {
2224 Image::ProtocolISAFixup pattern
;
2225 pattern
.startVmOffset
= runtimeOffset
;
2226 pattern
.repeatCount
= 1;
2227 pattern
.skipCount
= 0;
2228 assert(pattern
.startVmOffset
== runtimeOffset
);
2229 protocolFixups
.push_back(pattern
);
2231 lastOffset
= runtimeOffset
;
2235 // Selector references
2236 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::SelectorReferenceFixup
, selRefFixups
, 512);
2237 if ( !image
.selectorFixups
.empty() ) {
2238 uint64_t prevVMOffset
= 0;
2239 const uint64_t maxChainOffset
= (4 * ((1 << 7) - 1));
2240 for (const ObjCOptimizerImage::SelectorFixup
& selectorFixup
: image
.selectorFixups
) {
2241 assert( (selectorFixup
.fixupVMOffset
& 3) == 0 );
2242 if ( (selectorFixup
.fixupVMOffset
- prevVMOffset
) <= maxChainOffset
) {
2243 // Add this to the previous chain
2244 selRefFixups
.back().chainEntry
.next
= (uint32_t)(selectorFixup
.fixupVMOffset
- prevVMOffset
) / 4;
2246 // Need to start a new chain as the previous offset can't reach
2247 Image::SelectorReferenceFixup fixup
;
2248 fixup
.chainStartVMOffset
= selectorFixup
.fixupVMOffset
;
2249 selRefFixups
.push_back(fixup
);
2252 if ( selectorFixup
.isSharedCache
) {
2253 // If the entry is in the shared cache then we already have the index for it
2254 Image::SelectorReferenceFixup fixup
;
2255 fixup
.chainEntry
.index
= selectorFixup
.sharedCache
.selectorTableIndex
;
2256 fixup
.chainEntry
.next
= 0;
2257 fixup
.chainEntry
.inSharedCache
= 1;
2258 selRefFixups
.push_back(fixup
);
2260 // We had to record the string for the closure table entries as we don't know the
2262 uint32_t selectorTableIndex
= selectorStringTable
->getIndex(selectorFixup
.image
.selectorString
);
2263 assert(selectorTableIndex
!= ObjCSelectorOpt::indexNotFound
);
2264 Image::SelectorReferenceFixup fixup
;
2265 fixup
.chainEntry
.index
= selectorTableIndex
;
2266 fixup
.chainEntry
.next
= 0;
2267 fixup
.chainEntry
.inSharedCache
= 0;
2268 selRefFixups
.push_back(fixup
);
2271 prevVMOffset
= selectorFixup
.fixupVMOffset
;
2275 // Stable Swift fixups
2276 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ClassStableSwiftFixup
, stableSwiftFixups
, 512);
2277 if ( !image
.classStableSwiftFixups
.empty() ) {
2279 __block
uint64_t lastOffset
= -pointerSize
;
2280 for (uint64_t runtimeOffset
: image
.classStableSwiftFixups
) {
2281 bool mergedIntoPrevious
= false;
2282 if ( (runtimeOffset
> lastOffset
) && !stableSwiftFixups
.empty() ) {
2283 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2284 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2285 // misaligned pointer means we cannot optimize
2288 if ( (stableSwiftFixups
.back().repeatCount
== 1) && (stableSwiftFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2289 stableSwiftFixups
.back().repeatCount
= 2;
2290 stableSwiftFixups
.back().skipCount
= skipAmount
;
2291 assert(stableSwiftFixups
.back().skipCount
== skipAmount
); // check overflow
2292 mergedIntoPrevious
= true;
2294 else if ( (stableSwiftFixups
.back().skipCount
== skipAmount
) && (stableSwiftFixups
.back().repeatCount
< 0xfff) ) {
2295 uint32_t prevRepeatCount
= stableSwiftFixups
.back().repeatCount
;
2296 stableSwiftFixups
.back().repeatCount
+= 1;
2297 assert(stableSwiftFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2298 mergedIntoPrevious
= true;
2302 if ( !mergedIntoPrevious
) {
2303 Image::ClassStableSwiftFixup pattern
;
2304 pattern
.startVmOffset
= runtimeOffset
;
2305 pattern
.repeatCount
= 1;
2306 pattern
.skipCount
= 0;
2307 assert(pattern
.startVmOffset
== runtimeOffset
);
2308 stableSwiftFixups
.push_back(pattern
);
2310 lastOffset
= runtimeOffset
;
2314 // Method list fixups
2315 // TODO: Implement this
2316 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::MethodListFixup
, methodListFixups
, 512);
2318 image
.writer
->setObjCFixupInfo(objcProtocolClassTarget
, image
.objcImageInfoVMOffset
, protocolFixups
,
2319 selRefFixups
, stableSwiftFixups
, methodListFixups
);
2325 void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t
* objcSelOpt
,
2326 const Map
<const char*, dyld3::closure::Image::ObjCImageOffset
, HashCString
, EqualCString
>& closureSelectorMap
,
2327 ObjCOptimizerImage
& image
) {
2329 BuilderLoadedImage
& li
= *image
.loadedImage
;
2331 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2332 uint32_t pointerSize
= ma
->pointerSize();
2333 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2335 // The legacy (objc1) codebase uses a bunch of sections we don't want to reason about. If we see them just give up.
2336 __block
bool foundBadSection
= false;
2337 ma
->forEachSection(^(const MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
2338 if ( strcmp(sectInfo
.segInfo
.segName
, "__OBJC") != 0 )
2340 if (strcmp(sectInfo
.sectName
, "__module_info") == 0) {
2341 foundBadSection
= true;
2345 if (strcmp(sectInfo
.sectName
, "__protocol") == 0) {
2346 foundBadSection
= true;
2350 if (strcmp(sectInfo
.sectName
, "__message_refs") == 0) {
2351 foundBadSection
= true;
2356 if (foundBadSection
) {
2357 image
.diag
.error("Old objc section");
2361 __block
MachOAnalyzer::SectionCache
selectorStringSectionCache(ma
);
2363 uint32_t sharedCacheSentinelIndex
= objcSelOpt
->getSentinelIndex();
2365 auto visitReferenceToObjCSelector
= ^void(uint64_t selectorStringVMAddr
, uint64_t selectorReferenceVMAddr
) {
2367 uint64_t selectorUseImageOffset
= selectorReferenceVMAddr
- loadAddress
;
2368 if ( (selectorUseImageOffset
& 3) != 0 ) {
2369 image
.diag
.error("Unaligned selector reference fixup");
2373 // Image::SelectorReferenceFixup only has a 32-bit reach
2374 if ( selectorUseImageOffset
>= (1ULL << 32) ) {
2375 image
.diag
.error("Selector reference fixup exceeds supported vm offset");
2379 // Get the section for the name
2380 const char* selectorString
= nullptr;
2381 MachOAnalyzer::PrintableStringResult selectorStringResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2382 __block
uint64_t selectorStringSectionStartVMAddr
= 0;
2383 auto selectorStringSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2385 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2386 if (sectInfo
.sectSize
>= Image::ObjCImageOffset::maximumOffset
) {
2390 // We use 32-bit offsets so make sure the section is no larger than that.
2391 uint64_t classNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2392 if (classNameVMOffset
>= (1ULL << 32)) {
2396 selectorStringSectionStartVMAddr
= sectInfo
.sectAddr
;
2399 selectorString
= ma
->getPrintableString(selectorStringVMAddr
, selectorStringResult
,
2400 &selectorStringSectionCache
, selectorStringSectionHandler
);
2402 if ( selectorStringResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2403 image
.diag
.error("Invalid selector string for objc optimisation");
2407 uint32_t cacheSelectorIndex
= objcSelOpt
->getIndexForKey(selectorString
);
2408 //printf("selector: %p -> %p %s\n", methodName, cacheSelector, selectorString);
2410 if ( cacheSelectorIndex
!= sharedCacheSentinelIndex
) {
2411 // We got the selector from the cache so add a fixup to point there.
2412 ObjCOptimizerImage::SelectorFixup fixup
;
2413 fixup
.isSharedCache
= true;
2414 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2415 fixup
.sharedCache
.selectorTableIndex
= cacheSelectorIndex
;
2417 //printf("Overriding fixup at 0x%08llX to cache offset 0x%08llX\n", selectorUseImageOffset, (uint64_t)cacheSelector - (uint64_t)_dyldCache);
2418 image
.selectorFixups
.push_back(fixup
);
2422 // See if this selector is already in the closure map from a previous image
2423 auto closureSelectorIt
= closureSelectorMap
.find(selectorString
);
2424 if (closureSelectorIt
!= closureSelectorMap
.end()) {
2425 // This selector was found in a previous image, so use it here.
2426 ObjCOptimizerImage::SelectorFixup fixup
;
2427 fixup
.isSharedCache
= false;
2428 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2429 fixup
.image
.selectorString
= selectorString
;
2431 //printf("Overriding fixup at 0x%08llX to '%s' offset 0x%08llX\n", selectorUseImageOffset, findLoadedImage(target.image.imageNum).path(), target.image.offset);
2432 image
.selectorFixups
.push_back(fixup
);
2436 // See if this selector is already in the map for this image
2437 auto itAndInserted
= image
.selectorMap
.insert({ selectorString
, dyld3::closure::Image::ObjCImageOffset() });
2438 if (itAndInserted
.second
) {
2439 // We added the selector so its pointing in to our own image.
2440 // We don't need to add a fixup to our image, but we do need to
2441 // populate the data for other images later to point here.
2442 // First put our image in the list if its not already there.
2443 uint64_t methodNameVMOffset
= selectorStringSectionStartVMAddr
- loadAddress
;
2444 if (!image
.methodNameVMOffset
) {
2445 if ( _objcSelectorsHashTableImages
.count() == Image::ObjCImageOffset::maximumImageIndex
) {
2446 image
.diag
.error("Out of space for selector hash images");
2449 image
.methodNameVMOffset
= methodNameVMOffset
;
2451 // If we already set the offset to the start of the method names section, double check that
2452 // the section we are in right now is the same as that one. Otherwise we don't have the code
2453 // to handle both right now.
2454 if (*image
.methodNameVMOffset
!= methodNameVMOffset
) {
2455 image
.diag
.error("Cannot handle more than one selector strings section");
2460 dyld3::closure::Image::ObjCImageOffset target
;
2461 target
.imageIndex
= (uint32_t)_objcSelectorsHashTableImages
.count();
2462 target
.imageOffset
= (uint32_t)(selectorStringVMAddr
- selectorStringSectionStartVMAddr
);
2463 itAndInserted
.first
->second
= target
;
2467 // This selector was found elsewhere in our image. If this reference already points to the same
2468 // selector string as we found before (and it should!) then we have nothing to do. Otherwise we
2469 // need to add a fixup here to make sure we point to our chosen definition.
2470 uint32_t imageOffset
= (uint32_t)(selectorStringVMAddr
- loadAddress
);
2471 if ( imageOffset
== (*image
.methodNameVMOffset
+ itAndInserted
.first
->second
.imageOffset
) )
2474 ObjCOptimizerImage::SelectorFixup fixup
;
2475 fixup
.isSharedCache
= false;
2476 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2477 fixup
.image
.selectorString
= selectorString
;
2479 //printf("Overriding fixup at 0x%08llX to '%s' offset 0x%08llX\n", selectorUseImageOffset, findLoadedImage(target.image.imageNum).path(), target.image.offset);
2480 image
.selectorFixups
.push_back(fixup
);
2483 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
2484 visitReferenceToObjCSelector(method
.nameVMAddr
, method
.nameLocationVMAddr
);
2487 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
2488 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
2489 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
2490 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), li
.contentRebased
,
2494 auto visitCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
2495 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
2496 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, li
.contentRebased
,
2498 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, li
.contentRebased
,
2501 auto visitProtocol
= ^(Diagnostics
& diag
, uint64_t protocolVMAddr
,
2502 const dyld3::MachOAnalyzer::ObjCProtocol
& objCProtocol
) {
2503 ma
->forEachObjCMethod(objCProtocol
.instanceMethodsVMAddr
, li
.contentRebased
,
2505 ma
->forEachObjCMethod(objCProtocol
.classMethodsVMAddr
, li
.contentRebased
,
2507 ma
->forEachObjCMethod(objCProtocol
.optionalInstanceMethodsVMAddr
, li
.contentRebased
,
2509 ma
->forEachObjCMethod(objCProtocol
.optionalClassMethodsVMAddr
, li
.contentRebased
,
2513 // Walk the class list
2514 ma
->forEachObjCClass(image
.diag
, li
.contentRebased
, visitClass
);
2515 if (image
.diag
.hasError())
2518 // Walk the category list
2519 ma
->forEachObjCCategory(image
.diag
, li
.contentRebased
, visitCategory
);
2520 if (image
.diag
.hasError())
2523 // Walk the protocol list
2524 ma
->forEachObjCProtocol(image
.diag
, li
.contentRebased
, visitProtocol
);
2525 if (image
.diag
.hasError())
2528 // Visit the selector refs
2529 ma
->forEachObjCSelectorReference(image
.diag
, li
.contentRebased
, ^(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
) {
2530 visitReferenceToObjCSelector(selRefTargetVMAddr
, selRefVMAddr
);
2532 if (image
.diag
.hasError())
2535 // Visit the message refs
2536 // Note this isn't actually supported in libobjc any more. Its logic for deciding whether to support it is if this is true:
2537 // #if (defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))
2538 // So to keep it simple, lets only do this walk if we are x86_64
2539 if ( ma
->isArch("x86_64") || ma
->isArch("x86_64h") ) {
2540 if (ma
->hasObjCMessageReferences()) {
2541 image
.diag
.error("Cannot handle message refs");
2547 static const dyld3::MachOAnalyzer
* getMachHeaderFromObjCHeaderInfo(const void* opaqueHeaderInfo
, uint32_t pointerSize
) {
2548 if (pointerSize
== 8) {
2549 typedef int64_t PtrTy
;
2551 PtrTy mhdr_offset
; // offset to mach_header_64
2552 PtrTy info_offset
; // offset to objc_image_info *
2554 const HeaderInfo
* headerInfo
= (const HeaderInfo
*)opaqueHeaderInfo
;
2555 return (const dyld3::MachOAnalyzer
*)(((const uint8_t*)&headerInfo
->mhdr_offset
) + headerInfo
->mhdr_offset
);
2557 typedef int32_t PtrTy
;
2559 PtrTy mhdr_offset
; // offset to mach_header
2560 PtrTy info_offset
; // offset to objc_image_info *
2562 const HeaderInfo
* headerInfo
= (const HeaderInfo
*)opaqueHeaderInfo
;
2563 return (const dyld3::MachOAnalyzer
*)(((const uint8_t*)&headerInfo
->mhdr_offset
) + headerInfo
->mhdr_offset
);
2567 void ClosureBuilder::addDuplicateObjCClassWarning(const char* className
,
2568 const char* duplicateDefinitionPath
,
2569 const char* canonicalDefinitionPath
)
2571 if ( _objcDuplicateClassWarnings
== nullptr )
2572 _objcDuplicateClassWarnings
= PathPool::allocate();
2573 // Use a diagnostic to give us a buffer we can safely print to
2575 diag
.error("Class %s is implemented in both %s and %s. One of the two will be used. Which one is undefined.",
2576 className
, canonicalDefinitionPath
, duplicateDefinitionPath
);
2577 #if BUILDING_CACHE_BUILDER
2578 _objcDuplicateClassWarnings
->add(diag
.errorMessage().c_str());
2580 _objcDuplicateClassWarnings
->add(diag
.errorMessage());
2584 void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t
* objcClassOpt
,
2585 const Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
>& sharedCacheImagesMap
,
2586 const Map
<const char*, dyld3::closure::Image::ObjCDuplicateClass
, HashCString
, EqualCString
>& duplicateSharedCacheClasses
,
2587 ObjCOptimizerImage
& image
) {
2589 BuilderLoadedImage
& li
= *image
.loadedImage
;
2590 OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenClasses
= image
.seenClasses
;
2592 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2593 const uint32_t pointerSize
= ma
->pointerSize();
2594 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2596 // Keep track of any missing weak imports so that we can tell if the superclasses are nil
2597 // This is necessary as the shared cache will be marked with 'no missing weak superclasses'
2598 // and so we need to continue to satisfy that constraint
2599 __block Map
<uint64_t, bool, HashUInt64
, EqualUInt64
> missingWeakImportOffets
;
2600 if (li
.hasMissingWeakImports
) {
2601 if (ma
->hasChainedFixups()) {
2602 const Image
* closureImage
= image
.writer
->currentImage();
2604 const Array
<Image::ResolvedSymbolTarget
> targets
= closureImage
->chainedTargets();
2605 if ( !targets
.empty() ) {
2606 ma
->withChainStarts(_diag
, closureImage
->chainedStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) {
2607 ma
->forEachFixupInAllChains(_diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
,
2608 const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
2609 uint64_t fixupOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
2610 uint32_t bindOrdinal
;
2611 if ( fixupLoc
->isBind(segInfo
->pointer_format
, bindOrdinal
) ) {
2612 if ( bindOrdinal
< targets
.count() ) {
2613 const Image::ResolvedSymbolTarget
& target
= targets
[bindOrdinal
];
2614 if ( (target
.absolute
.kind
== Image::ResolvedSymbolTarget::kindAbsolute
) && (target
.absolute
.value
== 0) )
2615 missingWeakImportOffets
[fixupOffset
] = true;
2618 image
.diag
.error("out of range bind ordinal %d (max %lu)", bindOrdinal
, targets
.count());
2624 if (image
.diag
.hasError())
2628 forEachBind(li
, ^(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
) {
2629 if ( (target
.absolute
.kind
== Image::ResolvedSymbolTarget::kindAbsolute
) && (target
.absolute
.value
== 0) )
2630 missingWeakImportOffets
[runtimeOffset
] = true;
2631 }, ^(const char *strongSymbolName
) {
2636 // Class names and data may be in different sections depending on swift vs objc so handle multiple sections
2637 __block
MachOAnalyzer::SectionCache
classNameSectionCache(ma
);
2638 __block
MachOAnalyzer::SectionCache
classSectionCache(ma
);
2640 ma
->forEachObjCClass(image
.diag
, li
.contentRebased
, ^(Diagnostics
&diag
, uint64_t classVMAddr
,
2641 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
2642 const MachOAnalyzer::ObjCClassInfo
&objcClass
, bool isMetaClass
) {
2643 if (isMetaClass
) return;
2645 // Make sure the superclass pointer is not nil
2646 uint64_t superclassRuntimeOffset
= classSuperclassVMAddr
- loadAddress
;
2647 if (missingWeakImportOffets
.find(superclassRuntimeOffset
) != missingWeakImportOffets
.end()) {
2648 diag
.error("Missing weak superclass");
2652 // Does this class need to be fixed up for stable Swift ABI.
2653 // Note the order matches the objc runtime in that we always do this fix before checking for dupes,
2654 // but after excluding classes with missing weak superclasses.
2655 if (objcClass
.isUnfixedBackwardDeployingStableSwift()) {
2656 // Class really is stable Swift, pretending to be pre-stable.
2657 // Fix its lie. This involves fixing the FAST bits on the class data value, so record that vmaddr
2658 image
.classStableSwiftFixups
.push_back(classDataVMAddr
- loadAddress
);
2661 // Get the section for the name
2662 const char* className
= nullptr;
2663 MachOAnalyzer::PrintableStringResult classNameResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2664 __block
uint64_t classNameSectionStartVMAddr
= 0;
2665 auto classNameSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2666 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2667 if (sectInfo
.sectSize
>= Image::ObjCClassNameImageOffset::maximumOffset
) {
2671 // We use 32-bit offsets so make sure the section is no larger than that.
2672 uint64_t classNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2673 if (classNameVMOffset
>= (1ULL << 32)) {
2677 classNameSectionStartVMAddr
= sectInfo
.sectAddr
;
2680 uint64_t classNameVMAddr
= objcClass
.nameVMAddr(pointerSize
);
2681 className
= ma
->getPrintableString(classNameVMAddr
, classNameResult
,
2682 &classNameSectionCache
, classNameSectionHandler
);
2684 if ( classNameResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2685 diag
.error("Invalid class name for objc optimisation");
2689 // If the class also exists in a shared cache image which is loaded, then objc
2690 // would have found that one, regardless of load order. So we can just skip this one.
2695 uint32_t count
= objcClassOpt
->getClassHeaderAndIndex(className
, cls
, hi
, index
);
2697 // exactly one matching class. Check if its loaded
2698 const dyld3::MachOAnalyzer
* sharedCacheMA
= getMachHeaderFromObjCHeaderInfo(hi
, pointerSize
);
2699 if (sharedCacheImagesMap
.find(sharedCacheMA
) != sharedCacheImagesMap
.end()) {
2700 addDuplicateObjCClassWarning(className
, li
.path(), sharedCacheMA
->installName());
2702 // We have a duplicate class, so check if we've already got it in our map.
2703 if ( duplicateSharedCacheClasses
.find(className
) == duplicateSharedCacheClasses
.end() ) {
2704 // We haven't seen this one yet
2705 Image::ObjCDuplicateClass duplicateClass
;
2706 duplicateClass
.sharedCacheClassOptIndex
= index
;
2707 duplicateClass
.sharedCacheClassDuplicateIndex
= 0;
2708 image
.classSharedCacheDuplicates
.insert({ className
, duplicateClass
});
2712 else if (count
> 1) {
2713 // more than one matching class - find one that is loaded
2714 void *clslist
[count
];
2715 void *hilist
[count
];
2716 objcClassOpt
->getClassesAndHeaders(className
, clslist
, hilist
);
2717 for (uint32_t i
= 0; i
< count
; i
++) {
2718 const dyld3::MachOAnalyzer
* sharedCacheMA
= getMachHeaderFromObjCHeaderInfo(hilist
[i
], pointerSize
);
2719 if (sharedCacheImagesMap
.find(sharedCacheMA
) != sharedCacheImagesMap
.end()) {
2720 addDuplicateObjCClassWarning(className
, li
.path(), sharedCacheMA
->installName());
2722 // We have a duplicate class, so check if we've already got it in our map.
2723 if ( duplicateSharedCacheClasses
.find(className
) == duplicateSharedCacheClasses
.end() ) {
2724 // We haven't seen this one yet
2725 Image::ObjCDuplicateClass duplicateClass
;
2726 duplicateClass
.sharedCacheClassOptIndex
= index
;
2727 duplicateClass
.sharedCacheClassDuplicateIndex
= i
;
2728 image
.classSharedCacheDuplicates
.insert({ className
, duplicateClass
});
2737 // Get the section for the class itself
2738 __block
uint64_t classSectionStartVMAddr
= 0;
2739 auto classSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2740 // We only have 23-bits in ObjCClassImageOffset to index in to the classes
2741 if (sectInfo
.sectSize
> Image::ObjCClassImageOffset::maximumOffset
) {
2745 // We use 32-bit offsets so make sure the section is no larger than that.
2746 uint64_t classDatasVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2747 if (classDatasVMOffset
>= (1ULL << 32)) {
2751 classSectionStartVMAddr
= sectInfo
.sectAddr
;
2754 if (!classSectionCache
.findSectionForVMAddr(classVMAddr
, classSectionHandler
)) {
2755 diag
.error("Invalid class for objc optimisation");
2759 // Make sure we have an entry for our images offsets for later
2760 uint64_t classNameSectionVMOffset
= classNameSectionStartVMAddr
- loadAddress
;
2761 uint64_t classSectionVMOffset
= classSectionStartVMAddr
- loadAddress
;
2762 uint64_t hashTableVMOffsetsIndex
= 0;
2763 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2764 if ( (nameAndDataVMOffset
.first
== classNameSectionVMOffset
) && (nameAndDataVMOffset
.second
== classSectionVMOffset
) )
2766 ++hashTableVMOffsetsIndex
;
2769 if (hashTableVMOffsetsIndex
== image
.classesNameAndDataVMOffsets
.count()) {
2770 // Didn't find an image entry with this offset. Add one if we have space
2771 uint64_t totalHashTableImages
= image
.classesNameAndDataVMOffsets
.count() + _objcClassesHashTableImages
.count();
2772 if ( totalHashTableImages
== Image::ObjCClassNameImageOffset::maximumImageIndex
) {
2773 // No more space. We need to give up
2774 diag
.error("No more space for class hash table image");
2777 image
.classesNameAndDataVMOffsets
.push_back({ classNameSectionVMOffset
, classSectionVMOffset
});
2780 hashTableVMOffsetsIndex
+= _objcClassesHashTableImages
.count();
2782 uint64_t classNameOffset
= classNameVMAddr
- classNameSectionStartVMAddr
;
2783 uint64_t classDataOffset
= classVMAddr
- classSectionStartVMAddr
;
2785 closure::Image::ObjCClassNameImageOffset classNameTarget
;
2786 classNameTarget
.classNameImageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2787 classNameTarget
.classNameImageOffset
= (uint32_t)classNameOffset
;
2789 dyld3::closure::Image::ObjCClassImageOffset classDataTarget
;
2790 classDataTarget
.classData
.imageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2791 classDataTarget
.classData
.imageOffset
= (uint32_t)classDataOffset
;
2792 classDataTarget
.classData
.isDuplicate
= 0;
2794 seenClasses
.push_back({ classNameTarget
, classDataTarget
});
2798 void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t
* objcProtocolOpt
,
2799 const Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
>& sharedCacheImagesMap
,
2800 ObjCOptimizerImage
& image
) {
2802 BuilderLoadedImage
& li
= *image
.loadedImage
;
2803 OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenProtocols
= image
.seenProtocols
;
2805 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2806 const uint32_t pointerSize
= ma
->pointerSize();
2807 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2809 // Protocol names and data may be in different sections depending on swift vs objc so handle multiple sections
2810 __block
MachOAnalyzer::SectionCache
protocolNameSectionCache(ma
);
2811 __block
MachOAnalyzer::SectionCache
protocolSectionCache(ma
);
2813 ma
->forEachObjCProtocol(image
.diag
, li
.contentRebased
, ^(Diagnostics
&diag
, uint64_t protocolVMAddr
,
2814 const dyld3::MachOAnalyzer::ObjCProtocol
&objCProtocol
) {
2815 if ( objCProtocol
.requiresObjCReallocation
) {
2816 // We can't optimize this protocol as the runtime needs all fields to be present
2817 diag
.error("Protocol is too small to be optimized");
2820 if ( objCProtocol
.isaVMAddr
!= 0 ) {
2821 // We can't optimize this protocol if it has an ISA as we want to override it
2822 diag
.error("Protocol ISA cannot be non-zero");
2826 // Get the section for the name
2827 const char* protocolName
= nullptr;
2828 MachOAnalyzer::PrintableStringResult protocolNameResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2829 __block
uint64_t protocolNameSectionStartVMAddr
= 0;
2830 auto protocolNameSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2831 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2832 if (sectInfo
.sectSize
>= Image::ObjCClassNameImageOffset::maximumOffset
) {
2836 // We use 32-bit offsets so make sure the section is no larger than that.
2837 uint64_t protocolNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2838 if (protocolNameVMOffset
>= (1ULL << 32)) {
2842 protocolNameSectionStartVMAddr
= sectInfo
.sectAddr
;
2845 uint64_t protocolNameVMAddr
= objCProtocol
.nameVMAddr
;
2846 protocolName
= ma
->getPrintableString(protocolNameVMAddr
, protocolNameResult
,
2847 &protocolNameSectionCache
, protocolNameSectionHandler
);
2849 if ( protocolNameResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2850 diag
.error("Invalid protocol name for objc optimisation");
2854 // If the protocol also exists in a shared cache image which is loaded, then objc
2855 // would have found that one, regardless of load order. So we can just skip this one.
2859 uint32_t count
= objcProtocolOpt
->getClassAndHeader(protocolName
, cls
, hi
);
2861 // exactly one matching protocol. Check if its loaded
2862 if (sharedCacheImagesMap
.find(getMachHeaderFromObjCHeaderInfo(hi
, pointerSize
)) != sharedCacheImagesMap
.end())
2865 else if (count
> 1) {
2866 // more than one matching protocol - find one that is loaded
2867 void *clslist
[count
];
2868 void *hilist
[count
];
2869 objcProtocolOpt
->getClassesAndHeaders(protocolName
, clslist
, hilist
);
2870 for (uint32_t i
= 0; i
< count
; i
++) {
2871 if (sharedCacheImagesMap
.find(getMachHeaderFromObjCHeaderInfo(hilist
[i
], pointerSize
)) != sharedCacheImagesMap
.end())
2877 // Get the section for the protocol itself
2878 __block
uint64_t protocolSectionStartVMAddr
= 0;
2879 auto protocolSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2880 // We only have 23-bits in ObjCClassImageOffset to index in to the protocols
2881 if (sectInfo
.sectSize
> Image::ObjCClassImageOffset::maximumOffset
) {
2885 // We use 32-bit offsets so make sure the section is no larger than that.
2886 uint64_t protocolDatasVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2887 if (protocolDatasVMOffset
>= (1ULL << 32)) {
2891 protocolSectionStartVMAddr
= sectInfo
.sectAddr
;
2894 if (!protocolSectionCache
.findSectionForVMAddr(protocolVMAddr
, protocolSectionHandler
)) {
2895 diag
.error("Invalid protocol for objc optimisation");
2899 // Make sure we have an entry for our images offsets for later
2900 uint64_t protocolNameSectionVMOffset
= protocolNameSectionStartVMAddr
- loadAddress
;
2901 uint64_t protocolSectionVMOffset
= protocolSectionStartVMAddr
- loadAddress
;
2902 uint64_t hashTableVMOffsetsIndex
= 0;
2903 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2904 if ( (nameAndDataVMOffset
.first
== protocolNameSectionVMOffset
) && (nameAndDataVMOffset
.second
== protocolSectionVMOffset
) )
2906 ++hashTableVMOffsetsIndex
;
2909 if (hashTableVMOffsetsIndex
== image
.classesNameAndDataVMOffsets
.count()) {
2910 // Didn't find an image entry with this offset. Add one if we have space
2911 uint64_t totalHashTableImages
= image
.classesNameAndDataVMOffsets
.count() + _objcClassesHashTableImages
.count();
2912 if ( totalHashTableImages
== Image::ObjCClassNameImageOffset::maximumImageIndex
) {
2913 // No more space. We need to give up
2914 diag
.error("No more space for protocol hash table image");
2917 image
.classesNameAndDataVMOffsets
.push_back({ protocolNameSectionVMOffset
, protocolSectionVMOffset
});
2920 hashTableVMOffsetsIndex
+= _objcClassesHashTableImages
.count();
2922 uint64_t protocolNameOffset
= protocolNameVMAddr
- protocolNameSectionStartVMAddr
;
2923 uint64_t protocolDataOffset
= protocolVMAddr
- protocolSectionStartVMAddr
;
2925 closure::Image::ObjCClassNameImageOffset protocolNameTarget
;
2926 protocolNameTarget
.classNameImageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2927 protocolNameTarget
.classNameImageOffset
= (uint32_t)protocolNameOffset
;
2929 dyld3::closure::Image::ObjCClassImageOffset protocolDataTarget
;
2930 protocolDataTarget
.classData
.imageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2931 protocolDataTarget
.classData
.imageOffset
= (uint32_t)protocolDataOffset
;
2932 protocolDataTarget
.classData
.isDuplicate
= 0;
2934 seenProtocols
.push_back({ protocolNameTarget
, protocolDataTarget
});
2938 // used at launch by dyld when kernel has already mapped main executable
2939 const LaunchClosure
* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo
& fileInfo
, bool allowInsertFailures
)
2941 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_BUILD_CLOSURE
, 0, 0, 0);
2942 const mach_header
* mainMH
= (const mach_header
*)fileInfo
.fileContent
;
2943 // set up stack based storage for all arrays
2944 BuilderLoadedImage loadImagesStorage
[512];
2945 Image::LinkedImage dependenciesStorage
[512*8];
2946 InterposingTuple tuplesStorage
[64];
2947 Closure::PatchEntry cachePatchStorage
[64];
2948 const char* weakDefNameStorage
[64];
2949 _loadedImages
.setInitialStorage(loadImagesStorage
, 512);
2950 _dependencies
.setInitialStorage(dependenciesStorage
, 512*8);
2951 _interposingTuples
.setInitialStorage(tuplesStorage
, 64);
2952 _weakDefCacheOverrides
.setInitialStorage(cachePatchStorage
, 64);
2953 _weakDefsFromChainedBinds
.setInitialStorage(weakDefNameStorage
, 64);
2954 ArrayFinalizer
<BuilderLoadedImage
> scopedCleanup(_loadedImages
, ^(BuilderLoadedImage
& li
) { if (li
.unmapWhenDone
) {_fileSystem
.unloadFile(li
.loadedFileInfo
); li
.unmapWhenDone
=false;} });
2956 const MachOAnalyzer
* mainExecutable
= MachOAnalyzer::validMainExecutable(_diag
, mainMH
, fileInfo
.path
, fileInfo
.sliceLen
, _archs
, _platform
);
2957 if ( mainExecutable
== nullptr )
2959 if ( !mainExecutable
->isDynamicExecutable() ) {
2960 _diag
.error("not a main executable");
2963 _isLaunchClosure
= true;
2964 _allowMissingLazies
= true;
2968 // add main executable
2969 __block BuilderLoadedImage mainEntry
;
2970 mainEntry
.loadedFileInfo
= fileInfo
;
2971 mainEntry
.imageNum
= 0; // We can't fill this in until we've done inserted dylibs
2972 mainEntry
.unmapWhenDone
= false;
2973 mainEntry
.contentRebased
= false;
2974 mainEntry
.hasInits
= false;
2975 mainEntry
.markNeverUnload
= true;
2976 mainEntry
.rtldLocal
= false;
2977 mainEntry
.isBadImage
= false;
2978 mainEntry
.mustBuildClosure
= true;
2979 mainEntry
.hasMissingWeakImports
= false;
2980 mainEntry
.overrideImageNum
= 0;
2982 // Set the executable load path so that @executable_path can use it later
2983 _mainProgLoadPath
= fileInfo
.path
;
2985 // add any DYLD_INSERT_LIBRARIES
2986 _pathOverrides
.forEachInsertedDylib(^(const char* dylibPath
, bool &stop
) {
2987 LoadedImageChain chainMain
= { nullptr, mainEntry
};
2988 BuilderLoadedImage
* foundTopImage
;
2989 if ( !findImage(dylibPath
, chainMain
, foundTopImage
, LinkageType::kInserted
, 0, true) ) {
2990 if ( !allowInsertFailures
) {
2991 if ( _diag
.noError() )
2992 _diag
.error("could not load inserted dylib %s", dylibPath
);
2996 _diag
.clearError(); // FIXME add way to plumb back warning
3000 if ( _diag
.hasError() )
3003 _mainProgLoadIndex
= (uint32_t)_loadedImages
.count();
3004 mainEntry
.imageNum
= _startImageNum
+ _nextIndex
++;
3005 _loadedImages
.push_back(mainEntry
);
3007 // get mach_headers for all images needed to launch this main executable
3008 LoadedImageChain chainStart
= { nullptr, _loadedImages
[_mainProgLoadIndex
] };
3009 recursiveLoadDependents(chainStart
);
3010 if ( _diag
.hasError() )
3012 for (uint32_t i
=0; i
< _mainProgLoadIndex
; ++i
) {
3013 LoadedImageChain insertChainStart
= { nullptr, _loadedImages
[i
] };
3014 recursiveLoadDependents(insertChainStart
);
3015 if ( _diag
.hasError() )
3018 loadDanglingUpwardLinks();
3020 // If we have an on-disk image then we need all images which are dependent on the disk image to get a new
3021 // initializer order. Its not enough to just do the top level image as we may dlopen while in dlopen
3022 invalidateInitializerRoots();
3024 // now that everything loaded, set _libDyldImageNum and _libSystemImageNum
3025 for (BuilderLoadedImage
& li
: _loadedImages
) {
3026 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) )
3027 _libDyldImageNum
= li
.imageNum
;
3028 else if ( strcmp(li
.path(), "/usr/lib/libSystem.B.dylib") == 0 )
3029 _libSystemImageNum
= li
.imageNum
;
3032 // only some images need to go into closure (non-rooted ones from dyld cache do not)
3033 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3034 for (BuilderLoadedImage
& li
: _loadedImages
) {
3035 if ( li
.mustBuildClosure
) {
3036 writers
.push_back(ImageWriter());
3037 buildImage(writers
.back(), li
);
3038 if ( _diag
.hasError() )
3043 bool optimizedObjC
= optimizeObjC(writers
);
3045 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3046 for (uintptr_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
3047 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
3048 if ( li
.mustBuildClosure
) {
3049 computeInitOrder(writers
[writerIndex
], (uint32_t)imageIndex
);
3054 // combine all Image objects into one ImageArray
3055 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3056 for (ImageWriter
& writer
: writers
) {
3057 imageArrayWriter
.appendImage(writer
.finalize());
3058 writer
.deallocate();
3060 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3062 // merge ImageArray object into LaunchClosure object
3063 __block LaunchClosureWriter
closureWriter(imageArray
);
3065 if (optimizedObjC
) {
3066 if (!_objcSelectorsHashTable
.empty())
3067 closureWriter
.setObjCSelectorInfo(_objcSelectorsHashTable
, _objcSelectorsHashTableImages
);
3069 if (!_objcClassesHashTableImages
.empty()) {
3070 closureWriter
.setObjCClassAndProtocolInfo(_objcClassesHashTable
, _objcProtocolsHashTable
,
3071 _objcClassesHashTableImages
);
3074 if ( _objcDuplicateClassWarnings
!= nullptr ) {
3075 _objcDuplicateClassWarnings
->forEachPath(^(const char* warning
) {
3076 closureWriter
.addWarning(Closure::Warning::duplicateObjCClass
, warning
);
3080 if (!_objcClassesDuplicatesHashTable
.empty())
3081 closureWriter
.setObjCDuplicateClassesInfo(_objcClassesDuplicatesHashTable
);
3084 // record shared cache info
3085 if ( _dyldCache
!= nullptr ) {
3086 // record cache UUID
3088 _dyldCache
->getUUID(cacheUUID
);
3089 closureWriter
.setDyldCacheUUID(cacheUUID
);
3091 // record any cache patching needed because of dylib overriding cache
3092 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3093 if ( li
.overrideImageNum
!= 0 ) {
3094 uint32_t imageIndex
= li
.overrideImageNum
- (uint32_t)_dyldImageArray
->startImageNum();
3095 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, _dyldCache
->patchableExportCount(imageIndex
));
3096 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
3097 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
3099 //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
3100 _dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* symbolName
) {
3101 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
3102 Diagnostics patchDiag
;
3103 Closure::PatchEntry patch
;
3104 patch
.overriddenDylibInCache
= li
.overrideImageNum
;
3105 patch
.exportCacheOffset
= cacheOffsetOfImpl
;
3106 if ( li
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3107 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3108 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3109 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3110 patch
.replacement
.image
.offset
= foundInfo
.value
;
3113 // this means the symbol is missing in the cache override dylib, so set any uses to NULL
3114 patch
.replacement
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
3115 patch
.replacement
.absolute
.value
= 0;
3117 patches
.push_back(patch
);
3119 closureWriter
.addCachePatches(patches
);
3123 // handle any extra weak-def coalescing needed by chained fixups
3124 if ( !_weakDefsFromChainedBinds
.empty() ) {
3125 for (const char* symbolName
: _weakDefsFromChainedBinds
) {
3126 Image::ResolvedSymbolTarget cacheOverrideTarget
;
3127 bool haveCacheOverride
= false;
3128 bool foundCachOverrideIsWeakDef
= false;
3129 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3130 if ( !li
.loadAddress()->hasWeakDefs() )
3132 Image::ResolvedSymbolTarget target
;
3133 ResolvedTargetInfo targetInfo
;
3134 if ( findSymbolInImage(li
.loadAddress(), symbolName
, 0, false, false, target
, targetInfo
) ) {
3135 if ( li
.loadAddress()->inDyldCache() ) {
3136 if ( haveCacheOverride
) {
3137 Closure::PatchEntry patch
;
3138 patch
.exportCacheOffset
= (uint32_t)target
.sharedCache
.offset
;
3139 patch
.overriddenDylibInCache
= li
.imageNum
;
3140 patch
.replacement
= cacheOverrideTarget
;
3141 _weakDefCacheOverrides
.push_back(patch
);
3144 // found first in cached dylib, so no need to patch cache for this symbol
3149 // found image that exports this symbol and is not in cache
3150 if ( !haveCacheOverride
|| (foundCachOverrideIsWeakDef
&& !targetInfo
.isWeakDef
) ) {
3151 // update cache to use this symbol if it if first found or it is first non-weak found
3152 cacheOverrideTarget
= target
;
3153 foundCachOverrideIsWeakDef
= targetInfo
.isWeakDef
;
3154 haveCacheOverride
= true;
3162 // record any cache patching needed because weak-def C++ symbols override dyld cache
3163 if ( !_weakDefCacheOverrides
.empty() )
3164 closureWriter
.addCachePatches(_weakDefCacheOverrides
);
3168 #if __IPHONE_OS_VERSION_MIN_REQUIRED
3169 // if closure is built on-device for iOS, then record boot UUID
3170 char bootSessionUUID
[256] = { 0 };
3171 size_t bootSize
= sizeof(bootSessionUUID
);
3172 if ( sysctlbyname("kern.bootsessionuuid", bootSessionUUID
, &bootSize
, NULL
, 0) == 0 )
3173 closureWriter
.setBootUUID(bootSessionUUID
);
3176 // record any interposing info
3177 imageArray
->forEachImage(^(const Image
* image
, bool &stop
) {
3178 if ( !image
->inDyldCache() )
3179 addInterposingTuples(closureWriter
, image
, findLoadedImage(image
->imageNum()).loadAddress());
3182 // modify fixups in contained Images by applying interposing tuples
3183 closureWriter
.applyInterposing((const LaunchClosure
*)closureWriter
.currentTypedBytes());
3186 closureWriter
.setUsedAtPaths(_atPathUsed
);
3187 closureWriter
.setUsedFallbackPaths(_fallbackPathUsed
);
3188 closureWriter
.setHasInsertedLibraries(_mainProgLoadIndex
> 0);
3189 closureWriter
.setInitImageCount((uint32_t)_loadedImages
.count());
3191 // add other closure attributes
3192 addClosureInfo(closureWriter
);
3195 const LaunchClosure
* result
= closureWriter
.finalize();
3196 imageArrayWriter
.deallocate();
3198 timer
.setData4(dyld3::DyldTimingBuildClosure::LaunchClosure_Built
);
3203 // used by libdyld for dlopen()
3204 const DlopenClosure
* ClosureBuilder::makeDlopenClosure(const char* path
, const LaunchClosure
* mainClosure
, const Array
<LoadedImage
>& alreadyLoadedList
,
3205 closure::ImageNum callerImageNum
, bool noLoad
, bool forceBindLazies
, bool canUseSharedCacheClosure
, closure::ImageNum
* topImageNum
)
3207 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_BUILD_CLOSURE
, 0, 0, 0);
3208 // set up stack based storage for all arrays
3209 BuilderLoadedImage loadImagesStorage
[300];
3210 Image::LinkedImage dependenciesStorage
[128];
3211 Closure::PatchEntry cachePatchStorage
[64];
3212 _loadedImages
.setInitialStorage(loadImagesStorage
, 300);
3213 _dependencies
.setInitialStorage(dependenciesStorage
, 128);
3214 _weakDefCacheOverrides
.setInitialStorage(cachePatchStorage
, 64);
3215 ArrayFinalizer
<BuilderLoadedImage
> scopedCleanup(_loadedImages
, ^(BuilderLoadedImage
& li
) { if (li
.unmapWhenDone
) {_fileSystem
.unloadFile(li
.loadedFileInfo
); li
.unmapWhenDone
=false;} });
3217 // fill in builder array from already loaded images
3218 bool cachedDylibsExpectedOnDisk
= _dyldCache
? _dyldCache
->header
.dylibsExpectedOnDisk
: true;
3219 uintptr_t callerImageIndex
= UINTPTR_MAX
;
3220 for (const LoadedImage
& ali
: alreadyLoadedList
) {
3221 const Image
* image
= ali
.image();
3222 const MachOAnalyzer
* ma
= (MachOAnalyzer
*)(ali
.loadedAddress());
3223 bool inDyldCache
= ma
->inDyldCache();
3224 BuilderLoadedImage entry
;
3225 ImageNum overrideImageNum
;
3226 entry
.loadedFileInfo
.path
= image
->path();
3227 entry
.loadedFileInfo
.fileContent
= ma
;
3228 entry
.loadedFileInfo
.sliceOffset
= 0;
3229 entry
.loadedFileInfo
.inode
= 0;
3230 entry
.loadedFileInfo
.mtime
= 0;
3231 entry
.imageNum
= image
->imageNum();
3232 entry
.dependents
= image
->dependentsArray();
3233 entry
.unmapWhenDone
= false;
3234 entry
.contentRebased
= inDyldCache
;
3235 entry
.hasInits
= false;
3236 entry
.markNeverUnload
= image
->neverUnload();
3237 entry
.rtldLocal
= ali
.hideFromFlatSearch();
3238 entry
.isBadImage
= false;
3239 entry
.mustBuildClosure
= false;
3240 entry
.hasMissingWeakImports
= false;
3241 entry
.overrideImageNum
= 0;
3242 if ( !inDyldCache
&& image
->isOverrideOfDyldCacheImage(overrideImageNum
) ) {
3243 entry
.overrideImageNum
= overrideImageNum
;
3244 canUseSharedCacheClosure
= false;
3246 if ( !inDyldCache
|| cachedDylibsExpectedOnDisk
)
3247 image
->hasFileModTimeAndInode(entry
.loadedFileInfo
.inode
, entry
.loadedFileInfo
.mtime
);
3248 if ( entry
.imageNum
== callerImageNum
)
3249 callerImageIndex
= _loadedImages
.count();
3250 _loadedImages
.push_back(entry
);
3252 _alreadyInitedIndex
= (uint32_t)_loadedImages
.count();
3254 // find main executable (may be needed for @executable_path)
3255 _isLaunchClosure
= false;
3256 for (uint32_t i
=0; i
< alreadyLoadedList
.count(); ++i
) {
3257 if ( _loadedImages
[i
].loadAddress()->isMainExecutable() ) {
3258 _mainProgLoadIndex
= i
;
3259 _mainProgLoadPath
= _loadedImages
[i
].path();
3264 // We can't use an existing dlopen closure if the main closure had interposing tuples
3265 if (canUseSharedCacheClosure
) {
3266 if (mainClosure
->hasInterposings())
3267 canUseSharedCacheClosure
= false;
3270 // add top level dylib being dlopen()ed
3271 BuilderLoadedImage
* foundTopImage
;
3273 // @rpath has caller's LC_PRATH, then main executable's LC_RPATH
3274 BuilderLoadedImage
& callerImage
= (callerImageIndex
!= UINTPTR_MAX
) ? _loadedImages
[callerImageIndex
] : _loadedImages
[_mainProgLoadIndex
];
3275 LoadedImageChain chainCaller
= { nullptr, callerImage
};
3276 LoadedImageChain chainMain
= { &chainCaller
, _loadedImages
[_mainProgLoadIndex
] };
3277 if ( !findImage(path
, chainMain
, foundTopImage
, LinkageType::kDynamic
, 0, canUseSharedCacheClosure
) ) {
3278 // If we didn't find the image, it might be a symlink to something in the dyld cache that is not on disk
3279 if ( (_dyldCache
!= nullptr) && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
3280 char resolvedPath
[PATH_MAX
];
3281 if ( _fileSystem
.getRealPath(path
, resolvedPath
) ) {
3283 if ( !findImage(resolvedPath
, chainMain
, foundTopImage
, LinkageType::kDynamic
, 0, canUseSharedCacheClosure
) ) {
3287 // We didn't find a new path from realpath
3291 // cached dylibs on disk, so don't call realpath() again, it would have been found first call to findImage()
3296 // exit early in RTLD_NOLOAD mode
3298 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_NoLoad
);
3299 // if no new images added to _loadedImages, then requested path was already loaded
3300 if ( (uint32_t)_loadedImages
.count() == _alreadyInitedIndex
)
3301 *topImageNum
= foundTopImage
->imageNum
;
3307 // fast path if roots are not allowed and target is in dyld cache or is other
3308 if ( (_dyldCache
!= nullptr) && (_dyldCache
->header
.cacheType
== kDyldSharedCacheTypeProduction
) ) {
3309 if ( foundTopImage
->imageNum
< closure::kFirstLaunchClosureImageNum
) {
3310 if (foundTopImage
->imageNum
< closure::kLastDyldCacheImageNum
)
3311 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
);
3313 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
);
3314 *topImageNum
= foundTopImage
->imageNum
;
3319 // recursive load dependents
3320 // @rpath for stuff top dylib depends on uses LC_RPATH from caller, main exe, and dylib being dlopen()ed
3321 LoadedImageChain chainTopDylib
= { &chainMain
, *foundTopImage
};
3322 recursiveLoadDependents(chainTopDylib
, canUseSharedCacheClosure
);
3323 if ( _diag
.hasError() )
3325 loadDanglingUpwardLinks(canUseSharedCacheClosure
);
3326 if ( _diag
.hasError() )
3329 // RTLD_NOW means fail the dlopen() if a symbol cannot be bound
3330 _allowMissingLazies
= !forceBindLazies
;
3332 // only some images need to go into closure (ones from dyld cache do not, unless the cache format changed)
3333 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3334 if ( _foundNonCachedImage
|| _foundDyldCacheRoots
) {
3335 // If we have an on-disk image then we need all images which are dependent on the disk image to get a new
3336 // initializer order. Its not enough to just do the top level image as we may dlopen while in dlopen
3337 invalidateInitializerRoots();
3339 for (uintptr_t loadedImageIndex
= 0; loadedImageIndex
!= _loadedImages
.count(); ++loadedImageIndex
) {
3340 BuilderLoadedImage
& li
= _loadedImages
[loadedImageIndex
];
3341 if ( li
.mustBuildClosure
) {
3342 writers
.push_back(ImageWriter());
3343 buildImage(writers
.back(), li
);
3344 if ( _diag
.hasError() )
3349 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3350 for (uintptr_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
3351 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
3352 if ( li
.mustBuildClosure
) {
3353 computeInitOrder(writers
[writerIndex
], (uint32_t)imageIndex
);
3358 if ( _diag
.hasError() )
3361 // check if top image loaded is in shared cache along with everything it depends on
3362 *topImageNum
= foundTopImage
->imageNum
;
3363 if ( _foundNonCachedImage
|| _foundDyldCacheRoots
) {
3364 if ( canUseSharedCacheClosure
&& ( foundTopImage
->imageNum
< closure::kFirstLaunchClosureImageNum
) ) {
3365 // We used a shared cache built closure, but now discovered roots. We need to try again
3367 return sRetryDlopenClosure
;
3370 if (foundTopImage
->imageNum
< closure::kLastDyldCacheImageNum
)
3371 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
);
3373 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
);
3377 // combine all Image objects into one ImageArray
3378 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3379 for (ImageWriter
& writer
: writers
) {
3380 imageArrayWriter
.appendImage(writer
.finalize());
3381 writer
.deallocate();
3383 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3385 // merge ImageArray object into LaunchClosure object
3386 DlopenClosureWriter
closureWriter(imageArray
);
3388 // add other closure attributes
3389 closureWriter
.setTopImageNum(foundTopImage
->imageNum
);
3391 // record any cache patching needed because of dylib overriding cache
3392 if ( _dyldCache
!= nullptr ) {
3393 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3394 if ( (li
.overrideImageNum
!= 0) && (li
.imageNum
>= _startImageNum
) ) {
3395 const Image
* cacheImage
= _dyldImageArray
->imageForNum(li
.overrideImageNum
);
3396 uint32_t imageIndex
= cacheImage
->imageNum() - (uint32_t)_dyldCache
->cachedDylibsImageArray()->startImageNum();
3397 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, _dyldCache
->patchableExportCount(imageIndex
));
3398 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
3399 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
3401 //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
3402 _dyldCache
->forEachPatchableExport(imageIndex
,
3403 ^(uint32_t cacheOffsetOfImpl
, const char* symbolName
) {
3404 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
3405 Diagnostics patchDiag
;
3406 Closure::PatchEntry patch
;
3407 patch
.overriddenDylibInCache
= li
.overrideImageNum
;
3408 patch
.exportCacheOffset
= cacheOffsetOfImpl
;
3409 if ( li
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3410 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3411 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3412 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3413 patch
.replacement
.image
.offset
= foundInfo
.value
;
3416 patch
.replacement
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
3417 patch
.replacement
.absolute
.value
= 0;
3419 patches
.push_back(patch
);
3421 closureWriter
.addCachePatches(patches
);
3426 // modify fixups in contained Images by applying interposing tuples
3427 closureWriter
.applyInterposing(mainClosure
);
3429 // Dlopen's should never keep track of missing paths as we don't cache these closures.
3430 assert(_mustBeMissingPaths
== nullptr);
3432 // make final DlopenClosure object
3433 const DlopenClosure
* result
= closureWriter
.finalize();
3434 imageArrayWriter
.deallocate();
3435 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_Built
);
3440 // used by dyld_closure_util
3441 const LaunchClosure
* ClosureBuilder::makeLaunchClosure(const char* mainPath
, bool allowInsertFailures
)
3443 char realerPath
[MAXPATHLEN
];
3444 closure::LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, mainPath
, _archs
, _platform
, realerPath
);
3445 const MachOAnalyzer
* mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
3446 loadedFileInfo
.path
= mainPath
;
3447 if (_diag
.hasError())
3449 if (mh
== nullptr) {
3450 _diag
.error("could not load file");
3453 if (!mh
->isDynamicExecutable()) {
3454 _diag
.error("file is not an executable");
3457 const_cast<PathOverrides
*>(&_pathOverrides
)->setMainExecutable(mh
, mainPath
);
3458 const LaunchClosure
* launchClosure
= makeLaunchClosure(loadedFileInfo
, allowInsertFailures
);
3459 loadedFileInfo
.unload(loadedFileInfo
);
3460 return launchClosure
;
3463 void ClosureBuilder::setDyldCacheInvalidFormatVersion() {
3464 _dyldCacheInvalidFormatVersion
= true;
3468 // used by dyld shared cache builder
3469 const ImageArray
* ClosureBuilder::makeDyldCacheImageArray(bool customerCache
, const Array
<CachedDylibInfo
>& dylibs
, const Array
<CachedDylibAlias
>& aliases
)
3471 // because this is run in cache builder using dispatch_apply() there is minimal stack space
3472 // so set up storage for all arrays to be vm_allocated
3473 uintptr_t maxImageCount
= dylibs
.count() + 16;
3474 _loadedImages
.reserve(maxImageCount
);
3475 _dependencies
.reserve(maxImageCount
*16);
3477 _makingDyldCacheImages
= true;
3478 _allowMissingLazies
= false;
3479 _makingCustomerCache
= customerCache
;
3480 _aliases
= &aliases
;
3482 // build _loadedImages[] with every dylib in cache
3483 __block ImageNum imageNum
= _startImageNum
;
3484 for (const CachedDylibInfo
& aDylibInfo
: dylibs
) {
3485 BuilderLoadedImage entry
;
3486 entry
.loadedFileInfo
= aDylibInfo
.fileInfo
;
3487 entry
.imageNum
= imageNum
++;
3488 entry
.unmapWhenDone
= false;
3489 entry
.contentRebased
= false;
3490 entry
.hasInits
= false;
3491 entry
.markNeverUnload
= true;
3492 entry
.rtldLocal
= false;
3493 entry
.isBadImage
= false;
3494 entry
.mustBuildClosure
= false;
3495 entry
.hasMissingWeakImports
= false;
3496 entry
.overrideImageNum
= 0;
3497 _loadedImages
.push_back(entry
);
3500 // wire up dependencies between cached dylibs
3501 for (BuilderLoadedImage
& li
: _loadedImages
) {
3502 LoadedImageChain chainStart
= { nullptr, li
};
3503 recursiveLoadDependents(chainStart
);
3504 if ( _diag
.hasError() )
3507 assert(_loadedImages
.count() == dylibs
.count());
3509 // create an ImageWriter for each cached dylib
3510 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3511 for (BuilderLoadedImage
& li
: _loadedImages
) {
3512 writers
.push_back(ImageWriter());
3513 buildImage(writers
.back(), li
);
3516 // add initializer order into each dylib
3517 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3518 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3519 uint32_t index
= li
.imageNum
- _startImageNum
;
3520 computeInitOrder(writers
[index
], index
);
3523 // combine all Image objects into one ImageArray
3524 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3525 for (ImageWriter
& writer
: writers
) {
3526 imageArrayWriter
.appendImage(writer
.finalize());
3527 writer
.deallocate();
3529 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3535 #if BUILDING_CACHE_BUILDER
3536 const ImageArray
* ClosureBuilder::makeOtherDylibsImageArray(const Array
<LoadedFileInfo
>& otherDylibs
, uint32_t cachedDylibsCount
)
3538 // because this is run in cache builder using dispatch_apply() there is minimal stack space
3539 // so set up storage for all arrays to be vm_allocated
3540 uintptr_t maxImageCount
= otherDylibs
.count() + cachedDylibsCount
+ 128;
3541 _loadedImages
.reserve(maxImageCount
);
3542 _dependencies
.reserve(maxImageCount
*16);
3544 // build _loadedImages[] with every dylib in cache, followed by others
3546 for (const LoadedFileInfo
& aDylibInfo
: otherDylibs
) {
3547 BuilderLoadedImage entry
;
3548 entry
.loadedFileInfo
= aDylibInfo
;
3549 entry
.imageNum
= _startImageNum
+ _nextIndex
++;
3550 entry
.unmapWhenDone
= false;
3551 entry
.contentRebased
= false;
3552 entry
.hasInits
= false;
3553 entry
.markNeverUnload
= false;
3554 entry
.rtldLocal
= false;
3555 entry
.isBadImage
= false;
3556 entry
.mustBuildClosure
= false;
3557 entry
.hasMissingWeakImports
= false;
3558 entry
.overrideImageNum
= 0;
3559 _loadedImages
.push_back(entry
);
3562 // wire up dependencies between cached dylibs
3563 // Note, _loadedImages can grow when we call recursiveLoadDependents so we need
3564 // to check the count on each iteration.
3565 for (uint64_t index
= 0; index
!= _loadedImages
.count(); ++index
) {
3566 BuilderLoadedImage
& li
= _loadedImages
[index
];
3567 LoadedImageChain chainStart
= { nullptr, li
};
3568 recursiveLoadDependents(chainStart
);
3569 if ( _diag
.hasError() ) {
3570 _diag
.warning("while building dlopen closure for %s: %s", li
.loadedFileInfo
.path
, _diag
.errorMessage().c_str());
3571 //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
3573 li
.isBadImage
= true; // mark bad
3577 auto invalidateBadImages
= [&]() {
3578 // Invalidate images with bad dependencies
3580 bool madeChange
= false;
3581 for (BuilderLoadedImage
& li
: _loadedImages
) {
3582 if (li
.isBadImage
) {
3583 // Already invalidated
3586 for (Image::LinkedImage depIndex
: li
.dependents
) {
3587 if ( depIndex
.imageNum() == kMissingWeakLinkedImage
)
3589 if ( depIndex
.imageNum() >= dyld3::closure::kLastDyldCacheImageNum
) {
3590 // dlopen closures can only depend on the shared cache. This is because if foo.dylib links bar.dylib
3591 // and bar.dylib is loaded in to the launch closure, then the dlopen closure for foo.dylib wouldn't see
3592 // bar.dylib at the image num in the launch closure
3593 _diag
.warning("while building dlopen closure for %s: dependent dylib is not from shared cache", li
.loadedFileInfo
.path
);
3594 li
.isBadImage
= true; // mark bad
3598 BuilderLoadedImage
& depImage
= findLoadedImage(depIndex
.imageNum());
3599 if (depImage
.isBadImage
) {
3600 _diag
.warning("while building dlopen closure for %s: dependent dylib had error", li
.loadedFileInfo
.path
);
3601 li
.isBadImage
= true; // mark bad
3611 invalidateBadImages();
3613 // create an ImageWriter for each cached dylib
3614 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3615 for (BuilderLoadedImage
& li
: _loadedImages
) {
3616 if ( li
.isBadImage
) {
3617 writers
.push_back(ImageWriter());
3618 writers
.back().setInvalid();
3621 if ( li
.imageNum
< dyld3::closure::kLastDyldCacheImageNum
)
3623 writers
.push_back(ImageWriter());
3624 buildImage(writers
.back(), li
);
3625 if ( _diag
.hasError() ) {
3626 _diag
.warning("while building dlopen closure for %s: %s", li
.loadedFileInfo
.path
, _diag
.errorMessage().c_str());
3627 //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
3629 li
.isBadImage
= true; // mark bad
3630 writers
.back().setInvalid();
3634 invalidateBadImages();
3636 // add initializer order into each dylib
3637 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3638 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3639 if ( li
.imageNum
< dyld3::closure::kLastDyldCacheImageNum
)
3643 uint32_t index
= li
.imageNum
- _startImageNum
;
3644 computeInitOrder(writers
[index
], index
);
3647 // combine all Image objects into one ImageArray
3648 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3649 for (ImageWriter
& writer
: writers
) {
3650 imageArrayWriter
.appendImage(writer
.finalize());
3651 writer
.deallocate();
3653 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3660 bool ClosureBuilder::inLoadedImageArray(const Array
<LoadedImage
>& loadedList
, ImageNum imageNum
)
3662 for (const LoadedImage
& ali
: loadedList
) {
3663 if ( ali
.image()->representsImageNum(imageNum
) )
3669 void ClosureBuilder::buildLoadOrderRecurse(Array
<LoadedImage
>& loadedList
, const Array
<const ImageArray
*>& imagesArrays
, const Image
* image
)
3671 // breadth first load
3672 STACK_ALLOC_ARRAY(const Image
*, needToRecurse
, 256);
3673 image
->forEachDependentImage(^(uint32_t dependentIndex
, dyld3::closure::Image::LinkKind kind
, ImageNum depImageNum
, bool &stop
) {
3674 if ( !inLoadedImageArray(loadedList
, depImageNum
) ) {
3675 const Image
* depImage
= ImageArray::findImage(imagesArrays
, depImageNum
);
3676 loadedList
.push_back(LoadedImage::make(depImage
));
3677 needToRecurse
.push_back(depImage
);
3682 for (const Image
* img
: needToRecurse
) {
3683 buildLoadOrderRecurse(loadedList
, imagesArrays
, img
);
3687 void ClosureBuilder::buildLoadOrder(Array
<LoadedImage
>& loadedList
, const Array
<const ImageArray
*>& imagesArrays
, const Closure
* toAdd
)
3689 const dyld3::closure::Image
* topImage
= ImageArray::findImage(imagesArrays
, toAdd
->topImage());
3690 loadedList
.push_back(LoadedImage::make(topImage
));
3691 buildLoadOrderRecurse(loadedList
, imagesArrays
, topImage
);
3696 //////////////////////////// ObjCStringTable ////////////////////////////////////////
3698 template<typename PerfectHashT
, typename ImageOffsetT
>
3699 void ObjCStringTable::write(const PerfectHashT
& phash
, const Array
<std::pair
<const char*, ImageOffsetT
>>& strings
)
3701 ObjCSelectorOpt::StringTarget sentinel
= (ObjCSelectorOpt::StringTarget
)ImageOffsetT::sentinelValue
;
3703 capacity
= phash
.capacity
;
3704 occupied
= phash
.occupied
;
3705 shift
= phash
.shift
;
3707 sentinelTarget
= sentinel
;
3708 roundedTabSize
= std::max(phash
.mask
+1, 4U);
3712 for (uint32_t i
= 0; i
< 256; i
++) {
3713 scramble
[i
] = phash
.scramble
[i
];
3715 for (uint32_t i
= 0; i
< phash
.mask
+1; i
++) {
3716 tab
[i
] = phash
.tab
[i
];
3719 dyld3::Array
<StringTarget
> targetsArray
= targets();
3720 dyld3::Array
<StringHashCheckByte
> checkBytesArray
= checkBytes();
3722 // Set offsets to the sentinel
3723 for (uint32_t i
= 0; i
< phash
.capacity
; i
++) {
3724 targetsArray
[i
] = sentinel
;
3726 // Set checkbytes to 0
3727 for (uint32_t i
= 0; i
< phash
.capacity
; i
++) {
3728 checkBytesArray
[i
] = 0;
3731 // Set real string offsets and checkbytes
3732 for (const auto& s
: strings
) {
3733 assert(s
.second
.raw
!= sentinelTarget
);
3734 uint32_t h
= hash(s
.first
);
3735 targetsArray
[h
] = s
.second
.raw
;
3736 checkBytesArray
[h
] = checkbyte(s
.first
);
3740 //////////////////////////// ObjCClassOpt ////////////////////////////////////////
3743 template<typename PerfectHashT
, typename ImageOffsetT
, typename ClassesMapT
>
3744 void ObjCClassOpt::write(const PerfectHashT
& phash
, const Array
<std::pair
<const char*, ImageOffsetT
>>& strings
,
3745 const ClassesMapT
& classes
, uint32_t preCalculatedDuplicateCount
)
3747 ObjCStringTable::write(phash
, strings
);
3749 __block
dyld3::Array
<ClassTarget
> classOffsetsArray
= classOffsets();
3750 __block
dyld3::Array
<ClassTarget
> duplicateOffsetsArray
= duplicateOffsets(preCalculatedDuplicateCount
);
3752 // Set class offsets to 0
3753 for (uint32_t i
= 0; i
< capacity
; i
++) {
3754 classOffsetsArray
[i
].raw
= dyld3::closure::Image::ObjCImageOffset::sentinelValue
;
3757 classes
.forEachEntry(^(const char *const &key
, const Image::ObjCClassImageOffset
**values
, uint64_t valuesCount
) {
3758 uint32_t keyIndex
= getIndex(key
);
3759 assert(keyIndex
!= indexNotFound
);
3760 assert(classOffsetsArray
[keyIndex
].raw
== dyld3::closure::Image::ObjCImageOffset::sentinelValue
);
3762 if (valuesCount
== 1) {
3763 // Only one entry so write it in to the class offsets directly
3764 Image::ObjCClassImageOffset classImageOffset
= *(values
[0]);
3765 assert(classImageOffset
.classData
.isDuplicate
== 0);
3766 classOffsetsArray
[keyIndex
] = classImageOffset
;
3770 // We have more than one value. We add a placeholder to the class offsets which tells us the head
3771 // of the linked list of classes in the duplicates array
3772 uint32_t dest
= duplicateCount();
3773 duplicateCount() += valuesCount
;
3775 Image::ObjCClassImageOffset classImagePlaceholder
;
3776 assert(valuesCount
< (1 << 8));
3777 classImagePlaceholder
.duplicateData
.count
= (uint32_t)valuesCount
;
3778 classImagePlaceholder
.duplicateData
.index
= dest
;
3779 classImagePlaceholder
.duplicateData
.isDuplicate
= 1;
3780 classOffsetsArray
[keyIndex
] = classImagePlaceholder
;
3782 for (uint64_t i
= 0; i
!= valuesCount
; ++i
) {
3783 Image::ObjCClassImageOffset classImageOffset
= *(values
[i
]);
3784 assert(classImageOffset
.classData
.isDuplicate
== 0);
3785 duplicateOffsetsArray
.push_back(classImageOffset
);
3790 } // namespace closure
3791 } // namespace dyld3