dyld-551.3.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / ImageProxy.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <string.h>
26 #include <stdint.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <sys/errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mman.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>
41 #include <os/log.h>
42
43 #include <string>
44 #include <vector>
45 #include <array>
46
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"
55
56 namespace dyld3 {
57
58 typedef launch_cache::TargetSymbolValue TargetSymbolValue;
59
60
61
62 /////////////////////////// ImageProxy ///////////////////////////
63
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)
69 {
70 }
71
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)
77 {
78 }
79
80
81 void ImageProxy::processRPaths(ImageProxyGroup& owningGroup)
82 {
83 // parse LC_RPATH
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());
89 return;
90 }
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;
97 }
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);
103 else
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));
115 }
116 }
117 }
118 }
119 }
120 else {
121 _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str());
122 }
123 }
124 else if ( thisRPath == "@executable_path" ) {
125 std::string mainPath = owningGroup.mainProgRuntimePath();
126 if ( mainPath.empty() && parser.isDynamicExecutable() ) {
127 mainPath = _runtimePath;
128 }
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);
133 }
134 else {
135 _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str());
136 }
137 }
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);
141 bool found = false;
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));
146 found = true;
147 break;
148 }
149 }
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));
160 found = true;
161 break;
162 }
163 }
164 }
165 }
166 if ( !found ) {
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());
171 }
172 }
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);
178 }
179 else if ( rpath[0] == '@' ) {
180 _diag.warning("LC_RPATH with unknown @ variable (%s) in %s", rpath, _runtimePath.c_str());
181 }
182 else {
183 if ( rpath[0] == '/' )
184 _diag.warning("LC_RPATH is absolute path (%s) in %s", rpath, _runtimePath.c_str());
185 _rpaths.push_back(rpath);
186 }
187 });
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());
192 //}
193 }
194
195 void ImageProxy::addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* prev, bool staticallyReferenced)
196 {
197 // mark binaries that are statically referenced and thus will never be unloaded
198 if ( staticallyReferenced )
199 _staticallyReferenced = true;
200
201 if ( _deepDependentsSet )
202 return;
203
204 // find all immediate dependents
205 addDependentsShallow(owningGroup, prev);
206 if ( _diag.hasError() ) {
207 _invalid = true;
208 return;
209 }
210
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() )
219 _invalid = true;
220 }
221
222 _deepDependentsSet = true;
223 }
224
225 void ImageProxy::addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* prev)
226 {
227 if ( _directDependentsSet )
228 return;
229
230 MachOParser thisParser(mh(), _dyldCacheIsRaw);
231 dyld3::Platform thisPlatform = thisParser.platform();
232
233 processRPaths(owningGroup);
234 __block RPathChain rchain = { this, prev, _rpaths };
235
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());
239 }
240 Diagnostics depDiag;
241 ImageProxy* dep = owningGroup.findImage(depDiag, loadPath, isWeak, &rchain);
242 if ( (dep == nullptr) || dep->invalid() ) {
243 if (isWeak) {
244 // weak link against a broken dylib, pretend dylib is not there
245 dep = nullptr;
246 } else {
247 if ( depDiag.warnings().empty() ) {
248 if ( thisParser.header()->filetype == MH_EXECUTE )
249 _diag.error("required dylib '%s' not found", loadPath);
250 else
251 _diag.error("required dylib '%s' not found, needed by '%s'", loadPath, runtimePath().c_str());
252 }
253 else {
254 std::string allTries;
255 for (const std::string& warn : depDiag.warnings()) {
256 if ( allTries.empty() )
257 allTries = warn;
258 else
259 allTries = allTries + ", " + warn;
260 }
261 _diag.error("required dylib '%s' not found, needed by '%s'. Did try: %s", loadPath, runtimePath().c_str(), allTries.c_str());
262 }
263 }
264 }
265 else {
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());
275 }
276 }
277 }
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());
285 }
286 else {
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());
291 }
292 }
293 }
294 }
295 if ( _diag.hasError() ) {
296 stop = true;
297 _invalid = true;
298 }
299 _dependents.push_back(dep);
300 if ( isWeak )
301 _dependentsKind.push_back(launch_cache::Image::LinkKind::weak);
302 else if ( isReExport )
303 _dependentsKind.push_back(launch_cache::Image::LinkKind::reExport);
304 else if ( isUpward )
305 _dependentsKind.push_back(launch_cache::Image::LinkKind::upward);
306 else
307 _dependentsKind.push_back(launch_cache::Image::LinkKind::regular);
308 });
309 _directDependentsSet = true;
310 }
311
312 bool ImageProxy::inLibSystem() const
313 {
314 return startsWith(runtimePath(), "/usr/lib/system/") || startsWith(runtimePath(), "/usr/lib/libSystem.");
315 }
316
317 void ImageProxy::forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const
318 {
319 for (int i=0; i < _dependents.size(); ++i) {
320 handler(_dependents[i], _dependentsKind[i]);
321 }
322 }
323
324
325 bool ImageProxy::findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const
326 {
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;
334 return true;
335 }
336 return false;
337 });
338 }
339
340 bool ImageProxy::InitOrderInfo::beforeHas(ImageRef ref)
341 {
342 ImageRef clearRef = ref;
343 clearRef.clearKind();
344 return ( std::find(initBefore.begin(), initBefore.end(), clearRef) != initBefore.end() );
345 }
346
347 bool ImageProxy::InitOrderInfo::upwardHas(ImageProxy* proxy)
348 {
349 return ( std::find(danglingUpward.begin(), danglingUpward.end(), proxy) != danglingUpward.end() );
350 }
351
352 void ImageProxy::InitOrderInfo::removeRedundantUpwards()
353 {
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());
359 }
360
361
362 //
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.
367 //
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.
372 //
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.
375 //
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.
380 //
381
382 void ImageProxy::recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup)
383 {
384 if ( _initBeforesComputed )
385 return;
386 _initBeforesComputed = true; // break cycles
387
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());
400 Diagnostics diag;
401 ImageProxy* p = groupP->findAbsoluteImage(diag, dyldCacheImage.path(), false, false);
402 if ( diag.noError() )
403 _initBeforesInfo.danglingUpward.push_back(p);
404 }
405 else {
406 _initBeforesInfo.initBefore.push_back(ref);
407 }
408 });
409 }
410 else {
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);
416 }
417 else {
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
422 }
423 else {
424 ImageRef ref(0, depProxy->_groupNum, depProxy->_indexInGroup);
425 if ( _initBeforesInfo.beforeHas(ref) ) {
426 // already in before list, do nothing
427 }
428 else {
429 // add to upward list
430 _initBeforesInfo.danglingUpward.push_back(depProxy);
431 }
432 }
433 }
434 else {
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);
441 }
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
447 }
448 else if ( _initBeforesInfo.upwardHas(upProxy) ) {
449 // already in current danglingUpward list, so ignore this upward
450 }
451 else {
452 // append to current danglingUpward list
453 _initBeforesInfo.danglingUpward.push_back(upProxy);
454 }
455 }
456 }
457 }
458 ++depIndex;
459 }
460 // eliminate any upward links added to befores list by some other dependent
461 _initBeforesInfo.removeRedundantUpwards();
462
463 // if this images has initializer(s) (or +load), add it to list
464 MachOParser parser(_mh, _dyldCacheIsRaw);
465 Diagnostics diag;
466 if ( parser.hasInitializer(diag) || parser.hasPlusLoadMethod(diag) ) {
467 launch_cache::binary_format::ImageRef ref(0, _groupNum, _indexInGroup);
468 _initBeforesInfo.initBefore.push_back(ref);
469 }
470
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());
476 }
477 }
478
479 void ImageProxy::convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup)
480 {
481 if ( _initBeforesInfo.danglingUpward.empty() ) {
482 _initBeforesArray = _initBeforesInfo.initBefore;
483 }
484 else {
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() )
491 continue;
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);
496 }
497 ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup);
498 if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), ref) == _initBeforesArray.end() )
499 _initBeforesArray.push_back(ref);
500 }
501 }
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);
505 //}
506 }
507
508 const std::vector<ImageRef>& ImageProxy::getInitBeforeList(ImageProxyGroup& owningGroup)
509 {
510 if ( !_initBeforesArraySet ) {
511 _initBeforesArraySet = true; // break cycles
512 recursiveBuildInitBeforeInfo(owningGroup);
513 convertInitBeforeInfoToArray(owningGroup);
514 }
515 return _initBeforesArray;
516 }
517
518 ImageProxy::FixupInfo ImageProxy::buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const
519 {
520 __block ImageProxy::FixupInfo info;
521 MachOParser image(_mh, _dyldCacheIsRaw);
522
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;
527 switch ( type ) {
528 case REBASE_TYPE_POINTER:
529 fixupType = launch_cache::ImageGroupWriter::FixupType::rebase;
530 break;
531 case REBASE_TYPE_TEXT_ABSOLUTE32:
532 fixupType = launch_cache::ImageGroupWriter::FixupType::rebaseText;
533 info.hasTextRelocs = true;
534 break;
535 case REBASE_TYPE_TEXT_PCREL32:
536 diag.error("pcrel text rebasing not supported");
537 stop = true;
538 rebaseError = true;
539 break;
540 default:
541 diag.error("unknown rebase type");
542 stop = true;
543 rebaseError = true;
544 break;
545 }
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);
548 });
549 if ( diag.hasError() )
550 return FixupInfo();
551
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;
556 switch ( type ) {
557 case BIND_TYPE_POINTER:
558 if ( lazy )
559 fixupType = launch_cache::ImageGroupWriter::FixupType::pointerLazyBind;
560 else
561 fixupType = launch_cache::ImageGroupWriter::FixupType::pointerBind;
562 break;
563 case BIND_TYPE_TEXT_ABSOLUTE32:
564 fixupType = launch_cache::ImageGroupWriter::FixupType::bindText;
565 info.hasTextRelocs = true;
566 break;
567 case BIND_TYPE_TEXT_PCREL32:
568 fixupType = launch_cache::ImageGroupWriter::FixupType::bindTextRel;
569 info.hasTextRelocs = true;
570 break;
571 case BIND_TYPE_IMPORT_JMP_REL32:
572 fixupType = launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel;
573 break;
574 default:
575 diag.error("malformed executable, unknown bind type (%d)", type);
576 stop = true;
577 return;
578 }
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)});
586 return;
587 }
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)});
593 return;
594 }
595 else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
596 depProxy = this;
597 }
598 else if ( (libOrdinal >= 1) && (libOrdinal <= _dependents.size()) ) {
599 isWeakDylib = (_dependentsKind[libOrdinal-1] == LinkKind::weak);
600 depProxy = _dependents[libOrdinal-1];
601 }
602 else {
603 diag.error("ordinal %d not supported", libOrdinal);
604 stop = true;
605 return;
606 }
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)});
617 }
618 else {
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)});
623 }
624 break;
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);
628 stop = true;
629 return;
630 }
631 info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(foundInfo.value+addend)});
632 break;
633 }
634 }
635 else {
636 if ( !weakImport ) {
637 diag.error("symbol '%s' not found, expected in '%s'", symbolName, depProxy->runtimePath().c_str());
638 stop = true;
639 }
640 // mark fixup needs to set fixup location to zero
641 info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)});
642 }
643 }
644 else {
645 if ( isWeakDylib ) {
646 // dylib not found and is weak, set pointers into it to zero
647 info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)});
648 }
649 else {
650 diag.error("dylib ordinal %d not found and not weak", libOrdinal);
651 stop = true;
652 }
653 }
654 });
655 if ( diag.hasError() )
656 return FixupInfo();
657
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) {
660 if ( strongDef )
661 return;
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);
669 altered = true;
670 }
671 }
672 if ( !altered ) {
673 if ( image.isSlideable() ) {
674 fprintf(stderr, "weak def for %s can't find underlying rebase/bind in %s\n", symbolName, runtimePath().c_str());
675 }
676 else {
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)} );
680 }
681 }
682
683 });
684
685 return info;
686 }
687
688
689 void ImageProxy::setOverrideOf(uint32_t groupNum, uint32_t indexInGroup)
690 {
691 _overrideOf = ImageRef(0, groupNum, indexInGroup);
692 }
693
694
695 static bool alreadyInList(const std::vector<ImageProxy*>& imageList, ImageProxy* image)
696 {
697 for (ImageProxy* proxy : imageList) {
698 if ( proxy == image )
699 return true;
700 }
701 return false;
702 }
703
704 void ImageProxy::addToFlatLookup(std::vector<ImageProxy*>& imageList)
705 {
706 // add all images shallow
707 bool addedSomething = false;
708 for (ImageProxy* dep : _dependents) {
709 if ( dep == nullptr )
710 continue;
711 if ( !alreadyInList(imageList, dep) ) {
712 imageList.push_back(dep);
713 addedSomething = true;
714 }
715 }
716 // recurse
717 if ( addedSomething ) {
718 for (ImageProxy* dep : _dependents) {
719 if ( dep == nullptr )
720 continue;
721 dep->addToFlatLookup(imageList);
722 }
723 }
724 }
725
726
727 /////////////////////////// ImageProxyGroup ///////////////////////////
728
729
730 class StringPool
731 {
732 public:
733 uint32_t add(const std::string& str);
734 size_t size() const { return _buffer.size(); }
735 const char* buffer() const { return &_buffer[0]; }
736 void align();
737 private:
738 std::vector<char> _buffer;
739 std::unordered_map<std::string, uint32_t> _existingEntries;
740 };
741
742 uint32_t StringPool::add(const std::string& str)
743 {
744 auto pos = _existingEntries.find(str);
745 if ( pos != _existingEntries.end() )
746 return pos->second;
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;
753 }
754
755 void StringPool::align()
756 {
757 while ( (_buffer.size() % 4) != 0 )
758 _buffer.push_back('\0');
759 }
760
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)
770 {
771 _archName = dyldCache.cacheHeader()->archName();
772 _platform = (Platform)(dyldCache.cacheHeader()->platform());
773 }
774
775
776 ImageProxyGroup::~ImageProxyGroup()
777 {
778 for (DyldSharedCache::MappedMachO& mapping : _ownedMappings ) {
779 vm_deallocate(mach_task_self(), (vm_address_t)mapping.mh, mapping.length);
780 }
781 for (ImageProxy* proxy : _images) {
782 delete proxy;
783 }
784 }
785
786
787 std::string ImageProxyGroup::normalizedPath(const std::string& path)
788 {
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;
797 }
798 }
799 break;
800 }
801 }
802 return path;
803 }
804
805
806 ImageProxy* ImageProxyGroup::findImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, ImageProxy::RPathChain* rChain)
807 {
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;
818 stop = true;
819 return;
820 }
821 }
822 }
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;
827 stop = true;
828 return;
829 }
830 }
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;
840 stop = true;
841 return;
842 }
843 }
844 }
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;
856 stop = true;
857 return;
858 }
859 }
860 }
861 }
862 }
863 else {
864 // load command is full path to dylib
865 result = findAbsoluteImage(diag, possiblePath, canBeMissing, false);
866 if ( result != nullptr ) {
867 stop = true;
868 return;
869 }
870 }
871 });
872
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 )
877 continue;
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);
882 break;
883 }
884 }
885 }
886
887 return result;
888 }
889
890
891 bool ImageProxyGroup::builtImageStillValid(const launch_cache::Image& image)
892 {
893 // only do checks when running on system
894 if ( _buildTimePrefixes.size() != 1 )
895 return true;
896 if ( _buildTimePrefixes.front().size() != 0 )
897 return true;
898 if ( _platform != MachOParser::currentPlatform() )
899 return true;
900
901 struct stat statBuf;
902 bool expectedOnDisk = image.group().dylibsExpectedOnDisk();
903 bool overridableDylib = image.overridableDylib();
904 bool cachedDylib = !image.isDiskImage();
905 bool fileFound = ( ::stat(image.path(), &statBuf) == 0 );
906
907 if ( cachedDylib ) {
908 if ( expectedOnDisk ) {
909 if ( fileFound ) {
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) );
912 }
913 else {
914 // macOS case: dylib missing
915 return false;
916 }
917 }
918 else {
919 if ( fileFound ) {
920 if ( overridableDylib ) {
921 // iOS case: internal install with dylib root
922 return false;
923 }
924 else {
925 // iOS case: customer install, ignore dylib on disk
926 return true;
927 }
928 }
929 else {
930 // iOS case: cached dylib not on disk as expected
931 return true;
932 }
933 }
934 }
935 else {
936 if ( fileFound ) {
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) );
940 }
941 else {
942 // FIXME: need to verify file cdhash
943 return true;
944 }
945 }
946 else {
947 // dylib not on disk as expected
948 return false;
949 }
950 }
951 }
952
953 ImageProxy* ImageProxyGroup::findAbsoluteImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, bool makeErrorMessage, bool pathIsAlreadyReal)
954 {
955 auto pos = _pathToProxy.find(runtimeLoadPath);
956 if ( pos != _pathToProxy.end() )
957 return pos->second;
958
959 // see if this ImageProxyGroup is a proxy for an ImageGroup from the dyld shared cache
960 if ( _basedOn != nullptr ) {
961 uint32_t foundIndex;
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());
970 }
971 else {
972 DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath);
973 if ( mapping != nullptr ) {
974 proxy = new ImageProxy(*mapping, _groupNum, foundIndex, false);
975 }
976 }
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;
983 }
984 return proxy;
985 }
986 }
987 }
988 }
989
990 if ( _nextSearchGroup != nullptr ) {
991 ImageProxy* result = _nextSearchGroup->findAbsoluteImage(diag, runtimeLoadPath, true, false);
992 if ( result != nullptr )
993 return result;
994 }
995
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 )
1008 return proxy;
1009 }
1010 }
1011 }
1012 }
1013
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();
1019 return aProxy;
1020 }
1021 }
1022 }
1023
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);
1029 return proxy;
1030 }
1031 }
1032
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());
1038 }
1039 else {
1040 diag.error("could not find '%s'", runtimeLoadPath.c_str());
1041 }
1042 }
1043 else {
1044 std::string allTries;
1045 for (const std::string& warn : diag.warnings()) {
1046 if ( allTries.empty() )
1047 allTries = warn;
1048 else
1049 allTries = allTries + ", " + warn;
1050 }
1051 diag.clearWarnings();
1052 diag.error("could not use '%s'. Did try: %s", runtimeLoadPath.c_str(), allTries.c_str());
1053 }
1054 }
1055
1056 // record locations not found so it can be verified they are still missing at runtime
1057 _mustBeMissingFiles.insert(runtimeLoadPath);
1058
1059 return nullptr;
1060 }
1061
1062
1063 DyldSharedCache::MappedMachO* ImageProxyGroup::addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables)
1064 {
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 )
1070 continue;
1071 fileFound = true;
1072 // map whole file and determine if it is mach-o or a fat file
1073 int fd = ::open(fullPath.c_str(), O_RDONLY);
1074 if ( fd < 0 ) {
1075 diag.warning("file not open()able '%s' errno=%d", fullPath.c_str(), errno);
1076 continue;
1077 }
1078 size_t len = (size_t)statBuf.st_size;
1079 size_t offset = 0;
1080 const void* p = ::mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
1081 if ( p != MAP_FAILED ) {
1082 size_t sliceLen;
1083 size_t sliceOffset;
1084 bool missingSlice;
1085 Diagnostics fatDiag;
1086 if ( FatUtil::isFatFileWithSlice(fatDiag, p, len, _archName, sliceOffset, sliceLen, missingSlice) ) {
1087 // unmap whole file
1088 ::munmap((void*)p, len);
1089 // remap just slice
1090 p = ::mmap(NULL, sliceLen, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
1091 if ( p != MAP_FAILED ) {
1092 offset = sliceOffset;
1093 len = sliceLen;
1094 }
1095 }
1096 else if ( fatDiag.hasError() ) {
1097 diag.warning("%s", fatDiag.errorMessage().c_str());
1098 }
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);
1103 ::close(fd);
1104 return &_ownedMappings.back();
1105 }
1106 else if (p != MAP_FAILED) {
1107 ::munmap((void*)p, len);
1108 }
1109 }
1110 ::close(fd);
1111 }
1112 if ( !fileFound )
1113 diag.warning("file not found '%s'", runtimePath.c_str());
1114
1115 return nullptr;
1116 }
1117
1118 static bool dontExamineDir(const std::string& dirPath)
1119 {
1120 return endsWith(dirPath, ".app") || endsWith(dirPath, ".xctoolchain") || endsWith(dirPath, ".sdk") || endsWith(dirPath, ".platform");
1121 }
1122
1123 void ImageProxyGroup::addExtraMachOsInBundle(const std::string& appDir)
1124 {
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);
1128 if ( !hasXBit )
1129 return;
1130
1131 // ignore files too small
1132 if ( statBuf.st_size < 0x1000 )
1133 return;
1134
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);
1144 }
1145 }
1146 }
1147 });
1148 }
1149
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)
1155 {
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;
1160
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;
1167 }
1168
1169 // verify libdyld is compatible
1170 ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef();
1171 uint32_t libdyldEntryOffset;
1172 groupProxy->findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset);
1173 if ( diag.hasError() ) {
1174 delete groupProxy;
1175 return nullptr;
1176 }
1177
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() ) {
1185 hadError = true;
1186 diag.copy(proxy->diagnostics());
1187 break;
1188 }
1189 }
1190
1191 if ( hadError ) {
1192 delete groupProxy;
1193 return nullptr;
1194 }
1195
1196 return groupProxy;
1197 }
1198
1199
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)
1204 {
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);
1211
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++, true);
1216 groupProxy->_images.push_back(proxy);
1217 groupProxy->_pathToProxy[mapping.runtimePath] = proxy;
1218 }
1219
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();
1229 }
1230 }
1231 // propagate invalidness
1232 __block bool somethingInvalid;
1233 do {
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;
1240 }
1241 });
1242 }
1243 } while (somethingInvalid);
1244
1245 return groupProxy;
1246 }
1247
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)
1252 {
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);
1257
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());
1264 else
1265 diag.error("%s", (*warnings.begin()).c_str());
1266 }
1267 return nullptr;
1268 }
1269
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());
1273 return nullptr;
1274 }
1275 dlopenGroupProxy._images.push_back(topImageProxy);
1276 dlopenGroupProxy._pathToProxy[imagePath] = topImageProxy;
1277
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());
1282 return nullptr;
1283 }
1284
1285 const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag);
1286
1287 return result;
1288 }
1289
1290
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)
1295 {
1296 // _basedOn can not be set until ImageGroup is built
1297 if ( cachedDylibsGroup->_basedOn == nullptr ) {
1298 cachedDylibsGroup->_basedOn = dyldCache.cachedDylibsGroup();
1299 }
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);
1306
1307 ImageProxy* mainProxy = new ImageProxy(mainProgMapping, 2, 0, true);
1308 if ( mainProxy == nullptr ) {
1309 diag.error("can't find slice matching dyld cache in %s", mainProgMapping.runtimePath.c_str());
1310 return nullptr;
1311 }
1312 mainClosureGroupProxy._images.push_back(mainProxy);
1313 mainClosureGroupProxy._pathToProxy[mainProgMapping.runtimePath] = mainProxy;
1314
1315 return mainClosureGroupProxy.makeClosureBinary(diag, mainProxy, false);
1316 }
1317
1318
1319 bool ImageProxyGroup::addInsertedDylibs(Diagnostics& diag)
1320 {
1321 __block bool success = true;
1322 _pathOverrides.forEachInsertedDylib(^(const char* dylibPath) {
1323 ImageProxy* insertProxy = findAbsoluteImage(diag, dylibPath, false, true);
1324 if ( insertProxy == nullptr )
1325 success = false;
1326 });
1327 return success;
1328 }
1329
1330 static DyldCacheParser findDyldCache(Diagnostics& diag, const ClosureBuffer::CacheIdent& cacheIdent, task_t requestor, bool* dealloc)
1331 {
1332 *dealloc = false;
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(&currentCacheSize);
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);
1341 }
1342 #endif
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
1350 else
1351 diag.error("dyld cache uuid has changed");
1352 }
1353 #if BUILDING_CLOSURED
1354 else {
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);
1358 kern_return_t r;
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);
1363 }
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);
1371 }
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);
1384 }
1385 if ( curProt != VM_PROT_READ )
1386 vm_protect(mach_task_self(), (long)mappedAddress, (long)mappedSize, false, VM_PROT_READ);
1387 }
1388 *dealloc = true;
1389 return DyldCacheParser((DyldSharedCache*)bufferAddress, false); // assumes cache in other process is mapped as three regions
1390 }
1391 #endif
1392 return DyldCacheParser(nullptr, false);
1393 }
1394
1395 BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes)
1396 {
1397 // unpack buffer
1398 bool deallocCacheCopy;
1399 DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy);
1400 if ( diag.hasError() )
1401 return nullptr;
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]);
1409 }
1410
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);
1419 else
1420 realBuildTimePrefixes.push_back(prefix);
1421 }
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);
1426
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 ) {
1432 // build closure
1433 result = mainClosureGroupProxy.makeClosureBinary(diag, proxy, false);
1434 }
1435 }
1436
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);
1440
1441 return result;
1442 }
1443
1444 ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
1445 {
1446 Diagnostics diag;
1447 const BinaryImageGroupData* newGroup = ImageProxyGroup::makeDlopenGroup(diag, input, mach_task_self(), {""});
1448
1449 if ( diag.noError() ) {
1450 // on success return the ImageGroup binary in the ClosureBuffer
1451 dyld3::ClosureBuffer result(newGroup);
1452 free((void*)newGroup);
1453 return result;
1454 }
1455 else {
1456 // on failure return the error message in the ClosureBuffer
1457 dyld3::ClosureBuffer err(diag.errorMessage().c_str());
1458 return err;
1459 }
1460 }
1461
1462 const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector<std::string>& buildTimePrefixes)
1463 {
1464 // unpack buffer
1465 bool deallocCacheCopy;
1466 DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy);
1467 if ( diag.hasError() )
1468 return nullptr;
1469
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]);
1477 }
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]);
1483
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();
1496 }
1497 ImageProxyGroup dlopenGroupProxy(groupCount, dyldCache, nullptr, prevProxy, targetDylib, existingGroups, buildTimePrefixes, envVars);
1498
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() )
1505 allWarnings = warn;
1506 else
1507 allWarnings = allWarnings + ", " + warn;
1508 }
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);
1513 return nullptr;
1514 }
1515
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);
1522 return nullptr;
1523 }
1524 dlopenGroupProxy._images.push_back(topImageProxy);
1525 dlopenGroupProxy._pathToProxy[targetDylib] = topImageProxy;
1526
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);
1533 return nullptr;
1534 }
1535
1536 // construct ImageGroup from ImageProxies
1537 const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag);
1538
1539 // clean up
1540 if ( deallocCacheCopy )
1541 vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize);
1542
1543 return result;
1544 }
1545
1546
1547
1548
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)
1554 {
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);
1562 else
1563 realBuildTimePrefixes.push_back(prefix);
1564 }
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);
1569
1570 // add any DYLD_INSERTED_LIBRARIES into closure
1571 if ( !mainClosureGroupProxy.addInsertedDylibs(diag) )
1572 return nullptr;
1573
1574 ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true);
1575 if ( proxy == nullptr )
1576 return nullptr;
1577
1578 return mainClosureGroupProxy.makeClosureBinary(diag, proxy, includeDylibsInDir);
1579 }
1580
1581 const char* sSkipPrograms_macOS[] = {
1582 "/Applications/iBooks.app/Contents/MacOS/iBooks",
1583 };
1584
1585 const char* sSkipPrograms_embeddedOSes[] = {
1586 "/sbin/launchd",
1587 "/usr/local/sbin/launchd.debug",
1588 "/usr/local/sbin/launchd.development"
1589 };
1590
1591 BinaryClosureData* ImageProxyGroup::makeClosureBinary(Diagnostics& diag, ImageProxy* mainProgProxy, bool includeDylibsInDir)
1592 {
1593 assert(mainProgProxy != nullptr);
1594 assert(_images.size() >= 1);
1595
1596 // check black list
1597 if ( _platform == Platform::macOS ) {
1598 for (const char* skipProg : sSkipPrograms_macOS) {
1599 if ( mainProgProxy->runtimePath() == skipProg ) {
1600 diag.error("black listed program");
1601 return nullptr;
1602 }
1603 }
1604 } else {
1605 for (const char* skipProg : sSkipPrograms_embeddedOSes) {
1606 if ( mainProgProxy->runtimePath() == skipProg ) {
1607 diag.error("black listed program");
1608 return nullptr;
1609 }
1610 }
1611 }
1612
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());
1618 return nullptr;
1619 }
1620
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;
1623 std::string appDir;
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;
1630 }
1631 else if ( posApp != std::string::npos ) {
1632 appDir = mainProgProxy->runtimePath().substr(0, posApp+leafName.size()+5);
1633 isAppMainExecutable = true;
1634 }
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());
1645 }
1646 }
1647 }
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);
1657 }
1658 }
1659 }
1660
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() )
1666 return nullptr;
1667 }
1668 }
1669
1670 // gather warnings from all statically dependent images
1671 for (ImageProxy* proxy : _images) {
1672 if ( !proxy->staticallyReferenced() && proxy->diagnostics().hasError() )
1673 continue;
1674 diag.copy(proxy->diagnostics());
1675 if ( diag.hasError() ) {
1676 return nullptr;
1677 }
1678 }
1679
1680 // get program entry
1681 MachOParser mainExecutableParser(mainProgProxy->mh(), _dyldCache.cacheIsMappedRaw());
1682 bool usesCRT;
1683 uint32_t entryOffset;
1684 mainExecutableParser.getEntry(entryOffset, usesCRT);
1685
1686 // build ImageGroupWriter
1687 launch_cache::ImageGroupWriter groupWriter(_groupNum, mainExecutableParser.uses16KPages(), mainExecutableParser.is64(), _dylibsExpectedOnDisk, _inodesAreSameAsRuntime);
1688 populateGroupWriter(diag, groupWriter);
1689 if ( diag.hasError() )
1690 return nullptr;
1691
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() )
1697 return nullptr;
1698 ImageRef libSystemImageRef = ImageRef::makeEmptyImageRef();
1699
1700 findLibSystem(diag, mainExecutableParser.isSimulatorBinary(), libSystemImageRef);
1701 if ( diag.hasError() )
1702 return nullptr;
1703
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) {
1710 size_t start = 1;
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);
1718 }
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
1723 }
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));
1730 ++envVarCount;
1731 });
1732
1733 // 4-byte align string pool size
1734 stringPool.align();
1735
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)
1742 + groupSize
1743 + missingFilesArraySize
1744 + envVarsSize
1745 + stringPoolSize;
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));
1750
1751 uint32_t maxImageLoadCount = groupWriter.maxLoadCount(diag, _knownGroups, &clo->group);
1752
1753 if ( mainImage.isInvalid() ) {
1754 free((void*)clo);
1755 diag.error("depends on invalid dylib");
1756 return nullptr;
1757 }
1758
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();
1764 clo->padding = 0;
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);
1777
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);
1782 }
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());
1789
1790 return clo;
1791 }
1792
1793 const BinaryImageGroupData* ImageProxyGroup::makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[])
1794 {
1795 const bool continueIfErrors = (_groupNum == 1);
1796 bool uses16KPages = true;
1797 bool is64 = true;
1798 if ( !_images.empty() ) {
1799 MachOParser firstParser(_images.front()->mh(), _dyldCache.cacheIsMappedRaw());
1800 uses16KPages = firstParser.uses16KPages();
1801 is64 = firstParser.is64();
1802 }
1803 launch_cache::ImageGroupWriter groupWriter(_groupNum, uses16KPages, is64, _dylibsExpectedOnDisk, _inodesAreSameAsRuntime);
1804 populateGroupWriter(diag, groupWriter, neverEliminateStubs);
1805 if ( diag.hasError() )
1806 return nullptr;
1807
1808 // malloc a buffer and fill in ImageGroup part
1809 BinaryImageGroupData* groupData = (BinaryImageGroupData*)malloc(groupWriter.size());
1810 groupWriter.finalizeTo(diag, _knownGroups, groupData);
1811
1812 if ( !continueIfErrors && groupWriter.isInvalid(0) ) {
1813 free((void*)groupData);
1814 diag.error("depends on invalid dylib");
1815 return nullptr;
1816 }
1817
1818 return groupData;
1819 }
1820
1821
1822 void ImageProxyGroup::findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& vmOffsetInLibDyld)
1823 {
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");
1828 return;
1829 }
1830 ref = ImageRef(0, libDyldProxy->groupNum(), libDyldProxy->indexInGroup());
1831
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");
1838 return;
1839 }
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);
1844 return;
1845 }
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");
1850 }
1851
1852 void ImageProxyGroup::findLibSystem(Diagnostics& diag, bool forSimulator, ImageRef& ref)
1853 {
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");
1858 return;
1859 }
1860 ref = ImageRef(0, libSystemProxy->groupNum(), libSystemProxy->indexInGroup());
1861 }
1862
1863
1864 std::vector<ImageProxy*> ImageProxyGroup::flatLookupOrder()
1865 {
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]);
1870
1871 // recursive add dependents of main executable
1872 _images[_mainExecutableIndex]->addToFlatLookup(results);
1873
1874 // recursive add dependents of any inserted dylibs
1875 for (uint32_t i=0; i < _mainExecutableIndex; ++i)
1876 _images[i]->addToFlatLookup(results);
1877
1878 return results;
1879 }
1880
1881 void ImageProxyGroup::populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[])
1882 {
1883 const bool buildingDylibsInCache = (_groupNum == 0);
1884 const bool continueIfErrors = (_groupNum == 1);
1885
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);
1890 }
1891
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;
1907 if ( hasObjC ) {
1908 mayHavePlusLoad = isEncrypted || imageParser.hasPlusLoadMethod(diag);
1909 groupWriter.setImageMayHavePlusLoads(i, mayHavePlusLoad);
1910 }
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));
1915 uuid_t uuid;
1916 if ( imageParser.getUuid(uuid) )
1917 groupWriter.setImageUUID(i, uuid);
1918 if ( _inodesAreSameAsRuntime ) {
1919 groupWriter.setImageFileMtimeAndInode(i, _images[i]->fileModTime(), _images[i]->fileInode());
1920 }
1921 else {
1922 uint8_t cdHash[20];
1923 if ( !imageParser.getCDHash(cdHash) )
1924 bzero(cdHash, 20);
1925 // if image is not code signed, cdHash filled with all zeros
1926 groupWriter.setImageCdHash(i, cdHash);
1927 }
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);
1938 }
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);
1946 });
1947 groupWriter.setImageInitializerOffsets(i, initOffsets);
1948 if ( diag.hasError() && !continueIfErrors ) {
1949 return;
1950 }
1951 // add DOFs to image
1952 __block std::vector<uint32_t> dofOffsets;
1953 imageParser.forEachDOFSection(diag, ^(uint32_t offset) {
1954 dofOffsets.push_back(offset);
1955 });
1956 groupWriter.setImageDOFOffsets(i, dofOffsets);
1957 if ( diag.hasError() && !continueIfErrors ) {
1958 return;
1959 }
1960 bool neverUnload = false;
1961 if ( buildingDylibsInCache )
1962 neverUnload = true;
1963 if ( _images[i]->staticallyReferenced() )
1964 neverUnload = true;
1965 if ( imageParser.hasObjC() && (imageParser.fileType() == MH_DYLIB) )
1966 neverUnload = true;
1967 if ( imageParser.hasThreadLocalVariables() )
1968 neverUnload = true;
1969 if ( !dofOffsets.empty() )
1970 neverUnload = true;
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);
1979 }
1980
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);
1988 }
1989 }
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");
1993 }
1994 }
1995 }
1996
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());
2005 else
2006 groupWriter.setImageInvalid(i);
2007 }
2008 else {
2009 launch_cache::binary_format::ImageRef ref((uint8_t)kind, dep->groupNum(), dep->indexInGroup());
2010 groupWriter.setImageDependent(i, depIndex, ref);
2011 }
2012 ++depIndex;
2013 });
2014 }
2015
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;
2020 do {
2021 somethingInvalidated = false;
2022 for (uint32_t i=0; i < imageCount; ++i) {
2023 if ( groupWriter.isInvalid(i) )
2024 continue;
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 )
2029 continue;
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;
2036 break;
2037 }
2038 }
2039 }
2040 }
2041 } while (somethingInvalidated);
2042 }
2043
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) )
2052 continue;
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());
2061 continue;
2062 }
2063 else {
2064 diag.error("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str());
2065 return;
2066 }
2067 }
2068 }
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) )
2077 continue;
2078 ImageProxy* thisProxy = _images[imageIndex];
2079 // Only process interposing info on dylibs statically linked into closure
2080 if ( !thisProxy->staticallyReferenced() )
2081 continue;
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);
2087 return;
2088 }
2089 TargetSymbolValue interposeReplacee = TargetSymbolValue::makeInvalid();
2090 TargetSymbolValue interposeReplacement = TargetSymbolValue::makeInvalid();
2091 for (const FixUp& fixup : fixupInfos[imageIndex].fixups) {
2092 if ( fixup.segIndex != segIndex )
2093 continue;
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);
2098 }
2099 else {
2100 diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str());
2101 return;
2102 }
2103 }
2104 else if ( fixup.segOffset == replaceeSegOffset ) {
2105 if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::pointerBind ) {
2106 interposeReplacee = fixup.target;
2107 }
2108 else {
2109 diag.warning("bad interposing target in %s", _images[imageIndex]->runtimePath().c_str());
2110 return;
2111 }
2112 }
2113 }
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 )
2117 continue;
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;
2125 break;
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:
2132 break;
2133 }
2134 }
2135 }
2136 if ( interposeReplacee.isInvalid() || interposeReplacement.isInvalid() ) {
2137 diag.error("malformed interposing section in %s", _images[imageIndex]->runtimePath().c_str());
2138 tupleStop = true;
2139 return;
2140 }
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 if ( replacementOffsetInImage >= 0xFFFFFFFFULL ) {
2153 diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str());
2154 return;
2155 }
2156 DyldCacheOverride cacheOverride;
2157 cacheOverride.patchTableIndex = patchTableIndex;
2158 cacheOverride.imageIndex = replacementIndexInGroup;
2159 cacheOverride.imageOffset = replacementOffsetInImage;
2160 _cacheOverrides.push_back(cacheOverride);
2161 }
2162 }
2163 });
2164 if ( diag.hasError() && !continueIfErrors ) {
2165 return;
2166 }
2167 // if any dylibs in the closure override a dyld cache dylib, then record the cache patches needed
2168 ImageRef overrideOf = thisProxy->overrideOf();
2169 if ( (overrideOf != ImageRef::makeEmptyImageRef()) && (overrideOf.groupNum() == 0) ) {
2170 //fprintf(stderr, "need to patch %s into cache\n", thisProxy->runtimePath().c_str());
2171 const launch_cache::Image imageInCache = dyldCacheGroup.image(overrideOf.indexInGroup());
2172 const mach_header* imageInCacheMH = (mach_header*)((char*)(_dyldCache.cacheHeader()) + imageInCache.cacheOffset());
2173 MachOParser inCacheParser(imageInCacheMH, _dyldCache.cacheIsMappedRaw());
2174 // walk all exported symbols in dylib in cache
2175 inCacheParser.forEachExportedSymbol(diag, ^(const char* symbolName, uint64_t imageOffset, bool isReExport, bool &stop) {
2176 if ( isReExport )
2177 return;
2178 uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + imageOffset);
2179 //fprintf(stderr, " patch cache offset 0x%08X which is %s\n", cacheOffsetOfSymbol, symbolName);
2180 // for each exported symbol, see if it is in patch table (used by something else in cache)
2181 uint32_t patchTableIndex;
2182 if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) {
2183 //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
2184 // lookup address of symbol in override dylib and add patch info
2185 MachOParser::FoundSymbol foundInfo;
2186 if ( imageParser.findExportedSymbol(diag, symbolName, nullptr, foundInfo, nullptr) ) {
2187 DyldCacheOverride cacheOverride;
2188 assert(patchTableIndex < (1 << 24));
2189 assert(thisProxy->indexInGroup() < (1 << 8));
2190 assert(foundInfo.value < (1ULL << 32));
2191 cacheOverride.patchTableIndex = patchTableIndex;
2192 cacheOverride.imageIndex = thisProxy->indexInGroup();
2193 cacheOverride.imageOffset = foundInfo.value;
2194 _cacheOverrides.push_back(cacheOverride);
2195 }
2196 }
2197 });
2198 }
2199 // save off all images in closure with weak defines
2200 if ( thisProxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK) ) {
2201 staticImagesWithWeakDefs.insert(thisProxy);
2202 }
2203 }
2204 // if any dylibs in the closure override a weak symbol in a cached dylib, then record the cache patches needed
2205 if ( !staticImagesWithWeakDefs.empty() ) {
2206 // build list of all weak def symbol names
2207 __block std::unordered_map<std::string, DyldCacheOverride> weakSymbols;
2208 for (ImageProxy* proxy : staticImagesWithWeakDefs ) {
2209 MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
2210 weakDefParser.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) {
2211 weakSymbols[symbolName] = { 0, 0, 0 };
2212 });
2213 }
2214 // do a flat namespace walk of all images
2215 std::vector<ImageProxy*> flatSearchOrder = flatLookupOrder();
2216 for (ImageProxy* proxy : flatSearchOrder) {
2217 // only look at images that participate in weak coalescing
2218 if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 )
2219 continue;
2220 // look only at images in closure
2221 if ( proxy->groupNum() == 2 ) {
2222 MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
2223 // check if this closure image defines any of the not-yet found weak symbols
2224 for (auto& entry : weakSymbols ) {
2225 if ( entry.second.imageOffset != 0 )
2226 continue;
2227 Diagnostics weakDiag;
2228 MachOParser::FoundSymbol foundInfo;
2229 if ( weakDefParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) {
2230 assert(proxy->indexInGroup() < (1 << 8));
2231 if ( foundInfo.value >= (1ULL << 32) ) {
2232 diag.warning("bad weak symbol address in %s", proxy->runtimePath().c_str());
2233 return;
2234 }
2235 entry.second.imageIndex = proxy->indexInGroup();
2236 entry.second.imageOffset = foundInfo.value;
2237 }
2238 }
2239 }
2240 }
2241 for (ImageProxy* proxy : flatSearchOrder) {
2242 // only look at images that participate in weak coalescing
2243 if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 )
2244 continue;
2245 // look only at images in dyld cache
2246 if ( proxy->groupNum() == 0 ) {
2247 const launch_cache::Image imageInCache = dyldCacheGroup.image(proxy->indexInGroup());
2248 MachOParser inCacheParser(proxy->mh(), _dyldCache.cacheIsMappedRaw());
2249 Diagnostics cacheDiag;
2250 for (auto& entry : weakSymbols) {
2251 if ( entry.second.imageOffset == 0 )
2252 continue;
2253 Diagnostics weakDiag;
2254 MachOParser::FoundSymbol foundInfo;
2255 if ( inCacheParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) {
2256 uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + foundInfo.value);
2257 // see if this symbol is in patch table (used by something else in cache)
2258 uint32_t patchTableIndex;
2259 if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) {
2260 //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol);
2261 DyldCacheOverride cacheOverride;
2262 cacheOverride.patchTableIndex = patchTableIndex;
2263 cacheOverride.imageIndex = entry.second.imageIndex;
2264 cacheOverride.imageOffset = entry.second.imageOffset;
2265 _cacheOverrides.push_back(cacheOverride);
2266 }
2267 }
2268 }
2269 }
2270 }
2271 }
2272 }
2273 // record fixups for each image
2274 for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
2275 groupWriter.setImageFixups(diag, imageIndex, fixupInfos[imageIndex].fixups, fixupInfos[imageIndex].hasTextRelocs);
2276 }
2277 }
2278
2279 // pass 5: invalidate any images dependent on invalid images)
2280 if ( someBadFixups && continueIfErrors ) {
2281 __block bool somethingInvalidated = false;
2282 do {
2283 somethingInvalidated = false;
2284 for (uint32_t i=0; i < imageCount; ++i) {
2285 if ( groupWriter.isInvalid(i) )
2286 continue;
2287 uint32_t depCount = groupWriter.imageDependentsCount(i);
2288 for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) {
2289 launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex);
2290 if ( ref.groupNum() == _groupNum ) {
2291 if ( groupWriter.isInvalid(ref.indexInGroup()) ) {
2292 // this image depends on something invalid, so mark it invalid
2293 //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str());
2294 groupWriter.setImageInvalid(i);
2295 somethingInvalidated = true;
2296 break;
2297 }
2298 }
2299 }
2300 }
2301 } while (somethingInvalidated);
2302 }
2303
2304 // pass 6: compute initializer lists for each image
2305 const bool log = false;
2306 for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
2307 if ( groupWriter.isInvalid(imageIndex) )
2308 continue;
2309
2310 auto inits = _images[imageIndex]->getInitBeforeList(*this);
2311 if ( log && buildingDylibsInCache ) {
2312 fprintf(stderr, "%s\n init list: ", _images[imageIndex]->runtimePath().c_str());
2313 for (launch_cache::binary_format::ImageRef ref : inits) {
2314 if ( ref.groupNum() == 0 ) {
2315 std::string dep = _images[ref.indexInGroup()]->runtimePath();
2316 size_t off = dep.rfind('/');
2317 fprintf(stderr, "%s, ", dep.substr(off+1).c_str());
2318 }
2319 }
2320 fprintf(stderr, "\n");
2321 }
2322 groupWriter.setImageInitBefore(imageIndex, inits);
2323 }
2324
2325 // pass 7: compute DOFs
2326 for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) {
2327 if ( groupWriter.isInvalid(imageIndex) )
2328 continue;
2329
2330 auto inits = _images[imageIndex]->getInitBeforeList(*this);
2331 if ( log && buildingDylibsInCache ) {
2332 fprintf(stderr, "%s\n DOFs: ", _images[imageIndex]->runtimePath().c_str());
2333 for (launch_cache::binary_format::ImageRef ref : inits) {
2334 if ( ref.groupNum() == 0 ) {
2335 std::string dep = _images[ref.indexInGroup()]->runtimePath();
2336 size_t off = dep.rfind('/');
2337 fprintf(stderr, "%s, ", dep.substr(off+1).c_str());
2338 }
2339 }
2340 fprintf(stderr, "\n");
2341 }
2342 groupWriter.setImageInitBefore(imageIndex, inits);
2343 }
2344
2345 // pass 8: add patch table entries iff this is dyld cache ImageGroup
2346 assert(buildingDylibsInCache == (_patchTable != nullptr));
2347 if ( _patchTable != nullptr ) {
2348 for (uint32_t i=0; i < imageCount; ++i) {
2349 const auto pos = _patchTable->find(_images[i]->mh());
2350 if ( pos != _patchTable->end() ) {
2351 for (const auto& entry : pos->second ) {
2352 uint32_t defFunctionOffset = entry.first;
2353 groupWriter.setImagePatchLocations(i, defFunctionOffset, entry.second);
2354 }
2355 }
2356 }
2357 }
2358
2359 // if this is a main closure group with an interposing dylib, add cache overrides
2360 if ( !_cacheOverrides.empty() ) {
2361 groupWriter.setGroupCacheOverrides(_cacheOverrides);
2362 }
2363
2364 // align string pool
2365 groupWriter.alignStringPool();
2366 }
2367
2368
2369
2370 } // namespace dyld3
2371
2372