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@
31 #include <sys/errno.h>
32 #include <sys/types.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
37 #include <mach/mach_vm.h>
38 #include <mach-o/dyld.h>
39 #include <mach-o/dyld_priv.h>
40 #include <uuid/uuid.h>
47 #include "ImageProxy.h"
48 #include "FileUtils.h"
49 #include "StringUtils.h"
50 #include "MachOParser.h"
51 #include "LaunchCacheFormat.h"
52 #include "LaunchCacheWriter.h"
53 #include "PathOverrides.h"
54 #include "libdyldEntryVector.h"
58 typedef launch_cache::TargetSymbolValue TargetSymbolValue
;
62 /////////////////////////// ImageProxy ///////////////////////////
64 ImageProxy::ImageProxy(const mach_header
* mh
, const BinaryImageData
* imageData
, uint32_t indexInGroup
, bool dyldCacheIsRaw
)
65 : _mh(mh
), _sliceFileOffset(0), _modTime(0), _inode(0), _imageBinaryData(imageData
), _runtimePath(launch_cache::Image(imageData
).path()),
66 _groupNum(0), _indexInGroup(indexInGroup
), _isSetUID(false), _dyldCacheIsRaw(dyldCacheIsRaw
), _platformBinary(false), _overrideOf(ImageRef::weakImportMissing()),
67 _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false),
68 _invalid(launch_cache::Image(imageData
).isInvalid()), _staticallyReferenced(false), _cwdMustBeThisDir(false)
72 ImageProxy::ImageProxy(const DyldSharedCache::MappedMachO
& mapping
, uint32_t groupNum
, uint32_t indexInGroup
, bool dyldCacheIsRaw
)
73 : _mh(mapping
.mh
), _sliceFileOffset(mapping
.sliceFileOffset
), _modTime(mapping
.modTime
), _inode(mapping
.inode
), _imageBinaryData(nullptr), _runtimePath(mapping
.runtimePath
),
74 _groupNum(groupNum
), _indexInGroup(indexInGroup
), _isSetUID(mapping
.isSetUID
), _dyldCacheIsRaw(dyldCacheIsRaw
), _platformBinary(mapping
.protectedBySIP
),
75 _overrideOf(ImageRef::weakImportMissing()), _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false),
76 _invalid(false), _staticallyReferenced(false), _cwdMustBeThisDir(false)
81 void ImageProxy::processRPaths(ImageProxyGroup
& owningGroup
)
84 __block
std::unordered_set
<std::string
> rawRpaths
;
85 MachOParser
parser(_mh
, _dyldCacheIsRaw
);
86 parser
.forEachRPath(^(const char* rpath
, bool& stop
) {
87 if ( rawRpaths
.count(rpath
) ) {
88 _diag
.warning("duplicate LC_RPATH (%s) in %s", rpath
, _runtimePath
.c_str());
91 rawRpaths
.insert(rpath
);
92 std::string thisRPath
= rpath
;
93 if ( startsWith(thisRPath
, "@executable_path/") ) {
94 std::string mainPath
= owningGroup
.mainProgRuntimePath();
95 if ( mainPath
.empty() && parser
.isDynamicExecutable() ) {
96 mainPath
= _runtimePath
;
98 if ( !mainPath
.empty() ) {
99 std::string newPath
= mainPath
.substr(0, mainPath
.rfind('/')+1) + thisRPath
.substr(17);
100 std::string normalizedPath
= owningGroup
.normalizedPath(newPath
);
101 if ( fileExists(normalizedPath
) )
102 _rpaths
.push_back(normalizedPath
);
104 _diag
.warning("LC_RPATH to nowhere (%s) in %s", rpath
, _runtimePath
.c_str());
105 char resolvedMainPath
[PATH_MAX
];
106 if ( (realpath(mainPath
.c_str(), resolvedMainPath
) != nullptr) && (mainPath
.c_str() != resolvedMainPath
) ) {
107 std::string realMainPath
= resolvedMainPath
;
108 size_t lastSlashPos
= realMainPath
.rfind('/');
109 std::string newRealPath
= realMainPath
.substr(0, lastSlashPos
+1) + thisRPath
.substr(17);
110 if ( realMainPath
!= mainPath
) {
111 for (const std::string
& pre
: owningGroup
._buildTimePrefixes
) {
112 std::string aPath
= owningGroup
.normalizedPath(pre
+ newRealPath
);
113 if ( fileExists(aPath
) ) {
114 _rpaths
.push_back(owningGroup
.normalizedPath(newRealPath
));
121 _diag
.warning("LC_RPATH uses @executable_path in %s", _runtimePath
.c_str());
124 else if ( thisRPath
== "@executable_path" ) {
125 std::string mainPath
= owningGroup
.mainProgRuntimePath();
126 if ( mainPath
.empty() && parser
.isDynamicExecutable() ) {
127 mainPath
= _runtimePath
;
129 if ( !mainPath
.empty() ) {
130 std::string newPath
= mainPath
.substr(0, mainPath
.rfind('/')+1);
131 std::string normalizedPath
= owningGroup
.normalizedPath(newPath
);
132 _rpaths
.push_back(normalizedPath
);
135 _diag
.warning("LC_RPATH uses @executable_path in %s", _runtimePath
.c_str());
138 else if ( startsWith(thisRPath
, "@loader_path/") ) {
139 size_t lastSlashPos
= _runtimePath
.rfind('/');
140 std::string newPath
= _runtimePath
.substr(0, lastSlashPos
+1) + thisRPath
.substr(13);
142 for (const std::string
& pre
: owningGroup
._buildTimePrefixes
) {
143 std::string aPath
= owningGroup
.normalizedPath(pre
+ newPath
);
144 if ( fileExists(aPath
) ) {
145 _rpaths
.push_back(owningGroup
.normalizedPath(newPath
));
150 char resolvedPath
[PATH_MAX
];
151 if ( (realpath(_runtimePath
.c_str(), resolvedPath
) != nullptr) && (_runtimePath
.c_str() != resolvedPath
) ) {
152 std::string realRunPath
= resolvedPath
;
153 lastSlashPos
= realRunPath
.rfind('/');
154 std::string newRealPath
= realRunPath
.substr(0, lastSlashPos
+1) + thisRPath
.substr(13);
155 if ( newRealPath
!= newPath
) {
156 for (const std::string
& pre
: owningGroup
._buildTimePrefixes
) {
157 std::string aPath
= owningGroup
.normalizedPath(pre
+ newRealPath
);
158 if ( fileExists(aPath
) ) {
159 _rpaths
.push_back(owningGroup
.normalizedPath(newRealPath
));
167 // even though this path does not exist, we need to add it to must-be-missing paths
168 // in case it shows up at launch time
169 _rpaths
.push_back(owningGroup
.normalizedPath(newPath
));
170 _diag
.warning("LC_RPATH to nowhere (%s) in %s", rpath
, _runtimePath
.c_str());
173 else if ( thisRPath
== "@loader_path" ) {
174 size_t lastSlashPos
= _runtimePath
.rfind('/');
175 std::string newPath
= _runtimePath
.substr(0, lastSlashPos
+1);
176 std::string normalizedPath
= owningGroup
.normalizedPath(newPath
);
177 _rpaths
.push_back(normalizedPath
);
179 else if ( rpath
[0] == '@' ) {
180 _diag
.warning("LC_RPATH with unknown @ variable (%s) in %s", rpath
, _runtimePath
.c_str());
183 if ( rpath
[0] == '/' )
184 _diag
.warning("LC_RPATH is absolute path (%s) in %s", rpath
, _runtimePath
.c_str());
185 _rpaths
.push_back(rpath
);
188 //if ( !_rpaths.empty() ) {
189 // fprintf(stderr, "for %s\n", _runtimePath.c_str());
190 // for (const std::string& p : _rpaths)
191 // fprintf(stderr, " %s\n", p.c_str());
195 void ImageProxy::addDependentsDeep(ImageProxyGroup
& owningGroup
, RPathChain
* prev
, bool staticallyReferenced
)
197 // mark binaries that are statically referenced and thus will never be unloaded
198 if ( staticallyReferenced
)
199 _staticallyReferenced
= true;
201 if ( _deepDependentsSet
)
204 // find all immediate dependents
205 addDependentsShallow(owningGroup
, prev
);
206 if ( _diag
.hasError() ) {
211 // recurse though each dependent
212 RPathChain rchain
= { this, prev
, _rpaths
};
213 for (ImageProxy
* proxy
: _dependents
) {
214 if ( proxy
== nullptr )
215 continue; // skip over weak missing dependents
216 if ( !proxy
->_directDependentsSet
)
217 proxy
->addDependentsDeep(owningGroup
, &rchain
, staticallyReferenced
);
218 if ( proxy
->invalid() )
222 _deepDependentsSet
= true;
225 void ImageProxy::addDependentsShallow(ImageProxyGroup
& owningGroup
, RPathChain
* prev
)
227 if ( _directDependentsSet
)
230 MachOParser
thisParser(mh(), _dyldCacheIsRaw
);
231 dyld3::Platform thisPlatform
= thisParser
.platform();
233 processRPaths(owningGroup
);
234 __block RPathChain rchain
= { this, prev
, _rpaths
};
236 thisParser
.forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
237 if ( (loadPath
[0] != '/') && (loadPath
[0] != '@') ) {
238 _diag
.warning("load path is file system relative (%s) in %s", loadPath
, runtimePath().c_str());
241 ImageProxy
* dep
= owningGroup
.findImage(depDiag
, loadPath
, isWeak
, &rchain
);
242 if ( (dep
== nullptr) || dep
->invalid() ) {
244 // weak link against a broken dylib, pretend dylib is not there
247 if ( depDiag
.warnings().empty() ) {
248 if ( thisParser
.header()->filetype
== MH_EXECUTE
)
249 _diag
.error("required dylib '%s' not found", loadPath
);
251 _diag
.error("required dylib '%s' not found, needed by '%s'", loadPath
, runtimePath().c_str());
254 std::string allTries
;
255 for (const std::string
& warn
: depDiag
.warnings()) {
256 if ( allTries
.empty() )
259 allTries
= allTries
+ ", " + warn
;
261 _diag
.error("required dylib '%s' not found, needed by '%s'. Did try: %s", loadPath
, runtimePath().c_str(), allTries
.c_str());
266 MachOParser
depParser(dep
->mh(), _dyldCacheIsRaw
);
267 if ( _diag
.noError() ) {
268 // verify found image has compatible version and matching platform
269 dyld3::Platform depPlatform
= depParser
.platform();
270 if ( depPlatform
!= thisPlatform
) {
271 // simulator allows a few macOS libSystem dylibs
272 if ( !inLibSystem() || !dep
->inLibSystem() ) {
273 _diag
.error("found '%s' but it was built for different platform '%s' than required '%s'. Needed by '%s'", dep
->runtimePath().c_str(),
274 MachOParser::platformName(depPlatform
).c_str(), MachOParser::platformName(thisPlatform
).c_str(), runtimePath().c_str());
278 if ( _diag
.noError() ) {
279 // verify compat version
280 const char* installName
;
281 uint32_t foundCompatVers
;
282 uint32_t foundCurrentVers
;
283 if ( depParser
.header()->filetype
!= MH_DYLIB
) {
284 _diag
.error("found '%s' which is not a dylib. Needed by '%s'", dep
->runtimePath().c_str(), runtimePath().c_str());
287 depParser
.getDylibInstallName(&installName
, &foundCompatVers
, &foundCurrentVers
);
288 if ( foundCompatVers
< compatVersion
) {
289 _diag
.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'", dep
->runtimePath().c_str(),
290 MachOParser::versionString(foundCompatVers
).c_str(), MachOParser::versionString(compatVersion
).c_str(), runtimePath().c_str());
295 if ( _diag
.hasError() ) {
299 _dependents
.push_back(dep
);
301 _dependentsKind
.push_back(launch_cache::Image::LinkKind::weak
);
302 else if ( isReExport
)
303 _dependentsKind
.push_back(launch_cache::Image::LinkKind::reExport
);
305 _dependentsKind
.push_back(launch_cache::Image::LinkKind::upward
);
307 _dependentsKind
.push_back(launch_cache::Image::LinkKind::regular
);
309 _directDependentsSet
= true;
312 bool ImageProxy::inLibSystem() const
314 return startsWith(runtimePath(), "/usr/lib/system/") || startsWith(runtimePath(), "/usr/lib/libSystem.");
317 void ImageProxy::forEachDependent(void (^handler
)(ImageProxy
* dep
, LinkKind
)) const
319 for (int i
=0; i
< _dependents
.size(); ++i
) {
320 handler(_dependents
[i
], _dependentsKind
[i
]);
325 bool ImageProxy::findExportedSymbol(Diagnostics
& diag
, const char* symbolName
, MachOParser::FoundSymbol
& foundInfo
) const
327 MachOParser
parser(_mh
, _dyldCacheIsRaw
);
328 return parser
.findExportedSymbol(diag
, symbolName
, (void*)this, foundInfo
, ^(uint32_t depIndex
, const char* depLoadPath
, void* extra
, const mach_header
** foundMH
, void** foundExtra
) {
329 ImageProxy
* proxy
= (ImageProxy
*)extra
;
330 if ( depIndex
< proxy
->_dependents
.size() ) {
331 ImageProxy
* depProxy
= proxy
->_dependents
[depIndex
];
332 *foundMH
= depProxy
->_mh
;
333 *foundExtra
= (void*)depProxy
;
340 bool ImageProxy::InitOrderInfo::beforeHas(ImageRef ref
)
342 ImageRef clearRef
= ref
;
343 clearRef
.clearKind();
344 return ( std::find(initBefore
.begin(), initBefore
.end(), clearRef
) != initBefore
.end() );
347 bool ImageProxy::InitOrderInfo::upwardHas(ImageProxy
* proxy
)
349 return ( std::find(danglingUpward
.begin(), danglingUpward
.end(), proxy
) != danglingUpward
.end() );
352 void ImageProxy::InitOrderInfo::removeRedundantUpwards()
354 danglingUpward
.erase(std::remove_if(danglingUpward
.begin(), danglingUpward
.end(),
355 [&](ImageProxy
* proxy
) {
356 ImageRef
ref(0, proxy
->_groupNum
, proxy
->_indexInGroup
);
357 return beforeHas(ref
);
358 }), danglingUpward
.end());
363 // Every image has a list of "init-before" which means if that image was dlopen()ed
364 // here is the exact list of images to initialize in the exact order. This makes
365 // the runtime easy. It just walks the init-before list in order and runs each
366 // initializer if it has not already been run.
368 // The init-before list for each image is calculated based on the init-before list
369 // of each of its dependents. It simply starts with the list of its first dependent,
370 // then appends the list of the next, removing entries already in the list, etc.
371 // Lastly if the current image has an initializer, it is appended to its init-before list.
373 // To handle cycles, when recursing to get a dependent's init-before list, any image
374 // whose list is still being calculated (cycle), just returns its list so far.
376 // Explicit upward links are handled in two parts. First, in the algorithm described above,
377 // all upward links are ignored, which works fine as long as anything upward linked is
378 // downward linked at some point. If not, it is called a "dangling upward link". Since
379 // nothing depends on those, they are added to the end of the final init-before list.
382 void ImageProxy::recursiveBuildInitBeforeInfo(ImageProxyGroup
& owningGroup
)
384 if ( _initBeforesComputed
)
386 _initBeforesComputed
= true; // break cycles
388 if ( _imageBinaryData
!= nullptr ) {
389 assert(_groupNum
== 0);
390 // if this is proxy for something in dyld cache, get its list from cache
391 // and parse list into befores and upwards
392 launch_cache::Image
image(_imageBinaryData
);
393 image
.forEachInitBefore(^(launch_cache::binary_format::ImageRef ref
) {
394 if ( (LinkKind
)ref
.kind() == LinkKind::upward
) {
395 ImageProxyGroup
* groupP
= &owningGroup
;
396 while (groupP
->_groupNum
!= 0)
397 groupP
= groupP
->_nextSearchGroup
;
398 launch_cache::ImageGroup
dyldCacheGroup(groupP
->_basedOn
);
399 launch_cache::Image dyldCacheImage
= dyldCacheGroup
.image(ref
.indexInGroup());
401 ImageProxy
* p
= groupP
->findAbsoluteImage(diag
, dyldCacheImage
.path(), false, false);
402 if ( diag
.noError() )
403 _initBeforesInfo
.danglingUpward
.push_back(p
);
406 _initBeforesInfo
.initBefore
.push_back(ref
);
411 // calculate init-before list for this image by merging init-before's of all its dependent dylibs
412 unsigned depIndex
= 0;
413 for (ImageProxy
* depProxy
: _dependents
) {
414 if ( depProxy
== nullptr ) {
415 assert(_dependentsKind
[depIndex
] == LinkKind::weak
);
418 if ( _dependentsKind
[depIndex
] == LinkKind::upward
) {
419 // if this upward link is already in the list, we ignore it. Otherwise add to front of list
420 if ( _initBeforesInfo
.upwardHas(depProxy
) ) {
421 // already in upward list, do nothing
424 ImageRef
ref(0, depProxy
->_groupNum
, depProxy
->_indexInGroup
);
425 if ( _initBeforesInfo
.beforeHas(ref
) ) {
426 // already in before list, do nothing
429 // add to upward list
430 _initBeforesInfo
.danglingUpward
.push_back(depProxy
);
435 // compute init-befores of downward dependents
436 depProxy
->recursiveBuildInitBeforeInfo(owningGroup
);
437 // merge befores from this downward link into current befores list
438 for (ImageRef depInit
: depProxy
->_initBeforesInfo
.initBefore
) {
439 if ( !_initBeforesInfo
.beforeHas(depInit
) )
440 _initBeforesInfo
.initBefore
.push_back(depInit
);
442 // merge upwards from this downward link into current befores list
443 for (ImageProxy
* upProxy
: depProxy
->_initBeforesInfo
.danglingUpward
) {
444 ImageRef
ref(0, upProxy
->_groupNum
, upProxy
->_indexInGroup
);
445 if ( _initBeforesInfo
.beforeHas(ref
) ) {
446 // already in current initBefore list, so ignore this upward
448 else if ( _initBeforesInfo
.upwardHas(upProxy
) ) {
449 // already in current danglingUpward list, so ignore this upward
452 // append to current danglingUpward list
453 _initBeforesInfo
.danglingUpward
.push_back(upProxy
);
460 // eliminate any upward links added to befores list by some other dependent
461 _initBeforesInfo
.removeRedundantUpwards();
463 // if this images has initializer(s) (or +load), add it to list
464 MachOParser
parser(_mh
, _dyldCacheIsRaw
);
466 if ( parser
.hasInitializer(diag
) || parser
.hasPlusLoadMethod(diag
) ) {
467 launch_cache::binary_format::ImageRef
ref(0, _groupNum
, _indexInGroup
);
468 _initBeforesInfo
.initBefore
.push_back(ref
);
471 //fprintf(stderr, "info for (%d, %d) %s\n", _group, _index, _runtimePath.c_str());
472 //for (ImageRef ref : _initBeforesInfo.initBefore)
473 // fprintf(stderr, " ref = {%d, %d, %d}\n", ref.kind(), ref.group(), ref.index());
474 //for (ImageProxy* p : _initBeforesInfo.danglingUpward)
475 // fprintf(stderr, " up = %s\n", p->runtimePath().c_str());
479 void ImageProxy::convertInitBeforeInfoToArray(ImageProxyGroup
& owningGroup
)
481 if ( _initBeforesInfo
.danglingUpward
.empty() ) {
482 _initBeforesArray
= _initBeforesInfo
.initBefore
;
485 for (ImageRef ref
: _initBeforesInfo
.initBefore
)
486 _initBeforesArray
.push_back(ref
);
487 bool inLibSys
= inLibSystem();
488 for (ImageProxy
* proxy
: _initBeforesInfo
.danglingUpward
) {
489 // ignore upward dependendencies between stuff within libSystem.dylib
490 if ( inLibSys
&& proxy
->inLibSystem() )
492 proxy
->getInitBeforeList(owningGroup
);
493 for (ImageRef depInit
: proxy
->_initBeforesInfo
.initBefore
) {
494 if ( std::find(_initBeforesArray
.begin(), _initBeforesArray
.end(), depInit
) == _initBeforesArray
.end() )
495 _initBeforesArray
.push_back(depInit
);
497 ImageRef
ref(0, proxy
->_groupNum
, proxy
->_indexInGroup
);
498 if ( std::find(_initBeforesArray
.begin(), _initBeforesArray
.end(), ref
) == _initBeforesArray
.end() )
499 _initBeforesArray
.push_back(ref
);
502 //fprintf(stderr, "final init-before info for %s\n", _runtimePath.c_str());
503 //for (ImageRef ref : _initBeforesArray) {
504 // fprintf(stderr, " ref = {%d, %d, %d}\n", ref.linkKind, ref.group, ref.index);
508 const std::vector
<ImageRef
>& ImageProxy::getInitBeforeList(ImageProxyGroup
& owningGroup
)
510 if ( !_initBeforesArraySet
) {
511 _initBeforesArraySet
= true; // break cycles
512 recursiveBuildInitBeforeInfo(owningGroup
);
513 convertInitBeforeInfoToArray(owningGroup
);
515 return _initBeforesArray
;
518 ImageProxy::FixupInfo
ImageProxy::buildFixups(Diagnostics
& diag
, uint64_t cacheUnslideBaseAddress
, launch_cache::ImageGroupWriter
& groupWriter
) const
520 __block
ImageProxy::FixupInfo info
;
521 MachOParser
image(_mh
, _dyldCacheIsRaw
);
523 // add fixup for each rebase
524 __block
bool rebaseError
= false;
525 image
.forEachRebase(diag
, ^(uint32_t segIndex
, uint64_t segOffset
, uint8_t type
, bool& stop
) {
526 dyld3::launch_cache::ImageGroupWriter::FixupType fixupType
= launch_cache::ImageGroupWriter::FixupType::rebase
;
528 case REBASE_TYPE_POINTER
:
529 fixupType
= launch_cache::ImageGroupWriter::FixupType::rebase
;
531 case REBASE_TYPE_TEXT_ABSOLUTE32
:
532 fixupType
= launch_cache::ImageGroupWriter::FixupType::rebaseText
;
533 info
.hasTextRelocs
= true;
535 case REBASE_TYPE_TEXT_PCREL32
:
536 diag
.error("pcrel text rebasing not supported");
541 diag
.error("unknown rebase type");
546 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeInvalid()});
547 //fprintf(stderr, "rebase: segIndex=%d, segOffset=0x%0llX, type=%d\n", segIndex, segOffset, type);
549 if ( diag
.hasError() )
552 // add fixup for each bind
553 image
.forEachBind(diag
, ^(uint32_t segIndex
, uint64_t segOffset
, uint8_t type
, int libOrdinal
,
554 uint64_t addend
, const char* symbolName
, bool weakImport
, bool lazy
, bool& stop
) {
555 launch_cache::ImageGroupWriter::FixupType fixupType
;
557 case BIND_TYPE_POINTER
:
559 fixupType
= launch_cache::ImageGroupWriter::FixupType::pointerLazyBind
;
561 fixupType
= launch_cache::ImageGroupWriter::FixupType::pointerBind
;
563 case BIND_TYPE_TEXT_ABSOLUTE32
:
564 fixupType
= launch_cache::ImageGroupWriter::FixupType::bindText
;
565 info
.hasTextRelocs
= true;
567 case BIND_TYPE_TEXT_PCREL32
:
568 fixupType
= launch_cache::ImageGroupWriter::FixupType::bindTextRel
;
569 info
.hasTextRelocs
= true;
571 case BIND_TYPE_IMPORT_JMP_REL32
:
572 fixupType
= launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel
;
575 diag
.error("malformed executable, unknown bind type (%d)", type
);
579 const ImageProxy
* depProxy
= nullptr;
580 bool isWeakDylib
= false;
581 if ( libOrdinal
== BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
) {
582 // -bundle_loader symbols cannot be bound ahead of time, we must look them up at load time
583 uint32_t imagePathPoolOffset
= groupWriter
.addString("@main");
584 uint32_t imageSymbolPoolOffset
= groupWriter
.addString(symbolName
);
585 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset
, imageSymbolPoolOffset
, weakImport
)});
588 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_FLAT_LOOKUP
) {
589 // -dynamic_lookup symbols cannot be bound ahead of time, we must look them up at load time
590 uint32_t imagePathPoolOffset
= groupWriter
.addString("@flat");
591 uint32_t imageSymbolPoolOffset
= groupWriter
.addString(symbolName
);
592 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset
, imageSymbolPoolOffset
, weakImport
)});
595 else if ( libOrdinal
== BIND_SPECIAL_DYLIB_SELF
) {
598 else if ( (libOrdinal
>= 1) && (libOrdinal
<= _dependents
.size()) ) {
599 isWeakDylib
= (_dependentsKind
[libOrdinal
-1] == LinkKind::weak
);
600 depProxy
= _dependents
[libOrdinal
-1];
603 diag
.error("ordinal %d not supported", libOrdinal
);
607 if ( depProxy
!= nullptr ) {
608 MachOParser::FoundSymbol foundInfo
;
609 if ( depProxy
->findExportedSymbol(diag
, symbolName
, foundInfo
) ) {
610 MachOParser
implDylib(foundInfo
.foundInDylib
, _dyldCacheIsRaw
);
611 switch ( foundInfo
.kind
) {
612 case MachOParser::FoundSymbol::Kind::headerOffset
:
613 case MachOParser::FoundSymbol::Kind::resolverOffset
:
614 if ( implDylib
.inDyldCache() ) {
615 uint32_t cacheOffset
= (uint32_t)(implDylib
.preferredLoadAddress() + foundInfo
.value
- cacheUnslideBaseAddress
+ addend
);
616 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeSharedCacheOffset(cacheOffset
)});
619 ImageProxy
* foundProxy
= (ImageProxy
*)(foundInfo
.foundExtra
);
620 bool isIndirectGroupNum
= foundProxy
->_groupNum
>= 128;
621 uint32_t groupNum
= isIndirectGroupNum
? groupWriter
.addIndirectGroupNum(foundProxy
->_groupNum
) : foundProxy
->_groupNum
;
622 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeGroupValue(groupNum
, foundProxy
->_indexInGroup
, foundInfo
.value
+addend
, isIndirectGroupNum
)});
625 case MachOParser::FoundSymbol::Kind::absolute
:
626 if (((((intptr_t)(foundInfo
.value
+addend
)) << 2) >> 2) != (foundInfo
.value
+addend
)) {
627 diag
.error("absolute value %lld not supported", foundInfo
.value
+addend
);
631 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeAbsolute(foundInfo
.value
+addend
)});
637 diag
.error("symbol '%s' not found, expected in '%s'", symbolName
, depProxy
->runtimePath().c_str());
640 // mark fixup needs to set fixup location to zero
641 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeAbsolute(0)});
646 // dylib not found and is weak, set pointers into it to zero
647 info
.fixups
.push_back({segIndex
, segOffset
, fixupType
, TargetSymbolValue::makeAbsolute(0)});
650 diag
.error("dylib ordinal %d not found and not weak", libOrdinal
);
655 if ( diag
.hasError() )
658 uint32_t weakDefPathPoolOffset
= groupWriter
.addString("@weak_def");
659 image
.forEachWeakDef(diag
, ^(bool strongDef
, uint32_t segIndex
, uint64_t segOffset
, uint64_t addend
, const char* symbolName
, bool& stop
) {
662 // find fixup for that location and change it to be a @weakdef dynamic target
663 bool altered
= false;
664 for (FixUp
& fixup
: info
.fixups
) {
665 if ( (fixup
.segOffset
== segOffset
) && (fixup
.segIndex
== segIndex
) ) {
666 uint32_t symbolPoolOffset
= groupWriter
.addString(symbolName
);
667 fixup
.type
= launch_cache::ImageGroupWriter::FixupType::pointerBind
;
668 fixup
.target
= TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset
, symbolPoolOffset
, false);
673 if ( image
.isSlideable() ) {
674 fprintf(stderr
, "weak def for %s can't find underlying rebase/bind in %s\n", symbolName
, runtimePath().c_str());
677 // no-pie executable does not have rebase for weak-def fixup, so add fixup
678 uint32_t symbolPoolOffset
= groupWriter
.addString(symbolName
);
679 info
.fixups
.push_back({segIndex
, segOffset
, launch_cache::ImageGroupWriter::FixupType::pointerBind
, TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset
, symbolPoolOffset
, false)} );
689 void ImageProxy::setOverrideOf(uint32_t groupNum
, uint32_t indexInGroup
)
691 _overrideOf
= ImageRef(0, groupNum
, indexInGroup
);
695 static bool alreadyInList(const std::vector
<ImageProxy
*>& imageList
, ImageProxy
* image
)
697 for (ImageProxy
* proxy
: imageList
) {
698 if ( proxy
== image
)
704 void ImageProxy::addToFlatLookup(std::vector
<ImageProxy
*>& imageList
)
706 // add all images shallow
707 bool addedSomething
= false;
708 for (ImageProxy
* dep
: _dependents
) {
709 if ( dep
== nullptr )
711 if ( !alreadyInList(imageList
, dep
) ) {
712 imageList
.push_back(dep
);
713 addedSomething
= true;
717 if ( addedSomething
) {
718 for (ImageProxy
* dep
: _dependents
) {
719 if ( dep
== nullptr )
721 dep
->addToFlatLookup(imageList
);
727 /////////////////////////// ImageProxyGroup ///////////////////////////
733 uint32_t add(const std::string
& str
);
734 size_t size() const { return _buffer
.size(); }
735 const char* buffer() const { return &_buffer
[0]; }
738 std::vector
<char> _buffer
;
739 std::unordered_map
<std::string
, uint32_t> _existingEntries
;
742 uint32_t StringPool::add(const std::string
& str
)
744 auto pos
= _existingEntries
.find(str
);
745 if ( pos
!= _existingEntries
.end() )
747 size_t len
= str
.size() + 1;
748 size_t offset
= _buffer
.size();
749 _buffer
.insert(_buffer
.end(), &str
[0], &str
[len
]);
750 _existingEntries
[str
] = (uint32_t)offset
;
751 assert(offset
< 0xFFFF);
752 return (uint32_t)offset
;
755 void StringPool::align()
757 while ( (_buffer
.size() % 4) != 0 )
758 _buffer
.push_back('\0');
761 ImageProxyGroup::ImageProxyGroup(uint32_t groupNum
, const DyldCacheParser
& dyldCache
, const launch_cache::binary_format::ImageGroup
* basedOn
,
762 ImageProxyGroup
* next
, const std::string
& mainProgRuntimePath
,
763 const std::vector
<const BinaryImageGroupData
*>& knownGroups
,
764 const std::vector
<std::string
>& buildTimePrefixes
,
765 const std::vector
<std::string
>& envVars
,
766 bool stubsEliminated
, bool dylibsExpectedOnDisk
, bool inodesAreSameAsRuntime
)
767 : _pathOverrides(envVars
), _patchTable(nullptr), _basedOn(basedOn
), _dyldCache(dyldCache
), _nextSearchGroup(next
), _groupNum(groupNum
),
768 _stubEliminated(stubsEliminated
), _dylibsExpectedOnDisk(dylibsExpectedOnDisk
), _inodesAreSameAsRuntime(inodesAreSameAsRuntime
),
769 _knownGroups(knownGroups
), _buildTimePrefixes(buildTimePrefixes
), _mainProgRuntimePath(mainProgRuntimePath
), _platform(Platform::unknown
)
771 _archName
= dyldCache
.cacheHeader()->archName();
772 _platform
= (Platform
)(dyldCache
.cacheHeader()->platform());
776 ImageProxyGroup::~ImageProxyGroup()
778 for (DyldSharedCache::MappedMachO
& mapping
: _ownedMappings
) {
779 vm_deallocate(mach_task_self(), (vm_address_t
)mapping
.mh
, mapping
.length
);
781 for (ImageProxy
* proxy
: _images
) {
787 std::string
ImageProxyGroup::normalizedPath(const std::string
& path
)
789 for (const std::string
& prefix
: _buildTimePrefixes
) {
790 std::string fullPath
= prefix
+ path
;
791 if ( fileExists(fullPath
) ) {
792 if ( (fullPath
.find("/../") != std::string::npos
) || (fullPath
.find("//") != std::string::npos
) || (fullPath
.find("/./") != std::string::npos
) ) {
793 char resolvedPath
[PATH_MAX
];
794 if ( realpath(fullPath
.c_str(), resolvedPath
) != nullptr ) {
795 std::string resolvedUnPrefixed
= &resolvedPath
[prefix
.size()];
796 return resolvedUnPrefixed
;
806 ImageProxy
* ImageProxyGroup::findImage(Diagnostics
& diag
, const std::string
& runtimeLoadPath
, bool canBeMissing
, ImageProxy::RPathChain
* rChain
)
808 __block ImageProxy
* result
= nullptr;
809 _pathOverrides
.forEachPathVariant(runtimeLoadPath
.c_str(), _platform
, ^(const char* possiblePath
, bool& stop
) {
810 if ( startsWith(possiblePath
, "@rpath/") ) {
811 std::string trailing
= &possiblePath
[6];
812 for (const ImageProxy::RPathChain
* cur
=rChain
; cur
!= nullptr; cur
= cur
->prev
) {
813 for (const std::string
& rpath
: cur
->rpaths
) {
814 std::string aPath
= rpath
+ trailing
;
815 result
= findAbsoluteImage(diag
, aPath
, true, false);
816 if ( result
!= nullptr ) {
817 _pathToProxy
[runtimeLoadPath
] = result
;
823 // if cannot be found via current stack of rpaths, check if already found
824 auto pos
= _pathToProxy
.find(possiblePath
);
825 if ( pos
!= _pathToProxy
.end() ) {
826 result
= pos
->second
;
831 else if ( startsWith(possiblePath
, "@loader_path/") ) {
832 std::string loaderFile
= rChain
->inProxy
->runtimePath();
833 size_t lastSlash
= loaderFile
.rfind('/');
834 if ( lastSlash
!= std::string::npos
) {
835 std::string loaderDir
= loaderFile
.substr(0, lastSlash
);
836 std::string newPath
= loaderDir
+ &possiblePath
[12];
837 result
= findAbsoluteImage(diag
, newPath
, canBeMissing
, false);
838 if ( result
!= nullptr ) {
839 _pathToProxy
[runtimeLoadPath
] = result
;
845 else if ( startsWith(possiblePath
, "@executable_path/") ) {
846 for (const ImageProxy::RPathChain
* cur
=rChain
; cur
!= nullptr; cur
= cur
->prev
) {
847 if ( cur
->inProxy
->mh()->filetype
== MH_EXECUTE
) {
848 std::string mainProg
= cur
->inProxy
->runtimePath();
849 size_t lastSlash
= mainProg
.rfind('/');
850 if ( lastSlash
!= std::string::npos
) {
851 std::string mainDir
= mainProg
.substr(0, lastSlash
);
852 std::string newPath
= mainDir
+ &possiblePath
[16];
853 result
= findAbsoluteImage(diag
, newPath
, canBeMissing
, false);
854 if ( result
!= nullptr ) {
855 _pathToProxy
[runtimeLoadPath
] = result
;
864 // load command is full path to dylib
865 result
= findAbsoluteImage(diag
, possiblePath
, canBeMissing
, false);
866 if ( result
!= nullptr ) {
873 // when building closure, check if an added dylib is an override for something in the cache
874 if ( (result
!= nullptr) && (_groupNum
> 1) && !result
->isProxyForCachedDylib() ) {
875 for (ImageProxyGroup
* grp
= this; grp
!= nullptr; grp
= grp
->_nextSearchGroup
) {
876 if ( grp
->_basedOn
== nullptr )
878 uint32_t indexInGroup
;
879 launch_cache::ImageGroup
imageGroup(grp
->_basedOn
);
880 if ( imageGroup
.findImageByPath(runtimeLoadPath
.c_str(), indexInGroup
) ) {
881 result
->setOverrideOf(imageGroup
.groupNum(), indexInGroup
);
891 bool ImageProxyGroup::builtImageStillValid(const launch_cache::Image
& image
)
893 // only do checks when running on system
894 if ( _buildTimePrefixes
.size() != 1 )
896 if ( _buildTimePrefixes
.front().size() != 0 )
898 if ( _platform
!= MachOParser::currentPlatform() )
902 bool expectedOnDisk
= image
.group().dylibsExpectedOnDisk();
903 bool overridableDylib
= image
.overridableDylib();
904 bool cachedDylib
= !image
.isDiskImage();
905 bool fileFound
= ( ::stat(image
.path(), &statBuf
) == 0 );
908 if ( expectedOnDisk
) {
910 // macOS case: verify dylib file info matches what it was when cache was built
911 return ( (image
.fileModTime() == statBuf
.st_mtime
) && (image
.fileINode() == statBuf
.st_ino
) );
914 // macOS case: dylib missing
920 if ( overridableDylib
) {
921 // iOS case: internal install with dylib root
925 // iOS case: customer install, ignore dylib on disk
930 // iOS case: cached dylib not on disk as expected
937 if ( image
.validateUsingModTimeAndInode() ) {
938 // macOS case: verify dylib file info matches what it was when cache was built
939 return ( (image
.fileModTime() == statBuf
.st_mtime
) && (image
.fileINode() == statBuf
.st_ino
) );
942 // FIXME: need to verify file cdhash
947 // dylib not on disk as expected
953 ImageProxy
* ImageProxyGroup::findAbsoluteImage(Diagnostics
& diag
, const std::string
& runtimeLoadPath
, bool canBeMissing
, bool makeErrorMessage
, bool pathIsAlreadyReal
)
955 auto pos
= _pathToProxy
.find(runtimeLoadPath
);
956 if ( pos
!= _pathToProxy
.end() )
959 // see if this ImageProxyGroup is a proxy for an ImageGroup from the dyld shared cache
960 if ( _basedOn
!= nullptr ) {
962 launch_cache::ImageGroup
imageGroup(_basedOn
);
963 if ( imageGroup
.findImageByPath(runtimeLoadPath
.c_str(), foundIndex
) ) {
964 launch_cache::Image image
= imageGroup
.image(foundIndex
);
965 if ( builtImageStillValid(image
) ) {
966 ImageProxy
* proxy
= nullptr;
967 if ( _groupNum
== 0 ) {
968 const mach_header
* mh
= (mach_header
*)((uint8_t*)(_dyldCache
.cacheHeader()) + image
.cacheOffset());
969 proxy
= new ImageProxy(mh
, image
.binaryData(), foundIndex
, _dyldCache
.cacheIsMappedRaw());
972 DyldSharedCache::MappedMachO
* mapping
= addMappingIfValidMachO(diag
, runtimeLoadPath
);
973 if ( mapping
!= nullptr ) {
974 proxy
= new ImageProxy(*mapping
, _groupNum
, foundIndex
, false);
977 if ( proxy
!= nullptr ) {
978 _pathToProxy
[runtimeLoadPath
] = proxy
;
979 _images
.push_back(proxy
);
980 if ( runtimeLoadPath
!= image
.path() ) {
981 // lookup path is an alias, add real path too
982 _pathToProxy
[image
.path()] = proxy
;
990 if ( _nextSearchGroup
!= nullptr ) {
991 ImageProxy
* result
= _nextSearchGroup
->findAbsoluteImage(diag
, runtimeLoadPath
, true, false);
992 if ( result
!= nullptr )
996 // see if this is a symlink to a dylib
997 if ( !pathIsAlreadyReal
) {
998 for (const std::string
& prefix
: _buildTimePrefixes
) {
999 std::string fullPath
= prefix
+ runtimeLoadPath
;
1000 if ( endsWith(prefix
, "/") )
1001 fullPath
= prefix
.substr(0, prefix
.size()-1) + runtimeLoadPath
;
1002 if ( fileExists(fullPath
) ) {
1003 std::string resolvedPath
= realFilePath(fullPath
);
1004 if ( !resolvedPath
.empty() && (resolvedPath
!= fullPath
) ) {
1005 std::string resolvedRuntimePath
= resolvedPath
.substr(prefix
.size());
1006 ImageProxy
* proxy
= findAbsoluteImage(diag
, resolvedRuntimePath
, true, false, true);
1007 if ( proxy
!= nullptr )
1014 if ( (_groupNum
>= 2) && (_basedOn
== nullptr) ) {
1015 if ( (runtimeLoadPath
[0] != '/') && (runtimeLoadPath
[0] != '@') ) {
1016 for (ImageProxy
* aProxy
: _images
) {
1017 if ( endsWith(aProxy
->runtimePath(), runtimeLoadPath
) ) {
1018 aProxy
->setCwdMustBeThisDir();
1024 DyldSharedCache::MappedMachO
* mapping
= addMappingIfValidMachO(diag
, runtimeLoadPath
);
1025 if ( mapping
!= nullptr ) {
1026 ImageProxy
* proxy
= new ImageProxy(*mapping
, _groupNum
, (uint32_t)_images
.size(), false);
1027 _pathToProxy
[runtimeLoadPath
] = proxy
;
1028 _images
.push_back(proxy
);
1033 if ( !canBeMissing
&& makeErrorMessage
) {
1034 if ( diag
.warnings().empty() ) {
1035 if ( diag
.hasError() ) {
1036 std::string orgMsg
= diag
.errorMessage();
1037 diag
.error("'%s' %s", runtimeLoadPath
.c_str(), orgMsg
.c_str());
1040 diag
.error("could not find '%s'", runtimeLoadPath
.c_str());
1044 std::string allTries
;
1045 for (const std::string
& warn
: diag
.warnings()) {
1046 if ( allTries
.empty() )
1049 allTries
= allTries
+ ", " + warn
;
1051 diag
.clearWarnings();
1052 diag
.error("could not use '%s'. Did try: %s", runtimeLoadPath
.c_str(), allTries
.c_str());
1056 // record locations not found so it can be verified they are still missing at runtime
1057 _mustBeMissingFiles
.insert(runtimeLoadPath
);
1063 DyldSharedCache::MappedMachO
* ImageProxyGroup::addMappingIfValidMachO(Diagnostics
& diag
, const std::string
& runtimePath
, bool ignoreMainExecutables
)
1065 bool fileFound
= false;
1066 for (const std::string
& prefix
: _buildTimePrefixes
) {
1067 std::string fullPath
= prefix
+ runtimePath
;
1068 struct stat statBuf
;
1069 if ( stat(fullPath
.c_str(), &statBuf
) != 0 )
1072 // map whole file and determine if it is mach-o or a fat file
1073 int fd
= ::open(fullPath
.c_str(), O_RDONLY
);
1075 diag
.warning("file not open()able '%s' errno=%d", fullPath
.c_str(), errno
);
1078 size_t len
= (size_t)statBuf
.st_size
;
1080 const void* p
= ::mmap(NULL
, len
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
1081 if ( p
!= MAP_FAILED
) {
1085 Diagnostics fatDiag
;
1086 if ( FatUtil::isFatFileWithSlice(fatDiag
, p
, len
, _archName
, sliceOffset
, sliceLen
, missingSlice
) ) {
1088 ::munmap((void*)p
, len
);
1090 p
= ::mmap(NULL
, sliceLen
, PROT_READ
, MAP_PRIVATE
, fd
, sliceOffset
);
1091 if ( p
!= MAP_FAILED
) {
1092 offset
= sliceOffset
;
1096 else if ( fatDiag
.hasError() ) {
1097 diag
.warning("%s", fatDiag
.errorMessage().c_str());
1099 if ( (p
!= MAP_FAILED
) && !missingSlice
&& MachOParser::isValidMachO(diag
, _archName
, _platform
, p
, len
, fullPath
, ignoreMainExecutables
) ) {
1100 bool issetuid
= (statBuf
.st_mode
& (S_ISUID
|S_ISGID
));
1101 bool sip
= false; // FIXME
1102 _ownedMappings
.emplace_back(runtimePath
, (mach_header
*)p
, len
, issetuid
, sip
, offset
, statBuf
.st_mtime
, statBuf
.st_ino
);
1104 return &_ownedMappings
.back();
1106 else if (p
!= MAP_FAILED
) {
1107 ::munmap((void*)p
, len
);
1113 diag
.warning("file not found '%s'", runtimePath
.c_str());
1118 static bool dontExamineDir(const std::string
& dirPath
)
1120 return endsWith(dirPath
, ".app") || endsWith(dirPath
, ".xctoolchain") || endsWith(dirPath
, ".sdk") || endsWith(dirPath
, ".platform");
1123 void ImageProxyGroup::addExtraMachOsInBundle(const std::string
& appDir
)
1125 iterateDirectoryTree("", appDir
, ^(const std::string
& dirPath
) { return dontExamineDir(dirPath
); }, ^(const std::string
& path
, const struct stat
& statBuf
) {
1126 // ignore files that don't have 'x' bit set (all runnable mach-o files do)
1127 const bool hasXBit
= ((statBuf
.st_mode
& S_IXOTH
) == S_IXOTH
);
1131 // ignore files too small
1132 if ( statBuf
.st_size
< 0x1000 )
1135 // if the file is mach-o, add to list
1136 if ( _pathToProxy
.find(path
) == _pathToProxy
.end() ) {
1137 Diagnostics machoDiag
;
1138 DyldSharedCache::MappedMachO
* mapping
= addMappingIfValidMachO(machoDiag
, path
, true);
1139 if ( mapping
!= nullptr ) {
1140 ImageProxy
* proxy
= new ImageProxy(*mapping
, _groupNum
, (uint32_t)_images
.size(), false);
1141 if ( proxy
!= nullptr ) {
1142 _pathToProxy
[path
] = proxy
;
1143 _images
.push_back(proxy
);
1150 // used when building dyld shared cache
1151 ImageProxyGroup
* ImageProxyGroup::makeDyldCacheDylibsGroup(Diagnostics
& diag
, const DyldCacheParser
& dyldCache
,
1152 const std::vector
<DyldSharedCache::MappedMachO
>& cachedDylibs
,
1153 const std::vector
<std::string
>& buildTimePrefixes
,
1154 const PatchTable
& patchTable
, bool stubEliminated
, bool dylibsExpectedOnDisk
)
1156 std::vector
<std::string
> emptyEnvVars
; // Note: this method only used when constructing dyld cache where envs are not used
1157 std::vector
<const BinaryImageGroupData
*> noExistingGroups
;
1158 ImageProxyGroup
* groupProxy
= new ImageProxyGroup(0, dyldCache
, nullptr, nullptr, "", noExistingGroups
, buildTimePrefixes
, emptyEnvVars
, stubEliminated
, dylibsExpectedOnDisk
);
1159 groupProxy
->_patchTable
= &patchTable
;
1161 // add every dylib in shared cache to _images
1162 uint32_t indexInGroup
= 0;
1163 for (const DyldSharedCache::MappedMachO
& mapping
: cachedDylibs
) {
1164 ImageProxy
* proxy
= new ImageProxy(mapping
, 0, indexInGroup
++, true);
1165 groupProxy
->_images
.push_back(proxy
);
1166 groupProxy
->_pathToProxy
[mapping
.runtimePath
] = proxy
;
1169 // verify libdyld is compatible
1170 ImageRef libdyldEntryImageRef
= ImageRef::makeEmptyImageRef();
1171 uint32_t libdyldEntryOffset
;
1172 groupProxy
->findLibdyldEntry(diag
, libdyldEntryImageRef
, libdyldEntryOffset
);
1173 if ( diag
.hasError() ) {
1178 // wire up dependents
1179 bool hadError
= false;
1180 for (size_t i
=0; i
< groupProxy
->_images
.size(); ++i
) {
1181 // note: addDependentsShallow() can append to _images, so can't use regular iterator
1182 ImageProxy
* proxy
= groupProxy
->_images
[i
];
1183 proxy
->addDependentsShallow(*groupProxy
);
1184 if ( proxy
->diagnostics().hasError() ) {
1186 diag
.copy(proxy
->diagnostics());
1200 // used when building dyld shared cache
1201 ImageProxyGroup
* ImageProxyGroup::makeOtherOsGroup(Diagnostics
& diag
, const DyldCacheParser
& dyldCache
, ImageProxyGroup
* cachedDylibsGroup
,
1202 const std::vector
<DyldSharedCache::MappedMachO
>& otherDylibsAndBundles
,
1203 bool inodesAreSameAsRuntime
, const std::vector
<std::string
>& buildTimePrefixes
)
1205 std::vector
<std::string
> emptyEnvVars
; // Note: this method only used when constructing dyld cache where envs are not used
1206 const BinaryImageGroupData
* cachedDylibsGroupData
= dyldCache
.cachedDylibsGroup();
1207 std::vector
<const BinaryImageGroupData
*> existingGroups
= { cachedDylibsGroupData
};
1208 ImageProxyGroup
dyldCacheDylibProxyGroup(0, dyldCache
, cachedDylibsGroupData
, nullptr, "", existingGroups
, buildTimePrefixes
, emptyEnvVars
);
1209 ImageProxyGroup
* groupProxy
= new ImageProxyGroup(1, dyldCache
, nullptr, cachedDylibsGroup
, "", existingGroups
, buildTimePrefixes
, emptyEnvVars
,
1210 false, true, inodesAreSameAsRuntime
);
1212 // add every dylib/bundle in "other: list to _images
1213 uint32_t indexInGroup
= 0;
1214 for (const DyldSharedCache::MappedMachO
& mapping
: otherDylibsAndBundles
) {
1215 ImageProxy
* proxy
= new ImageProxy(mapping
, 1, indexInGroup
++, false);
1216 groupProxy
->_images
.push_back(proxy
);
1217 groupProxy
->_pathToProxy
[mapping
.runtimePath
] = proxy
;
1220 // wire up dependents
1221 for (size_t i
=0; i
< groupProxy
->_images
.size(); ++i
) {
1222 // note: addDependentsShallow() can append to _images, so can't use regular iterator
1223 ImageProxy
* proxy
= groupProxy
->_images
[i
];
1224 // note: other-dylibs can only depend on dylibs in this group or group 0, so no need for deep dependents
1225 proxy
->addDependentsShallow(*groupProxy
);
1226 if ( proxy
->diagnostics().hasError() ) {
1227 diag
.warning("adding dependents to %s: %s", proxy
->runtimePath().c_str(), proxy
->diagnostics().errorMessage().c_str());
1228 proxy
->markInvalid();
1231 // propagate invalidness
1232 __block
bool somethingInvalid
;
1234 somethingInvalid
= false;
1235 for (ImageProxy
* proxy
: groupProxy
->_images
) {
1236 proxy
->forEachDependent(^(ImageProxy
* dep
, LinkKind
) {
1237 if ( (dep
!= nullptr) && dep
->invalid() && !proxy
->invalid()) {
1238 proxy
->markInvalid();
1239 somethingInvalid
= true;
1243 } while (somethingInvalid
);
1248 // used by closured for dlopen of unknown dylibs
1249 const BinaryImageGroupData
* ImageProxyGroup::makeDlopenGroup(Diagnostics
& diag
, const DyldCacheParser
& dyldCache
, uint32_t groupNum
,
1250 const std::vector
<const BinaryImageGroupData
*>& existingGroups
,
1251 const std::string
& imagePath
, const std::vector
<std::string
>& envVars
)
1253 const std::vector
<std::string
>& noBuildTimePrefixes
= {""};
1254 ImageProxyGroup
dyldCacheDylibProxyGroup(0, dyldCache
, existingGroups
[0], nullptr, "", existingGroups
, noBuildTimePrefixes
, envVars
);
1255 ImageProxyGroup
dyldCacheOtherProxyGroup(1, dyldCache
, nullptr, &dyldCacheDylibProxyGroup
, "", existingGroups
, noBuildTimePrefixes
, envVars
);
1256 ImageProxyGroup
dlopenGroupProxy(groupNum
, dyldCache
, nullptr, &dyldCacheOtherProxyGroup
, imagePath
, existingGroups
, noBuildTimePrefixes
, envVars
, false, true, true);
1258 DyldSharedCache::MappedMachO
* topMapping
= dlopenGroupProxy
.addMappingIfValidMachO(diag
, imagePath
, true);
1259 if ( topMapping
== nullptr ) {
1260 if ( diag
.noError() ) {
1261 const std::set
<std::string
>& warnings
= diag
.warnings();
1262 if ( warnings
.empty() )
1263 diag
.error("no loadable mach-o in %s", imagePath
.c_str());
1265 diag
.error("%s", (*warnings
.begin()).c_str());
1270 ImageProxy
* topImageProxy
= new ImageProxy(*topMapping
, groupNum
, 0, false);
1271 if ( topImageProxy
== nullptr ) {
1272 diag
.error("can't find slice matching dyld cache in %s", imagePath
.c_str());
1275 dlopenGroupProxy
._images
.push_back(topImageProxy
);
1276 dlopenGroupProxy
._pathToProxy
[imagePath
] = topImageProxy
;
1278 // add all dylibs needed by dylib and are not in dyld cache
1279 topImageProxy
->addDependentsDeep(dlopenGroupProxy
, nullptr, false);
1280 if ( topImageProxy
->diagnostics().hasError() ) {
1281 diag
.copy(topImageProxy
->diagnostics());
1285 const BinaryImageGroupData
* result
= dlopenGroupProxy
.makeImageGroupBinary(diag
);
1291 // used when building dyld shared cache
1292 BinaryClosureData
* ImageProxyGroup::makeClosure(Diagnostics
& diag
, const DyldCacheParser
& dyldCache
, ImageProxyGroup
* cachedDylibsGroup
,
1293 ImageProxyGroup
* otherOsDylibs
, const DyldSharedCache::MappedMachO
& mainProgMapping
,
1294 bool inodesAreSameAsRuntime
, const std::vector
<std::string
>& buildTimePrefixes
)
1296 // _basedOn can not be set until ImageGroup is built
1297 if ( cachedDylibsGroup
->_basedOn
== nullptr ) {
1298 cachedDylibsGroup
->_basedOn
= dyldCache
.cachedDylibsGroup();
1300 const BinaryImageGroupData
* cachedDylibsGroupData
= dyldCache
.cachedDylibsGroup();
1301 const BinaryImageGroupData
* otherDylibsGroupData
= dyldCache
.otherDylibsGroup();
1302 std::vector
<const BinaryImageGroupData
*> existingGroups
= { cachedDylibsGroupData
, otherDylibsGroupData
};
1303 std::vector
<std::string
> emptyEnvVars
; // Note: this method only used when constructing dyld cache where envs are not used
1304 ImageProxyGroup
mainClosureGroupProxy(2, dyldCache
, nullptr, otherOsDylibs
, mainProgMapping
.runtimePath
, existingGroups
, buildTimePrefixes
,
1305 emptyEnvVars
, false, true, inodesAreSameAsRuntime
);
1307 ImageProxy
* mainProxy
= new ImageProxy(mainProgMapping
, 2, 0, false);
1308 if ( mainProxy
== nullptr ) {
1309 diag
.error("can't find slice matching dyld cache in %s", mainProgMapping
.runtimePath
.c_str());
1312 mainClosureGroupProxy
._images
.push_back(mainProxy
);
1313 mainClosureGroupProxy
._pathToProxy
[mainProgMapping
.runtimePath
] = mainProxy
;
1315 return mainClosureGroupProxy
.makeClosureBinary(diag
, mainProxy
, false);
1319 bool ImageProxyGroup::addInsertedDylibs(Diagnostics
& diag
)
1321 __block
bool success
= true;
1322 _pathOverrides
.forEachInsertedDylib(^(const char* dylibPath
) {
1323 ImageProxy
* insertProxy
= findAbsoluteImage(diag
, dylibPath
, false, true);
1324 if ( insertProxy
== nullptr )
1330 static DyldCacheParser
findDyldCache(Diagnostics
& diag
, const ClosureBuffer::CacheIdent
& cacheIdent
, task_t requestor
, bool* dealloc
)
1333 #if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
1334 size_t currentCacheSize
;
1335 const DyldSharedCache
* currentCache
= (const DyldSharedCache
*)_dyld_get_shared_cache_range(¤tCacheSize
);
1336 if ( currentCache
!= nullptr ) {
1337 uuid_t currentCacheUUID
;
1338 currentCache
->getUUID(currentCacheUUID
);
1339 if ( memcmp(currentCacheUUID
, cacheIdent
.cacheUUID
, 16) == 0 )
1340 return DyldCacheParser((const DyldSharedCache
*)currentCache
, false);
1343 if ( requestor
== mach_task_self() ) {
1344 // handle dyld_closure_util case where -cache_file option maps raw cache file into this process
1345 const DyldSharedCache
* altCache
= (DyldSharedCache
*)cacheIdent
.cacheAddress
;
1346 uuid_t altCacheUUID
;
1347 altCache
->getUUID(altCacheUUID
);
1348 if ( memcmp(altCacheUUID
, cacheIdent
.cacheUUID
, 16) == 0 )
1349 return DyldCacheParser(altCache
, true); // only one cache can be mapped into process, so this must be raw
1351 diag
.error("dyld cache uuid has changed");
1353 #if BUILDING_CLOSURED
1355 // handle case where requestor to closured is running with a different dyld cache that closured
1356 uint8_t cacheBuffer
[4096];
1357 mach_vm_size_t actualReadSize
= sizeof(cacheBuffer
);
1359 r
= mach_vm_read_overwrite(requestor
, cacheIdent
.cacheAddress
, sizeof(cacheBuffer
), (vm_address_t
)&cacheBuffer
, &actualReadSize
);
1360 if ( r
!= KERN_SUCCESS
) {
1361 diag
.error("unable to read cache header from requesting process (addr=0x%llX), kern err=%d", cacheIdent
.cacheAddress
, r
);
1362 return DyldCacheParser(nullptr, false);
1364 const dyld_cache_header
* header
= (dyld_cache_header
*)cacheBuffer
;
1365 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)(cacheBuffer
+ header
->mappingOffset
);
1366 vm_address_t bufferAddress
= 0;
1367 r
= vm_allocate(mach_task_self(), &bufferAddress
, (long)cacheIdent
.cacheMappedSize
, VM_FLAGS_ANYWHERE
);
1368 if ( r
!= KERN_SUCCESS
) {
1369 diag
.error("unable to allocate space to copy custom dyld cache (size=0x%llX), kern err=%d", cacheIdent
.cacheMappedSize
, r
);
1370 return DyldCacheParser(nullptr, false);
1372 uint64_t slide
= cacheIdent
.cacheAddress
- mappings
[0].address
;
1373 for (int i
=0; i
< 3; ++i
) {
1374 mach_vm_address_t mappedAddress
= bufferAddress
+ (mappings
[i
].address
- mappings
[0].address
);
1375 mach_vm_size_t mappedSize
= mappings
[i
].size
;
1376 vm_prot_t curProt
= VM_PROT_READ
;
1377 vm_prot_t maxProt
= VM_PROT_READ
;
1378 r
= mach_vm_remap(mach_task_self(), &mappedAddress
, mappedSize
, 0, VM_FLAGS_FIXED
| VM_FLAGS_OVERWRITE
,
1379 requestor
, mappings
[i
].address
+slide
, true, &curProt
, &maxProt
, VM_INHERIT_NONE
);
1380 if ( r
!= KERN_SUCCESS
) {
1381 diag
.error("unable to mach_vm_remap region %d custom dyld cache (request addr=0x%llX, size=0x%llX), kern err=%d, localBuffer=0x%lX, localMapTarget=0x%llX",
1382 i
, mappings
[i
].address
+slide
, mappedSize
, r
, (long)bufferAddress
, mappedAddress
);
1383 return DyldCacheParser(nullptr, false);
1385 if ( curProt
!= VM_PROT_READ
)
1386 vm_protect(mach_task_self(), (long)mappedAddress
, (long)mappedSize
, false, VM_PROT_READ
);
1389 return DyldCacheParser((DyldSharedCache
*)bufferAddress
, false); // assumes cache in other process is mapped as three regions
1392 return DyldCacheParser(nullptr, false);
1395 BinaryClosureData
* ImageProxyGroup::makeClosure(Diagnostics
& diag
, const ClosureBuffer
& buffer
, task_t requestor
, const std::vector
<std::string
>& buildTimePrefixes
)
1398 bool deallocCacheCopy
;
1399 DyldCacheParser dyldCache
= findDyldCache(diag
, buffer
.cacheIndent(), requestor
, &deallocCacheCopy
);
1400 if ( diag
.hasError() )
1402 const char* mainProg
= buffer
.targetPath();
1403 std::vector
<std::string
> envVars
;
1404 int envCount
= buffer
.envVarCount();
1405 const char* envVarCStrings
[envCount
];
1406 buffer
.copyImageGroups(envVarCStrings
);
1407 for (int i
=0; i
< envCount
; ++i
) {
1408 envVars
.push_back(envVarCStrings
[i
]);
1411 // make ImageProxyGroups: 0, 1, 2
1412 const BinaryImageGroupData
* cachedDylibsGroupData
= dyldCache
.cachedDylibsGroup();
1413 const BinaryImageGroupData
* otherDylibsGroupData
= dyldCache
.otherDylibsGroup();
1414 std::vector
<std::string
> realBuildTimePrefixes
;
1415 for (const std::string
& prefix
: buildTimePrefixes
) {
1416 char resolvedPath
[PATH_MAX
];
1417 if ( realpath(prefix
.c_str(), resolvedPath
) != nullptr )
1418 realBuildTimePrefixes
.push_back(resolvedPath
);
1420 realBuildTimePrefixes
.push_back(prefix
);
1422 std::vector
<const BinaryImageGroupData
*> existingGroups
= { cachedDylibsGroupData
, otherDylibsGroupData
};
1423 ImageProxyGroup
dyldCacheDylibProxyGroup(0, dyldCache
, cachedDylibsGroupData
, nullptr, "", existingGroups
, realBuildTimePrefixes
, envVars
);
1424 ImageProxyGroup
dyldCacheOtherProxyGroup(1, dyldCache
, otherDylibsGroupData
, &dyldCacheDylibProxyGroup
, "", existingGroups
, realBuildTimePrefixes
, envVars
);
1425 ImageProxyGroup
mainClosureGroupProxy( 2, dyldCache
, nullptr, &dyldCacheOtherProxyGroup
, mainProg
, existingGroups
, realBuildTimePrefixes
, envVars
, false, true, true);
1427 // add any DYLD_INSERTED_LIBRARIES then main program into closure
1428 BinaryClosureData
* result
= nullptr;
1429 if ( mainClosureGroupProxy
.addInsertedDylibs(diag
) ) {
1430 ImageProxy
* proxy
= mainClosureGroupProxy
.findAbsoluteImage(diag
, mainProg
, false, true);
1431 if ( proxy
!= nullptr ) {
1433 result
= mainClosureGroupProxy
.makeClosureBinary(diag
, proxy
, false);
1437 // if client has a different cache, unmap our copy
1438 if ( deallocCacheCopy
)
1439 vm_deallocate(mach_task_self(), (vm_address_t
)dyldCache
.cacheHeader(), (long)buffer
.cacheIndent().cacheMappedSize
);
1444 ClosureBuffer
closured_CreateImageGroup(const ClosureBuffer
& input
)
1447 const BinaryImageGroupData
* newGroup
= ImageProxyGroup::makeDlopenGroup(diag
, input
, mach_task_self(), {""});
1449 if ( diag
.noError() ) {
1450 // on success return the ImageGroup binary in the ClosureBuffer
1451 dyld3::ClosureBuffer
result(newGroup
);
1452 free((void*)newGroup
);
1456 // on failure return the error message in the ClosureBuffer
1457 dyld3::ClosureBuffer
err(diag
.errorMessage().c_str());
1462 const BinaryImageGroupData
* ImageProxyGroup::makeDlopenGroup(Diagnostics
& diag
, const ClosureBuffer
& buffer
, task_t requestor
, const std::vector
<std::string
>& buildTimePrefixes
)
1465 bool deallocCacheCopy
;
1466 DyldCacheParser dyldCache
= findDyldCache(diag
, buffer
.cacheIndent(), requestor
, &deallocCacheCopy
);
1467 if ( diag
.hasError() )
1470 const char* targetDylib
= buffer
.targetPath();
1471 std::vector
<std::string
> envVars
;
1472 int envCount
= buffer
.envVarCount();
1473 const char* envVarCStrings
[envCount
];
1474 buffer
.copyImageGroups(envVarCStrings
);
1475 for (int i
=0; i
< envCount
; ++i
) {
1476 envVars
.push_back(envVarCStrings
[i
]);
1478 uint32_t groupCount
= buffer
.imageGroupCount() + 2;
1479 const launch_cache::BinaryImageGroupData
* groupDataPtrs
[groupCount
];
1480 groupDataPtrs
[0] = dyldCache
.cachedDylibsGroup();
1481 groupDataPtrs
[1] = dyldCache
.otherDylibsGroup();
1482 buffer
.copyImageGroups(&groupDataPtrs
[2]);
1484 // build an ImageProxyGroup for each existing group, and one for new group being constructed
1485 std::vector
<const launch_cache::BinaryImageGroupData
*> existingGroups
;
1486 std::vector
<std::unique_ptr
<ImageProxyGroup
>> proxies
;
1487 ImageProxyGroup
* prevProxy
= nullptr;
1488 for (uint32_t i
=0; i
< groupCount
; ++i
) {
1489 const launch_cache::BinaryImageGroupData
* groupData
= groupDataPtrs
[i
];
1490 existingGroups
.push_back(groupData
);
1491 launch_cache::ImageGroup
group(groupData
);
1492 uint32_t groupNum
= group
.groupNum();
1493 assert(groupNum
== proxies
.size());
1494 proxies
.emplace_back(new ImageProxyGroup(groupNum
, dyldCache
, groupData
, prevProxy
, "", existingGroups
, buildTimePrefixes
, envVars
));
1495 prevProxy
= proxies
.back().get();
1497 ImageProxyGroup
dlopenGroupProxy(groupCount
, dyldCache
, nullptr, prevProxy
, targetDylib
, existingGroups
, buildTimePrefixes
, envVars
);
1499 // find and mmap() top level dylib
1500 DyldSharedCache::MappedMachO
* topMapping
= dlopenGroupProxy
.addMappingIfValidMachO(diag
, targetDylib
, true);
1501 if ( topMapping
== nullptr ) {
1502 std::string allWarnings
;
1503 for (const std::string
& warn
: diag
.warnings()) {
1504 if ( allWarnings
.empty() )
1507 allWarnings
= allWarnings
+ ", " + warn
;
1509 diag
.clearWarnings();
1510 diag
.error("%s", allWarnings
.c_str());
1511 if ( deallocCacheCopy
)
1512 vm_deallocate(mach_task_self(), (vm_address_t
)dyldCache
.cacheHeader(), (long)buffer
.cacheIndent().cacheMappedSize
);
1516 // make ImageProxy for top level dylib
1517 ImageProxy
* topImageProxy
= new ImageProxy(*topMapping
, groupCount
, 0, false);
1518 if ( topImageProxy
== nullptr ) {
1519 diag
.error("can't find slice matching dyld cache in %s", targetDylib
);
1520 if ( deallocCacheCopy
)
1521 vm_deallocate(mach_task_self(), (vm_address_t
)dyldCache
.cacheHeader(), (long)buffer
.cacheIndent().cacheMappedSize
);
1524 dlopenGroupProxy
._images
.push_back(topImageProxy
);
1525 dlopenGroupProxy
._pathToProxy
[targetDylib
] = topImageProxy
;
1527 // add all dylibs needed by dylib and are not in dyld cache
1528 topImageProxy
->addDependentsDeep(dlopenGroupProxy
, nullptr, false);
1529 if ( topImageProxy
->diagnostics().hasError() ) {
1530 diag
.copy(topImageProxy
->diagnostics());
1531 if ( deallocCacheCopy
)
1532 vm_deallocate(mach_task_self(), (vm_address_t
)dyldCache
.cacheHeader(), (long)buffer
.cacheIndent().cacheMappedSize
);
1536 // construct ImageGroup from ImageProxies
1537 const BinaryImageGroupData
* result
= dlopenGroupProxy
.makeImageGroupBinary(diag
);
1540 if ( deallocCacheCopy
)
1541 vm_deallocate(mach_task_self(), (vm_address_t
)dyldCache
.cacheHeader(), (long)buffer
.cacheIndent().cacheMappedSize
);
1549 // Used by closured and dyld_closure_util
1550 BinaryClosureData
* ImageProxyGroup::makeClosure(Diagnostics
& diag
, const DyldCacheParser
& dyldCache
,
1551 const std::string
& mainProg
, bool includeDylibsInDir
,
1552 const std::vector
<std::string
>& buildTimePrefixes
,
1553 const std::vector
<std::string
>& envVars
)
1555 const BinaryImageGroupData
* cachedDylibsGroupData
= dyldCache
.cachedDylibsGroup();
1556 const BinaryImageGroupData
* otherDylibsGroupData
= dyldCache
.otherDylibsGroup();
1557 std::vector
<std::string
> realBuildTimePrefixes
;
1558 for (const std::string
& prefix
: buildTimePrefixes
) {
1559 char resolvedPath
[PATH_MAX
];
1560 if ( realpath(prefix
.c_str(), resolvedPath
) != nullptr )
1561 realBuildTimePrefixes
.push_back(resolvedPath
);
1563 realBuildTimePrefixes
.push_back(prefix
);
1565 std::vector
<const BinaryImageGroupData
*> existingGroups
= { cachedDylibsGroupData
, otherDylibsGroupData
};
1566 ImageProxyGroup
dyldCacheDylibProxyGroup(0, dyldCache
, cachedDylibsGroupData
, nullptr, "", existingGroups
, realBuildTimePrefixes
, envVars
);
1567 ImageProxyGroup
dyldCacheOtherProxyGroup(1, dyldCache
, otherDylibsGroupData
, &dyldCacheDylibProxyGroup
, "", existingGroups
, realBuildTimePrefixes
, envVars
);
1568 ImageProxyGroup
mainClosureGroupProxy( 2, dyldCache
, nullptr, &dyldCacheOtherProxyGroup
, mainProg
, existingGroups
, realBuildTimePrefixes
, envVars
, false, true, true);
1570 // add any DYLD_INSERTED_LIBRARIES into closure
1571 if ( !mainClosureGroupProxy
.addInsertedDylibs(diag
) )
1574 ImageProxy
* proxy
= mainClosureGroupProxy
.findAbsoluteImage(diag
, mainProg
, false, true);
1575 if ( proxy
== nullptr )
1578 return mainClosureGroupProxy
.makeClosureBinary(diag
, proxy
, includeDylibsInDir
);
1581 const char* sSkipPrograms_macOS
[] = {
1582 "/Applications/iBooks.app/Contents/MacOS/iBooks",
1585 const char* sSkipPrograms_embeddedOSes
[] = {
1587 "/usr/local/sbin/launchd.debug",
1588 "/usr/local/sbin/launchd.development"
1591 BinaryClosureData
* ImageProxyGroup::makeClosureBinary(Diagnostics
& diag
, ImageProxy
* mainProgProxy
, bool includeDylibsInDir
)
1593 assert(mainProgProxy
!= nullptr);
1594 assert(_images
.size() >= 1);
1597 if ( _platform
== Platform::macOS
) {
1598 for (const char* skipProg
: sSkipPrograms_macOS
) {
1599 if ( mainProgProxy
->runtimePath() == skipProg
) {
1600 diag
.error("black listed program");
1605 for (const char* skipProg
: sSkipPrograms_embeddedOSes
) {
1606 if ( mainProgProxy
->runtimePath() == skipProg
) {
1607 diag
.error("black listed program");
1613 _mainExecutableIndex
= (uint32_t)_images
.size() - 1;
1614 // add all dylibs needed by main excutable and are not in dyld cache
1615 mainProgProxy
->addDependentsDeep(*this, nullptr, true);
1616 if ( mainProgProxy
->diagnostics().hasError() ) {
1617 diag
.copy(mainProgProxy
->diagnostics());
1621 // if main program is in .app bundle, look for other mach-o files to add to closure for use by dlopen
1622 bool isAppMainExecutable
= false;
1624 std::string leafName
= basePath(mainProgProxy
->runtimePath());
1625 size_t posAppX
= mainProgProxy
->runtimePath().rfind(std::string("/") + leafName
+ ".appex/");
1626 size_t posApp
= mainProgProxy
->runtimePath().rfind(std::string("/") + leafName
+ ".app/");
1627 if ( posAppX
!= std::string::npos
) {
1628 appDir
= mainProgProxy
->runtimePath().substr(0, posAppX
+leafName
.size()+7);
1629 isAppMainExecutable
= true;
1631 else if ( posApp
!= std::string::npos
) {
1632 appDir
= mainProgProxy
->runtimePath().substr(0, posApp
+leafName
.size()+5);
1633 isAppMainExecutable
= true;
1635 if ( isAppMainExecutable
) {
1636 addExtraMachOsInBundle(appDir
);
1637 for (size_t i
=0; i
< _images
.size(); ++i
) {
1638 // note: addDependentsDeep() can append to _images, so can't use regular iterator
1639 ImageProxy
* aProxy
= _images
[i
];
1640 ImageProxy::RPathChain base
= { aProxy
, nullptr, mainProgProxy
->rpaths() };
1641 aProxy
->addDependentsDeep(*this, &base
, false);
1642 if ( aProxy
->diagnostics().hasError() ) {
1643 aProxy
->markInvalid();
1644 diag
.warning("%s could not be added to closure because %s", aProxy
->runtimePath().c_str(), aProxy
->diagnostics().errorMessage().c_str());
1648 else if ( includeDylibsInDir
) {
1649 size_t pos
= mainProgProxy
->runtimePath().rfind('/');
1650 if ( pos
!= std::string::npos
) {
1651 std::string mainDir
= mainProgProxy
->runtimePath().substr(0, pos
);
1652 addExtraMachOsInBundle(mainDir
);
1653 for (size_t i
=0; i
< _images
.size(); ++i
) {
1654 // note: addDependentsDeep() can append to _images, so can't use regular iterator
1655 ImageProxy
* aProxy
= _images
[i
];
1656 aProxy
->addDependentsDeep(*this, nullptr, false);
1661 // add addition dependents of any inserted libraries
1662 if ( _mainExecutableIndex
!= 0 ) {
1663 for (uint32_t i
=0; i
< _mainExecutableIndex
; ++i
) {
1664 _images
[i
]->addDependentsDeep(*this, nullptr, true);
1665 if ( _images
[i
]->diagnostics().hasError() )
1670 // gather warnings from all statically dependent images
1671 for (ImageProxy
* proxy
: _images
) {
1672 if ( !proxy
->staticallyReferenced() && proxy
->diagnostics().hasError() )
1674 diag
.copy(proxy
->diagnostics());
1675 if ( diag
.hasError() ) {
1680 // get program entry
1681 MachOParser
mainExecutableParser(mainProgProxy
->mh(), _dyldCache
.cacheIsMappedRaw());
1683 uint32_t entryOffset
;
1684 mainExecutableParser
.getEntry(entryOffset
, usesCRT
);
1686 // build ImageGroupWriter
1687 launch_cache::ImageGroupWriter
groupWriter(_groupNum
, mainExecutableParser
.uses16KPages(), mainExecutableParser
.is64(), _dylibsExpectedOnDisk
, _inodesAreSameAsRuntime
);
1688 populateGroupWriter(diag
, groupWriter
);
1689 if ( diag
.hasError() )
1692 // pre-compute libSystem and libdyld into closure
1693 ImageRef libdyldEntryImageRef
= ImageRef::makeEmptyImageRef();
1694 uint32_t libdyldEntryOffset
;
1695 findLibdyldEntry(diag
, libdyldEntryImageRef
, libdyldEntryOffset
);
1696 if ( diag
.hasError() )
1698 ImageRef libSystemImageRef
= ImageRef::makeEmptyImageRef();
1700 findLibSystem(diag
, mainExecutableParser
.isSimulatorBinary(), libSystemImageRef
);
1701 if ( diag
.hasError() )
1704 // build info about missing files and env vars
1705 __block StringPool stringPool
;
1706 __block
std::vector
<uint32_t> envVarOffsets
;
1707 std::vector
<uint16_t> missingFileComponentOffsets
;
1708 stringPool
.add(" ");
1709 for (const std::string
& path
: _mustBeMissingFiles
) {
1711 size_t slashPos
= path
.find('/', start
);
1712 while (slashPos
!= std::string::npos
) {
1713 std::string component
= path
.substr(start
, slashPos
- start
);
1714 uint16_t offset
= stringPool
.add(component
);
1715 missingFileComponentOffsets
.push_back(offset
);
1716 start
= slashPos
+ 1;
1717 slashPos
= path
.find('/', start
);
1719 std::string lastComponent
= path
.substr(start
);
1720 uint16_t offset
= stringPool
.add(lastComponent
);
1721 missingFileComponentOffsets
.push_back(offset
);
1722 missingFileComponentOffsets
.push_back(0); // mark end of a path
1724 missingFileComponentOffsets
.push_back(0); // mark end of all paths
1725 if ( missingFileComponentOffsets
.size() & 1 )
1726 missingFileComponentOffsets
.push_back(0); // 4-byte align array
1727 __block
uint32_t envVarCount
= 0;
1728 _pathOverrides
.forEachEnvVar(^(const char* envVar
) {
1729 envVarOffsets
.push_back(stringPool
.add(envVar
));
1733 // 4-byte align string pool size
1736 // malloc a buffer and fill in ImageGroup part
1737 uint32_t groupSize
= groupWriter
.size();
1738 uint32_t missingFilesArraySize
= (uint32_t)((missingFileComponentOffsets
.size()*sizeof(uint16_t) + 3) & (-4));
1739 uint32_t envVarsSize
= (uint32_t)(envVarOffsets
.size()*sizeof(uint32_t));
1740 uint32_t stringPoolSize
= (uint32_t)stringPool
.size();
1741 size_t allocSize
= sizeof(launch_cache::binary_format::Closure
)
1743 + missingFilesArraySize
1746 BinaryClosureData
* clo
= (BinaryClosureData
*)malloc(allocSize
);
1747 groupWriter
.finalizeTo(diag
, _knownGroups
, &clo
->group
);
1748 launch_cache::ImageGroup
cloGroup(&clo
->group
);
1749 launch_cache::Image
mainImage(cloGroup
.imageBinary(_mainExecutableIndex
));
1751 uint32_t maxImageLoadCount
= groupWriter
.maxLoadCount(diag
, _knownGroups
, &clo
->group
);
1753 if ( mainImage
.isInvalid() ) {
1755 diag
.error("depends on invalid dylib");
1759 // fill in closure attributes
1760 clo
->magic
= launch_cache::binary_format::Closure::magicV1
;
1761 clo
->usesCRT
= usesCRT
;
1762 clo
->isRestricted
= mainProgProxy
->isSetUID() || mainExecutableParser
.isRestricted();
1763 clo
->usesLibraryValidation
= mainExecutableParser
.usesLibraryValidation();
1765 clo
->missingFileComponentsOffset
= offsetof(launch_cache::binary_format::Closure
, group
) + groupSize
;
1766 clo
->dyldEnvVarsOffset
= clo
->missingFileComponentsOffset
+ missingFilesArraySize
;
1767 clo
->dyldEnvVarsCount
= envVarCount
;
1768 clo
->stringPoolOffset
= clo
->dyldEnvVarsOffset
+ envVarsSize
;
1769 clo
->stringPoolSize
= stringPoolSize
;
1770 clo
->libSystemRef
= libSystemImageRef
;
1771 clo
->libDyldRef
= libdyldEntryImageRef
;
1772 clo
->libdyldVectorOffset
= libdyldEntryOffset
;
1773 clo
->mainExecutableIndexInGroup
= _mainExecutableIndex
;
1774 clo
->mainExecutableEntryOffset
= entryOffset
;
1775 clo
->initialImageCount
= maxImageLoadCount
;
1776 _dyldCache
.cacheHeader()->getUUID(clo
->dyldCacheUUID
);
1778 if ( !mainExecutableParser
.getCDHash(clo
->mainExecutableCdHash
) ) {
1779 // if no code signature, fill in 16-bytes with UUID then 4 bytes of zero
1780 bzero(clo
->mainExecutableCdHash
, 20);
1781 mainExecutableParser
.getUuid(clo
->mainExecutableCdHash
);
1783 if ( missingFilesArraySize
!= 0 )
1784 memcpy((uint8_t*)clo
+ clo
->missingFileComponentsOffset
, &missingFileComponentOffsets
[0], missingFileComponentOffsets
.size()*sizeof(uint16_t));
1785 if ( envVarsSize
!= 0 )
1786 memcpy((uint8_t*)clo
+ clo
->dyldEnvVarsOffset
, &envVarOffsets
[0], envVarsSize
);
1787 if ( stringPool
.size() != 0 )
1788 memcpy((uint8_t*)clo
+ clo
->stringPoolOffset
, stringPool
.buffer(), stringPool
.size());
1793 const BinaryImageGroupData
* ImageProxyGroup::makeImageGroupBinary(Diagnostics
& diag
, const char* const neverEliminateStubs
[])
1795 const bool continueIfErrors
= (_groupNum
== 1);
1796 bool uses16KPages
= true;
1798 if ( !_images
.empty() ) {
1799 MachOParser
firstParser(_images
.front()->mh(), _dyldCache
.cacheIsMappedRaw());
1800 uses16KPages
= firstParser
.uses16KPages();
1801 is64
= firstParser
.is64();
1803 launch_cache::ImageGroupWriter
groupWriter(_groupNum
, uses16KPages
, is64
, _dylibsExpectedOnDisk
, _inodesAreSameAsRuntime
);
1804 populateGroupWriter(diag
, groupWriter
, neverEliminateStubs
);
1805 if ( diag
.hasError() )
1808 // malloc a buffer and fill in ImageGroup part
1809 BinaryImageGroupData
* groupData
= (BinaryImageGroupData
*)malloc(groupWriter
.size());
1810 groupWriter
.finalizeTo(diag
, _knownGroups
, groupData
);
1812 if ( !continueIfErrors
&& groupWriter
.isInvalid(0) ) {
1813 free((void*)groupData
);
1814 diag
.error("depends on invalid dylib");
1822 void ImageProxyGroup::findLibdyldEntry(Diagnostics
& diag
, ImageRef
& ref
, uint32_t& vmOffsetInLibDyld
)
1824 Diagnostics libDyldDiag
;
1825 ImageProxy
* libDyldProxy
= findImage(libDyldDiag
, "/usr/lib/system/libdyld.dylib", false, nullptr);
1826 if ( libDyldProxy
== nullptr ) {
1827 diag
.error("can't find libdyld.dylib");
1830 ref
= ImageRef(0, libDyldProxy
->groupNum(), libDyldProxy
->indexInGroup());
1832 // find offset of "dyld3::entryVectorForDyld" in libdyld.dylib
1833 Diagnostics entryDiag
;
1834 MachOParser::FoundSymbol dyldEntryInfo
;
1835 MachOParser
libDyldParser(libDyldProxy
->mh(), _dyldCache
.cacheIsMappedRaw());
1836 if ( !libDyldParser
.findExportedSymbol(entryDiag
, "__ZN5dyld318entryVectorForDyldE", nullptr, dyldEntryInfo
, nullptr) ) {
1837 diag
.error("can't find dyld entry point into libdyld.dylib");
1840 vmOffsetInLibDyld
= (uint32_t)dyldEntryInfo
.value
;
1841 const LibDyldEntryVector
* entry
= (LibDyldEntryVector
*)(libDyldParser
.content(vmOffsetInLibDyld
));
1842 if ( entry
== nullptr ) {
1843 diag
.error("dyld entry point at offset 0x%0X not found in libdyld.dylib", vmOffsetInLibDyld
);
1846 if ( entry
->vectorVersion
!= LibDyldEntryVector::kCurrentVectorVersion
)
1847 diag
.error("libdyld.dylib vector version is incompatible with this dyld cache builder");
1848 else if ( entry
->binaryFormatVersion
!= launch_cache::binary_format::kFormatVersion
)
1849 diag
.error("libdyld.dylib closures binary format version is incompatible with this dyld cache builder");
1852 void ImageProxyGroup::findLibSystem(Diagnostics
& diag
, bool forSimulator
, ImageRef
& ref
)
1854 Diagnostics libSysDiag
;
1855 ImageProxy
* libSystemProxy
= findImage(libSysDiag
, forSimulator
? "/usr/lib/libSystem.dylib" : "/usr/lib/libSystem.B.dylib" , false, nullptr);
1856 if ( libSystemProxy
== nullptr ) {
1857 diag
.error("can't find libSystem.dylib");
1860 ref
= ImageRef(0, libSystemProxy
->groupNum(), libSystemProxy
->indexInGroup());
1864 std::vector
<ImageProxy
*> ImageProxyGroup::flatLookupOrder()
1866 std::vector
<ImageProxy
*> results
;
1867 // start with main executable and any inserted dylibs
1868 for (uint32_t i
=0; i
<= _mainExecutableIndex
; ++i
)
1869 results
.push_back(_images
[i
]);
1871 // recursive add dependents of main executable
1872 _images
[_mainExecutableIndex
]->addToFlatLookup(results
);
1874 // recursive add dependents of any inserted dylibs
1875 for (uint32_t i
=0; i
< _mainExecutableIndex
; ++i
)
1876 _images
[i
]->addToFlatLookup(results
);
1881 void ImageProxyGroup::populateGroupWriter(Diagnostics
& diag
, launch_cache::ImageGroupWriter
& groupWriter
, const char* const neverEliminateStubs
[])
1883 const bool buildingDylibsInCache
= (_groupNum
== 0);
1884 const bool continueIfErrors
= (_groupNum
== 1);
1886 std::unordered_set
<std::string
> neverStubEliminate
;
1887 if ( neverEliminateStubs
!= nullptr ) {
1888 for (const char* const* nes
=neverEliminateStubs
; *nes
!= nullptr; ++nes
)
1889 neverStubEliminate
.insert(*nes
);
1892 // pass 1: add all images
1893 const uint64_t cacheUnslideBaseAddress
= _dyldCache
.cacheHeader()->unslidLoadAddress();
1894 const uint32_t imageCount
= (uint32_t)_images
.size();
1895 groupWriter
.setImageCount(imageCount
);
1896 for (uint32_t i
=0; i
< imageCount
; ++i
) {
1897 MachOParser
imageParser(_images
[i
]->mh(), _dyldCache
.cacheIsMappedRaw());
1898 assert((imageParser
.inDyldCache() == buildingDylibsInCache
) && "all images must be same type");
1899 // add info for each image
1900 groupWriter
.setImagePath(i
, _images
[i
]->runtimePath().c_str());
1901 groupWriter
.setImageIsBundle(i
, (imageParser
.fileType() == MH_BUNDLE
));
1902 bool hasObjC
= imageParser
.hasObjC();
1903 groupWriter
.setImageHasObjC(i
, hasObjC
);
1904 bool isEncrypted
= imageParser
.isEncrypted();
1905 groupWriter
.setImageIsEncrypted(i
, isEncrypted
);
1906 bool mayHavePlusLoad
= false;
1908 mayHavePlusLoad
= isEncrypted
|| imageParser
.hasPlusLoadMethod(diag
);
1909 groupWriter
.setImageMayHavePlusLoads(i
, mayHavePlusLoad
);
1911 groupWriter
.setImageHasWeakDefs(i
, imageParser
.hasWeakDefs());
1912 groupWriter
.setImageMustBeThisDir(i
, _images
[i
]->cwdMustBeThisDir());
1913 groupWriter
.setImageIsPlatformBinary(i
, _images
[i
]->isPlatformBinary());
1914 groupWriter
.setImageOverridableDylib(i
, !_stubEliminated
|| (neverStubEliminate
.count(_images
[i
]->runtimePath()) != 0));
1916 if ( imageParser
.getUuid(uuid
) )
1917 groupWriter
.setImageUUID(i
, uuid
);
1918 if ( _inodesAreSameAsRuntime
) {
1919 groupWriter
.setImageFileMtimeAndInode(i
, _images
[i
]->fileModTime(), _images
[i
]->fileInode());
1923 if ( !imageParser
.getCDHash(cdHash
) )
1925 // if image is not code signed, cdHash filled with all zeros
1926 groupWriter
.setImageCdHash(i
, cdHash
);
1928 if ( !buildingDylibsInCache
) {
1929 groupWriter
.setImageSliceOffset(i
, _images
[i
]->sliceFileOffset());
1930 uint32_t fairPlayTextOffset
;
1931 uint32_t fairPlaySize
;
1932 if ( imageParser
.isFairPlayEncrypted(fairPlayTextOffset
, fairPlaySize
) )
1933 groupWriter
.setImageFairPlayRange(i
, fairPlayTextOffset
, fairPlaySize
);
1934 uint32_t codeSigOffset
;
1935 uint32_t codeSigSize
;
1936 if ( imageParser
.hasCodeSignature(codeSigOffset
, codeSigSize
) )
1937 groupWriter
.setImageCodeSignatureLocation(i
, codeSigOffset
, codeSigSize
);
1939 groupWriter
.setImageDependentsCount(i
, imageParser
.dependentDylibCount());
1940 // add segments to image
1941 groupWriter
.setImageSegments(i
, imageParser
, cacheUnslideBaseAddress
);
1942 // add initializers to image
1943 __block
std::vector
<uint32_t> initOffsets
;
1944 imageParser
.forEachInitializer(diag
, ^(uint32_t offset
) {
1945 initOffsets
.push_back(offset
);
1947 groupWriter
.setImageInitializerOffsets(i
, initOffsets
);
1948 if ( diag
.hasError() && !continueIfErrors
) {
1951 // add DOFs to image
1952 __block
std::vector
<uint32_t> dofOffsets
;
1953 imageParser
.forEachDOFSection(diag
, ^(uint32_t offset
) {
1954 dofOffsets
.push_back(offset
);
1956 groupWriter
.setImageDOFOffsets(i
, dofOffsets
);
1957 if ( diag
.hasError() && !continueIfErrors
) {
1960 bool neverUnload
= false;
1961 if ( buildingDylibsInCache
)
1963 if ( _images
[i
]->staticallyReferenced() )
1965 if ( imageParser
.hasObjC() && (imageParser
.fileType() == MH_DYLIB
) )
1967 if ( imageParser
.hasThreadLocalVariables() )
1969 if ( !dofOffsets
.empty() )
1971 groupWriter
.setImageNeverUnload(i
, neverUnload
);
1972 if ( _images
[i
]->invalid() )
1973 groupWriter
.setImageInvalid(i
);
1974 // record if this is an override of an OS dylib
1975 ImageRef stdRef
= _images
[i
]->overrideOf();
1976 if ( stdRef
!= ImageRef::weakImportMissing() ) {
1977 ImageRef
thisImageRef(0, _groupNum
, i
);
1978 groupWriter
.addImageIsOverride(stdRef
, thisImageRef
);
1981 // add alias if runtimepath does not match installName
1982 if ( imageParser
.fileType() == MH_DYLIB
) {
1983 const char* installName
= imageParser
.installName();
1984 if ( installName
[0] == '/' ) {
1985 if ( _images
[i
]->runtimePath() != installName
) {
1986 // add install name as an alias
1987 groupWriter
.addImageAliasPath(i
, installName
);
1990 // IOKit.framework on embedded uses not flat bundle, but clients dlopen() it as if it were flat
1991 if ( buildingDylibsInCache
&& (_platform
!= Platform::macOS
) && (_images
[i
]->runtimePath() == "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit") ) {
1992 groupWriter
.addImageAliasPath(i
, "/System/Library/Frameworks/IOKit.framework/IOKit");
1997 // pass 2: add all dependencies (now that we have indexes defined)
1998 for (uint32_t i
=0; (i
< imageCount
) && diag
.noError(); ++i
) {
1999 // add dependents to image
2000 __block
uint32_t depIndex
= 0;
2001 _images
[i
]->forEachDependent(^(ImageProxy
* dep
, LinkKind kind
) {
2002 if ( dep
== nullptr ) {
2003 if ( kind
== LinkKind::weak
)
2004 groupWriter
.setImageDependent(i
, depIndex
, launch_cache::binary_format::ImageRef::weakImportMissing());
2006 groupWriter
.setImageInvalid(i
);
2009 launch_cache::binary_format::ImageRef
ref((uint8_t)kind
, dep
->groupNum(), dep
->indexInGroup());
2010 groupWriter
.setImageDependent(i
, depIndex
, ref
);
2016 // pass 3: invalidate any images dependent on invalid images)
2017 if ( continueIfErrors
) {
2018 const launch_cache::binary_format::ImageRef missingRef
= launch_cache::binary_format::ImageRef::weakImportMissing();
2019 __block
bool somethingInvalidated
= false;
2021 somethingInvalidated
= false;
2022 for (uint32_t i
=0; i
< imageCount
; ++i
) {
2023 if ( groupWriter
.isInvalid(i
) )
2025 uint32_t depCount
= groupWriter
.imageDependentsCount(i
);
2026 for (uint32_t depIndex
=0; depIndex
< depCount
; ++depIndex
) {
2027 launch_cache::binary_format::ImageRef ref
= groupWriter
.imageDependent(i
, depIndex
);
2028 if ( ref
== missingRef
)
2030 if ( ref
.groupNum() == _groupNum
) {
2031 if ( groupWriter
.isInvalid(ref
.indexInGroup()) ) {
2032 // this image depends on something invalid, so mark it invalid
2033 //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
2034 groupWriter
.setImageInvalid(i
);
2035 somethingInvalidated
= true;
2041 } while (somethingInvalidated
);
2044 // pass 4: add fixups for each image, if needed
2045 bool someBadFixups
= false;
2046 if ( !buildingDylibsInCache
) {
2047 // compute fix ups for all images
2048 __block
std::vector
<ImageProxy::FixupInfo
> fixupInfos
;
2049 fixupInfos
.resize(imageCount
);
2050 for (uint32_t imageIndex
=0; imageIndex
< imageCount
; ++imageIndex
) {
2051 if ( groupWriter
.isInvalid(imageIndex
) )
2053 Diagnostics fixupDiag
;
2054 fixupInfos
[imageIndex
] = _images
[imageIndex
]->buildFixups(fixupDiag
, cacheUnslideBaseAddress
, groupWriter
);
2055 if ( fixupDiag
.hasError() ) {
2056 // disable image in group
2057 someBadFixups
= true;
2058 groupWriter
.setImageInvalid(imageIndex
);
2059 if ( continueIfErrors
) {
2060 diag
.warning("fixup problem in %s: %s", _images
[imageIndex
]->runtimePath().c_str(), fixupDiag
.errorMessage().c_str());
2064 diag
.error("fixup problem in %s: %s", _images
[imageIndex
]->runtimePath().c_str(), fixupDiag
.errorMessage().c_str());
2069 // if building closure, build patches to shared cache
2070 if ( _groupNum
== 2) {
2071 std::unordered_set
<ImageProxy
*> staticImagesWithWeakDefs
;
2072 ImageProxyGroup
* cacheGroup
= _nextSearchGroup
->_nextSearchGroup
;
2073 assert(cacheGroup
->_basedOn
!= nullptr);
2074 launch_cache::ImageGroup
dyldCacheGroup(cacheGroup
->_basedOn
);
2075 for (uint32_t imageIndex
=0; imageIndex
< imageCount
; ++imageIndex
) {
2076 if ( groupWriter
.isInvalid(imageIndex
) )
2078 ImageProxy
* thisProxy
= _images
[imageIndex
];
2079 // Only process interposing info on dylibs statically linked into closure
2080 if ( !thisProxy
->staticallyReferenced() )
2082 MachOParser
imageParser(thisProxy
->mh(), _dyldCache
.cacheIsMappedRaw());
2083 // if any images in closure interpose on something in dyld cache, record the cache patches needed
2084 imageParser
.forEachInterposingTuple(diag
, ^(uint32_t segIndex
, uint64_t replacementSegOffset
, uint64_t replaceeSegOffset
, uint64_t replacementContent
, bool& tupleStop
) {
2085 if ( _groupNum
!= 2 ) {
2086 groupWriter
.setImageInvalid(imageIndex
);
2089 TargetSymbolValue interposeReplacee
= TargetSymbolValue::makeInvalid();
2090 TargetSymbolValue interposeReplacement
= TargetSymbolValue::makeInvalid();
2091 for (const FixUp
& fixup
: fixupInfos
[imageIndex
].fixups
) {
2092 if ( fixup
.segIndex
!= segIndex
)
2094 if ( fixup
.segOffset
== replacementSegOffset
) {
2095 if ( fixup
.type
== launch_cache::ImageGroupWriter::FixupType::rebase
) {
2096 uint64_t offsetInImage
= replacementContent
- imageParser
.preferredLoadAddress();
2097 interposeReplacement
= TargetSymbolValue::makeGroupValue(2, imageIndex
, offsetInImage
, false);
2100 diag
.warning("bad interposing implementation in %s", _images
[imageIndex
]->runtimePath().c_str());
2104 else if ( fixup
.segOffset
== replaceeSegOffset
) {
2105 if ( fixup
.type
== launch_cache::ImageGroupWriter::FixupType::pointerBind
) {
2106 interposeReplacee
= fixup
.target
;
2109 diag
.warning("bad interposing target in %s", _images
[imageIndex
]->runtimePath().c_str());
2114 // scan through fixups of other images in closure looking to see what functions this entry references
2115 for (uint32_t otherIndex
=0; otherIndex
< imageCount
; ++otherIndex
) {
2116 if ( otherIndex
== imageIndex
)
2118 for (FixUp
& fixup
: fixupInfos
[otherIndex
].fixups
) {
2119 switch ( fixup
.type
) {
2120 case launch_cache::ImageGroupWriter::FixupType::pointerBind
:
2121 case launch_cache::ImageGroupWriter::FixupType::pointerLazyBind
:
2122 // alter fixup to use interposed function instead of requested
2123 if ( fixup
.target
== interposeReplacee
)
2124 fixup
.target
= interposeReplacement
;
2126 case launch_cache::ImageGroupWriter::FixupType::rebase
:
2127 case launch_cache::ImageGroupWriter::FixupType::rebaseText
:
2128 case launch_cache::ImageGroupWriter::FixupType::ignore
:
2129 case launch_cache::ImageGroupWriter::FixupType::bindText
:
2130 case launch_cache::ImageGroupWriter::FixupType::bindTextRel
:
2131 case launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel
:
2136 if ( interposeReplacee
.isInvalid() || interposeReplacement
.isInvalid() ) {
2137 diag
.error("malformed interposing section in %s", _images
[imageIndex
]->runtimePath().c_str());
2141 // record any overrides in shared cache that will need to be applied at launch time
2142 uint64_t offsetInCache
;
2143 if ( interposeReplacee
.isSharedCacheTarget(offsetInCache
) ) {
2144 uint32_t patchTableIndex
;
2145 if ( dyldCacheGroup
.hasPatchTableIndex((uint32_t)offsetInCache
, patchTableIndex
) ) {
2146 uint32_t replacementGroupNum
;
2147 uint32_t replacementIndexInGroup
;
2148 uint64_t replacementOffsetInImage
;
2149 assert(interposeReplacement
.isGroupImageTarget(replacementGroupNum
, replacementIndexInGroup
, replacementOffsetInImage
));
2150 assert(replacementGroupNum
== 2);
2151 assert(replacementIndexInGroup
< (1 << 8));
2152 assert(replacementOffsetInImage
< 0xFFFFFFFFULL
);
2153 DyldCacheOverride cacheOverride
;
2154 cacheOverride
.patchTableIndex
= patchTableIndex
;
2155 cacheOverride
.imageIndex
= replacementIndexInGroup
;
2156 cacheOverride
.imageOffset
= replacementOffsetInImage
;
2157 _cacheOverrides
.push_back(cacheOverride
);
2161 if ( diag
.hasError() && !continueIfErrors
) {
2164 // if any dylibs in the closure override a dyld cache dylib, then record the cache patches needed
2165 ImageRef overrideOf
= thisProxy
->overrideOf();
2166 if ( (overrideOf
!= ImageRef::makeEmptyImageRef()) && (overrideOf
.groupNum() == 0) ) {
2167 //fprintf(stderr, "need to patch %s into cache\n", thisProxy->runtimePath().c_str());
2168 const launch_cache::Image imageInCache
= dyldCacheGroup
.image(overrideOf
.indexInGroup());
2169 const mach_header
* imageInCacheMH
= (mach_header
*)((char*)(_dyldCache
.cacheHeader()) + imageInCache
.cacheOffset());
2170 MachOParser
inCacheParser(imageInCacheMH
, _dyldCache
.cacheIsMappedRaw());
2171 // walk all exported symbols in dylib in cache
2172 inCacheParser
.forEachExportedSymbol(diag
, ^(const char* symbolName
, uint64_t imageOffset
, bool isReExport
, bool &stop
) {
2175 uint32_t cacheOffsetOfSymbol
= (uint32_t)(imageInCache
.cacheOffset() + imageOffset
);
2176 //fprintf(stderr, " patch cache offset 0x%08X which is %s\n", cacheOffsetOfSymbol, symbolName);
2177 // for each exported symbol, see if it is in patch table (used by something else in cache)
2178 uint32_t patchTableIndex
;
2179 if ( dyldCacheGroup
.hasPatchTableIndex(cacheOffsetOfSymbol
, patchTableIndex
) ) {
2180 //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
2181 // lookup address of symbol in override dylib and add patch info
2182 MachOParser::FoundSymbol foundInfo
;
2183 if ( imageParser
.findExportedSymbol(diag
, symbolName
, nullptr, foundInfo
, nullptr) ) {
2184 DyldCacheOverride cacheOverride
;
2185 assert(patchTableIndex
< (1 << 24));
2186 assert(thisProxy
->indexInGroup() < (1 << 8));
2187 assert(foundInfo
.value
< (1ULL << 32));
2188 cacheOverride
.patchTableIndex
= patchTableIndex
;
2189 cacheOverride
.imageIndex
= thisProxy
->indexInGroup();
2190 cacheOverride
.imageOffset
= foundInfo
.value
;
2191 _cacheOverrides
.push_back(cacheOverride
);
2196 // save off all images in closure with weak defines
2197 if ( thisProxy
->mh()->flags
& (MH_WEAK_DEFINES
|MH_BINDS_TO_WEAK
) ) {
2198 staticImagesWithWeakDefs
.insert(thisProxy
);
2201 // if any dylibs in the closure override a weak symbol in a cached dylib, then record the cache patches needed
2202 if ( !staticImagesWithWeakDefs
.empty() ) {
2203 // build list of all weak def symbol names
2204 __block
std::unordered_map
<std::string
, DyldCacheOverride
> weakSymbols
;
2205 for (ImageProxy
* proxy
: staticImagesWithWeakDefs
) {
2206 MachOParser
weakDefParser(proxy
->mh(), _dyldCache
.cacheIsMappedRaw());
2207 weakDefParser
.forEachWeakDef(diag
, ^(bool strongDef
, uint32_t segIndex
, uint64_t segOffset
, uint64_t addend
, const char* symbolName
, bool& stop
) {
2208 weakSymbols
[symbolName
] = { 0, 0, 0 };
2211 // do a flat namespace walk of all images
2212 std::vector
<ImageProxy
*> flatSearchOrder
= flatLookupOrder();
2213 for (ImageProxy
* proxy
: flatSearchOrder
) {
2214 // only look at images that participate in weak coalescing
2215 if ( (proxy
->mh()->flags
& (MH_WEAK_DEFINES
|MH_BINDS_TO_WEAK
)) == 0 )
2217 // look only at images in closure
2218 if ( proxy
->groupNum() == 2 ) {
2219 MachOParser
weakDefParser(proxy
->mh(), _dyldCache
.cacheIsMappedRaw());
2220 // check if this closure image defines any of the not-yet found weak symbols
2221 for (auto& entry
: weakSymbols
) {
2222 if ( entry
.second
.imageOffset
!= 0 )
2224 Diagnostics weakDiag
;
2225 MachOParser::FoundSymbol foundInfo
;
2226 if ( weakDefParser
.findExportedSymbol(weakDiag
, entry
.first
.c_str(), nullptr, foundInfo
, nullptr) ) {
2227 assert(proxy
->indexInGroup() < (1 << 8));
2228 assert(foundInfo
.value
< (1ULL << 32));
2229 entry
.second
.imageIndex
= proxy
->indexInGroup();
2230 entry
.second
.imageOffset
= foundInfo
.value
;
2235 for (ImageProxy
* proxy
: flatSearchOrder
) {
2236 // only look at images that participate in weak coalescing
2237 if ( (proxy
->mh()->flags
& (MH_WEAK_DEFINES
|MH_BINDS_TO_WEAK
)) == 0 )
2239 // look only at images in dyld cache
2240 if ( proxy
->groupNum() == 0 ) {
2241 const launch_cache::Image imageInCache
= dyldCacheGroup
.image(proxy
->indexInGroup());
2242 MachOParser
inCacheParser(proxy
->mh(), _dyldCache
.cacheIsMappedRaw());
2243 Diagnostics cacheDiag
;
2244 for (auto& entry
: weakSymbols
) {
2245 if ( entry
.second
.imageOffset
== 0 )
2247 Diagnostics weakDiag
;
2248 MachOParser::FoundSymbol foundInfo
;
2249 if ( inCacheParser
.findExportedSymbol(weakDiag
, entry
.first
.c_str(), nullptr, foundInfo
, nullptr) ) {
2250 uint32_t cacheOffsetOfSymbol
= (uint32_t)(imageInCache
.cacheOffset() + foundInfo
.value
);
2251 // see if this symbol is in patch table (used by something else in cache)
2252 uint32_t patchTableIndex
;
2253 if ( dyldCacheGroup
.hasPatchTableIndex(cacheOffsetOfSymbol
, patchTableIndex
) ) {
2254 //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
2255 DyldCacheOverride cacheOverride
;
2256 cacheOverride
.patchTableIndex
= patchTableIndex
;
2257 cacheOverride
.imageIndex
= entry
.second
.imageIndex
;
2258 cacheOverride
.imageOffset
= entry
.second
.imageOffset
;
2259 _cacheOverrides
.push_back(cacheOverride
);
2267 // record fixups for each image
2268 for (uint32_t imageIndex
=0; imageIndex
< imageCount
; ++imageIndex
) {
2269 groupWriter
.setImageFixups(diag
, imageIndex
, fixupInfos
[imageIndex
].fixups
, fixupInfos
[imageIndex
].hasTextRelocs
);
2273 // pass 5: invalidate any images dependent on invalid images)
2274 if ( someBadFixups
&& continueIfErrors
) {
2275 __block
bool somethingInvalidated
= false;
2277 somethingInvalidated
= false;
2278 for (uint32_t i
=0; i
< imageCount
; ++i
) {
2279 if ( groupWriter
.isInvalid(i
) )
2281 uint32_t depCount
= groupWriter
.imageDependentsCount(i
);
2282 for (uint32_t depIndex
=0; depIndex
< depCount
; ++depIndex
) {
2283 launch_cache::binary_format::ImageRef ref
= groupWriter
.imageDependent(i
, depIndex
);
2284 if ( ref
.groupNum() == _groupNum
) {
2285 if ( groupWriter
.isInvalid(ref
.indexInGroup()) ) {
2286 // this image depends on something invalid, so mark it invalid
2287 //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
2288 groupWriter
.setImageInvalid(i
);
2289 somethingInvalidated
= true;
2295 } while (somethingInvalidated
);
2298 // pass 6: compute initializer lists for each image
2299 const bool log
= false;
2300 for (uint32_t imageIndex
=0; imageIndex
< imageCount
; ++imageIndex
) {
2301 if ( groupWriter
.isInvalid(imageIndex
) )
2304 auto inits
= _images
[imageIndex
]->getInitBeforeList(*this);
2305 if ( log
&& buildingDylibsInCache
) {
2306 fprintf(stderr
, "%s\n init list: ", _images
[imageIndex
]->runtimePath().c_str());
2307 for (launch_cache::binary_format::ImageRef ref
: inits
) {
2308 if ( ref
.groupNum() == 0 ) {
2309 std::string dep
= _images
[ref
.indexInGroup()]->runtimePath();
2310 size_t off
= dep
.rfind('/');
2311 fprintf(stderr
, "%s, ", dep
.substr(off
+1).c_str());
2314 fprintf(stderr
, "\n");
2316 groupWriter
.setImageInitBefore(imageIndex
, inits
);
2319 // pass 7: compute DOFs
2320 for (uint32_t imageIndex
=0; imageIndex
< imageCount
; ++imageIndex
) {
2321 if ( groupWriter
.isInvalid(imageIndex
) )
2324 auto inits
= _images
[imageIndex
]->getInitBeforeList(*this);
2325 if ( log
&& buildingDylibsInCache
) {
2326 fprintf(stderr
, "%s\n DOFs: ", _images
[imageIndex
]->runtimePath().c_str());
2327 for (launch_cache::binary_format::ImageRef ref
: inits
) {
2328 if ( ref
.groupNum() == 0 ) {
2329 std::string dep
= _images
[ref
.indexInGroup()]->runtimePath();
2330 size_t off
= dep
.rfind('/');
2331 fprintf(stderr
, "%s, ", dep
.substr(off
+1).c_str());
2334 fprintf(stderr
, "\n");
2336 groupWriter
.setImageInitBefore(imageIndex
, inits
);
2339 // pass 8: add patch table entries iff this is dyld cache ImageGroup
2340 assert(buildingDylibsInCache
== (_patchTable
!= nullptr));
2341 if ( _patchTable
!= nullptr ) {
2342 for (uint32_t i
=0; i
< imageCount
; ++i
) {
2343 const auto pos
= _patchTable
->find(_images
[i
]->mh());
2344 if ( pos
!= _patchTable
->end() ) {
2345 for (const auto& entry
: pos
->second
) {
2346 uint32_t defFunctionOffset
= entry
.first
;
2347 groupWriter
.setImagePatchLocations(i
, defFunctionOffset
, entry
.second
);
2353 // if this is a main closure group with an interposing dylib, add cache overrides
2354 if ( !_cacheOverrides
.empty() ) {
2355 groupWriter
.setGroupCacheOverrides(_cacheOverrides
);
2358 // align string pool
2359 groupWriter
.alignStringPool();
2364 } // namespace dyld3