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@
29 #include <uuid/uuid.h>
30 #include <mach/mach.h>
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
35 #include <sys/dtrace.h>
36 #include <sys/errno.h>
38 #include <System/sys/mman.h>
39 #include <System/sys/csr.h>
40 #include <System/machine/cpu_capabilities.h>
41 #include <bootstrap.h>
42 #include <CommonCrypto/CommonDigest.h>
44 #include <sandbox/private.h>
45 #include <dispatch/dispatch.h>
47 #include "LaunchCache.h"
48 #include "LaunchCacheFormat.h"
51 #include "MachOParser.h"
53 #include "dyld_cache_format.h"
56 #include "closuredProtocol.h"
60 void log(const char* m
, ...);
68 static bool sandboxBlocked(const char* path
, const char* kind
)
70 #if BUILDING_LIBDYLD || !TARGET_IPHONE_SIMULATOR
71 sandbox_filter_type filter
= (sandbox_filter_type
)(SANDBOX_FILTER_PATH
| SANDBOX_CHECK_NO_REPORT
);
72 return ( sandbox_check(getpid(), kind
, filter
, path
) > 0 );
74 // sandbox calls not yet supported in dyld_sim
79 static bool sandboxBlockedMmap(const char* path
)
81 return sandboxBlocked(path
, "file-map-executable");
84 static bool sandboxBlockedOpen(const char* path
)
86 return sandboxBlocked(path
, "file-read-data");
89 static bool sandboxBlockedStat(const char* path
)
91 return sandboxBlocked(path
, "file-read-metadata");
95 static uint64_t pageAlign(uint64_t value
)
97 return (value
+ 4095) & (-4096);
101 static void updateSliceOffset(uint64_t& sliceOffset
, uint64_t codeSignEndOffset
, size_t fileLen
)
104 if ( sliceOffset
!= 0 ) {
105 if ( pageAlign(codeSignEndOffset
) == pageAlign(fileLen
) ) {
106 // cache builder saw fat file, but file is now thin
114 static const mach_header
* mapImage(const dyld3::launch_cache::Image image
, Diagnostics
& diag
, LogFunc log_loads
, LogFunc log_segments
)
116 uint64_t sliceOffset
= image
.sliceOffsetInFile();
117 const uint64_t totalVMSize
= image
.vmSizeToMap();
118 const uint32_t codeSignFileOffset
= image
.asDiskImage()->codeSignFileOffset
;
119 const uint32_t codeSignFileSize
= image
.asDiskImage()->codeSignFileSize
;
122 int fd
= ::open(image
.path(), O_RDONLY
, 0);
125 if ( (openErr
== EPERM
) && sandboxBlockedOpen(image
.path()) )
126 diag
.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image
.path());
128 diag
.error("open(\"%s\", O_RDONLY) failed with errno=%d", image
.path(), openErr
);
134 #if TARGET_IPHONE_SIMULATOR
135 if ( stat(image
.path(), &statBuf
) != 0 ) {
137 if ( fstat(fd
, &statBuf
) != 0 ) {
140 if ( (statErr
== EPERM
) && sandboxBlockedStat(image
.path()) )
141 diag
.error("file system sandbox blocked stat(\"%s\")", image
.path());
143 diag
.error("stat(\"%s\") failed with errno=%d", image
.path(), statErr
);
148 // verify file has not changed since closure was built
149 if ( image
.validateUsingModTimeAndInode() ) {
150 if ( (statBuf
.st_mtime
!= image
.fileModTime()) || (statBuf
.st_ino
!= image
.fileINode()) ) {
151 diag
.error("file mtime/inode changed since closure was built for '%s'", image
.path());
157 // handle OS dylibs being thinned after closure was built
158 if ( image
.group().groupNum() == 1 )
159 updateSliceOffset(sliceOffset
, codeSignFileOffset
+codeSignFileSize
, (size_t)statBuf
.st_size
);
161 // register code signature
162 uint64_t coveredCodeLength
= UINT64_MAX
;
163 if ( codeSignFileOffset
!= 0 ) {
164 fsignatures_t siginfo
;
165 siginfo
.fs_file_start
= sliceOffset
; // start of mach-o slice in fat file
166 siginfo
.fs_blob_start
= (void*)(long)(codeSignFileOffset
); // start of CD in mach-o file
167 siginfo
.fs_blob_size
= codeSignFileSize
; // size of CD
168 int result
= fcntl(fd
, F_ADDFILESIGS_RETURN
, &siginfo
);
169 if ( result
== -1 ) {
170 int errnoCopy
= errno
;
171 if ( (errnoCopy
== EPERM
) || (errnoCopy
== EBADEXEC
) ) {
172 diag
.error("code signature invalid (errno=%d) sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
173 errnoCopy
, sliceOffset
, codeSignFileOffset
, codeSignFileSize
, image
.path());
176 diag
.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
177 errnoCopy
, sliceOffset
, codeSignFileOffset
, codeSignFileSize
, image
.path());
182 coveredCodeLength
= siginfo
.fs_file_start
;
183 if ( coveredCodeLength
< image
.asDiskImage()->codeSignFileOffset
) {
184 diag
.error("code signature does not cover entire file up to signature");
189 // <rdar://problem/32684903> always call F_CHECK_LV to preflight
191 char messageBuffer
[512];
192 messageBuffer
[0] = '\0';
193 checkInfo
.lv_file_start
= sliceOffset
;
194 checkInfo
.lv_error_message_size
= sizeof(messageBuffer
);
195 checkInfo
.lv_error_message
= messageBuffer
;
196 int res
= fcntl(fd
, F_CHECK_LV
, &checkInfo
);
198 diag
.error("code signature in (%s) not valid for use in process: %s", image
.path(), messageBuffer
);
204 // reserve address range
205 vm_address_t loadAddress
= 0;
206 kern_return_t r
= vm_allocate(mach_task_self(), &loadAddress
, (vm_size_t
)totalVMSize
, VM_FLAGS_ANYWHERE
);
207 if ( r
!= KERN_SUCCESS
) {
208 diag
.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize
, r
);
213 if ( sliceOffset
!= 0 )
214 log_segments("dyld: Mapping %s (slice offset=%llu)\n", image
.path(), sliceOffset
);
216 log_segments("dyld: Mapping %s\n", image
.path());
219 __block
bool mmapFailure
= false;
220 __block
const uint8_t* codeSignatureStartAddress
= nullptr;
221 __block
const uint8_t* linkeditEndAddress
= nullptr;
222 __block
bool mappedFirstSegment
= false;
223 image
.forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool& stop
) {
224 // <rdar://problem/32363581> Mapping zero filled segments fails with mmap of size 0
227 void* segAddress
= mmap((void*)(loadAddress
+vmOffset
), fileSize
, permissions
, MAP_FIXED
| MAP_PRIVATE
, fd
, sliceOffset
+fileOffset
);
229 if ( segAddress
== MAP_FAILED
) {
230 if ( mmapErr
== EPERM
) {
231 if ( sandboxBlockedMmap(image
.path()) )
232 diag
.error("file system sandbox blocked mmap() of '%s'", image
.path());
234 diag
.error("code signing blocked mmap() of '%s'", image
.path());
237 diag
.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress
+vmOffset
, fileSize
, mmapErr
, image
.path());
242 else if ( codeSignFileOffset
> fileOffset
) {
243 codeSignatureStartAddress
= (uint8_t*)segAddress
+ (codeSignFileOffset
-fileOffset
);
244 linkeditEndAddress
= (uint8_t*)segAddress
+ vmSize
;
246 // sanity check first segment is mach-o header
247 if ( (segAddress
!= MAP_FAILED
) && !mappedFirstSegment
) {
248 mappedFirstSegment
= true;
249 if ( !MachOParser::isMachO(diag
, segAddress
, fileSize
) ) {
254 if ( !mmapFailure
) {
255 MachOParser
parser((mach_header
*)loadAddress
);
256 log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser
.segmentName(segIndex
),
257 (permissions
& PROT_READ
) ? 'r' : '.', (permissions
& PROT_WRITE
) ? 'w' : '.', (permissions
& PROT_EXEC
) ? 'x' : '.' ,
258 (long)segAddress
, (long)segAddress
+vmSize
-1);
262 vm_deallocate(mach_task_self(), loadAddress
, (vm_size_t
)totalVMSize
);
271 // verify file has not changed since closure was built by checking code signature has not changed
272 if ( image
.validateUsingCdHash() ) {
273 if ( codeSignatureStartAddress
== nullptr ) {
274 diag
.error("code signature missing");
276 else if ( codeSignatureStartAddress
+codeSignFileSize
> linkeditEndAddress
) {
277 diag
.error("code signature extends beyond end of __LINKEDIT");
281 if ( MachOParser::cdHashOfCodeSignature(codeSignatureStartAddress
, codeSignFileSize
, cdHash
) ) {
282 if ( memcmp(image
.cdHash16(), cdHash
, 16) != 0 )
283 diag
.error("code signature changed since closure was built");
286 diag
.error("code signature format invalid");
289 if ( diag
.hasError() ) {
290 vm_deallocate(mach_task_self(), loadAddress
, (vm_size_t
)totalVMSize
);
296 #if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
297 // tell kernel about fairplay encrypted regions
298 uint32_t fpTextOffset
;
300 if ( image
.isFairPlayEncrypted(fpTextOffset
, fpSize
) ) {
301 const mach_header
* mh
= (mach_header
*)loadAddress
;
302 int result
= mremap_encrypted(((uint8_t*)mh
) + fpTextOffset
, fpSize
, 1, mh
->cputype
, mh
->cpusubtype
);
303 diag
.error("could not register fairplay decryption, mremap_encrypted() => %d", result
);
304 vm_deallocate(mach_task_self(), loadAddress
, (vm_size_t
)totalVMSize
);
309 log_loads("dyld: load %s\n", image
.path());
311 return (mach_header
*)loadAddress
;
315 void unmapImage(const launch_cache::binary_format::Image
* binImage
, const mach_header
* loadAddress
)
317 assert(loadAddress
!= nullptr);
318 launch_cache::Image
image(binImage
);
319 vm_deallocate(mach_task_self(), (vm_address_t
)loadAddress
, (vm_size_t
)(image
.vmSizeToMap()));
323 static void applyFixupsToImage(Diagnostics
& diag
, const mach_header
* imageMH
, const launch_cache::binary_format::Image
* imageData
,
324 launch_cache::TargetSymbolValue::LoadedImages
& imageResolver
, LogFunc log_fixups
)
326 launch_cache::Image
image(imageData
);
327 MachOParser
imageParser(imageMH
);
328 // Note, these are cached here to avoid recalculating them on every loop iteration
329 const launch_cache::ImageGroup
& imageGroup
= image
.group();
330 const char* leafName
= image
.leafName();
331 intptr_t slide
= imageParser
.getSlide();
332 image
.forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t protections
, bool& segStop
) {
333 if ( !image
.segmentHasFixups(segIndex
) )
335 const launch_cache::MemoryRange segContent
= { (char*)imageMH
+ vmOffset
, vmSize
};
337 bool textRelocs
= ((protections
& VM_PROT_WRITE
) == 0);
339 kern_return_t r
= vm_protect(mach_task_self(), (vm_address_t
)segContent
.address
, (vm_size_t
)segContent
.size
, false, VM_PROT_WRITE
| VM_PROT_READ
);
340 if ( r
!= KERN_SUCCESS
) {
341 diag
.error("vm_protect() failed trying to make text segment writable, result=%d", r
);
346 if ( (protections
& VM_PROT_WRITE
) == 0 ) {
347 diag
.error("fixups found in non-writable segment of %s", image
.path());
351 image
.forEachFixup(segIndex
, segContent
, ^(uint64_t segOffset
, launch_cache::Image::FixupKind kind
, launch_cache::TargetSymbolValue targetValue
, bool& stop
) {
352 if ( segOffset
> segContent
.size
) {
353 diag
.error("fixup is past end of segment. segOffset=0x%0llX, segSize=0x%0llX, segIndex=%d", segOffset
, segContent
.size
, segIndex
);
357 uintptr_t* fixUpLoc
= (uintptr_t*)((char*)(segContent
.address
) + segOffset
);
363 //dyld::log("fixup loc=%p\n", fixUpLoc);
366 case launch_cache::Image::FixupKind::rebase64
:
368 case launch_cache::Image::FixupKind::rebase32
:
371 log_fixups("dyld: fixup: %s:%p += %p\n", leafName
, fixUpLoc
, (void*)slide
);
374 case launch_cache::Image::FixupKind::bind64
:
376 case launch_cache::Image::FixupKind::bind32
:
378 value
= targetValue
.resolveTarget(diag
, imageGroup
, imageResolver
);
379 log_fixups("dyld: fixup: %s:%p = %p\n", leafName
, fixUpLoc
, (void*)value
);
383 case launch_cache::Image::FixupKind::rebaseText32
:
384 log_fixups("dyld: text fixup: %s:%p += %p\n", leafName
, fixUpLoc
, (void*)slide
);
387 case launch_cache::Image::FixupKind::bindText32
:
388 value
= targetValue
.resolveTarget(diag
, imageGroup
, imageResolver
);
389 log_fixups("dyld: text fixup: %s:%p = %p\n", leafName
, fixUpLoc
, (void*)value
);
392 case launch_cache::Image::FixupKind::bindTextRel32
:
393 // CALL instruction uses pc-rel value
394 value
= targetValue
.resolveTarget(diag
, imageGroup
, imageResolver
);
395 log_fixups("dyld: CALL fixup: %s:%p = %p (pc+0x%08X)\n", leafName
, fixUpLoc
, (void*)value
, (value
- (uintptr_t)(fixUpLoc
)));
396 *fixUpLoc
= (value
- (uintptr_t)(fixUpLoc
));
398 case launch_cache::Image::FixupKind::bindImportJmp32
:
399 // JMP instruction in __IMPORT segment uses pc-rel value
400 jumpSlot
= (uint8_t*)fixUpLoc
;
401 value
= targetValue
.resolveTarget(diag
, imageGroup
, imageResolver
);
402 rel32
= (value
- ((uintptr_t)(fixUpLoc
)+5));
403 log_fixups("dyld: JMP fixup: %s:%p = %p (pc+0x%08X)\n", leafName
, fixUpLoc
, (void*)value
, rel32
);
404 jumpSlot
[0] = 0xE9; // JMP rel32
405 jumpSlot
[1] = rel32
& 0xFF;
406 jumpSlot
[2] = (rel32
>> 8) & 0xFF;
407 jumpSlot
[3] = (rel32
>> 16) & 0xFF;
408 jumpSlot
[4] = (rel32
>> 24) & 0xFF;
412 diag
.error("unknown fixup kind %d", kind
);
414 if ( diag
.hasError() )
419 kern_return_t r
= vm_protect(mach_task_self(), (vm_address_t
)segContent
.address
, (vm_size_t
)segContent
.size
, false, protections
);
420 if ( r
!= KERN_SUCCESS
) {
421 diag
.error("vm_protect() failed trying to make text segment non-writable, result=%d", r
);
431 class VIS_HIDDEN CurrentLoadImages
: public launch_cache::TargetSymbolValue::LoadedImages
434 CurrentLoadImages(launch_cache::DynArray
<ImageInfo
>& images
, const uint8_t* cacheAddr
)
435 : _dyldCacheLoadAddress(cacheAddr
), _images(images
) { }
437 virtual const uint8_t* dyldCacheLoadAddressForImage();
438 virtual const mach_header
* loadAddressFromGroupAndIndex(uint32_t groupNum
, uint32_t indexInGroup
);
439 virtual void forEachImage(void (^handler
)(uint32_t anIndex
, const launch_cache::binary_format::Image
*, const mach_header
*, bool& stop
));
440 virtual void setAsNeverUnload(uint32_t anIndex
) { _images
[anIndex
].neverUnload
= true; }
442 const uint8_t* _dyldCacheLoadAddress
;
443 launch_cache::DynArray
<ImageInfo
>& _images
;
446 const uint8_t* CurrentLoadImages::dyldCacheLoadAddressForImage()
448 return _dyldCacheLoadAddress
;
451 const mach_header
* CurrentLoadImages::loadAddressFromGroupAndIndex(uint32_t groupNum
, uint32_t indexInGroup
)
453 __block
const mach_header
* result
= nullptr;
454 forEachImage(^(uint32_t anIndex
, const launch_cache::binary_format::Image
* imageData
, const mach_header
* mh
, bool& stop
) {
455 launch_cache::Image
image(imageData
);
456 launch_cache::ImageGroup imageGroup
= image
.group();
457 if ( imageGroup
.groupNum() != groupNum
)
459 if ( imageGroup
.indexInGroup(imageData
) == indexInGroup
) {
467 void CurrentLoadImages::forEachImage(void (^handler
)(uint32_t anIndex
, const launch_cache::binary_format::Image
*, const mach_header
*, bool& stop
))
470 for (int i
=0; i
< _images
.count(); ++i
) {
471 ImageInfo
& info
= _images
[i
];
472 handler(i
, info
.imageData
, info
.loadAddress
, stop
);
480 const mach_header
* imageHeader
;
481 const char* imageShortName
;
484 static void registerDOFs(const DOFInfo
* dofs
, uint32_t dofSectionCount
, LogFunc log_dofs
)
486 if ( dofSectionCount
!= 0 ) {
487 int fd
= open("/dev/" DTRACEMNR_HELPER
, O_RDWR
);
489 log_dofs("can't open /dev/" DTRACEMNR_HELPER
" to register dtrace DOF sections\n");
492 // allocate a buffer on the stack for the variable length dof_ioctl_data_t type
493 uint8_t buffer
[sizeof(dof_ioctl_data_t
) + dofSectionCount
*sizeof(dof_helper_t
)];
494 dof_ioctl_data_t
* ioctlData
= (dof_ioctl_data_t
*)buffer
;
496 // fill in buffer with one dof_helper_t per DOF section
497 ioctlData
->dofiod_count
= dofSectionCount
;
498 for (unsigned int i
=0; i
< dofSectionCount
; ++i
) {
499 strlcpy(ioctlData
->dofiod_helpers
[i
].dofhp_mod
, dofs
[i
].imageShortName
, DTRACE_MODNAMELEN
);
500 ioctlData
->dofiod_helpers
[i
].dofhp_dof
= (uintptr_t)(dofs
[i
].dof
);
501 ioctlData
->dofiod_helpers
[i
].dofhp_addr
= (uintptr_t)(dofs
[i
].dof
);
504 // tell kernel about all DOF sections en mas
505 // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel
506 user_addr_t val
= (user_addr_t
)(unsigned long)ioctlData
;
507 if ( ioctl(fd
, DTRACEHIOC_ADDDOF
, &val
) != -1 ) {
508 // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field.
509 // Note, the closure marked the image as being never unload, so we don't need to keep the ID around
510 // or support unregistering it later.
511 for (unsigned int i
=0; i
< dofSectionCount
; ++i
) {
512 log_dofs("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n",
513 dofs
[i
].dof
, dofs
[i
].imageShortName
, (int)(ioctlData
->dofiod_helpers
[i
].dofhp_dof
));
517 //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n");
525 void mapAndFixupImages(Diagnostics
& diag
, launch_cache::DynArray
<ImageInfo
>& images
, const uint8_t* cacheLoadAddress
,
526 LogFunc log_loads
, LogFunc log_segments
, LogFunc log_fixups
, LogFunc log_dofs
)
528 // scan array and map images not already loaded
529 for (int i
=0; i
< images
.count(); ++i
) {
530 ImageInfo
& info
= images
[i
];
531 const dyld3::launch_cache::Image
image(info
.imageData
);
532 if ( info
.loadAddress
!= nullptr ) {
533 // log main executable's segments
534 if ( (info
.groupNum
== 2) && (info
.loadAddress
->filetype
== MH_EXECUTE
) && !info
.previouslyFixedUp
) {
535 if ( log_segments("dyld: mapped by kernel %s\n", image
.path()) ) {
536 MachOParser
parser(info
.loadAddress
);
537 image
.forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool& stop
) {
538 uint64_t start
= (long)info
.loadAddress
+ vmOffset
;
539 uint64_t end
= start
+vmSize
-1;
540 if ( (segIndex
== 0) && (permissions
== 0) ) {
543 log_segments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", parser
.segmentName(segIndex
),
544 (permissions
& PROT_READ
) ? 'r' : '.', (permissions
& PROT_WRITE
) ? 'w' : '.', (permissions
& PROT_EXEC
) ? 'x' : '.' ,
549 // skip over ones already loaded
552 if ( image
.isDiskImage() ) {
553 //dyld::log("need to load image[%d] %s\n", i, image.path());
554 info
.loadAddress
= mapImage(image
, diag
, log_loads
, log_segments
);
555 if ( diag
.hasError() ) {
556 break; // out of for loop
558 info
.justMapped
= true;
561 bool expectedOnDisk
= image
.group().dylibsExpectedOnDisk();
562 bool overridableDylib
= image
.overridableDylib();
563 if ( expectedOnDisk
|| overridableDylib
) {
565 if ( ::stat(image
.path(), &statBuf
) == 0 ) {
566 if ( expectedOnDisk
) {
567 // macOS case: verify dylib file info matches what it was when cache was built
568 if ( image
.fileModTime() != statBuf
.st_mtime
) {
569 diag
.error("cached dylib mod-time has changed, dylib cache has: 0x%08llX, file has: 0x%08lX, for: %s", image
.fileModTime(), (long)statBuf
.st_mtime
, image
.path());
570 break; // out of for loop
572 if ( image
.fileINode() != statBuf
.st_ino
) {
573 diag
.error("cached dylib inode has changed, dylib cache has: 0x%08llX, file has: 0x%08llX, for: %s", image
.fileINode(), statBuf
.st_ino
, image
.path());
574 break; // out of for loop
578 // iOS internal: dylib override installed
579 diag
.error("cached dylib overridden: %s", image
.path());
580 break; // out of for loop
584 if ( expectedOnDisk
) {
585 // macOS case: dylib that existed when cache built no longer exists
586 diag
.error("missing cached dylib: %s", image
.path());
587 break; // out of for loop
591 info
.loadAddress
= (mach_header
*)(cacheLoadAddress
+ image
.cacheOffset());
592 info
.justUsedFromDyldCache
= true;
593 if ( log_segments("dyld: Using from dyld cache %s\n", image
.path()) ) {
594 MachOParser
parser(info
.loadAddress
);
595 image
.forEachCacheSegment(^(uint32_t segIndex
, uint64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool &stop
) {
596 log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser
.segmentName(segIndex
),
597 (permissions
& PROT_READ
) ? 'r' : '.', (permissions
& PROT_WRITE
) ? 'w' : '.', (permissions
& PROT_EXEC
) ? 'x' : '.' ,
598 (long)cacheLoadAddress
+vmOffset
, (long)cacheLoadAddress
+vmOffset
+vmSize
-1);
603 if ( diag
.hasError() ) {
604 // back out and unmapped images all loaded so far
605 for (uint32_t j
=0; j
< images
.count(); ++j
) {
606 ImageInfo
& anInfo
= images
[j
];
607 if ( anInfo
.justMapped
)
608 unmapImage(anInfo
.imageData
, anInfo
.loadAddress
);
609 anInfo
.loadAddress
= nullptr;
615 CurrentLoadImages
fixupHelper(images
, cacheLoadAddress
);
616 for (int i
=0; i
< images
.count(); ++i
) {
617 ImageInfo
& info
= images
[i
];
618 // images in shared cache do not need fixups applied
619 launch_cache::Image
image(info
.imageData
);
620 if ( !image
.isDiskImage() )
622 // previously loaded images were previously fixed up
623 if ( info
.previouslyFixedUp
)
625 //dyld::log("apply fixups to mh=%p, path=%s\n", info.loadAddress, Image(info.imageData).path());
626 dyld3::loader::applyFixupsToImage(diag
, info
.loadAddress
, info
.imageData
, fixupHelper
, log_fixups
);
627 if ( diag
.hasError() )
631 // Record dtrace DOFs
632 // if ( /* FIXME! register dofs */ )
634 __block
uint32_t dofCount
= 0;
635 for (int i
=0; i
< images
.count(); ++i
) {
636 ImageInfo
& info
= images
[i
];
637 launch_cache::Image
image(info
.imageData
);
638 // previously loaded images were previously fixed up
639 if ( info
.previouslyFixedUp
)
641 image
.forEachDOF(nullptr, ^(const void* section
) {
642 // DOFs cause the image to be never-unload
643 assert(image
.neverUnload());
648 // struct RegisteredDOF { const mach_header* mh; int registrationID; };
649 DOFInfo dofImages
[dofCount
];
650 __block DOFInfo
* dofImagesBase
= dofImages
;
652 for (int i
=0; i
< images
.count(); ++i
) {
653 ImageInfo
& info
= images
[i
];
654 launch_cache::Image
image(info
.imageData
);
655 // previously loaded images were previously fixed up
656 if ( info
.previouslyFixedUp
)
658 image
.forEachDOF(info
.loadAddress
, ^(const void* section
) {
660 dofInfo
.dof
= section
;
661 dofInfo
.imageHeader
= info
.loadAddress
;
662 dofInfo
.imageShortName
= image
.leafName();
663 dofImagesBase
[dofCount
++] = dofInfo
;
666 registerDOFs(dofImages
, dofCount
, log_dofs
);
671 void forEachLineInFile(const char* path
, void (^lineHandler
)(const char* line
, bool& stop
))
673 int fd
= dyld::my_open(path
, O_RDONLY
, 0);
676 if ( fstat(fd
, &statBuf
) == 0 ) {
677 const char* lines
= (const char*)mmap(nullptr, (size_t)statBuf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
678 if ( lines
!= MAP_FAILED
) {
680 const char* const eof
= &lines
[statBuf
.st_size
];
681 for (const char* s
= lines
; s
< eof
; ++s
) {
682 char lineBuffer
[MAXPATHLEN
];
683 char* t
= lineBuffer
;
684 char* tEnd
= &lineBuffer
[MAXPATHLEN
];
685 while ( (s
< eof
) && (t
!= tEnd
) ) {
691 lineHandler(lineBuffer
, stop
);
695 munmap((void*)lines
, (size_t)statBuf
.st_size
);
703 bool internalInstall()
705 #if TARGET_IPHONE_SIMULATOR
707 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
708 uint32_t devFlags
= *((uint32_t*)_COMM_PAGE_DEV_FIRM
);
709 return ( (devFlags
& 1) == 1 );
711 return ( csr_check(CSR_ALLOW_APPLE_INTERNAL
) == 0 );
715 /* Checks to see if there are any args that impact dyld. These args
716 * can be set sevaral ways. These will only be honored on development
717 * and Apple Internal builds.
719 * First the existence of a file is checked for:
720 * /S/L/C/com.apple.dyld/dyld-bootargs
721 * If it exists it will be mapped and scanned line by line. If the executable
722 * exists in the file then the arguments on its line will be applied. "*" may
723 * be used a wildcard to represent all apps. First matching line will be used,
724 * the wild card must be one the last line. Additionally, lines must end with
730 /bin/ls:force_dyld2=1
731 /usr/bin/sw_vers:force_dyld2=1
735 If no file exists then the kernel boot-args will be scanned.
737 bool bootArgsContains(const char* arg
)
739 //FIXME: Use strnstr(). Unfortunately we are missing an imp libc.
740 #if TARGET_IPHONE_SIMULATOR
743 // don't check for boot-args on customer installs
744 if ( !internalInstall() )
747 char pathBuffer
[MAXPATHLEN
+1];
748 #if __IPHONE_OS_VERSION_MIN_REQUIRED
749 strlcpy(pathBuffer
, IPHONE_DYLD_SHARED_CACHE_DIR
, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR
));
751 strlcpy(pathBuffer
, MACOSX_DYLD_SHARED_CACHE_DIR
, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR
));
753 strlcat(pathBuffer
, "dyld-bootargs", MAXPATHLEN
+1);
754 __block
bool result
= false;
755 forEachLineInFile(pathBuffer
, ^(const char* line
, bool& stop
) {
756 const char* delim
= strchr(line
, ':');
757 if ( delim
== nullptr )
759 char binary
[MAXPATHLEN
];
760 char options
[MAXPATHLEN
];
761 strlcpy(binary
, line
, MAXPATHLEN
);
762 binary
[delim
-line
] = '\0';
763 strlcpy(options
, delim
+1, MAXPATHLEN
);
764 if ( (strcmp(dyld::getExecutablePath(), binary
) == 0) || (strcmp("*", binary
) == 0) ) {
765 result
= (strstr(options
, arg
) != nullptr);
770 // get length of full boot-args string
772 if ( sysctlbyname("kern.bootargs", NULL
, &len
, NULL
, 0) != 0 )
775 // get copy of boot-args string
776 char bootArgsBuffer
[len
];
777 if ( sysctlbyname("kern.bootargs", bootArgsBuffer
, &len
, NULL
, 0) != 0 )
780 // return true if 'arg' is a sub-string of boot-args
781 return (strstr(bootArgsBuffer
, arg
) != nullptr);
787 // hack because libdyld.dylib should not link with libc++.dylib
788 extern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden")));
789 void __cxa_pure_virtual()
796 #endif // DYLD_IN_PROCESS
798 } // namespace loader