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 "MachOAnalyzerSet.h"
43 #include "libdyldEntryVector.h"
44 #include "RootsChecker.h"
47 #define CLOSURE_SELOPT_WRITE
48 #include "objc-shared-cache.h"
51 namespace dyld
{ void log(const char*, ...); }
58 const DlopenClosure
* ClosureBuilder::sRetryDlopenClosure
= (const DlopenClosure
*)(-1);
60 ClosureBuilder::ClosureBuilder(uint32_t startImageNum
, const FileSystem
& fileSystem
, const RootsChecker
& rootsChecker
,
61 const DyldSharedCache
* dyldCache
, bool dyldCacheIsLive
,
62 const GradedArchs
& archs
, const PathOverrides
& pathOverrides
, AtPath atPathHandling
, bool allowRelativePaths
,
63 LaunchErrorInfo
* errorInfo
, Platform platform
, DylibFixupHandler handler
)
64 : _fileSystem(fileSystem
), _rootsChecker(rootsChecker
), _dyldCache(dyldCache
), _pathOverrides(pathOverrides
), _archs(archs
), _platform(platform
), _startImageNum(startImageNum
),
65 _dylibFixupHandler(handler
), _atPathHandling(atPathHandling
), _launchErrorInfo(errorInfo
), _dyldCacheIsLive(dyldCacheIsLive
), _allowRelativePaths(allowRelativePaths
)
67 if ( dyldCache
!= nullptr ) {
68 _dyldImageArray
= dyldCache
->cachedDylibsImageArray();
73 ClosureBuilder::~ClosureBuilder() {
74 if ( _tempPaths
!= nullptr )
75 PathPool::deallocate(_tempPaths
);
76 if ( _mustBeMissingPaths
!= nullptr )
77 PathPool::deallocate(_mustBeMissingPaths
);
78 if ( _objcDuplicateClassWarnings
!= nullptr )
79 PathPool::deallocate(_objcDuplicateClassWarnings
);
82 static bool iOSSupport(const char* path
)
84 return ( strncmp(path
, "/System/iOSSupport/", 19) == 0 );
87 bool ClosureBuilder::findImage(const char* loadPath
, const LoadedImageChain
& forImageChain
, BuilderLoadedImage
*& foundImage
, LinkageType linkageType
,
88 uint32_t compatVersion
, bool canUseSharedCacheClosure
)
90 // 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
91 _diag
.assertNoError();
93 __block
bool result
= false;
95 // record if this is a non-overridable path
96 bool pathIsInDyldCacheWhichCannotBeOverridden
= false;
97 bool dylibsExpectedOnDisk
= true;
98 if ( _dyldCache
!= nullptr ) {
99 pathIsInDyldCacheWhichCannotBeOverridden
= _dyldCache
->hasNonOverridablePath(loadPath
);
100 dylibsExpectedOnDisk
= _dyldCache
->header
.dylibsExpectedOnDisk
;
103 // when building dyld cache for macOS, if requesting dylib is iOSMac unzippered twin, tell pathOverrides object to look in /System/iOSSupport first
104 dyld3::Platform targetPlatform
= _platform
;
105 if ( _makingDyldCacheImages
&& (_platform
== dyld3::Platform::macOS
) ) {
106 if ( forImageChain
.image
.loadAddress()->builtForPlatform(Platform::iOSMac
, true) )
107 targetPlatform
= Platform::iOSMac
;
110 _pathOverrides
.forEachPathVariant(loadPath
, pathIsInDyldCacheWhichCannotBeOverridden
, ^(const char* possibleVariantPath
, bool isFallbackPath
, bool& stopPathVariant
) {
112 // This check is within forEachPathVariant() to let DYLD_LIBRARY_PATH override LC_RPATH
113 bool isRPath
= (strncmp(possibleVariantPath
, "@rpath/", 7) == 0);
115 // passing a leaf name to dlopen() allows rpath searching for it
116 // FIXME: Does this apply to DYLD_INSERT_LIBRARIES too?
117 bool implictRPath
= (linkageType
== LinkageType::kDynamic
) && (loadPath
[0] != '/') && (loadPath
== possibleVariantPath
) && (_atPathHandling
!= AtPath::none
);
120 forEachResolvedPathVar(possibleVariantPath
, forImageChain
, implictRPath
, linkageType
,
121 ^(const char* possiblePath
, bool& stop
) {
122 if ( possibleVariantPath
!= possiblePath
)
125 // look at already loaded images
126 const char* leafName
= strrchr(possiblePath
, '/');
127 for (BuilderLoadedImage
& li
: _loadedImages
) {
128 if ( strcmp(li
.path(), possiblePath
) == 0 ) {
134 else if ( isRPath
) {
135 // Special case @rpath/ because name in li.fileInfo.path is full path.
136 // Getting installName is expensive, so first see if an already loaded image
137 // has same leaf name and if so see if its installName matches request @rpath
138 if (const char* aLeaf
= strrchr(li
.path(), '/')) {
139 if ( strcmp(aLeaf
, leafName
) == 0 ) {
140 if ( li
.loadAddress()->isDylib() && (strcmp(loadPath
, li
.loadAddress()->installName()) == 0) ) {
151 // look to see if image already loaded via a different symlink
152 bool fileFound
= false;
153 uint64_t fileFoundINode
= 0;
154 uint64_t fileFoundMTime
= 0;
155 bool inodesMatchRuntime
= false;
156 // Note, we only do this check if we even expect to find this on-disk
157 // We can also use the pathIsInDyldCacheWhichCannotBeOverridden result if we are still trying the same path
158 // it was computed from
159 if ( dylibsExpectedOnDisk
|| !pathIsInDyldCacheWhichCannotBeOverridden
|| (loadPath
!= possiblePath
) ) {
160 if ( _fileSystem
.fileExists(possiblePath
, &fileFoundINode
, &fileFoundMTime
, nullptr, &inodesMatchRuntime
) ) {
162 for (BuilderLoadedImage
& li
: _loadedImages
) {
163 if ( (li
.loadedFileInfo
.inode
== 0) && (li
.loadedFileInfo
.mtime
== 0) ) {
164 // Some already loaded image does not have an inode/mtime recorded, fix that if we can
165 if ( dylibsExpectedOnDisk
|| !li
.loadAddress()->inDyldCache() ) {
166 _fileSystem
.fileExists(li
.path(), &li
.loadedFileInfo
.inode
, &li
.loadedFileInfo
.mtime
, nullptr, nullptr);
169 if ( (li
.loadedFileInfo
.inode
== fileFoundINode
) && (li
.loadedFileInfo
.mtime
== fileFoundMTime
) ) {
179 // We record the realpath of the file in the loaded images, but we might be loading via a symlink path.
180 // We need to search using the realpath just in case the dylib the symlink points to was overwritten while
181 // the process is running
183 char realPath
[MAXPATHLEN
];
184 if ( _fileSystem
.getRealPath(possiblePath
, realPath
) ) {
185 for (BuilderLoadedImage
& li
: _loadedImages
) {
186 if ( strcmp(li
.path(), realPath
) == 0 ) {
196 bool unmapWhenDone
= false;
197 bool contentRebased
= false;
198 bool hasInits
= false;
199 bool markNeverUnload
= false;
200 bool mustBuildClosure
= _dyldCacheInvalidFormatVersion
;
201 ImageNum overrideImageNum
= 0;
202 ImageNum foundImageNum
= 0;
203 const MachOAnalyzer
* mh
= nullptr;
204 const char* filePath
= nullptr;
205 LoadedFileInfo loadedFileInfo
;
207 // look in dyld cache
208 filePath
= possiblePath
;
209 char realPath
[MAXPATHLEN
];
210 if ( _dyldImageArray
!= nullptr ) {
211 uint32_t dyldCacheImageIndex
;
212 bool foundInCache
= _dyldCache
->hasImagePath(possiblePath
, dyldCacheImageIndex
);
213 if ( !foundInCache
&& fileFound
) {
214 // see if this is an OS dylib/bundle with a pre-built dlopen closure
215 // We can only use the pre-built closure if we are dynamic linkage (a dlopen) and
216 // there are no roots
217 if ( canUseSharedCacheClosure
&& (linkageType
== LinkageType::kDynamic
) ) {
218 if (const dyld3::closure::Image
* otherImage
= _dyldCache
->findDlopenOtherImage(possiblePath
) ) {
219 uint64_t expectedInode
;
220 uint64_t expectedModTime
;
221 if ( !otherImage
->isInvalid() ) {
222 bool hasInodeInfo
= otherImage
->hasFileModTimeAndInode(expectedInode
, expectedModTime
);
223 // use pre-built Image if it does not have mtime/inode or it does and it has matches current file info
224 if ( !hasInodeInfo
|| ((expectedInode
== fileFoundINode
) && (expectedModTime
== fileFoundMTime
)) ) {
225 loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, possiblePath
, _archs
, _platform
, realPath
);
226 if ( _diag
.noError() ) {
227 mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
228 foundImageNum
= otherImage
->imageNum();
229 unmapWhenDone
= true;
230 contentRebased
= false;
231 hasInits
= otherImage
->hasInitializers() || otherImage
->mayHavePlusLoads();
232 // Use the realpath in the case where we loaded a symlink
233 // The closure must have recordered an alias path
234 if (realPath
[0] != '\0')
243 // If found in the cache, but not on-disk, this may be an already loaded image, but we are opening the alias.
244 // For example, we are trying to open .../AppKit but we already have a loaded root of .../Versions/C/AppKit
245 // This doesn't work with the calls to realpath when the symlinks themselves were removed from disk.
246 if ( foundInCache
&& !fileFound
) {
247 ImageNum dyldCacheImageNum
= dyldCacheImageIndex
+ 1;
248 for (BuilderLoadedImage
& li
: _loadedImages
) {
249 if ( (li
.overrideImageNum
== dyldCacheImageNum
) || (li
.imageNum
== dyldCacheImageNum
) ) {
258 // if not found in cache, may be a symlink to something in cache
259 // We have to do this check even if the symlink target is not on disk as we may
260 // have symlinks in iOSMac dlopen paths which are resolved to a dylib removed from disk
261 if ( !foundInCache
&& (mh
== nullptr) ) {
262 if ( _fileSystem
.getRealPath(possiblePath
, realPath
) ) {
263 foundInCache
= _dyldCache
->hasImagePath(realPath
, dyldCacheImageIndex
);
264 if ( foundInCache
) {
265 ImageNum dyldCacheImageNum
= dyldCacheImageIndex
+ 1;
266 const Image
* image
= _dyldImageArray
->imageForNum(dyldCacheImageNum
);
267 filePath
= image
->path();
269 // handle case where OS dylib was updated after this process launched
270 if ( foundInCache
) {
271 for (BuilderLoadedImage
& li
: _loadedImages
) {
272 if ( strcmp(li
.path(), filePath
) == 0 ) {
285 // if using a cached dylib, look to see if there is an override
286 if ( foundInCache
) {
287 ImageNum dyldCacheImageNum
= dyldCacheImageIndex
+ 1;
288 bool useCache
= true;
289 markNeverUnload
= true; // dylibs in cache, or dylibs that override cache should not be unloaded at runtime
290 bool ignoreCacheDylib
= false;
291 const Image
* image
= _dyldImageArray
->imageForNum(dyldCacheImageNum
);
292 if ( image
->overridableDylib() ) {
294 if ( _makingClosuresInCache
) {
295 // during iOS cache build, don't look at files on disk, use ones in cache
297 } else if ( !_rootsChecker
.onDiskFileIsRoot(filePath
, _dyldCache
, image
,
298 &_fileSystem
, fileFoundINode
, fileFoundMTime
) ) {
299 // file exists, but is not a root
302 // iOS internal build. Any disk on cache overrides cache
306 if ( useCache
&& ((targetPlatform
== Platform::iOSMac
) || (targetPlatform
== Platform::macOS
)) ) {
307 // check this cached dylib is suitable for catalyst or mac program
308 mh
= (MachOAnalyzer
*)_dyldCache
->getIndexedImageEntry(dyldCacheImageNum
-1, loadedFileInfo
.mtime
, loadedFileInfo
.inode
);
309 if ( !mh
->loadableIntoProcess(targetPlatform
, possiblePath
) ) {
312 ignoreCacheDylib
= true;
315 if ( !useCache
&& !ignoreCacheDylib
) {
316 overrideImageNum
= dyldCacheImageNum
;
317 _foundDyldCacheRoots
= true;
321 foundImageNum
= dyldCacheImageNum
;
322 mh
= (MachOAnalyzer
*)_dyldCache
->getIndexedImageEntry(foundImageNum
-1, loadedFileInfo
.mtime
, loadedFileInfo
.inode
);
323 unmapWhenDone
= false;
324 // if we are building ImageArray in dyld cache, content is not rebased
325 contentRebased
= !_makingDyldCacheImages
&& _dyldCacheIsLive
;
326 hasInits
= image
->hasInitializers() || image
->mayHavePlusLoads();
327 // If the cache format is different from dyld/libdyld then we can't use this closure.
328 if ( (_dyldCache
->header
.formatVersion
!= dyld3::closure::kFormatVersion
) || !canUseSharedCacheClosure
) {
329 mustBuildClosure
= true;
330 _foundDyldCacheRoots
= true;
336 // If we are building the cache, and don't find an image, then it might be weak so just return
337 if (_makingDyldCacheImages
) {
338 addMustBeMissingPath(possiblePath
);
342 // if not found yet, mmap file
343 if ( mh
== nullptr ) {
344 loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, filePath
, _archs
, _platform
, realPath
);
345 mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
346 if ( mh
== nullptr ) {
347 // Don't add must be missing paths for dlopen as we don't cache dlopen closures
348 if (_isLaunchClosure
) {
349 // If we found the file then we want to skip it as its not a valid macho for this platform/arch
350 // We can't record skipped file mtime/inode for caches built on a different machine that it runs on.
351 // In that case, we expect the file to be mastered out, as otherwise we couldn't detect if its
352 // changed or not on the device side
353 if (fileFound
&& inodesMatchRuntime
) {
354 addSkippedFile(possiblePath
, fileFoundINode
, fileFoundMTime
);
356 addMustBeMissingPath(possiblePath
);
361 if ( linkageType
!= LinkageType::kDynamic
) {
362 // LC_LOAD_DYLIB can only link with dylibs, and DYLD_INSERT_LIBRARIES can only be dylibs
363 if ( !mh
->isDylib() ) {
364 _diag
.error("found '%s' which is not a dylib. Needed by '%s'", filePath
, forImageChain
.image
.path());
367 // verify this is compatable dylib version
368 const char* installName
;
369 uint32_t foundCompatVers
;
370 uint32_t foundCurrentVers
;
371 mh
->getDylibInstallName(&installName
, &foundCompatVers
, &foundCurrentVers
);
372 if ( (foundCompatVers
< compatVersion
) && mh
->enforceCompatVersion() ) {
374 char requiredStr
[32];
375 MachOFile::packedVersionToString(foundCompatVers
, foundStr
);
376 MachOFile::packedVersionToString(compatVersion
, requiredStr
);
377 _diag
.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'",
378 filePath
, foundStr
, requiredStr
, forImageChain
.image
.path());
382 else if ( mh
->isMainExecutable() ) {
383 // when dlopen()ing a main executable, it must be dynamic Position Independent Executable
384 if ( !mh
->isPIE() || !mh
->isDynamicExecutable() ) {
385 _diag
.error("not PIE");
389 // Use the realpath in the case where we loaded a symlink
390 // The closure must have recordered an alias path
391 if (realPath
[0] != '\0')
393 foundImageNum
= _startImageNum
+ _nextIndex
++;
394 _foundNonCachedImage
= true;
395 mustBuildClosure
= true;
396 unmapWhenDone
= true;
398 loadedFileInfo
.fileContent
= mh
;
401 if ( mh
->inDyldCache() ) {
402 // We may be loading from a symlink, so use the path in the cache which is the realpath
403 filePath
= _dyldImageArray
->imageForNum(foundImageNum
)->path();
406 // if path is not original path, or its an inserted path (as forEachInColonList uses a stack temporary)
407 if ( (filePath
!= loadPath
) || (linkageType
== LinkageType::kInserted
) ) {
408 if ( !mh
->inDyldCache() ) {
409 // possiblePath may be a temporary (stack) string, since we found file at that path, make it permanent
410 filePath
= strdup_temp(filePath
);
412 // check if this overrides what would have been found in cache
413 // This is the case where we didn't find the image with the path in the shared cache, perhaps as it used library paths
414 // but the path we requested had pointed in to the cache
415 // FIXME: What if load path is via an @rpath and we will override the cache?
416 if ( overrideImageNum
== 0 ) {
417 if ( _dyldImageArray
!= nullptr ) {
418 uint32_t dyldCacheImageIndex
;
419 if ( _dyldCache
->hasImagePath(loadPath
, dyldCacheImageIndex
) ) {
420 ImageNum possibleOverrideNum
= dyldCacheImageIndex
+1;
421 if ( possibleOverrideNum
!= foundImageNum
)
422 overrideImageNum
= possibleOverrideNum
;
428 // check if this is an iOSMac dylib that is overriding a macOS dylib in the dyld cache
429 if ( mh
->inDyldCache() && iOSSupport(filePath
) ) {
430 const char* twinPath
= &filePath
[18];
431 uint32_t dyldCacheImageIndex
;
432 if ( (_dyldCache
!= nullptr) && _dyldCache
->hasImagePath(twinPath
, dyldCacheImageIndex
) ) {
433 ImageNum possibleOverrideNum
= dyldCacheImageIndex
+1;
434 if ( possibleOverrideNum
!= foundImageNum
)
435 overrideImageNum
= possibleOverrideNum
;
439 if ( !markNeverUnload
) {
440 switch (linkageType
) {
441 case LinkageType::kStatic
:
442 // Static linkages can only be unloaded if the image loading us can be unloaded
443 markNeverUnload
= forImageChain
.image
.markNeverUnload
;
445 case LinkageType::kDynamic
:
446 markNeverUnload
= false;
448 case LinkageType::kInserted
:
449 // Inserted libraries must never be unloaded
450 markNeverUnload
= true;
455 if ( !markNeverUnload
) {
456 // If the parent didn't force us to be never unload, other conditions still may
457 markNeverUnload
= mh
->markNeverUnload(_diag
);
460 // Set the path again just in case it was strdup'ed.
461 loadedFileInfo
.path
= filePath
;
464 BuilderLoadedImage entry
;
465 entry
.loadedFileInfo
= loadedFileInfo
;
466 entry
.imageNum
= foundImageNum
;
467 entry
.unmapWhenDone
= unmapWhenDone
;
468 entry
.contentRebased
= contentRebased
;
469 entry
.hasInits
= hasInits
;
470 entry
.markNeverUnload
= markNeverUnload
;
471 entry
.rtldLocal
= false;
472 entry
.isBadImage
= false;
473 entry
.mustBuildClosure
= mustBuildClosure
;
474 entry
.hasMissingWeakImports
= false;
475 entry
.hasInterposingTuples
= !mh
->inDyldCache() && mh
->hasInterposingTuples();
476 entry
.overrideImageNum
= overrideImageNum
;
477 entry
.exportsTrieOffset
= 0;
478 entry
.exportsTrieSize
= 0;
479 _loadedImages
.push_back(entry
);
480 foundImage
= &_loadedImages
.back();
481 if ( isFallbackPath
)
482 _fallbackPathUsed
= true;
487 stopPathVariant
= true;
490 // If we found a file, but also had an error, then we must have logged a diagnostic for a file we couldn't use.
491 // Clear that for now.
492 // FIXME: Surface this to the user in case they wanted to see the error
493 if (result
&& _diag
.hasError())
499 bool ClosureBuilder::expandAtLoaderPath(const char* loadPath
, bool fromLCRPATH
, const BuilderLoadedImage
& loadedImage
, char fixedPath
[])
501 switch ( _atPathHandling
) {
504 case AtPath::onlyInRPaths
:
505 if ( !fromLCRPATH
) {
506 // <rdar://42360708> allow @loader_path in LC_LOAD_DYLIB during dlopen()
507 if ( _isLaunchClosure
)
514 if ( strncmp(loadPath
, "@loader_path/", 13) == 0 ) {
515 strlcpy(fixedPath
, loadedImage
.path(), PATH_MAX
);
516 char* lastSlash
= strrchr(fixedPath
, '/');
517 if ( lastSlash
!= nullptr ) {
518 strcpy(lastSlash
+1, &loadPath
[13]);
522 else if ( fromLCRPATH
&& (strcmp(loadPath
, "@loader_path") == 0) ) {
523 // <rdar://problem/52881387> in LC_RPATH allow "@loader_path" without trailing slash
524 strlcpy(fixedPath
, loadedImage
.path(), PATH_MAX
);
525 char* lastSlash
= strrchr(fixedPath
, '/');
526 if ( lastSlash
!= nullptr ) {
535 bool ClosureBuilder::expandAtExecutablePath(const char* loadPath
, bool fromLCRPATH
, bool fromLCRPATHinMain
, char fixedPath
[])
537 switch ( _atPathHandling
) {
540 case AtPath::onlyInRPaths
:
543 // main executables can always have an LC_RPATH that uses @executable_path, other images cannot if restricted
544 if ( !fromLCRPATHinMain
)
551 if ( strncmp(loadPath
, "@executable_path/", 17) == 0 ) {
552 strlcpy(fixedPath
, _mainProgLoadPath
, PATH_MAX
);
553 char* lastSlash
= strrchr(fixedPath
, '/');
554 if ( lastSlash
!= nullptr ) {
555 strcpy(lastSlash
+1, &loadPath
[17]);
559 else if ( fromLCRPATH
&& (strcmp(loadPath
, "@executable_path") == 0) ) {
560 // <rdar://problem/52881387> in LC_RPATH allow "@executable_path" without trailing slash
561 strlcpy(fixedPath
, _mainProgLoadPath
, PATH_MAX
);
562 char* lastSlash
= strrchr(fixedPath
, '/');
563 if ( lastSlash
!= nullptr ) {
572 void ClosureBuilder::forEachResolvedPathVar(const char* loadPath
, const LoadedImageChain
& forImageChain
,
573 bool implictRPath
, LinkageType linkageType
,
574 void (^handler
)(const char* possiblePath
, bool& stop
))
576 // don't expand @loader_path or @executable_path if disallowed
577 if ( (_atPathHandling
== AtPath::none
) && (loadPath
[0] == '@') && (loadPath
[1] != 'r') ) {
579 handler(loadPath
, stop
);
583 // quick out if not @ path or not implicit rpath
584 if ( !implictRPath
&& (loadPath
[0] != '@') ) {
586 handler(loadPath
, stop
);
590 // expand @loader_path
591 // Note this isn't supported for DYLD_INSERT_LIBRARIES
592 BLOCK_ACCCESSIBLE_ARRAY(char, tempPath
, PATH_MAX
); // read as: char tempPath[PATH_MAX];
593 if ( (linkageType
!= LinkageType::kInserted
) && expandAtLoaderPath(loadPath
, false, forImageChain
.image
, tempPath
) ) {
595 handler(tempPath
, stop
);
599 // expand @executable_path
600 // Note this is supported for DYLD_INSERT_LIBRARIES
601 if ( expandAtExecutablePath(loadPath
, false, false, tempPath
) ) {
603 handler(tempPath
, stop
);
608 // Note this isn't supported for DYLD_INSERT_LIBRARIES
609 const char* rpathTail
= nullptr;
610 char implicitRpathBuffer
[PATH_MAX
];
611 if ( linkageType
!= LinkageType::kInserted
) {
612 if ( strncmp(loadPath
, "@rpath/", 7) == 0 ) {
613 // note: rpathTail starts with '/'
614 rpathTail
= &loadPath
[6];
616 else if ( implictRPath
) {
617 // make rpathTail starts with '/'
618 strlcpy(implicitRpathBuffer
, "/", PATH_MAX
);
619 strlcat(implicitRpathBuffer
, loadPath
, PATH_MAX
);
620 rpathTail
= implicitRpathBuffer
;
623 if ( rpathTail
!= nullptr ) {
624 // rpath is expansion is technically a stack of rpath dirs built starting with main executable and pushing
625 // LC_RPATHS from each dylib as they are recursively loaded. Our imageChain represents that stack.
626 __block
bool done
= false;
627 for (const LoadedImageChain
* link
= &forImageChain
; (link
!= nullptr) && !done
; link
= link
->previous
) {
628 bool mainExecutable
= link
->image
.loadAddress()->isMainExecutable();
629 link
->image
.loadAddress()->forEachRPath(^(const char* rPath
, bool& stop
) {
630 // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.loadedFileInfo.path);
631 if ( expandAtLoaderPath(rPath
, true, link
->image
, tempPath
) || expandAtExecutablePath(rPath
, true, mainExecutable
, tempPath
) ) {
632 // @loader_path allowed and expended
633 strlcat(tempPath
, rpathTail
, PATH_MAX
);
634 handler(tempPath
, stop
);
636 else if ( rPath
[0] == '/' ) {
637 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
638 if ( (_platform
== Platform::iOS
) && (strncmp(rPath
, "/usr/lib/swift", 14) == 0) ) {
639 // LC_RPATH is to /usr/lib/swift, but running on macOS that is /System/iOSSupport/usr/lib/swift
640 strlcpy(tempPath
, "/System/iOSSupport", PATH_MAX
);
641 strlcat(tempPath
, rPath
, PATH_MAX
);
642 strlcat(tempPath
, rpathTail
, PATH_MAX
);
643 handler(tempPath
, stop
);
650 // LC_RPATH is an absolute path, not blocked by AtPath::none
651 strlcpy(tempPath
, rPath
, PATH_MAX
);
652 strlcat(tempPath
, rpathTail
, PATH_MAX
);
653 handler(tempPath
, stop
);
658 if ( _fileSystem
.fileExists(tempPath
) ) {
660 result
= strdup_temp(tempPath
);
663 // Don't add must be missing paths for dlopen as we don't cache dlopen closures
664 if (_isLaunchClosure
) {
665 addMustBeMissingPath(tempPath
);
676 handler(loadPath
, stop
);
679 const char* ClosureBuilder::strdup_temp(const char* path
) const
681 if ( _tempPaths
== nullptr )
682 _tempPaths
= PathPool::allocate();
683 return _tempPaths
->add(path
);
686 void ClosureBuilder::addMustBeMissingPath(const char* path
)
688 //fprintf(stderr, "must be missing: %s\n", path);
689 if ( _mustBeMissingPaths
== nullptr )
690 _mustBeMissingPaths
= PathPool::allocate();
691 // don't add path if already in list
692 if ( !_mustBeMissingPaths
->contains(path
) )
693 _mustBeMissingPaths
->add(path
);
696 void ClosureBuilder::addSkippedFile(const char* path
, uint64_t inode
, uint64_t mtime
)
698 _skippedFiles
.push_back({ strdup_temp(path
), inode
, mtime
});
701 ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(ImageNum imageNum
)
703 for (BuilderLoadedImage
& li
: _loadedImages
) {
704 if ( li
.imageNum
== imageNum
) {
708 for (BuilderLoadedImage
& li
: _loadedImages
) {
709 if ( li
.overrideImageNum
== imageNum
) {
713 assert(0 && "LoadedImage not found by num");
716 const ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(ImageNum imageNum
) const
718 for (const BuilderLoadedImage
& li
: _loadedImages
) {
719 if ( li
.imageNum
== imageNum
) {
723 for (const BuilderLoadedImage
& li
: _loadedImages
) {
724 if ( li
.overrideImageNum
== imageNum
) {
728 assert(0 && "LoadedImage not found");
731 ClosureBuilder::BuilderLoadedImage
& ClosureBuilder::findLoadedImage(const MachOAnalyzer
* mh
)
733 for (BuilderLoadedImage
& li
: _loadedImages
) {
734 if ( li
.loadAddress() == mh
) {
738 assert(0 && "LoadedImage not found by mh");
741 const MachOAnalyzer
* ClosureBuilder::machOForImageNum(ImageNum imageNum
)
743 return findLoadedImage(imageNum
).loadAddress();
746 const MachOAnalyzer
* ClosureBuilder::findDependent(const MachOLoaded
* mh
, uint32_t depIndex
)
748 for (const BuilderLoadedImage
& li
: _loadedImages
) {
749 if ( li
.loadAddress() == mh
) {
751 // Bad image duting building group 1 closures, so the dependents array
752 // is potentially incomplete.
755 ImageNum childNum
= li
.dependents
[depIndex
].imageNum();
756 // This is typically something like a missing weak-dylib we are re-exporting a weak-import symbol from
757 if (childNum
== kMissingWeakLinkedImage
)
759 return machOForImageNum(childNum
);
765 ImageNum
ClosureBuilder::imageNumForMachO(const MachOAnalyzer
* mh
)
767 for (const BuilderLoadedImage
& li
: _loadedImages
) {
768 if ( li
.loadAddress() == mh
) {
772 assert(0 && "unknown mach-o");
776 void ClosureBuilder::recursiveLoadDependents(LoadedImageChain
& forImageChain
, bool canUseSharedCacheClosure
)
778 // if dependents is set, then we have already loaded this
779 if ( forImageChain
.image
.dependents
.begin() != nullptr )
782 uintptr_t startDepIndex
= _dependencies
.count();
784 __block
uint32_t depIndex
= 0;
785 forImageChain
.image
.loadAddress()->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
786 Image::LinkKind kind
= Image::LinkKind::regular
;
788 kind
= Image::LinkKind::weak
;
789 else if ( isReExport
)
790 kind
= Image::LinkKind::reExport
;
792 kind
= Image::LinkKind::upward
;
793 BuilderLoadedImage
* foundImage
;
794 if ( findImage(loadPath
, forImageChain
, foundImage
, LinkageType::kStatic
, compatVersion
, canUseSharedCacheClosure
) ) {
795 ImageNum foundImageNum
= foundImage
->imageNum
;
796 if ( _diag
.noError() )
797 _dependencies
.push_back(Image::LinkedImage(kind
, foundImageNum
));
800 _dependencies
.push_back(Image::LinkedImage(Image::LinkKind::weak
, kMissingWeakLinkedImage
));
801 // <rdar://problem/54387345> don't let an error loading weak dylib cause everything to fail
802 // _diag is checked after each dependent load, so if there is an error it was with loading the current dylib.
803 // Since it is a weak load, it is ok to ignore and and go on.
807 BLOCK_ACCCESSIBLE_ARRAY(char, extra
, 4096);
809 const char* targetLeaf
= strrchr(loadPath
, '/');
810 if ( targetLeaf
== nullptr )
811 targetLeaf
= loadPath
;
812 if ( _mustBeMissingPaths
!= nullptr ) {
813 strcpy(extra
, ", tried but didn't find: ");
814 _mustBeMissingPaths
->forEachPath(^(const char* aPath
) {
815 const char* aLeaf
= strrchr(aPath
, '/');
816 if ( aLeaf
== nullptr )
818 if ( strcmp(targetLeaf
, aLeaf
) == 0 ) {
819 strlcat(extra
, "'", 4096);
820 strlcat(extra
, aPath
, 4096);
821 strlcat(extra
, "' ", 4096);
825 if ( !_skippedFiles
.empty() ) {
826 strcpy(extra
, ", tried but invalid: ");
827 for (const SkippedFile
& skippedFile
: _skippedFiles
) {
828 const char* aPath
= skippedFile
.path
;
829 const char* aLeaf
= strrchr(aPath
, '/');
830 if ( aLeaf
== nullptr )
832 if ( strcmp(targetLeaf
, aLeaf
) == 0 ) {
833 strlcat(extra
, "'", 4096);
834 strlcat(extra
, aPath
, 4096);
835 strlcat(extra
, "' ", 4096);
839 if ( _diag
.hasError() ) {
840 #if BUILDING_CACHE_BUILDER
841 std::string errorMessageBuffer
= _diag
.errorMessage();
842 const char* msg
= errorMessageBuffer
.c_str();
844 const char* msg
= _diag
.errorMessage();
846 char msgCopy
[strlen(msg
)+4];
847 strcpy(msgCopy
, msg
);
848 _diag
.error("dependent dylib '%s' not found for '%s'. %s", loadPath
, forImageChain
.image
.path(), msgCopy
);
851 _diag
.error("dependent dylib '%s' not found for '%s'%s", loadPath
, forImageChain
.image
.path(), extra
);
853 if ( _launchErrorInfo
!= nullptr ) {
854 _launchErrorInfo
->kind
= DYLD_EXIT_REASON_DYLIB_MISSING
;
855 _launchErrorInfo
->clientOfDylibPath
= strdup_temp(forImageChain
.image
.path());
856 _launchErrorInfo
->targetDylibPath
= strdup_temp(loadPath
);
857 _launchErrorInfo
->symbol
= nullptr;
861 if ( _diag
.hasError() )
864 if ( _diag
.hasError() )
866 forImageChain
.image
.dependents
= _dependencies
.subArray(startDepIndex
, depIndex
);
868 // breadth first recurse
869 for (Image::LinkedImage dep
: forImageChain
.image
.dependents
) {
870 // don't recurse upwards
871 if ( dep
.kind() == Image::LinkKind::upward
)
873 // don't recurse down missing weak links
874 if ( (dep
.kind() == Image::LinkKind::weak
) && (dep
.imageNum() == kMissingWeakLinkedImage
) )
876 BuilderLoadedImage
& depLoadedImage
= findLoadedImage(dep
.imageNum());
877 LoadedImageChain chain
= { &forImageChain
, depLoadedImage
};
878 recursiveLoadDependents(chain
, canUseSharedCacheClosure
);
879 if ( _diag
.hasError() )
884 void ClosureBuilder::loadDanglingUpwardLinks(bool canUseSharedCacheClosure
)
888 danglingFixed
= false;
889 for (BuilderLoadedImage
& li
: _loadedImages
) {
890 if ( li
.dependents
.begin() == nullptr ) {
891 // this image has not have dependents set (probably a dangling upward link or referenced by upward link)
892 LoadedImageChain chain
= { nullptr, li
};
893 recursiveLoadDependents(chain
, canUseSharedCacheClosure
);
894 danglingFixed
= true;
898 } while (danglingFixed
&& _diag
.noError());
901 bool ClosureBuilder::overridableDylib(const BuilderLoadedImage
& forImage
)
903 // on macOS, the cache can be customer/development in the basesystem/main OS
904 // on embedded platforms with Internal cache, allow overrides
905 // on customer caches, only allow libdispatch.dylib to be overridden
906 return _dyldCache
->isOverridablePath(forImage
.path());
909 void ClosureBuilder::buildImage(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
911 const MachOAnalyzer
* macho
= forImage
.loadAddress();
913 writer
.setImageNum(forImage
.imageNum
);
916 writer
.setHasWeakDefs(macho
->hasWeakDefs());
917 writer
.setIsBundle(macho
->isBundle());
918 writer
.setIsDylib(macho
->isDylib());
919 writer
.setIs64(macho
->is64());
920 writer
.setIsExecutable(macho
->isMainExecutable());
921 writer
.setUses16KPages(macho
->uses16KPages());
922 if ( macho
->inDyldCache() ) {
923 // only set on dylibs in the dyld shared cache
924 writer
.setOverridableDylib(overridableDylib(forImage
));
926 writer
.setInDyldCache(macho
->inDyldCache());
927 if ( macho
->hasObjC() ) {
928 writer
.setHasObjC(true);
929 bool hasPlusLoads
= macho
->hasPlusLoadMethod(_diag
);
930 writer
.setHasPlusLoads(hasPlusLoads
);
932 forImage
.hasInits
= true;
935 writer
.setHasObjC(false);
936 writer
.setHasPlusLoads(false);
939 if ( forImage
.markNeverUnload
) {
940 writer
.setNeverUnload(true);
943 #if BUILDING_DYLD || BUILDING_LIBDYLD
944 if ( _foundDyldCacheRoots
) {
945 // If we had roots, then some images are potentially on-disk while others are
946 // being rebuilt for a new initializer order, but do not exist on disk
947 if ( macho
->inDyldCache() && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
948 // don't add file info for shared cache files mastered out of final file system
951 // file is either not in cache or is in cache but not mastered out
952 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
955 // shared cache not built by dyld or libdyld.dylib, so must be real file
956 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
959 // in cache builder code
960 if ( !_dyldCache
->header
.dylibsExpectedOnDisk
) {
961 // don't add file info for shared cache files mastered out of final file system
962 // This also covers executable and dlopen closures as we are not running on a live
963 // file system. no we don't have access to accurate inode/mtime
966 // file is either not in cache or is in cache but not mastered out
967 writer
.setFileInfo(forImage
.loadedFileInfo
.inode
, forImage
.loadedFileInfo
.mtime
);
971 // add info on how to load image
972 if ( !macho
->inDyldCache() ) {
973 writer
.setMappingInfo(forImage
.loadedFileInfo
.sliceOffset
, macho
->mappedSize());
974 // add code signature, if signed
975 uint32_t codeSigFileOffset
;
976 uint32_t codeSigSize
;
977 if ( macho
->hasCodeSignature(codeSigFileOffset
, codeSigSize
) ) {
978 writer
.setCodeSignatureLocation(codeSigFileOffset
, codeSigSize
);
979 macho
->forEachCDHash(^(const uint8_t *cdHash
) {
980 writer
.addCDHash(cdHash
);
983 // add FairPlay encryption range if encrypted
984 uint32_t fairPlayFileOffset
;
985 uint32_t fairPlaySize
;
986 if ( macho
->isFairPlayEncrypted(fairPlayFileOffset
, fairPlaySize
) ) {
987 writer
.setFairPlayEncryptionRange(fairPlayFileOffset
, fairPlaySize
);
992 writer
.addPath(forImage
.path());
993 if ( _aliases
!= nullptr ) {
994 for (const CachedDylibAlias
& alias
: *_aliases
) {
995 if ( strcmp(alias
.realPath
, forImage
.path()) == 0 )
996 writer
.addPath(alias
.aliasPath
);
1000 // set uuid, if has one
1002 if ( macho
->getUuid(uuid
) )
1003 writer
.setUUID(uuid
);
1006 writer
.setDependents(forImage
.dependents
);
1009 addSegments(writer
, macho
);
1011 // if shared cache contains two variants of same framework (macOS and iOS), mark iOS one as override of macOS one
1012 if ( _makingDyldCacheImages
&& iOSSupport(forImage
.path()) ) {
1013 const char* truncName
= forImage
.path()+18;
1014 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1015 if ( strcmp(li
.path(), truncName
) == 0 ) {
1016 writer
.setAsOverrideOf(li
.imageNum
);
1021 // record if this dylib overrides something in the cache
1022 if ( forImage
.overrideImageNum
!= 0 ) {
1023 writer
.setAsOverrideOf(forImage
.overrideImageNum
);
1024 const char* overridePath
= _dyldImageArray
->imageForNum(forImage
.overrideImageNum
)->path();
1025 writer
.addPath(overridePath
);
1026 if ( strcmp(overridePath
, "/usr/lib/system/libdyld.dylib") == 0 )
1027 _libDyldImageNum
= forImage
.imageNum
;
1028 else if ( strcmp(overridePath
, "/usr/lib/libSystem.B.dylib") == 0 )
1029 _libSystemImageNum
= forImage
.imageNum
;
1032 // record fix up info
1033 if ( macho
->inDyldCache() && !_makingDyldCacheImages
) {
1034 // when building app closures, don't record fix up info about dylibs in the cache
1036 else if ( _makeMinimalClosure
) {
1037 // don't record fix up info in dyld3s mode
1038 writer
.setFixupsNotEncoded();
1040 else if ( !_makingDyldCacheImages
&& macho
->hasChainedFixups() ) {
1041 // when building app closures, just evaluate target of chain binds and record that table
1042 addChainedFixupInfo(writer
, forImage
);
1045 // run rebase/bind opcodes or chained fixups
1046 addFixupInfo(writer
, forImage
);
1048 if ( _diag
.hasError() ) {
1049 writer
.setInvalid();
1055 #if BUILDING_CACHE_BUILDER
1057 // In the shared cache builder, we'll only ever see 'inDyldCache' images here for the shared
1058 // cache dylibs themselves. These are in an intermediate state where the cache is not live, the pointers
1059 // are unslid, but the pointers also don't contain fixup chains
1060 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= macho
->makeVMAddrConverter(forImage
.contentRebased
);
1061 if ( macho
->inDyldCache() ) {
1062 vmAddrConverter
.preferredLoadAddress
= 0;
1063 vmAddrConverter
.slide
= 0;
1064 vmAddrConverter
.chainedPointerFormat
= 0;
1065 vmAddrConverter
.contentRebased
= false;
1066 vmAddrConverter
.sharedCacheChainedPointerFormat
= MachOAnalyzer::VMAddrConverter::SharedCacheFormat::none
;
1071 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= macho
->makeVMAddrConverter(forImage
.contentRebased
);
1072 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
1073 // The shared cache is always live in dyld/libdyld, but if we get here then we are an offline tool
1074 // In that case, use the shared cache vmAddrConverter if we need it
1075 if ( macho
->inDyldCache() )
1076 vmAddrConverter
= _dyldCache
->makeVMAddrConverter(forImage
.contentRebased
);
1079 #endif // BUILDING_CACHE_BUILDER
1081 __block
unsigned initCount
= 0;
1082 Diagnostics initializerDiag
;
1083 macho
->forEachInitializer(initializerDiag
, vmAddrConverter
, ^(uint32_t offset
) {
1086 if ( initializerDiag
.noError() ) {
1087 if ( initCount
!= 0 ) {
1088 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets
, initCount
);
1089 __block
unsigned index
= 0;
1090 macho
->forEachInitializer(_diag
, vmAddrConverter
, ^(uint32_t offset
) {
1091 initOffsets
[index
++] = offset
;
1093 writer
.setInitOffsets(initOffsets
, initCount
);
1094 forImage
.hasInits
= true;
1098 // mod_init_func section is malformed, might be self modifying pointers
1099 macho
->forEachInitializerPointerSection(_diag
, ^(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
) {
1100 writer
.setInitSectRange(sectionOffset
, sectionSize
);
1101 forImage
.hasInits
= true;
1106 // add terminators (except for dylibs in the cache because they are never unloaded)
1107 if ( !macho
->inDyldCache() ) {
1108 __block
unsigned termCount
= 0;
1109 macho
->forEachTerminator(_diag
, vmAddrConverter
, ^(uint32_t offset
) {
1112 if ( termCount
!= 0 ) {
1113 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, termOffsets
, termCount
);
1114 __block
unsigned index
= 0;
1115 macho
->forEachTerminator(_diag
, vmAddrConverter
, ^(uint32_t offset
) {
1116 termOffsets
[index
++] = offset
;
1118 writer
.setTermOffsets(termOffsets
, termCount
);
1122 // record if image has DOF sections
1123 STACK_ALLOC_ARRAY(uint32_t, dofSectionOffsets
, 256);
1124 macho
->forEachDOFSection(_diag
, ^(uint32_t offset
) {
1125 dofSectionOffsets
.push_back(offset
);
1127 if ( !dofSectionOffsets
.empty() ) {
1128 writer
.setDofOffsets(dofSectionOffsets
);
1133 void ClosureBuilder::addSegments(ImageWriter
& writer
, const MachOAnalyzer
* mh
)
1135 const uint32_t segCount
= mh
->segmentCount();
1136 if ( mh
->inDyldCache() ) {
1137 uint64_t cacheUnslideBaseAddress
= _dyldCache
->unslidLoadAddress();
1138 BLOCK_ACCCESSIBLE_ARRAY(Image::DyldCacheSegment
, segs
, segCount
);
1139 mh
->forEachSegment(^(const MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
1140 segs
[info
.segIndex
] = { (uint32_t)(info
.vmAddr
-cacheUnslideBaseAddress
), (uint32_t)info
.vmSize
, info
.protections
};
1142 writer
.setCachedSegments(segs
, segCount
);
1145 const uint32_t pageSize
= (mh
->uses16KPages() ? 0x4000 : 0x1000);
1146 __block
uint32_t diskSegIndex
= 0;
1147 __block
uint32_t totalPageCount
= 0;
1148 __block
uint32_t lastFileOffsetEnd
= 0;
1149 __block
uint64_t lastVmAddrEnd
= 0;
1150 BLOCK_ACCCESSIBLE_ARRAY(Image::DiskSegment
, dsegs
, segCount
*3); // room for padding
1151 mh
->forEachSegment(^(const MachOAnalyzer::SegmentInfo
& info
, bool& stop
) {
1152 if ( (info
.fileOffset
!= 0) && (info
.fileOffset
!= lastFileOffsetEnd
) ) {
1153 Image::DiskSegment filePadding
;
1154 filePadding
.filePageCount
= (info
.fileOffset
- lastFileOffsetEnd
)/pageSize
;
1155 filePadding
.vmPageCount
= 0;
1156 filePadding
.permissions
= 0;
1157 filePadding
.paddingNotSeg
= 1;
1158 dsegs
[diskSegIndex
++] = filePadding
;
1160 if ( (lastVmAddrEnd
!= 0) && (info
.vmAddr
!= lastVmAddrEnd
) ) {
1161 Image::DiskSegment vmPadding
;
1162 vmPadding
.filePageCount
= 0;
1163 vmPadding
.vmPageCount
= (info
.vmAddr
- lastVmAddrEnd
)/pageSize
;
1164 vmPadding
.permissions
= 0;
1165 vmPadding
.paddingNotSeg
= 1;
1166 dsegs
[diskSegIndex
++] = vmPadding
;
1167 totalPageCount
+= vmPadding
.vmPageCount
;
1170 Image::DiskSegment segInfo
;
1171 segInfo
.filePageCount
= (info
.fileSize
+pageSize
-1)/pageSize
;
1172 segInfo
.vmPageCount
= (info
.vmSize
+pageSize
-1)/pageSize
;
1173 segInfo
.permissions
= info
.protections
& 7;
1174 segInfo
.paddingNotSeg
= 0;
1175 if ( info
.readOnlyData
)
1176 segInfo
.permissions
= Image::DiskSegment::kReadOnlyDataPermissions
;
1177 dsegs
[diskSegIndex
++] = segInfo
;
1178 totalPageCount
+= segInfo
.vmPageCount
;
1179 if ( info
.fileSize
!= 0 )
1180 lastFileOffsetEnd
= (uint32_t)(info
.fileOffset
+ info
.fileSize
);
1181 if ( info
.vmSize
!= 0 )
1182 lastVmAddrEnd
= info
.vmAddr
+ info
.vmSize
;
1185 writer
.setDiskSegments(dsegs
, diskSegIndex
);
1189 static bool isTupleFixup(uint64_t tupleSectVmStartOffset
, uint64_t tupleSectVmEndOffset
, uint64_t imageOffsetOfFixup
, uint32_t entrySize
, uint32_t& tupleIndex
)
1191 if ( imageOffsetOfFixup
< tupleSectVmStartOffset
)
1193 if ( imageOffsetOfFixup
> tupleSectVmEndOffset
)
1195 uint64_t offsetIntoSection
= imageOffsetOfFixup
- tupleSectVmStartOffset
;
1196 tupleIndex
= (uint32_t)(offsetIntoSection
/entrySize
);
1197 return (tupleIndex
*entrySize
== offsetIntoSection
) || ((tupleIndex
*entrySize
+entrySize
/2) == offsetIntoSection
);
1200 void ClosureBuilder::addInterposingTuples(LaunchClosureWriter
& writer
, const Image
* image
, const MachOAnalyzer
* mh
)
1202 const unsigned pointerSize
= mh
->pointerSize();
1203 const uint64_t baseAddress
= mh
->preferredLoadAddress();
1204 mh
->forEachInterposingSection(_diag
, ^(uint64_t sectVmOffset
, uint64_t sectVmSize
, bool &stop
) {
1205 const uint32_t entrySize
= 2*pointerSize
;
1206 const uint32_t tupleCount
= (uint32_t)(sectVmSize
/entrySize
);
1207 const uint64_t sectVmEndOffset
= sectVmOffset
+ sectVmSize
;
1208 BLOCK_ACCCESSIBLE_ARRAY(InterposingTuple
, resolvedTuples
, tupleCount
);
1209 for (uint32_t i
=0; i
< tupleCount
; ++i
) {
1210 resolvedTuples
[i
].stockImplementation
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1211 resolvedTuples
[i
].stockImplementation
.absolute
.value
= 0;
1212 resolvedTuples
[i
].newImplementation
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1213 resolvedTuples
[i
].newImplementation
.absolute
.value
= 0;
1215 // figure out what the replacement (rebase) and replacement (bind) of the tuple point to
1216 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool& rebaseStop
) {
1217 uint32_t tupleIndex
;
1218 if ( isTupleFixup(sectVmOffset
, sectVmEndOffset
, imageOffsetToRebase
, entrySize
, tupleIndex
) ) {
1219 const void* content
= (uint8_t*)mh
+ imageOffsetToRebase
;
1220 uint64_t unslidTargetAddress
= mh
->is64() ? *(uint64_t*)content
: *(uint32_t*)content
;
1221 resolvedTuples
[tupleIndex
].newImplementation
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1222 resolvedTuples
[tupleIndex
].newImplementation
.image
.imageNum
= image
->imageNum();
1223 resolvedTuples
[tupleIndex
].newImplementation
.image
.offset
= unslidTargetAddress
- mh
->preferredLoadAddress();
1226 ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &bindStop
) {
1227 uint32_t tupleIndex
;
1228 if ( isTupleFixup(sectVmOffset
, sectVmEndOffset
, imageOffsetToBind
, entrySize
, tupleIndex
) ) {
1229 resolvedTuples
[tupleIndex
].stockImplementation
= bindTarget
;
1232 ^(uint64_t imageOffsetToStartsInfo
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& chainStop
) {
1233 mh
->withChainStarts(_diag
, imageOffsetToStartsInfo
, ^(const dyld_chained_starts_in_image
* startsInfo
) {
1234 mh
->forEachFixupInAllChains(_diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
1235 uint64_t fixupOffset
= (uint8_t*)fixupLoc
- (uint8_t*)mh
;
1236 uint32_t tupleIndex
;
1237 if ( !isTupleFixup(sectVmOffset
, sectVmEndOffset
, fixupOffset
, entrySize
, tupleIndex
) )
1239 uint32_t bindOrdinal
;
1241 uint64_t rebaseTargetOffset
;
1242 if ( fixupLoc
->isBind(segInfo
->pointer_format
, bindOrdinal
, addend
) ) {
1243 if ( bindOrdinal
< targets
.count() ) {
1244 resolvedTuples
[tupleIndex
].stockImplementation
= targets
[bindOrdinal
];
1247 _diag
.error("out of range bind ordinal %d (max %lu)", bindOrdinal
, targets
.count());
1251 else if ( fixupLoc
->isRebase(segInfo
->pointer_format
, baseAddress
, rebaseTargetOffset
) ) {
1252 resolvedTuples
[tupleIndex
].newImplementation
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1253 resolvedTuples
[tupleIndex
].newImplementation
.image
.imageNum
= image
->imageNum();
1254 resolvedTuples
[tupleIndex
].newImplementation
.image
.offset
= rebaseTargetOffset
;
1259 ^(uint64_t imageOffsetToFixup
) {
1260 // objc optimisation can't be interposed so nothing to do here.
1262 ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &bindStop
) {
1263 // objc protocol optimisation fixups can't be interposed so nothing to do here.
1265 ^(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool &fixupStop
) {
1266 // objc selector optimisation fixups can't be interposed so nothing to do here.
1268 ^(uint64_t imageOffsetToFixup
, bool &fixupStop
) {
1269 // objc stable Swift optimisation fixups can't be interposed so nothing to do here.
1271 ^(uint64_t imageOffsetToFixup
, bool &fixupStop
) {
1272 // objc method list optimisation fixups can't be interposed so nothing to do here.
1275 // remove any tuples in which both sides are not set (or target is weak-import NULL)
1276 STACK_ALLOC_ARRAY(InterposingTuple
, goodTuples
, tupleCount
);
1277 for (uint32_t i
=0; i
< tupleCount
; ++i
) {
1278 if ( (resolvedTuples
[i
].stockImplementation
.image
.kind
!= Image::ResolvedSymbolTarget::kindAbsolute
)
1279 && (resolvedTuples
[i
].newImplementation
.image
.kind
!= Image::ResolvedSymbolTarget::kindAbsolute
) )
1280 goodTuples
.push_back(resolvedTuples
[i
]);
1282 writer
.addInterposingTuples(goodTuples
);
1283 _interposingTuplesUsed
= !goodTuples
.empty();
1285 // if the target of the interposing is in the dyld shared cache, add a PatchEntry so the cache is fixed up at launch
1286 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, goodTuples
.count());
1287 for (const InterposingTuple
& aTuple
: goodTuples
) {
1288 if ( aTuple
.stockImplementation
.sharedCache
.kind
== Image::ResolvedSymbolTarget::kindSharedCache
) {
1289 uint32_t imageIndex
;
1290 assert(_dyldCache
->addressInText((uint32_t)aTuple
.stockImplementation
.sharedCache
.offset
, &imageIndex
));
1291 ImageNum imageInCache
= imageIndex
+1;
1292 Closure::PatchEntry patch
;
1293 patch
.exportCacheOffset
= (uint32_t)aTuple
.stockImplementation
.sharedCache
.offset
;
1294 patch
.overriddenDylibInCache
= imageInCache
;
1295 patch
.replacement
= aTuple
.newImplementation
;
1296 patches
.push_back(patch
);
1299 writer
.addCachePatches(patches
);
1303 const Image::RebasePattern
RebasePatternBuilder::_s_maxLeapPattern
= { 0xFFFFF, 0, 0xF};
1304 const uint64_t RebasePatternBuilder::_s_maxLeapCount
= _s_maxLeapPattern
.repeatCount
* _s_maxLeapPattern
.skipCount
;
1308 RebasePatternBuilder::RebasePatternBuilder(OverflowSafeArray
<closure::Image::RebasePattern
>& entriesStorage
, uint64_t ptrSize
)
1309 : _rebaseEntries(entriesStorage
), _lastLocation(-ptrSize
), _ptrSize(ptrSize
)
1313 void RebasePatternBuilder::add(uint64_t runtimeOffset
)
1315 const uint64_t delta
= runtimeOffset
- _lastLocation
;
1316 const bool aligned
= ((delta
% _ptrSize
) == 0);
1317 if ( delta
== _ptrSize
) {
1318 // this rebase location is contiguous to previous
1319 if ( _rebaseEntries
.back().contigCount
< 255 ) {
1320 // just bump previous's contigCount
1321 _rebaseEntries
.back().contigCount
++;
1324 // previous contiguous run already has max 255, so start a new run
1325 _rebaseEntries
.push_back({ 1, 1, 0 });
1328 else if ( aligned
&& (delta
<= (_ptrSize
*15)) ) {
1329 // this rebase is within skip distance of last rebase
1330 _rebaseEntries
.back().skipCount
= (uint8_t)((delta
-_ptrSize
)/_ptrSize
);
1331 int lastIndex
= (int)(_rebaseEntries
.count() - 1);
1332 if ( lastIndex
> 1 ) {
1333 if ( (_rebaseEntries
[lastIndex
].contigCount
== _rebaseEntries
[lastIndex
-1].contigCount
)
1334 && (_rebaseEntries
[lastIndex
].skipCount
== _rebaseEntries
[lastIndex
-1].skipCount
) ) {
1335 // this entry as same contig and skip as prev, so remove it and bump repeat count of previous
1336 _rebaseEntries
.pop_back();
1337 _rebaseEntries
.back().repeatCount
+= 1;
1340 _rebaseEntries
.push_back({ 1, 1, 0 });
1343 uint64_t advanceCount
= (delta
-_ptrSize
);
1344 if ( (runtimeOffset
< _lastLocation
) && (_lastLocation
!= -_ptrSize
) ) {
1345 // out of rebases! handle this be resting rebase offset to zero
1346 _rebaseEntries
.push_back({ 0, 0, 0 });
1347 advanceCount
= runtimeOffset
;
1349 // if next rebase is too far to reach with one pattern, use series
1350 while ( advanceCount
> _s_maxLeapCount
) {
1351 _rebaseEntries
.push_back(_s_maxLeapPattern
);
1352 advanceCount
-= _s_maxLeapCount
;
1354 // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate
1355 while ( advanceCount
> _s_maxLeapPattern
.repeatCount
) {
1356 uint64_t count
= advanceCount
/ _s_maxLeapPattern
.skipCount
;
1357 _rebaseEntries
.push_back({ (uint32_t)count
, 0, _s_maxLeapPattern
.skipCount
});
1358 advanceCount
-= (count
*_s_maxLeapPattern
.skipCount
);
1360 if ( advanceCount
!= 0 )
1361 _rebaseEntries
.push_back({ (uint32_t)advanceCount
, 0, 1 });
1362 _rebaseEntries
.push_back({ 1, 1, 0 });
1364 _lastLocation
= runtimeOffset
;
1369 BindPatternBuilder::BindPatternBuilder(OverflowSafeArray
<closure::Image::BindPattern
>& entriesStorage
, uint64_t ptrSize
)
1370 : _bindEntries(entriesStorage
), _ptrSize(ptrSize
), _lastOffset(-ptrSize
), _lastTarget({ {0, 0} })
1374 void BindPatternBuilder::add(uint64_t runtimeOffset
, Image::ResolvedSymbolTarget target
, bool weakBindCoalese
)
1376 if ( weakBindCoalese
) {
1377 // may be previous bind to this location
1378 // if so, update that rather create new BindPattern
1379 for (Image::BindPattern
& aBind
: _bindEntries
) {
1380 if ( (aBind
.startVmOffset
== runtimeOffset
) && (aBind
.repeatCount
== 1) && (aBind
.skipCount
== 0) ) {
1381 aBind
.target
= target
;
1386 bool mergedIntoPrevious
= false;
1387 if ( !mergedIntoPrevious
&& (target
== _lastTarget
) && (runtimeOffset
> _lastOffset
) && !_bindEntries
.empty() ) {
1388 uint64_t skipAmount
= (runtimeOffset
- _lastOffset
- _ptrSize
)/_ptrSize
;
1389 if ( skipAmount
*_ptrSize
!= (runtimeOffset
- _lastOffset
- _ptrSize
) ) {
1390 // misaligned pointer means we cannot optimize
1393 if ( (_bindEntries
.back().repeatCount
== 1) && (_bindEntries
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
1394 _bindEntries
.back().repeatCount
= 2;
1395 _bindEntries
.back().skipCount
= skipAmount
;
1396 assert(_bindEntries
.back().skipCount
== skipAmount
); // check overflow
1397 mergedIntoPrevious
= true;
1399 else if ( (_bindEntries
.back().skipCount
== skipAmount
) && (_bindEntries
.back().repeatCount
< 0xfff) ) {
1400 uint32_t prevRepeatCount
= _bindEntries
.back().repeatCount
;
1401 _bindEntries
.back().repeatCount
+= 1;
1402 assert(_bindEntries
.back().repeatCount
> prevRepeatCount
); // check overflow
1403 mergedIntoPrevious
= true;
1407 if ( (target
== _lastTarget
) && (runtimeOffset
== _lastOffset
) && !_bindEntries
.empty() ) {
1408 // duplicate bind for same location, ignore this one
1409 mergedIntoPrevious
= true;
1411 if ( !mergedIntoPrevious
) {
1412 Image::BindPattern pattern
;
1413 pattern
.target
= target
;
1414 pattern
.startVmOffset
= runtimeOffset
;
1415 pattern
.repeatCount
= 1;
1416 pattern
.skipCount
= 0;
1417 assert(pattern
.startVmOffset
== runtimeOffset
);
1418 _bindEntries
.push_back(pattern
);
1420 _lastTarget
= target
;
1421 _lastOffset
= runtimeOffset
;
1425 bool ClosureBuilder::mas_fromImageWeakDefLookup(const WrappedMachO
& fromWmo
, const char* symbolName
, uint64_t addend
, CachePatchHandler patcher
, FixupTarget
& target
) const
1427 // when building dylibs into the dyld cache, there is no load-order, so we cannot use the standard algorithm
1428 // otherwise call through to standard weak-def coalescing algorithm
1429 if ( !_makingDyldCacheImages
)
1430 return MachOAnalyzerSet::mas_fromImageWeakDefLookup(fromWmo
, symbolName
, addend
, patcher
, target
);
1433 // look first in /usr/lib/libc++, most will be here
1435 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1436 if ( li
.loadAddress()->hasWeakDefs() && (strncmp(li
.path(), "/usr/lib/libc++", 15) == 0) ) {
1437 WrappedMachO
libcxxWmo(li
.loadAddress(), this, (void*)&li
);
1438 if ( libcxxWmo
.findSymbolIn(diag
, symbolName
, addend
, target
) )
1443 // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself
1444 if ( fromWmo
.findSymbolIn(diag
, symbolName
, addend
, target
) )
1447 // if we link with something that also defines this weak-def, use it
1448 ClosureBuilder::BuilderLoadedImage
* fromImage
= (ClosureBuilder::BuilderLoadedImage
*)(fromWmo
._other
);
1449 for (Image::LinkedImage child
: fromImage
->dependents
) {
1450 if (child
.imageNum() == kMissingWeakLinkedImage
)
1452 if (child
.kind() == Image::LinkKind::upward
)
1454 const BuilderLoadedImage
& childLi
= findLoadedImage(child
.imageNum());
1455 if ( childLi
.loadAddress()->hasWeakDefs() ) {
1456 WrappedMachO
childWmo(childLi
.loadAddress(), this, (void*)&childLi
);
1457 if ( childWmo
.findSymbolIn(diag
, symbolName
, addend
, target
) )
1464 void ClosureBuilder::mas_forEachImage(void (^handler
)(const WrappedMachO
& wmo
, bool hidden
, bool& stop
)) const
1467 for (const ClosureBuilder::BuilderLoadedImage
& li
: _loadedImages
) {
1468 WrappedMachO
wmo(li
.loadAddress(), this, (void*)&li
);
1469 handler(wmo
, li
.rtldLocal
, stop
);
1475 bool ClosureBuilder::wmo_missingSymbolResolver(const WrappedMachO
* fromWmo
, bool weakImport
, bool lazyBind
, const char* symbolName
, const char* expectedInDylibPath
, const char* clientPath
, FixupTarget
& target
) const
1477 // if weakImport and missing, bind to NULL
1479 // construct NULL target
1480 target
.offsetInImage
= 0;
1481 target
.kind
= FixupTarget::Kind::bindAbsolute
;
1482 target
.requestedSymbolName
= symbolName
;
1483 target
.foundSymbolName
= nullptr;
1484 // Record that we found a missing weak import so that the objc optimizer doens't have to check
1485 ClosureBuilder::BuilderLoadedImage
* fromBLI
= (ClosureBuilder::BuilderLoadedImage
*)(fromWmo
->_other
);
1486 fromBLI
->hasMissingWeakImports
= true;
1489 // dyld3 binds everything ahead of time, to simulator lazy failure
1490 // if non-weakImport and lazy, then bind to __dyld_missing_symbol_abort()
1491 if ( lazyBind
&& _allowMissingLazies
) {
1492 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1493 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) {
1494 WrappedMachO
libdyldWmo(li
.loadAddress(), this, (void*)&li
);
1496 if ( libdyldWmo
.findSymbolIn(diag
, "__dyld_missing_symbol_abort", 0, target
) ) {
1497 // <rdar://problem/44315944> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace
1504 // support abort payload
1505 if ( _launchErrorInfo
!= nullptr ) {
1506 _launchErrorInfo
->kind
= DYLD_EXIT_REASON_SYMBOL_MISSING
;
1507 _launchErrorInfo
->clientOfDylibPath
= strdup_temp(clientPath
);
1508 _launchErrorInfo
->targetDylibPath
= strdup_temp(expectedInDylibPath
);
1509 _launchErrorInfo
->symbol
= symbolName
;
1514 void ClosureBuilder::mas_mainExecutable(WrappedMachO
& wmo
) const
1516 const ClosureBuilder::BuilderLoadedImage
& mainLi
= _loadedImages
[_mainProgLoadIndex
];
1517 WrappedMachO
mainWmo(mainLi
.loadAddress(), this, (void*)&mainLi
);
1521 void* ClosureBuilder::mas_dyldCache() const
1523 return (void*)_dyldCache
;
1526 bool ClosureBuilder::wmo_dependent(const WrappedMachO
* wmo
, uint32_t depIndex
, WrappedMachO
& childWmo
, bool& missingWeakDylib
) const
1528 ClosureBuilder::BuilderLoadedImage
* forImage
= (ClosureBuilder::BuilderLoadedImage
*)(wmo
->_other
);
1530 if ( depIndex
>= forImage
->dependents
.count() )
1533 ImageNum childNum
= forImage
->dependents
[depIndex
].imageNum();
1534 if ( childNum
== kMissingWeakLinkedImage
) {
1535 missingWeakDylib
= true;
1538 const BuilderLoadedImage
& depLoadedImage
= this->findLoadedImage(childNum
);
1539 childWmo
= WrappedMachO(depLoadedImage
.loadAddress(), this, (void*)&depLoadedImage
);
1540 missingWeakDylib
= false;
1544 const char* ClosureBuilder::wmo_path(const WrappedMachO
* wmo
) const
1546 ClosureBuilder::BuilderLoadedImage
* forImage
= (ClosureBuilder::BuilderLoadedImage
*)(wmo
->_other
);
1547 return forImage
->loadedFileInfo
.path
;
1550 MachOAnalyzerSet::ExportsTrie
ClosureBuilder::wmo_getExportsTrie(const WrappedMachO
* wmo
) const
1552 ClosureBuilder::BuilderLoadedImage
* forImage
= (ClosureBuilder::BuilderLoadedImage
*)(wmo
->_other
);
1553 if ( forImage
->exportsTrieOffset
== 0 ) {
1554 // if trie location not already cached, look it up
1555 wmo
->_mh
->hasExportTrie(forImage
->exportsTrieOffset
, forImage
->exportsTrieSize
);
1557 const uint8_t* start
= nullptr;
1558 const uint8_t* end
= nullptr;
1559 if ( forImage
->exportsTrieOffset
!= 0 ) {
1560 start
= (uint8_t*)wmo
->_mh
+ forImage
->exportsTrieOffset
;
1561 end
= start
+ forImage
->exportsTrieSize
;
1563 return { start
, end
};
1567 Image::ResolvedSymbolTarget
ClosureBuilder::makeResolvedTarget(const FixupTarget
& target
) const
1569 Image::ResolvedSymbolTarget resolvedTarget
;
1570 switch ( target
.kind
) {
1571 case MachOAnalyzerSet::FixupTarget::Kind::rebase
:
1572 assert(0 && "target is a rebase");
1574 case MachOAnalyzerSet::FixupTarget::Kind::bindToImage
:
1575 if ( target
.foundInImage
._mh
->inDyldCache() ) {
1576 resolvedTarget
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
1577 resolvedTarget
.sharedCache
.offset
= (uint8_t*)target
.foundInImage
._mh
- (uint8_t*)_dyldCache
+ target
.offsetInImage
;
1580 ClosureBuilder::BuilderLoadedImage
* targetBuildLoaderImage
= (ClosureBuilder::BuilderLoadedImage
*)(target
.foundInImage
._other
);
1581 resolvedTarget
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1582 resolvedTarget
.image
.imageNum
= targetBuildLoaderImage
->imageNum
;
1583 resolvedTarget
.image
.offset
= target
.offsetInImage
;
1585 return resolvedTarget
;
1586 case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute
:
1587 resolvedTarget
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
1588 resolvedTarget
.absolute
.value
= target
.offsetInImage
;
1589 return resolvedTarget
;
1590 case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol
:
1591 assert(0 && "unknown FixupTarget::Kind::bindMissingSymbol found in closure");
1594 assert(0 && "unknown FixupTarget kind");
1597 void ClosureBuilder::addFixupInfo(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1599 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern
, rebaseEntries
, 1024);
1600 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern
, binds
, 512);
1601 __block RebasePatternBuilder
rebaseBuilder(rebaseEntries
, forImage
.loadAddress()->pointerSize());
1602 __block BindPatternBuilder
bindBuilder(binds
, forImage
.loadAddress()->pointerSize());
1604 const bool stompedLazyOpcodes
= forImage
.loadAddress()->hasStompedLazyOpcodes();
1605 WrappedMachO
forImage_wmo(forImage
.loadAddress(), this, (void*)&forImage
);
1606 forImage_wmo
.forEachFixup(_diag
,
1607 ^(uint64_t fixupLocRuntimeOffset
, PointerMetaData pmd
, const MachOAnalyzerSet::FixupTarget
& target
, bool& stop
) {
1608 if ( target
.kind
== MachOAnalyzerSet::FixupTarget::Kind::rebase
) {
1609 // normally ignore rebase on lazy pointer because dyld3 will immediately bind that same pointer
1610 // but if app is licensewared and stomps lazy bind opcodes, keep the rebases
1611 if ( target
.isLazyBindRebase
&& !stompedLazyOpcodes
)
1614 if ( _dylibFixupHandler
) {
1615 // applying fixups to dylibs in dyld cache as the cache is being built
1616 _dylibFixupHandler(forImage
.loadAddress(), fixupLocRuntimeOffset
, pmd
, target
);
1619 switch ( target
.kind
) {
1620 case MachOAnalyzerSet::FixupTarget::Kind::rebase
:
1621 if ( !_leaveRebasesAsOpcodes
)
1622 rebaseBuilder
.add(fixupLocRuntimeOffset
);
1624 case MachOAnalyzerSet::FixupTarget::Kind::bindToImage
:
1625 case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute
:
1626 bindBuilder
.add(fixupLocRuntimeOffset
, makeResolvedTarget(target
), target
.weakCoalesced
);
1628 case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol
:
1629 // this is last call from forEachFixup() because a symbol could not be resolved
1633 ^(uint32_t cachedDylibIndex
, uint32_t exportCacheOffset
, const FixupTarget
& target
) {
1634 addWeakDefCachePatch(cachedDylibIndex
, exportCacheOffset
, target
);
1638 // check for __dyld section in main executable to support licenseware
1639 if ( forImage
.loadAddress()->filetype
== MH_EXECUTE
) {
1640 forImage
.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
1641 if ( (strcmp(sectInfo
.sectName
, "__dyld") == 0) && (strcmp(sectInfo
.segInfo
.segName
, "__DATA") == 0) ) {
1642 // find dyld3::compatFuncLookup in libdyld.dylib
1643 assert(_libDyldImageNum
!= 0);
1644 const BuilderLoadedImage
& libdyldImage
= findLoadedImage(_libDyldImageNum
);
1645 WrappedMachO
libdyldWmo(libdyldImage
.loadAddress(), this, (void*)&libdyldImage
);
1646 FixupTarget libdyldCompatTarget
;
1647 if ( libdyldWmo
.findSymbolIn(_diag
, "__ZN5dyld316compatFuncLookupEPKcPPv", 0, libdyldCompatTarget
) ) {
1648 // dyld_func_lookup is second pointer in __dyld section
1649 uint64_t fixupLocRuntimeOffset
= sectInfo
.sectAddr
- forImage
.loadAddress()->preferredLoadAddress() + forImage
.loadAddress()->pointerSize();
1650 bindBuilder
.add(fixupLocRuntimeOffset
, makeResolvedTarget(libdyldCompatTarget
), false);
1653 _diag
.error("libdyld.dylib is missing dyld3::compatFuncLookup");
1659 // add all rebase and bind info into closure, unless building dyld cache
1660 if ( !_makingDyldCacheImages
) {
1661 if ( _leaveRebasesAsOpcodes
)
1662 writer
.setRebasesNotEncoded();
1664 writer
.setRebaseInfo(rebaseEntries
);
1665 writer
.setBindInfo(binds
);
1668 // i386 programs also use text relocs to rebase stubs
1669 if ( (forImage
.loadAddress()->cputype
== CPU_TYPE_I386
) && !_makingDyldCacheImages
) {
1670 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern
, textRebases
, 512);
1671 __block
uint64_t lastOffset
= -4;
1672 forImage
.loadAddress()->forEachTextRebase(_diag
, ^(uint64_t runtimeOffset
, bool& stop
) {
1673 if ( textRebases
.freeCount() < 2 ) {
1674 _diag
.error("too many text rebase locations (%ld) in %s", textRebases
.maxCount(), writer
.currentImage()->path());
1677 bool mergedIntoPrevious
= false;
1678 if ( (runtimeOffset
> lastOffset
) && !textRebases
.empty() ) {
1679 uint32_t skipAmount
= (uint32_t)(runtimeOffset
- lastOffset
);
1680 if ( (textRebases
.back().repeatCount
== 1) && (textRebases
.back().skipCount
== 0) ) {
1681 textRebases
.back().repeatCount
= 2;
1682 textRebases
.back().skipCount
= skipAmount
;
1683 mergedIntoPrevious
= true;
1685 else if ( textRebases
.back().skipCount
== skipAmount
) {
1686 textRebases
.back().repeatCount
+= 1;
1687 mergedIntoPrevious
= true;
1690 if ( !mergedIntoPrevious
) {
1691 Image::TextFixupPattern pattern
;
1692 pattern
.target
.raw
= 0;
1693 pattern
.startVmOffset
= (uint32_t)runtimeOffset
;
1694 pattern
.repeatCount
= 1;
1695 pattern
.skipCount
= 0;
1696 textRebases
.push_back(pattern
);
1698 lastOffset
= runtimeOffset
;
1700 writer
.setTextRebaseInfo(textRebases
);
1708 void ClosureBuilder::addWeakDefCachePatch(uint32_t cachedDylibIndex
, uint32_t exportCacheOffset
, const FixupTarget
& patchTarget
)
1710 // minimal closures don't need weak def patches, they are regenerated at launch
1711 if ( _makeMinimalClosure
)
1714 // don't add duplicates
1715 for (const Closure::PatchEntry
& aPatch
: _weakDefCacheOverrides
) {
1716 if ( aPatch
.exportCacheOffset
== exportCacheOffset
)
1719 // add new patch entry
1720 ClosureBuilder::BuilderLoadedImage
* targetImage
= (ClosureBuilder::BuilderLoadedImage
*)(patchTarget
.foundInImage
._other
);
1721 Closure::PatchEntry patch
;
1722 patch
.overriddenDylibInCache
= cachedDylibIndex
+1; // convert image index to ImageNum
1723 patch
.exportCacheOffset
= exportCacheOffset
;
1724 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1725 patch
.replacement
.image
.imageNum
= targetImage
->imageNum
;
1726 patch
.replacement
.image
.offset
= patchTarget
.offsetInImage
;
1727 _weakDefCacheOverrides
.push_back(patch
);
1730 void ClosureBuilder::addChainedFixupInfo(ImageWriter
& writer
, BuilderLoadedImage
& forImage
)
1732 // as a side effect of building targets array, we discover if anything in dyld cache uses weak-defs that need
1733 // to be redirected to an impl in some other dylib (cache patched)
1734 auto patchAddr
= ^(uint32_t cachedDylibIndex
, uint32_t exportCacheOffset
, const FixupTarget
& patchTarget
) {
1735 addWeakDefCachePatch(cachedDylibIndex
, exportCacheOffset
, patchTarget
);
1738 // build array of targets
1739 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ResolvedSymbolTarget
, targets
, 1024);
1740 forImage
.loadAddress()->forEachChainedFixupTarget(_diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1742 WrappedMachO
forImageWmo(forImage
.loadAddress(), this, (void*)&forImage
);
1743 if ( wmo_findSymbolFrom(&forImageWmo
, _diag
, libOrdinal
, symbolName
, weakImport
, false, addend
, patchAddr
, target
) )
1744 targets
.push_back(makeResolvedTarget(target
));
1748 if ( _diag
.hasError() )
1751 // C++ main executables can overide operator new, check for that
1752 if ( forImage
.loadAddress()->isMainExecutable() && forImage
.loadAddress()->hasWeakDefs() ) {
1753 WrappedMachO
mainWmo(forImage
.loadAddress(), this, (void*)&forImage
);
1754 wmo_findExtraSymbolFrom(&mainWmo
, patchAddr
);
1757 uint64_t chainStartsOffset
= forImage
.loadAddress()->chainStartsOffset();
1758 writer
.setChainedFixups(chainStartsOffset
, targets
);
1761 void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex
, InitInfo initInfos
[], uint32_t& initOrder
, bool& hasError
)
1763 if ( initInfos
[loadIndex
].visited
)
1765 initInfos
[loadIndex
].visited
= true;
1766 initInfos
[loadIndex
].danglingUpward
= false;
1768 if (_loadedImages
[loadIndex
].isBadImage
) {
1772 for (const Image::LinkedImage
& dep
: _loadedImages
[loadIndex
].dependents
) {
1773 if ( dep
.imageNum() == kMissingWeakLinkedImage
)
1775 const ClosureBuilder::BuilderLoadedImage
& depLi
= findLoadedImage(dep
.imageNum());
1776 uint32_t depLoadIndex
= (uint32_t)_loadedImages
.index(depLi
);
1777 if ( dep
.kind() == Image::LinkKind::upward
) {
1778 if ( !initInfos
[depLoadIndex
].visited
)
1779 initInfos
[depLoadIndex
].danglingUpward
= true;
1782 depthFirstRecurseSetInitInfo(depLoadIndex
, initInfos
, initOrder
, hasError
);
1787 initInfos
[loadIndex
].initOrder
= initOrder
++;
1790 void ClosureBuilder::computeInitOrder(ImageWriter
& imageWriter
, uint32_t loadIndex
)
1792 // allocate array to track initializers
1793 InitInfo initInfos
[_loadedImages
.count()];
1794 bzero(initInfos
, sizeof(initInfos
));
1796 // recurse all images and build initializer list from bottom up
1797 uint32_t initOrder
= 1;
1798 bool hasMissingDependent
= false;
1799 depthFirstRecurseSetInitInfo(loadIndex
, initInfos
, initOrder
, hasMissingDependent
);
1800 if (hasMissingDependent
) {
1801 imageWriter
.setInvalid();
1805 // any images not visited yet are are danging, force add them to end of init list
1806 for (uint32_t i
=0; i
< (uint32_t)_loadedImages
.count(); ++i
) {
1807 if ( !initInfos
[i
].visited
&& initInfos
[i
].danglingUpward
) {
1808 depthFirstRecurseSetInitInfo(i
, initInfos
, initOrder
, hasMissingDependent
);
1812 if (hasMissingDependent
) {
1813 imageWriter
.setInvalid();
1817 // build array of just images with initializer
1818 STACK_ALLOC_ARRAY(uint32_t, indexOfImagesWithInits
, _loadedImages
.count());
1820 for (const BuilderLoadedImage
& li
: _loadedImages
) {
1821 if ( initInfos
[index
].visited
&& li
.hasInits
) {
1822 indexOfImagesWithInits
.push_back(index
);
1827 // bubble sort (FIXME)
1828 if ( indexOfImagesWithInits
.count() > 1 ) {
1829 for (uint32_t i
=0; i
< indexOfImagesWithInits
.count()-1; ++i
) {
1830 for (uint32_t j
=0; j
< indexOfImagesWithInits
.count()-i
-1; ++j
) {
1831 if ( initInfos
[indexOfImagesWithInits
[j
]].initOrder
> initInfos
[indexOfImagesWithInits
[j
+1]].initOrder
) {
1832 uint32_t temp
= indexOfImagesWithInits
[j
];
1833 indexOfImagesWithInits
[j
] = indexOfImagesWithInits
[j
+1];
1834 indexOfImagesWithInits
[j
+1] = temp
;
1840 // copy ImageNum of each image with initializers into array
1841 ImageNum initNums
[indexOfImagesWithInits
.count()];
1842 for (uint32_t i
=0; i
< indexOfImagesWithInits
.count(); ++i
) {
1843 initNums
[i
] = _loadedImages
[indexOfImagesWithInits
[i
]].imageNum
;
1846 // add to closure info
1847 imageWriter
.setInitsOrder(initNums
, (uint32_t)indexOfImagesWithInits
.count());
1850 void ClosureBuilder::addClosureInfo(LaunchClosureWriter
& closureWriter
)
1852 // record which is libSystem
1853 assert(_libSystemImageNum
!= 0);
1854 closureWriter
.setLibSystemImageNum(_libSystemImageNum
);
1856 // record which is libdyld
1857 assert(_libDyldImageNum
!= 0);
1858 const BuilderLoadedImage
& libdyldImage
= findLoadedImage(_libDyldImageNum
);
1859 WrappedMachO
libdyldWmo(libdyldImage
.loadAddress(), this, (void*)&libdyldImage
);
1860 FixupTarget libdyldEntryTarget
;
1861 if ( libdyldWmo
.findSymbolIn(_diag
, "__ZN5dyld318entryVectorForDyldE", 0, libdyldEntryTarget
) ) {
1862 const dyld3::LibDyldEntryVector
* libDyldEntry
= nullptr;
1863 if ( libdyldEntryTarget
.kind
== MachOAnalyzerSet::FixupTarget::Kind::bindToImage
) {
1864 libDyldEntry
= (dyld3::LibDyldEntryVector
*)((uint8_t*)libdyldEntryTarget
.foundInImage
._mh
+ libdyldEntryTarget
.offsetInImage
);
1866 // peak at entry vector to see if version is compatible
1867 if ( (libDyldEntry
!= nullptr) && ((libDyldEntry
->binaryFormatVersion
& LibDyldEntryVector::kBinaryFormatVersionMask
) == dyld3::closure::kFormatVersion
) ) {
1868 Image::ResolvedSymbolTarget entryLocation
= makeResolvedTarget(libdyldEntryTarget
);
1869 closureWriter
.setLibDyldEntry(entryLocation
);
1872 _diag
.error("libdyld.dylib entry vector is incompatible");
1875 _diag
.error("libdyld.dylib is missing entry vector");
1878 // record which is main executable
1879 ImageNum mainProgImageNum
= _loadedImages
[_mainProgLoadIndex
].imageNum
;
1880 closureWriter
.setTopImageNum(mainProgImageNum
);
1883 uint64_t entryOffset
;
1885 if ( _loadedImages
[_mainProgLoadIndex
].loadAddress()->getEntry(entryOffset
, usesCRT
) ) {
1886 Image::ResolvedSymbolTarget location
;
1887 location
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
1888 location
.image
.imageNum
= mainProgImageNum
;
1889 location
.image
.offset
= (uint32_t)entryOffset
;
1891 closureWriter
.setStartEntry(location
);
1893 closureWriter
.setMainEntry(location
);
1896 // add env vars that must match at launch time
1897 _pathOverrides
.forEachEnvVar(^(const char* envVar
) {
1898 closureWriter
.addEnvVar(envVar
);
1901 // add list of files which must be missing
1902 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const char*, paths
, 8192);
1903 if ( _mustBeMissingPaths
!= nullptr ) {
1904 _mustBeMissingPaths
->forEachPath(^(const char* aPath
) {
1905 paths
.push_back(aPath
);
1908 closureWriter
.setMustBeMissingFiles(paths
);
1910 // add list of files which must be be present with a specific inode/mtime
1911 if (!_skippedFiles
.empty())
1912 closureWriter
.setMustExistFiles(_skippedFiles
);
1914 void ClosureBuilder::invalidateInitializerRoots()
1917 bool madeChange
= false;
1918 for (uintptr_t loadedImageIndex
= _alreadyInitedIndex
; loadedImageIndex
!= _loadedImages
.count(); ++loadedImageIndex
) {
1919 BuilderLoadedImage
& li
= _loadedImages
[loadedImageIndex
];
1920 if ( li
.mustBuildClosure
) {
1921 // Already invalidated
1924 for (Image::LinkedImage depIndex
: li
.dependents
) {
1925 if ( depIndex
.imageNum() == kMissingWeakLinkedImage
)
1927 const BuilderLoadedImage
& depImage
= findLoadedImage(depIndex
.imageNum());
1928 // If a dependent is bad, or a new image num, or an override, then we need this image to get a new closure
1929 if ( depImage
.mustBuildClosure
) {
1930 li
.mustBuildClosure
= true; // mark bad
1937 // If we made a change, then we detected an existing image with a dependent which needed to be rebuilt.
1938 // This corresponds to a root of the shared cache where the existing image is a shared cache one and the root is the depImage
1939 _foundDyldCacheRoots
= true;
1943 size_t ClosureBuilder::HashCString::hash(const char* v
) {
1944 // FIXME: Use hash<string_view> when it has the correct visibility markup
1945 return __gnu_cxx::hash
<const char*>{}(v
);
1948 bool ClosureBuilder::EqualCString::equal(const char* s1
, const char* s2
) {
1949 return strcmp(s1
, s2
) == 0;
1955 static size_t hash(const uint64_t& v
) {
1956 return std::hash
<uint64_t>{}(v
);
1960 struct EqualUInt64
{
1961 static bool equal(uint64_t s1
, uint64_t s2
) {
1966 void ClosureBuilder::writeClassOrProtocolHashTable(bool classes
, Array
<ObjCOptimizerImage
>& objcImages
) {
1967 __block MultiMap
<const char*, dyld3::closure::Image::ObjCClassImageOffset
, HashCString
, EqualCString
> seenClassesMap
;
1968 __block Map
<const char*, dyld3::closure::Image::ObjCClassNameImageOffset
, HashCString
, EqualCString
> classNameMap
;
1969 __block OverflowSafeArray
<const char*> classNames
;
1971 // Note we walk the images backwards as we want them in load order to match the order they are registered with objc
1972 for (size_t imageIndex
= 0, reverseIndex
= (objcImages
.count() - 1); imageIndex
!= objcImages
.count(); ++imageIndex
, --reverseIndex
) {
1973 if (objcImages
[reverseIndex
].diag
.hasError())
1975 ObjCOptimizerImage
& image
= objcImages
[reverseIndex
];
1976 const OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenClasses
= classes
? image
.seenClasses
: image
.seenProtocols
;
1978 for (const ObjCOptimizerImage::SeenClass
& seenClass
: seenClasses
) {
1979 closure::Image::ObjCClassNameImageOffset classNameTarget
= seenClass
.first
;
1980 dyld3::closure::Image::ObjCClassImageOffset classDataTarget
= seenClass
.second
;
1981 Image::ObjCClassImage classImage
= _objcClassesHashTableImages
[classNameTarget
.classNameImageIndex
];
1983 const BuilderLoadedImage
& li
= findLoadedImage(classImage
.imageNum
);
1984 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
1986 const char* className
= ((const char*)ma
) + classImage
.offsetOfClassNames
+ classNameTarget
.classNameImageOffset
;
1987 //uint64_t nameVMAddr = ma->preferredLoadAddress() + classImage.offsetOfClassNames + classNameTarget.classNameImageOffset;
1988 //printf("%s: 0x%08llx = '%s'\n", li.path(), nameVMAddr, className);
1989 seenClassesMap
.insert({ className
, classDataTarget
});
1991 // Also track the name
1992 auto itAndInserted
= classNameMap
.insert({ className
, dyld3::closure::Image::ObjCClassNameImageOffset() });
1993 if (itAndInserted
.second
) {
1994 // We inserted the class name so we need to add it to the strings for the closure hash table
1995 classNames
.push_back(className
);
1997 // We already computed a class name target in a previous loop so use that one
1998 itAndInserted
.first
->second
= seenClass
.first
;
2000 // If we are processing protocols, and this is the first one we've seen, then track its ISA to be fixed up
2002 uint64_t protocolVMOffset
= classImage
.offsetOfClasses
+ classDataTarget
.classData
.imageOffset
;
2003 image
.protocolISAFixups
.push_back(protocolVMOffset
);
2009 __block
uint32_t duplicateCount
= 0;
2010 seenClassesMap
.forEachEntry(^(const char *const &key
, const Image::ObjCClassImageOffset
**values
,
2011 uint64_t valuesCount
) {
2012 if (valuesCount
!= 1)
2013 duplicateCount
+= valuesCount
;
2016 // If we have closure class names, we need to make a hash table for them.
2017 OverflowSafeArray
<uint8_t>& hashTable
= classes
? _objcClassesHashTable
: _objcProtocolsHashTable
;
2018 if (!classNames
.empty()) {
2019 objc_opt::perfect_hash phash
;
2020 objc_opt::make_perfect(classNames
, phash
);
2021 size_t size
= ObjCClassOpt::size(phash
, duplicateCount
);
2022 hashTable
.resize(size
);
2023 //printf("Class table size: %lld\n", size);
2024 ObjCClassOpt
* classesHashTable
= (ObjCClassOpt
*)hashTable
.begin();
2025 classesHashTable
->write(phash
, classNameMap
.array(), seenClassesMap
, duplicateCount
);
2029 bool ClosureBuilder::optimizeObjC(Array
<ImageWriter
>& writers
) {
2030 if ( _dyldCache
== nullptr )
2033 // If we have the read only data, make sure it has a valid selector table inside.
2034 const objc_opt::objc_clsopt_t
* objcClassOpt
= nullptr;
2035 const objc_opt::objc_selopt_t
* objcSelOpt
= nullptr;
2036 const objc_opt::objc_protocolopt2_t
* objcProtocolOpt
= nullptr;
2037 if (const objc_opt::objc_opt_t
* optObjCHeader
= _dyldCache
->objcOpt()) {
2038 objcClassOpt
= optObjCHeader
->clsopt();
2039 objcSelOpt
= optObjCHeader
->selopt();
2040 objcProtocolOpt
= optObjCHeader
->protocolopt2();
2043 if ( !objcClassOpt
|| !objcSelOpt
|| !objcProtocolOpt
)
2046 // We have 24 bits of index in SelectorReferenceFixup so we can't handle a
2047 // shared cache selector table larger than that
2048 if ( objcSelOpt
->usedCount() >= (1 << 24) )
2051 // Make sure we have the pointers section with the pointer to the protocol class
2052 const void* objcOptPtrs
= _dyldCache
->objcOptPtrs();
2053 if ( objcOptPtrs
== nullptr )
2056 uint32_t pointerSize
= _loadedImages
.begin()->loadAddress()->pointerSize();
2057 uint64_t classProtocolVMAddr
= (pointerSize
== 8) ? *(uint64_t*)objcOptPtrs
: *(uint32_t*)objcOptPtrs
;
2059 Image::ResolvedSymbolTarget objcProtocolClassTarget
;
2060 objcProtocolClassTarget
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
2061 if ( _dyldCacheIsLive
) {
2062 // If we are on arm64e, the protocol ISA in the shared cache was signed. We don't
2063 // want the signature bits in the encoded value
2064 #if __has_feature(ptrauth_calls)
2065 classProtocolVMAddr
= (uint64_t)__builtin_ptrauth_strip((void*)classProtocolVMAddr
, ptrauth_key_asda
);
2067 objcProtocolClassTarget
.sharedCache
.offset
= classProtocolVMAddr
- (uint64_t)_dyldCache
;
2069 objcProtocolClassTarget
.sharedCache
.offset
= classProtocolVMAddr
- _dyldCache
->unslidLoadAddress();
2072 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ObjCOptimizerImage
, objcImages
, 32);
2073 ArrayFinalizer
<ObjCOptimizerImage
> scopedCleanup(objcImages
,
2074 ^(ObjCOptimizerImage
& objcImage
) {
2075 objcImage
.~ObjCOptimizerImage();
2078 // Find all the images with valid objc info
2079 // Also add shared cache images to a map so that we can see them later for looking up classes
2080 Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
> sharedCacheImagesMap
;
2081 for (size_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
2082 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
2084 // Skip shared cache images as even if they need a new closure, the objc runtime can still use
2085 // the optimized shared cache tables.
2086 if ( li
.loadAddress()->inDyldCache() ) {
2087 sharedCacheImagesMap
.insert({ li
.loadAddress(), true });
2088 // Bump the writer index if we have a writer for this image
2089 if ( li
.mustBuildClosure
)
2093 // Images which don't need a closure can be skipped. They are from the shared cache
2094 if ( !li
.mustBuildClosure
)
2097 // If we have a root of libobjc, just give up for now
2098 if ( !strcmp(li
.path(), "/usr/lib/libobjc.A.dylib"))
2101 ImageWriter
& writer
= writers
[writerIndex
];
2104 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2106 // Skip images with chained fixups other than arm64e legacy fixups until we can test them
2107 // FIXME: Handle chained fixups
2108 if ( ma
->hasChainedFixups() ) {
2109 switch ( ma
->chainedPointerFormat() ) {
2110 case DYLD_CHAINED_PTR_ARM64E
:
2111 case DYLD_CHAINED_PTR_64
:
2112 // We've tested the 64-bit chained fixups.
2114 case DYLD_CHAINED_PTR_64_OFFSET
:
2115 case DYLD_CHAINED_PTR_ARM64E_USERLAND
:
2116 case DYLD_CHAINED_PTR_ARM64E_USERLAND24
:
2117 case DYLD_CHAINED_PTR_ARM64E_FIRMWARE
:
2118 // FIXME: Test 64-bit offset chained fixups then enable this.
2120 case DYLD_CHAINED_PTR_32
:
2121 case DYLD_CHAINED_PTR_32_CACHE
:
2122 case DYLD_CHAINED_PTR_32_FIRMWARE
:
2123 // FIXME: Test 32-bit chained fixups then enable this.
2128 const MachOAnalyzer::ObjCImageInfo
* objcImageInfo
= ma
->objcImageInfo();
2129 if ( objcImageInfo
== nullptr )
2132 // This image is good so record it for use later.
2133 objcImages
.default_constuct_back();
2134 ObjCOptimizerImage
& image
= objcImages
.back();
2135 image
.loadedImage
= &li
;
2136 image
.writer
= &writer
;
2138 // Find FairPlay encryption range if encrypted
2139 uint32_t fairPlayFileOffset
;
2140 uint32_t fairPlaySize
;
2141 if ( ma
->isFairPlayEncrypted(fairPlayFileOffset
, fairPlaySize
) ) {
2142 image
.fairplayFileOffsetStart
= fairPlayFileOffset
;
2143 image
.fairplayFileOffsetEnd
= fairPlayFileOffset
;
2146 // Set the offset to the objc image info
2147 image
.objcImageInfoVMOffset
= (uint64_t)objcImageInfo
- (uint64_t)ma
;
2150 // objc supports a linker set which is a magic section of duplicate objc classes to ignore
2151 // We need to match that behaviour
2152 Map
<const char*, bool, HashCString
, EqualCString
> duplicateClassesToIgnore
;
2153 parseObjCClassDuplicates(duplicateClassesToIgnore
);
2155 OverflowSafeArray
<const char*> closureSelectorStrings
;
2156 Map
<const char*, dyld3::closure::Image::ObjCImageOffset
, HashCString
, EqualCString
> closureSelectorMap
;
2157 OverflowSafeArray
<const char*> closureDuplicateSharedCacheClassNames
;
2158 Map
<const char*, dyld3::closure::Image::ObjCDuplicateClass
, HashCString
, EqualCString
> closureDuplicateSharedCacheClassMap
;
2159 for (ObjCOptimizerImage
& image
: objcImages
) {
2160 optimizeObjCClasses(objcClassOpt
, sharedCacheImagesMap
, closureDuplicateSharedCacheClassMap
, duplicateClassesToIgnore
, image
);
2161 if (image
.diag
.hasError())
2164 optimizeObjCProtocols(objcProtocolOpt
, sharedCacheImagesMap
, image
);
2165 if (image
.diag
.hasError())
2168 optimizeObjCSelectors(objcSelOpt
, closureSelectorMap
, image
);
2169 if (image
.diag
.hasError())
2172 // If this image is still valid, then add its intermediate results to the main tables
2175 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2176 uint64_t nameVMOffset
= nameAndDataVMOffset
.first
;
2177 uint64_t dataVMOffset
= nameAndDataVMOffset
.second
;
2178 _objcClassesHashTableImages
.push_back({ image
.loadedImage
->imageNum
, (uint32_t)nameVMOffset
, (uint32_t)dataVMOffset
});
2180 image
.classesNameAndDataVMOffsets
.clear();
2182 for (const auto& stringAndDuplicate
: image
.classSharedCacheDuplicates
) {
2183 closureDuplicateSharedCacheClassMap
[stringAndDuplicate
.first
] = stringAndDuplicate
.second
;
2184 closureDuplicateSharedCacheClassNames
.push_back(stringAndDuplicate
.first
);
2188 // Note we don't need to add the selector binds here. Its easier just to process them later from each image
2189 for (const auto& stringAndTarget
: image
.selectorMap
) {
2190 closureSelectorMap
[stringAndTarget
.first
] = stringAndTarget
.second
;
2191 closureSelectorStrings
.push_back(stringAndTarget
.first
);
2193 if (image
.methodNameVMOffset
)
2194 _objcSelectorsHashTableImages
.push_back({ image
.loadedImage
->imageNum
, (uint32_t)*image
.methodNameVMOffset
});
2197 // If we successfully analyzed the classes and selectors, we can now emit their data
2198 // Set all the writers to have optimized objc
2199 for (ObjCOptimizerImage
& image
: objcImages
) {
2200 if (image
.diag
.hasError())
2202 image
.writer
->setHasPrecomputedObjC(true);
2205 // Write out the class table
2206 writeClassOrProtocolHashTable(true, objcImages
);
2208 // Write out the protocol table
2209 writeClassOrProtocolHashTable(false, objcImages
);
2211 // If we have closure duplicate classes, we need to make a hash table for them.
2212 closure::ObjCStringTable
* duplicateClassesTable
= nullptr;
2213 if (!closureDuplicateSharedCacheClassNames
.empty()) {
2214 objc_opt::perfect_hash phash
;
2215 objc_opt::make_perfect(closureDuplicateSharedCacheClassNames
, phash
);
2216 size_t size
= ObjCStringTable::size(phash
);
2217 _objcClassesDuplicatesHashTable
.resize(size
);
2218 //printf("Duplicate classes table size: %lld\n", size);
2219 duplicateClassesTable
= (closure::ObjCClassDuplicatesOpt
*)_objcClassesDuplicatesHashTable
.begin();
2220 duplicateClassesTable
->write(phash
, closureDuplicateSharedCacheClassMap
.array());
2223 // If we have closure selectors, we need to make a hash table for them.
2224 closure::ObjCStringTable
* selectorStringTable
= nullptr;
2225 if (!closureSelectorStrings
.empty()) {
2226 objc_opt::perfect_hash phash
;
2227 objc_opt::make_perfect(closureSelectorStrings
, phash
);
2228 size_t size
= ObjCStringTable::size(phash
);
2229 _objcSelectorsHashTable
.resize(size
);
2230 //printf("Selector table size: %lld\n", size);
2231 selectorStringTable
= (closure::ObjCStringTable
*)_objcSelectorsHashTable
.begin();
2232 selectorStringTable
->write(phash
, closureSelectorMap
.array());
2235 // Add fixups for the image info, protocol ISAs, and selector refs
2236 for (ObjCOptimizerImage
& image
: objcImages
) {
2237 if (image
.diag
.hasError())
2240 // Protocol ISA references
2241 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ProtocolISAFixup
, protocolFixups
, 512);
2242 if ( !image
.protocolISAFixups
.empty() ) {
2244 __block
uint64_t lastOffset
= -pointerSize
;
2245 for (uint64_t runtimeOffset
: image
.protocolISAFixups
) {
2246 bool mergedIntoPrevious
= false;
2247 if ( (runtimeOffset
> lastOffset
) && !protocolFixups
.empty() ) {
2248 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2249 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2250 // misaligned pointer means we cannot optimize
2253 if ( (protocolFixups
.back().repeatCount
== 1) && (protocolFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2254 protocolFixups
.back().repeatCount
= 2;
2255 protocolFixups
.back().skipCount
= skipAmount
;
2256 assert(protocolFixups
.back().skipCount
== skipAmount
); // check overflow
2257 mergedIntoPrevious
= true;
2259 else if ( (protocolFixups
.back().skipCount
== skipAmount
) && (protocolFixups
.back().repeatCount
< 0xfff) ) {
2260 uint32_t prevRepeatCount
= protocolFixups
.back().repeatCount
;
2261 protocolFixups
.back().repeatCount
+= 1;
2262 assert(protocolFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2263 mergedIntoPrevious
= true;
2267 if ( !mergedIntoPrevious
) {
2268 Image::ProtocolISAFixup pattern
;
2269 pattern
.startVmOffset
= runtimeOffset
;
2270 pattern
.repeatCount
= 1;
2271 pattern
.skipCount
= 0;
2272 assert(pattern
.startVmOffset
== runtimeOffset
);
2273 protocolFixups
.push_back(pattern
);
2275 lastOffset
= runtimeOffset
;
2279 // Selector references
2280 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::SelectorReferenceFixup
, selRefFixups
, 512);
2281 if ( !image
.selectorFixups
.empty() ) {
2282 uint64_t prevVMOffset
= 0;
2283 const uint64_t maxChainOffset
= (4 * ((1 << 7) - 1));
2284 for (const ObjCOptimizerImage::SelectorFixup
& selectorFixup
: image
.selectorFixups
) {
2285 assert( (selectorFixup
.fixupVMOffset
& 3) == 0 );
2286 if ( (selectorFixup
.fixupVMOffset
- prevVMOffset
) <= maxChainOffset
) {
2287 // Add this to the previous chain
2288 selRefFixups
.back().chainEntry
.next
= (uint32_t)(selectorFixup
.fixupVMOffset
- prevVMOffset
) / 4;
2290 // Need to start a new chain as the previous offset can't reach
2291 Image::SelectorReferenceFixup fixup
;
2292 fixup
.chainStartVMOffset
= selectorFixup
.fixupVMOffset
;
2293 selRefFixups
.push_back(fixup
);
2296 if ( selectorFixup
.isSharedCache
) {
2297 // If the entry is in the shared cache then we already have the index for it
2298 Image::SelectorReferenceFixup fixup
;
2299 fixup
.chainEntry
.index
= selectorFixup
.sharedCache
.selectorTableIndex
;
2300 fixup
.chainEntry
.next
= 0;
2301 fixup
.chainEntry
.inSharedCache
= 1;
2302 selRefFixups
.push_back(fixup
);
2304 // We had to record the string for the closure table entries as we don't know the
2306 uint32_t selectorTableIndex
= selectorStringTable
->getIndex(selectorFixup
.image
.selectorString
);
2307 assert(selectorTableIndex
!= ObjCSelectorOpt::indexNotFound
);
2308 Image::SelectorReferenceFixup fixup
;
2309 fixup
.chainEntry
.index
= selectorTableIndex
;
2310 fixup
.chainEntry
.next
= 0;
2311 fixup
.chainEntry
.inSharedCache
= 0;
2312 selRefFixups
.push_back(fixup
);
2315 prevVMOffset
= selectorFixup
.fixupVMOffset
;
2319 // Stable Swift fixups
2320 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ClassStableSwiftFixup
, stableSwiftFixups
, 512);
2321 if ( !image
.classStableSwiftFixups
.empty() ) {
2323 __block
uint64_t lastOffset
= -pointerSize
;
2324 for (uint64_t runtimeOffset
: image
.classStableSwiftFixups
) {
2325 bool mergedIntoPrevious
= false;
2326 if ( (runtimeOffset
> lastOffset
) && !stableSwiftFixups
.empty() ) {
2327 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2328 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2329 // misaligned pointer means we cannot optimize
2332 if ( (stableSwiftFixups
.back().repeatCount
== 1) && (stableSwiftFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2333 stableSwiftFixups
.back().repeatCount
= 2;
2334 stableSwiftFixups
.back().skipCount
= skipAmount
;
2335 assert(stableSwiftFixups
.back().skipCount
== skipAmount
); // check overflow
2336 mergedIntoPrevious
= true;
2338 else if ( (stableSwiftFixups
.back().skipCount
== skipAmount
) && (stableSwiftFixups
.back().repeatCount
< 0xfff) ) {
2339 uint32_t prevRepeatCount
= stableSwiftFixups
.back().repeatCount
;
2340 stableSwiftFixups
.back().repeatCount
+= 1;
2341 assert(stableSwiftFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2342 mergedIntoPrevious
= true;
2346 if ( !mergedIntoPrevious
) {
2347 Image::ClassStableSwiftFixup pattern
;
2348 pattern
.startVmOffset
= runtimeOffset
;
2349 pattern
.repeatCount
= 1;
2350 pattern
.skipCount
= 0;
2351 assert(pattern
.startVmOffset
== runtimeOffset
);
2352 stableSwiftFixups
.push_back(pattern
);
2354 lastOffset
= runtimeOffset
;
2358 // Method list fixups
2359 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::MethodListFixup
, methodListFixups
, 512);
2360 if ( !image
.methodListFixups
.empty() ) {
2362 __block
uint64_t lastOffset
= -pointerSize
;
2363 for (uint64_t runtimeOffset
: image
.methodListFixups
) {
2364 bool mergedIntoPrevious
= false;
2365 if ( (runtimeOffset
> lastOffset
) && !methodListFixups
.empty() ) {
2366 uint64_t skipAmount
= (runtimeOffset
- lastOffset
- pointerSize
)/pointerSize
;
2367 if ( skipAmount
*pointerSize
!= (runtimeOffset
- lastOffset
- pointerSize
) ) {
2368 // misaligned pointer means we cannot optimize
2371 if ( (methodListFixups
.back().repeatCount
== 1) && (methodListFixups
.back().skipCount
== 0) && (skipAmount
<= 255) ) {
2372 methodListFixups
.back().repeatCount
= 2;
2373 methodListFixups
.back().skipCount
= skipAmount
;
2374 assert(methodListFixups
.back().skipCount
== skipAmount
); // check overflow
2375 mergedIntoPrevious
= true;
2377 else if ( (methodListFixups
.back().skipCount
== skipAmount
) && (methodListFixups
.back().repeatCount
< 0xfff) ) {
2378 uint32_t prevRepeatCount
= methodListFixups
.back().repeatCount
;
2379 methodListFixups
.back().repeatCount
+= 1;
2380 assert(methodListFixups
.back().repeatCount
> prevRepeatCount
); // check overflow
2381 mergedIntoPrevious
= true;
2385 if ( !mergedIntoPrevious
) {
2386 Image::MethodListFixup pattern
;
2387 pattern
.startVmOffset
= runtimeOffset
;
2388 pattern
.repeatCount
= 1;
2389 pattern
.skipCount
= 0;
2390 assert(pattern
.startVmOffset
== runtimeOffset
);
2391 methodListFixups
.push_back(pattern
);
2393 lastOffset
= runtimeOffset
;
2397 image
.writer
->setObjCFixupInfo(objcProtocolClassTarget
, image
.objcImageInfoVMOffset
, protocolFixups
,
2398 selRefFixups
, stableSwiftFixups
, methodListFixups
);
2404 void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t
* objcSelOpt
,
2405 const Map
<const char*, dyld3::closure::Image::ObjCImageOffset
, HashCString
, EqualCString
>& closureSelectorMap
,
2406 ObjCOptimizerImage
& image
) {
2408 BuilderLoadedImage
& li
= *image
.loadedImage
;
2410 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2411 uint32_t pointerSize
= ma
->pointerSize();
2412 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2413 const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= ma
->makeVMAddrConverter(li
.contentRebased
);
2415 // The legacy (objc1) codebase uses a bunch of sections we don't want to reason about. If we see them just give up.
2416 __block
bool foundBadSection
= false;
2417 ma
->forEachSection(^(const MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
2418 if ( strcmp(sectInfo
.segInfo
.segName
, "__OBJC") != 0 )
2420 if (strcmp(sectInfo
.sectName
, "__module_info") == 0) {
2421 foundBadSection
= true;
2425 if (strcmp(sectInfo
.sectName
, "__protocol") == 0) {
2426 foundBadSection
= true;
2430 if (strcmp(sectInfo
.sectName
, "__message_refs") == 0) {
2431 foundBadSection
= true;
2436 if (foundBadSection
) {
2437 image
.diag
.error("Old objc section");
2441 __block
MachOAnalyzer::SectionCache
selectorStringSectionCache(ma
);
2443 uint32_t sharedCacheSentinelIndex
= objcSelOpt
->getSentinelIndex();
2445 // Track the locations where we've updated selector references. With relative method lists,
2446 // we share selref slots across classes, categories, protocols, and SEL() expressions, so we may
2447 // visit a location more than once
2448 __block Map
<uint64_t, bool, HashUInt64
, EqualUInt64
> seenSelectorReferenceImageOffsets
;
2450 auto visitReferenceToObjCSelector
= ^void(uint64_t selectorStringVMAddr
, uint64_t selectorReferenceVMAddr
) {
2452 uint64_t selectorUseImageOffset
= selectorReferenceVMAddr
- loadAddress
;
2453 auto selUseItAndInserted
= seenSelectorReferenceImageOffsets
.insert({ selectorUseImageOffset
, true });
2454 if ( !selUseItAndInserted
.second
) {
2455 // If we didn't insert the selector reference, then its already there so we should skip it
2459 if ( (selectorUseImageOffset
& 3) != 0 ) {
2460 image
.diag
.error("Unaligned selector reference fixup");
2464 // Image::SelectorReferenceFixup only has a 32-bit reach
2465 if ( selectorUseImageOffset
>= (1ULL << 32) ) {
2466 image
.diag
.error("Selector reference fixup exceeds supported vm offset");
2470 // Get the section for the name
2471 const char* selectorString
= nullptr;
2472 MachOAnalyzer::PrintableStringResult selectorStringResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2473 __block
uint64_t selectorStringSectionStartVMAddr
= 0;
2474 auto selectorStringSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2476 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2477 if (sectInfo
.sectSize
>= Image::ObjCImageOffset::maximumOffset
) {
2481 // We use 32-bit offsets so make sure the section is no larger than that.
2482 uint64_t classNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2483 if (classNameVMOffset
>= (1ULL << 32)) {
2487 selectorStringSectionStartVMAddr
= sectInfo
.sectAddr
;
2490 selectorString
= ma
->getPrintableString(selectorStringVMAddr
, selectorStringResult
,
2491 &selectorStringSectionCache
, selectorStringSectionHandler
);
2493 if ( selectorStringResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2494 image
.diag
.error("Invalid selector string for objc optimisation");
2498 uint32_t cacheSelectorIndex
= objcSelOpt
->getIndexForKey(selectorString
);
2499 //printf("selector: %p -> %p %s\n", methodName, cacheSelector, selectorString);
2501 if ( cacheSelectorIndex
!= sharedCacheSentinelIndex
) {
2502 // We got the selector from the cache so add a fixup to point there.
2503 ObjCOptimizerImage::SelectorFixup fixup
;
2504 fixup
.isSharedCache
= true;
2505 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2506 fixup
.sharedCache
.selectorTableIndex
= cacheSelectorIndex
;
2508 //printf("Overriding fixup at 0x%08llX to cache offset 0x%08llX\n", selectorUseImageOffset, (uint64_t)cacheSelector - (uint64_t)_dyldCache);
2509 image
.selectorFixups
.push_back(fixup
);
2513 // See if this selector is already in the closure map from a previous image
2514 auto closureSelectorIt
= closureSelectorMap
.find(selectorString
);
2515 if (closureSelectorIt
!= closureSelectorMap
.end()) {
2516 // This selector was found in a previous image, so use it here.
2517 ObjCOptimizerImage::SelectorFixup fixup
;
2518 fixup
.isSharedCache
= false;
2519 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2520 fixup
.image
.selectorString
= selectorString
;
2522 //printf("Overriding fixup at 0x%08llX to '%s' offset 0x%08llX\n", selectorUseImageOffset, findLoadedImage(target.image.imageNum).path(), target.image.offset);
2523 image
.selectorFixups
.push_back(fixup
);
2527 // See if this selector is already in the map for this image
2528 auto itAndInserted
= image
.selectorMap
.insert({ selectorString
, dyld3::closure::Image::ObjCImageOffset() });
2529 if (itAndInserted
.second
) {
2530 // We added the selector so its pointing in to our own image.
2531 // We don't need to add a fixup to our image, but we do need to
2532 // populate the data for other images later to point here.
2533 // First put our image in the list if its not already there.
2534 uint64_t methodNameVMOffset
= selectorStringSectionStartVMAddr
- loadAddress
;
2535 if (!image
.methodNameVMOffset
) {
2536 if ( _objcSelectorsHashTableImages
.count() == Image::ObjCImageOffset::maximumImageIndex
) {
2537 image
.diag
.error("Out of space for selector hash images");
2540 image
.methodNameVMOffset
= methodNameVMOffset
;
2542 // If we already set the offset to the start of the method names section, double check that
2543 // the section we are in right now is the same as that one. Otherwise we don't have the code
2544 // to handle both right now.
2545 if (*image
.methodNameVMOffset
!= methodNameVMOffset
) {
2546 image
.diag
.error("Cannot handle more than one selector strings section");
2551 dyld3::closure::Image::ObjCImageOffset target
;
2552 target
.imageIndex
= (uint32_t)_objcSelectorsHashTableImages
.count();
2553 target
.imageOffset
= (uint32_t)(selectorStringVMAddr
- selectorStringSectionStartVMAddr
);
2554 itAndInserted
.first
->second
= target
;
2558 // This selector was found elsewhere in our image. If this reference already points to the same
2559 // selector string as we found before (and it should!) then we have nothing to do. Otherwise we
2560 // need to add a fixup here to make sure we point to our chosen definition.
2561 uint32_t imageOffset
= (uint32_t)(selectorStringVMAddr
- loadAddress
);
2562 if ( imageOffset
== (*image
.methodNameVMOffset
+ itAndInserted
.first
->second
.imageOffset
) )
2565 ObjCOptimizerImage::SelectorFixup fixup
;
2566 fixup
.isSharedCache
= false;
2567 fixup
.fixupVMOffset
= (uint32_t)selectorUseImageOffset
;
2568 fixup
.image
.selectorString
= selectorString
;
2570 //printf("Overriding fixup at 0x%08llX to '%s' offset 0x%08llX\n", selectorUseImageOffset, findLoadedImage(target.image.imageNum).path(), target.image.offset);
2571 image
.selectorFixups
.push_back(fixup
);
2574 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
2575 visitReferenceToObjCSelector(method
.nameVMAddr
, method
.nameLocationVMAddr
);
2578 auto visitMethodList
= ^(uint64_t methodListVMAddr
) {
2579 if ( methodListVMAddr
== 0 )
2581 bool isRelativeMethodList
= false;
2582 ma
->forEachObjCMethod(methodListVMAddr
, vmAddrConverter
, visitMethod
, &isRelativeMethodList
);
2583 if (image
.diag
.hasError())
2585 // Record the offset to the method list so that we can mark it as being uniqued
2586 // We can only do this if we have a pointer based method list as relative method lists are
2587 // in read-only memory
2588 if ( !isRelativeMethodList
)
2589 image
.methodListFixups
.push_back(methodListVMAddr
- loadAddress
);
2592 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
2593 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
2594 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
2595 visitMethodList(objcClass
.baseMethodsVMAddr(pointerSize
));
2598 auto visitCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
2599 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
2600 visitMethodList(objcCategory
.instanceMethodsVMAddr
);
2601 visitMethodList(objcCategory
.classMethodsVMAddr
);
2603 auto visitProtocol
= ^(Diagnostics
& diag
, uint64_t protocolVMAddr
,
2604 const dyld3::MachOAnalyzer::ObjCProtocol
& objCProtocol
) {
2605 visitMethodList(objCProtocol
.instanceMethodsVMAddr
);
2606 visitMethodList(objCProtocol
.classMethodsVMAddr
);
2607 visitMethodList(objCProtocol
.optionalInstanceMethodsVMAddr
);
2608 visitMethodList(objCProtocol
.optionalClassMethodsVMAddr
);
2611 // Walk the class list
2612 ma
->forEachObjCClass(image
.diag
, vmAddrConverter
, visitClass
);
2613 if (image
.diag
.hasError())
2616 // Walk the category list
2617 ma
->forEachObjCCategory(image
.diag
, vmAddrConverter
, visitCategory
);
2618 if (image
.diag
.hasError())
2621 // Walk the protocol list
2622 ma
->forEachObjCProtocol(image
.diag
, vmAddrConverter
, visitProtocol
);
2623 if (image
.diag
.hasError())
2626 // Visit the selector refs
2627 ma
->forEachObjCSelectorReference(image
.diag
, vmAddrConverter
, ^(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
) {
2628 visitReferenceToObjCSelector(selRefTargetVMAddr
, selRefVMAddr
);
2630 if (image
.diag
.hasError())
2633 // Visit the message refs
2634 // Note this isn't actually supported in libobjc any more. Its logic for deciding whether to support it is if this is true:
2635 // #if (defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))
2636 // So to keep it simple, lets only do this walk if we are x86_64
2637 if ( ma
->isArch("x86_64") || ma
->isArch("x86_64h") ) {
2638 if (ma
->hasObjCMessageReferences()) {
2639 image
.diag
.error("Cannot handle message refs");
2645 static const dyld3::MachOAnalyzer
* getMachHeaderFromObjCHeaderInfo(const void* opaqueHeaderInfo
, uint32_t pointerSize
) {
2646 if (pointerSize
== 8) {
2647 typedef int64_t PtrTy
;
2649 PtrTy mhdr_offset
; // offset to mach_header_64
2650 PtrTy info_offset
; // offset to objc_image_info *
2652 const HeaderInfo
* headerInfo
= (const HeaderInfo
*)opaqueHeaderInfo
;
2653 return (const dyld3::MachOAnalyzer
*)(((const uint8_t*)&headerInfo
->mhdr_offset
) + headerInfo
->mhdr_offset
);
2655 typedef int32_t PtrTy
;
2657 PtrTy mhdr_offset
; // offset to mach_header
2658 PtrTy info_offset
; // offset to objc_image_info *
2660 const HeaderInfo
* headerInfo
= (const HeaderInfo
*)opaqueHeaderInfo
;
2661 return (const dyld3::MachOAnalyzer
*)(((const uint8_t*)&headerInfo
->mhdr_offset
) + headerInfo
->mhdr_offset
);
2665 void ClosureBuilder::addDuplicateObjCClassWarning(const char* className
,
2666 const char* duplicateDefinitionPath
,
2667 const char* canonicalDefinitionPath
)
2669 if ( _objcDuplicateClassWarnings
== nullptr )
2670 _objcDuplicateClassWarnings
= PathPool::allocate();
2671 // Use a diagnostic to give us a buffer we can safely print to
2673 diag
.error("Class %s is implemented in both %s and %s. One of the two will be used. Which one is undefined.",
2674 className
, canonicalDefinitionPath
, duplicateDefinitionPath
);
2675 #if BUILDING_CACHE_BUILDER
2676 _objcDuplicateClassWarnings
->add(diag
.errorMessage().c_str());
2678 _objcDuplicateClassWarnings
->add(diag
.errorMessage());
2682 void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t
* objcClassOpt
,
2683 const Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
>& sharedCacheImagesMap
,
2684 const Map
<const char*, dyld3::closure::Image::ObjCDuplicateClass
, HashCString
, EqualCString
>& duplicateSharedCacheClasses
,
2685 const Map
<const char*, bool, HashCString
, EqualCString
>& duplicateClassesToIgnore
,
2686 ObjCOptimizerImage
& image
) {
2688 BuilderLoadedImage
& li
= *image
.loadedImage
;
2689 OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenClasses
= image
.seenClasses
;
2691 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2692 const uint32_t pointerSize
= ma
->pointerSize();
2693 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2694 const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= ma
->makeVMAddrConverter(li
.contentRebased
);
2696 // Keep track of any missing weak imports so that we can tell if the superclasses are nil
2697 // This is necessary as the shared cache will be marked with 'no missing weak superclasses'
2698 // and so we need to continue to satisfy that constraint
2699 __block Map
<uint64_t, bool, HashUInt64
, EqualUInt64
> missingWeakImportOffets
;
2700 if (li
.hasMissingWeakImports
) {
2701 const Image
* closureImage
= image
.writer
->currentImage();
2702 if ( closureImage
->hasChainedFixups() ) {
2703 const Array
<Image::ResolvedSymbolTarget
> targets
= closureImage
->chainedTargets();
2704 if ( !targets
.empty() ) {
2705 ma
->withChainStarts(_diag
, closureImage
->chainedStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) {
2706 ma
->forEachFixupInAllChains(_diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
,
2707 const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
2708 uint64_t fixupOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
2709 uint32_t bindOrdinal
;
2711 if ( fixupLoc
->isBind(segInfo
->pointer_format
, bindOrdinal
, addend
) ) {
2712 if ( bindOrdinal
< targets
.count() ) {
2713 const Image::ResolvedSymbolTarget
& target
= targets
[bindOrdinal
];
2714 if ( (target
.absolute
.kind
== Image::ResolvedSymbolTarget::kindAbsolute
) && (target
.absolute
.value
== 0) )
2715 missingWeakImportOffets
[fixupOffset
] = true;
2718 image
.diag
.error("out of range bind ordinal %d (max %lu)", bindOrdinal
, targets
.count());
2724 if (image
.diag
.hasError())
2729 closureImage
->forEachBind(^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget bindTarget
, bool &stop
) {
2730 if ( (bindTarget
.absolute
.kind
== Image::ResolvedSymbolTarget::kindAbsolute
) && (bindTarget
.absolute
.value
== 0) )
2731 missingWeakImportOffets
[imageOffsetToBind
] = true;
2736 // Class names and data may be in different sections depending on swift vs objc so handle multiple sections
2737 __block
MachOAnalyzer::SectionCache
classNameSectionCache(ma
);
2738 __block
MachOAnalyzer::SectionCache
classSectionCache(ma
);
2740 ma
->forEachObjCClass(image
.diag
, vmAddrConverter
, ^(Diagnostics
&diag
, uint64_t classVMAddr
,
2741 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
2742 const MachOAnalyzer::ObjCClassInfo
&objcClass
, bool isMetaClass
) {
2743 if (isMetaClass
) return;
2745 // Make sure the superclass pointer is not nil
2746 uint64_t superclassRuntimeOffset
= classSuperclassVMAddr
- loadAddress
;
2747 if (missingWeakImportOffets
.find(superclassRuntimeOffset
) != missingWeakImportOffets
.end()) {
2748 diag
.error("Missing weak superclass");
2752 // Does this class need to be fixed up for stable Swift ABI.
2753 // Note the order matches the objc runtime in that we always do this fix before checking for dupes,
2754 // but after excluding classes with missing weak superclasses.
2755 if (objcClass
.isUnfixedBackwardDeployingStableSwift()) {
2756 // Class really is stable Swift, pretending to be pre-stable.
2757 // Fix its lie. This involves fixing the FAST bits on the class data value, so record that vmaddr
2758 image
.classStableSwiftFixups
.push_back(classDataVMAddr
- loadAddress
);
2761 // Get the section for the name
2762 const char* className
= nullptr;
2763 MachOAnalyzer::PrintableStringResult classNameResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2764 __block
uint64_t classNameSectionStartVMAddr
= 0;
2765 auto classNameSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2766 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2767 if (sectInfo
.sectSize
>= Image::ObjCClassNameImageOffset::maximumOffset
) {
2771 // We use 32-bit offsets so make sure the section is no larger than that.
2772 uint64_t classNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2773 if (classNameVMOffset
>= (1ULL << 32)) {
2777 classNameSectionStartVMAddr
= sectInfo
.sectAddr
;
2780 uint64_t classNameVMAddr
= objcClass
.nameVMAddr(pointerSize
);
2781 className
= ma
->getPrintableString(classNameVMAddr
, classNameResult
,
2782 &classNameSectionCache
, classNameSectionHandler
);
2784 if ( classNameResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2785 diag
.error("Invalid class name for objc optimisation");
2789 // If the class also exists in a shared cache image which is loaded, then objc
2790 // would have found that one, regardless of load order. So we can just skip this one.
2795 uint32_t count
= objcClassOpt
->getClassHeaderAndIndex(className
, cls
, hi
, index
);
2797 // exactly one matching class. Check if its loaded
2798 const dyld3::MachOAnalyzer
* sharedCacheMA
= getMachHeaderFromObjCHeaderInfo(hi
, pointerSize
);
2799 if (sharedCacheImagesMap
.find(sharedCacheMA
) != sharedCacheImagesMap
.end()) {
2800 if ( duplicateClassesToIgnore
.find(className
) == duplicateClassesToIgnore
.end() )
2801 addDuplicateObjCClassWarning(className
, li
.path(), sharedCacheMA
->installName());
2803 // We have a duplicate class, so check if we've already got it in our map.
2804 if ( duplicateSharedCacheClasses
.find(className
) == duplicateSharedCacheClasses
.end() ) {
2805 // We haven't seen this one yet
2806 Image::ObjCDuplicateClass duplicateClass
;
2807 duplicateClass
.sharedCacheClassOptIndex
= index
;
2808 duplicateClass
.sharedCacheClassDuplicateIndex
= 0;
2809 image
.classSharedCacheDuplicates
.insert({ className
, duplicateClass
});
2813 else if (count
> 1) {
2814 // more than one matching class - find one that is loaded
2815 void *clslist
[count
];
2816 void *hilist
[count
];
2817 objcClassOpt
->getClassesAndHeaders(className
, clslist
, hilist
);
2818 for (uint32_t i
= 0; i
< count
; i
++) {
2819 const dyld3::MachOAnalyzer
* sharedCacheMA
= getMachHeaderFromObjCHeaderInfo(hilist
[i
], pointerSize
);
2820 if (sharedCacheImagesMap
.find(sharedCacheMA
) != sharedCacheImagesMap
.end()) {
2821 if ( duplicateClassesToIgnore
.find(className
) == duplicateClassesToIgnore
.end() )
2822 addDuplicateObjCClassWarning(className
, li
.path(), sharedCacheMA
->installName());
2824 // We have a duplicate class, so check if we've already got it in our map.
2825 if ( duplicateSharedCacheClasses
.find(className
) == duplicateSharedCacheClasses
.end() ) {
2826 // We haven't seen this one yet
2827 Image::ObjCDuplicateClass duplicateClass
;
2828 duplicateClass
.sharedCacheClassOptIndex
= index
;
2829 duplicateClass
.sharedCacheClassDuplicateIndex
= i
;
2830 image
.classSharedCacheDuplicates
.insert({ className
, duplicateClass
});
2839 // Get the section for the class itself
2840 __block
uint64_t classSectionStartVMAddr
= 0;
2841 auto classSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2842 // We only have 23-bits in ObjCClassImageOffset to index in to the classes
2843 if (sectInfo
.sectSize
> Image::ObjCClassImageOffset::maximumOffset
) {
2847 // We use 32-bit offsets so make sure the section is no larger than that.
2848 uint64_t classDatasVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2849 if (classDatasVMOffset
>= (1ULL << 32)) {
2853 classSectionStartVMAddr
= sectInfo
.sectAddr
;
2856 if (!classSectionCache
.findSectionForVMAddr(classVMAddr
, classSectionHandler
)) {
2857 diag
.error("Invalid class for objc optimisation");
2861 // Make sure we have an entry for our images offsets for later
2862 uint64_t classNameSectionVMOffset
= classNameSectionStartVMAddr
- loadAddress
;
2863 uint64_t classSectionVMOffset
= classSectionStartVMAddr
- loadAddress
;
2864 uint64_t hashTableVMOffsetsIndex
= 0;
2865 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
2866 if ( (nameAndDataVMOffset
.first
== classNameSectionVMOffset
) && (nameAndDataVMOffset
.second
== classSectionVMOffset
) )
2868 ++hashTableVMOffsetsIndex
;
2871 if (hashTableVMOffsetsIndex
== image
.classesNameAndDataVMOffsets
.count()) {
2872 // Didn't find an image entry with this offset. Add one if we have space
2873 uint64_t totalHashTableImages
= image
.classesNameAndDataVMOffsets
.count() + _objcClassesHashTableImages
.count();
2874 if ( totalHashTableImages
== Image::ObjCClassNameImageOffset::maximumImageIndex
) {
2875 // No more space. We need to give up
2876 diag
.error("No more space for class hash table image");
2879 image
.classesNameAndDataVMOffsets
.push_back({ classNameSectionVMOffset
, classSectionVMOffset
});
2882 hashTableVMOffsetsIndex
+= _objcClassesHashTableImages
.count();
2884 uint64_t classNameOffset
= classNameVMAddr
- classNameSectionStartVMAddr
;
2885 uint64_t classDataOffset
= classVMAddr
- classSectionStartVMAddr
;
2887 closure::Image::ObjCClassNameImageOffset classNameTarget
;
2888 classNameTarget
.classNameImageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2889 classNameTarget
.classNameImageOffset
= (uint32_t)classNameOffset
;
2891 dyld3::closure::Image::ObjCClassImageOffset classDataTarget
;
2892 classDataTarget
.classData
.imageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
2893 classDataTarget
.classData
.imageOffset
= (uint32_t)classDataOffset
;
2894 classDataTarget
.classData
.isDuplicate
= 0;
2896 seenClasses
.push_back({ classNameTarget
, classDataTarget
});
2900 void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t
* objcProtocolOpt
,
2901 const Map
<const dyld3::MachOAnalyzer
*, bool, HashPointer
, EqualPointer
>& sharedCacheImagesMap
,
2902 ObjCOptimizerImage
& image
) {
2904 BuilderLoadedImage
& li
= *image
.loadedImage
;
2905 OverflowSafeArray
<ObjCOptimizerImage::SeenClass
>& seenProtocols
= image
.seenProtocols
;
2907 const dyld3::MachOAnalyzer
* ma
= li
.loadAddress();
2908 const uint32_t pointerSize
= ma
->pointerSize();
2909 const uint64_t loadAddress
= ma
->preferredLoadAddress();
2910 const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= ma
->makeVMAddrConverter(li
.contentRebased
);
2912 // Protocol names and data may be in different sections depending on swift vs objc so handle multiple sections
2913 __block
MachOAnalyzer::SectionCache
protocolNameSectionCache(ma
);
2914 __block
MachOAnalyzer::SectionCache
protocolSectionCache(ma
);
2916 ma
->forEachObjCProtocol(image
.diag
, vmAddrConverter
, ^(Diagnostics
&diag
, uint64_t protocolVMAddr
,
2917 const dyld3::MachOAnalyzer::ObjCProtocol
&objCProtocol
) {
2918 if ( objCProtocol
.isaVMAddr
!= 0 ) {
2919 // We can't optimize this protocol if it has an ISA as we want to override it
2920 diag
.error("Protocol ISA cannot be non-zero");
2924 // Get the section for the name
2925 const char* protocolName
= nullptr;
2926 MachOAnalyzer::PrintableStringResult protocolNameResult
= MachOAnalyzer::PrintableStringResult::UnknownSection
;
2927 __block
uint64_t protocolNameSectionStartVMAddr
= 0;
2928 auto protocolNameSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2929 // We only have 24-bits in ObjCClassNameImageOffset to index in to the strings
2930 if (sectInfo
.sectSize
>= Image::ObjCClassNameImageOffset::maximumOffset
) {
2934 // We use 32-bit offsets so make sure the section is no larger than that.
2935 uint64_t protocolNameVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2936 if (protocolNameVMOffset
>= (1ULL << 32)) {
2940 protocolNameSectionStartVMAddr
= sectInfo
.sectAddr
;
2943 uint64_t protocolNameVMAddr
= objCProtocol
.nameVMAddr
;
2944 protocolName
= ma
->getPrintableString(protocolNameVMAddr
, protocolNameResult
,
2945 &protocolNameSectionCache
, protocolNameSectionHandler
);
2947 if ( protocolNameResult
!= MachOAnalyzer::PrintableStringResult::CanPrint
) {
2948 diag
.error("Invalid protocol name for objc optimisation");
2952 // If the protocol also exists in a shared cache image which is loaded, then objc
2953 // would have found that one, regardless of load order. So we can just skip this one.
2957 uint32_t count
= objcProtocolOpt
->getClassAndHeader(protocolName
, cls
, hi
);
2959 // exactly one matching protocol. Check if its loaded
2960 if (sharedCacheImagesMap
.find(getMachHeaderFromObjCHeaderInfo(hi
, pointerSize
)) != sharedCacheImagesMap
.end())
2963 else if (count
> 1) {
2964 // more than one matching protocol - find one that is loaded
2965 void *clslist
[count
];
2966 void *hilist
[count
];
2967 objcProtocolOpt
->getClassesAndHeaders(protocolName
, clslist
, hilist
);
2968 for (uint32_t i
= 0; i
< count
; i
++) {
2969 if (sharedCacheImagesMap
.find(getMachHeaderFromObjCHeaderInfo(hilist
[i
], pointerSize
)) != sharedCacheImagesMap
.end())
2975 // Get the section for the protocol itself
2976 __block
uint64_t protocolSectionStartVMAddr
= 0;
2977 auto protocolSectionHandler
= ^bool(const MachOAnalyzer::SectionInfo
& sectInfo
) {
2978 // We only have 23-bits in ObjCClassImageOffset to index in to the protocols
2979 if (sectInfo
.sectSize
> Image::ObjCClassImageOffset::maximumOffset
) {
2983 // We use 32-bit offsets so make sure the section is no larger than that.
2984 uint64_t protocolDatasVMOffset
= sectInfo
.sectAddr
- loadAddress
;
2985 if (protocolDatasVMOffset
>= (1ULL << 32)) {
2989 protocolSectionStartVMAddr
= sectInfo
.sectAddr
;
2992 if (!protocolSectionCache
.findSectionForVMAddr(protocolVMAddr
, protocolSectionHandler
)) {
2993 diag
.error("Invalid protocol for objc optimisation");
2997 // Make sure we have an entry for our images offsets for later
2998 uint64_t protocolNameSectionVMOffset
= protocolNameSectionStartVMAddr
- loadAddress
;
2999 uint64_t protocolSectionVMOffset
= protocolSectionStartVMAddr
- loadAddress
;
3000 uint64_t hashTableVMOffsetsIndex
= 0;
3001 for (auto nameAndDataVMOffset
: image
.classesNameAndDataVMOffsets
) {
3002 if ( (nameAndDataVMOffset
.first
== protocolNameSectionVMOffset
) && (nameAndDataVMOffset
.second
== protocolSectionVMOffset
) )
3004 ++hashTableVMOffsetsIndex
;
3007 if (hashTableVMOffsetsIndex
== image
.classesNameAndDataVMOffsets
.count()) {
3008 // Didn't find an image entry with this offset. Add one if we have space
3009 uint64_t totalHashTableImages
= image
.classesNameAndDataVMOffsets
.count() + _objcClassesHashTableImages
.count();
3010 if ( totalHashTableImages
== Image::ObjCClassNameImageOffset::maximumImageIndex
) {
3011 // No more space. We need to give up
3012 diag
.error("No more space for protocol hash table image");
3015 image
.classesNameAndDataVMOffsets
.push_back({ protocolNameSectionVMOffset
, protocolSectionVMOffset
});
3018 hashTableVMOffsetsIndex
+= _objcClassesHashTableImages
.count();
3020 uint64_t protocolNameOffset
= protocolNameVMAddr
- protocolNameSectionStartVMAddr
;
3021 uint64_t protocolDataOffset
= protocolVMAddr
- protocolSectionStartVMAddr
;
3023 closure::Image::ObjCClassNameImageOffset protocolNameTarget
;
3024 protocolNameTarget
.classNameImageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
3025 protocolNameTarget
.classNameImageOffset
= (uint32_t)protocolNameOffset
;
3027 dyld3::closure::Image::ObjCClassImageOffset protocolDataTarget
;
3028 protocolDataTarget
.classData
.imageIndex
= (uint32_t)hashTableVMOffsetsIndex
;
3029 protocolDataTarget
.classData
.imageOffset
= (uint32_t)protocolDataOffset
;
3030 protocolDataTarget
.classData
.isDuplicate
= 0;
3032 seenProtocols
.push_back({ protocolNameTarget
, protocolDataTarget
});
3036 void ClosureBuilder::parseObjCClassDuplicates(Map
<const char*, bool, HashCString
, EqualCString
>& duplicateClassesToIgnore
) {
3037 const ClosureBuilder::BuilderLoadedImage
& mainLi
= _loadedImages
[_mainProgLoadIndex
];
3039 const dyld3::MachOAnalyzer
* ma
= mainLi
.loadAddress();
3041 const uint32_t pointerSize
= ma
->pointerSize();
3042 const intptr_t slide
= ma
->getSlide();
3043 const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= ma
->makeVMAddrConverter(mainLi
.contentRebased
);
3045 uint64_t sectionSize
= 0;
3046 const void* section
= ma
->findSectionContent("__DATA", "__objc_dupclass", sectionSize
);
3051 // Ignore sections which are the wrong size
3052 if ( (sectionSize
% pointerSize
) != 0 )
3055 // Copied from objc-abi.h
3056 typedef struct _objc_duplicate_class
{
3059 const char name
[64];
3060 } objc_duplicate_class
;
3062 for (uint64_t offset
= 0; offset
!= sectionSize
; offset
+= pointerSize
) {
3063 uint64_t vmAddr
= *(uint64_t*)((uint64_t)section
+ offset
);
3064 vmAddr
= vmAddrConverter
.convertToVMAddr(vmAddr
);
3065 const objc_duplicate_class
* duplicateClass
= (const objc_duplicate_class
*)(vmAddr
+ slide
);
3066 duplicateClassesToIgnore
.insert({ duplicateClass
->name
, true });
3070 // used at launch by dyld when kernel has already mapped main executable
3071 const LaunchClosure
* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo
& fileInfo
, bool allowInsertFailures
)
3073 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_BUILD_CLOSURE
, 0, 0, 0);
3074 const mach_header
* mainMH
= (const mach_header
*)fileInfo
.fileContent
;
3075 // set up stack based storage for all arrays
3076 BuilderLoadedImage loadImagesStorage
[512];
3077 Image::LinkedImage dependenciesStorage
[512*8];
3078 InterposingTuple tuplesStorage
[64];
3079 Closure::PatchEntry cachePatchStorage
[64];
3080 _loadedImages
.setInitialStorage(loadImagesStorage
, 512);
3081 _dependencies
.setInitialStorage(dependenciesStorage
, 512*8);
3082 _interposingTuples
.setInitialStorage(tuplesStorage
, 64);
3083 _weakDefCacheOverrides
.setInitialStorage(cachePatchStorage
, 64);
3084 ArrayFinalizer
<BuilderLoadedImage
> scopedCleanup(_loadedImages
, ^(BuilderLoadedImage
& li
) { if (li
.unmapWhenDone
) {_fileSystem
.unloadFile(li
.loadedFileInfo
); li
.unmapWhenDone
=false;} });
3086 const MachOAnalyzer
* mainExecutable
= MachOAnalyzer::validMainExecutable(_diag
, mainMH
, fileInfo
.path
, fileInfo
.sliceLen
, _archs
, _platform
);
3087 if ( mainExecutable
== nullptr )
3089 if ( !mainExecutable
->isDynamicExecutable() ) {
3090 _diag
.error("not a main executable");
3093 if ( _platform
== Platform::macOS
) {
3094 // If this is an iOSMac program running on macOS, switch platforms
3095 if ( mainExecutable
->builtForPlatform(Platform::iOSMac
, true) ) {
3096 //_platform = Platform::iOSMac;
3097 Platform
* selfPlatform
= const_cast<Platform
*>(&_platform
);
3098 *selfPlatform
= Platform::iOSMac
;
3100 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
3101 else if ( mainExecutable
->builtForPlatform(Platform::iOS
, true) ) {
3102 //_platform = Platform::iOS;
3103 Platform
* selfPlatform
= const_cast<Platform
*>(&_platform
);
3104 *selfPlatform
= Platform::iOS
;
3107 if ( mainExecutable
->usesObjCGarbageCollection() ) {
3108 _diag
.error("program requires ObjC Garbage Collection which is no longer supported");
3112 // <rdar://problem/63308841> licenseware apps that zero out lazy bind opcodes cannot be pre-bound
3113 if ( mainExecutable
->hasStompedLazyOpcodes() )
3114 _makeMinimalClosure
= true;
3116 _isLaunchClosure
= true;
3117 _allowMissingLazies
= true;
3119 #if BUILDING_CACHE_BUILDER
3120 _makingClosuresInCache
= true;
3125 // add main executable
3126 __block BuilderLoadedImage mainEntry
;
3127 mainEntry
.loadedFileInfo
= fileInfo
;
3128 mainEntry
.imageNum
= 0; // We can't fill this in until we've done inserted dylibs
3129 mainEntry
.unmapWhenDone
= false;
3130 mainEntry
.contentRebased
= false;
3131 mainEntry
.hasInits
= false;
3132 mainEntry
.markNeverUnload
= true;
3133 mainEntry
.rtldLocal
= false;
3134 mainEntry
.isBadImage
= false;
3135 mainEntry
.mustBuildClosure
= true;
3136 mainEntry
.hasMissingWeakImports
= false;
3137 mainEntry
.hasInterposingTuples
= false; // only dylibs not in the dyld cache can have interposing tuples
3138 mainEntry
.overrideImageNum
= 0;
3139 mainEntry
.exportsTrieOffset
= 0;
3140 mainEntry
.exportsTrieSize
= 0;
3142 // Set the executable load path so that @executable_path can use it later
3143 _mainProgLoadPath
= fileInfo
.path
;
3145 // add any DYLD_INSERT_LIBRARIES
3146 _pathOverrides
.forEachInsertedDylib(^(const char* dylibPath
, bool &stop
) {
3147 LoadedImageChain chainMain
= { nullptr, mainEntry
};
3148 BuilderLoadedImage
* foundTopImage
;
3149 if ( !findImage(dylibPath
, chainMain
, foundTopImage
, LinkageType::kInserted
, 0, true) ) {
3150 if ( !allowInsertFailures
) {
3151 if ( _diag
.noError() )
3152 // if no other error was reported while trying to find the library, that means it is missing
3153 _diag
.error("could not load inserted dylib '%s' because image not found", dylibPath
);
3157 _diag
.clearError(); // FIXME add way to plumb back warning
3161 if ( _diag
.hasError() )
3164 _mainProgLoadIndex
= (uint32_t)_loadedImages
.count();
3165 mainEntry
.imageNum
= _startImageNum
+ _nextIndex
++;
3166 _loadedImages
.push_back(mainEntry
);
3168 // get mach_headers for all images needed to launch this main executable
3169 LoadedImageChain chainStart
= { nullptr, _loadedImages
[_mainProgLoadIndex
] };
3170 recursiveLoadDependents(chainStart
);
3171 if ( _diag
.hasError() )
3173 for (uint32_t i
=0; i
< _mainProgLoadIndex
; ++i
) {
3174 LoadedImageChain insertChainStart
= { nullptr, _loadedImages
[i
] };
3175 recursiveLoadDependents(insertChainStart
);
3176 if ( _diag
.hasError() )
3179 loadDanglingUpwardLinks();
3181 // If we have an on-disk image then we need all images which are dependent on the disk image to get a new
3182 // initializer order. Its not enough to just do the top level image as we may dlopen while in dlopen
3183 invalidateInitializerRoots();
3185 // now that everything loaded, set _libDyldImageNum and _libSystemImageNum
3186 for (BuilderLoadedImage
& li
: _loadedImages
) {
3187 if ( mainExecutable
->builtForPlatform(Platform::driverKit
) ) {
3188 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/System/DriverKit/usr/lib/system/libdyld.dylib") == 0) )
3189 _libDyldImageNum
= li
.imageNum
;
3190 else if ( strcmp(li
.path(), "/System/DriverKit/usr/lib/libSystem.dylib") == 0 )
3191 _libSystemImageNum
= li
.imageNum
;
3193 if ( li
.loadAddress()->isDylib() && (strcmp(li
.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) )
3194 _libDyldImageNum
= li
.imageNum
;
3195 else if ( strcmp(li
.path(), "/usr/lib/libSystem.B.dylib") == 0 )
3196 _libSystemImageNum
= li
.imageNum
;
3198 // don't use minimal closures when interposing is in play because we don't have runtime support to do interposing
3199 if ( li
.hasInterposingTuples
) {
3200 _makeMinimalClosure
= false;
3201 _leaveRebasesAsOpcodes
= false;
3205 // only some images need to go into closure (non-rooted ones from dyld cache do not)
3206 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3207 for (BuilderLoadedImage
& li
: _loadedImages
) {
3208 if ( li
.mustBuildClosure
) {
3209 writers
.push_back(ImageWriter());
3210 buildImage(writers
.back(), li
);
3211 if ( _diag
.hasError() )
3216 // only build objc closure info when building full closures
3217 bool optimizedObjC
= !_makeMinimalClosure
&& optimizeObjC(writers
);
3219 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3220 for (uintptr_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
3221 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
3222 if ( li
.mustBuildClosure
) {
3223 computeInitOrder(writers
[writerIndex
], (uint32_t)imageIndex
);
3228 // combine all Image objects into one ImageArray
3229 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3230 for (ImageWriter
& writer
: writers
) {
3231 imageArrayWriter
.appendImage(writer
.finalize());
3232 writer
.deallocate();
3234 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3236 // merge ImageArray object into LaunchClosure object
3237 __block LaunchClosureWriter
closureWriter(imageArray
);
3239 if (optimizedObjC
) {
3240 if (!_objcSelectorsHashTable
.empty())
3241 closureWriter
.setObjCSelectorInfo(_objcSelectorsHashTable
, _objcSelectorsHashTableImages
);
3243 if (!_objcClassesHashTableImages
.empty()) {
3244 closureWriter
.setObjCClassAndProtocolInfo(_objcClassesHashTable
, _objcProtocolsHashTable
,
3245 _objcClassesHashTableImages
);
3248 if ( _objcDuplicateClassWarnings
!= nullptr ) {
3249 _objcDuplicateClassWarnings
->forEachPath(^(const char* warning
) {
3250 closureWriter
.addWarning(Closure::Warning::duplicateObjCClass
, warning
);
3254 if (!_objcClassesDuplicatesHashTable
.empty())
3255 closureWriter
.setObjCDuplicateClassesInfo(_objcClassesDuplicatesHashTable
);
3258 // record shared cache info
3259 if ( _dyldCache
!= nullptr ) {
3260 // record cache UUID
3262 _dyldCache
->getUUID(cacheUUID
);
3263 closureWriter
.setDyldCacheUUID(cacheUUID
);
3265 // record any cache patching needed because of dylib overriding cache
3266 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3267 if ( li
.overrideImageNum
!= 0 ) {
3268 uint32_t imageIndex
= li
.overrideImageNum
- (uint32_t)_dyldImageArray
->startImageNum();
3269 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, _dyldCache
->patchableExportCount(imageIndex
));
3270 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
3271 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
3273 //fprintf(stderr, "'%s' overrides something in cache\n", li.loadedFileInfo.path);
3274 _dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* symbolName
) {
3275 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
3276 Diagnostics patchDiag
;
3277 Closure::PatchEntry patch
;
3278 patch
.overriddenDylibInCache
= li
.overrideImageNum
;
3279 patch
.exportCacheOffset
= cacheOffsetOfImpl
;
3280 if ( li
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3281 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3282 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3283 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3284 patch
.replacement
.image
.offset
= foundInfo
.value
;
3287 // this means the symbol is missing in the cache override dylib, see it moved to a sibling
3288 // <rdar://problem/59196856> allow patched impls to move between re-export sibling dylibs
3289 bool foundViaParent
= false;
3290 for (const BuilderLoadedImage
& li2
: _loadedImages
) {
3291 if ( (li2
.overrideImageNum
!= 0) && (li2
.imageNum
!= li
.imageNum
) ) {
3292 for (Image::LinkedImage aDep
: li2
.dependents
) {
3293 if ( (aDep
.kind() == Image::LinkKind::reExport
) && (aDep
.imageNum() == li
.imageNum
) ) {
3294 if ( li2
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3295 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3296 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3297 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3298 patch
.replacement
.image
.offset
= foundInfo
.value
;
3299 foundViaParent
= true;
3300 //fprintf(stderr, "found patch target '%s' previously in '%s', now in '%s'\n", symbolName, li.path(), li2.path());
3307 if ( !foundViaParent
) {
3308 // symbol is missing from override, set other cached dylibs that used it to NULL
3309 //fprintf(stderr, "could not find symbol '%s' in %s \n", symbolName, li.path());
3310 patch
.replacement
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
3311 patch
.replacement
.absolute
.value
= 0;
3314 patches
.push_back(patch
);
3316 closureWriter
.addCachePatches(patches
);
3320 // record any cache patching needed because weak-def C++ symbols override dyld cache
3321 if ( !_weakDefCacheOverrides
.empty() ) {
3322 closureWriter
.addCachePatches(_weakDefCacheOverrides
);
3327 uint32_t progVarsOffset
;
3328 if ( mainExecutable
->hasProgramVars(_diag
, progVarsOffset
) ) {
3329 // on macOS binaries may have a __dyld section that has ProgramVars to use
3330 closureWriter
.setHasProgramVars(progVarsOffset
);
3332 if ( _diag
.hasError() )
3336 // record any interposing info
3337 if ( !_interposingDisabled
) {
3338 imageArray
->forEachImage(^(const Image
* image
, bool &stop
) {
3339 if ( !image
->inDyldCache() )
3340 addInterposingTuples(closureWriter
, image
, findLoadedImage(image
->imageNum()).loadAddress());
3344 // modify fixups in contained Images by applying interposing tuples
3345 closureWriter
.applyInterposing((const LaunchClosure
*)closureWriter
.currentTypedBytes());
3348 closureWriter
.setUsedInterposing(_interposingTuplesUsed
);
3349 closureWriter
.setUsedAtPaths(_atPathUsed
);
3350 closureWriter
.setUsedFallbackPaths(_fallbackPathUsed
);
3351 closureWriter
.setHasInsertedLibraries(_mainProgLoadIndex
> 0);
3352 closureWriter
.setInitImageCount((uint32_t)_loadedImages
.count());
3354 // add other closure attributes
3355 addClosureInfo(closureWriter
);
3358 const LaunchClosure
* result
= closureWriter
.finalize();
3359 imageArrayWriter
.deallocate();
3361 timer
.setData4(dyld3::DyldTimingBuildClosure::LaunchClosure_Built
);
3366 // used by libdyld for dlopen()
3367 const DlopenClosure
* ClosureBuilder::makeDlopenClosure(const char* path
, const LaunchClosure
* mainClosure
, const Array
<LoadedImage
>& alreadyLoadedList
,
3368 closure::ImageNum callerImageNum
, bool noLoad
, bool forceBindLazies
, bool canUseSharedCacheClosure
, closure::ImageNum
* topImageNum
)
3370 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_BUILD_CLOSURE
, 0, 0, 0);
3371 // set up stack based storage for all arrays
3372 BuilderLoadedImage loadImagesStorage
[256];
3373 Image::LinkedImage dependenciesStorage
[128];
3374 Closure::PatchEntry cachePatchStorage
[64];
3375 _loadedImages
.setInitialStorage(loadImagesStorage
, 256);
3376 _dependencies
.setInitialStorage(dependenciesStorage
, 128);
3377 _weakDefCacheOverrides
.setInitialStorage(cachePatchStorage
, 64);
3378 ArrayFinalizer
<BuilderLoadedImage
> scopedCleanup(_loadedImages
, ^(BuilderLoadedImage
& li
) { if (li
.unmapWhenDone
) {_fileSystem
.unloadFile(li
.loadedFileInfo
); li
.unmapWhenDone
=false;} });
3380 // fill in builder array from already loaded images
3381 bool cachedDylibsExpectedOnDisk
= _dyldCache
? _dyldCache
->header
.dylibsExpectedOnDisk
: true;
3382 uintptr_t callerImageIndex
= UINTPTR_MAX
;
3383 for (const LoadedImage
& ali
: alreadyLoadedList
) {
3384 const Image
* image
= ali
.image();
3385 const MachOAnalyzer
* ma
= (MachOAnalyzer
*)(ali
.loadedAddress());
3386 bool inDyldCache
= ma
->inDyldCache();
3387 BuilderLoadedImage entry
;
3388 ImageNum overrideImageNum
;
3389 entry
.loadedFileInfo
.path
= image
->path();
3390 entry
.loadedFileInfo
.fileContent
= ma
;
3391 entry
.loadedFileInfo
.sliceOffset
= 0;
3392 entry
.loadedFileInfo
.inode
= 0;
3393 entry
.loadedFileInfo
.mtime
= 0;
3394 entry
.imageNum
= image
->imageNum();
3395 entry
.dependents
= image
->dependentsArray();
3396 entry
.unmapWhenDone
= false;
3397 entry
.contentRebased
= inDyldCache
;
3398 entry
.hasInits
= false;
3399 entry
.markNeverUnload
= image
->neverUnload();
3400 entry
.rtldLocal
= ali
.hideFromFlatSearch();
3401 entry
.isBadImage
= false;
3402 entry
.mustBuildClosure
= false;
3403 entry
.hasMissingWeakImports
= false;
3404 entry
.hasInterposingTuples
= !inDyldCache
&& ma
->isDylib() && ma
->hasInterposingTuples();
3405 entry
.overrideImageNum
= 0;
3406 entry
.exportsTrieOffset
= 0;
3407 entry
.exportsTrieSize
= 0;
3408 if ( image
->isOverrideOfDyldCacheImage(overrideImageNum
) ) {
3409 entry
.overrideImageNum
= overrideImageNum
;
3410 canUseSharedCacheClosure
= false;
3412 if ( !inDyldCache
|| cachedDylibsExpectedOnDisk
)
3413 image
->hasFileModTimeAndInode(entry
.loadedFileInfo
.inode
, entry
.loadedFileInfo
.mtime
);
3414 if ( entry
.imageNum
== callerImageNum
)
3415 callerImageIndex
= _loadedImages
.count();
3416 _loadedImages
.push_back(entry
);
3418 _alreadyInitedIndex
= (uint32_t)_loadedImages
.count();
3420 // find main executable (may be needed for @executable_path)
3421 _isLaunchClosure
= false;
3422 for (uint32_t i
=0; i
< alreadyLoadedList
.count(); ++i
) {
3423 if ( _loadedImages
[i
].loadAddress()->isMainExecutable() ) {
3424 _mainProgLoadIndex
= i
;
3425 _mainProgLoadPath
= _loadedImages
[i
].path();
3430 // We can't use an existing dlopen closure if the main closure had interposing tuples
3431 if (canUseSharedCacheClosure
) {
3432 if (mainClosure
->hasInterposings())
3433 canUseSharedCacheClosure
= false;
3436 // add top level dylib being dlopen()ed
3437 BuilderLoadedImage
* foundTopImage
= nullptr;
3439 // @rpath has caller's LC_PRATH, then main executable's LC_RPATH
3440 BuilderLoadedImage
& callerImage
= (callerImageIndex
!= UINTPTR_MAX
) ? _loadedImages
[callerImageIndex
] : _loadedImages
[_mainProgLoadIndex
];
3441 LoadedImageChain chainMain
= { nullptr, _loadedImages
[_mainProgLoadIndex
] };
3442 LoadedImageChain chainCaller
= { &chainMain
, callerImage
};
3443 if ( !findImage(path
, chainCaller
, foundTopImage
, LinkageType::kDynamic
, 0, canUseSharedCacheClosure
) ) {
3444 // If we didn't find the image, it might be a symlink to something in the dyld cache that is not on disk
3445 if ( (_dyldCache
!= nullptr) && !_dyldCache
->header
.dylibsExpectedOnDisk
) {
3446 char resolvedPath
[PATH_MAX
];
3447 if ( _fileSystem
.getRealPath(path
, resolvedPath
) ) {
3449 if ( !findImage(resolvedPath
, chainMain
, foundTopImage
, LinkageType::kDynamic
, 0, canUseSharedCacheClosure
) ) {
3453 // We didn't find a new path from realpath
3457 // cached dylibs on disk, so don't call realpath() again, it would have been found first call to findImage()
3462 // exit early in RTLD_NOLOAD mode
3464 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_NoLoad
);
3465 // if no new images added to _loadedImages, then requested path was already loaded
3466 if ( (uint32_t)_loadedImages
.count() == _alreadyInitedIndex
)
3467 *topImageNum
= foundTopImage
->imageNum
;
3473 // fast path if roots are not allowed and target is in dyld cache or is other
3474 if ( (_dyldCache
!= nullptr) && (_dyldCache
->header
.cacheType
== kDyldSharedCacheTypeProduction
) ) {
3475 if ( foundTopImage
->imageNum
< closure::kFirstLaunchClosureImageNum
) {
3476 if (foundTopImage
->imageNum
< closure::kLastDyldCacheImageNum
)
3477 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
);
3479 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
);
3480 *topImageNum
= foundTopImage
->imageNum
;
3485 // recursive load dependents
3486 // @rpath for stuff top dylib depends on uses LC_RPATH from caller, main exe, and dylib being dlopen()ed
3487 LoadedImageChain chainTopDylib
= { &chainMain
, *foundTopImage
};
3488 recursiveLoadDependents(chainTopDylib
, canUseSharedCacheClosure
);
3489 if ( _diag
.hasError() )
3491 loadDanglingUpwardLinks(canUseSharedCacheClosure
);
3492 if ( _diag
.hasError() )
3495 // RTLD_NOW means fail the dlopen() if a symbol cannot be bound
3496 _allowMissingLazies
= !forceBindLazies
;
3498 // If we got this far, we are not using a prebuilt dlopen-closure
3499 // Since dlopen closures are never saved to disk, don't put fixups into the closure
3500 // Except if interposing is used, since we don't have plumbing to apply interposing dynamically
3501 _makeMinimalClosure
= !mainClosure
->hasInterposings();
3503 // only some images need to go into closure (ones from dyld cache do not, unless the cache format changed)
3504 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3505 if ( _foundNonCachedImage
|| _foundDyldCacheRoots
) {
3506 // If we have an on-disk image then we need all images which are dependent on the disk image to get a new
3507 // initializer order. Its not enough to just do the top level image as we may dlopen while in dlopen
3508 invalidateInitializerRoots();
3510 for (uintptr_t loadedImageIndex
= 0; loadedImageIndex
!= _loadedImages
.count(); ++loadedImageIndex
) {
3511 BuilderLoadedImage
& li
= _loadedImages
[loadedImageIndex
];
3512 if ( li
.mustBuildClosure
) {
3513 writers
.push_back(ImageWriter());
3514 buildImage(writers
.back(), li
);
3515 if ( _diag
.hasError() )
3520 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3521 for (uintptr_t imageIndex
= 0, writerIndex
= 0; imageIndex
!= _loadedImages
.count(); ++imageIndex
) {
3522 BuilderLoadedImage
& li
= _loadedImages
[imageIndex
];
3523 if ( li
.mustBuildClosure
) {
3524 computeInitOrder(writers
[writerIndex
], (uint32_t)imageIndex
);
3529 if ( _diag
.hasError() )
3532 // check if top image loaded is in shared cache along with everything it depends on
3533 *topImageNum
= foundTopImage
->imageNum
;
3534 if ( _foundNonCachedImage
|| _foundDyldCacheRoots
) {
3535 if ( canUseSharedCacheClosure
&& ( foundTopImage
->imageNum
< closure::kFirstLaunchClosureImageNum
) ) {
3536 // We used a shared cache built closure, but now discovered roots. We need to try again
3538 return sRetryDlopenClosure
;
3541 if (foundTopImage
->imageNum
< closure::kLastDyldCacheImageNum
)
3542 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
);
3544 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
);
3548 // combine all Image objects into one ImageArray
3549 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3550 for (ImageWriter
& writer
: writers
) {
3551 imageArrayWriter
.appendImage(writer
.finalize());
3552 writer
.deallocate();
3554 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3556 // merge ImageArray object into LaunchClosure object
3557 DlopenClosureWriter
closureWriter(imageArray
);
3559 // add other closure attributes
3560 closureWriter
.setTopImageNum(foundTopImage
->imageNum
);
3562 // record any cache patching needed because of dylib overriding cache
3563 if ( _dyldCache
!= nullptr ) {
3564 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3565 if ( (li
.overrideImageNum
!= 0) && (li
.imageNum
>= _startImageNum
) ) {
3566 const Image
* cacheImage
= _dyldImageArray
->imageForNum(li
.overrideImageNum
);
3567 uint32_t imageIndex
= cacheImage
->imageNum() - (uint32_t)_dyldCache
->cachedDylibsImageArray()->startImageNum();
3568 STACK_ALLOC_ARRAY(Closure::PatchEntry
, patches
, _dyldCache
->patchableExportCount(imageIndex
));
3569 MachOLoaded::DependentToMachOLoaded reexportFinder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
3570 return (const MachOLoaded
*)findDependent(mh
, depIndex
);
3572 //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
3573 _dyldCache
->forEachPatchableExport(imageIndex
,
3574 ^(uint32_t cacheOffsetOfImpl
, const char* symbolName
) {
3575 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
3576 Diagnostics patchDiag
;
3577 Closure::PatchEntry patch
;
3578 patch
.overriddenDylibInCache
= li
.overrideImageNum
;
3579 patch
.exportCacheOffset
= cacheOffsetOfImpl
;
3580 if ( li
.loadAddress()->findExportedSymbol(patchDiag
, symbolName
, false, foundInfo
, reexportFinder
) ) {
3581 const MachOAnalyzer
* impDylib
= (const MachOAnalyzer
*)foundInfo
.foundInDylib
;
3582 patch
.replacement
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
3583 patch
.replacement
.image
.imageNum
= findLoadedImage(impDylib
).imageNum
;
3584 patch
.replacement
.image
.offset
= foundInfo
.value
;
3587 patch
.replacement
.absolute
.kind
= Image::ResolvedSymbolTarget::kindAbsolute
;
3588 patch
.replacement
.absolute
.value
= 0;
3590 patches
.push_back(patch
);
3592 closureWriter
.addCachePatches(patches
);
3597 // modify fixups in contained Images by applying interposing tuples
3598 closureWriter
.applyInterposing(mainClosure
);
3600 // Dlopen's should never keep track of missing paths as we don't cache these closures.
3601 assert(_mustBeMissingPaths
== nullptr);
3603 // make final DlopenClosure object
3604 const DlopenClosure
* result
= closureWriter
.finalize();
3605 imageArrayWriter
.deallocate();
3606 timer
.setData4(dyld3::DyldTimingBuildClosure::DlopenClosure_Built
);
3611 // used by dyld_closure_util
3612 const LaunchClosure
* ClosureBuilder::makeLaunchClosure(const char* mainPath
, bool allowInsertFailures
)
3614 char realerPath
[MAXPATHLEN
];
3615 closure::LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(_diag
, _fileSystem
, mainPath
, _archs
, _platform
, realerPath
);
3616 if ( _diag
.hasError() )
3618 loadedFileInfo
.path
= mainPath
;
3619 const MachOAnalyzer
* mh
= (const MachOAnalyzer
*)loadedFileInfo
.fileContent
;
3620 if (mh
== nullptr) {
3621 _diag
.error("could not load file");
3624 const_cast<PathOverrides
*>(&_pathOverrides
)->setMainExecutable(mh
, mainPath
);
3625 const LaunchClosure
* launchClosure
= makeLaunchClosure(loadedFileInfo
, allowInsertFailures
);
3626 loadedFileInfo
.unload(loadedFileInfo
);
3627 return launchClosure
;
3630 void ClosureBuilder::setDyldCacheInvalidFormatVersion() {
3631 _dyldCacheInvalidFormatVersion
= true;
3635 // used by dyld shared cache builder
3636 const ImageArray
* ClosureBuilder::makeDyldCacheImageArray(const Array
<CachedDylibInfo
>& dylibs
, const Array
<CachedDylibAlias
>& aliases
)
3638 // because this is run in cache builder using dispatch_apply() there is minimal stack space
3639 // so set up storage for all arrays to be vm_allocated
3640 uintptr_t maxImageCount
= dylibs
.count() + 16;
3641 _loadedImages
.reserve(maxImageCount
);
3642 _dependencies
.reserve(maxImageCount
*16);
3644 _makingDyldCacheImages
= true;
3645 _allowMissingLazies
= false;
3646 _aliases
= &aliases
;
3648 // build _loadedImages[] with every dylib in cache
3649 __block ImageNum imageNum
= _startImageNum
;
3650 for (const CachedDylibInfo
& aDylibInfo
: dylibs
) {
3651 BuilderLoadedImage entry
;
3652 entry
.loadedFileInfo
= aDylibInfo
.fileInfo
;
3653 entry
.imageNum
= imageNum
++;
3654 entry
.unmapWhenDone
= false;
3655 entry
.contentRebased
= false;
3656 entry
.hasInits
= false;
3657 entry
.markNeverUnload
= true;
3658 entry
.rtldLocal
= false;
3659 entry
.isBadImage
= false;
3660 entry
.mustBuildClosure
= false;
3661 entry
.hasMissingWeakImports
= false;
3662 entry
.hasInterposingTuples
= false; // dylibs in dyld cache cannot have interposing tuples
3663 entry
.overrideImageNum
= 0;
3664 entry
.exportsTrieOffset
= 0;
3665 entry
.exportsTrieSize
= 0;
3666 _loadedImages
.push_back(entry
);
3669 // wire up dependencies between cached dylibs
3670 for (BuilderLoadedImage
& li
: _loadedImages
) {
3671 LoadedImageChain chainStart
= { nullptr, li
};
3672 recursiveLoadDependents(chainStart
);
3673 if ( _diag
.hasError() )
3676 assert(_loadedImages
.count() == dylibs
.count());
3678 // create an ImageWriter for each cached dylib
3679 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3680 for (BuilderLoadedImage
& li
: _loadedImages
) {
3681 writers
.push_back(ImageWriter());
3682 buildImage(writers
.back(), li
);
3685 // add initializer order into each dylib
3686 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3687 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3688 uint32_t index
= li
.imageNum
- _startImageNum
;
3689 computeInitOrder(writers
[index
], index
);
3692 // combine all Image objects into one ImageArray
3693 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3694 for (ImageWriter
& writer
: writers
) {
3695 imageArrayWriter
.appendImage(writer
.finalize());
3696 writer
.deallocate();
3698 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3704 #if BUILDING_CACHE_BUILDER
3705 const ImageArray
* ClosureBuilder::makeOtherDylibsImageArray(const Array
<LoadedFileInfo
>& otherDylibs
, uint32_t cachedDylibsCount
)
3707 // because this is run in cache builder using dispatch_apply() there is minimal stack space
3708 // so set up storage for all arrays to be vm_allocated
3709 uintptr_t maxImageCount
= otherDylibs
.count() + cachedDylibsCount
+ 128;
3710 _loadedImages
.reserve(maxImageCount
);
3711 _dependencies
.reserve(maxImageCount
*16);
3713 // build _loadedImages[] with every dylib in cache, followed by others
3715 for (const LoadedFileInfo
& aDylibInfo
: otherDylibs
) {
3716 auto *mh
= (const MachOAnalyzer
*)aDylibInfo
.fileContent
;
3718 BuilderLoadedImage entry
;
3719 entry
.loadedFileInfo
= aDylibInfo
;
3720 entry
.imageNum
= _startImageNum
+ _nextIndex
++;
3721 entry
.unmapWhenDone
= false;
3722 entry
.contentRebased
= false;
3723 entry
.hasInits
= false;
3724 entry
.markNeverUnload
= mh
->markNeverUnload(_diag
);
3725 entry
.rtldLocal
= false;
3726 entry
.isBadImage
= false;
3727 entry
.mustBuildClosure
= false;
3728 entry
.hasMissingWeakImports
= false;
3729 entry
.hasInterposingTuples
= false; // all images here have passed canHavePrecomputedDlopenClosure() which does not allow interposing tuples
3730 entry
.overrideImageNum
= 0;
3731 entry
.exportsTrieOffset
= 0;
3732 entry
.exportsTrieSize
= 0;
3733 _loadedImages
.push_back(entry
);
3736 // wire up dependencies between cached dylibs
3737 // Note, _loadedImages can grow when we call recursiveLoadDependents so we need
3738 // to check the count on each iteration.
3739 for (uint64_t index
= 0; index
!= _loadedImages
.count(); ++index
) {
3740 BuilderLoadedImage
& li
= _loadedImages
[index
];
3741 LoadedImageChain chainStart
= { nullptr, li
};
3742 recursiveLoadDependents(chainStart
);
3743 if ( _diag
.hasError() ) {
3744 _diag
.warning("while building dlopen closure for %s: %s", li
.loadedFileInfo
.path
, _diag
.errorMessage().c_str());
3745 //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
3747 li
.isBadImage
= true; // mark bad
3751 auto invalidateBadImages
= [&]() {
3752 // Invalidate images with bad dependencies
3754 bool madeChange
= false;
3755 for (BuilderLoadedImage
& li
: _loadedImages
) {
3756 if (li
.isBadImage
) {
3757 // Already invalidated
3760 for (Image::LinkedImage depIndex
: li
.dependents
) {
3761 if ( depIndex
.imageNum() == kMissingWeakLinkedImage
)
3763 if ( depIndex
.imageNum() >= dyld3::closure::kLastDyldCacheImageNum
) {
3764 // dlopen closures can only depend on the shared cache. This is because if foo.dylib links bar.dylib
3765 // and bar.dylib is loaded in to the launch closure, then the dlopen closure for foo.dylib wouldn't see
3766 // bar.dylib at the image num in the launch closure
3767 _diag
.warning("while building dlopen closure for %s: dependent dylib is not from shared cache", li
.loadedFileInfo
.path
);
3768 li
.isBadImage
= true; // mark bad
3772 BuilderLoadedImage
& depImage
= findLoadedImage(depIndex
.imageNum());
3773 if (depImage
.isBadImage
) {
3774 _diag
.warning("while building dlopen closure for %s: dependent dylib had error", li
.loadedFileInfo
.path
);
3775 li
.isBadImage
= true; // mark bad
3785 invalidateBadImages();
3787 // create an ImageWriter for each cached dylib
3788 STACK_ALLOC_ARRAY(ImageWriter
, writers
, _loadedImages
.count());
3789 for (BuilderLoadedImage
& li
: _loadedImages
) {
3790 if ( li
.isBadImage
) {
3791 writers
.push_back(ImageWriter());
3792 writers
.back().setInvalid();
3795 if ( li
.imageNum
< dyld3::closure::kLastDyldCacheImageNum
)
3797 writers
.push_back(ImageWriter());
3798 buildImage(writers
.back(), li
);
3799 if ( _diag
.hasError() ) {
3800 _diag
.warning("while building dlopen closure for %s: %s", li
.loadedFileInfo
.path
, _diag
.errorMessage().c_str());
3801 //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
3803 li
.isBadImage
= true; // mark bad
3804 writers
.back().setInvalid();
3808 invalidateBadImages();
3810 // add initializer order into each dylib
3811 // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
3812 for (const BuilderLoadedImage
& li
: _loadedImages
) {
3813 if ( li
.imageNum
< dyld3::closure::kLastDyldCacheImageNum
)
3817 uint32_t index
= li
.imageNum
- _startImageNum
;
3818 computeInitOrder(writers
[index
], index
);
3821 // combine all Image objects into one ImageArray
3822 ImageArrayWriter
imageArrayWriter(_startImageNum
, (uint32_t)writers
.count(), _foundDyldCacheRoots
);
3823 for (ImageWriter
& writer
: writers
) {
3824 imageArrayWriter
.appendImage(writer
.finalize());
3825 writer
.deallocate();
3827 const ImageArray
* imageArray
= imageArrayWriter
.finalize();
3834 bool ClosureBuilder::inLoadedImageArray(const Array
<LoadedImage
>& loadedList
, ImageNum imageNum
)
3836 for (const LoadedImage
& ali
: loadedList
) {
3837 if ( ali
.image()->representsImageNum(imageNum
) )
3843 void ClosureBuilder::buildLoadOrderRecurse(Array
<LoadedImage
>& loadedList
, const Array
<const ImageArray
*>& imagesArrays
, const Image
* image
)
3845 // breadth first load
3846 STACK_ALLOC_ARRAY(const Image
*, needToRecurse
, 256);
3847 image
->forEachDependentImage(^(uint32_t dependentIndex
, dyld3::closure::Image::LinkKind kind
, ImageNum depImageNum
, bool &stop
) {
3848 if ( !inLoadedImageArray(loadedList
, depImageNum
) ) {
3849 const Image
* depImage
= ImageArray::findImage(imagesArrays
, depImageNum
);
3850 loadedList
.push_back(LoadedImage::make(depImage
));
3851 needToRecurse
.push_back(depImage
);
3856 for (const Image
* img
: needToRecurse
) {
3857 buildLoadOrderRecurse(loadedList
, imagesArrays
, img
);
3861 void ClosureBuilder::buildLoadOrder(Array
<LoadedImage
>& loadedList
, const Array
<const ImageArray
*>& imagesArrays
, const Closure
* toAdd
)
3863 const dyld3::closure::Image
* topImage
= ImageArray::findImage(imagesArrays
, toAdd
->topImageNum());
3864 loadedList
.push_back(LoadedImage::make(topImage
));
3865 buildLoadOrderRecurse(loadedList
, imagesArrays
, topImage
);
3870 //////////////////////////// ObjCStringTable ////////////////////////////////////////
3872 template<typename PerfectHashT
, typename ImageOffsetT
>
3873 void ObjCStringTable::write(const PerfectHashT
& phash
, const Array
<std::pair
<const char*, ImageOffsetT
>>& strings
)
3875 ObjCSelectorOpt::StringTarget sentinel
= (ObjCSelectorOpt::StringTarget
)ImageOffsetT::sentinelValue
;
3877 capacity
= phash
.capacity
;
3878 occupied
= phash
.occupied
;
3879 shift
= phash
.shift
;
3881 sentinelTarget
= sentinel
;
3882 roundedTabSize
= std::max(phash
.mask
+1, 4U);
3886 for (uint32_t i
= 0; i
< 256; i
++) {
3887 scramble
[i
] = phash
.scramble
[i
];
3889 for (uint32_t i
= 0; i
< phash
.mask
+1; i
++) {
3890 tab
[i
] = phash
.tab
[i
];
3893 dyld3::Array
<StringTarget
> targetsArray
= targets();
3894 dyld3::Array
<StringHashCheckByte
> checkBytesArray
= checkBytes();
3896 // Set offsets to the sentinel
3897 for (uint32_t i
= 0; i
< phash
.capacity
; i
++) {
3898 targetsArray
[i
] = sentinel
;
3900 // Set checkbytes to 0
3901 for (uint32_t i
= 0; i
< phash
.capacity
; i
++) {
3902 checkBytesArray
[i
] = 0;
3905 // Set real string offsets and checkbytes
3906 for (const auto& s
: strings
) {
3907 assert(s
.second
.raw
!= sentinelTarget
);
3908 uint32_t h
= hash(s
.first
);
3909 targetsArray
[h
] = s
.second
.raw
;
3910 checkBytesArray
[h
] = checkbyte(s
.first
);
3914 //////////////////////////// ObjCClassOpt ////////////////////////////////////////
3917 template<typename PerfectHashT
, typename ImageOffsetT
, typename ClassesMapT
>
3918 void ObjCClassOpt::write(const PerfectHashT
& phash
, const Array
<std::pair
<const char*, ImageOffsetT
>>& strings
,
3919 const ClassesMapT
& classes
, uint32_t preCalculatedDuplicateCount
)
3921 ObjCStringTable::write(phash
, strings
);
3923 __block
dyld3::Array
<ClassTarget
> classOffsetsArray
= classOffsets();
3924 __block
dyld3::Array
<ClassTarget
> duplicateOffsetsArray
= duplicateOffsets(preCalculatedDuplicateCount
);
3926 // Set class offsets to 0
3927 for (uint32_t i
= 0; i
< capacity
; i
++) {
3928 classOffsetsArray
[i
].raw
= dyld3::closure::Image::ObjCImageOffset::sentinelValue
;
3931 classes
.forEachEntry(^(const char *const &key
, const Image::ObjCClassImageOffset
**values
, uint64_t valuesCount
) {
3932 uint32_t keyIndex
= getIndex(key
);
3933 assert(keyIndex
!= indexNotFound
);
3934 assert(classOffsetsArray
[keyIndex
].raw
== dyld3::closure::Image::ObjCImageOffset::sentinelValue
);
3936 if (valuesCount
== 1) {
3937 // Only one entry so write it in to the class offsets directly
3938 Image::ObjCClassImageOffset classImageOffset
= *(values
[0]);
3939 assert(classImageOffset
.classData
.isDuplicate
== 0);
3940 classOffsetsArray
[keyIndex
] = classImageOffset
;
3944 // We have more than one value. We add a placeholder to the class offsets which tells us the head
3945 // of the linked list of classes in the duplicates array
3946 uint32_t dest
= duplicateCount();
3947 duplicateCount() += valuesCount
;
3949 Image::ObjCClassImageOffset classImagePlaceholder
;
3950 assert(valuesCount
< (1 << 8));
3951 classImagePlaceholder
.duplicateData
.count
= (uint32_t)valuesCount
;
3952 classImagePlaceholder
.duplicateData
.index
= dest
;
3953 classImagePlaceholder
.duplicateData
.isDuplicate
= 1;
3954 classOffsetsArray
[keyIndex
] = classImagePlaceholder
;
3956 for (uint64_t i
= 0; i
!= valuesCount
; ++i
) {
3957 Image::ObjCClassImageOffset classImageOffset
= *(values
[i
]);
3958 assert(classImageOffset
.classData
.isDuplicate
== 0);
3959 duplicateOffsetsArray
.push_back(classImageOffset
);
3965 } // namespace closure
3966 } // namespace dyld3