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 ) {
439 strlcpy(fixedPath
, loadedImage
.path(), PATH_MAX
);
440 char* lastSlash
= strrchr(fixedPath
, '/');
441 if ( lastSlash
!= nullptr ) {
442 strcpy(lastSlash
+1, &loadPath
[13]);
446 else if ( fromLCRPATH
&& (strcmp(loadPath
, "@loader_path") == 0) ) {
447 // <rdar://problem/52881387> in LC_RPATH allow "@loader_path" without trailing slash
448 strlcpy(fixedPath
, loadedImage
.path(), PATH_MAX
);
449 char* lastSlash
= strrchr(fixedPath
, '/');
450 if ( lastSlash
!= nullptr ) {
459 bool ClosureBuilder::expandAtExecutablePath(const char* loadPath
, bool fromLCRPATH
, char fixedPath
[])
461 switch ( _atPathHandling
) {
464 case AtPath::onlyInRPaths
:
472 if ( strncmp(loadPath
, "@executable_path/", 17) == 0 ) {
473 strlcpy(fixedPath
, _mainProgLoadPath
, PATH_MAX
);
474 char* lastSlash
= strrchr(fixedPath
, '/');
475 if ( lastSlash
!= nullptr ) {
476 strcpy(lastSlash
+1, &loadPath
[17]);
480 else if ( fromLCRPATH
&& (strcmp(loadPath
, "@executable_path") == 0) ) {
481 // <rdar://problem/52881387> in LC_RPATH allow "@executable_path" without trailing slash
482 strlcpy(fixedPath
, _mainProgLoadPath
, PATH_MAX
);
483 char* lastSlash
= strrchr(fixedPath
, '/');
484 if ( lastSlash
!= nullptr ) {
493 void ClosureBuilder::forEachResolvedPathVar(const char* loadPath
, const LoadedImageChain
& forImageChain
,
494 bool implictRPath
, LinkageType linkageType
,
495 void (^handler
)(const char* possiblePath
, bool& stop
))
497 // don't expand @loader_path or @executable_path if disallowed
498 if ( (_atPathHandling
== AtPath::none
) && (loadPath
[0] == '@') && (loadPath
[1] != 'r') ) {
500 handler(loadPath
, stop
);
504 // quick out if not @ path or not implicit rpath
505 if ( !implictRPath
&& (loadPath
[0] != '@') ) {
507 handler(loadPath
, stop
);
511 // expand @loader_path
512 // Note this isn't supported for DYLD_INSERT_LIBRARIES
513 BLOCK_ACCCESSIBLE_ARRAY(char, tempPath
, PATH_MAX
); // read as: char tempPath[PATH_MAX];
514 if ( (linkageType
!= LinkageType::kInserted
) && expandAtLoaderPath(loadPath
, false, forImageChain
.image
, tempPath
) ) {
516 handler(tempPath
, stop
);
520 // expand @executable_path
521 // Note this is supported for DYLD_INSERT_LIBRARIES
522 if ( expandAtExecutablePath(loadPath
, false, tempPath
) ) {
524 handler(tempPath
, stop
);
529 // Note this isn't supported for DYLD_INSERT_LIBRARIES
530 const char* rpathTail
= nullptr;
531 char implicitRpathBuffer
[PATH_MAX
];
532 if ( linkageType
!= LinkageType::kInserted
) {
533 if ( strncmp(loadPath
, "@rpath/", 7) == 0 ) {
534 // note: rpathTail starts with '/'
535 rpathTail
= &loadPath
[6];
537 else if ( implictRPath
) {
538 // make rpathTail starts with '/'
539 strlcpy(implicitRpathBuffer
, "/", PATH_MAX
);
540 strlcat(implicitRpathBuffer
, loadPath
, PATH_MAX
);
541 rpathTail
= implicitRpathBuffer
;
544 if ( rpathTail
!= nullptr ) {
545 // rpath is expansion is technically a stack of rpath dirs built starting with main executable and pushing
546 // LC_RPATHS from each dylib as they are recursively loaded. Our imageChain represents that stack.
547 __block
bool done
= false;
548 for (const LoadedImageChain
* link
= &forImageChain
; (link
!= nullptr) && !done
; link
= link
->previous
) {
549 link
->image
.loadAddress()->forEachRPath(^(const char* rPath
, bool& stop
) {
550 // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.loadedFileInfo.path);
551 if ( expandAtLoaderPath(rPath
, true, link
->image
, tempPath
) || expandAtExecutablePath(rPath
, true, tempPath
) ) {
552 // @loader_path allowed and expended
553 strlcat(tempPath
, rpathTail
, PATH_MAX
);
554 handler(tempPath
, stop
);
556 else if ( rPath
[0] == '/' ) {
557 // LC_RPATH is an absolute path, not blocked by AtPath::none
558 strlcpy(tempPath
, rPath
, PATH_MAX
);
559 strlcat(tempPath
, rpathTail
, PATH_MAX
);
560 handler(tempPath
, stop
);
565 if ( _fileSystem
.fileExists(tempPath
) ) {
567 result
= strdup_temp(tempPath
);
570 // Don't add must be missing paths for dlopen as we don't cache dlopen closures
571 if (_isLaunchClosure
) {
572 addMustBeMissingPath(tempPath
);
583 handler(loadPath
, stop
);
586 const char* ClosureBuilder::strdup_temp(const char* path
)
588 if ( _tempPaths
== nullptr )
589 _tempPaths
= PathPool::allocate();
590 return _tempPaths
->add(path
);
593 void ClosureBuilder::addMustBeMissingPath(const char* path
)
595 //fprintf(stderr, "must be missing: %s\n", path);
596 if ( _mustBeMissingPaths
== nullptr )
597 _mustBeMissingPaths
= PathPool::allocate();
598 _mustBeMissingPaths
->add(path
);
601 void ClosureBuilder::addSkippedFile(const char* path
, uint64_t inode
, uint64_t mtime
)
603 _skippedFiles
.push_back({ strdup_temp(path
), inode
, mtime
});
606 ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(ImageNum imageNum
)
608 for (BuilderLoadedImage
& li
: _loadedImages
) {
609 if ( li
.imageNum
== imageNum
) {
613 for (BuilderLoadedImage
& li
: _loadedImages
) {
614 if ( li
.overrideImageNum
== imageNum
) {
618 assert(0 && "LoadedImage not found");
621 ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(const MachOAnalyzer
* mh
)
623 for (BuilderLoadedImage
& li
: _loadedImages
) {
624 if ( li
.loadAddress() == mh
) {
628 assert(0 && "LoadedImage not found");
631 const MachOAnalyzer
* ClosureBuilder::machOForImageNum(ImageNum imageNum
)
633 return findLoadedImage(imageNum
).loadAddress();
636 const MachOAnalyzer
* ClosureBuilder::findDependent(const MachOLoaded
* mh
, uint32_t depIndex
)
638 for (const BuilderLoadedImage
& li
: _loadedImages
) {
639 if ( li
.loadAddress() == mh
) {
641 // Bad image duting building group 1 closures, so the dependents array
642 // is potentially incomplete.
645 ImageNum childNum
= li
.dependents
[depIndex
].imageNum();
646 // This is typically something like a missing weak-dylib we are re-exporting a weak-import symbol from
647 if (childNum
== kMissingWeakLinkedImage
)
649 return machOForImageNum(childNum
);
655 ImageNum
ClosureBuilder::imageNumForMachO(const MachOAnalyzer
* mh
)
657 for (const BuilderLoadedImage
& li
: _loadedImages
) {
658 if ( li
.loadAddress() == mh
) {
662 assert(0 && "unknown mach-o");
666 void ClosureBuilder::recursiveLoadDependents(LoadedImageChain
& forImageChain
, bool canUseSharedCacheClosure
)
668 // if dependents is set, then we have already loaded this
669 if ( forImageChain
.image
.dependents
.begin() != nullptr )
672 uintptr_t startDepIndex
= _dependencies
.count();
674 __block
uint32_t depIndex
= 0;
675 forImageChain
.image
.loadAddress()->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
676 Image::LinkKind kind
= Image::LinkKind::regular
;
678 kind
= Image::LinkKind::weak
;
679 else if ( isReExport
)
680 kind
= Image::LinkKind::reExport
;
682 kind
= Image::LinkKind::upward
;
683 BuilderLoadedImage
* foundImage
;
684 if ( findImage(loadPath
, forImageChain
, foundImage
, LinkageType::kStatic
, compatVersion
, canUseSharedCacheClosure
) ) {
685 ImageNum foundImageNum
= foundImage
->imageNum
;
686 if ( _diag
.noError() )
687 _dependencies
.push_back(Image::LinkedImage(kind
, foundImageNum
));
690 _dependencies
.push_back(Image::LinkedImage(Image::LinkKind::weak
, kMissingWeakLinkedImage
));
691 // <rdar://problem/54387345> don't let an error loading weak dylib cause everything to fail
692 // _diag is checked after each dependent load, so if there is an error it was with loading the current dylib.
693 // Since it is a weak load, it is ok to ignore and and go on.
697 BLOCK_ACCCESSIBLE_ARRAY(char, extra
, 4096);
699 const char* targetLeaf
= strrchr(loadPath
, '/');
700 if ( targetLeaf
== nullptr )
701 targetLeaf
= loadPath
;
702 if ( _mustBeMissingPaths
!= nullptr ) {
703 strcpy(extra
, ", tried but didn't find: ");
704 _mustBeMissingPaths
->forEachPath(^(const char* aPath
) {
705 const char* aLeaf
= strrchr(aPath
, '/');
706 if ( aLeaf
== nullptr )
708 if ( strcmp(targetLeaf
, aLeaf
) == 0 ) {
709 strlcat(extra
, "'", 4096);
710 strlcat(extra
, aPath
, 4096);
711 strlcat(extra
, "' ", 4096);
715 if ( !_skippedFiles
.empty() ) {
716 strcpy(extra
, ", tried but invalid: ");
717 for (const SkippedFile
& skippedFile
: _skippedFiles
) {
718 const char* aPath
= skippedFile
.path
;
719 const char* aLeaf
= strrchr(aPath
, '/');
720 if ( aLeaf
== nullptr )
722 if ( strcmp(targetLeaf
, aLeaf
) == 0 ) {
723 strlcat(extra
, "'", 4096);
724 strlcat(extra
, aPath
, 4096);
725 strlcat(extra
, "' ", 4096);
729 if ( _diag
.hasError() ) {
730 #if BUILDING_CACHE_BUILDER
731 std::string errorMessageBuffer
= _diag
.errorMessage();
732 const char* msg
= errorMessageBuffer
.c_str();
734 const char* msg
= _diag
.errorMessage();
736 char msgCopy
[strlen(msg
)+4];
737 strcpy(msgCopy
, msg
);
738 _diag
.error("dependent dylib '%s' not found for '%s'. %s", loadPath
, forImageChain
.image
.path(), msgCopy
);
741 _diag
.error("dependent dylib '%s' not found for '%s'%s", loadPath
, forImageChain
.image
.path(), extra
);
743 if ( _launchErrorInfo
!= nullptr ) {
744 _launchErrorInfo
->kind
= DYLD_EXIT_REASON_DYLIB_MISSING
;
745 _launchErrorInfo
->clientOfDylibPath
= strdup_temp(forImageChain
.image
.path());
746 _launchErrorInfo
->targetDylibPath
= strdup_temp(loadPath
);
747 _launchErrorInfo
->symbol
= nullptr;
751 if ( _diag
.hasError() )
754 if ( _diag
.hasError() )
756 forImageChain
.image
.dependents
= _dependencies
.subArray(startDepIndex
, depIndex
);
758 // breadth first recurse
759 for (Image::LinkedImage dep
: forImageChain
.image
.dependents
) {
760 // don't recurse upwards
761 if ( dep
.kind() == Image::LinkKind::upward
)
763 // don't recurse down missing weak links
764 if ( (dep
.kind() == Image::LinkKind::weak
) && (dep
.imageNum() == kMissingWeakLinkedImage
) )
766 BuilderLoadedImage
& depLoadedImage
= findLoadedImage(dep
.imageNum());
767 LoadedImageChain chain
= { &forImageChain
, depLoadedImage
};
768 recursiveLoadDependents(chain
, canUseSharedCacheClosure
);
769 if ( _diag
.hasError() )
774 void ClosureBuilder::loadDanglingUpwardLinks(bool canUseSharedCacheClosure
)
778 danglingFixed
= false;
779 for (BuilderLoadedImage
& li
: _loadedImages
) {
780 if ( li
.dependents
.begin() == nullptr ) {
781 // this image has not have dependents set (probably a dangling upward link or referenced by upward link)
782 LoadedImageChain chain
= { nullptr, li
};
783 recursiveLoadDependents(chain
, canUseSharedCacheClosure
);
784 danglingFixed
= true;
788 } while (danglingFixed
&& _diag
.noError());
791 bool ClosureBuilder::overridableDylib(const BuilderLoadedImage
& forImage
)
793 // only set on dylibs in the dyld shared cache
794 if ( !_makingDyldCacheImages
)
797 // on macOS dylibs always override cache
798 if ( _platform
== Platform::macOS
)
801 // on embedded platforms with Internal cache, allow overrides
802 if ( !_makingCustomerCache
)
805 // embedded platform customer caches, no overrides
806 return false; // FIXME, allow libdispatch.dylib to be overridden
809 void ClosureBuilder::buildImage(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
811 const MachOAnalyzer
* macho
= forImage
.loadAddress();
813 writer
.setImageNum(forImage
.imageNum
);
816 writer
.setHasWeakDefs(macho
->hasWeakDefs());
817 writer
.setIsBundle(macho
->isBundle());
818 writer
.setIsDylib(macho
->isDylib());
819 writer
.setIs64(macho
->is64());
820 writer
.setIsExecutable(macho
->isMainExecutable());
821 writer
.setUses16KPages(macho
->uses16KPages());
822 writer
.setOverridableDylib(overridableDylib(forImage
));
823 writer
.setInDyldCache(macho
->inDyldCache());
824 if ( macho
->hasObjC() ) {
825 writer
.setHasObjC(true);
826 bool hasPlusLoads
= macho
->hasPlusLoadMethod(_diag
);
827 writer
.setHasPlusLoads(hasPlusLoads
);
829 forImage
.hasInits
= true;
832 writer
.setHasObjC(false);
833 writer
.setHasPlusLoads(false);
836 if ( forImage
.markNeverUnload
) {
837 writer
.setNeverUnload(true);
840 #if BUILDING_DYLD || BUILDING_LIBDYLD
841 if ( _foundDyldCacheRoots
) {
842 // If we had roots, then some images are potentially on-disk while others are
843 // being rebuilt for a new initializer order, but do not exist on disk
844 if ( macho
->inDyldCache() && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
845 // don't add file info for shared cache files mastered out of final file system
848 // file is either not in cache or is in cache but not mastered out
849 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
852 // shared cache not built by dyld or libdyld.dylib, so must be real file
853 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
856 if ( _platform
== Platform::macOS
|| MachOFile::isSimulatorPlatform(_platform
) ) {
857 if ( macho
->inDyldCache() && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
858 // don't add file info for shared cache files mastered out of final file system
861 // file is either not in cache or is in cache but not mastered out
862 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
866 // all other platforms, cache is built off-device, so inodes are not known
870 // add info on how to load image
871 if ( !macho
->inDyldCache() ) {
872 writer
.setMappingInfo(forImage
.loadedFileInfo
.sliceOffset
, macho
->mappedSize());
873 // add code signature, if signed
874 uint32_t codeSigFileOffset
;
875 uint32_t codeSigSize
;
876 if ( macho
->hasCodeSignature(codeSigFileOffset
, codeSigSize
) ) {
877 writer
.setCodeSignatureLocation(codeSigFileOffset
, codeSigSize
);
878 macho
->forEachCDHash(^(const uint8_t *cdHash
) {
879 writer
.addCDHash(cdHash
);
882 // add FairPlay encryption range if encrypted
883 uint32_t fairPlayFileOffset
;
884 uint32_t fairPlaySize
;
885 if ( macho
->isFairPlayEncrypted(fairPlayFileOffset
, fairPlaySize
) ) {
886 writer
.setFairPlayEncryptionRange(fairPlayFileOffset
, fairPlaySize
);
891 writer
.addPath(forImage
.path());
892 if ( _aliases
!= nullptr ) {
893 for (const CachedDylibAlias
& alias
: *_aliases
) {
894 if ( strcmp(alias
.realPath
, forImage
.path()) == 0 )
895 writer
.addPath(alias
.aliasPath
);
899 // set uuid, if has one
901 if ( macho
->getUuid(uuid
) )
902 writer
.setUUID(uuid
);
905 writer
.setDependents(forImage
.dependents
);
908 addSegments(writer
, macho
);
910 // record if this dylib overrides something in the cache
911 if ( forImage
.overrideImageNum
!= 0 ) {
912 writer
.setAsOverrideOf(forImage
.overrideImageNum
);
913 const char* overridePath
= _dyldImageArray
->imageForNum(forImage
.overrideImageNum
)->path();
914 writer
.addPath(overridePath
);
915 if ( strcmp(overridePath
, "/usr/lib/system/libdyld.dylib") == 0 )
916 _libDyldImageNum
= forImage
.imageNum
;
917 else if ( strcmp(overridePath
, "/usr/lib/libSystem.B.dylib") == 0 )
918 _libSystemImageNum
= forImage
.imageNum
;
921 // do fix up info for non-cached, and cached if building cache
922 if ( !macho
->inDyldCache() || _makingDyldCacheImages
) {
923 if ( macho
->hasChainedFixups() ) {
924 addChainedFixupInfo(writer
, forImage
);
927 if ( _handlers
!= nullptr ) {
928 reportRebasesAndBinds(writer
, forImage
);
931 // Note we have to do binds before rebases so that we know if we have missing lazy binds
932 addBindInfo(writer
, forImage
);
933 if ( _diag
.noError() )
934 addRebaseInfo(writer
, macho
);
938 if ( _diag
.hasError() ) {
943 // Don't build iOSMac for now. Just add an invalid placeholder
944 if ( _makingDyldCacheImages
&& strncmp(forImage
.path(), "/System/iOSSupport/", 19) == 0 ) {
950 bool contentRebased
= forImage
.contentRebased
;
951 __block
unsigned initCount
= 0;
952 Diagnostics initializerDiag
;
953 macho
->forEachInitializer(initializerDiag
, contentRebased
, ^(uint32_t offset
) {
956 if ( initializerDiag
.noError() ) {
957 if ( initCount
!= 0 ) {
958 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets
, initCount
);
959 __block
unsigned index
= 0;
960 macho
->forEachInitializer(_diag
, contentRebased
, ^(uint32_t offset
) {
961 initOffsets
[index
++] = offset
;
963 writer
.setInitOffsets(initOffsets
, initCount
);
964 forImage
.hasInits
= true;
968 // mod_init_func section is malformed, might be self modifying pointers
969 macho
->forEachInitializerPointerSection(_diag
, ^(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
) {
970 writer
.setInitSectRange(sectionOffset
, sectionSize
);
971 forImage
.hasInits
= true;
976 // add terminators (except for dylibs in the cache because they are never unloaded)
977 if ( !macho
->inDyldCache() ) {
978 __block
unsigned termCount
= 0;
979 macho
->forEachTerminator(_diag
, contentRebased
, ^(uint32_t offset
) {
982 if ( termCount
!= 0 ) {
983 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, termOffsets
, termCount
);
984 __block
unsigned index
= 0;
985 macho
->forEachTerminator(_diag
, contentRebased
, ^(uint32_t offset
) {
986 termOffsets
[index
++] = offset
;
988 writer
.setTermOffsets(termOffsets
, termCount
);
992 // record if image has DOF sections
993 STACK_ALLOC_ARRAY(uint32_t, dofSectionOffsets
, 256);
994 macho
->forEachDOFSection(_diag
, ^(uint32_t offset
) {
995 dofSectionOffsets
.push_back(offset
);
997 if ( !dofSectionOffsets
.empty() ) {
998 writer
.setDofOffsets(dofSectionOffsets
);
1003 void ClosureBuilder::addSegments(ImageWriter
& writer
, const MachOAnalyzer
* mh
)
1005 const uint32_t segCount
= mh
->segmentCount();
1006 if ( mh
->inDyldCache() ) {
1007 uint64_t cacheUnslideBaseAddress
= _dyldCache
->unslidLoadAddress();
1008 BLOCK_ACCCESSIBLE_ARRAY(Image::DyldCacheSegment
, segs
, segCount
);
1009 mh
->forEachSegment(^(const MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
1010 segs
[info
.segIndex
] = { (uint32_t)(info
.vmAddr
-cacheUnslideBaseAddress
), (uint32_t)info
.vmSize
, info
.protections
};
1012 writer
.setCachedSegments(segs
, segCount
);
1015 const uint32_t pageSize
= (mh
->uses16KPages() ? 0x4000 : 0x1000);
1016 __block
uint32_t diskSegIndex
= 0;
1017 __block
uint32_t totalPageCount
= 0;
1018 __block
uint32_t lastFileOffsetEnd
= 0;
1019 __block
uint64_t lastVmAddrEnd
= 0;
1020 BLOCK_ACCCESSIBLE_ARRAY(Image::DiskSegment
, dsegs
, segCount
*3); // room for padding
1021 mh
->forEachSegment(^(const MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
1022 if ( (info
.fileOffset
!= 0) && (info
.fileOffset
!= lastFileOffsetEnd
) ) {
1023 Image::DiskSegment filePadding
;
1024 filePadding
.filePageCount
= (info
.fileOffset
- lastFileOffsetEnd
)/pageSize
;
1025 filePadding
.vmPageCount
= 0;
1026 filePadding
.permissions
= 0;
1027 filePadding
.paddingNotSeg
= 1;
1028 dsegs
[diskSegIndex
++] = filePadding
;
1030 if ( (lastVmAddrEnd
!= 0) && (info
.vmAddr
!= lastVmAddrEnd
) ) {
1031 Image::DiskSegment vmPadding
;
1032 vmPadding
.filePageCount
= 0;
1033 vmPadding
.vmPageCount
= (info
.vmAddr
- lastVmAddrEnd
)/pageSize
;
1034 vmPadding
.permissions
= 0;
1035 vmPadding
.paddingNotSeg
= 1;
1036 dsegs
[diskSegIndex
++] = vmPadding
;
1037 totalPageCount
+= vmPadding
.vmPageCount
;
1040 Image::DiskSegment segInfo
;
1041 segInfo
.filePageCount
= (info
.fileSize
+pageSize
-1)/pageSize
;
1042 segInfo
.vmPageCount
= (info
.vmSize
+pageSize
-1)/pageSize
;
1043 segInfo
.permissions
= info
.protections
& 7;
1044 segInfo
.paddingNotSeg
= 0;
1045 if ( info
.readOnlyData
)
1046 segInfo
.permissions
= Image::DiskSegment::kReadOnlyDataPermissions
;
1047 dsegs
[diskSegIndex
++] = segInfo
;
1048 totalPageCount
+= segInfo
.vmPageCount
;
1049 if ( info
.fileSize
!= 0 )
1050 lastFileOffsetEnd
= (uint32_t)(info
.fileOffset
+ info
.fileSize
);
1051 if ( info
.vmSize
!= 0 )
1052 lastVmAddrEnd
= info
.vmAddr
+ info
.vmSize
;
1055 writer
.setDiskSegments(dsegs
, diskSegIndex
);
1059 static bool isTupleFixup(uint64_t tupleSectVmStartOffset
, uint64_t tupleSectVmEndOffset
, uint64_t imageOffsetOfFixup
, uint32_t entrySize
, uint32_t& tupleIndex
)
1061 if ( imageOffsetOfFixup
< tupleSectVmStartOffset
)
1063 if ( imageOffsetOfFixup
> tupleSectVmEndOffset
)
1065 uint64_t offsetIntoSection
= imageOffsetOfFixup
- tupleSectVmStartOffset
;
1066 tupleIndex
= (uint32_t)(offsetIntoSection
/entrySize
);
1067 return (tupleIndex
*entrySize
== offsetIntoSection
) || ((tupleIndex
*entrySize
+entrySize
/2) == offsetIntoSection
);
1070 void ClosureBuilder::addInterposingTuples(LaunchClosureWriter
& writer
, const Image
* image
, const MachOAnalyzer
* mh
)
1072 const unsigned pointerSize
= mh
->pointerSize();
1073 const uint64_t baseAddress
= mh
->preferredLoadAddress();
1074 mh
->forEachInterposingSection(_diag
, ^(uint64_t sectVmOffset
, uint64_t sectVmSize
, bool &stop
) {
1075 const uint32_t entrySize
= 2*pointerSize
;
1076 const uint32_t tupleCount
= (uint32_t)(sectVmSize
/entrySize
);
1077 const uint64_t sectVmEndOffset
= sectVmOffset
+ sectVmSize
;
1078 BLOCK_ACCCESSIBLE_ARRAY(InterposingTuple
, resolvedTuples
, tupleCount
);
1079 for (uint32_t i
=0; i
< tupleCount
; ++i
) {
1080 resolvedTuples
[i
].stockImplementation
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1081 resolvedTuples
[i
].stockImplementation
.absolute
.value
= 0;
1082 resolvedTuples
[i
].newImplementation
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1083 resolvedTuples
[i
].newImplementation
.absolute
.value
= 0;
1085 // figure out what the replacement (rebase) and replacement (bind) of the tuple point to
1086 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool& rebaseStop
) {
1087 uint32_t tupleIndex
;
1088 if ( isTupleFixup(sectVmOffset
, sectVmEndOffset
, imageOffsetToRebase
, entrySize
, tupleIndex
) ) {
1089 const void* content
= (uint8_t*)mh
+ imageOffsetToRebase
;
1090 uint64_t unslidTargetAddress
= mh
->is64() ? *(uint64_t*)content
: *(uint32_t*)content
;
1091 resolvedTuples
[tupleIndex
].newImplementation
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1092 resolvedTuples
[tupleIndex
].newImplementation
.image
.imageNum
= image
->imageNum();
1093 resolvedTuples
[tupleIndex
].newImplementation
.image
.offset
= unslidTargetAddress
- mh
->preferredLoadAddress();
1096 ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &bindStop
) {
1097 uint32_t tupleIndex
;
1098 if ( isTupleFixup(sectVmOffset
, sectVmEndOffset
, imageOffsetToBind
, entrySize
, tupleIndex
) ) {
1099 resolvedTuples
[tupleIndex
].stockImplementation
= bindTarget
;
1102 ^(uint64_t imageOffsetToStartsInfo
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& chainStop
) {
1103 mh
->withChainStarts(_diag
, imageOffsetToStartsInfo
, ^(const dyld_chained_starts_in_image
* startsInfo
) {
1104 mh
->forEachFixupInAllChains(_diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
1105 uint64_t fixupOffset
= (uint8_t*)fixupLoc
- (uint8_t*)mh
;
1106 uint32_t tupleIndex
;
1107 if ( !isTupleFixup(sectVmOffset
, sectVmEndOffset
, fixupOffset
, entrySize
, tupleIndex
) )
1109 uint32_t bindOrdinal
;
1110 uint64_t rebaseTargetOffset
;
1111 if ( fixupLoc
->isBind(segInfo
->pointer_format
, bindOrdinal
) ) {
1112 if ( bindOrdinal
< targets
.count() ) {
1113 resolvedTuples
[tupleIndex
].stockImplementation
= targets
[bindOrdinal
];
1116 _diag
.error("out of range bind ordinal %d (max %lu)", bindOrdinal
, targets
.count());
1120 else if ( fixupLoc
->isRebase(segInfo
->pointer_format
, baseAddress
, rebaseTargetOffset
) ) {
1121 resolvedTuples
[tupleIndex
].newImplementation
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1122 resolvedTuples
[tupleIndex
].newImplementation
.image
.imageNum
= image
->imageNum();
1123 resolvedTuples
[tupleIndex
].newImplementation
.image
.offset
= rebaseTargetOffset
;
1128 ^(uint64_t imageOffsetToFixup
) {
1129 // objc optimisation can't be interposed so nothing to do here.
1131 ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &bindStop
) {
1132 // objc protocol optimisation fixups can't be interposed so nothing to do here.
1134 ^(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool &fixupStop
) {
1135 // objc selector optimisation fixups can't be interposed so nothing to do here.
1137 ^(uint64_t imageOffsetToFixup
, bool &fixupStop
) {
1138 // objc stable Swift optimisation fixups can't be interposed so nothing to do here.
1140 ^(uint64_t imageOffsetToFixup
, bool &fixupStop
) {
1141 // objc method list optimisation fixups can't be interposed so nothing to do here.
1144 // remove any tuples in which both sides are not set (or target is weak-import NULL)
1145 STACK_ALLOC_ARRAY(InterposingTuple
, goodTuples
, tupleCount
);
1146 for (uint32_t i
=0; i
< tupleCount
; ++i
) {
1147 if ( (resolvedTuples
[i
].stockImplementation
.image
.kind
!= Image::ResolvedSymbolTarget::kindAbsolute
)
1148 && (resolvedTuples
[i
].newImplementation
.image
.kind
!= Image::ResolvedSymbolTarget::kindAbsolute
) )
1149 goodTuples
.push_back(resolvedTuples
[i
]);
1151 writer
.addInterposingTuples(goodTuples
);
1152 _interposingTuplesUsed
= !goodTuples
.empty();
1154 // if the target of the interposing is in the dyld shared cache, add a PatchEntry so the cache is fixed up at launch
1155 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, goodTuples
.count());
1156 for (const InterposingTuple
& aTuple
: goodTuples
) {
1157 if ( aTuple
.stockImplementation
.sharedCache
.kind
== Image::ResolvedSymbolTarget::kindSharedCache
) {
1158 uint32_t imageIndex
;
1159 assert(_dyldCache
->addressInText((uint32_t)aTuple
.stockImplementation
.sharedCache
.offset
, &imageIndex
));
1160 ImageNum imageInCache
= imageIndex
+1;
1161 Closure::PatchEntry patch
;
1162 patch
.exportCacheOffset
= (uint32_t)aTuple
.stockImplementation
.sharedCache
.offset
;
1163 patch
.overriddenDylibInCache
= imageInCache
;
1164 patch
.replacement
= aTuple
.newImplementation
;
1165 patches
.push_back(patch
);
1168 writer
.addCachePatches(patches
);
1172 void ClosureBuilder::addRebaseInfo(ImageWriter
& writer
, const MachOAnalyzer
* mh
)
1174 const uint64_t ptrSize
= mh
->pointerSize();
1175 Image::RebasePattern maxLeapPattern
= { 0xFFFFF, 0, 0xF };
1176 const uint64_t maxLeapCount
= maxLeapPattern
.repeatCount
* maxLeapPattern
.skipCount
;
1177 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern
, rebaseEntries
, 1024);
1178 __block
uint64_t lastLocation
= -ptrSize
;
1179 mh
->forEachRebase(_diag
, !_foundMissingLazyBinds
, ^(uint64_t runtimeOffset
, bool& stop
) {
1180 const uint64_t delta
= runtimeOffset
- lastLocation
;
1181 const bool aligned
= ((delta
% ptrSize
) == 0);
1182 if ( delta
== ptrSize
) {
1183 // this rebase location is contiguous to previous
1184 if ( rebaseEntries
.back().contigCount
< 255 ) {
1185 // just bump previous's contigCount
1186 rebaseEntries
.back().contigCount
++;
1189 // previous contiguous run already has max 255, so start a new run
1190 rebaseEntries
.push_back({ 1, 1, 0 });
1193 else if ( aligned
&& (delta
<= (ptrSize
*15)) ) {
1194 // this rebase is within skip distance of last rebase
1195 rebaseEntries
.back().skipCount
= (uint8_t)((delta
-ptrSize
)/ptrSize
);
1196 int lastIndex
= (int)(rebaseEntries
.count() - 1);
1197 if ( lastIndex
> 1 ) {
1198 if ( (rebaseEntries
[lastIndex
].contigCount
== rebaseEntries
[lastIndex
-1].contigCount
)
1199 && (rebaseEntries
[lastIndex
].skipCount
== rebaseEntries
[lastIndex
-1].skipCount
) ) {
1200 // this entry as same contig and skip as prev, so remove it and bump repeat count of previous
1201 rebaseEntries
.pop_back();
1202 rebaseEntries
.back().repeatCount
+= 1;
1205 rebaseEntries
.push_back({ 1, 1, 0 });
1208 uint64_t advanceCount
= (delta
-ptrSize
);
1209 if ( (runtimeOffset
< lastLocation
) && (lastLocation
!= -ptrSize
) ) {
1210 // out of rebases! handle this be resting rebase offset to zero
1211 rebaseEntries
.push_back({ 0, 0, 0 });
1212 advanceCount
= runtimeOffset
;
1214 // if next rebase is too far to reach with one pattern, use series
1215 while ( advanceCount
> maxLeapCount
) {
1216 rebaseEntries
.push_back(maxLeapPattern
);
1217 advanceCount
-= maxLeapCount
;
1219 // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate
1220 while ( advanceCount
> maxLeapPattern
.repeatCount
) {
1221 uint64_t count
= advanceCount
/ maxLeapPattern
.skipCount
;
1222 rebaseEntries
.push_back({ (uint32_t)count
, 0, maxLeapPattern
.skipCount
});
1223 advanceCount
-= (count
*maxLeapPattern
.skipCount
);
1225 if ( advanceCount
!= 0 )
1226 rebaseEntries
.push_back({ (uint32_t)advanceCount
, 0, 1 });
1227 rebaseEntries
.push_back({ 1, 1, 0 });
1229 lastLocation
= runtimeOffset
;
1231 writer
.setRebaseInfo(rebaseEntries
);
1233 // i386 programs also use text relocs to rebase stubs
1234 if ( mh
->cputype
== CPU_TYPE_I386
) {
1235 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern
, textRebases
, 512);
1236 __block
uint64_t lastOffset
= -4;
1237 mh
->forEachTextRebase(_diag
, ^(uint64_t runtimeOffset
, bool& stop
) {
1238 if ( textRebases
.freeCount() < 2 ) {
1239 _diag
.error("too many text rebase locations (%ld) in %s", textRebases
.maxCount(), writer
.currentImage()->path());
1242 bool mergedIntoPrevious
= false;
1243 if ( (runtimeOffset
> lastOffset
) && !textRebases
.empty() ) {
1244 uint32_t skipAmount
= (uint32_t)(runtimeOffset
- lastOffset
);
1245 if ( (textRebases
.back().repeatCount
== 1) && (textRebases
.back().skipCount
== 0) ) {
1246 textRebases
.back().repeatCount
= 2;
1247 textRebases
.back().skipCount
= skipAmount
;
1248 mergedIntoPrevious
= true;
1250 else if ( textRebases
.back().skipCount
== skipAmount
) {
1251 textRebases
.back().repeatCount
+= 1;
1252 mergedIntoPrevious
= true;
1255 if ( !mergedIntoPrevious
) {
1256 Image::TextFixupPattern pattern
;
1257 pattern
.target
.raw
= 0;
1258 pattern
.startVmOffset
= (uint32_t)runtimeOffset
;
1259 pattern
.repeatCount
= 1;
1260 pattern
.skipCount
= 0;
1261 textRebases
.push_back(pattern
);
1263 lastOffset
= runtimeOffset
;
1265 writer
.setTextRebaseInfo(textRebases
);
1270 void ClosureBuilder::forEachBind(BuilderLoadedImage
& forImage
, void (^handler
)(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
),
1271 void (^strongHandler
)(const char* strongSymbolName
),
1272 void (^missingLazyBindHandler
)())
1274 __block
int lastLibOrdinal
= 256;
1275 __block
const char* lastSymbolName
= nullptr;
1276 __block
uint64_t lastAddend
= 0;
1277 __block
Image::ResolvedSymbolTarget target
;
1278 __block ResolvedTargetInfo targetInfo
;
1279 forImage
.loadAddress()->forEachBind(_diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
1280 if ( (symbolName
== lastSymbolName
) && (libOrdinal
== lastLibOrdinal
) && (addend
== lastAddend
) ) {
1281 // same symbol lookup as last location
1282 handler(runtimeOffset
, target
, targetInfo
, stop
);
1284 else if ( findSymbol(forImage
, libOrdinal
, symbolName
, weakImport
, lazyBind
, addend
, target
, targetInfo
) ) {
1285 if ( !targetInfo
.skippableWeakDef
) {
1286 handler(runtimeOffset
, target
, targetInfo
, stop
);
1287 lastSymbolName
= symbolName
;
1288 lastLibOrdinal
= libOrdinal
;
1289 lastAddend
= addend
;
1295 }, ^(const char* symbolName
) {
1296 strongHandler(symbolName
);
1298 missingLazyBindHandler();
1302 void ClosureBuilder::addBindInfo(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1304 const uint32_t ptrSize
= forImage
.loadAddress()->pointerSize();
1305 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern
, binds
, 512);
1306 __block
uint64_t lastOffset
= -ptrSize
;
1307 __block
Image::ResolvedSymbolTarget lastTarget
= { {0, 0} };
1308 forEachBind(forImage
, ^(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
) {
1309 if ( targetInfo
.weakBindCoalese
) {
1310 // may be previous bind to this location
1311 // if so, update that rather create new BindPattern
1312 for (Image::BindPattern
& aBind
: binds
) {
1313 if ( (aBind
.startVmOffset
== runtimeOffset
) && (aBind
.repeatCount
== 1) && (aBind
.skipCount
== 0) ) {
1314 aBind
.target
= target
;
1319 bool mergedIntoPrevious
= false;
1320 if ( !mergedIntoPrevious
&& (target
== lastTarget
) && (runtimeOffset
> lastOffset
) && !binds
.empty() ) {
1321 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- ptrSize
)/ptrSize
;
1322 if ( skipAmount
*ptrSize
!= (runtimeOffset
- lastOffset
- ptrSize
) ) {
1323 // misaligned pointer means we cannot optimize
1326 if ( (binds
.back().repeatCount
== 1) && (binds
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
1327 binds
.back().repeatCount
= 2;
1328 binds
.back().skipCount
= skipAmount
;
1329 assert(binds
.back().skipCount
== skipAmount
); // check overflow
1330 mergedIntoPrevious
= true;
1332 else if ( (binds
.back().skipCount
== skipAmount
) && (binds
.back().repeatCount
< 0xfff) ) {
1333 uint32_t prevRepeatCount
= binds
.back().repeatCount
;
1334 binds
.back().repeatCount
+= 1;
1335 assert(binds
.back().repeatCount
> prevRepeatCount
); // check overflow
1336 mergedIntoPrevious
= true;
1340 if ( (target
== lastTarget
) && (runtimeOffset
== lastOffset
) && !binds
.empty() ) {
1341 // duplicate bind for same location, ignore this one
1342 mergedIntoPrevious
= true;
1344 if ( !mergedIntoPrevious
) {
1345 Image::BindPattern pattern
;
1346 pattern
.target
= target
;
1347 pattern
.startVmOffset
= runtimeOffset
;
1348 pattern
.repeatCount
= 1;
1349 pattern
.skipCount
= 0;
1350 assert(pattern
.startVmOffset
== runtimeOffset
);
1351 binds
.push_back(pattern
);
1353 lastTarget
= target
;
1354 lastOffset
= runtimeOffset
;
1355 }, ^(const char* strongSymbolName
) {
1356 if ( !_makingDyldCacheImages
) {
1357 // something has a strong symbol definition that may override a weak impl in the dyld cache
1358 Image::ResolvedSymbolTarget strongOverride
;
1359 ResolvedTargetInfo strongTargetInfo
;
1360 if ( findSymbolInImage(forImage
.loadAddress(), strongSymbolName
, 0, false, false, strongOverride
, strongTargetInfo
) ) {
1361 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1362 if ( li
.loadAddress()->inDyldCache() && li
.loadAddress()->hasWeakDefs() ) {
1363 Image::ResolvedSymbolTarget implInCache
;
1364 ResolvedTargetInfo implInCacheInfo
;
1365 if ( findSymbolInImage(li
.loadAddress(), strongSymbolName
, 0, false, false, implInCache
, implInCacheInfo
) ) {
1366 // found another instance in some dylib in dyld cache, will need to patch it
1367 Closure::PatchEntry patch
;
1368 patch
.exportCacheOffset
= (uint32_t)implInCache
.sharedCache
.offset
;
1369 patch
.overriddenDylibInCache
= li
.imageNum
;
1370 patch
.replacement
= strongOverride
;
1371 _weakDefCacheOverrides
.push_back(patch
);
1378 _foundMissingLazyBinds
= true;
1381 // check for __dyld section in main executable to support licenseware
1382 if ( forImage
.loadAddress()->filetype
== MH_EXECUTE
) {
1383 forImage
.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
1384 if ( (strcmp(sectInfo
.sectName
, "__dyld") == 0) && (strcmp(sectInfo
.segInfo
.segName
, "__DATA") == 0) ) {
1385 // find dyld3::compatFuncLookup in libdyld.dylib
1386 assert(_libDyldImageNum
!= 0);
1387 Image::ResolvedSymbolTarget lookupFuncTarget
;
1388 ResolvedTargetInfo lookupFuncInfo
;
1389 if ( findSymbolInImage(findLoadedImage(_libDyldImageNum
).loadAddress(), "__ZN5dyld316compatFuncLookupEPKcPPv", 0, false, false, lookupFuncTarget
, lookupFuncInfo
) ) {
1390 // add bind to set second pointer in __dyld section to be dyld3::compatFuncLookup
1391 uint64_t runtimeOffset
= sectInfo
.sectAddr
- forImage
.loadAddress()->preferredLoadAddress() + forImage
.loadAddress()->pointerSize();
1392 Image::BindPattern compatFuncPattern
;
1393 compatFuncPattern
.target
= lookupFuncTarget
;
1394 compatFuncPattern
.startVmOffset
= runtimeOffset
;
1395 compatFuncPattern
.repeatCount
= 1;
1396 compatFuncPattern
.skipCount
= 0;
1397 assert(compatFuncPattern
.startVmOffset
== runtimeOffset
);
1398 binds
.push_back(compatFuncPattern
);
1401 _diag
.error("libdyld.dylib is dyld3::compatFuncLookup");
1407 writer
.setBindInfo(binds
);
1410 void ClosureBuilder::reportRebasesAndBinds(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1412 // report all rebases
1413 forImage
.loadAddress()->forEachRebase(_diag
, true, ^(uint64_t runtimeOffset
, bool& stop
) {
1414 _handlers
->rebase(forImage
.imageNum
, forImage
.loadAddress(), (uint32_t)runtimeOffset
);
1418 forEachBind(forImage
, ^(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
) {
1419 _handlers
->bind(forImage
.imageNum
, forImage
.loadAddress(), (uint32_t)runtimeOffset
, target
, targetInfo
);
1421 ^(const char* strongSymbolName
) {},
1424 // i386 programs also use text relocs to rebase stubs
1425 if ( forImage
.loadAddress()->cputype
== CPU_TYPE_I386
) {
1430 // These are mangled symbols for all the variants of operator new and delete
1431 // which a main executable can define (non-weak) and override the
1432 // weak-def implementation in the OS.
1433 static const char* const sTreatAsWeak
[] = {
1434 "__Znwm", "__ZnwmRKSt9nothrow_t",
1435 "__Znam", "__ZnamRKSt9nothrow_t",
1436 "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
1437 "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
1438 "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
1439 "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
1440 "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
1441 "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
1445 void ClosureBuilder::addChainedFixupInfo(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1447 // build array of targets
1448 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ResolvedSymbolTarget
, targets
, 1024);
1449 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ResolvedTargetInfo
, targetInfos
, 1024);
1450 forImage
.loadAddress()->forEachChainedFixupTarget(_diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1451 Image::ResolvedSymbolTarget target
;
1452 ResolvedTargetInfo targetInfo
;
1453 if ( !findSymbol(forImage
, libOrdinal
, symbolName
, weakImport
, false, addend
, target
, targetInfo
) ) {
1457 if ( libOrdinal
== BIND_SPECIAL_DYLIB_WEAK_LOOKUP
) {
1458 // add if not already in array
1459 bool alreadyInArray
= false;
1460 for (const char* sym
: _weakDefsFromChainedBinds
) {
1461 if ( strcmp(sym
, symbolName
) == 0 ) {
1462 alreadyInArray
= true;
1466 if ( !alreadyInArray
)
1467 _weakDefsFromChainedBinds
.push_back(symbolName
);
1469 targets
.push_back(target
);
1470 targetInfos
.push_back(targetInfo
);
1472 if ( _diag
.hasError() )
1475 uint64_t chainStartsOffset
= forImage
.loadAddress()->chainStartsOffset();
1476 if ( _handlers
!= nullptr ) {
1477 forImage
.loadAddress()->withChainStarts(_diag
, chainStartsOffset
, ^(const dyld_chained_starts_in_image
* starts
) {
1478 _handlers
->chainedBind(forImage
.imageNum
, forImage
.loadAddress(), starts
, targets
, targetInfos
);
1482 writer
.setChainedFixups(chainStartsOffset
, targets
);
1485 // with chained fixups, main executable may define symbol that overrides weak-defs but has no fixup
1486 if ( _isLaunchClosure
&& forImage
.loadAddress()->hasWeakDefs() && forImage
.loadAddress()->isMainExecutable() ) {
1487 for (const char* weakSymbolName
: sTreatAsWeak
) {
1488 Diagnostics exportDiag
;
1489 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
1490 if ( forImage
.loadAddress()->findExportedSymbol(exportDiag
, weakSymbolName
, false, foundInfo
, nullptr) ) {
1491 _weakDefsFromChainedBinds
.push_back(weakSymbolName
);
1498 bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer
* macho
, const char* symbolName
, uint64_t addend
, bool followReExports
,
1499 bool weakImport
, Image::ResolvedSymbolTarget
& target
, ResolvedTargetInfo
& targetInfo
)
1501 targetInfo
.foundInDylib
= nullptr;
1502 targetInfo
.requestedSymbolName
= symbolName
;
1503 targetInfo
.addend
= addend
;
1504 targetInfo
.weakBindCoalese
= false;
1505 targetInfo
.weakBindSameImage
= false;
1506 targetInfo
.isWeakDef
= false;
1507 targetInfo
.skippableWeakDef
= false;
1508 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
1509 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
1511 MachOAnalyzer::DependentToMachOLoaded finder
= nullptr;
1512 if ( followReExports
)
1513 finder
= reexportFinder
;
1515 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
1516 if ( macho
->findExportedSymbol(_diag
, symbolName
, weakImport
, foundInfo
, finder
) ) {
1517 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
1518 targetInfo
.foundInDylib
= foundInfo
.foundInDylib
;
1519 targetInfo
.foundSymbolName
= foundInfo
.foundSymbolName
;
1520 if ( foundInfo
.isWeakDef
)
1521 targetInfo
.isWeakDef
= true;
1522 if ( foundInfo
.kind
== MachOAnalyzer::FoundSymbol::Kind::absolute
) {
1523 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1524 target
.absolute
.value
= foundInfo
.value
+ addend
;
1526 else if ( impDylib
->inDyldCache() ) {
1527 uint64_t offsetValue
= (uint8_t*)impDylib
- (uint8_t*)_dyldCache
+ foundInfo
.value
+ addend
;
1528 target
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
1529 target
.sharedCache
.offset
= offsetValue
;
1530 assert(target
.sharedCache
.offset
== offsetValue
);
1533 uint64_t offsetValue
= foundInfo
.value
+ addend
;
1534 target
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1535 target
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
1536 target
.image
.offset
= offsetValue
;
1537 assert(target
.image
.offset
== offsetValue
);
1544 bool ClosureBuilder::findSymbol(BuilderLoadedImage
& fromImage
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
,
1545 uint64_t addend
, Image::ResolvedSymbolTarget
& target
, ResolvedTargetInfo
& targetInfo
)
1548 targetInfo
.weakBindCoalese
= false;
1549 targetInfo
.weakBindSameImage
= false;
1550 targetInfo
.isWeakDef
= false;
1551 targetInfo
.skippableWeakDef
= false;
1552 targetInfo
.requestedSymbolName
= symbolName
;
1553 targetInfo
.foundSymbolName
= nullptr;
1554 targetInfo
.libOrdinal
= libOrdinal
;
1555 if ( libOrdinal
== BIND_SPECIAL_DYLIB_FLAT_LOOKUP
) {
1556 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1557 if ( !li
.rtldLocal
&& findSymbolInImage(li
.loadAddress(), symbolName
, addend
, true, weakImport
, target
, targetInfo
) )
1561 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1562 target
.absolute
.value
= 0;
1563 // Record that we found a missing weak import so that the objc optimizer doens't have to check
1564 fromImage
.hasMissingWeakImports
= true;
1567 // <rdar://problem/44315944> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace
1568 if ( lazyBind
&& _allowMissingLazies
) {
1569 if ( findMissingSymbolHandler(target
, targetInfo
) )
1572 _diag
.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName
, fromImage
.path());
1574 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_WEAK_LOOKUP
) {
1575 // to resolve weakDef coalesing, we need to search all images in order and use first definition
1576 // but, if first found is a weakDef, a later non-weak def overrides that
1577 bool foundWeakDefImpl
= false;
1578 bool foundStrongDefImpl
= false;
1579 bool foundImpl
= false;
1581 if ( _makingDyldCacheImages
) {
1582 // _loadedImages is all dylibs in the dyld cache, it is not load-order, so need alterate weak-def binding algorithm
1583 // look first in /usr/lib/libc++, most will be here
1584 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1585 if ( li
.loadAddress()->hasWeakDefs() && (strncmp(li
.path(), "/usr/lib/libc++", 15) == 0) ) {
1586 if ( findSymbolInImage(li
.loadAddress(), symbolName
, addend
, false, weakImport
, target
, targetInfo
) ) {
1592 // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself
1594 if ( findSymbolInImage(fromImage
.loadAddress(), symbolName
, addend
, false, weakImport
, target
, targetInfo
) ) {
1598 // if still not found, then this is the rare case of a simple use of a weak-def symbol
1600 // look in all direct dependents
1601 for (Image::LinkedImage child
: fromImage
.dependents
) {
1602 if (child
.imageNum() == kMissingWeakLinkedImage
)
1604 BuilderLoadedImage
& childLi
= findLoadedImage(child
.imageNum());
1605 if ( childLi
.loadAddress()->hasWeakDefs() && findSymbolInImage(childLi
.loadAddress(), symbolName
, addend
, false, weakImport
, target
, targetInfo
) ) {
1611 targetInfo
.weakBindCoalese
= true;
1614 // walk images in load-order to find first that implements this symbol
1615 Image::ResolvedSymbolTarget aTarget
;
1616 ResolvedTargetInfo aTargetInfo
;
1617 STACK_ALLOC_ARRAY(const BuilderLoadedImage
*, cachedDylibsUsingSymbol
, 1024);
1618 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1619 // only search images with weak-defs that were not loaded with RTLD_LOCAL
1620 if ( li
.loadAddress()->hasWeakDefs() && !li
.rtldLocal
) {
1621 if ( findSymbolInImage(li
.loadAddress(), symbolName
, addend
, false, weakImport
, aTarget
, aTargetInfo
) ) {
1623 // with non-chained images, weak-defs first have a rebase to their local impl, and a weak-bind which allows earlier impls to override
1624 if ( !li
.loadAddress()->hasChainedFixups() && (aTargetInfo
.foundInDylib
== fromImage
.loadAddress()) )
1625 targetInfo
.weakBindSameImage
= true;
1626 if ( aTargetInfo
.isWeakDef
) {
1627 // found a weakDef impl, if this is first found, set target to this
1628 if ( !foundWeakDefImpl
&& !foundStrongDefImpl
) {
1630 targetInfo
= aTargetInfo
;
1632 foundWeakDefImpl
= true;
1635 // found a non-weak impl, use this (unless early strong found)
1636 if ( !foundStrongDefImpl
) {
1638 targetInfo
= aTargetInfo
;
1640 foundStrongDefImpl
= true;
1643 if ( foundImpl
&& li
.loadAddress()->inDyldCache() )
1644 cachedDylibsUsingSymbol
.push_back(&li
);
1648 // now that final target found, if any dylib in dyld cache uses that symbol name, redirect it to new target
1649 if ( !cachedDylibsUsingSymbol
.empty() ) {
1650 for (const BuilderLoadedImage
* li
: cachedDylibsUsingSymbol
) {
1651 Image::ResolvedSymbolTarget implInCache
;
1652 ResolvedTargetInfo implInCacheInfo
;
1653 if ( findSymbolInImage(li
->loadAddress(), symbolName
, addend
, false, weakImport
, implInCache
, implInCacheInfo
) ) {
1654 if ( implInCache
!= target
) {
1655 // found another instance in some dylib in dyld cache, will need to patch it
1656 Closure::PatchEntry patch
;
1657 patch
.exportCacheOffset
= (uint32_t)implInCache
.sharedCache
.offset
;
1658 patch
.overriddenDylibInCache
= li
->imageNum
;
1659 patch
.replacement
= target
;
1660 _weakDefCacheOverrides
.push_back(patch
);
1665 targetInfo
.weakBindCoalese
= true;
1671 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1672 target
.absolute
.value
= 0;
1675 if ( ! fromImage
.loadAddress()->hasChainedFixups() ) {
1676 // support old binaries where symbols have been stripped and have weak_bind to itself
1677 targetInfo
.skippableWeakDef
= true;
1681 _diag
.error("symbol '%s' not found, expected to be weak-def coalesced by '%s'", symbolName
, fromImage
.path());
1684 const BuilderLoadedImage
* targetLoadedImage
= nullptr;
1685 if ( (libOrdinal
> 0) && (libOrdinal
<= (int)fromImage
.dependents
.count()) ) {
1686 ImageNum childNum
= fromImage
.dependents
[libOrdinal
- 1].imageNum();
1687 if ( childNum
!= kMissingWeakLinkedImage
) {
1688 targetLoadedImage
= &findLoadedImage(childNum
);
1691 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_SELF
) {
1692 targetLoadedImage
= &fromImage
;
1694 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
) {
1695 targetLoadedImage
= &_loadedImages
[_mainProgLoadIndex
];
1698 _diag
.error("unknown special ordinal %d in %s", libOrdinal
, fromImage
.path());
1702 if ( targetLoadedImage
!= nullptr ) {
1703 if ( findSymbolInImage(targetLoadedImage
->loadAddress(), symbolName
, addend
, true, weakImport
, target
, targetInfo
) )
1708 target
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1709 target
.absolute
.value
= 0;
1710 // Record that we found a missing weak import so that the objc optimizer doens't have to check
1711 fromImage
.hasMissingWeakImports
= true;
1715 // <rdar://problem/43315403> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld
1716 if ( lazyBind
&& _allowMissingLazies
) {
1717 if ( findMissingSymbolHandler(target
, targetInfo
) )
1721 // symbol not found and not weak or lazy so error out
1722 const char* expectedInPath
= targetLoadedImage
? targetLoadedImage
->path() : "unknown";
1723 _diag
.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName
, expectedInPath
, fromImage
.path());
1724 if ( _launchErrorInfo
!= nullptr ) {
1725 _launchErrorInfo
->kind
= DYLD_EXIT_REASON_SYMBOL_MISSING
;
1726 _launchErrorInfo
->clientOfDylibPath
= strdup_temp(fromImage
.path());
1727 _launchErrorInfo
->targetDylibPath
= strdup_temp(expectedInPath
);
1728 _launchErrorInfo
->symbol
= symbolName
;
1735 bool ClosureBuilder::findMissingSymbolHandler(Image::ResolvedSymbolTarget
& target
, ResolvedTargetInfo
& targetInfo
)
1737 for (BuilderLoadedImage
& li
: _loadedImages
) {
1738 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) {
1739 if ( findSymbolInImage(li
.loadAddress(), "__dyld_missing_symbol_abort", 0, false, false, target
, targetInfo
) ) {
1748 void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex
, InitInfo initInfos
[], uint32_t& initOrder
, bool& hasError
)
1750 if ( initInfos
[loadIndex
].visited
)
1752 initInfos
[loadIndex
].visited
= true;
1753 initInfos
[loadIndex
].danglingUpward
= false;
1755 if (_loadedImages
[loadIndex
].isBadImage
) {
1760 for (const Image::LinkedImage
& dep
: _loadedImages
[loadIndex
].dependents
) {
1761 if ( dep
.imageNum() == kMissingWeakLinkedImage
)
1763 ClosureBuilder::BuilderLoadedImage
& depLi
= findLoadedImage(dep
.imageNum());
1764 uint32_t depLoadIndex
= (uint32_t)_loadedImages
.index(depLi
);
1765 if ( dep
.kind() == Image::LinkKind::upward
) {
1766 if ( !initInfos
[depLoadIndex
].visited
)
1767 initInfos
[depLoadIndex
].danglingUpward
= true;
1770 depthFirstRecurseSetInitInfo(depLoadIndex
, initInfos
, initOrder
, hasError
);
1775 initInfos
[loadIndex
].initOrder
= initOrder
++;
1778 void ClosureBuilder::computeInitOrder(ImageWriter
& imageWriter
, uint32_t loadIndex
)
1780 // allocate array to track initializers
1781 InitInfo initInfos
[_loadedImages
.count()];
1782 bzero(initInfos
, sizeof(initInfos
));
1784 // recurse all images and build initializer list from bottom up
1785 uint32_t initOrder
= 1;
1786 bool hasMissingDependent
= false;
1787 depthFirstRecurseSetInitInfo(loadIndex
, initInfos
, initOrder
, hasMissingDependent
);
1788 if (hasMissingDependent
) {
1789 imageWriter
.setInvalid();
1793 // any images not visited yet are are danging, force add them to end of init list
1794 for (uint32_t i
=0; i
< (uint32_t)_loadedImages
.count(); ++i
) {
1795 if ( !initInfos
[i
].visited
&& initInfos
[i
].danglingUpward
) {
1796 depthFirstRecurseSetInitInfo(i
, initInfos
, initOrder
, hasMissingDependent
);
1800 if (hasMissingDependent
) {
1801 imageWriter
.setInvalid();
1805 // build array of just images with initializer
1806 STACK_ALLOC_ARRAY(uint32_t, indexOfImagesWithInits
, _loadedImages
.count());
1808 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1809 if ( initInfos
[index
].visited
&& li
.hasInits
) {
1810 indexOfImagesWithInits
.push_back(index
);
1815 // bubble sort (FIXME)
1816 if ( indexOfImagesWithInits
.count() > 1 ) {
1817 for (uint32_t i
=0; i
< indexOfImagesWithInits
.count()-1; ++i
) {
1818 for (uint32_t j
=0; j
< indexOfImagesWithInits
.count()-i
-1; ++j
) {
1819 if ( initInfos
[indexOfImagesWithInits
[j
]].initOrder
> initInfos
[indexOfImagesWithInits
[j
+1]].initOrder
) {
1820 uint32_t temp
= indexOfImagesWithInits
[j
];
1821 indexOfImagesWithInits
[j
] = indexOfImagesWithInits
[j
+1];
1822 indexOfImagesWithInits
[j
+1] = temp
;
1828 // copy ImageNum of each image with initializers into array
1829 ImageNum initNums
[indexOfImagesWithInits
.count()];
1830 for (uint32_t i
=0; i
< indexOfImagesWithInits
.count(); ++i
) {
1831 initNums
[i
] = _loadedImages
[indexOfImagesWithInits
[i
]].imageNum
;
1834 // add to closure info
1835 imageWriter
.setInitsOrder(initNums
, (uint32_t)indexOfImagesWithInits
.count());
1838 void ClosureBuilder::addClosureInfo(LaunchClosureWriter
& closureWriter
)
1840 // record which is libSystem
1841 assert(_libSystemImageNum
!= 0);
1842 closureWriter
.setLibSystemImageNum(_libSystemImageNum
);
1844 // record which is libdyld
1845 assert(_libDyldImageNum
!= 0);
1846 Image::ResolvedSymbolTarget entryLocation
;
1847 ResolvedTargetInfo entryInfo
;
1848 if ( findSymbolInImage(findLoadedImage(_libDyldImageNum
).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, false, entryLocation
, entryInfo
) ) {
1849 const dyld3::LibDyldEntryVector
* libDyldEntry
= nullptr;
1850 switch ( entryLocation
.image
.kind
) {
1851 case Image::ResolvedSymbolTarget::kindSharedCache
:
1852 libDyldEntry
= (dyld3::LibDyldEntryVector
*)((uint8_t*)_dyldCache
+ entryLocation
.sharedCache
.offset
);
1854 case Image::ResolvedSymbolTarget::kindImage
:
1855 libDyldEntry
= (dyld3::LibDyldEntryVector
*)((uint8_t*)findLoadedImage(entryLocation
.image
.imageNum
).loadAddress() + entryLocation
.image
.offset
);
1858 if ( (libDyldEntry
!= nullptr) && ((libDyldEntry
->binaryFormatVersion
& LibDyldEntryVector::kBinaryFormatVersionMask
) == dyld3::closure::kFormatVersion
) )
1859 closureWriter
.setLibDyldEntry(entryLocation
);
1861 _diag
.error("libdyld.dylib entry vector is incompatible");
1864 _diag
.error("libdyld.dylib is missing entry vector");
1867 // record which is main executable
1868 ImageNum mainProgImageNum
= _loadedImages
[_mainProgLoadIndex
].imageNum
;
1869 closureWriter
.setTopImageNum(mainProgImageNum
);
1872 uint32_t entryOffset
;
1874 if ( _loadedImages
[_mainProgLoadIndex
].loadAddress()->getEntry(entryOffset
, usesCRT
) ) {
1875 Image::ResolvedSymbolTarget location
;
1876 location
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1877 location
.image
.imageNum
= mainProgImageNum
;
1878 location
.image
.offset
= entryOffset
;
1880 closureWriter
.setStartEntry(location
);
1882 closureWriter
.setMainEntry(location
);
1885 // add env vars that must match at launch time
1886 _pathOverrides
.forEachEnvVar(^(const char* envVar
) {
1887 closureWriter
.addEnvVar(envVar
);
1890 // add list of files which must be missing
1891 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const char*, paths
, 8192);
1892 if ( _mustBeMissingPaths
!= nullptr ) {
1893 _mustBeMissingPaths
->forEachPath(^(const char* aPath
) {
1894 paths
.push_back(aPath
);
1897 closureWriter
.setMustBeMissingFiles(paths
);
1899 // add list of files which must be be present with a specific inode/mtime
1900 if (!_skippedFiles
.empty())
1901 closureWriter
.setMustExistFiles(_skippedFiles
);
1903 void ClosureBuilder::invalidateInitializerRoots()
1906 bool madeChange
= false;
1907 for (uintptr_t loadedImageIndex
= _alreadyInitedIndex
; loadedImageIndex
!= _loadedImages
.count(); ++loadedImageIndex
) {
1908 BuilderLoadedImage
& li
= _loadedImages
[loadedImageIndex
];
1909 if ( li
.mustBuildClosure
) {
1910 // Already invalidated
1913 for (Image::LinkedImage depIndex
: li
.dependents
) {
1914 if ( depIndex
.imageNum() == kMissingWeakLinkedImage
)
1916 BuilderLoadedImage
& depImage
= findLoadedImage(depIndex
.imageNum());
1917 // If a dependent is bad, or a new image num, or an override, then we need this image to get a new closure
1918 if ( depImage
.mustBuildClosure
) {
1919 li
.mustBuildClosure
= true; // mark bad
1926 // If we made a change, then we detected an existing image with a dependent which needed to be rebuilt.
1927 // This corresponds to a root of the shared cache where the existing image is a shared cache one and the root is the depImage
1928 _foundDyldCacheRoots
= true;
1932 size_t ClosureBuilder::HashCString::hash(const char* v
) {
1933 // FIXME: Use hash<string_view> when it has the correct visibility markup
1934 return __gnu_cxx::hash
<const char*>{}(v
);
1937 bool ClosureBuilder::EqualCString::equal(const char* s1
, const char* s2
) {
1938 return strcmp(s1
, s2
) == 0;
1943 static size_t hash(const uint64_t& v
) {
1944 return std::hash
<uint64_t>{}(v
);
1948 struct EqualUInt64
{
1949 static bool equal(uint64_t s1
, uint64_t s2
) {
1954 void ClosureBuilder::writeClassOrProtocolHashTable(bool classes
, Array
<ObjCOptimizerImage
>& objcImages
) {
1955 __block MultiMap
<const char*, dyld3::closure::Image::ObjCClassImageOffset
, HashCString
, EqualCString
> seenClassesMap
;
1956 __block Map
<const char*, dyld3::closure::Image::ObjCClassNameImageOffset
, HashCString
, EqualCString
> classNameMap
;
1957 __block OverflowSafeArray
<const char*> classNames
;
1959 // Note we walk the images backwards as we want them in load order to match the order they are registered with objc
1960 for (size_t imageIndex
= 0, reverseIndex
= (objcImages
.count() - 1); imageIndex
!= objcImages
.count(); ++imageIndex
, --reverseIndex
) {
1961 if (objcImages
[reverseIndex
].diag
.hasError())
1963 ObjCOptimizerImage
& image
= objcImages
[reverseIndex
];
1964 const OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenClasses
= classes
? image
.seenClasses
: image
.seenProtocols
;
1966 for (const ObjCOptimizerImage::SeenClass
& seenClass
: seenClasses
) {
1967 closure::Image::ObjCClassNameImageOffset classNameTarget
= seenClass
.first
;
1968 dyld3::closure::Image::ObjCClassImageOffset classDataTarget
= seenClass
.second
;
1969 Image::ObjCClassImage classImage
= _objcClassesHashTableImages
[classNameTarget
.classNameImageIndex
];
1971 const BuilderLoadedImage
& li
= findLoadedImage(classImage
.imageNum
);
1972 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
1974 const char* className
= ((const char*)ma
) + classImage
.offsetOfClassNames
+ classNameTarget
.classNameImageOffset
;
1975 //uint64_t nameVMAddr = ma->preferredLoadAddress() + classImage.offsetOfClassNames + classNameTarget.classNameImageOffset;
1976 //printf("%s: 0x%08llx = '%s'\n", li.path(), nameVMAddr, className);
1977 seenClassesMap
.insert({ className
, classDataTarget
});
1979 // Also track the name
1980 auto itAndInserted
= classNameMap
.insert({ className
, dyld3::closure::Image::ObjCClassNameImageOffset() });
1981 if (itAndInserted
.second
) {
1982 // We inserted the class name so we need to add it to the strings for the closure hash table
1983 classNames
.push_back(className
);
1985 // We already computed a class name target in a previous loop so use that one
1986 itAndInserted
.first
->second
= seenClass
.first
;
1988 // If we are processing protocols, and this is the first one we've seen, then track its ISA to be fixed up
1990 uint64_t protocolVMOffset
= classImage
.offsetOfClasses
+ classDataTarget
.classData
.imageOffset
;
1991 image
.protocolISAFixups
.push_back(protocolVMOffset
);
1997 __block
uint32_t duplicateCount
= 0;
1998 seenClassesMap
.forEachEntry(^(const char *const &key
, const Image::ObjCClassImageOffset
**values
,
1999 uint64_t valuesCount
) {
2000 if (valuesCount
!= 1)
2001 duplicateCount
+= valuesCount
;
2004 // If we have closure class names, we need to make a hash table for them.
2005 OverflowSafeArray
<uint8_t>& hashTable
= classes
? _objcClassesHashTable
: _objcProtocolsHashTable
;
2006 if (!classNames
.empty()) {
2007 objc_opt::perfect_hash phash
;
2008 objc_opt::make_perfect(classNames
, phash
);
2009 size_t size
= ObjCClassOpt::size(phash
, duplicateCount
);
2010 hashTable
.resize(size
);
2011 //printf("Class table size: %lld\n", size);
2012 ObjCClassOpt
* classesHashTable
= (ObjCClassOpt
*)hashTable
.begin();
2013 classesHashTable
->write(phash
, classNameMap
.array(), seenClassesMap
, duplicateCount
);
2017 bool ClosureBuilder::optimizeObjC(Array
<ImageWriter
>& writers
) {
2018 if ( _dyldCache
== nullptr )
2021 // If we have the read only data, make sure it has a valid selector table inside.
2022 const objc_opt::objc_clsopt_t
* objcClassOpt
= nullptr;
2023 const objc_opt::objc_selopt_t
* objcSelOpt
= nullptr;
2024 const objc_opt::objc_protocolopt2_t
* objcProtocolOpt
= nullptr;
2025 if (const objc_opt::objc_opt_t
* optObjCHeader
= _dyldCache
->objcOpt()) {
2026 objcClassOpt
= optObjCHeader
->clsopt();
2027 objcSelOpt
= optObjCHeader
->selopt();
2028 objcProtocolOpt
= optObjCHeader
->protocolopt2();
2031 if ( !objcClassOpt
|| !objcSelOpt
|| !objcProtocolOpt
)
2034 // We have 24 bits of index in SelectorReferenceFixup so we can't handle a
2035 // shared cache selector table larger than that
2036 if ( objcSelOpt
->usedCount() >= (1 << 24) )
2039 // Make sure we have the pointers section with the pointer to the protocol class
2040 const void* objcOptPtrs
= _dyldCache
->objcOptPtrs();
2041 if ( objcOptPtrs
== nullptr )
2044 uint32_t pointerSize
= _loadedImages
.begin()->loadAddress()->pointerSize();
2045 uint64_t classProtocolVMAddr
= (pointerSize
== 8) ? *(uint64_t*)objcOptPtrs
: *(uint32_t*)objcOptPtrs
;
2047 Image::ResolvedSymbolTarget objcProtocolClassTarget
;
2048 objcProtocolClassTarget
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
2049 if ( _dyldCacheIsLive
) {
2050 objcProtocolClassTarget
.sharedCache
.offset
= classProtocolVMAddr
- (uint64_t)_dyldCache
;
2052 objcProtocolClassTarget
.sharedCache
.offset
= classProtocolVMAddr
- _dyldCache
->unslidLoadAddress();
2055 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ObjCOptimizerImage
, objcImages
, 32);
2056 ArrayFinalizer
<ObjCOptimizerImage
> scopedCleanup(objcImages
,
2057 ^(ObjCOptimizerImage
& objcImage
) {
2058 objcImage
.~ObjCOptimizerImage();
2061 // Find all the images with valid objc info
2062 // Also add shared cache images to a map so that we can see them later for looking up classes
2063 Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
> sharedCacheImagesMap
;
2064 for (size_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
2065 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
2067 // Skip shared cache images as even if they need a new closure, the objc runtime can still use
2068 // the optimized shared cache tables.
2069 if ( li
.loadAddress()->inDyldCache() ) {
2070 sharedCacheImagesMap
.insert({ li
.loadAddress(), true });
2071 // Bump the writer index if we have a writer for this image
2072 if ( li
.mustBuildClosure
)
2076 // Images which don't need a closure can be skipped. They are from the shared cache
2077 if ( !li
.mustBuildClosure
)
2080 // If we have a root of libobjc, just give up for now
2081 if ( !strcmp(li
.path(), "/usr/lib/libobjc.A.dylib"))
2084 ImageWriter
& writer
= writers
[writerIndex
];
2087 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2089 // Skip images with chained fixups other than arm64e legacy fixups until we can test them
2090 // FIXME: Handle chained fixups
2091 if ( ma
->hasChainedFixups() ) {
2092 switch ( ma
->chainedPointerFormat() ) {
2093 case DYLD_CHAINED_PTR_ARM64E
:
2094 case DYLD_CHAINED_PTR_64
:
2095 // We've tested the 64-bit chained fixups.
2097 case DYLD_CHAINED_PTR_64_OFFSET
:
2098 case DYLD_CHAINED_PTR_ARM64E_OFFSET
:
2099 case DYLD_CHAINED_PTR_ARM64E_USERLAND
:
2100 // FIXME: Test 64-bit offset chained fixups then enable this.
2102 case DYLD_CHAINED_PTR_32
:
2103 case DYLD_CHAINED_PTR_32_CACHE
:
2104 case DYLD_CHAINED_PTR_32_FIRMWARE
:
2105 // FIXME: Test 32-bit chained fixups then enable this.
2110 const MachOAnalyzer::ObjCImageInfo
* objcImageInfo
= ma
->objcImageInfo();
2111 if ( objcImageInfo
== nullptr )
2114 // This image is good so record it for use later.
2115 objcImages
.default_constuct_back();
2116 ObjCOptimizerImage
& image
= objcImages
.back();
2117 image
.loadedImage
= &li
;
2118 image
.writer
= &writer
;
2120 // Find FairPlay encryption range if encrypted
2121 uint32_t fairPlayFileOffset
;
2122 uint32_t fairPlaySize
;
2123 if ( ma
->isFairPlayEncrypted(fairPlayFileOffset
, fairPlaySize
) ) {
2124 image
.fairplayFileOffsetStart
= fairPlayFileOffset
;
2125 image
.fairplayFileOffsetEnd
= fairPlayFileOffset
;
2128 // Set the offset to the objc image info
2129 image
.objcImageInfoVMOffset
= (uint64_t)objcImageInfo
- (uint64_t)ma
;
2132 OverflowSafeArray
<const char*> closureSelectorStrings
;
2133 Map
<const char*, dyld3::closure::Image::ObjCImageOffset
, HashCString
, EqualCString
> closureSelectorMap
;
2134 OverflowSafeArray
<const char*> closureDuplicateSharedCacheClassNames
;
2135 Map
<const char*, dyld3::closure::Image::ObjCDuplicateClass
, HashCString
, EqualCString
> closureDuplicateSharedCacheClassMap
;
2136 for (ObjCOptimizerImage
& image
: objcImages
) {
2137 optimizeObjCClasses(objcClassOpt
, sharedCacheImagesMap
, closureDuplicateSharedCacheClassMap
, image
);
2138 if (image
.diag
.hasError())
2141 optimizeObjCProtocols(objcProtocolOpt
, sharedCacheImagesMap
, image
);
2142 if (image
.diag
.hasError())
2145 optimizeObjCSelectors(objcSelOpt
, closureSelectorMap
, image
);
2146 if (image
.diag
.hasError())
2149 // If this image is still valid, then add its intermediate results to the main tables
2152 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2153 uint64_t nameVMOffset
= nameAndDataVMOffset
.first
;
2154 uint64_t dataVMOffset
= nameAndDataVMOffset
.second
;
2155 _objcClassesHashTableImages
.push_back({ image
.loadedImage
->imageNum
, (uint32_t)nameVMOffset
, (uint32_t)dataVMOffset
});
2157 image
.classesNameAndDataVMOffsets
.clear();
2159 for (const auto& stringAndDuplicate
: image
.classSharedCacheDuplicates
) {
2160 closureDuplicateSharedCacheClassMap
[stringAndDuplicate
.first
] = stringAndDuplicate
.second
;
2161 closureDuplicateSharedCacheClassNames
.push_back(stringAndDuplicate
.first
);
2165 // Note we don't need to add the selector binds here. Its easier just to process them later from each image
2166 for (const auto& stringAndTarget
: image
.selectorMap
) {
2167 closureSelectorMap
[stringAndTarget
.first
] = stringAndTarget
.second
;
2168 closureSelectorStrings
.push_back(stringAndTarget
.first
);
2170 if (image
.methodNameVMOffset
)
2171 _objcSelectorsHashTableImages
.push_back({ image
.loadedImage
->imageNum
, (uint32_t)*image
.methodNameVMOffset
});
2174 // If we successfully analyzed the classes and selectors, we can now emit their data
2175 // Set all the writers to have optimized objc
2176 for (ObjCOptimizerImage
& image
: objcImages
) {
2177 if (image
.diag
.hasError())
2179 image
.writer
->setHasPrecomputedObjC(true);
2182 // Write out the class table
2183 writeClassOrProtocolHashTable(true, objcImages
);
2185 // Write out the protocol table
2186 writeClassOrProtocolHashTable(false, objcImages
);
2188 // If we have closure duplicate classes, we need to make a hash table for them.
2189 closure::ObjCStringTable
* duplicateClassesTable
= nullptr;
2190 if (!closureDuplicateSharedCacheClassNames
.empty()) {
2191 objc_opt::perfect_hash phash
;
2192 objc_opt::make_perfect(closureDuplicateSharedCacheClassNames
, phash
);
2193 size_t size
= ObjCStringTable::size(phash
);
2194 _objcClassesDuplicatesHashTable
.resize(size
);
2195 //printf("Duplicate classes table size: %lld\n", size);
2196 duplicateClassesTable
= (closure::ObjCClassDuplicatesOpt
*)_objcClassesDuplicatesHashTable
.begin();
2197 duplicateClassesTable
->write(phash
, closureDuplicateSharedCacheClassMap
.array());
2200 // If we have closure selectors, we need to make a hash table for them.
2201 closure::ObjCStringTable
* selectorStringTable
= nullptr;
2202 if (!closureSelectorStrings
.empty()) {
2203 objc_opt::perfect_hash phash
;
2204 objc_opt::make_perfect(closureSelectorStrings
, phash
);
2205 size_t size
= ObjCStringTable::size(phash
);
2206 _objcSelectorsHashTable
.resize(size
);
2207 //printf("Selector table size: %lld\n", size);
2208 selectorStringTable
= (closure::ObjCStringTable
*)_objcSelectorsHashTable
.begin();
2209 selectorStringTable
->write(phash
, closureSelectorMap
.array());
2212 // Add fixups for the image info, protocol ISAs, and selector refs
2213 for (ObjCOptimizerImage
& image
: objcImages
) {
2214 if (image
.diag
.hasError())
2217 // Protocol ISA references
2218 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ProtocolISAFixup
, protocolFixups
, 512);
2219 if ( !image
.protocolISAFixups
.empty() ) {
2221 __block
uint64_t lastOffset
= -pointerSize
;
2222 for (uint64_t runtimeOffset
: image
.protocolISAFixups
) {
2223 bool mergedIntoPrevious
= false;
2224 if ( (runtimeOffset
> lastOffset
) && !protocolFixups
.empty() ) {
2225 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2226 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2227 // misaligned pointer means we cannot optimize
2230 if ( (protocolFixups
.back().repeatCount
== 1) && (protocolFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2231 protocolFixups
.back().repeatCount
= 2;
2232 protocolFixups
.back().skipCount
= skipAmount
;
2233 assert(protocolFixups
.back().skipCount
== skipAmount
); // check overflow
2234 mergedIntoPrevious
= true;
2236 else if ( (protocolFixups
.back().skipCount
== skipAmount
) && (protocolFixups
.back().repeatCount
< 0xfff) ) {
2237 uint32_t prevRepeatCount
= protocolFixups
.back().repeatCount
;
2238 protocolFixups
.back().repeatCount
+= 1;
2239 assert(protocolFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2240 mergedIntoPrevious
= true;
2244 if ( !mergedIntoPrevious
) {
2245 Image::ProtocolISAFixup pattern
;
2246 pattern
.startVmOffset
= runtimeOffset
;
2247 pattern
.repeatCount
= 1;
2248 pattern
.skipCount
= 0;
2249 assert(pattern
.startVmOffset
== runtimeOffset
);
2250 protocolFixups
.push_back(pattern
);
2252 lastOffset
= runtimeOffset
;
2256 // Selector references
2257 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::SelectorReferenceFixup
, selRefFixups
, 512);
2258 if ( !image
.selectorFixups
.empty() ) {
2259 uint64_t prevVMOffset
= 0;
2260 const uint64_t maxChainOffset
= (4 * ((1 << 7) - 1));
2261 for (const ObjCOptimizerImage::SelectorFixup
& selectorFixup
: image
.selectorFixups
) {
2262 assert( (selectorFixup
.fixupVMOffset
& 3) == 0 );
2263 if ( (selectorFixup
.fixupVMOffset
- prevVMOffset
) <= maxChainOffset
) {
2264 // Add this to the previous chain
2265 selRefFixups
.back().chainEntry
.next
= (uint32_t)(selectorFixup
.fixupVMOffset
- prevVMOffset
) / 4;
2267 // Need to start a new chain as the previous offset can't reach
2268 Image::SelectorReferenceFixup fixup
;
2269 fixup
.chainStartVMOffset
= selectorFixup
.fixupVMOffset
;
2270 selRefFixups
.push_back(fixup
);
2273 if ( selectorFixup
.isSharedCache
) {
2274 // If the entry is in the shared cache then we already have the index for it
2275 Image::SelectorReferenceFixup fixup
;
2276 fixup
.chainEntry
.index
= selectorFixup
.sharedCache
.selectorTableIndex
;
2277 fixup
.chainEntry
.next
= 0;
2278 fixup
.chainEntry
.inSharedCache
= 1;
2279 selRefFixups
.push_back(fixup
);
2281 // We had to record the string for the closure table entries as we don't know the
2283 uint32_t selectorTableIndex
= selectorStringTable
->getIndex(selectorFixup
.image
.selectorString
);
2284 assert(selectorTableIndex
!= ObjCSelectorOpt::indexNotFound
);
2285 Image::SelectorReferenceFixup fixup
;
2286 fixup
.chainEntry
.index
= selectorTableIndex
;
2287 fixup
.chainEntry
.next
= 0;
2288 fixup
.chainEntry
.inSharedCache
= 0;
2289 selRefFixups
.push_back(fixup
);
2292 prevVMOffset
= selectorFixup
.fixupVMOffset
;
2296 // Stable Swift fixups
2297 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ClassStableSwiftFixup
, stableSwiftFixups
, 512);
2298 if ( !image
.classStableSwiftFixups
.empty() ) {
2300 __block
uint64_t lastOffset
= -pointerSize
;
2301 for (uint64_t runtimeOffset
: image
.classStableSwiftFixups
) {
2302 bool mergedIntoPrevious
= false;
2303 if ( (runtimeOffset
> lastOffset
) && !stableSwiftFixups
.empty() ) {
2304 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2305 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2306 // misaligned pointer means we cannot optimize
2309 if ( (stableSwiftFixups
.back().repeatCount
== 1) && (stableSwiftFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2310 stableSwiftFixups
.back().repeatCount
= 2;
2311 stableSwiftFixups
.back().skipCount
= skipAmount
;
2312 assert(stableSwiftFixups
.back().skipCount
== skipAmount
); // check overflow
2313 mergedIntoPrevious
= true;
2315 else if ( (stableSwiftFixups
.back().skipCount
== skipAmount
) && (stableSwiftFixups
.back().repeatCount
< 0xfff) ) {
2316 uint32_t prevRepeatCount
= stableSwiftFixups
.back().repeatCount
;
2317 stableSwiftFixups
.back().repeatCount
+= 1;
2318 assert(stableSwiftFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2319 mergedIntoPrevious
= true;
2323 if ( !mergedIntoPrevious
) {
2324 Image::ClassStableSwiftFixup pattern
;
2325 pattern
.startVmOffset
= runtimeOffset
;
2326 pattern
.repeatCount
= 1;
2327 pattern
.skipCount
= 0;
2328 assert(pattern
.startVmOffset
== runtimeOffset
);
2329 stableSwiftFixups
.push_back(pattern
);
2331 lastOffset
= runtimeOffset
;
2335 // Method list fixups
2336 // TODO: Implement this
2337 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::MethodListFixup
, methodListFixups
, 512);
2339 image
.writer
->setObjCFixupInfo(objcProtocolClassTarget
, image
.objcImageInfoVMOffset
, protocolFixups
,
2340 selRefFixups
, stableSwiftFixups
, methodListFixups
);
2346 void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t
* objcSelOpt
,
2347 const Map
<const char*, dyld3::closure::Image::ObjCImageOffset
, HashCString
, EqualCString
>& closureSelectorMap
,
2348 ObjCOptimizerImage
& image
) {
2350 BuilderLoadedImage
& li
= *image
.loadedImage
;
2352 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2353 uint32_t pointerSize
= ma
->pointerSize();
2354 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2356 // The legacy (objc1) codebase uses a bunch of sections we don't want to reason about. If we see them just give up.
2357 __block
bool foundBadSection
= false;
2358 ma
->forEachSection(^(const MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
2359 if ( strcmp(sectInfo
.segInfo
.segName
, "__OBJC") != 0 )
2361 if (strcmp(sectInfo
.sectName
, "__module_info") == 0) {
2362 foundBadSection
= true;
2366 if (strcmp(sectInfo
.sectName
, "__protocol") == 0) {
2367 foundBadSection
= true;
2371 if (strcmp(sectInfo
.sectName
, "__message_refs") == 0) {
2372 foundBadSection
= true;
2377 if (foundBadSection
) {
2378 image
.diag
.error("Old objc section");
2382 __block
MachOAnalyzer::SectionCache
selectorStringSectionCache(ma
);
2384 uint32_t sharedCacheSentinelIndex
= objcSelOpt
->getSentinelIndex();
2386 auto visitReferenceToObjCSelector
= ^void(uint64_t selectorStringVMAddr
, uint64_t selectorReferenceVMAddr
) {
2388 uint64_t selectorUseImageOffset
= selectorReferenceVMAddr
- loadAddress
;
2389 if ( (selectorUseImageOffset
& 3) != 0 ) {
2390 image
.diag
.error("Unaligned selector reference fixup");
2394 // Image::SelectorReferenceFixup only has a 32-bit reach
2395 if ( selectorUseImageOffset
>= (1ULL << 32) ) {
2396 image
.diag
.error("Selector reference fixup exceeds supported vm offset");
2400 // Get the section for the name
2401 const char* selectorString
= nullptr;
2402 MachOAnalyzer::PrintableStringResult selectorStringResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2403 __block
uint64_t selectorStringSectionStartVMAddr
= 0;
2404 auto selectorStringSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2406 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2407 if (sectInfo
.sectSize
>= Image::ObjCImageOffset::maximumOffset
) {
2411 // We use 32-bit offsets so make sure the section is no larger than that.
2412 uint64_t classNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2413 if (classNameVMOffset
>= (1ULL << 32)) {
2417 selectorStringSectionStartVMAddr
= sectInfo
.sectAddr
;
2420 selectorString
= ma
->getPrintableString(selectorStringVMAddr
, selectorStringResult
,
2421 &selectorStringSectionCache
, selectorStringSectionHandler
);
2423 if ( selectorStringResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2424 image
.diag
.error("Invalid selector string for objc optimisation");
2428 uint32_t cacheSelectorIndex
= objcSelOpt
->getIndexForKey(selectorString
);
2429 //printf("selector: %p -> %p %s\n", methodName, cacheSelector, selectorString);
2431 if ( cacheSelectorIndex
!= sharedCacheSentinelIndex
) {
2432 // We got the selector from the cache so add a fixup to point there.
2433 ObjCOptimizerImage::SelectorFixup fixup
;
2434 fixup
.isSharedCache
= true;
2435 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2436 fixup
.sharedCache
.selectorTableIndex
= cacheSelectorIndex
;
2438 //printf("Overriding fixup at 0x%08llX to cache offset 0x%08llX\n", selectorUseImageOffset, (uint64_t)cacheSelector - (uint64_t)_dyldCache);
2439 image
.selectorFixups
.push_back(fixup
);
2443 // See if this selector is already in the closure map from a previous image
2444 auto closureSelectorIt
= closureSelectorMap
.find(selectorString
);
2445 if (closureSelectorIt
!= closureSelectorMap
.end()) {
2446 // This selector was found in a previous image, so use it here.
2447 ObjCOptimizerImage::SelectorFixup fixup
;
2448 fixup
.isSharedCache
= false;
2449 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2450 fixup
.image
.selectorString
= selectorString
;
2452 //printf("Overriding fixup at 0x%08llX to '%s' offset 0x%08llX\n", selectorUseImageOffset, findLoadedImage(target.image.imageNum).path(), target.image.offset);
2453 image
.selectorFixups
.push_back(fixup
);
2457 // See if this selector is already in the map for this image
2458 auto itAndInserted
= image
.selectorMap
.insert({ selectorString
, dyld3::closure::Image::ObjCImageOffset() });
2459 if (itAndInserted
.second
) {
2460 // We added the selector so its pointing in to our own image.
2461 // We don't need to add a fixup to our image, but we do need to
2462 // populate the data for other images later to point here.
2463 // First put our image in the list if its not already there.
2464 uint64_t methodNameVMOffset
= selectorStringSectionStartVMAddr
- loadAddress
;
2465 if (!image
.methodNameVMOffset
) {
2466 if ( _objcSelectorsHashTableImages
.count() == Image::ObjCImageOffset::maximumImageIndex
) {
2467 image
.diag
.error("Out of space for selector hash images");
2470 image
.methodNameVMOffset
= methodNameVMOffset
;
2472 // If we already set the offset to the start of the method names section, double check that
2473 // the section we are in right now is the same as that one. Otherwise we don't have the code
2474 // to handle both right now.
2475 if (*image
.methodNameVMOffset
!= methodNameVMOffset
) {
2476 image
.diag
.error("Cannot handle more than one selector strings section");
2481 dyld3::closure::Image::ObjCImageOffset target
;
2482 target
.imageIndex
= (uint32_t)_objcSelectorsHashTableImages
.count();
2483 target
.imageOffset
= (uint32_t)(selectorStringVMAddr
- selectorStringSectionStartVMAddr
);
2484 itAndInserted
.first
->second
= target
;
2488 // This selector was found elsewhere in our image. If this reference already points to the same
2489 // selector string as we found before (and it should!) then we have nothing to do. Otherwise we
2490 // need to add a fixup here to make sure we point to our chosen definition.
2491 uint32_t imageOffset
= (uint32_t)(selectorStringVMAddr
- loadAddress
);
2492 if ( imageOffset
== (*image
.methodNameVMOffset
+ itAndInserted
.first
->second
.imageOffset
) )
2495 ObjCOptimizerImage::SelectorFixup fixup
;
2496 fixup
.isSharedCache
= false;
2497 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2498 fixup
.image
.selectorString
= selectorString
;
2500 //printf("Overriding fixup at 0x%08llX to '%s' offset 0x%08llX\n", selectorUseImageOffset, findLoadedImage(target.image.imageNum).path(), target.image.offset);
2501 image
.selectorFixups
.push_back(fixup
);
2504 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
2505 visitReferenceToObjCSelector(method
.nameVMAddr
, method
.nameLocationVMAddr
);
2508 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
2509 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
2510 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
2511 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), li
.contentRebased
,
2515 auto visitCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
2516 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
2517 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, li
.contentRebased
,
2519 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, li
.contentRebased
,
2522 auto visitProtocol
= ^(Diagnostics
& diag
, uint64_t protocolVMAddr
,
2523 const dyld3::MachOAnalyzer::ObjCProtocol
& objCProtocol
) {
2524 ma
->forEachObjCMethod(objCProtocol
.instanceMethodsVMAddr
, li
.contentRebased
,
2526 ma
->forEachObjCMethod(objCProtocol
.classMethodsVMAddr
, li
.contentRebased
,
2528 ma
->forEachObjCMethod(objCProtocol
.optionalInstanceMethodsVMAddr
, li
.contentRebased
,
2530 ma
->forEachObjCMethod(objCProtocol
.optionalClassMethodsVMAddr
, li
.contentRebased
,
2534 // Walk the class list
2535 ma
->forEachObjCClass(image
.diag
, li
.contentRebased
, visitClass
);
2536 if (image
.diag
.hasError())
2539 // Walk the category list
2540 ma
->forEachObjCCategory(image
.diag
, li
.contentRebased
, visitCategory
);
2541 if (image
.diag
.hasError())
2544 // Walk the protocol list
2545 ma
->forEachObjCProtocol(image
.diag
, li
.contentRebased
, visitProtocol
);
2546 if (image
.diag
.hasError())
2549 // Visit the selector refs
2550 ma
->forEachObjCSelectorReference(image
.diag
, li
.contentRebased
, ^(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
) {
2551 visitReferenceToObjCSelector(selRefTargetVMAddr
, selRefVMAddr
);
2553 if (image
.diag
.hasError())
2556 // Visit the message refs
2557 // Note this isn't actually supported in libobjc any more. Its logic for deciding whether to support it is if this is true:
2558 // #if (defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))
2559 // So to keep it simple, lets only do this walk if we are x86_64
2560 if ( ma
->isArch("x86_64") || ma
->isArch("x86_64h") ) {
2561 if (ma
->hasObjCMessageReferences()) {
2562 image
.diag
.error("Cannot handle message refs");
2568 static const dyld3::MachOAnalyzer
* getMachHeaderFromObjCHeaderInfo(const void* opaqueHeaderInfo
, uint32_t pointerSize
) {
2569 if (pointerSize
== 8) {
2570 typedef int64_t PtrTy
;
2572 PtrTy mhdr_offset
; // offset to mach_header_64
2573 PtrTy info_offset
; // offset to objc_image_info *
2575 const HeaderInfo
* headerInfo
= (const HeaderInfo
*)opaqueHeaderInfo
;
2576 return (const dyld3::MachOAnalyzer
*)(((const uint8_t*)&headerInfo
->mhdr_offset
) + headerInfo
->mhdr_offset
);
2578 typedef int32_t PtrTy
;
2580 PtrTy mhdr_offset
; // offset to mach_header
2581 PtrTy info_offset
; // offset to objc_image_info *
2583 const HeaderInfo
* headerInfo
= (const HeaderInfo
*)opaqueHeaderInfo
;
2584 return (const dyld3::MachOAnalyzer
*)(((const uint8_t*)&headerInfo
->mhdr_offset
) + headerInfo
->mhdr_offset
);
2588 void ClosureBuilder::addDuplicateObjCClassWarning(const char* className
,
2589 const char* duplicateDefinitionPath
,
2590 const char* canonicalDefinitionPath
)
2592 if ( _objcDuplicateClassWarnings
== nullptr )
2593 _objcDuplicateClassWarnings
= PathPool::allocate();
2594 // Use a diagnostic to give us a buffer we can safely print to
2596 diag
.error("Class %s is implemented in both %s and %s. One of the two will be used. Which one is undefined.",
2597 className
, canonicalDefinitionPath
, duplicateDefinitionPath
);
2598 #if BUILDING_CACHE_BUILDER
2599 _objcDuplicateClassWarnings
->add(diag
.errorMessage().c_str());
2601 _objcDuplicateClassWarnings
->add(diag
.errorMessage());
2605 void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t
* objcClassOpt
,
2606 const Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
>& sharedCacheImagesMap
,
2607 const Map
<const char*, dyld3::closure::Image::ObjCDuplicateClass
, HashCString
, EqualCString
>& duplicateSharedCacheClasses
,
2608 ObjCOptimizerImage
& image
) {
2610 BuilderLoadedImage
& li
= *image
.loadedImage
;
2611 OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenClasses
= image
.seenClasses
;
2613 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2614 const uint32_t pointerSize
= ma
->pointerSize();
2615 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2617 // Keep track of any missing weak imports so that we can tell if the superclasses are nil
2618 // This is necessary as the shared cache will be marked with 'no missing weak superclasses'
2619 // and so we need to continue to satisfy that constraint
2620 __block Map
<uint64_t, bool, HashUInt64
, EqualUInt64
> missingWeakImportOffets
;
2621 if (li
.hasMissingWeakImports
) {
2622 if (ma
->hasChainedFixups()) {
2623 const Image
* closureImage
= image
.writer
->currentImage();
2625 const Array
<Image::ResolvedSymbolTarget
> targets
= closureImage
->chainedTargets();
2626 if ( !targets
.empty() ) {
2627 ma
->withChainStarts(_diag
, closureImage
->chainedStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) {
2628 ma
->forEachFixupInAllChains(_diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
,
2629 const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
2630 uint64_t fixupOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
2631 uint32_t bindOrdinal
;
2632 if ( fixupLoc
->isBind(segInfo
->pointer_format
, bindOrdinal
) ) {
2633 if ( bindOrdinal
< targets
.count() ) {
2634 const Image::ResolvedSymbolTarget
& target
= targets
[bindOrdinal
];
2635 if ( (target
.absolute
.kind
== Image::ResolvedSymbolTarget::kindAbsolute
) && (target
.absolute
.value
== 0) )
2636 missingWeakImportOffets
[fixupOffset
] = true;
2639 image
.diag
.error("out of range bind ordinal %d (max %lu)", bindOrdinal
, targets
.count());
2645 if (image
.diag
.hasError())
2649 forEachBind(li
, ^(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, const ResolvedTargetInfo
& targetInfo
, bool& stop
) {
2650 if ( (target
.absolute
.kind
== Image::ResolvedSymbolTarget::kindAbsolute
) && (target
.absolute
.value
== 0) )
2651 missingWeakImportOffets
[runtimeOffset
] = true;
2652 }, ^(const char *strongSymbolName
) {
2657 // Class names and data may be in different sections depending on swift vs objc so handle multiple sections
2658 __block
MachOAnalyzer::SectionCache
classNameSectionCache(ma
);
2659 __block
MachOAnalyzer::SectionCache
classSectionCache(ma
);
2661 ma
->forEachObjCClass(image
.diag
, li
.contentRebased
, ^(Diagnostics
&diag
, uint64_t classVMAddr
,
2662 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
2663 const MachOAnalyzer::ObjCClassInfo
&objcClass
, bool isMetaClass
) {
2664 if (isMetaClass
) return;
2666 // Make sure the superclass pointer is not nil
2667 uint64_t superclassRuntimeOffset
= classSuperclassVMAddr
- loadAddress
;
2668 if (missingWeakImportOffets
.find(superclassRuntimeOffset
) != missingWeakImportOffets
.end()) {
2669 diag
.error("Missing weak superclass");
2673 // Does this class need to be fixed up for stable Swift ABI.
2674 // Note the order matches the objc runtime in that we always do this fix before checking for dupes,
2675 // but after excluding classes with missing weak superclasses.
2676 if (objcClass
.isUnfixedBackwardDeployingStableSwift()) {
2677 // Class really is stable Swift, pretending to be pre-stable.
2678 // Fix its lie. This involves fixing the FAST bits on the class data value, so record that vmaddr
2679 image
.classStableSwiftFixups
.push_back(classDataVMAddr
- loadAddress
);
2682 // Get the section for the name
2683 const char* className
= nullptr;
2684 MachOAnalyzer::PrintableStringResult classNameResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2685 __block
uint64_t classNameSectionStartVMAddr
= 0;
2686 auto classNameSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2687 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2688 if (sectInfo
.sectSize
>= Image::ObjCClassNameImageOffset::maximumOffset
) {
2692 // We use 32-bit offsets so make sure the section is no larger than that.
2693 uint64_t classNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2694 if (classNameVMOffset
>= (1ULL << 32)) {
2698 classNameSectionStartVMAddr
= sectInfo
.sectAddr
;
2701 uint64_t classNameVMAddr
= objcClass
.nameVMAddr(pointerSize
);
2702 className
= ma
->getPrintableString(classNameVMAddr
, classNameResult
,
2703 &classNameSectionCache
, classNameSectionHandler
);
2705 if ( classNameResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2706 diag
.error("Invalid class name for objc optimisation");
2710 // If the class also exists in a shared cache image which is loaded, then objc
2711 // would have found that one, regardless of load order. So we can just skip this one.
2716 uint32_t count
= objcClassOpt
->getClassHeaderAndIndex(className
, cls
, hi
, index
);
2718 // exactly one matching class. Check if its loaded
2719 const dyld3::MachOAnalyzer
* sharedCacheMA
= getMachHeaderFromObjCHeaderInfo(hi
, pointerSize
);
2720 if (sharedCacheImagesMap
.find(sharedCacheMA
) != sharedCacheImagesMap
.end()) {
2721 addDuplicateObjCClassWarning(className
, li
.path(), sharedCacheMA
->installName());
2723 // We have a duplicate class, so check if we've already got it in our map.
2724 if ( duplicateSharedCacheClasses
.find(className
) == duplicateSharedCacheClasses
.end() ) {
2725 // We haven't seen this one yet
2726 Image::ObjCDuplicateClass duplicateClass
;
2727 duplicateClass
.sharedCacheClassOptIndex
= index
;
2728 duplicateClass
.sharedCacheClassDuplicateIndex
= 0;
2729 image
.classSharedCacheDuplicates
.insert({ className
, duplicateClass
});
2733 else if (count
> 1) {
2734 // more than one matching class - find one that is loaded
2735 void *clslist
[count
];
2736 void *hilist
[count
];
2737 objcClassOpt
->getClassesAndHeaders(className
, clslist
, hilist
);
2738 for (uint32_t i
= 0; i
< count
; i
++) {
2739 const dyld3::MachOAnalyzer
* sharedCacheMA
= getMachHeaderFromObjCHeaderInfo(hilist
[i
], pointerSize
);
2740 if (sharedCacheImagesMap
.find(sharedCacheMA
) != sharedCacheImagesMap
.end()) {
2741 addDuplicateObjCClassWarning(className
, li
.path(), sharedCacheMA
->installName());
2743 // We have a duplicate class, so check if we've already got it in our map.
2744 if ( duplicateSharedCacheClasses
.find(className
) == duplicateSharedCacheClasses
.end() ) {
2745 // We haven't seen this one yet
2746 Image::ObjCDuplicateClass duplicateClass
;
2747 duplicateClass
.sharedCacheClassOptIndex
= index
;
2748 duplicateClass
.sharedCacheClassDuplicateIndex
= i
;
2749 image
.classSharedCacheDuplicates
.insert({ className
, duplicateClass
});
2758 // Get the section for the class itself
2759 __block
uint64_t classSectionStartVMAddr
= 0;
2760 auto classSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2761 // We only have 23-bits in ObjCClassImageOffset to index in to the classes
2762 if (sectInfo
.sectSize
> Image::ObjCClassImageOffset::maximumOffset
) {
2766 // We use 32-bit offsets so make sure the section is no larger than that.
2767 uint64_t classDatasVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2768 if (classDatasVMOffset
>= (1ULL << 32)) {
2772 classSectionStartVMAddr
= sectInfo
.sectAddr
;
2775 if (!classSectionCache
.findSectionForVMAddr(classVMAddr
, classSectionHandler
)) {
2776 diag
.error("Invalid class for objc optimisation");
2780 // Make sure we have an entry for our images offsets for later
2781 uint64_t classNameSectionVMOffset
= classNameSectionStartVMAddr
- loadAddress
;
2782 uint64_t classSectionVMOffset
= classSectionStartVMAddr
- loadAddress
;
2783 uint64_t hashTableVMOffsetsIndex
= 0;
2784 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2785 if ( (nameAndDataVMOffset
.first
== classNameSectionVMOffset
) && (nameAndDataVMOffset
.second
== classSectionVMOffset
) )
2787 ++hashTableVMOffsetsIndex
;
2790 if (hashTableVMOffsetsIndex
== image
.classesNameAndDataVMOffsets
.count()) {
2791 // Didn't find an image entry with this offset. Add one if we have space
2792 uint64_t totalHashTableImages
= image
.classesNameAndDataVMOffsets
.count() + _objcClassesHashTableImages
.count();
2793 if ( totalHashTableImages
== Image::ObjCClassNameImageOffset::maximumImageIndex
) {
2794 // No more space. We need to give up
2795 diag
.error("No more space for class hash table image");
2798 image
.classesNameAndDataVMOffsets
.push_back({ classNameSectionVMOffset
, classSectionVMOffset
});
2801 hashTableVMOffsetsIndex
+= _objcClassesHashTableImages
.count();
2803 uint64_t classNameOffset
= classNameVMAddr
- classNameSectionStartVMAddr
;
2804 uint64_t classDataOffset
= classVMAddr
- classSectionStartVMAddr
;
2806 closure::Image::ObjCClassNameImageOffset classNameTarget
;
2807 classNameTarget
.classNameImageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2808 classNameTarget
.classNameImageOffset
= (uint32_t)classNameOffset
;
2810 dyld3::closure::Image::ObjCClassImageOffset classDataTarget
;
2811 classDataTarget
.classData
.imageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2812 classDataTarget
.classData
.imageOffset
= (uint32_t)classDataOffset
;
2813 classDataTarget
.classData
.isDuplicate
= 0;
2815 seenClasses
.push_back({ classNameTarget
, classDataTarget
});
2819 void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t
* objcProtocolOpt
,
2820 const Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
>& sharedCacheImagesMap
,
2821 ObjCOptimizerImage
& image
) {
2823 BuilderLoadedImage
& li
= *image
.loadedImage
;
2824 OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenProtocols
= image
.seenProtocols
;
2826 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2827 const uint32_t pointerSize
= ma
->pointerSize();
2828 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2830 // Protocol names and data may be in different sections depending on swift vs objc so handle multiple sections
2831 __block
MachOAnalyzer::SectionCache
protocolNameSectionCache(ma
);
2832 __block
MachOAnalyzer::SectionCache
protocolSectionCache(ma
);
2834 ma
->forEachObjCProtocol(image
.diag
, li
.contentRebased
, ^(Diagnostics
&diag
, uint64_t protocolVMAddr
,
2835 const dyld3::MachOAnalyzer::ObjCProtocol
&objCProtocol
) {
2836 if ( objCProtocol
.requiresObjCReallocation
) {
2837 // We can't optimize this protocol as the runtime needs all fields to be present
2838 diag
.error("Protocol is too small to be optimized");
2841 if ( objCProtocol
.isaVMAddr
!= 0 ) {
2842 // We can't optimize this protocol if it has an ISA as we want to override it
2843 diag
.error("Protocol ISA cannot be non-zero");
2847 // Get the section for the name
2848 const char* protocolName
= nullptr;
2849 MachOAnalyzer::PrintableStringResult protocolNameResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2850 __block
uint64_t protocolNameSectionStartVMAddr
= 0;
2851 auto protocolNameSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2852 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2853 if (sectInfo
.sectSize
>= Image::ObjCClassNameImageOffset::maximumOffset
) {
2857 // We use 32-bit offsets so make sure the section is no larger than that.
2858 uint64_t protocolNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2859 if (protocolNameVMOffset
>= (1ULL << 32)) {
2863 protocolNameSectionStartVMAddr
= sectInfo
.sectAddr
;
2866 uint64_t protocolNameVMAddr
= objCProtocol
.nameVMAddr
;
2867 protocolName
= ma
->getPrintableString(protocolNameVMAddr
, protocolNameResult
,
2868 &protocolNameSectionCache
, protocolNameSectionHandler
);
2870 if ( protocolNameResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2871 diag
.error("Invalid protocol name for objc optimisation");
2875 // If the protocol also exists in a shared cache image which is loaded, then objc
2876 // would have found that one, regardless of load order. So we can just skip this one.
2880 uint32_t count
= objcProtocolOpt
->getClassAndHeader(protocolName
, cls
, hi
);
2882 // exactly one matching protocol. Check if its loaded
2883 if (sharedCacheImagesMap
.find(getMachHeaderFromObjCHeaderInfo(hi
, pointerSize
)) != sharedCacheImagesMap
.end())
2886 else if (count
> 1) {
2887 // more than one matching protocol - find one that is loaded
2888 void *clslist
[count
];
2889 void *hilist
[count
];
2890 objcProtocolOpt
->getClassesAndHeaders(protocolName
, clslist
, hilist
);
2891 for (uint32_t i
= 0; i
< count
; i
++) {
2892 if (sharedCacheImagesMap
.find(getMachHeaderFromObjCHeaderInfo(hilist
[i
], pointerSize
)) != sharedCacheImagesMap
.end())
2898 // Get the section for the protocol itself
2899 __block
uint64_t protocolSectionStartVMAddr
= 0;
2900 auto protocolSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2901 // We only have 23-bits in ObjCClassImageOffset to index in to the protocols
2902 if (sectInfo
.sectSize
> Image::ObjCClassImageOffset::maximumOffset
) {
2906 // We use 32-bit offsets so make sure the section is no larger than that.
2907 uint64_t protocolDatasVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2908 if (protocolDatasVMOffset
>= (1ULL << 32)) {
2912 protocolSectionStartVMAddr
= sectInfo
.sectAddr
;
2915 if (!protocolSectionCache
.findSectionForVMAddr(protocolVMAddr
, protocolSectionHandler
)) {
2916 diag
.error("Invalid protocol for objc optimisation");
2920 // Make sure we have an entry for our images offsets for later
2921 uint64_t protocolNameSectionVMOffset
= protocolNameSectionStartVMAddr
- loadAddress
;
2922 uint64_t protocolSectionVMOffset
= protocolSectionStartVMAddr
- loadAddress
;
2923 uint64_t hashTableVMOffsetsIndex
= 0;
2924 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2925 if ( (nameAndDataVMOffset
.first
== protocolNameSectionVMOffset
) && (nameAndDataVMOffset
.second
== protocolSectionVMOffset
) )
2927 ++hashTableVMOffsetsIndex
;
2930 if (hashTableVMOffsetsIndex
== image
.classesNameAndDataVMOffsets
.count()) {
2931 // Didn't find an image entry with this offset. Add one if we have space
2932 uint64_t totalHashTableImages
= image
.classesNameAndDataVMOffsets
.count() + _objcClassesHashTableImages
.count();
2933 if ( totalHashTableImages
== Image::ObjCClassNameImageOffset::maximumImageIndex
) {
2934 // No more space. We need to give up
2935 diag
.error("No more space for protocol hash table image");
2938 image
.classesNameAndDataVMOffsets
.push_back({ protocolNameSectionVMOffset
, protocolSectionVMOffset
});
2941 hashTableVMOffsetsIndex
+= _objcClassesHashTableImages
.count();
2943 uint64_t protocolNameOffset
= protocolNameVMAddr
- protocolNameSectionStartVMAddr
;
2944 uint64_t protocolDataOffset
= protocolVMAddr
- protocolSectionStartVMAddr
;
2946 closure::Image::ObjCClassNameImageOffset protocolNameTarget
;
2947 protocolNameTarget
.classNameImageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2948 protocolNameTarget
.classNameImageOffset
= (uint32_t)protocolNameOffset
;
2950 dyld3::closure::Image::ObjCClassImageOffset protocolDataTarget
;
2951 protocolDataTarget
.classData
.imageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2952 protocolDataTarget
.classData
.imageOffset
= (uint32_t)protocolDataOffset
;
2953 protocolDataTarget
.classData
.isDuplicate
= 0;
2955 seenProtocols
.push_back({ protocolNameTarget
, protocolDataTarget
});
2959 // used at launch by dyld when kernel has already mapped main executable
2960 const LaunchClosure
* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo
& fileInfo
, bool allowInsertFailures
)
2962 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_BUILD_CLOSURE
, 0, 0, 0);
2963 const mach_header
* mainMH
= (const mach_header
*)fileInfo
.fileContent
;
2964 // set up stack based storage for all arrays
2965 BuilderLoadedImage loadImagesStorage
[512];
2966 Image::LinkedImage dependenciesStorage
[512*8];
2967 InterposingTuple tuplesStorage
[64];
2968 Closure::PatchEntry cachePatchStorage
[64];
2969 const char* weakDefNameStorage
[64];
2970 _loadedImages
.setInitialStorage(loadImagesStorage
, 512);
2971 _dependencies
.setInitialStorage(dependenciesStorage
, 512*8);
2972 _interposingTuples
.setInitialStorage(tuplesStorage
, 64);
2973 _weakDefCacheOverrides
.setInitialStorage(cachePatchStorage
, 64);
2974 _weakDefsFromChainedBinds
.setInitialStorage(weakDefNameStorage
, 64);
2975 ArrayFinalizer
<BuilderLoadedImage
> scopedCleanup(_loadedImages
, ^(BuilderLoadedImage
& li
) { if (li
.unmapWhenDone
) {_fileSystem
.unloadFile(li
.loadedFileInfo
); li
.unmapWhenDone
=false;} });
2977 const MachOAnalyzer
* mainExecutable
= MachOAnalyzer::validMainExecutable(_diag
, mainMH
, fileInfo
.path
, fileInfo
.sliceLen
, _archs
, _platform
);
2978 if ( mainExecutable
== nullptr )
2980 if ( !mainExecutable
->isDynamicExecutable() ) {
2981 _diag
.error("not a main executable");
2984 _isLaunchClosure
= true;
2985 _allowMissingLazies
= true;
2989 // add main executable
2990 __block BuilderLoadedImage mainEntry
;
2991 mainEntry
.loadedFileInfo
= fileInfo
;
2992 mainEntry
.imageNum
= 0; // We can't fill this in until we've done inserted dylibs
2993 mainEntry
.unmapWhenDone
= false;
2994 mainEntry
.contentRebased
= false;
2995 mainEntry
.hasInits
= false;
2996 mainEntry
.markNeverUnload
= true;
2997 mainEntry
.rtldLocal
= false;
2998 mainEntry
.isBadImage
= false;
2999 mainEntry
.mustBuildClosure
= true;
3000 mainEntry
.hasMissingWeakImports
= false;
3001 mainEntry
.overrideImageNum
= 0;
3003 // Set the executable load path so that @executable_path can use it later
3004 _mainProgLoadPath
= fileInfo
.path
;
3006 // add any DYLD_INSERT_LIBRARIES
3007 _pathOverrides
.forEachInsertedDylib(^(const char* dylibPath
, bool &stop
) {
3008 LoadedImageChain chainMain
= { nullptr, mainEntry
};
3009 BuilderLoadedImage
* foundTopImage
;
3010 if ( !findImage(dylibPath
, chainMain
, foundTopImage
, LinkageType::kInserted
, 0, true) ) {
3011 if ( !allowInsertFailures
) {
3012 if ( _diag
.noError() )
3013 _diag
.error("could not load inserted dylib %s", dylibPath
);
3017 _diag
.clearError(); // FIXME add way to plumb back warning
3021 if ( _diag
.hasError() )
3024 _mainProgLoadIndex
= (uint32_t)_loadedImages
.count();
3025 mainEntry
.imageNum
= _startImageNum
+ _nextIndex
++;
3026 _loadedImages
.push_back(mainEntry
);
3028 // get mach_headers for all images needed to launch this main executable
3029 LoadedImageChain chainStart
= { nullptr, _loadedImages
[_mainProgLoadIndex
] };
3030 recursiveLoadDependents(chainStart
);
3031 if ( _diag
.hasError() )
3033 for (uint32_t i
=0; i
< _mainProgLoadIndex
; ++i
) {
3034 LoadedImageChain insertChainStart
= { nullptr, _loadedImages
[i
] };
3035 recursiveLoadDependents(insertChainStart
);
3036 if ( _diag
.hasError() )
3039 loadDanglingUpwardLinks();
3041 // If we have an on-disk image then we need all images which are dependent on the disk image to get a new
3042 // initializer order. Its not enough to just do the top level image as we may dlopen while in dlopen
3043 invalidateInitializerRoots();
3045 // now that everything loaded, set _libDyldImageNum and _libSystemImageNum
3046 for (BuilderLoadedImage
& li
: _loadedImages
) {
3047 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) )
3048 _libDyldImageNum
= li
.imageNum
;
3049 else if ( strcmp(li
.path(), "/usr/lib/libSystem.B.dylib") == 0 )
3050 _libSystemImageNum
= li
.imageNum
;
3053 // only some images need to go into closure (non-rooted ones from dyld cache do not)
3054 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3055 for (BuilderLoadedImage
& li
: _loadedImages
) {
3056 if ( li
.mustBuildClosure
) {
3057 writers
.push_back(ImageWriter());
3058 buildImage(writers
.back(), li
);
3059 if ( _diag
.hasError() )
3064 bool optimizedObjC
= optimizeObjC(writers
);
3066 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3067 for (uintptr_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
3068 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
3069 if ( li
.mustBuildClosure
) {
3070 computeInitOrder(writers
[writerIndex
], (uint32_t)imageIndex
);
3075 // combine all Image objects into one ImageArray
3076 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3077 for (ImageWriter
& writer
: writers
) {
3078 imageArrayWriter
.appendImage(writer
.finalize());
3079 writer
.deallocate();
3081 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3083 // merge ImageArray object into LaunchClosure object
3084 __block LaunchClosureWriter
closureWriter(imageArray
);
3086 if (optimizedObjC
) {
3087 if (!_objcSelectorsHashTable
.empty())
3088 closureWriter
.setObjCSelectorInfo(_objcSelectorsHashTable
, _objcSelectorsHashTableImages
);
3090 if (!_objcClassesHashTableImages
.empty()) {
3091 closureWriter
.setObjCClassAndProtocolInfo(_objcClassesHashTable
, _objcProtocolsHashTable
,
3092 _objcClassesHashTableImages
);
3095 if ( _objcDuplicateClassWarnings
!= nullptr ) {
3096 _objcDuplicateClassWarnings
->forEachPath(^(const char* warning
) {
3097 closureWriter
.addWarning(Closure::Warning::duplicateObjCClass
, warning
);
3101 if (!_objcClassesDuplicatesHashTable
.empty())
3102 closureWriter
.setObjCDuplicateClassesInfo(_objcClassesDuplicatesHashTable
);
3105 // record shared cache info
3106 if ( _dyldCache
!= nullptr ) {
3107 // record cache UUID
3109 _dyldCache
->getUUID(cacheUUID
);
3110 closureWriter
.setDyldCacheUUID(cacheUUID
);
3112 // record any cache patching needed because of dylib overriding cache
3113 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3114 if ( li
.overrideImageNum
!= 0 ) {
3115 uint32_t imageIndex
= li
.overrideImageNum
- (uint32_t)_dyldImageArray
->startImageNum();
3116 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, _dyldCache
->patchableExportCount(imageIndex
));
3117 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
3118 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
3120 //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
3121 _dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* symbolName
) {
3122 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
3123 Diagnostics patchDiag
;
3124 Closure::PatchEntry patch
;
3125 patch
.overriddenDylibInCache
= li
.overrideImageNum
;
3126 patch
.exportCacheOffset
= cacheOffsetOfImpl
;
3127 if ( li
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3128 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3129 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3130 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3131 patch
.replacement
.image
.offset
= foundInfo
.value
;
3134 // this means the symbol is missing in the cache override dylib, so set any uses to NULL
3135 patch
.replacement
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
3136 patch
.replacement
.absolute
.value
= 0;
3138 patches
.push_back(patch
);
3140 closureWriter
.addCachePatches(patches
);
3144 // handle any extra weak-def coalescing needed by chained fixups
3145 if ( !_weakDefsFromChainedBinds
.empty() ) {
3146 for (const char* symbolName
: _weakDefsFromChainedBinds
) {
3147 Image::ResolvedSymbolTarget cacheOverrideTarget
;
3148 bool haveCacheOverride
= false;
3149 bool foundCachOverrideIsWeakDef
= false;
3150 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3151 if ( !li
.loadAddress()->hasWeakDefs() )
3153 Image::ResolvedSymbolTarget target
;
3154 ResolvedTargetInfo targetInfo
;
3155 if ( findSymbolInImage(li
.loadAddress(), symbolName
, 0, false, false, target
, targetInfo
) ) {
3156 if ( li
.loadAddress()->inDyldCache() ) {
3157 if ( haveCacheOverride
) {
3158 Closure::PatchEntry patch
;
3159 patch
.exportCacheOffset
= (uint32_t)target
.sharedCache
.offset
;
3160 patch
.overriddenDylibInCache
= li
.imageNum
;
3161 patch
.replacement
= cacheOverrideTarget
;
3162 _weakDefCacheOverrides
.push_back(patch
);
3165 // found first in cached dylib, so no need to patch cache for this symbol
3170 // found image that exports this symbol and is not in cache
3171 if ( !haveCacheOverride
|| (foundCachOverrideIsWeakDef
&& !targetInfo
.isWeakDef
) ) {
3172 // update cache to use this symbol if it if first found or it is first non-weak found
3173 cacheOverrideTarget
= target
;
3174 foundCachOverrideIsWeakDef
= targetInfo
.isWeakDef
;
3175 haveCacheOverride
= true;
3183 // record any cache patching needed because weak-def C++ symbols override dyld cache
3184 if ( !_weakDefCacheOverrides
.empty() )
3185 closureWriter
.addCachePatches(_weakDefCacheOverrides
);
3189 #if __IPHONE_OS_VERSION_MIN_REQUIRED
3190 // if closure is built on-device for iOS, then record boot UUID
3191 char bootSessionUUID
[256] = { 0 };
3192 size_t bootSize
= sizeof(bootSessionUUID
);
3193 if ( sysctlbyname("kern.bootsessionuuid", bootSessionUUID
, &bootSize
, NULL
, 0) == 0 )
3194 closureWriter
.setBootUUID(bootSessionUUID
);
3197 #if __MAC_OS_X_VERSION_MIN_REQUIRED
3198 uint32_t progVarsOffset
;
3199 if ( mainExecutable
->hasProgramVars(_diag
, progVarsOffset
) ) {
3200 // on macOS binaries may have a __dyld section that has ProgramVars to use
3201 closureWriter
.setHasProgramVars(progVarsOffset
);
3203 if ( _diag
.hasError() )
3207 // record any interposing info
3208 if ( !_interposingDisabled
) {
3209 imageArray
->forEachImage(^(const Image
* image
, bool &stop
) {
3210 if ( !image
->inDyldCache() )
3211 addInterposingTuples(closureWriter
, image
, findLoadedImage(image
->imageNum()).loadAddress());
3215 // modify fixups in contained Images by applying interposing tuples
3216 closureWriter
.applyInterposing((const LaunchClosure
*)closureWriter
.currentTypedBytes());
3219 closureWriter
.setUsedInterposing(_interposingTuplesUsed
);
3220 closureWriter
.setUsedAtPaths(_atPathUsed
);
3221 closureWriter
.setUsedFallbackPaths(_fallbackPathUsed
);
3222 closureWriter
.setHasInsertedLibraries(_mainProgLoadIndex
> 0);
3223 closureWriter
.setInitImageCount((uint32_t)_loadedImages
.count());
3225 // add other closure attributes
3226 addClosureInfo(closureWriter
);
3229 const LaunchClosure
* result
= closureWriter
.finalize();
3230 imageArrayWriter
.deallocate();
3232 timer
.setData4(dyld3::DyldTimingBuildClosure::LaunchClosure_Built
);
3237 // used by libdyld for dlopen()
3238 const DlopenClosure
* ClosureBuilder::makeDlopenClosure(const char* path
, const LaunchClosure
* mainClosure
, const Array
<LoadedImage
>& alreadyLoadedList
,
3239 closure::ImageNum callerImageNum
, bool noLoad
, bool forceBindLazies
, bool canUseSharedCacheClosure
, closure::ImageNum
* topImageNum
)
3241 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_BUILD_CLOSURE
, 0, 0, 0);
3242 // set up stack based storage for all arrays
3243 BuilderLoadedImage loadImagesStorage
[300];
3244 Image::LinkedImage dependenciesStorage
[128];
3245 Closure::PatchEntry cachePatchStorage
[64];
3246 _loadedImages
.setInitialStorage(loadImagesStorage
, 300);
3247 _dependencies
.setInitialStorage(dependenciesStorage
, 128);
3248 _weakDefCacheOverrides
.setInitialStorage(cachePatchStorage
, 64);
3249 ArrayFinalizer
<BuilderLoadedImage
> scopedCleanup(_loadedImages
, ^(BuilderLoadedImage
& li
) { if (li
.unmapWhenDone
) {_fileSystem
.unloadFile(li
.loadedFileInfo
); li
.unmapWhenDone
=false;} });
3251 // fill in builder array from already loaded images
3252 bool cachedDylibsExpectedOnDisk
= _dyldCache
? _dyldCache
->header
.dylibsExpectedOnDisk
: true;
3253 uintptr_t callerImageIndex
= UINTPTR_MAX
;
3254 for (const LoadedImage
& ali
: alreadyLoadedList
) {
3255 const Image
* image
= ali
.image();
3256 const MachOAnalyzer
* ma
= (MachOAnalyzer
*)(ali
.loadedAddress());
3257 bool inDyldCache
= ma
->inDyldCache();
3258 BuilderLoadedImage entry
;
3259 ImageNum overrideImageNum
;
3260 entry
.loadedFileInfo
.path
= image
->path();
3261 entry
.loadedFileInfo
.fileContent
= ma
;
3262 entry
.loadedFileInfo
.sliceOffset
= 0;
3263 entry
.loadedFileInfo
.inode
= 0;
3264 entry
.loadedFileInfo
.mtime
= 0;
3265 entry
.imageNum
= image
->imageNum();
3266 entry
.dependents
= image
->dependentsArray();
3267 entry
.unmapWhenDone
= false;
3268 entry
.contentRebased
= inDyldCache
;
3269 entry
.hasInits
= false;
3270 entry
.markNeverUnload
= image
->neverUnload();
3271 entry
.rtldLocal
= ali
.hideFromFlatSearch();
3272 entry
.isBadImage
= false;
3273 entry
.mustBuildClosure
= false;
3274 entry
.hasMissingWeakImports
= false;
3275 entry
.overrideImageNum
= 0;
3276 if ( !inDyldCache
&& image
->isOverrideOfDyldCacheImage(overrideImageNum
) ) {
3277 entry
.overrideImageNum
= overrideImageNum
;
3278 canUseSharedCacheClosure
= false;
3280 if ( !inDyldCache
|| cachedDylibsExpectedOnDisk
)
3281 image
->hasFileModTimeAndInode(entry
.loadedFileInfo
.inode
, entry
.loadedFileInfo
.mtime
);
3282 if ( entry
.imageNum
== callerImageNum
)
3283 callerImageIndex
= _loadedImages
.count();
3284 _loadedImages
.push_back(entry
);
3286 _alreadyInitedIndex
= (uint32_t)_loadedImages
.count();
3288 // find main executable (may be needed for @executable_path)
3289 _isLaunchClosure
= false;
3290 for (uint32_t i
=0; i
< alreadyLoadedList
.count(); ++i
) {
3291 if ( _loadedImages
[i
].loadAddress()->isMainExecutable() ) {
3292 _mainProgLoadIndex
= i
;
3293 _mainProgLoadPath
= _loadedImages
[i
].path();
3298 // We can't use an existing dlopen closure if the main closure had interposing tuples
3299 if (canUseSharedCacheClosure
) {
3300 if (mainClosure
->hasInterposings())
3301 canUseSharedCacheClosure
= false;
3304 // add top level dylib being dlopen()ed
3305 BuilderLoadedImage
* foundTopImage
;
3307 // @rpath has caller's LC_PRATH, then main executable's LC_RPATH
3308 BuilderLoadedImage
& callerImage
= (callerImageIndex
!= UINTPTR_MAX
) ? _loadedImages
[callerImageIndex
] : _loadedImages
[_mainProgLoadIndex
];
3309 LoadedImageChain chainCaller
= { nullptr, callerImage
};
3310 LoadedImageChain chainMain
= { &chainCaller
, _loadedImages
[_mainProgLoadIndex
] };
3311 if ( !findImage(path
, chainMain
, foundTopImage
, LinkageType::kDynamic
, 0, canUseSharedCacheClosure
) ) {
3312 // If we didn't find the image, it might be a symlink to something in the dyld cache that is not on disk
3313 if ( (_dyldCache
!= nullptr) && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
3314 char resolvedPath
[PATH_MAX
];
3315 if ( _fileSystem
.getRealPath(path
, resolvedPath
) ) {
3317 if ( !findImage(resolvedPath
, chainMain
, foundTopImage
, LinkageType::kDynamic
, 0, canUseSharedCacheClosure
) ) {
3321 // We didn't find a new path from realpath
3325 // cached dylibs on disk, so don't call realpath() again, it would have been found first call to findImage()
3330 // exit early in RTLD_NOLOAD mode
3332 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_NoLoad
);
3333 // if no new images added to _loadedImages, then requested path was already loaded
3334 if ( (uint32_t)_loadedImages
.count() == _alreadyInitedIndex
)
3335 *topImageNum
= foundTopImage
->imageNum
;
3341 // fast path if roots are not allowed and target is in dyld cache or is other
3342 if ( (_dyldCache
!= nullptr) && (_dyldCache
->header
.cacheType
== kDyldSharedCacheTypeProduction
) ) {
3343 if ( foundTopImage
->imageNum
< closure::kFirstLaunchClosureImageNum
) {
3344 if (foundTopImage
->imageNum
< closure::kLastDyldCacheImageNum
)
3345 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
);
3347 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
);
3348 *topImageNum
= foundTopImage
->imageNum
;
3353 // recursive load dependents
3354 // @rpath for stuff top dylib depends on uses LC_RPATH from caller, main exe, and dylib being dlopen()ed
3355 LoadedImageChain chainTopDylib
= { &chainMain
, *foundTopImage
};
3356 recursiveLoadDependents(chainTopDylib
, canUseSharedCacheClosure
);
3357 if ( _diag
.hasError() )
3359 loadDanglingUpwardLinks(canUseSharedCacheClosure
);
3360 if ( _diag
.hasError() )
3363 // RTLD_NOW means fail the dlopen() if a symbol cannot be bound
3364 _allowMissingLazies
= !forceBindLazies
;
3366 // only some images need to go into closure (ones from dyld cache do not, unless the cache format changed)
3367 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3368 if ( _foundNonCachedImage
|| _foundDyldCacheRoots
) {
3369 // If we have an on-disk image then we need all images which are dependent on the disk image to get a new
3370 // initializer order. Its not enough to just do the top level image as we may dlopen while in dlopen
3371 invalidateInitializerRoots();
3373 for (uintptr_t loadedImageIndex
= 0; loadedImageIndex
!= _loadedImages
.count(); ++loadedImageIndex
) {
3374 BuilderLoadedImage
& li
= _loadedImages
[loadedImageIndex
];
3375 if ( li
.mustBuildClosure
) {
3376 writers
.push_back(ImageWriter());
3377 buildImage(writers
.back(), li
);
3378 if ( _diag
.hasError() )
3383 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3384 for (uintptr_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
3385 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
3386 if ( li
.mustBuildClosure
) {
3387 computeInitOrder(writers
[writerIndex
], (uint32_t)imageIndex
);
3392 if ( _diag
.hasError() )
3395 // check if top image loaded is in shared cache along with everything it depends on
3396 *topImageNum
= foundTopImage
->imageNum
;
3397 if ( _foundNonCachedImage
|| _foundDyldCacheRoots
) {
3398 if ( canUseSharedCacheClosure
&& ( foundTopImage
->imageNum
< closure::kFirstLaunchClosureImageNum
) ) {
3399 // We used a shared cache built closure, but now discovered roots. We need to try again
3401 return sRetryDlopenClosure
;
3404 if (foundTopImage
->imageNum
< closure::kLastDyldCacheImageNum
)
3405 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
);
3407 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
);
3411 // combine all Image objects into one ImageArray
3412 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3413 for (ImageWriter
& writer
: writers
) {
3414 imageArrayWriter
.appendImage(writer
.finalize());
3415 writer
.deallocate();
3417 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3419 // merge ImageArray object into LaunchClosure object
3420 DlopenClosureWriter
closureWriter(imageArray
);
3422 // add other closure attributes
3423 closureWriter
.setTopImageNum(foundTopImage
->imageNum
);
3425 // record any cache patching needed because of dylib overriding cache
3426 if ( _dyldCache
!= nullptr ) {
3427 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3428 if ( (li
.overrideImageNum
!= 0) && (li
.imageNum
>= _startImageNum
) ) {
3429 const Image
* cacheImage
= _dyldImageArray
->imageForNum(li
.overrideImageNum
);
3430 uint32_t imageIndex
= cacheImage
->imageNum() - (uint32_t)_dyldCache
->cachedDylibsImageArray()->startImageNum();
3431 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, _dyldCache
->patchableExportCount(imageIndex
));
3432 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
3433 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
3435 //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
3436 _dyldCache
->forEachPatchableExport(imageIndex
,
3437 ^(uint32_t cacheOffsetOfImpl
, const char* symbolName
) {
3438 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
3439 Diagnostics patchDiag
;
3440 Closure::PatchEntry patch
;
3441 patch
.overriddenDylibInCache
= li
.overrideImageNum
;
3442 patch
.exportCacheOffset
= cacheOffsetOfImpl
;
3443 if ( li
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3444 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3445 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3446 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3447 patch
.replacement
.image
.offset
= foundInfo
.value
;
3450 patch
.replacement
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
3451 patch
.replacement
.absolute
.value
= 0;
3453 patches
.push_back(patch
);
3455 closureWriter
.addCachePatches(patches
);
3460 // modify fixups in contained Images by applying interposing tuples
3461 closureWriter
.applyInterposing(mainClosure
);
3463 // Dlopen's should never keep track of missing paths as we don't cache these closures.
3464 assert(_mustBeMissingPaths
== nullptr);
3466 // make final DlopenClosure object
3467 const DlopenClosure
* result
= closureWriter
.finalize();
3468 imageArrayWriter
.deallocate();
3469 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_Built
);
3474 // used by dyld_closure_util
3475 const LaunchClosure
* ClosureBuilder::makeLaunchClosure(const char* mainPath
, bool allowInsertFailures
)
3477 char realerPath
[MAXPATHLEN
];
3478 closure::LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, mainPath
, _archs
, _platform
, realerPath
);
3479 const MachOAnalyzer
* mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
3480 loadedFileInfo
.path
= mainPath
;
3481 if (_diag
.hasError())
3483 if (mh
== nullptr) {
3484 _diag
.error("could not load file");
3487 if (!mh
->isDynamicExecutable()) {
3488 _diag
.error("file is not an executable");
3491 const_cast<PathOverrides
*>(&_pathOverrides
)->setMainExecutable(mh
, mainPath
);
3492 const LaunchClosure
* launchClosure
= makeLaunchClosure(loadedFileInfo
, allowInsertFailures
);
3493 loadedFileInfo
.unload(loadedFileInfo
);
3494 return launchClosure
;
3497 void ClosureBuilder::setDyldCacheInvalidFormatVersion() {
3498 _dyldCacheInvalidFormatVersion
= true;
3502 // used by dyld shared cache builder
3503 const ImageArray
* ClosureBuilder::makeDyldCacheImageArray(bool customerCache
, const Array
<CachedDylibInfo
>& dylibs
, const Array
<CachedDylibAlias
>& aliases
)
3505 // because this is run in cache builder using dispatch_apply() there is minimal stack space
3506 // so set up storage for all arrays to be vm_allocated
3507 uintptr_t maxImageCount
= dylibs
.count() + 16;
3508 _loadedImages
.reserve(maxImageCount
);
3509 _dependencies
.reserve(maxImageCount
*16);
3511 _makingDyldCacheImages
= true;
3512 _allowMissingLazies
= false;
3513 _makingCustomerCache
= customerCache
;
3514 _aliases
= &aliases
;
3516 // build _loadedImages[] with every dylib in cache
3517 __block ImageNum imageNum
= _startImageNum
;
3518 for (const CachedDylibInfo
& aDylibInfo
: dylibs
) {
3519 BuilderLoadedImage entry
;
3520 entry
.loadedFileInfo
= aDylibInfo
.fileInfo
;
3521 entry
.imageNum
= imageNum
++;
3522 entry
.unmapWhenDone
= false;
3523 entry
.contentRebased
= false;
3524 entry
.hasInits
= false;
3525 entry
.markNeverUnload
= true;
3526 entry
.rtldLocal
= false;
3527 entry
.isBadImage
= false;
3528 entry
.mustBuildClosure
= false;
3529 entry
.hasMissingWeakImports
= false;
3530 entry
.overrideImageNum
= 0;
3531 _loadedImages
.push_back(entry
);
3534 // wire up dependencies between cached dylibs
3535 for (BuilderLoadedImage
& li
: _loadedImages
) {
3536 LoadedImageChain chainStart
= { nullptr, li
};
3537 recursiveLoadDependents(chainStart
);
3538 if ( _diag
.hasError() )
3541 assert(_loadedImages
.count() == dylibs
.count());
3543 // create an ImageWriter for each cached dylib
3544 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3545 for (BuilderLoadedImage
& li
: _loadedImages
) {
3546 writers
.push_back(ImageWriter());
3547 buildImage(writers
.back(), li
);
3550 // add initializer order into each dylib
3551 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3552 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3553 uint32_t index
= li
.imageNum
- _startImageNum
;
3554 computeInitOrder(writers
[index
], index
);
3557 // combine all Image objects into one ImageArray
3558 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3559 for (ImageWriter
& writer
: writers
) {
3560 imageArrayWriter
.appendImage(writer
.finalize());
3561 writer
.deallocate();
3563 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3569 #if BUILDING_CACHE_BUILDER
3570 const ImageArray
* ClosureBuilder::makeOtherDylibsImageArray(const Array
<LoadedFileInfo
>& otherDylibs
, uint32_t cachedDylibsCount
)
3572 // because this is run in cache builder using dispatch_apply() there is minimal stack space
3573 // so set up storage for all arrays to be vm_allocated
3574 uintptr_t maxImageCount
= otherDylibs
.count() + cachedDylibsCount
+ 128;
3575 _loadedImages
.reserve(maxImageCount
);
3576 _dependencies
.reserve(maxImageCount
*16);
3578 // build _loadedImages[] with every dylib in cache, followed by others
3580 for (const LoadedFileInfo
& aDylibInfo
: otherDylibs
) {
3581 BuilderLoadedImage entry
;
3582 entry
.loadedFileInfo
= aDylibInfo
;
3583 entry
.imageNum
= _startImageNum
+ _nextIndex
++;
3584 entry
.unmapWhenDone
= false;
3585 entry
.contentRebased
= false;
3586 entry
.hasInits
= false;
3587 entry
.markNeverUnload
= false;
3588 entry
.rtldLocal
= false;
3589 entry
.isBadImage
= false;
3590 entry
.mustBuildClosure
= false;
3591 entry
.hasMissingWeakImports
= false;
3592 entry
.overrideImageNum
= 0;
3593 _loadedImages
.push_back(entry
);
3596 // wire up dependencies between cached dylibs
3597 // Note, _loadedImages can grow when we call recursiveLoadDependents so we need
3598 // to check the count on each iteration.
3599 for (uint64_t index
= 0; index
!= _loadedImages
.count(); ++index
) {
3600 BuilderLoadedImage
& li
= _loadedImages
[index
];
3601 LoadedImageChain chainStart
= { nullptr, li
};
3602 recursiveLoadDependents(chainStart
);
3603 if ( _diag
.hasError() ) {
3604 _diag
.warning("while building dlopen closure for %s: %s", li
.loadedFileInfo
.path
, _diag
.errorMessage().c_str());
3605 //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
3607 li
.isBadImage
= true; // mark bad
3611 auto invalidateBadImages
= [&]() {
3612 // Invalidate images with bad dependencies
3614 bool madeChange
= false;
3615 for (BuilderLoadedImage
& li
: _loadedImages
) {
3616 if (li
.isBadImage
) {
3617 // Already invalidated
3620 for (Image::LinkedImage depIndex
: li
.dependents
) {
3621 if ( depIndex
.imageNum() == kMissingWeakLinkedImage
)
3623 if ( depIndex
.imageNum() >= dyld3::closure::kLastDyldCacheImageNum
) {
3624 // dlopen closures can only depend on the shared cache. This is because if foo.dylib links bar.dylib
3625 // and bar.dylib is loaded in to the launch closure, then the dlopen closure for foo.dylib wouldn't see
3626 // bar.dylib at the image num in the launch closure
3627 _diag
.warning("while building dlopen closure for %s: dependent dylib is not from shared cache", li
.loadedFileInfo
.path
);
3628 li
.isBadImage
= true; // mark bad
3632 BuilderLoadedImage
& depImage
= findLoadedImage(depIndex
.imageNum());
3633 if (depImage
.isBadImage
) {
3634 _diag
.warning("while building dlopen closure for %s: dependent dylib had error", li
.loadedFileInfo
.path
);
3635 li
.isBadImage
= true; // mark bad
3645 invalidateBadImages();
3647 // create an ImageWriter for each cached dylib
3648 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3649 for (BuilderLoadedImage
& li
: _loadedImages
) {
3650 if ( li
.isBadImage
) {
3651 writers
.push_back(ImageWriter());
3652 writers
.back().setInvalid();
3655 if ( li
.imageNum
< dyld3::closure::kLastDyldCacheImageNum
)
3657 writers
.push_back(ImageWriter());
3658 buildImage(writers
.back(), li
);
3659 if ( _diag
.hasError() ) {
3660 _diag
.warning("while building dlopen closure for %s: %s", li
.loadedFileInfo
.path
, _diag
.errorMessage().c_str());
3661 //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
3663 li
.isBadImage
= true; // mark bad
3664 writers
.back().setInvalid();
3668 invalidateBadImages();
3670 // add initializer order into each dylib
3671 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3672 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3673 if ( li
.imageNum
< dyld3::closure::kLastDyldCacheImageNum
)
3677 uint32_t index
= li
.imageNum
- _startImageNum
;
3678 computeInitOrder(writers
[index
], index
);
3681 // combine all Image objects into one ImageArray
3682 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3683 for (ImageWriter
& writer
: writers
) {
3684 imageArrayWriter
.appendImage(writer
.finalize());
3685 writer
.deallocate();
3687 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3694 bool ClosureBuilder::inLoadedImageArray(const Array
<LoadedImage
>& loadedList
, ImageNum imageNum
)
3696 for (const LoadedImage
& ali
: loadedList
) {
3697 if ( ali
.image()->representsImageNum(imageNum
) )
3703 void ClosureBuilder::buildLoadOrderRecurse(Array
<LoadedImage
>& loadedList
, const Array
<const ImageArray
*>& imagesArrays
, const Image
* image
)
3705 // breadth first load
3706 STACK_ALLOC_ARRAY(const Image
*, needToRecurse
, 256);
3707 image
->forEachDependentImage(^(uint32_t dependentIndex
, dyld3::closure::Image::LinkKind kind
, ImageNum depImageNum
, bool &stop
) {
3708 if ( !inLoadedImageArray(loadedList
, depImageNum
) ) {
3709 const Image
* depImage
= ImageArray::findImage(imagesArrays
, depImageNum
);
3710 loadedList
.push_back(LoadedImage::make(depImage
));
3711 needToRecurse
.push_back(depImage
);
3716 for (const Image
* img
: needToRecurse
) {
3717 buildLoadOrderRecurse(loadedList
, imagesArrays
, img
);
3721 void ClosureBuilder::buildLoadOrder(Array
<LoadedImage
>& loadedList
, const Array
<const ImageArray
*>& imagesArrays
, const Closure
* toAdd
)
3723 const dyld3::closure::Image
* topImage
= ImageArray::findImage(imagesArrays
, toAdd
->topImage());
3724 loadedList
.push_back(LoadedImage::make(topImage
));
3725 buildLoadOrderRecurse(loadedList
, imagesArrays
, topImage
);
3730 //////////////////////////// ObjCStringTable ////////////////////////////////////////
3732 template<typename PerfectHashT
, typename ImageOffsetT
>
3733 void ObjCStringTable::write(const PerfectHashT
& phash
, const Array
<std::pair
<const char*, ImageOffsetT
>>& strings
)
3735 ObjCSelectorOpt::StringTarget sentinel
= (ObjCSelectorOpt::StringTarget
)ImageOffsetT::sentinelValue
;
3737 capacity
= phash
.capacity
;
3738 occupied
= phash
.occupied
;
3739 shift
= phash
.shift
;
3741 sentinelTarget
= sentinel
;
3742 roundedTabSize
= std::max(phash
.mask
+1, 4U);
3746 for (uint32_t i
= 0; i
< 256; i
++) {
3747 scramble
[i
] = phash
.scramble
[i
];
3749 for (uint32_t i
= 0; i
< phash
.mask
+1; i
++) {
3750 tab
[i
] = phash
.tab
[i
];
3753 dyld3::Array
<StringTarget
> targetsArray
= targets();
3754 dyld3::Array
<StringHashCheckByte
> checkBytesArray
= checkBytes();
3756 // Set offsets to the sentinel
3757 for (uint32_t i
= 0; i
< phash
.capacity
; i
++) {
3758 targetsArray
[i
] = sentinel
;
3760 // Set checkbytes to 0
3761 for (uint32_t i
= 0; i
< phash
.capacity
; i
++) {
3762 checkBytesArray
[i
] = 0;
3765 // Set real string offsets and checkbytes
3766 for (const auto& s
: strings
) {
3767 assert(s
.second
.raw
!= sentinelTarget
);
3768 uint32_t h
= hash(s
.first
);
3769 targetsArray
[h
] = s
.second
.raw
;
3770 checkBytesArray
[h
] = checkbyte(s
.first
);
3774 //////////////////////////// ObjCClassOpt ////////////////////////////////////////
3777 template<typename PerfectHashT
, typename ImageOffsetT
, typename ClassesMapT
>
3778 void ObjCClassOpt::write(const PerfectHashT
& phash
, const Array
<std::pair
<const char*, ImageOffsetT
>>& strings
,
3779 const ClassesMapT
& classes
, uint32_t preCalculatedDuplicateCount
)
3781 ObjCStringTable::write(phash
, strings
);
3783 __block
dyld3::Array
<ClassTarget
> classOffsetsArray
= classOffsets();
3784 __block
dyld3::Array
<ClassTarget
> duplicateOffsetsArray
= duplicateOffsets(preCalculatedDuplicateCount
);
3786 // Set class offsets to 0
3787 for (uint32_t i
= 0; i
< capacity
; i
++) {
3788 classOffsetsArray
[i
].raw
= dyld3::closure::Image::ObjCImageOffset::sentinelValue
;
3791 classes
.forEachEntry(^(const char *const &key
, const Image::ObjCClassImageOffset
**values
, uint64_t valuesCount
) {
3792 uint32_t keyIndex
= getIndex(key
);
3793 assert(keyIndex
!= indexNotFound
);
3794 assert(classOffsetsArray
[keyIndex
].raw
== dyld3::closure::Image::ObjCImageOffset::sentinelValue
);
3796 if (valuesCount
== 1) {
3797 // Only one entry so write it in to the class offsets directly
3798 Image::ObjCClassImageOffset classImageOffset
= *(values
[0]);
3799 assert(classImageOffset
.classData
.isDuplicate
== 0);
3800 classOffsetsArray
[keyIndex
] = classImageOffset
;
3804 // We have more than one value. We add a placeholder to the class offsets which tells us the head
3805 // of the linked list of classes in the duplicates array
3806 uint32_t dest
= duplicateCount();
3807 duplicateCount() += valuesCount
;
3809 Image::ObjCClassImageOffset classImagePlaceholder
;
3810 assert(valuesCount
< (1 << 8));
3811 classImagePlaceholder
.duplicateData
.count
= (uint32_t)valuesCount
;
3812 classImagePlaceholder
.duplicateData
.index
= dest
;
3813 classImagePlaceholder
.duplicateData
.isDuplicate
= 1;
3814 classOffsetsArray
[keyIndex
] = classImagePlaceholder
;
3816 for (uint64_t i
= 0; i
!= valuesCount
; ++i
) {
3817 Image::ObjCClassImageOffset classImageOffset
= *(values
[i
]);
3818 assert(classImageOffset
.classData
.isDuplicate
== 0);
3819 duplicateOffsetsArray
.push_back(classImageOffset
);
3824 } // namespace closure
3825 } // namespace dyld3