2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
30 #include <uuid/uuid.h>
31 #include <mach/mach.h>
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
36 #include <sys/dtrace.h>
37 #include <sys/errno.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>
45 #include <sandbox/private.h>
46 #include <dispatch/dispatch.h>
47 #include <mach/vm_page_size.h>
49 #include "MachOFile.h"
50 #include "MachOLoaded.h"
55 #include "dyld_cache_format.h"
59 void log(const char* m
, ...);
65 // utility to track a set of ImageNum's in use
66 class VIS_HIDDEN ImageNumSet
69 void add(dyld3::closure::ImageNum num
);
70 bool contains(dyld3::closure::ImageNum num
) const;
73 std::bitset
<5120> _bitmap
;
74 dyld3::OverflowSafeArray
<dyld3::closure::ImageNum
> _overflowArray
;
77 void ImageNumSet::add(dyld3::closure::ImageNum num
)
82 _overflowArray
.push_back(num
);
85 bool ImageNumSet::contains(dyld3::closure::ImageNum num
) const
88 return _bitmap
.test(num
);
90 for (dyld3::closure::ImageNum existingNum
: _overflowArray
) {
91 if ( existingNum
== num
)
96 } // namespace anonymous
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
)
108 void Loader::addImage(const LoadedImage
& li
)
110 _allImages
.push_back(li
);
113 LoadedImage
* Loader::findImage(closure::ImageNum targetImageNum
)
115 for (LoadedImage
& info
: _allImages
) {
116 if ( info
.image()->representsImageNum(targetImageNum
) )
122 uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target
)
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
;
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
;
135 case closure::Image::ResolvedSymbolTarget::kindAbsolute
:
136 if ( target
.absolute
.value
& (1ULL << 62) )
137 return (uintptr_t)(target
.absolute
.value
| 0xC000000000000000ULL
);
139 return (uintptr_t)target
.absolute
.value
;
141 assert(0 && "malformed ResolvedSymbolTarget");
146 void Loader::completeAllDependents(Diagnostics
& diag
, uintptr_t topIndex
)
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
)
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
);
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());
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
;
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");
193 _allImages
.push_back(LoadedImage::make(depImage
));
195 alreadyLoaded
.add(depImageNum
);
198 diag
.error("unable to locate imageNum=0x%04X, depIndex=%d of %s", depImageNum
, depIndex
, image
->path());
207 void Loader::mapAndFixupAllImages(Diagnostics
& diag
, bool processDOFs
, bool fromOFI
, uintptr_t topIndex
)
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) ) {
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' : '.' ,
228 // skip over ones already loaded
231 if ( info
.image()->inDyldCache() ) {
232 if ( info
.image()->overridableDylib() ) {
234 if ( stat(info
.image()->path(), &statBuf
) == 0 ) {
235 // verify file has not changed since closure was built
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());
244 diag
.error("dylib file not expected on disk, must be a root '%s'", info
.image()->path());
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());
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);
264 mapImage(diag
, info
, fromOFI
);
265 if ( diag
.hasError() )
266 break; // out of for loop
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());
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() )
287 // previously loaded images were previously fixed up
288 if ( info
.state() < LoadedImage::State::fixedUp
) {
289 applyFixupsToImage(diag
, info
);
290 if ( diag
.hasError() )
292 info
.setState(LoadedImage::State::fixedUp
);
296 // find and register dtrace DOFs
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
) {
303 dofInfo
.dof
= section
;
304 dofInfo
.imageHeader
= info
.loadedAddress();
305 dofInfo
.imageShortName
= info
.image()->leafName();
306 dofImages
.push_back(dofInfo
);
309 registerDOFs(dofImages
);
313 bool Loader::sandboxBlocked(const char* path
, const char* kind
)
315 #if TARGET_IPHONE_SIMULATOR
316 // sandbox calls not yet supported in dyld_sim
319 sandbox_filter_type filter
= (sandbox_filter_type
)(SANDBOX_FILTER_PATH
| SANDBOX_CHECK_NO_REPORT
);
320 return ( sandbox_check(getpid(), kind
, filter
, path
) > 0 );
324 bool Loader::sandboxBlockedMmap(const char* path
)
326 return sandboxBlocked(path
, "file-map-executable");
329 bool Loader::sandboxBlockedOpen(const char* path
)
331 return sandboxBlocked(path
, "file-read-data");
334 bool Loader::sandboxBlockedStat(const char* path
)
336 return sandboxBlocked(path
, "file-read-metadata");
339 void Loader::mapImage(Diagnostics
& diag
, LoadedImage
& info
, bool fromOFI
)
341 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_MAP_IMAGE
, info
.image()->path(), 0, 0);
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
);
351 int fd
= ::open(info
.image()->path(), O_RDONLY
, 0);
354 if ( (openErr
== EPERM
) && sandboxBlockedOpen(image
->path()) )
355 diag
.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image
->path());
357 diag
.error("open(\"%s\", O_RDONLY) failed with errno=%d", image
->path(), openErr
);
363 #if TARGET_IPHONE_SIMULATOR
364 if ( stat(image
->path(), &statBuf
) != 0 ) {
366 if ( fstat(fd
, &statBuf
) != 0 ) {
369 if ( (statErr
== EPERM
) && sandboxBlockedStat(image
->path()) )
370 diag
.error("file system sandbox blocked stat(\"%s\")", image
->path());
372 diag
.error("stat(\"%s\") failed with errno=%d", image
->path(), statErr
);
377 // verify file has not changed since closure was built
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());
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
) ) {
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());
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());
420 coveredCodeLength
= siginfo
.fs_file_start
;
421 if ( coveredCodeLength
< codeSignFileOffset
) {
422 diag
.error("code signature does not cover entire file up to signature");
428 // <rdar://problem/41015217> dyld should use F_CHECK_LV even on unsigned binaries
430 // <rdar://problem/32684903> always call F_CHECK_LV to preflight
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
);
439 diag
.error("code signature in (%s) not valid for use in process: %s", image
->path(), messageBuffer
);
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
);
454 if ( sliceOffset
!= 0 )
455 _logSegments("dyld: Mapping %s (slice offset=%llu)\n", image
->path(), sliceOffset
);
457 _logSegments("dyld: Mapping %s\n", image
->path());
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
468 void* segAddress
= mmap((void*)(loadAddress
+vmOffset
), fileSize
, permissions
, MAP_FIXED
| MAP_PRIVATE
, fd
, sliceOffset
+fileOffset
);
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());
475 diag
.error("code signing blocked mmap() of '%s'", image
->path());
478 diag
.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress
+vmOffset
, fileSize
, mmapErr
, image
->path());
483 else if ( codeSignFileOffset
> fileOffset
) {
484 codeSignatureStartAddress
= (uint8_t*)segAddress
+ (codeSignFileOffset
-fileOffset
);
485 linkeditEndAddress
= (uint8_t*)segAddress
+ vmSize
;
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
) ) {
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);
504 ::vm_deallocate(mach_task_self(), loadAddress
, (vm_size_t
)totalVMSize
);
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");
519 else if ( codeSignatureStartAddress
+codeSignFileSize
> linkeditEndAddress
) {
520 diag
.error("code signature extends beyond end of __LINKEDIT");
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");
530 diag
.error("code signature format invalid");
533 if ( diag
.hasError() ) {
534 ::vm_deallocate(mach_task_self(), loadAddress
, (vm_size_t
)totalVMSize
);
540 #if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
541 // tell kernel about fairplay encrypted regions
542 uint32_t fpTextOffset
;
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
);
548 diag
.error("could not register fairplay decryption, mremap_encrypted() => %d", result
);
549 ::vm_deallocate(mach_task_self(), loadAddress
, (vm_size_t
)totalVMSize
);
555 _logLoads("dyld: load %s\n", image
->path());
557 timer
.setData4((uint64_t)loadAddress
);
558 info
.setLoadedAddress((MachOLoaded
*)loadAddress
);
559 info
.setState(LoadedImage::State::mapped
);
562 void Loader::unmapImage(LoadedImage
& info
)
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);
569 void Loader::registerDOFs(const Array
<DOFInfo
>& dofs
)
574 int fd
= open("/dev/" DTRACEMNR_HELPER
, O_RDWR
);
576 _logDofs("can't open /dev/" DTRACEMNR_HELPER
" to register dtrace DOF sections\n");
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
;
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
);
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
));
604 _logDofs("dyld: ioctl to register dtrace DOF section failed\n");
610 bool Loader::dtraceUserProbesEnabled()
612 #if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
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 );
620 // dtrace is always available for macOS and simulators
626 void Loader::vmAccountingSetSuspended(bool suspend
, LogFunc logger
)
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;
633 size_t newlen
= sizeof(newValue
);
634 size_t oldlen
= sizeof(oldValue
);
635 sysctlbyname("vm.footprint_suspend", &oldValue
, &oldlen
, &newValue
, newlen
);
639 void Loader::applyFixupsToImage(Diagnostics
& diag
, LoadedImage
& info
)
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
);
653 _logFixups("dyld: fixup: %s:%p += %p\n", leafName
, fixUpLoc
, (void*)slide
);
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
);
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.
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
;
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
;
684 diag
.error("malformed chained pointer");
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
;
697 uint64_t targetAddr
= fixupInfo
.plainRebase
.signExtendedTarget() + slide
;
698 _logFixups("dyld: fixup: %s:%p += %p\n", leafName
, fixupLoc
, (void*)slide
);
699 *fixupLoc
= targetAddr
;
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
);
712 _logFixups("dyld: fixup: %s:%p += %p\n", leafName
, fixUpLoc
, (void*)slide
);
714 ^(uint32_t imageOffsetToBind
, closure::Image::ResolvedSymbolTarget bindTarget
, bool& stop
) {
717 if ( segmentsMadeWritable
)
718 setSegmentProtects(info
, false);
721 if ( overrideOfCache
)
722 vmAccountingSetSuspended(false, _logFixups
);
726 void Loader::setSegmentProtects(const LoadedImage
& info
, bool write
)
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
)
731 uint32_t regionProt
= protections
;
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
);
741 void forEachLineInFile(const char* buffer
, size_t bufferLen
, void (^lineHandler
)(const char* line
, bool& stop
))
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
) ) {
755 lineHandler(lineBuffer
, stop
);
761 void forEachLineInFile(const char* path
, void (^lineHandler
)(const char* line
, bool& stop
))
763 int fd
= dyld::my_open(path
, O_RDONLY
, 0);
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
);
778 bool internalInstall()
780 #if TARGET_IPHONE_SIMULATOR
782 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
783 uint32_t devFlags
= *((uint32_t*)_COMM_PAGE_DEV_FIRM
);
784 return ( (devFlags
& 1) == 1 );
786 return ( csr_check(CSR_ALLOW_APPLE_INTERNAL
) == 0 );
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.
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
805 /bin/ls:force_dyld2=1
806 /usr/bin/sw_vers:force_dyld2=1
810 If no file exists then the kernel boot-args will be scanned.
812 bool bootArgsContains(const char* arg
)
814 //FIXME: Use strnstr(). Unfortunately we are missing an imp libc.
815 #if TARGET_IPHONE_SIMULATOR
818 // don't check for boot-args on customer installs
819 if ( !internalInstall() )
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
));
826 strlcpy(pathBuffer
, MACOSX_DYLD_SHARED_CACHE_DIR
, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR
));
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 )
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);
845 // get length of full boot-args string
847 if ( sysctlbyname("kern.bootargs", NULL
, &len
, NULL
, 0) != 0 )
850 // get copy of boot-args string
851 char bootArgsBuffer
[len
];
852 if ( sysctlbyname("kern.bootargs", bootArgsBuffer
, &len
, NULL
, 0) != 0 )
855 // return true if 'arg' is a sub-string of boot-args
856 return (strstr(bootArgsBuffer
, arg
) != nullptr);
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()