]> git.saurik.com Git - apple/dyld.git/blob - dyld3/Loading.cpp
dyld-655.1.tar.gz
[apple/dyld.git] / dyld3 / Loading.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 <bitset>
26
27 #include <stdint.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <uuid/uuid.h>
31 #include <mach/mach.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35 #include <fcntl.h>
36 #include <sys/dtrace.h>
37 #include <sys/errno.h>
38 #include <unistd.h>
39 #include <System/sys/mman.h>
40 #include <System/sys/csr.h>
41 #include <System/machine/cpu_capabilities.h>
42 #include <bootstrap.h>
43 #include <CommonCrypto/CommonDigest.h>
44 #include <sandbox.h>
45 #include <sandbox/private.h>
46 #include <dispatch/dispatch.h>
47 #include <mach/vm_page_size.h>
48
49 #include "MachOFile.h"
50 #include "MachOLoaded.h"
51 #include "Logging.h"
52 #include "Loading.h"
53 #include "Tracing.h"
54 #include "dyld.h"
55 #include "dyld_cache_format.h"
56
57
58 namespace dyld {
59 void log(const char* m, ...);
60 }
61
62
63 namespace {
64
65 // utility to track a set of ImageNum's in use
66 class VIS_HIDDEN ImageNumSet
67 {
68 public:
69 void add(dyld3::closure::ImageNum num);
70 bool contains(dyld3::closure::ImageNum num) const;
71
72 private:
73 std::bitset<5120> _bitmap;
74 dyld3::OverflowSafeArray<dyld3::closure::ImageNum> _overflowArray;
75 };
76
77 void ImageNumSet::add(dyld3::closure::ImageNum num)
78 {
79 if ( num < 5120 )
80 _bitmap.set(num);
81 else
82 _overflowArray.push_back(num);
83 }
84
85 bool ImageNumSet::contains(dyld3::closure::ImageNum num) const
86 {
87 if ( num < 5120 )
88 return _bitmap.test(num);
89
90 for (dyld3::closure::ImageNum existingNum : _overflowArray) {
91 if ( existingNum == num )
92 return true;
93 }
94 return false;
95 }
96 } // namespace anonymous
97
98
99 namespace dyld3 {
100
101 Loader::Loader(Array<LoadedImage>& storage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
102 LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs)
103 : _allImages(storage), _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress),
104 _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs)
105 {
106 }
107
108 void Loader::addImage(const LoadedImage& li)
109 {
110 _allImages.push_back(li);
111 }
112
113 LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
114 {
115 for (LoadedImage& info : _allImages) {
116 if ( info.image()->representsImageNum(targetImageNum) )
117 return &info;
118 }
119 return nullptr;
120 }
121
122 uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target)
123 {
124 const LoadedImage* info;
125 switch ( target.sharedCache.kind ) {
126 case closure::Image::ResolvedSymbolTarget::kindSharedCache:
127 assert(_dyldCacheAddress != nullptr);
128 return (uintptr_t)_dyldCacheAddress + (uintptr_t)target.sharedCache.offset;
129
130 case closure::Image::ResolvedSymbolTarget::kindImage:
131 info = findImage(target.image.imageNum);
132 assert(info != nullptr);
133 return (uintptr_t)(info->loadedAddress()) + (uintptr_t)target.image.offset;
134
135 case closure::Image::ResolvedSymbolTarget::kindAbsolute:
136 if ( target.absolute.value & (1ULL << 62) )
137 return (uintptr_t)(target.absolute.value | 0xC000000000000000ULL);
138 else
139 return (uintptr_t)target.absolute.value;
140 }
141 assert(0 && "malformed ResolvedSymbolTarget");
142 return 0;
143 }
144
145
146 void Loader::completeAllDependents(Diagnostics& diag, uintptr_t topIndex)
147 {
148 // accumulate all image overrides
149 STACK_ALLOC_ARRAY(ImageOverride, overrides, _allImages.maxCount());
150 for (const auto anArray : _imagesArrays) {
151 // ignore prebuilt Image* in dyld cache
152 if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum )
153 continue;
154 anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
155 ImageOverride overrideEntry;
156 if ( image->isOverrideOfDyldCacheImage(overrideEntry.inCache) ) {
157 overrideEntry.replacement = image->imageNum();
158 overrides.push_back(overrideEntry);
159 }
160 });
161 }
162
163 // make cache for fast lookup of already loaded images
164 __block ImageNumSet alreadyLoaded;
165 for (int i=0; i <= topIndex; ++i) {
166 alreadyLoaded.add(_allImages[i].image()->imageNum());
167 }
168
169 // for each image in _allImages, starting at topIndex, make sure its depenents are in _allImages
170 uintptr_t index = topIndex;
171 while ( (index < _allImages.count()) && diag.noError() ) {
172 const closure::Image* image = _allImages[index].image();
173 //fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path());
174 image->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
175 // check if imageNum needs to be changed to an override
176 for (const ImageOverride& entry : overrides) {
177 if ( entry.inCache == depImageNum ) {
178 depImageNum = entry.replacement;
179 break;
180 }
181 }
182 // check if this dependent is already loaded
183 if ( !alreadyLoaded.contains(depImageNum) ) {
184 // if not, look in imagesArrays
185 const closure::Image* depImage = closure::ImageArray::findImage(_imagesArrays, depImageNum);
186 if ( depImage != nullptr ) {
187 //dyld::log(" load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path());
188 if ( _allImages.freeCount() == 0 ) {
189 diag.error("too many initial images");
190 stop = true;
191 }
192 else {
193 _allImages.push_back(LoadedImage::make(depImage));
194 }
195 alreadyLoaded.add(depImageNum);
196 }
197 else {
198 diag.error("unable to locate imageNum=0x%04X, depIndex=%d of %s", depImageNum, depIndex, image->path());
199 stop = true;
200 }
201 }
202 });
203 ++index;
204 }
205 }
206
207 void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, uintptr_t topIndex)
208 {
209 // scan array and map images not already loaded
210 for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
211 LoadedImage& info = _allImages[i];
212 if ( info.loadedAddress() != nullptr ) {
213 // log main executable's segments
214 if ( (info.loadedAddress()->filetype == MH_EXECUTE) && (info.state() == LoadedImage::State::mapped) ) {
215 if ( _logSegments("dyld: mapped by kernel %s\n", info.image()->path()) ) {
216 info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
217 uint64_t start = (long)info.loadedAddress() + vmOffset;
218 uint64_t end = start+vmSize-1;
219 if ( (segIndex == 0) && (permissions == 0) ) {
220 start = 0;
221 }
222 _logSegments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", info.loadedAddress()->segmentName(segIndex),
223 (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
224 start, end);
225 });
226 }
227 }
228 // skip over ones already loaded
229 continue;
230 }
231 if ( info.image()->inDyldCache() ) {
232 if ( info.image()->overridableDylib() ) {
233 struct stat statBuf;
234 if ( stat(info.image()->path(), &statBuf) == 0 ) {
235 // verify file has not changed since closure was built
236 uint64_t inode;
237 uint64_t mtime;
238 if ( info.image()->hasFileModTimeAndInode(inode, mtime) ) {
239 if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
240 diag.error("dylib file mtime/inode changed since closure was built for '%s'", info.image()->path());
241 }
242 }
243 else {
244 diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path());
245 }
246 }
247 else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) {
248 diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path());
249 }
250 }
251 if ( diag.noError() ) {
252 info.setLoadedAddress((MachOLoaded*)((uintptr_t)_dyldCacheAddress + info.image()->cacheOffset()));
253 info.setState(LoadedImage::State::fixedUp);
254 if ( _logSegments("dyld: Using from dyld cache %s\n", info.image()->path()) ) {
255 info.image()->forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
256 _logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", info.loadedAddress()->segmentName(segIndex),
257 (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
258 (long)info.loadedAddress()+(long)vmOffset, (long)info.loadedAddress()+(long)vmOffset+(long)vmSize-1);
259 });
260 }
261 }
262 }
263 else {
264 mapImage(diag, info, fromOFI);
265 if ( diag.hasError() )
266 break; // out of for loop
267 }
268
269 }
270 if ( diag.hasError() ) {
271 // bummer, need to clean up by unmapping any images just mapped
272 for (LoadedImage& info : _allImages) {
273 if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) {
274 _logSegments("dyld: unmapping %s\n", info.image()->path());
275 unmapImage(info);
276 }
277 }
278 return;
279 }
280
281 // apply fixups
282 for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
283 LoadedImage& info = _allImages[i];
284 // images in shared cache do not need fixups applied
285 if ( info.image()->inDyldCache() )
286 continue;
287 // previously loaded images were previously fixed up
288 if ( info.state() < LoadedImage::State::fixedUp ) {
289 applyFixupsToImage(diag, info);
290 if ( diag.hasError() )
291 break;
292 info.setState(LoadedImage::State::fixedUp);
293 }
294 }
295
296 // find and register dtrace DOFs
297 if ( processDOFs ) {
298 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(DOFInfo, dofImages, _allImages.count());
299 for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
300 LoadedImage& info = _allImages[i];
301 info.image()->forEachDOF(info.loadedAddress(), ^(const void* section) {
302 DOFInfo dofInfo;
303 dofInfo.dof = section;
304 dofInfo.imageHeader = info.loadedAddress();
305 dofInfo.imageShortName = info.image()->leafName();
306 dofImages.push_back(dofInfo);
307 });
308 }
309 registerDOFs(dofImages);
310 }
311 }
312
313 bool Loader::sandboxBlocked(const char* path, const char* kind)
314 {
315 #if TARGET_IPHONE_SIMULATOR
316 // sandbox calls not yet supported in dyld_sim
317 return false;
318 #else
319 sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
320 return ( sandbox_check(getpid(), kind, filter, path) > 0 );
321 #endif
322 }
323
324 bool Loader::sandboxBlockedMmap(const char* path)
325 {
326 return sandboxBlocked(path, "file-map-executable");
327 }
328
329 bool Loader::sandboxBlockedOpen(const char* path)
330 {
331 return sandboxBlocked(path, "file-read-data");
332 }
333
334 bool Loader::sandboxBlockedStat(const char* path)
335 {
336 return sandboxBlocked(path, "file-read-metadata");
337 }
338
339 void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
340 {
341 dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0);
342
343 const closure::Image* image = info.image();
344 uint64_t sliceOffset = image->sliceOffsetInFile();
345 const uint64_t totalVMSize = image->vmSizeToMap();
346 uint32_t codeSignFileOffset;
347 uint32_t codeSignFileSize;
348 bool isCodeSigned = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize);
349
350 // open file
351 int fd = ::open(info.image()->path(), O_RDONLY, 0);
352 if ( fd == -1 ) {
353 int openErr = errno;
354 if ( (openErr == EPERM) && sandboxBlockedOpen(image->path()) )
355 diag.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image->path());
356 else
357 diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image->path(), openErr);
358 return;
359 }
360
361 // get file info
362 struct stat statBuf;
363 #if TARGET_IPHONE_SIMULATOR
364 if ( stat(image->path(), &statBuf) != 0 ) {
365 #else
366 if ( fstat(fd, &statBuf) != 0 ) {
367 #endif
368 int statErr = errno;
369 if ( (statErr == EPERM) && sandboxBlockedStat(image->path()) )
370 diag.error("file system sandbox blocked stat(\"%s\")", image->path());
371 else
372 diag.error("stat(\"%s\") failed with errno=%d", image->path(), statErr);
373 close(fd);
374 return;
375 }
376
377 // verify file has not changed since closure was built
378 uint64_t inode;
379 uint64_t mtime;
380 if ( image->hasFileModTimeAndInode(inode, mtime) ) {
381 if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
382 diag.error("file mtime/inode changed since closure was built for '%s'", image->path());
383 close(fd);
384 return;
385 }
386 }
387
388 // handle case on iOS where sliceOffset in closure is wrong because file was thinned after cache was built
389 if ( (_dyldCacheAddress != nullptr) && !(((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk) ) {
390 if ( sliceOffset != 0 ) {
391 if ( round_page_kernel(codeSignFileOffset+codeSignFileSize) == round_page_kernel(statBuf.st_size) ) {
392 // file is now thin
393 sliceOffset = 0;
394 }
395 }
396 }
397
398 // register code signature
399 uint64_t coveredCodeLength = UINT64_MAX;
400 if ( isCodeSigned ) {
401 auto sigTimer = ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 0);
402 fsignatures_t siginfo;
403 siginfo.fs_file_start = sliceOffset; // start of mach-o slice in fat file
404 siginfo.fs_blob_start = (void*)(long)(codeSignFileOffset); // start of CD in mach-o file
405 siginfo.fs_blob_size = codeSignFileSize; // size of CD
406 int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
407 if ( result == -1 ) {
408 int errnoCopy = errno;
409 if ( (errnoCopy == EPERM) || (errnoCopy == EBADEXEC) ) {
410 diag.error("code signature invalid (errno=%d) sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
411 errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image->path());
412 }
413 else {
414 diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
415 errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image->path());
416 }
417 close(fd);
418 return;
419 }
420 coveredCodeLength = siginfo.fs_file_start;
421 if ( coveredCodeLength < codeSignFileOffset ) {
422 diag.error("code signature does not cover entire file up to signature");
423 close(fd);
424 return;
425 }
426 }
427
428 // <rdar://problem/41015217> dyld should use F_CHECK_LV even on unsigned binaries
429 {
430 // <rdar://problem/32684903> always call F_CHECK_LV to preflight
431 fchecklv checkInfo;
432 char messageBuffer[512];
433 messageBuffer[0] = '\0';
434 checkInfo.lv_file_start = sliceOffset;
435 checkInfo.lv_error_message_size = sizeof(messageBuffer);
436 checkInfo.lv_error_message = messageBuffer;
437 int res = fcntl(fd, F_CHECK_LV, &checkInfo);
438 if ( res == -1 ) {
439 diag.error("code signature in (%s) not valid for use in process: %s", image->path(), messageBuffer);
440 close(fd);
441 return;
442 }
443 }
444
445 // reserve address range
446 vm_address_t loadAddress = 0;
447 kern_return_t r = vm_allocate(mach_task_self(), &loadAddress, (vm_size_t)totalVMSize, VM_FLAGS_ANYWHERE);
448 if ( r != KERN_SUCCESS ) {
449 diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r);
450 close(fd);
451 return;
452 }
453
454 if ( sliceOffset != 0 )
455 _logSegments("dyld: Mapping %s (slice offset=%llu)\n", image->path(), sliceOffset);
456 else
457 _logSegments("dyld: Mapping %s\n", image->path());
458
459 // map each segment
460 __block bool mmapFailure = false;
461 __block const uint8_t* codeSignatureStartAddress = nullptr;
462 __block const uint8_t* linkeditEndAddress = nullptr;
463 __block bool mappedFirstSegment = false;
464 image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
465 // <rdar://problem/32363581> Mapping zero filled segments fails with mmap of size 0
466 if ( fileSize == 0 )
467 return;
468 void* segAddress = mmap((void*)(loadAddress+vmOffset), fileSize, permissions, MAP_FIXED | MAP_PRIVATE, fd, sliceOffset+fileOffset);
469 int mmapErr = errno;
470 if ( segAddress == MAP_FAILED ) {
471 if ( mmapErr == EPERM ) {
472 if ( sandboxBlockedMmap(image->path()) )
473 diag.error("file system sandbox blocked mmap() of '%s'", image->path());
474 else
475 diag.error("code signing blocked mmap() of '%s'", image->path());
476 }
477 else {
478 diag.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress+vmOffset, fileSize, mmapErr, image->path());
479 }
480 mmapFailure = true;
481 stop = true;
482 }
483 else if ( codeSignFileOffset > fileOffset ) {
484 codeSignatureStartAddress = (uint8_t*)segAddress + (codeSignFileOffset-fileOffset);
485 linkeditEndAddress = (uint8_t*)segAddress + vmSize;
486 }
487 // sanity check first segment is mach-o header
488 if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) {
489 mappedFirstSegment = true;
490 const MachOFile* mf = (MachOFile*)segAddress;
491 if ( !mf->isMachO(diag, fileSize) ) {
492 mmapFailure = true;
493 stop = true;
494 }
495 }
496 if ( !mmapFailure ) {
497 const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
498 _logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", lmo->segmentName(segIndex),
499 (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
500 (long)segAddress, (long)segAddress+(long)vmSize-1);
501 }
502 });
503 if ( mmapFailure ) {
504 ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
505 ::close(fd);
506 return;
507 }
508
509 // close file
510 close(fd);
511
512 #if BUILDING_LIBDYLD
513 // verify file has not changed since closure was built by checking code signature has not changed
514 uint8_t cdHashExpected[20];
515 if ( image->hasCdHash(cdHashExpected) ) {
516 if ( codeSignatureStartAddress == nullptr ) {
517 diag.error("code signature missing");
518 }
519 else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) {
520 diag.error("code signature extends beyond end of __LINKEDIT");
521 }
522 else {
523 uint8_t cdHashFound[20];
524 const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
525 if ( lmo->cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHashFound) ) {
526 if ( ::memcmp(cdHashFound, cdHashExpected, 20) != 0 )
527 diag.error("code signature changed since closure was built");
528 }
529 else {
530 diag.error("code signature format invalid");
531 }
532 }
533 if ( diag.hasError() ) {
534 ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
535 return;
536 }
537 }
538 #endif
539
540 #if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
541 // tell kernel about fairplay encrypted regions
542 uint32_t fpTextOffset;
543 uint32_t fpSize;
544 if ( image->isFairPlayEncrypted(fpTextOffset, fpSize) ) {
545 const mach_header* mh = (mach_header*)loadAddress;
546 int result = ::mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype);
547 if ( result != 0 ) {
548 diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
549 ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
550 return;
551 }
552 }
553 #endif
554
555 _logLoads("dyld: load %s\n", image->path());
556
557 timer.setData4((uint64_t)loadAddress);
558 info.setLoadedAddress((MachOLoaded*)loadAddress);
559 info.setState(LoadedImage::State::mapped);
560 }
561
562 void Loader::unmapImage(LoadedImage& info)
563 {
564 assert(info.loadedAddress() != nullptr);
565 ::vm_deallocate(mach_task_self(), (vm_address_t)info.loadedAddress(), (vm_size_t)(info.image()->vmSizeToMap()));
566 info.setLoadedAddress(nullptr);
567 }
568
569 void Loader::registerDOFs(const Array<DOFInfo>& dofs)
570 {
571 if ( dofs.empty() )
572 return;
573
574 int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
575 if ( fd < 0 ) {
576 _logDofs("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n");
577 }
578 else {
579 // allocate a buffer on the stack for the variable length dof_ioctl_data_t type
580 uint8_t buffer[sizeof(dof_ioctl_data_t) + dofs.count()*sizeof(dof_helper_t)];
581 dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer;
582
583 // fill in buffer with one dof_helper_t per DOF section
584 ioctlData->dofiod_count = dofs.count();
585 for (unsigned int i=0; i < dofs.count(); ++i) {
586 strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN);
587 ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof);
588 ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof);
589 }
590
591 // tell kernel about all DOF sections en mas
592 // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel
593 user_addr_t val = (user_addr_t)(unsigned long)ioctlData;
594 if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) {
595 // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field.
596 // Note, the closure marked the image as being never unload, so we don't need to keep the ID around
597 // or support unregistering it later.
598 for (unsigned int i=0; i < dofs.count(); ++i) {
599 _logDofs("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n",
600 dofs[i].dof, dofs[i].imageShortName, (int)(ioctlData->dofiod_helpers[i].dofhp_dof));
601 }
602 }
603 else {
604 _logDofs("dyld: ioctl to register dtrace DOF section failed\n");
605 }
606 close(fd);
607 }
608 }
609
610 bool Loader::dtraceUserProbesEnabled()
611 {
612 #if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
613 int dof_mode;
614 size_t dof_mode_size = sizeof(dof_mode);
615 if ( sysctlbyname("kern.dtrace.dof_mode", &dof_mode, &dof_mode_size, nullptr, 0) == 0 ) {
616 return ( dof_mode != 0 );
617 }
618 return false;
619 #else
620 // dtrace is always available for macOS and simulators
621 return true;
622 #endif
623 }
624
625
626 void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
627 {
628 #if __arm__ || __arm64__
629 // <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots
630 logger("vm.footprint_suspend=%d\n", suspend);
631 int newValue = suspend ? 1 : 0;
632 int oldValue = 0;
633 size_t newlen = sizeof(newValue);
634 size_t oldlen = sizeof(oldValue);
635 sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
636 #endif
637 }
638
639 void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
640 {
641 dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0);
642 closure::ImageNum cacheImageNum;
643 const char* leafName = info.image()->leafName();
644 const closure::Image* image = info.image();
645 const uint8_t* imageLoadAddress = (uint8_t*)info.loadedAddress();
646 uintptr_t slide = info.loadedAddress()->getSlide();
647 bool overrideOfCache = info.image()->isOverrideOfDyldCacheImage(cacheImageNum);
648 if ( overrideOfCache )
649 vmAccountingSetSuspended(true, _logFixups);
650 image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
651 uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
652 *fixUpLoc += slide;
653 _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
654 },
655 ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool &stop) {
656 uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
657 uintptr_t value = resolveTarget(bindTarget);
658 _logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
659 *fixUpLoc = value;
660 },
661 ^(uint64_t imageOffsetStart, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
662 // walk each fixup in the chain
663 image->forEachChainedFixup((void*)imageLoadAddress, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
664 if ( fixupInfo.authRebase.auth ) {
665 #if __has_feature(ptrauth_calls)
666 if ( fixupInfo.authBind.bind ) {
667 closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
668 uint64_t targetAddr = resolveTarget(bindTarget);
669 // Don't sign missing weak imports.
670 if (targetAddr != 0)
671 targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
672 _logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
673 fixupLoc, (void*)targetAddr, fixupInfo.authBind.diversity, fixupInfo.authBind.addrDiv, fixupInfo.authBind.keyName());
674 *fixupLoc = targetAddr;
675 }
676 else {
677 uint64_t targetAddr = (uint64_t)imageLoadAddress + fixupInfo.authRebase.target;
678 targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
679 _logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
680 fixupLoc, (void*)targetAddr, fixupInfo.authRebase.diversity, fixupInfo.authRebase.addrDiv, fixupInfo.authRebase.keyName());
681 *fixupLoc = targetAddr;
682 }
683 #else
684 diag.error("malformed chained pointer");
685 stop = true;
686 stopChain = true;
687 #endif
688 }
689 else {
690 if ( fixupInfo.plainRebase.bind ) {
691 closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
692 uint64_t targetAddr = resolveTarget(bindTarget) + fixupInfo.plainBind.signExtendedAddend();
693 _logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixupLoc, (void*)targetAddr);
694 *fixupLoc = targetAddr;
695 }
696 else {
697 uint64_t targetAddr = fixupInfo.plainRebase.signExtendedTarget() + slide;
698 _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixupLoc, (void*)slide);
699 *fixupLoc = targetAddr;
700 }
701 }
702 });
703 });
704
705 #if __i386__
706 __block bool segmentsMadeWritable = false;
707 image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool& stop) {
708 if ( !segmentsMadeWritable )
709 setSegmentProtects(info, true);
710 uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
711 *fixUpLoc += slide;
712 _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
713 },
714 ^(uint32_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
715 // FIXME
716 });
717 if ( segmentsMadeWritable )
718 setSegmentProtects(info, false);
719 #endif
720
721 if ( overrideOfCache )
722 vmAccountingSetSuspended(false, _logFixups);
723 }
724
725 #if __i386__
726 void Loader::setSegmentProtects(const LoadedImage& info, bool write)
727 {
728 info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) {
729 if ( protections & VM_PROT_WRITE )
730 return;
731 uint32_t regionProt = protections;
732 if ( write )
733 regionProt = VM_PROT_WRITE | VM_PROT_READ;
734 kern_return_t r = vm_protect(mach_task_self(), ((uintptr_t)info.loadedAddress())+(uintptr_t)vmOffset, (uintptr_t)vmSize, false, regionProt);
735 assert( r == KERN_SUCCESS );
736 });
737 }
738 #endif
739
740 #if BUILDING_DYLD
741 void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop))
742 {
743 bool stop = false;
744 const char* const eof = &buffer[bufferLen];
745 for (const char* s = buffer; s < eof; ++s) {
746 char lineBuffer[MAXPATHLEN];
747 char* t = lineBuffer;
748 char* tEnd = &lineBuffer[MAXPATHLEN];
749 while ( (s < eof) && (t != tEnd) ) {
750 if ( *s == '\n' )
751 break;
752 *t++ = *s++;
753 }
754 *t = '\0';
755 lineHandler(lineBuffer, stop);
756 if ( stop )
757 break;
758 }
759 }
760
761 void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop))
762 {
763 int fd = dyld::my_open(path, O_RDONLY, 0);
764 if ( fd != -1 ) {
765 struct stat statBuf;
766 if ( fstat(fd, &statBuf) == 0 ) {
767 const char* lines = (const char*)mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
768 if ( lines != MAP_FAILED ) {
769 forEachLineInFile(lines, (size_t)statBuf.st_size, lineHandler);
770 munmap((void*)lines, (size_t)statBuf.st_size);
771 }
772 }
773 close(fd);
774 }
775 }
776
777
778 bool internalInstall()
779 {
780 #if TARGET_IPHONE_SIMULATOR
781 return false;
782 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
783 uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
784 return ( (devFlags & 1) == 1 );
785 #else
786 return ( csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0 );
787 #endif
788 }
789
790 /* Checks to see if there are any args that impact dyld. These args
791 * can be set sevaral ways. These will only be honored on development
792 * and Apple Internal builds.
793 *
794 * First the existence of a file is checked for:
795 * /S/L/C/com.apple.dyld/dyld-bootargs
796 * If it exists it will be mapped and scanned line by line. If the executable
797 * exists in the file then the arguments on its line will be applied. "*" may
798 * be used a wildcard to represent all apps. First matching line will be used,
799 * the wild card must be one the last line. Additionally, lines must end with
800 * a "\n"
801 *
802 *
803 * SAMPLE FILE:
804
805 /bin/ls:force_dyld2=1
806 /usr/bin/sw_vers:force_dyld2=1
807 *:force_dyld3=1
808 EOL
809
810 If no file exists then the kernel boot-args will be scanned.
811 */
812 bool bootArgsContains(const char* arg)
813 {
814 //FIXME: Use strnstr(). Unfortunately we are missing an imp libc.
815 #if TARGET_IPHONE_SIMULATOR
816 return false;
817 #else
818 // don't check for boot-args on customer installs
819 if ( !internalInstall() )
820 return false;
821
822 char pathBuffer[MAXPATHLEN+1];
823 #if __IPHONE_OS_VERSION_MIN_REQUIRED
824 strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
825 #else
826 strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
827 #endif
828 strlcat(pathBuffer, "dyld-bootargs", MAXPATHLEN+1);
829 __block bool result = false;
830 forEachLineInFile(pathBuffer, ^(const char* line, bool& stop) {
831 const char* delim = strchr(line, ':');
832 if ( delim == nullptr )
833 return;
834 char binary[MAXPATHLEN];
835 char options[MAXPATHLEN];
836 strlcpy(binary, line, MAXPATHLEN);
837 binary[delim-line] = '\0';
838 strlcpy(options, delim+1, MAXPATHLEN);
839 if ( (strcmp(dyld::getExecutablePath(), binary) == 0) || (strcmp("*", binary) == 0) ) {
840 result = (strstr(options, arg) != nullptr);
841 return;
842 }
843 });
844
845 // get length of full boot-args string
846 size_t len;
847 if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 )
848 return false;
849
850 // get copy of boot-args string
851 char bootArgsBuffer[len];
852 if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 )
853 return false;
854
855 // return true if 'arg' is a sub-string of boot-args
856 return (strstr(bootArgsBuffer, arg) != nullptr);
857 #endif
858 }
859 #endif
860
861 #if BUILDING_LIBDYLD
862 // hack because libdyld.dylib should not link with libc++.dylib
863 extern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden")));
864 void __cxa_pure_virtual()
865 {
866 abort();
867 }
868 #endif
869
870 } // namespace dyld3
871
872
873
874
875