1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2004-2013 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
34 #include <sys/param.h>
35 #include <mach/mach_time.h> // mach_absolute_time()
36 #include <mach/mach_init.h>
37 #include <sys/types.h>
39 #include <sys/syscall.h>
40 #include <sys/socket.h>
42 #include <sys/syslog.h>
44 #include <sys/xattr.h>
45 #include <mach/mach.h>
46 #include <mach-o/fat.h>
47 #include <mach-o/loader.h>
48 #include <mach-o/ldsyms.h>
49 #include <libkern/OSByteOrder.h>
50 #include <libkern/OSAtomic.h>
51 #include <mach/mach.h>
52 #include <sys/sysctl.h>
54 #include <sys/dtrace.h>
55 #include <libkern/OSAtomic.h>
56 #include <Availability.h>
57 #include <System/sys/codesign.h>
58 #include <System/sys/csr.h>
60 #include <os/lock_private.h>
61 #include <System/machine/cpu_capabilities.h>
62 #include <System/sys/reason.h>
63 #include <kern/kcdata.h>
65 #include <sys/fsgetpath.h>
66 #include <System/sys/content_protection.h>
68 #define SUPPORT_LOGGING_TO_CONSOLE !(__i386__ || __x86_64__ || TARGET_OS_SIMULATOR)
69 #if SUPPORT_LOGGING_TO_CONSOLE
70 #include <paths.h> // for logging to console
73 #if !TARGET_OS_SIMULATOR
75 // The comm page is being renamed, so set our define to the new value if the old
77 #ifndef _COMM_PAGE_DYLD_SYSTEM_FLAGS
79 #ifndef _COMM_PAGE_DYLD_FLAGS
80 #error Must define _COMM_PAGE_DYLD_FLAGS or _COMM_PAGE_DYLD_SYSTEM_FLAGS
83 #define _COMM_PAGE_DYLD_SYSTEM_FLAGS _COMM_PAGE_DYLD_FLAGS
89 #if TARGET_OS_SIMULATOR
91 AMFI_DYLD_INPUT_PROC_IN_SIMULATOR
= (1 << 0),
93 enum amfi_dyld_policy_output_flag_set
{
94 AMFI_DYLD_OUTPUT_ALLOW_AT_PATH
= (1 << 0),
95 AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS
= (1 << 1),
96 AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE
= (1 << 2),
97 AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS
= (1 << 3),
98 AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS
= (1 << 4),
99 AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION
= (1 << 5),
100 AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING
= (1 << 6),
102 extern "C" int amfi_check_dyld_policy_self(uint64_t input_flags
, uint64_t* output_flags
);
108 #include <sandbox/private.h>
109 #if __has_feature(ptrauth_calls)
113 extern "C" int __fork();
121 #include "ImageLoader.h"
122 #include "ImageLoaderMachO.h"
123 #include "dyldLibSystemInterface.h"
124 #include "dyld_cache_format.h"
125 #include "dyld_process_info_internal.h"
127 #if SUPPORT_ACCELERATE_TABLES
128 #include "ImageLoaderMegaDylib.h"
131 #if TARGET_OS_SIMULATOR
132 extern "C" void* gSyscallHelpers
;
134 #include "dyldSyscallInterface.h"
138 #include "libdyldEntryVector.h"
139 #include "MachOLoaded.h"
141 #include "DyldSharedCache.h"
142 #include "SharedCacheRuntime.h"
143 #include "StringUtils.h"
145 #include "ClosureBuilder.h"
146 #include "ClosureFileSystemPhysical.h"
147 #include "FileUtils.h"
148 #include "BootArgs.h"
150 #include "RootsChecker.h"
153 #define MH_HAS_OBJC 0x40000000
156 // not libc header for send() syscall interface
157 extern "C" ssize_t
__sendto(int, const void *, size_t, int, const struct sockaddr
*, socklen_t
);
160 // ARM and x86_64 are the only architecture that use cpu-sub-types
161 #define CPU_SUBTYPES_SUPPORTED ((__arm__ || __arm64__ || __x86_64__) && !TARGET_OS_SIMULATOR)
164 #define LC_SEGMENT_COMMAND LC_SEGMENT_64
165 #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT
166 #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO
167 #define macho_segment_command segment_command_64
168 #define macho_section section_64
170 #define LC_SEGMENT_COMMAND LC_SEGMENT
171 #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64
172 #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO_64
173 #define macho_segment_command segment_command
174 #define macho_section section
177 #define DYLD_CLOSURE_XATTR_NAME "com.apple.dyld"
179 #define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */
182 /* implemented in dyld_gdb.cpp */
183 extern void resetAllImages();
184 extern void addImagesToAllImages(uint32_t infoCount
, const dyld_image_info info
[]);
185 extern void addAotImagesToAllAotImages(uint32_t aotInfoCount
, const dyld_aot_image_info aotInfo
[]);
186 extern void removeImageFromAllImages(const mach_header
* mh
);
187 extern void addNonSharedCacheImageUUID(const dyld_uuid_info
& info
);
188 extern const char* notifyGDB(enum dyld_image_states state
, uint32_t infoCount
, const dyld_image_info info
[]);
189 extern size_t allImagesCount();
191 // magic so CrashReporter logs message
193 char error_string
[1024];
196 // magic linker symbol for start of dyld binary
197 extern "C" const macho_header __dso_handle
;
201 // The file contains the core of dyld used to get a process to main().
202 // The API's that dyld supports are implemented in dyldAPIs.cpp.
209 struct RegisteredDOF
{ const mach_header
* mh
; int registrationID
; };
210 struct DylibOverride
{ const char* installName
; const char* override
; };
214 VECTOR_NEVER_DESTRUCTED(ImageLoader
*);
215 VECTOR_NEVER_DESTRUCTED(dyld::RegisteredDOF
);
216 VECTOR_NEVER_DESTRUCTED(dyld::ImageCallback
);
217 VECTOR_NEVER_DESTRUCTED(dyld::DylibOverride
);
218 VECTOR_NEVER_DESTRUCTED(ImageLoader::DynamicReference
);
220 VECTOR_NEVER_DESTRUCTED(dyld_image_state_change_handler
);
226 // state of all environment variables dyld uses
228 struct EnvironmentVariables
{
229 const char* const * DYLD_FRAMEWORK_PATH
;
230 const char* const * DYLD_FALLBACK_FRAMEWORK_PATH
;
231 const char* const * DYLD_LIBRARY_PATH
;
232 const char* const * DYLD_FALLBACK_LIBRARY_PATH
;
233 const char* const * DYLD_INSERT_LIBRARIES
;
234 const char* const * LD_LIBRARY_PATH
; // for unix conformance
235 const char* const * DYLD_VERSIONED_LIBRARY_PATH
;
236 const char* const * DYLD_VERSIONED_FRAMEWORK_PATH
;
237 bool DYLD_PRINT_LIBRARIES_POST_LAUNCH
;
238 bool DYLD_BIND_AT_LAUNCH
;
239 bool DYLD_PRINT_STATISTICS
;
240 bool DYLD_PRINT_STATISTICS_DETAILS
;
241 bool DYLD_PRINT_OPTS
;
243 bool DYLD_DISABLE_DOFS
;
245 // DYLD_SHARED_CACHE_DIR ==> sSharedCacheOverrideDir
246 // DYLD_ROOT_PATH ==> gLinkContext.rootPaths
247 // DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix
248 // DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts
249 // DYLD_PRINT_ENV ==> gLinkContext.verboseEnv
250 // DYLD_FORCE_FLAT_NAMESPACE ==> gLinkContext.bindFlat
251 // DYLD_PRINT_INITIALIZERS ==> gLinkContext.verboseInit
252 // DYLD_PRINT_SEGMENTS ==> gLinkContext.verboseMapping
253 // DYLD_PRINT_BINDINGS ==> gLinkContext.verboseBind
254 // DYLD_PRINT_WEAK_BINDINGS ==> gLinkContext.verboseWeakBind
255 // DYLD_PRINT_REBASINGS ==> gLinkContext.verboseRebase
256 // DYLD_PRINT_DOFS ==> gLinkContext.verboseDOF
257 // DYLD_PRINT_APIS ==> gLogAPIs
258 // DYLD_IGNORE_PREBINDING ==> gLinkContext.prebindUsage
259 // DYLD_PREBIND_DEBUG ==> gLinkContext.verbosePrebinding
260 // DYLD_NEW_LOCAL_SHARED_REGIONS ==> gLinkContext.sharedRegionMode
261 // DYLD_SHARED_REGION ==> gLinkContext.sharedRegionMode
262 // DYLD_PRINT_WARNINGS ==> gLinkContext.verboseWarnings
263 // DYLD_PRINT_RPATHS ==> gLinkContext.verboseRPaths
264 // DYLD_PRINT_INTERPOSING ==> gLinkContext.verboseInterposing
265 // DYLD_PRINT_LIBRARIES ==> gLinkContext.verboseLoading
270 typedef std::vector
<dyld_image_state_change_handler
> StateHandlers
;
273 enum EnvVarMode
{ envNone
, envPrintOnly
, envAll
};
276 static const char* sExecPath
= NULL
;
277 static const char* sExecShortName
= NULL
;
278 static const macho_header
* sMainExecutableMachHeader
= NULL
;
279 static uintptr_t sMainExecutableSlide
= 0;
280 #if CPU_SUBTYPES_SUPPORTED
281 static cpu_type_t sHostCPU
;
282 static cpu_subtype_t sHostCPUsubtype
;
284 static ImageLoaderMachO
* sMainExecutable
= NULL
;
285 static size_t sInsertedDylibCount
= 0;
286 static std::vector
<ImageLoader
*> sAllImages
;
287 static std::vector
<ImageLoader
*> sImageRoots
;
288 static std::vector
<ImageLoader
*> sImageFilesNeedingTermination
;
289 static std::vector
<RegisteredDOF
> sImageFilesNeedingDOFUnregistration
;
290 static std::vector
<ImageCallback
> sAddImageCallbacks
;
291 static std::vector
<ImageCallback
> sRemoveImageCallbacks
;
292 static std::vector
<LoadImageCallback
> sAddLoadImageCallbacks
;
293 static std::vector
<LoadImageBulkCallback
> sAddBulkLoadImageCallbacks
;
294 static bool sRemoveImageCallbacksInUse
= false;
295 static void* sSingleHandlers
[7][3];
296 static void* sBatchHandlers
[7][3];
297 static ImageLoader
* sLastImageByAddressCache
;
298 static EnvironmentVariables sEnv
;
300 static const char* sFrameworkFallbackPaths
[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL
};
301 static const char* sLibraryFallbackPaths
[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL
};
302 static const char* sRestrictedFrameworkFallbackPaths
[] = { "/System/Library/Frameworks", NULL
};
303 static const char* sRestrictedLibraryFallbackPaths
[] = { "/usr/lib", NULL
};
305 static const char* sFrameworkFallbackPaths
[] = { "/System/Library/Frameworks", NULL
};
306 static const char* sLibraryFallbackPaths
[] = { "/usr/local/lib", "/usr/lib", NULL
};
308 static UndefinedHandler sUndefinedHandler
= NULL
;
309 static ImageLoader
* sBundleBeingLoaded
= NULL
; // hack until OFI is reworked
310 static dyld3::SharedCacheLoadInfo sSharedCacheLoadInfo
;
311 static const char* sSharedCacheOverrideDir
;
312 bool gSharedCacheOverridden
= false;
313 ImageLoader::LinkContext gLinkContext
;
314 bool gLogAPIs
= false;
315 #if SUPPORT_ACCELERATE_TABLES
316 bool gLogAppAPIs
= false;
318 const struct LibSystemHelpers
* gLibSystemHelpers
= NULL
;
319 #if SUPPORT_OLD_CRT_INITIALIZATION
320 bool gRunInitializersOldWay
= false;
322 static std::vector
<DylibOverride
> sDylibOverrides
;
323 #if !TARGET_OS_SIMULATOR
324 static int sLogSocket
= -1;
326 static bool sFrameworksFoundAsDylibs
= false;
327 #if __x86_64__ && !TARGET_OS_SIMULATOR
328 static bool sHaswell
= false;
330 static std::vector
<ImageLoader::DynamicReference
> sDynamicReferences
;
331 #pragma clang diagnostic push
332 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
333 static OSSpinLock sDynamicReferencesLock
= 0;
334 #pragma clang diagnostic pop
335 #if !TARGET_OS_SIMULATOR
336 static bool sLogToFile
= false;
338 static char sLoadingCrashMessage
[1024] = "dyld: launch, loading dependent libraries";
339 static _dyld_objc_notify_mapped sNotifyObjCMapped
;
340 static _dyld_objc_notify_init sNotifyObjCInit
;
341 static _dyld_objc_notify_unmapped sNotifyObjCUnmapped
;
343 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
344 static bool sForceStderr
= false;
348 #if SUPPORT_ACCELERATE_TABLES
349 static ImageLoaderMegaDylib
* sAllCacheImagesProxy
= NULL
;
350 // Note these are now off by default as everything should use dyld3.
351 static bool sDisableAcceleratorTables
= true;
354 bool gUseDyld3
= false;
355 static uint32_t sLaunchModeUsed
= 0;
356 static bool sSkipMain
= false;
357 static void (*sEntryOverride
)() = nullptr;
358 static bool sJustBuildClosure
= false;
359 #if !TARGET_OS_SIMULATOR
360 static bool sLogClosureFailure
= false;
362 static bool sKeysDisabled
= false;
363 static bool sOnlyPlatformArm64e
= false; // arm64e binaries can only be loaded if they are part of the OS
365 static dyld3::RootsChecker sRootsChecker
;
367 enum class ClosureMode
{
368 // Unset means we haven't provided an env variable or boot-arg to explicitly choose a mode
370 // On means we set DYLD_USE_CLOSURES=1, or we didn't have DYLD_USE_CLOSURES=0 but did have
371 // -force_dyld3=1 env variable or a customer cache on iOS
373 // Off means we set DYLD_USE_CLOSURES=0, or we didn't have DYLD_USE_CLOSURES=1 but did have
374 // -force_dyld2=1 env variable or an internal cache on iOS
376 // PreBuiltOnly means only use a shared cache closure and don't try build a new one
380 enum class ClosureKind
{
386 static ClosureMode sClosureMode
= ClosureMode::Unset
;
387 static ClosureKind sClosureKind
= ClosureKind::unset
;
388 static bool sForceInvalidSharedCacheClosureFormat
= false;
389 static uint64_t launchTraceID
= 0;
391 // These flags are the values in the 64-bit _COMM_PAGE_DYLD_SYSTEM_FLAGS entry
392 // Note we own this and can write it from PID 1
393 enum CommPageFlags
: uint64_t {
396 // The boot args can set the low 32-bits of the comm page. We'll reserve the high 32-bits
397 // for runtime (launchd) set values.
398 CommPageBootArgMask
= 0xFFFFFFFF,
400 // Are the simulator support dylibs definitely roots when launchd scanned them
401 libsystemKernelIsRoot
= 1ULL << 32,
402 libsystemPlatformIsRoot
= 1ULL << 33,
403 libsystemPThreadIsRoot
= 1ULL << 34,
405 // Is the file system writable, ie, could the simulator support dylibs be written
406 // later, after PID 1
407 fileSystemCanBeModified
= 1ULL << 35
411 // The MappedRanges structure is used for fast address->image lookups.
412 // The table is only updated when the dyld lock is held, so we don't
413 // need to worry about multiple writers. But readers may look at this
414 // data without holding the lock. Therefore, all updates must be done
415 // in an order that will never cause readers to see inconsistent data.
416 // The general rule is that if the image field is non-NULL then
417 // the other fields are valid.
430 static MappedRanges
* sMappedRangesStart
;
432 #pragma clang diagnostic push
433 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
434 void addMappedRange(ImageLoader
* image
, uintptr_t start
, uintptr_t end
)
436 //dyld::log("addMappedRange(0x%lX->0x%lX) for %s\n", start, end, image->getShortName());
437 for (MappedRanges
* p
= sMappedRangesStart
; p
!= NULL
; p
= p
->next
) {
438 for (unsigned long i
=0; i
< p
->count
; ++i
) {
439 if ( p
->array
[i
].image
== NULL
) {
440 p
->array
[i
].start
= start
;
441 p
->array
[i
].end
= end
;
442 // add image field last with a barrier so that any reader will see consistent records
444 p
->array
[i
].image
= image
;
449 // table must be full, chain another
450 #if SUPPORT_ACCELERATE_TABLES
451 unsigned count
= (sAllCacheImagesProxy
!= NULL
) ? 16 : 400;
453 unsigned count
= 400;
455 size_t allocationSize
= sizeof(MappedRanges
) + (count
-1)*3*sizeof(void*);
456 MappedRanges
* newRanges
= (MappedRanges
*)malloc(allocationSize
);
457 bzero(newRanges
, allocationSize
);
458 newRanges
->count
= count
;
459 newRanges
->array
[0].start
= start
;
460 newRanges
->array
[0].end
= end
;
461 newRanges
->array
[0].image
= image
;
463 if ( sMappedRangesStart
== NULL
) {
464 sMappedRangesStart
= newRanges
;
467 for (MappedRanges
* p
= sMappedRangesStart
; p
!= NULL
; p
= p
->next
) {
468 if ( p
->next
== NULL
) {
477 void removedMappedRanges(ImageLoader
* image
)
479 for (MappedRanges
* p
= sMappedRangesStart
; p
!= NULL
; p
= p
->next
) {
480 for (unsigned long i
=0; i
< p
->count
; ++i
) {
481 if ( p
->array
[i
].image
== image
) {
482 // clear with a barrier so that any reader will see consistent records
484 p
->array
[i
].image
= NULL
;
489 #pragma clang diagnostic pop
491 ImageLoader
* findMappedRange(uintptr_t target
)
493 for (MappedRanges
* p
= sMappedRangesStart
; p
!= NULL
; p
= p
->next
) {
494 for (unsigned long i
=0; i
< p
->count
; ++i
) {
495 if ( p
->array
[i
].image
!= NULL
) {
496 if ( (p
->array
[i
].start
<= target
) && (target
< p
->array
[i
].end
) )
497 return p
->array
[i
].image
;
506 const char* mkstringf(const char* format
, ...)
508 _SIMPLE_STRING buf
= _simple_salloc();
511 va_start(list
, format
);
512 _simple_vsprintf(buf
, format
, list
);
514 const char* t
= strdup(_simple_string(buf
));
519 return "mkstringf, out of memory error";
523 void throwf(const char* format
, ...)
525 _SIMPLE_STRING buf
= _simple_salloc();
528 va_start(list
, format
);
529 _simple_vsprintf(buf
, format
, list
);
531 const char* t
= strdup(_simple_string(buf
));
536 throw "throwf, out of memory error";
540 #if !TARGET_OS_SIMULATOR
541 static int sLogfile
= STDERR_FILENO
;
544 #if !TARGET_OS_SIMULATOR
545 // based on CFUtilities.c: also_do_stderr()
546 static bool useSyslog()
548 // Use syslog() for processes managed by launchd
549 static bool launchdChecked
= false;
550 static bool launchdOwned
= false;
551 if ( !launchdChecked
&& gProcessInfo
->libSystemInitialized
) {
552 if ( (gLibSystemHelpers
!= NULL
) && (gLibSystemHelpers
->version
>= 11) ) {
553 // <rdar://problem/23520449> only call isLaunchdOwned() after libSystem is initialized
554 launchdOwned
= (*gLibSystemHelpers
->isLaunchdOwned
)();
555 launchdChecked
= true;
558 if ( launchdChecked
&& launchdOwned
)
561 // If stderr is not available, use syslog()
563 int result
= fstat(STDERR_FILENO
, &sb
);
565 return true; // file descriptor 2 is closed
571 static void socket_syslogv(int priority
, const char* format
, va_list list
)
573 // lazily create socket and connection to syslogd
574 if ( sLogSocket
== -1 ) {
575 sLogSocket
= ::socket(AF_UNIX
, SOCK_DGRAM
, 0);
576 if (sLogSocket
== -1)
577 return; // cannot log
578 ::fcntl(sLogSocket
, F_SETFD
, 1);
580 struct sockaddr_un addr
;
581 addr
.sun_family
= AF_UNIX
;
582 strncpy(addr
.sun_path
, _PATH_LOG
, sizeof(addr
.sun_path
));
583 if ( ::connect(sLogSocket
, (struct sockaddr
*)&addr
, sizeof(addr
)) == -1 ) {
590 // format message to syslogd like: "<priority>Process[pid]: message"
591 _SIMPLE_STRING buf
= _simple_salloc();
594 if ( _simple_sprintf(buf
, "<%d>%s[%d]: ", LOG_USER
|LOG_NOTICE
, sExecShortName
, getpid()) == 0 ) {
595 if ( _simple_vsprintf(buf
, format
, list
) == 0 ) {
596 const char* p
= _simple_string(buf
);
597 ::__sendto(sLogSocket
, p
, strlen(p
), 0, NULL
, 0);
605 void vlog(const char* format
, va_list list
)
607 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
608 // <rdar://problem/25965832> log to console when running iOS app from Xcode
609 if ( !sLogToFile
&& !sForceStderr
&& useSyslog() )
611 if ( !sLogToFile
&& useSyslog() )
613 socket_syslogv(LOG_ERR
, format
, list
);
615 _simple_vdprintf(sLogfile
, format
, list
);
619 void log(const char* format
, ...)
622 va_start(list
, format
);
628 void vwarn(const char* format
, va_list list
)
630 _simple_dprintf(sLogfile
, "dyld: warning, ");
631 _simple_vdprintf(sLogfile
, format
, list
);
634 void warn(const char* format
, ...)
637 va_start(list
, format
);
642 void logToConsole(const char* format
, ...) {
643 #if SUPPORT_LOGGING_TO_CONSOLE
644 int cfd
= open(_PATH_CONSOLE
, O_WRONLY
|O_NOCTTY
);
649 va_start(list
, format
);
650 _simple_vdprintf(cfd
, format
, list
);
658 extern void vlog(const char* format
, va_list list
);
659 #endif // !TARGET_OS_SIMULATOR
662 #pragma clang diagnostic push
663 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
664 // <rdar://problem/8867781> control access to sAllImages through a lock
665 // because global dyld lock is not held during initialization phase of dlopen()
666 // <rdar://problem/16145518> Use OSSpinLockLock to allow yielding
667 static OSSpinLock sAllImagesLock
= 0;
669 static void allImagesLock()
671 OSSpinLockLock(&sAllImagesLock
);
674 static void allImagesUnlock()
676 OSSpinLockUnlock(&sAllImagesLock
);
678 #pragma clang diagnostic pop
681 // utility class to assure files are closed when an exception is thrown
684 FileOpener(const char* path
);
686 int getFileDescriptor() { return fd
; }
691 FileOpener::FileOpener(const char* path
)
694 fd
= dyld3::open(path
, O_RDONLY
, 0);
697 FileOpener::~FileOpener()
704 static void registerDOFs(const std::vector
<ImageLoader::DOFInfo
>& dofs
)
706 const size_t dofSectionCount
= dofs
.size();
707 if ( !sEnv
.DYLD_DISABLE_DOFS
&& (dofSectionCount
!= 0) ) {
708 int fd
= open("/dev/" DTRACEMNR_HELPER
, O_RDWR
);
710 //dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n");
713 // allocate a buffer on the stack for the variable length dof_ioctl_data_t type
714 uint8_t buffer
[sizeof(dof_ioctl_data_t
) + dofSectionCount
*sizeof(dof_helper_t
)];
715 dof_ioctl_data_t
* ioctlData
= (dof_ioctl_data_t
*)buffer
;
717 // fill in buffer with one dof_helper_t per DOF section
718 ioctlData
->dofiod_count
= dofSectionCount
;
719 for (unsigned int i
=0; i
< dofSectionCount
; ++i
) {
720 strlcpy(ioctlData
->dofiod_helpers
[i
].dofhp_mod
, dofs
[i
].imageShortName
, DTRACE_MODNAMELEN
);
721 ioctlData
->dofiod_helpers
[i
].dofhp_dof
= (uintptr_t)(dofs
[i
].dof
);
722 ioctlData
->dofiod_helpers
[i
].dofhp_addr
= (uintptr_t)(dofs
[i
].dof
);
725 // tell kernel about all DOF sections en mas
726 // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel
727 user_addr_t val
= (user_addr_t
)(unsigned long)ioctlData
;
728 if ( ioctl(fd
, DTRACEHIOC_ADDDOF
, &val
) != -1 ) {
729 // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field.
730 for (unsigned int i
=0; i
< dofSectionCount
; ++i
) {
732 info
.mh
= dofs
[i
].imageHeader
;
733 info
.registrationID
= (int)(ioctlData
->dofiod_helpers
[i
].dofhp_dof
);
734 sImageFilesNeedingDOFUnregistration
.push_back(info
);
735 if ( gLinkContext
.verboseDOF
) {
736 dyld::log("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n",
737 dofs
[i
].dof
, dofs
[i
].imageShortName
, info
.registrationID
);
742 //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n");
749 static void unregisterDOF(int registrationID
)
751 int fd
= open("/dev/" DTRACEMNR_HELPER
, O_RDWR
, 0);
753 dyld::warn("can't open /dev/" DTRACEMNR_HELPER
" to unregister dtrace DOF section\n");
756 ioctl(fd
, DTRACEHIOC_REMOVE
, registrationID
);
758 if ( gLinkContext
.verboseInit
)
759 dyld::warn("unregistering DOF section ID=0x%08X with dtrace\n", registrationID
);
765 // _dyld_register_func_for_add_image() is implemented as part of the general image state change notification
766 // Returns true if we did call add image callbacks on this image
768 static bool notifyAddImageCallbacks(ImageLoader
* image
)
770 // use guard so that we cannot notify about the same image twice
771 if ( ! image
->addFuncNotified() ) {
772 for (std::vector
<ImageCallback
>::iterator it
=sAddImageCallbacks
.begin(); it
!= sAddImageCallbacks
.end(); it
++) {
773 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)image
->machHeader(), (uint64_t)(*it
), 0);
774 (*it
)(image
->machHeader(), image
->getSlide());
776 for (LoadImageCallback func
: sAddLoadImageCallbacks
) {
777 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)image
->machHeader(), (uint64_t)(*func
), 0);
778 (*func
)(image
->machHeader(), image
->getPath(), !image
->neverUnload());
780 image
->setAddFuncNotified();
788 // notify gdb about these new images
789 static const char* updateAllImages(enum dyld_image_states state
, uint32_t infoCount
, const struct dyld_image_info info
[])
791 // <rdar://problem/8812589> don't add images without paths to all-image-info-list
792 if ( info
[0].imageFilePath
!= NULL
)
793 addImagesToAllImages(infoCount
, info
);
798 static StateHandlers
* stateToHandlers(dyld_image_states state
, void* handlersArray
[7][3])
801 case dyld_image_state_mapped
:
802 return reinterpret_cast<StateHandlers
*>(&handlersArray
[0]);
804 case dyld_image_state_dependents_mapped
:
805 return reinterpret_cast<StateHandlers
*>(&handlersArray
[1]);
807 case dyld_image_state_rebased
:
808 return reinterpret_cast<StateHandlers
*>(&handlersArray
[2]);
810 case dyld_image_state_bound
:
811 return reinterpret_cast<StateHandlers
*>(&handlersArray
[3]);
813 case dyld_image_state_dependents_initialized
:
814 return reinterpret_cast<StateHandlers
*>(&handlersArray
[4]);
816 case dyld_image_state_initialized
:
817 return reinterpret_cast<StateHandlers
*>(&handlersArray
[5]);
819 case dyld_image_state_terminated
:
820 return reinterpret_cast<StateHandlers
*>(&handlersArray
[6]);
825 #if SUPPORT_ACCELERATE_TABLES
826 static dyld_image_state_change_handler
getPreInitNotifyHandler(unsigned index
)
828 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(dyld_image_state_dependents_initialized
, sSingleHandlers
);
829 if ( index
>= handlers
->size() )
831 return (*handlers
)[index
];
834 static dyld_image_state_change_handler
getBoundBatchHandler(unsigned index
)
836 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(dyld_image_state_bound
, sBatchHandlers
);
837 if ( index
>= handlers
->size() )
839 return (*handlers
)[index
];
842 static void notifySingleFromCache(dyld_image_states state
, const mach_header
* mh
, const char* path
)
844 //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
845 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(state
, sSingleHandlers
);
846 if ( handlers
!= NULL
) {
847 dyld_image_info info
;
848 info
.imageLoadAddress
= mh
;
849 info
.imageFilePath
= path
;
850 info
.imageFileModDate
= 0;
851 for (dyld_image_state_change_handler handler
: *handlers
) {
852 const char* result
= (*handler
)(state
, 1, &info
);
853 if ( (result
!= NULL
) && (state
== dyld_image_state_mapped
) ) {
854 //fprintf(stderr, " image rejected by handler=%p\n", *it);
855 // make copy of thrown string so that later catch clauses can free it
856 const char* str
= strdup(result
);
861 if ( (state
== dyld_image_state_dependents_initialized
) && (sNotifyObjCInit
!= NULL
) && (mh
->flags
& MH_HAS_OBJC
) ) {
862 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_OBJC_INIT
, (uint64_t)mh
, 0, 0);
863 (*sNotifyObjCInit
)(path
, mh
);
868 #if !TARGET_OS_SIMULATOR
869 static void sendMessage(unsigned portSlot
, mach_msg_id_t msgId
, mach_msg_size_t sendSize
, mach_msg_header_t
* buffer
, mach_msg_size_t bufferSize
) {
870 // Allocate a port to listen on in this monitoring task
871 mach_port_t sendPort
= dyld::gProcessInfo
->notifyPorts
[portSlot
];
872 if (sendPort
== MACH_PORT_NULL
) {
875 mach_port_t replyPort
= MACH_PORT_NULL
;
876 mach_port_options_t options
= { .flags
= MPO_CONTEXT_AS_GUARD
| MPO_STRICT
,
878 kern_return_t kr
= mach_port_construct(mach_task_self(), &options
, (mach_port_context_t
)&replyPort
, &replyPort
);
879 if (kr
!= KERN_SUCCESS
) {
882 // Assemble a message
883 mach_msg_header_t
* h
= buffer
;
884 h
->msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
,MACH_MSG_TYPE_MAKE_SEND_ONCE
);
886 h
->msgh_local_port
= replyPort
;
887 h
->msgh_remote_port
= sendPort
;
888 h
->msgh_reserved
= 0;
889 h
->msgh_size
= sendSize
;
890 kr
= mach_msg(h
, MACH_SEND_MSG
| MACH_RCV_MSG
, h
->msgh_size
, bufferSize
, replyPort
, 0, MACH_PORT_NULL
);
892 if ( kr
== MACH_SEND_INVALID_DEST
) {
893 if (OSAtomicCompareAndSwap32(sendPort
, 0, (volatile int32_t*)&dyld::gProcessInfo
->notifyPorts
[portSlot
])) {
894 mach_port_deallocate(mach_task_self(), sendPort
);
897 mach_port_destruct(mach_task_self(), replyPort
, 0, (mach_port_context_t
)&replyPort
);
900 static void notifyMonitoringDyld(bool unloading
, unsigned imageCount
, const struct mach_header
* loadAddresses
[],
901 const char* imagePaths
[])
903 dyld3::ScopedTimer(DBG_DYLD_REMOTE_IMAGE_NOTIFIER
, 0, 0, 0);
904 for (int slot
=0; slot
< DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT
; ++slot
) {
905 if ( dyld::gProcessInfo
->notifyPorts
[slot
] == 0) continue;
906 unsigned entriesSize
= imageCount
*sizeof(dyld_process_info_image_entry
);
907 unsigned pathsSize
= 0;
908 for (unsigned j
=0; j
< imageCount
; ++j
) {
909 pathsSize
+= (strlen(imagePaths
[j
]) + 1);
912 unsigned totalSize
= (sizeof(struct dyld_process_info_notify_header
) + entriesSize
+ pathsSize
+ 127) & -128; // align
913 // The reciever has a fixed buffer of DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE, whcih needs to hold both the message and a trailer.
914 // If the total size exceeds that we need to fragment the message.
915 if ( (totalSize
+ MAX_TRAILER_SIZE
) > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE
) {
916 // Putting all image paths into one message would make buffer too big.
917 // Instead split into two messages. Recurse as needed until paths fit in buffer.
918 unsigned imageHalfCount
= imageCount
/2;
919 notifyMonitoringDyld(unloading
, imageHalfCount
, loadAddresses
, imagePaths
);
920 notifyMonitoringDyld(unloading
, imageCount
- imageHalfCount
, &loadAddresses
[imageHalfCount
], &imagePaths
[imageHalfCount
]);
923 uint8_t buffer
[totalSize
+ MAX_TRAILER_SIZE
];
924 dyld_process_info_notify_header
* header
= (dyld_process_info_notify_header
*)buffer
;
926 header
->imageCount
= imageCount
;
927 header
->imagesOffset
= sizeof(dyld_process_info_notify_header
);
928 header
->stringsOffset
= sizeof(dyld_process_info_notify_header
) + entriesSize
;
929 header
->timestamp
= dyld::gProcessInfo
->infoArrayChangeTimestamp
;
930 dyld_process_info_image_entry
* entries
= (dyld_process_info_image_entry
*)&buffer
[header
->imagesOffset
];
931 char* const pathPoolStart
= (char*)&buffer
[header
->stringsOffset
];
932 char* pathPool
= pathPoolStart
;
933 for (unsigned j
=0; j
< imageCount
; ++j
) {
934 strcpy(pathPool
, imagePaths
[j
]);
935 uint32_t len
= (uint32_t)strlen(pathPool
);
936 bzero(entries
->uuid
, 16);
937 dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)loadAddresses
[j
];
938 mf
->getUuid(entries
->uuid
);
939 entries
->loadAddress
= (uint64_t)loadAddresses
[j
];
940 entries
->pathStringOffset
= (uint32_t)(pathPool
- pathPoolStart
);
941 entries
->pathLength
= len
;
942 pathPool
+= (len
+1);
946 sendMessage(slot
, DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID
, totalSize
, (mach_msg_header_t
*)buffer
, totalSize
);
948 sendMessage(slot
, DYLD_PROCESS_INFO_NOTIFY_LOAD_ID
, totalSize
, (mach_msg_header_t
*)buffer
, totalSize
);
953 static void notifyMonitoringDyldMain()
955 dyld3::ScopedTimer(DBG_DYLD_REMOTE_IMAGE_NOTIFIER
, 0, 0, 0);
956 for (int slot
=0; slot
< DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT
; ++slot
) {
957 if ( dyld::gProcessInfo
->notifyPorts
[slot
] == 0) continue;
958 uint8_t buffer
[sizeof(mach_msg_header_t
) + MAX_TRAILER_SIZE
];
959 sendMessage(slot
, DYLD_PROCESS_INFO_NOTIFY_MAIN_ID
, sizeof(mach_msg_header_t
), (mach_msg_header_t
*)buffer
, sizeof(mach_msg_header_t
) + MAX_TRAILER_SIZE
);
963 extern void notifyMonitoringDyldMain() VIS_HIDDEN
;
964 extern void notifyMonitoringDyld(bool unloading
, unsigned imageCount
, const struct mach_header
* loadAddresses
[],
965 const char* imagePaths
[]) VIS_HIDDEN
;
968 void notifyKernel(const ImageLoader
& image
, bool loading
) {
969 uint32_t baseCode
= loading
? DBG_DYLD_UUID_MAP_A
: DBG_DYLD_UUID_UNMAP_A
;
972 if ( image
.inSharedCache() ) {
973 dyld3::kdebug_trace_dyld_image(baseCode
, image
.getInstallPath(), (const uuid_t
*)&uuid
, {0}, {{ 0, 0 }}, image
.machHeader());
975 fsid_t fsid
= {{0, 0}};
976 fsobj_id_t fsobj
= {0};
977 ino_t inode
= image
.getInode();
978 fsobj
.fid_objno
= (uint32_t)inode
;
979 fsobj
.fid_generation
= (uint32_t)(inode
>>32);
980 fsid
.val
[0] = image
.getDevice();
981 dyld3::kdebug_trace_dyld_image(baseCode
, image
.getPath(), (const uuid_t
*)&uuid
, fsobj
, fsid
, image
.machHeader());
985 static void notifySingle(dyld_image_states state
, const ImageLoader
* image
, ImageLoader::InitializerTimingList
* timingInfo
)
987 //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath());
988 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(state
, sSingleHandlers
);
989 if ( handlers
!= NULL
) {
990 dyld_image_info info
;
991 info
.imageLoadAddress
= image
->machHeader();
992 info
.imageFilePath
= image
->getRealPath();
993 info
.imageFileModDate
= image
->lastModified();
994 for (std::vector
<dyld_image_state_change_handler
>::iterator it
= handlers
->begin(); it
!= handlers
->end(); ++it
) {
995 const char* result
= (*it
)(state
, 1, &info
);
996 if ( (result
!= NULL
) && (state
== dyld_image_state_mapped
) ) {
997 //fprintf(stderr, " image rejected by handler=%p\n", *it);
998 // make copy of thrown string so that later catch clauses can free it
999 const char* str
= strdup(result
);
1004 if ( state
== dyld_image_state_mapped
) {
1005 // <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
1006 // <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
1007 if (!image
->inSharedCache()
1008 || (gLinkContext
.sharedRegionMode
== ImageLoader::kUsePrivateSharedRegion
)) {
1009 dyld_uuid_info info
;
1010 if ( image
->getUUID(info
.imageUUID
) ) {
1011 info
.imageLoadAddress
= image
->machHeader();
1012 addNonSharedCacheImageUUID(info
);
1016 if ( (state
== dyld_image_state_dependents_initialized
) && (sNotifyObjCInit
!= NULL
) && image
->notifyObjC() ) {
1017 uint64_t t0
= mach_absolute_time();
1018 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_OBJC_INIT
, (uint64_t)image
->machHeader(), 0, 0);
1019 (*sNotifyObjCInit
)(image
->getRealPath(), image
->machHeader());
1020 uint64_t t1
= mach_absolute_time();
1021 uint64_t t2
= mach_absolute_time();
1022 uint64_t timeInObjC
= t1
-t0
;
1023 uint64_t emptyTime
= (t2
-t1
)*100;
1024 if ( (timeInObjC
> emptyTime
) && (timingInfo
!= NULL
) ) {
1025 timingInfo
->addTime(image
->getShortName(), timeInObjC
);
1028 // mach message csdlc about dynamically unloaded images
1029 if ( image
->addFuncNotified() && (state
== dyld_image_state_terminated
) ) {
1030 notifyKernel(*image
, false);
1031 const struct mach_header
* loadAddress
[] = { image
->machHeader() };
1032 const char* loadPath
[] = { image
->getPath() };
1033 notifyMonitoringDyld(true, 1, loadAddress
, loadPath
);
1039 // Normally, dyld_all_image_infos is only updated in batches after an entire
1040 // graph is loaded. But if there is an error loading the initial set of
1041 // dylibs needed by the main executable, dyld_all_image_infos is not yet set
1042 // up, leading to usually brief crash logs.
1044 // This function manually adds the images loaded so far to dyld::gProcessInfo.
1045 // It should only be called before terminating.
1047 void syncAllImages()
1049 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); ++it
) {
1050 dyld_image_info info
;
1051 ImageLoader
* image
= *it
;
1052 info
.imageLoadAddress
= image
->machHeader();
1053 info
.imageFilePath
= image
->getRealPath();
1054 info
.imageFileModDate
= image
->lastModified();
1055 // add to all_image_infos if not already there
1057 int existingCount
= dyld::gProcessInfo
->infoArrayCount
;
1058 const dyld_image_info
* existing
= dyld::gProcessInfo
->infoArray
;
1059 if ( existing
!= NULL
) {
1060 for (int i
=0; i
< existingCount
; ++i
) {
1061 if ( existing
[i
].imageLoadAddress
== info
.imageLoadAddress
) {
1062 //dyld::log("not adding %s\n", info.imageFilePath);
1069 //dyld::log("adding %s\n", info.imageFilePath);
1070 addImagesToAllImages(1, &info
);
1076 static int imageSorter(const void* l
, const void* r
)
1078 const ImageLoader
* left
= *((ImageLoader
**)l
);
1079 const ImageLoader
* right
= *((ImageLoader
**)r
);
1080 return left
->compare(right
);
1083 static void notifyBatchPartial(dyld_image_states state
, bool orLater
, dyld_image_state_change_handler onlyHandler
, bool preflightOnly
, bool onlyObjCMappedNotification
)
1085 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(state
, sBatchHandlers
);
1086 if ( (handlers
!= NULL
) || ((state
== dyld_image_state_bound
) && (sNotifyObjCMapped
!= NULL
)) ) {
1087 // don't use a vector because it will use malloc/free and we want notifcation to be low cost
1089 dyld_image_info infos
[allImagesCount()+1];
1090 ImageLoader
* images
[allImagesCount()+1];
1091 ImageLoader
** end
= images
;
1092 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
1093 dyld_image_states imageState
= (*it
)->getState();
1094 if ( (imageState
== state
) || (orLater
&& (imageState
> state
)) )
1097 if ( sBundleBeingLoaded
!= NULL
) {
1098 dyld_image_states imageState
= sBundleBeingLoaded
->getState();
1099 if ( (imageState
== state
) || (orLater
&& (imageState
> state
)) )
1100 *end
++ = sBundleBeingLoaded
;
1102 const char* dontLoadReason
= NULL
;
1103 uint32_t imageCount
= (uint32_t)(end
-images
);
1104 if ( imageCount
!= 0 ) {
1106 qsort(images
, imageCount
, sizeof(ImageLoader
*), &imageSorter
);
1108 const mach_header
* mhs
[imageCount
];
1109 const char* paths
[imageCount
];
1110 uint32_t bulkNotifyImageCount
= 0;
1113 for (unsigned int i
=0; i
< imageCount
; ++i
) {
1114 dyld_image_info
* p
= &infos
[i
];
1115 ImageLoader
* image
= images
[i
];
1116 //dyld::log(" state=%d, name=%s\n", state, image->getPath());
1117 p
->imageLoadAddress
= image
->machHeader();
1118 p
->imageFilePath
= image
->getRealPath();
1119 p
->imageFileModDate
= image
->lastModified();
1120 // get these registered with the kernel as early as possible
1121 if ( state
== dyld_image_state_dependents_mapped
)
1122 notifyKernel(*image
, true);
1123 // special case for add_image hook
1124 if ( state
== dyld_image_state_bound
) {
1125 if ( notifyAddImageCallbacks(image
) ) {
1126 // Add this to the list of images to bulk notify
1127 mhs
[bulkNotifyImageCount
] = infos
[i
].imageLoadAddress
;
1128 paths
[bulkNotifyImageCount
] = infos
[i
].imageFilePath
;
1129 ++bulkNotifyImageCount
;
1134 if ( (state
== dyld_image_state_bound
) && !sAddBulkLoadImageCallbacks
.empty() && (bulkNotifyImageCount
!= 0) ) {
1135 for (LoadImageBulkCallback func
: sAddBulkLoadImageCallbacks
) {
1136 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)mhs
[0], (uint64_t)func
, 0);
1137 (*func
)(bulkNotifyImageCount
, mhs
, paths
);
1141 #if SUPPORT_ACCELERATE_TABLES
1142 if ( sAllCacheImagesProxy
!= NULL
) {
1143 unsigned cacheCount
= sAllCacheImagesProxy
->appendImagesToNotify(state
, orLater
, &infos
[imageCount
]);
1144 // support _dyld_register_func_for_add_image()
1145 if ( state
== dyld_image_state_bound
) {
1146 for (ImageCallback callback
: sAddImageCallbacks
) {
1147 for (unsigned i
=0; i
< cacheCount
; ++i
) {
1148 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)infos
[imageCount
+i
].imageLoadAddress
, (uint64_t)(*callback
), 0);
1149 (*callback
)(infos
[imageCount
+i
].imageLoadAddress
, sSharedCacheLoadInfo
.slide
);
1152 for (LoadImageCallback func
: sAddLoadImageCallbacks
) {
1153 for (unsigned i
=0; i
< cacheCount
; ++i
) {
1154 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)infos
[imageCount
+i
].imageLoadAddress
, (uint64_t)(*func
), 0);
1155 (*func
)(infos
[imageCount
+i
].imageLoadAddress
, infos
[imageCount
+i
].imageFilePath
, false);
1158 if ( !sAddBulkLoadImageCallbacks
.empty() ) {
1159 const mach_header
* bulk_mhs
[cacheCount
];
1160 const char* bulk_paths
[cacheCount
];
1161 for (int i
=0; i
< cacheCount
; ++i
) {
1162 bulk_mhs
[i
] = infos
[imageCount
+i
].imageLoadAddress
;
1163 bulk_paths
[i
] = infos
[imageCount
+i
].imageFilePath
;
1165 for (LoadImageBulkCallback func
: sAddBulkLoadImageCallbacks
) {
1166 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)bulk_mhs
[0], (uint64_t)func
, 0);
1167 (*func
)(cacheCount
, bulk_mhs
, bulk_paths
);
1171 imageCount
+= cacheCount
;
1174 if ( imageCount
!= 0 ) {
1175 if ( !onlyObjCMappedNotification
) {
1176 if ( onlyHandler
!= NULL
) {
1177 const char* result
= NULL
;
1178 if ( result
== NULL
) {
1179 result
= (*onlyHandler
)(state
, imageCount
, infos
);
1181 if ( (result
!= NULL
) && (state
== dyld_image_state_dependents_mapped
) ) {
1182 //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler);
1183 // make copy of thrown string so that later catch clauses can free it
1184 dontLoadReason
= strdup(result
);
1188 // call each handler with whole array
1189 if ( handlers
!= NULL
) {
1190 for (std::vector
<dyld_image_state_change_handler
>::iterator it
= handlers
->begin(); it
!= handlers
->end(); ++it
) {
1191 const char* result
= (*it
)(state
, imageCount
, infos
);
1192 if ( (result
!= NULL
) && (state
== dyld_image_state_dependents_mapped
) ) {
1193 //fprintf(stderr, " images rejected by handler=%p\n", *it);
1194 // make copy of thrown string so that later catch clauses can free it
1195 dontLoadReason
= strdup(result
);
1202 // tell objc about new images
1203 if ( (onlyHandler
== NULL
) && ((state
== dyld_image_state_bound
) || (orLater
&& (dyld_image_state_bound
> state
))) && (sNotifyObjCMapped
!= NULL
) ) {
1204 const char* paths
[imageCount
];
1205 const mach_header
* mhs
[imageCount
];
1206 unsigned objcImageCount
= 0;
1207 for (int i
=0; i
< imageCount
; ++i
) {
1208 ImageLoader
* image
= findImageByMachHeader(infos
[i
].imageLoadAddress
);
1209 bool hasObjC
= false;
1210 if ( image
!= NULL
) {
1211 if ( image
->objCMappedNotified() )
1213 hasObjC
= image
->notifyObjC();
1215 #if SUPPORT_ACCELERATE_TABLES
1216 else if ( sAllCacheImagesProxy
!= NULL
) {
1217 const mach_header
* mh
;
1220 if ( sAllCacheImagesProxy
->addressInCache(infos
[i
].imageLoadAddress
, &mh
, &path
, &index
) ) {
1221 hasObjC
= (mh
->flags
& MH_HAS_OBJC
);
1226 paths
[objcImageCount
] = infos
[i
].imageFilePath
;
1227 mhs
[objcImageCount
] = infos
[i
].imageLoadAddress
;
1229 if ( image
!= NULL
)
1230 image
->setObjCMappedNotified();
1233 if ( objcImageCount
!= 0 ) {
1234 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_OBJC_MAP
, 0, 0, 0);
1235 uint64_t t0
= mach_absolute_time();
1236 (*sNotifyObjCMapped
)(objcImageCount
, paths
, mhs
);
1237 uint64_t t1
= mach_absolute_time();
1238 ImageLoader::fgTotalObjCSetupTime
+= (t1
-t0
);
1243 if ( dontLoadReason
!= NULL
)
1244 throw dontLoadReason
;
1245 if ( !preflightOnly
&& (state
== dyld_image_state_dependents_mapped
) ) {
1246 const struct mach_header
* loadAddresses
[imageCount
];
1247 const char* loadPaths
[imageCount
];
1248 for(uint32_t i
= 0; i
<imageCount
; ++i
) {
1249 loadAddresses
[i
] = infos
[i
].imageLoadAddress
;
1250 loadPaths
[i
] = infos
[i
].imageFilePath
;
1252 notifyMonitoringDyld(false, imageCount
, loadAddresses
, loadPaths
);
1257 static void notifyBatch(dyld_image_states state
, bool preflightOnly
)
1259 notifyBatchPartial(state
, false, NULL
, preflightOnly
, false);
1264 void coresymbolication_load_notifier(void* connection
, uint64_t timestamp
, const char* path
, const struct mach_header
* mh
)
1266 const struct mach_header
* loadAddress
[] = { mh
};
1267 const char* loadPath
[] = { path
};
1268 notifyMonitoringDyld(false, 1, loadAddress
, loadPath
);
1272 void coresymbolication_unload_notifier(void* connection
, uint64_t timestamp
, const char* path
, const struct mach_header
* mh
)
1274 const struct mach_header
* loadAddress
= { mh
};
1275 const char* loadPath
= { path
};
1276 notifyMonitoringDyld(true, 1, &loadAddress
, &loadPath
);
1280 kern_return_t
legacy_task_register_dyld_image_infos(task_t task
, dyld_kernel_image_info_array_t dyld_images
,
1281 mach_msg_type_number_t dyld_imagesCnt
)
1283 return KERN_SUCCESS
;
1287 kern_return_t
legacy_task_unregister_dyld_image_infos(task_t task
, dyld_kernel_image_info_array_t dyld_images
,
1288 mach_msg_type_number_t dyld_imagesCnt
)
1290 return KERN_SUCCESS
;
1294 kern_return_t
legacy_task_get_dyld_image_infos(task_inspect_t task
, dyld_kernel_image_info_array_t
*dyld_images
,
1295 mach_msg_type_number_t
*dyld_imagesCnt
)
1297 return KERN_SUCCESS
;
1301 kern_return_t
legacy_task_register_dyld_shared_cache_image_info(task_t task
, dyld_kernel_image_info_t dyld_cache_image
,
1302 boolean_t no_cache
, boolean_t private_cache
)
1304 return KERN_SUCCESS
;
1308 kern_return_t
legacy_task_register_dyld_set_dyld_state(task_t task
, uint8_t dyld_state
)
1310 return KERN_SUCCESS
;
1314 kern_return_t
legacy_task_register_dyld_get_process_state(task_t task
, dyld_kernel_process_info_t
*dyld_process_state
)
1316 return KERN_SUCCESS
;
1320 // In order for register_func_for_add_image() callbacks to to be called bottom up,
1321 // we need to maintain a list of root images. The main executable is usally the
1322 // first root. Any images dynamically added are also roots (unless already loaded).
1323 // If DYLD_INSERT_LIBRARIES is used, those libraries are first.
1324 static void addRootImage(ImageLoader
* image
)
1326 //dyld::log("addRootImage(%p, %s)\n", image, image->getPath());
1327 // add to list of roots
1328 sImageRoots
.push_back(image
);
1332 static void clearAllDepths()
1334 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++)
1335 (*it
)->clearDepth();
1338 static void printAllDepths()
1340 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++)
1341 dyld::log("%03d %s\n", (*it
)->getDepth(), (*it
)->getShortName());
1345 static unsigned int imageCount()
1348 unsigned int result
= (unsigned int)sAllImages
.size();
1354 static void setNewProgramVars(const ProgramVars
& newVars
)
1356 // make a copy of the pointers to program variables
1357 gLinkContext
.programVars
= newVars
;
1359 // now set each program global to their initial value
1360 *gLinkContext
.programVars
.NXArgcPtr
= gLinkContext
.argc
;
1361 *gLinkContext
.programVars
.NXArgvPtr
= gLinkContext
.argv
;
1362 *gLinkContext
.programVars
.environPtr
= gLinkContext
.envp
;
1363 *gLinkContext
.programVars
.__prognamePtr
= gLinkContext
.progname
;
1366 #if SUPPORT_OLD_CRT_INITIALIZATION
1367 static void setRunInitialzersOldWay()
1369 gRunInitializersOldWay
= true;
1373 static bool sandboxBlocked(const char* path
, const char* kind
)
1375 #if TARGET_OS_SIMULATOR
1376 // sandbox calls not yet supported in simulator runtime
1379 sandbox_filter_type filter
= (sandbox_filter_type
)(SANDBOX_FILTER_PATH
| SANDBOX_CHECK_NO_REPORT
);
1380 return ( sandbox_check(getpid(), kind
, filter
, path
) > 0 );
1384 bool sandboxBlockedMmap(const char* path
)
1386 return sandboxBlocked(path
, "file-map-executable");
1389 bool sandboxBlockedOpen(const char* path
)
1391 return sandboxBlocked(path
, "file-read-data");
1394 bool sandboxBlockedStat(const char* path
)
1396 return sandboxBlocked(path
, "file-read-metadata");
1400 static void addDynamicReference(ImageLoader
* from
, ImageLoader
* to
) {
1401 // don't add dynamic reference if target is in the shared cache (since it can't be unloaded)
1402 if ( to
->inSharedCache() )
1405 // don't add dynamic reference if there already is a static one
1406 if ( from
->dependsOn(to
) )
1409 #pragma clang diagnostic push
1410 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1411 // don't add if this combination already exists
1412 OSSpinLockLock(&sDynamicReferencesLock
);
1413 for (std::vector
<ImageLoader::DynamicReference
>::iterator it
=sDynamicReferences
.begin(); it
!= sDynamicReferences
.end(); ++it
) {
1414 if ( (it
->from
== from
) && (it
->to
== to
) ) {
1415 OSSpinLockUnlock(&sDynamicReferencesLock
);
1420 //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName());
1421 ImageLoader::DynamicReference t
;
1424 sDynamicReferences
.push_back(t
);
1425 OSSpinLockUnlock(&sDynamicReferencesLock
);
1426 #pragma clang diagnostic pop
1429 static void addImage(ImageLoader
* image
)
1431 // add to master list
1433 sAllImages
.push_back(image
);
1436 // update mapped ranges
1437 uintptr_t lastSegStart
= 0;
1438 uintptr_t lastSegEnd
= 0;
1439 for(unsigned int i
=0, e
=image
->segmentCount(); i
< e
; ++i
) {
1440 if ( image
->segUnaccessible(i
) )
1442 uintptr_t start
= image
->segActualLoadAddress(i
);
1443 uintptr_t end
= image
->segActualEndAddress(i
);
1444 if ( start
== lastSegEnd
) {
1445 // two segments are contiguous, just record combined segments
1449 // non-contiguous segments, record last (if any)
1450 if ( lastSegEnd
!= 0 )
1451 addMappedRange(image
, lastSegStart
, lastSegEnd
);
1452 lastSegStart
= start
;
1456 if ( lastSegEnd
!= 0 )
1457 addMappedRange(image
, lastSegStart
, lastSegEnd
);
1460 if ( gLinkContext
.verboseLoading
|| (sEnv
.DYLD_PRINT_LIBRARIES_POST_LAUNCH
&& (sMainExecutable
!=NULL
) && sMainExecutable
->isLinked()) ) {
1461 const char *imagePath
= image
->getPath();
1463 if ( image
->getUUID(imageUUID
) ) {
1464 uuid_string_t imageUUIDStr
;
1465 uuid_unparse_upper(imageUUID
, imageUUIDStr
);
1466 dyld::log("dyld: loaded: <%s> %s\n", imageUUIDStr
, imagePath
);
1469 dyld::log("dyld: loaded: %s\n", imagePath
);
1476 // Helper for std::remove_if
1478 class RefUsesImage
{
1480 RefUsesImage(ImageLoader
* image
) : _image(image
) {}
1481 bool operator()(const ImageLoader::DynamicReference
& ref
) const {
1482 return ( (ref
.from
== _image
) || (ref
.to
== _image
) );
1485 ImageLoader
* _image
;
1490 void removeImage(ImageLoader
* image
)
1492 // if has dtrace DOF section, tell dtrace it is going away, then remove from sImageFilesNeedingDOFUnregistration
1493 for (std::vector
<RegisteredDOF
>::iterator it
=sImageFilesNeedingDOFUnregistration
.begin(); it
!= sImageFilesNeedingDOFUnregistration
.end(); ) {
1494 if ( it
->mh
== image
->machHeader() ) {
1495 unregisterDOF(it
->registrationID
);
1496 sImageFilesNeedingDOFUnregistration
.erase(it
);
1497 // don't increment iterator, the erase caused next element to be copied to where this iterator points
1504 // tell all registered remove image handlers about this
1505 // do this before removing image from internal data structures so that the callback can query dyld about the image
1506 if ( image
->getState() >= dyld_image_state_bound
) {
1507 sRemoveImageCallbacksInUse
= true; // This only runs inside dyld's global lock, so ok to use a global for the in-use flag.
1508 for (std::vector
<ImageCallback
>::iterator it
=sRemoveImageCallbacks
.begin(); it
!= sRemoveImageCallbacks
.end(); it
++) {
1509 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE
, (uint64_t)image
->machHeader(), (uint64_t)(*it
), 0);
1510 (*it
)(image
->machHeader(), image
->getSlide());
1512 sRemoveImageCallbacksInUse
= false;
1514 if ( sNotifyObjCUnmapped
!= NULL
&& image
->notifyObjC() )
1515 (*sNotifyObjCUnmapped
)(image
->getRealPath(), image
->machHeader());
1519 notifySingle(dyld_image_state_terminated
, image
, NULL
);
1521 // remove from mapped images table
1522 removedMappedRanges(image
);
1524 // remove from master list
1526 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
1527 if ( *it
== image
) {
1528 sAllImages
.erase(it
);
1534 #pragma clang diagnostic push
1535 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1536 // remove from sDynamicReferences
1537 OSSpinLockLock(&sDynamicReferencesLock
);
1538 sDynamicReferences
.erase(std::remove_if(sDynamicReferences
.begin(), sDynamicReferences
.end(), RefUsesImage(image
)), sDynamicReferences
.end());
1539 OSSpinLockUnlock(&sDynamicReferencesLock
);
1540 #pragma clang diagnostic pop
1542 // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back)
1543 if ( sLastImageByAddressCache
== image
)
1544 sLastImageByAddressCache
= NULL
;
1546 // if in root list, pull it out
1547 for (std::vector
<ImageLoader
*>::iterator it
=sImageRoots
.begin(); it
!= sImageRoots
.end(); it
++) {
1548 if ( *it
== image
) {
1549 sImageRoots
.erase(it
);
1554 // If this image is the potential canonical definition of any weak defs, then set them to a tombstone value
1555 if ( gLinkContext
.weakDefMapInitialized
&& image
->hasCoalescedExports() && (image
->getState() >= dyld_image_state_bound
) ) {
1557 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)image
->machHeader();
1558 ma
->forEachWeakDef(diag
, ^(const char *symbolName
, uint64_t imageOffset
, bool isFromExportTrie
) {
1559 auto it
= gLinkContext
.weakDefMap
.find(symbolName
);
1560 assert(it
!= gLinkContext
.weakDefMap
.end());
1561 it
->second
= { nullptr, 0 };
1562 if ( !isFromExportTrie
) {
1563 // The string was already duplicated if we are an export trie
1564 // so only strdup as we are the nlist
1565 size_t hash1
= ImageLoader::HashCString::hash(it
->first
);
1566 it
->first
= strdup(it
->first
);
1567 size_t hash2
= ImageLoader::HashCString::hash(it
->first
);
1568 assert(hash1
== hash2
);
1574 if ( gLinkContext
.verboseLoading
|| (sEnv
.DYLD_PRINT_LIBRARIES_POST_LAUNCH
&& (sMainExecutable
!=NULL
) && sMainExecutable
->isLinked()) ) {
1575 const char *imagePath
= image
->getPath();
1577 if ( image
->getUUID(imageUUID
) ) {
1578 uuid_string_t imageUUIDStr
;
1579 uuid_unparse_upper(imageUUID
, imageUUIDStr
);
1580 dyld::log("dyld: unloaded: <%s> %s\n", imageUUIDStr
, imagePath
);
1583 dyld::log("dyld: unloaded: %s\n", imagePath
);
1587 // tell gdb, new way
1588 removeImageFromAllImages(image
->machHeader());
1592 void runImageStaticTerminators(ImageLoader
* image
)
1594 // if in termination list, pull it out and run terminator
1597 mightBeMore
= false;
1598 for (std::vector
<ImageLoader
*>::iterator it
=sImageFilesNeedingTermination
.begin(); it
!= sImageFilesNeedingTermination
.end(); it
++) {
1599 if ( *it
== image
) {
1600 sImageFilesNeedingTermination
.erase(it
);
1601 if (gLogAPIs
) dyld::log("dlclose(), running static terminators for %p %s\n", image
, image
->getShortName());
1602 image
->doTermination(gLinkContext
);
1607 } while ( mightBeMore
);
1610 static void terminationRecorder(ImageLoader
* image
)
1612 sImageFilesNeedingTermination
.push_back(image
);
1615 const char* getExecutablePath()
1620 static void runAllStaticTerminators(void* extra
)
1623 const size_t imageCount
= sImageFilesNeedingTermination
.size();
1624 for(size_t i
=imageCount
; i
> 0; --i
){
1625 ImageLoader
* image
= sImageFilesNeedingTermination
[i
-1];
1626 image
->doTermination(gLinkContext
);
1628 sImageFilesNeedingTermination
.clear();
1629 notifyBatch(dyld_image_state_terminated
, false);
1631 catch (const char* msg
) {
1636 void initializeMainExecutable()
1638 // record that we've reached this step
1639 gLinkContext
.startedInitializingMainExecutable
= true;
1641 // run initialzers for any inserted dylibs
1642 ImageLoader::InitializerTimingList initializerTimes
[allImagesCount()];
1643 initializerTimes
[0].count
= 0;
1644 const size_t rootCount
= sImageRoots
.size();
1645 if ( rootCount
> 1 ) {
1646 for(size_t i
=1; i
< rootCount
; ++i
) {
1647 sImageRoots
[i
]->runInitializers(gLinkContext
, initializerTimes
[0]);
1651 // run initializers for main executable and everything it brings up
1652 sMainExecutable
->runInitializers(gLinkContext
, initializerTimes
[0]);
1654 // register cxa_atexit() handler to run static terminators in all loaded images when this process exits
1655 if ( gLibSystemHelpers
!= NULL
)
1656 (*gLibSystemHelpers
->cxa_atexit
)(&runAllStaticTerminators
, NULL
, NULL
);
1658 // dump info if requested
1659 if ( sEnv
.DYLD_PRINT_STATISTICS
)
1660 ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes
[0]);
1661 if ( sEnv
.DYLD_PRINT_STATISTICS_DETAILS
)
1662 ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes
[0]);
1665 bool mainExecutablePrebound()
1667 return sMainExecutable
->usablePrebinding(gLinkContext
);
1670 ImageLoader
* mainExecutable()
1672 return sMainExecutable
;
1678 #if SUPPORT_VERSIONED_PATHS
1680 // forward reference
1681 static bool getDylibVersionAndInstallname(const char* dylibPath
, uint32_t* version
, char* installName
);
1685 // Examines a dylib file and if its current_version is newer than the installed
1686 // dylib at its install_name, then add the dylib file to sDylibOverrides.
1688 static void checkDylibOverride(const char* dylibFile
)
1690 //dyld::log("checkDylibOverride('%s')\n", dylibFile);
1691 uint32_t altVersion
;
1692 char sysInstallName
[PATH_MAX
];
1693 if ( getDylibVersionAndInstallname(dylibFile
, &altVersion
, sysInstallName
) && (sysInstallName
[0] =='/') ) {
1694 //dyld::log("%s has version 0x%08X and install name %s\n", dylibFile, altVersion, sysInstallName);
1695 uint32_t sysVersion
;
1696 if ( getDylibVersionAndInstallname(sysInstallName
, &sysVersion
, NULL
) ) {
1697 //dyld::log("%s has version 0x%08X\n", sysInstallName, sysVersion);
1698 if ( altVersion
> sysVersion
) {
1699 //dyld::log("override found: %s -> %s\n", sysInstallName, dylibFile);
1700 // see if there already is an override for this dylib
1701 bool entryExists
= false;
1702 for (std::vector
<DylibOverride
>::iterator it
= sDylibOverrides
.begin(); it
!= sDylibOverrides
.end(); ++it
) {
1703 if ( strcmp(it
->installName
, sysInstallName
) == 0 ) {
1705 uint32_t prevVersion
;
1706 if ( getDylibVersionAndInstallname(it
->override
, &prevVersion
, NULL
) ) {
1707 if ( altVersion
> prevVersion
) {
1708 // found an even newer override
1709 free((void*)(it
->override
));
1710 char resolvedPath
[PATH_MAX
];
1711 if ( realpath(dylibFile
, resolvedPath
) != NULL
)
1712 it
->override
= strdup(resolvedPath
);
1714 it
->override
= strdup(dylibFile
);
1720 if ( ! entryExists
) {
1721 DylibOverride entry
;
1722 entry
.installName
= strdup(sysInstallName
);
1723 char resolvedPath
[PATH_MAX
];
1724 if ( realpath(dylibFile
, resolvedPath
) != NULL
)
1725 entry
.override
= strdup(resolvedPath
);
1727 entry
.override
= strdup(dylibFile
);
1728 sDylibOverrides
.push_back(entry
);
1729 //dyld::log("added override: %s -> %s\n", entry.installName, entry.override);
1737 static void checkDylibOverridesInDir(const char* dirPath
)
1739 //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath);
1740 char dylibPath
[PATH_MAX
];
1741 long dirPathLen
= strlcpy(dylibPath
, dirPath
, PATH_MAX
-1);
1742 if ( dirPathLen
>= PATH_MAX
)
1744 DIR* dirp
= opendir(dirPath
);
1745 if ( dirp
!= NULL
) {
1747 dirent
* entp
= NULL
;
1748 while ( readdir_r(dirp
, &entry
, &entp
) == 0 ) {
1751 if ( entp
->d_type
!= DT_REG
)
1753 dylibPath
[dirPathLen
] = '/';
1754 dylibPath
[dirPathLen
+1] = '\0';
1755 if ( strlcat(dylibPath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1757 checkDylibOverride(dylibPath
);
1764 static void checkFrameworkOverridesInDir(const char* dirPath
)
1766 //dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath);
1767 char frameworkPath
[PATH_MAX
];
1768 long dirPathLen
= strlcpy(frameworkPath
, dirPath
, PATH_MAX
-1);
1769 if ( dirPathLen
>= PATH_MAX
)
1771 DIR* dirp
= opendir(dirPath
);
1772 if ( dirp
!= NULL
) {
1774 dirent
* entp
= NULL
;
1775 while ( readdir_r(dirp
, &entry
, &entp
) == 0 ) {
1778 if ( entp
->d_type
!= DT_DIR
)
1780 frameworkPath
[dirPathLen
] = '/';
1781 frameworkPath
[dirPathLen
+1] = '\0';
1782 int dirNameLen
= (int)strlen(entp
->d_name
);
1783 if ( dirNameLen
< 11 )
1785 if ( strcmp(&entp
->d_name
[dirNameLen
-10], ".framework") != 0 )
1787 if ( strlcat(frameworkPath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1789 if ( strlcat(frameworkPath
, "/", PATH_MAX
) >= PATH_MAX
)
1791 if ( strlcat(frameworkPath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1793 frameworkPath
[strlen(frameworkPath
)-10] = '\0';
1794 checkDylibOverride(frameworkPath
);
1799 #endif // SUPPORT_VERSIONED_PATHS
1803 // Turns a colon separated list of strings into a NULL terminated array
1804 // of string pointers. If mainExecutableDir param is not NULL,
1805 // substitutes @loader_path with main executable's dir.
1807 static const char** parseColonList(const char* list
, const char* mainExecutableDir
)
1809 static const char* sEmptyList
[] = { NULL
};
1811 if ( list
[0] == '\0' )
1815 for(const char* s
=list
; *s
!= '\0'; ++s
) {
1821 const char* start
= list
;
1822 char** result
= new char*[colonCount
+2];
1823 for(const char* s
=list
; *s
!= '\0'; ++s
) {
1825 size_t len
= s
-start
;
1826 if ( (mainExecutableDir
!= NULL
) && (strncmp(start
, "@loader_path/", 13) == 0) ) {
1827 if ( !gLinkContext
.allowAtPaths
) {
1828 dyld::log("dyld: warning: @loader_path/ ignored because of amfi policy (Codesign main executable with Library Validation to allow @ paths)\n");
1831 size_t mainExecDirLen
= strlen(mainExecutableDir
);
1832 char* str
= new char[mainExecDirLen
+len
+1];
1833 strcpy(str
, mainExecutableDir
);
1834 strlcat(str
, &start
[13], mainExecDirLen
+len
+1);
1835 str
[mainExecDirLen
+len
-13] = '\0';
1837 result
[index
++] = str
;
1839 else if ( (mainExecutableDir
!= NULL
) && (strncmp(start
, "@executable_path/", 17) == 0) ) {
1840 if ( !gLinkContext
.allowAtPaths
) {
1841 dyld::log("dyld: warning: @executable_path/ ignored because of amfi policy (Codesign main executable with Library Validation to allow @ paths)\n");
1844 size_t mainExecDirLen
= strlen(mainExecutableDir
);
1845 char* str
= new char[mainExecDirLen
+len
+1];
1846 strcpy(str
, mainExecutableDir
);
1847 strlcat(str
, &start
[17], mainExecDirLen
+len
+1);
1848 str
[mainExecDirLen
+len
-17] = '\0';
1850 result
[index
++] = str
;
1853 char* str
= new char[len
+1];
1854 strncpy(str
, start
, len
);
1857 result
[index
++] = str
;
1861 size_t len
= strlen(start
);
1862 if ( (mainExecutableDir
!= NULL
) && (strncmp(start
, "@loader_path/", 13) == 0) ) {
1863 if ( !gLinkContext
.allowAtPaths
) {
1864 dyld::log("dyld: warning: @loader_path/ ignored because of amfi policy (Codesign main executable with Library Validation to allow @ paths)\n");
1868 size_t mainExecDirLen
= strlen(mainExecutableDir
);
1869 char* str
= new char[mainExecDirLen
+len
+1];
1870 strcpy(str
, mainExecutableDir
);
1871 strlcat(str
, &start
[13], mainExecDirLen
+len
+1);
1872 str
[mainExecDirLen
+len
-13] = '\0';
1873 result
[index
++] = str
;
1876 else if ( (mainExecutableDir
!= NULL
) && (strncmp(start
, "@executable_path/", 17) == 0) ) {
1877 if ( !gLinkContext
.allowAtPaths
) {
1878 dyld::log("dyld: warning: @executable_path/ ignored because of amfi policy (Codesign main executable with Library Validation to allow @ paths)\n");
1882 size_t mainExecDirLen
= strlen(mainExecutableDir
);
1883 char* str
= new char[mainExecDirLen
+len
+1];
1884 strcpy(str
, mainExecutableDir
);
1885 strlcat(str
, &start
[17], mainExecDirLen
+len
+1);
1886 str
[mainExecDirLen
+len
-17] = '\0';
1887 result
[index
++] = str
;
1891 char* str
= new char[len
+1];
1893 result
[index
++] = str
;
1895 result
[index
] = NULL
;
1897 //dyld::log("parseColonList(%s)\n", list);
1898 //for(int i=0; result[i] != NULL; ++i)
1899 // dyld::log(" %s\n", result[i]);
1900 return (const char**)result
;
1903 static void appendParsedColonList(const char* list
, const char* mainExecutableDir
, const char* const ** storage
)
1905 const char** newlist
= parseColonList(list
, mainExecutableDir
);
1906 if ( *storage
== NULL
) {
1907 // first time, just set
1911 // need to append to existing list
1912 const char* const* existing
= *storage
;
1914 for(int i
=0; existing
[i
] != NULL
; ++i
)
1916 for(int i
=0; newlist
[i
] != NULL
; ++i
)
1918 const char** combinedList
= new const char*[count
+2];
1920 for(int i
=0; existing
[i
] != NULL
; ++i
)
1921 combinedList
[index
++] = existing
[i
];
1922 for(int i
=0; newlist
[i
] != NULL
; ++i
)
1923 combinedList
[index
++] = newlist
[i
];
1924 combinedList
[index
] = NULL
;
1925 delete[] newlist
; // free array, note: strings in newList may be leaked
1926 *storage
= combinedList
;
1931 static void paths_expand_roots(const char **paths
, const char *key
, const char *val
)
1933 // assert(val != NULL);
1934 // assert(paths != NULL);
1936 size_t keyLen
= strlen(key
);
1937 for(int i
=0; paths
[i
] != NULL
; ++i
) {
1938 if ( strncmp(paths
[i
], key
, keyLen
) == 0 ) {
1939 char* newPath
= new char[strlen(val
) + (strlen(paths
[i
]) - keyLen
) + 1];
1940 strcpy(newPath
, val
);
1941 strcat(newPath
, &paths
[i
][keyLen
]);
1949 static void removePathWithPrefix(const char* paths
[], const char* prefix
)
1951 size_t prefixLen
= strlen(prefix
);
1954 for(i
= 0; paths
[i
] != NULL
; ++i
) {
1955 if ( strncmp(paths
[i
], prefix
, prefixLen
) == 0 )
1958 paths
[i
-skip
] = paths
[i
];
1960 paths
[i
-skip
] = NULL
;
1966 static void paths_dump(const char **paths
)
1968 // assert(paths != NULL);
1969 const char **strs
= paths
;
1970 while(*strs
!= NULL
)
1972 dyld::log("\"%s\"\n", *strs
);
1981 static void printOptions(const char* argv
[])
1984 while ( NULL
!= argv
[i
] ) {
1985 dyld::log("opt[%i] = \"%s\"\n", i
, argv
[i
]);
1990 static void printEnvironmentVariables(const char* envp
[])
1992 while ( NULL
!= *envp
) {
1993 dyld::log("%s\n", *envp
);
1998 void processDyldEnvironmentVariable(const char* key
, const char* value
, const char* mainExecutableDir
)
2000 if ( strcmp(key
, "DYLD_FRAMEWORK_PATH") == 0 ) {
2001 appendParsedColonList(value
, mainExecutableDir
, &sEnv
.DYLD_FRAMEWORK_PATH
);
2002 sEnv
.hasOverride
= true;
2004 else if ( strcmp(key
, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
2005 appendParsedColonList(value
, mainExecutableDir
, &sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
);
2006 sEnv
.hasOverride
= true;
2008 else if ( strcmp(key
, "DYLD_LIBRARY_PATH") == 0 ) {
2009 appendParsedColonList(value
, mainExecutableDir
, &sEnv
.DYLD_LIBRARY_PATH
);
2010 sEnv
.hasOverride
= true;
2012 else if ( strcmp(key
, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
2013 appendParsedColonList(value
, mainExecutableDir
, &sEnv
.DYLD_FALLBACK_LIBRARY_PATH
);
2014 sEnv
.hasOverride
= true;
2016 #if SUPPORT_ROOT_PATH
2017 else if ( (strcmp(key
, "DYLD_ROOT_PATH") == 0) || (strcmp(key
, "DYLD_PATHS_ROOT") == 0) ) {
2018 if ( strcmp(value
, "/") != 0 ) {
2019 gLinkContext
.rootPaths
= parseColonList(value
, mainExecutableDir
);
2020 for (int i
=0; gLinkContext
.rootPaths
[i
] != NULL
; ++i
) {
2021 if ( gLinkContext
.rootPaths
[i
][0] != '/' ) {
2022 dyld::warn("DYLD_ROOT_PATH not used because it contains a non-absolute path\n");
2023 gLinkContext
.rootPaths
= NULL
;
2028 sEnv
.hasOverride
= true;
2031 else if ( strcmp(key
, "DYLD_IMAGE_SUFFIX") == 0 ) {
2032 gLinkContext
.imageSuffix
= parseColonList(value
, NULL
);
2033 sEnv
.hasOverride
= true;
2035 else if ( strcmp(key
, "DYLD_INSERT_LIBRARIES") == 0 ) {
2036 sEnv
.DYLD_INSERT_LIBRARIES
= parseColonList(value
, NULL
);
2037 #if SUPPORT_ACCELERATE_TABLES
2038 sDisableAcceleratorTables
= true;
2040 sEnv
.hasOverride
= true;
2042 else if ( strcmp(key
, "DYLD_PRINT_OPTS") == 0 ) {
2043 sEnv
.DYLD_PRINT_OPTS
= true;
2045 else if ( strcmp(key
, "DYLD_PRINT_ENV") == 0 ) {
2046 sEnv
.DYLD_PRINT_ENV
= true;
2048 else if ( strcmp(key
, "DYLD_DISABLE_DOFS") == 0 ) {
2049 sEnv
.DYLD_DISABLE_DOFS
= true;
2051 else if ( strcmp(key
, "DYLD_PRINT_LIBRARIES") == 0 ) {
2052 gLinkContext
.verboseLoading
= true;
2054 else if ( strcmp(key
, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) {
2055 sEnv
.DYLD_PRINT_LIBRARIES_POST_LAUNCH
= true;
2057 else if ( strcmp(key
, "DYLD_BIND_AT_LAUNCH") == 0 ) {
2058 sEnv
.DYLD_BIND_AT_LAUNCH
= true;
2060 else if ( strcmp(key
, "DYLD_FORCE_FLAT_NAMESPACE") == 0 ) {
2061 gLinkContext
.bindFlat
= true;
2063 else if ( strcmp(key
, "DYLD_NEW_LOCAL_SHARED_REGIONS") == 0 ) {
2064 // ignore, no longer relevant but some scripts still set it
2066 else if ( strcmp(key
, "DYLD_NO_FIX_PREBINDING") == 0 ) {
2068 else if ( strcmp(key
, "DYLD_PREBIND_DEBUG") == 0 ) {
2069 gLinkContext
.verbosePrebinding
= true;
2071 else if ( strcmp(key
, "DYLD_PRINT_INITIALIZERS") == 0 ) {
2072 gLinkContext
.verboseInit
= true;
2074 else if ( strcmp(key
, "DYLD_PRINT_DOFS") == 0 ) {
2075 gLinkContext
.verboseDOF
= true;
2077 else if ( strcmp(key
, "DYLD_PRINT_STATISTICS") == 0 ) {
2078 sEnv
.DYLD_PRINT_STATISTICS
= true;
2079 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
2080 // <rdar://problem/26614838> DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps
2081 sForceStderr
= true;
2084 else if ( strcmp(key
, "DYLD_PRINT_TO_STDERR") == 0 ) {
2085 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
2086 // <rdar://problem/26633440> DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps
2087 sForceStderr
= true;
2090 else if ( strcmp(key
, "DYLD_PRINT_STATISTICS_DETAILS") == 0 ) {
2091 sEnv
.DYLD_PRINT_STATISTICS_DETAILS
= true;
2093 else if ( strcmp(key
, "DYLD_PRINT_SEGMENTS") == 0 ) {
2094 gLinkContext
.verboseMapping
= true;
2096 else if ( strcmp(key
, "DYLD_PRINT_BINDINGS") == 0 ) {
2097 gLinkContext
.verboseBind
= true;
2099 else if ( strcmp(key
, "DYLD_PRINT_WEAK_BINDINGS") == 0 ) {
2100 gLinkContext
.verboseWeakBind
= true;
2102 else if ( strcmp(key
, "DYLD_PRINT_REBASINGS") == 0 ) {
2103 gLinkContext
.verboseRebase
= true;
2105 else if ( strcmp(key
, "DYLD_PRINT_APIS") == 0 ) {
2108 #if SUPPORT_ACCELERATE_TABLES
2109 else if ( strcmp(key
, "DYLD_PRINT_APIS_APP") == 0 ) {
2113 else if ( strcmp(key
, "DYLD_PRINT_WARNINGS") == 0 ) {
2114 gLinkContext
.verboseWarnings
= true;
2116 else if ( strcmp(key
, "DYLD_PRINT_RPATHS") == 0 ) {
2117 gLinkContext
.verboseRPaths
= true;
2119 else if ( strcmp(key
, "DYLD_PRINT_INTERPOSING") == 0 ) {
2120 gLinkContext
.verboseInterposing
= true;
2122 else if ( strcmp(key
, "DYLD_PRINT_CODE_SIGNATURES") == 0 ) {
2123 gLinkContext
.verboseCodeSignatures
= true;
2125 else if ( (strcmp(key
, "DYLD_SHARED_REGION") == 0) && gLinkContext
.allowEnvVarsSharedCache
) {
2126 if ( strcmp(value
, "private") == 0 ) {
2127 gLinkContext
.sharedRegionMode
= ImageLoader::kUsePrivateSharedRegion
;
2129 else if ( strcmp(value
, "avoid") == 0 ) {
2130 gLinkContext
.sharedRegionMode
= ImageLoader::kDontUseSharedRegion
;
2132 else if ( strcmp(value
, "use") == 0 ) {
2133 gLinkContext
.sharedRegionMode
= ImageLoader::kUseSharedRegion
;
2135 else if ( value
[0] == '\0' ) {
2136 gLinkContext
.sharedRegionMode
= ImageLoader::kUseSharedRegion
;
2139 dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n");
2142 else if ( (strcmp(key
, "DYLD_SHARED_CACHE_DIR") == 0) && gLinkContext
.allowEnvVarsSharedCache
) {
2143 sSharedCacheOverrideDir
= value
;
2145 else if ( strcmp(key
, "DYLD_USE_CLOSURES") == 0 ) {
2146 // Handled elsewhere
2148 else if ( strcmp(key
, "DYLD_FORCE_INVALID_CACHE_CLOSURES") == 0 ) {
2149 if ( dyld3::internalInstall() ) {
2150 sForceInvalidSharedCacheClosureFormat
= true;
2153 else if ( strcmp(key
, "DYLD_IGNORE_PREBINDING") == 0 ) {
2154 if ( strcmp(value
, "all") == 0 ) {
2155 gLinkContext
.prebindUsage
= ImageLoader::kUseNoPrebinding
;
2157 else if ( strcmp(value
, "app") == 0 ) {
2158 gLinkContext
.prebindUsage
= ImageLoader::kUseAllButAppPredbinding
;
2160 else if ( strcmp(value
, "nonsplit") == 0 ) {
2161 gLinkContext
.prebindUsage
= ImageLoader::kUseSplitSegPrebinding
;
2163 else if ( value
[0] == '\0' ) {
2164 gLinkContext
.prebindUsage
= ImageLoader::kUseSplitSegPrebinding
;
2167 dyld::warn("unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n");
2170 #if SUPPORT_VERSIONED_PATHS
2171 else if ( strcmp(key
, "DYLD_VERSIONED_LIBRARY_PATH") == 0 ) {
2172 appendParsedColonList(value
, mainExecutableDir
, &sEnv
.DYLD_VERSIONED_LIBRARY_PATH
);
2173 #if SUPPORT_ACCELERATE_TABLES
2174 sDisableAcceleratorTables
= true;
2176 sEnv
.hasOverride
= true;
2178 else if ( strcmp(key
, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) {
2179 appendParsedColonList(value
, mainExecutableDir
, &sEnv
.DYLD_VERSIONED_FRAMEWORK_PATH
);
2180 #if SUPPORT_ACCELERATE_TABLES
2181 sDisableAcceleratorTables
= true;
2183 sEnv
.hasOverride
= true;
2186 #if !TARGET_OS_SIMULATOR
2187 else if ( (strcmp(key
, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir
== NULL
) && gLinkContext
.allowEnvVarsSharedCache
) {
2188 int fd
= dyld3::open(value
, O_WRONLY
| O_CREAT
| O_APPEND
, 0644);
2194 dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value
, errno
);
2197 else if ( (strcmp(key
, "DYLD_SKIP_MAIN") == 0)) {
2198 if ( dyld3::internalInstall() )
2201 else if ( (strcmp(key
, "DYLD_JUST_BUILD_CLOSURE") == 0) ) {
2202 // handled elsewhere
2205 else if (strcmp(key
, "DYLD_FORCE_PLATFORM") == 0) {
2206 // handled elsewhere
2208 else if (strcmp(key
, "DYLD_AMFI_FAKE") == 0) {
2209 // handled elsewhere
2212 dyld::warn("unknown environment variable: %s\n", key
);
2217 #if SUPPORT_LC_DYLD_ENVIRONMENT
2218 static void checkLoadCommandEnvironmentVariables()
2220 // <rdar://problem/8440934> Support augmenting dyld environment variables in load commands
2221 const uint32_t cmd_count
= sMainExecutableMachHeader
->ncmds
;
2222 const struct load_command
* const cmds
= (struct load_command
*)(((char*)sMainExecutableMachHeader
)+sizeof(macho_header
));
2223 const struct load_command
* cmd
= cmds
;
2224 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2226 case LC_DYLD_ENVIRONMENT
:
2228 const struct dylinker_command
* envcmd
= (struct dylinker_command
*)cmd
;
2229 const char* keyEqualsValue
= (char*)envcmd
+ envcmd
->name
.offset
;
2230 char mainExecutableDir
[strlen(sExecPath
)+2];
2231 strcpy(mainExecutableDir
, sExecPath
);
2232 char* lastSlash
= strrchr(mainExecutableDir
, '/');
2233 if ( lastSlash
!= NULL
)
2234 lastSlash
[1] = '\0';
2235 // only process variables that start with DYLD_ and end in _PATH
2236 if ( (strncmp(keyEqualsValue
, "DYLD_", 5) == 0) ) {
2237 const char* equals
= strchr(keyEqualsValue
, '=');
2238 if ( equals
!= NULL
) {
2239 if ( strncmp(&equals
[-5], "_PATH", 5) == 0 ) {
2240 const char* value
= &equals
[1];
2241 const size_t keyLen
= equals
-keyEqualsValue
;
2242 // <rdar://problem/22799635> don't let malformed load command overflow stack
2243 if ( keyLen
< 40 ) {
2245 strncpy(key
, keyEqualsValue
, keyLen
);
2247 //dyld::log("processing: %s\n", keyEqualsValue);
2248 //dyld::log("mainExecutableDir: %s\n", mainExecutableDir);
2249 #if SUPPORT_ROOT_PATH
2250 if ( (strcmp(key
, "DYLD_ROOT_PATH") == 0) || (strcmp(key
, "DYLD_PATHS_ROOT") == 0) )
2253 processDyldEnvironmentVariable(key
, value
, mainExecutableDir
);
2261 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2264 #endif // SUPPORT_LC_DYLD_ENVIRONMENT
2267 static bool hasCodeSignatureLoadCommand(const macho_header
* mh
)
2269 const uint32_t cmd_count
= mh
->ncmds
;
2270 const struct load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
2271 const struct load_command
* cmd
= cmds
;
2272 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2273 if (cmd
->cmd
== LC_CODE_SIGNATURE
)
2275 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2281 #if SUPPORT_VERSIONED_PATHS
2282 static void checkVersionedPaths()
2284 // search DYLD_VERSIONED_LIBRARY_PATH directories for dylibs and check if they are newer
2285 if ( sEnv
.DYLD_VERSIONED_LIBRARY_PATH
!= NULL
) {
2286 for(const char* const* lp
= sEnv
.DYLD_VERSIONED_LIBRARY_PATH
; *lp
!= NULL
; ++lp
) {
2287 checkDylibOverridesInDir(*lp
);
2291 // search DYLD_VERSIONED_FRAMEWORK_PATH directories for dylibs and check if they are newer
2292 if ( sEnv
.DYLD_VERSIONED_FRAMEWORK_PATH
!= NULL
) {
2293 for(const char* const* fp
= sEnv
.DYLD_VERSIONED_FRAMEWORK_PATH
; *fp
!= NULL
; ++fp
) {
2294 checkFrameworkOverridesInDir(*fp
);
2303 // For security, setuid programs ignore DYLD_* environment variables.
2304 // Additionally, the DYLD_* enviroment variables are removed
2305 // from the environment, so that any child processes don't see them.
2307 static void pruneEnvironmentVariables(const char* envp
[], const char*** applep
)
2309 #if SUPPORT_LC_DYLD_ENVIRONMENT
2310 checkLoadCommandEnvironmentVariables();
2313 // Are we testing dyld on an internal config?
2314 if ( _simple_getenv(envp
, "DYLD_SKIP_MAIN") != NULL
) {
2315 if ( dyld3::internalInstall() )
2319 // delete all DYLD_* and LD_LIBRARY_PATH environment variables
2320 int removedCount
= 0;
2321 const char** d
= envp
;
2322 for(const char** s
= envp
; *s
!= NULL
; s
++) {
2324 if ( (strncmp(*s
, "DYLD_", 5) != 0) && (strncmp(*s
, "LD_LIBRARY_PATH=", 16) != 0) ) {
2332 // slide apple parameters
2333 if ( removedCount
> 0 ) {
2336 *d
= d
[removedCount
];
2337 } while ( *d
++ != NULL
);
2338 for(int i
=0; i
< removedCount
; ++i
)
2342 // disable framework and library fallback paths for setuid binaries rdar://problem/4589305
2343 sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
= NULL
;
2344 sEnv
.DYLD_FALLBACK_LIBRARY_PATH
= NULL
;
2346 if ( removedCount
> 0 )
2347 strlcat(sLoadingCrashMessage
, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage
));
2351 static void defaultUninitializedFallbackPaths(const char* envp
[])
2354 if ( !gLinkContext
.allowClassicFallbackPaths
) {
2355 sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
= sRestrictedFrameworkFallbackPaths
;
2356 sEnv
.DYLD_FALLBACK_LIBRARY_PATH
= sRestrictedLibraryFallbackPaths
;
2360 // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment
2361 const char* home
= _simple_getenv(envp
, "HOME");;
2362 if ( sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
== NULL
) {
2363 const char** fpaths
= sFrameworkFallbackPaths
;
2365 removePathWithPrefix(fpaths
, "$HOME");
2367 paths_expand_roots(fpaths
, "$HOME", home
);
2368 sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
= fpaths
;
2371 // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment
2372 if ( sEnv
.DYLD_FALLBACK_LIBRARY_PATH
== NULL
) {
2373 const char** lpaths
= sLibraryFallbackPaths
;
2375 removePathWithPrefix(lpaths
, "$HOME");
2377 paths_expand_roots(lpaths
, "$HOME", home
);
2378 sEnv
.DYLD_FALLBACK_LIBRARY_PATH
= lpaths
;
2381 if ( sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
== NULL
)
2382 sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
= sFrameworkFallbackPaths
;
2384 if ( sEnv
.DYLD_FALLBACK_LIBRARY_PATH
== NULL
)
2385 sEnv
.DYLD_FALLBACK_LIBRARY_PATH
= sLibraryFallbackPaths
;
2390 static void checkEnvironmentVariables(const char* envp
[])
2392 if ( !gLinkContext
.allowEnvVarsPath
&& !gLinkContext
.allowEnvVarsPrint
)
2395 for(p
= envp
; *p
!= NULL
; p
++) {
2396 const char* keyEqualsValue
= *p
;
2397 if ( strncmp(keyEqualsValue
, "DYLD_", 5) == 0 ) {
2398 const char* equals
= strchr(keyEqualsValue
, '=');
2399 if ( equals
!= NULL
) {
2400 strlcat(sLoadingCrashMessage
, "\n", sizeof(sLoadingCrashMessage
));
2401 strlcat(sLoadingCrashMessage
, keyEqualsValue
, sizeof(sLoadingCrashMessage
));
2402 const char* value
= &equals
[1];
2403 const size_t keyLen
= equals
-keyEqualsValue
;
2405 strncpy(key
, keyEqualsValue
, keyLen
);
2407 if ( (strncmp(key
, "DYLD_PRINT_", 11) == 0) && !gLinkContext
.allowEnvVarsPrint
)
2409 processDyldEnvironmentVariable(key
, value
, NULL
);
2412 else if ( strncmp(keyEqualsValue
, "LD_LIBRARY_PATH=", 16) == 0 ) {
2413 const char* path
= &keyEqualsValue
[16];
2414 sEnv
.LD_LIBRARY_PATH
= parseColonList(path
, NULL
);
2418 #if SUPPORT_LC_DYLD_ENVIRONMENT
2419 checkLoadCommandEnvironmentVariables();
2420 #endif // SUPPORT_LC_DYLD_ENVIRONMENT
2422 #if SUPPORT_ROOT_PATH
2423 // <rdar://problem/11281064> DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together
2424 if ( (gLinkContext
.imageSuffix
!= NULL
&& *gLinkContext
.imageSuffix
!= NULL
) && (gLinkContext
.rootPaths
!= NULL
) ) {
2425 dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n");
2426 gLinkContext
.imageSuffix
= NULL
; // this leaks allocations from parseColonList
2431 #if __x86_64__ && !TARGET_OS_SIMULATOR
2432 static bool isGCProgram(const macho_header
* mh
, uintptr_t slide
)
2434 const uint32_t cmd_count
= mh
->ncmds
;
2435 const struct load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
2436 const struct load_command
* cmd
= cmds
;
2437 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
2439 case LC_SEGMENT_COMMAND
:
2441 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
2442 if (strcmp(seg
->segname
, "__DATA") == 0) {
2443 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
2444 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
2445 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2446 if (strncmp(sect
->sectname
, "__objc_imageinfo", 16) == 0) {
2447 const uint32_t* objcInfo
= (uint32_t*)(sect
->addr
+ slide
);
2448 return (objcInfo
[1] & 6); // 6 = (OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC)
2455 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
2461 static void getHostInfo(const macho_header
* mainExecutableMH
, uintptr_t mainExecutableSlide
)
2463 #if CPU_SUBTYPES_SUPPORTED
2465 sHostCPU
= CPU_TYPE_ARM
;
2466 sHostCPUsubtype
= CPU_SUBTYPE_ARM_V7K
;
2467 #elif __ARM_ARCH_7A__
2468 sHostCPU
= CPU_TYPE_ARM
;
2469 sHostCPUsubtype
= CPU_SUBTYPE_ARM_V7
;
2470 #elif __ARM_ARCH_6K__
2471 sHostCPU
= CPU_TYPE_ARM
;
2472 sHostCPUsubtype
= CPU_SUBTYPE_ARM_V6
;
2473 #elif __ARM_ARCH_7F__
2474 sHostCPU
= CPU_TYPE_ARM
;
2475 sHostCPUsubtype
= CPU_SUBTYPE_ARM_V7F
;
2476 #elif __ARM_ARCH_7S__
2477 sHostCPU
= CPU_TYPE_ARM
;
2478 sHostCPUsubtype
= CPU_SUBTYPE_ARM_V7S
;
2479 #elif __ARM64_ARCH_8_32__
2480 sHostCPU
= CPU_TYPE_ARM64_32
;
2481 sHostCPUsubtype
= CPU_SUBTYPE_ARM64_32_V8
;
2483 sHostCPU
= CPU_TYPE_ARM64
;
2484 sHostCPUsubtype
= CPU_SUBTYPE_ARM64E
;
2486 sHostCPU
= CPU_TYPE_ARM64
;
2487 sHostCPUsubtype
= CPU_SUBTYPE_ARM64_V8
;
2489 struct host_basic_info info
;
2490 mach_msg_type_number_t count
= HOST_BASIC_INFO_COUNT
;
2491 mach_port_t hostPort
= mach_host_self();
2492 kern_return_t result
= host_info(hostPort
, HOST_BASIC_INFO
, (host_info_t
)&info
, &count
);
2493 if ( result
!= KERN_SUCCESS
)
2494 throw "host_info() failed";
2495 sHostCPU
= info
.cpu_type
;
2496 sHostCPUsubtype
= info
.cpu_subtype
;
2497 mach_port_deallocate(mach_task_self(), hostPort
);
2499 // host_info returns CPU_TYPE_I386 even for x86_64. Override that here so that
2500 // we don't need to mask the cpu type later.
2501 sHostCPU
= CPU_TYPE_X86_64
;
2502 #if !TARGET_OS_SIMULATOR
2503 sHaswell
= (sHostCPUsubtype
== CPU_SUBTYPE_X86_64_H
);
2504 // <rdar://problem/18528074> x86_64h: Fall back to the x86_64 slice if an app requires GC.
2506 if ( isGCProgram(mainExecutableMH
, mainExecutableSlide
) ) {
2507 // When running a GC program on a haswell machine, don't use and 'h slices
2508 sHostCPUsubtype
= CPU_SUBTYPE_X86_64_ALL
;
2510 gLinkContext
.sharedRegionMode
= ImageLoader::kDontUseSharedRegion
;
2519 static void checkSharedRegionDisable(const dyld3::MachOLoaded
* mainExecutableMH
, uintptr_t mainExecutableSlide
)
2522 // if main executable has segments that overlap the shared region,
2523 // then disable using the shared region
2524 if ( mainExecutableMH
->intersectsRange(SHARED_REGION_BASE
, SHARED_REGION_SIZE
) ) {
2525 gLinkContext
.sharedRegionMode
= ImageLoader::kDontUseSharedRegion
;
2526 if ( gLinkContext
.verboseMapping
)
2527 dyld::warn("disabling shared region because main executable overlaps\n");
2530 if ( !gLinkContext
.allowEnvVarsPath
) {
2531 // <rdar://problem/15280847> use private or no shared region for suid processes
2532 gLinkContext
.sharedRegionMode
= ImageLoader::kUsePrivateSharedRegion
;
2536 #if TARGET_OS_SIMULATOR
2537 gLinkContext
.sharedRegionMode
= ImageLoader::kUsePrivateSharedRegion
;
2539 // iOS cannot run without shared region
2542 bool validImage(const ImageLoader
* possibleImage
)
2544 const size_t imageCount
= sAllImages
.size();
2545 for(size_t i
=0; i
< imageCount
; ++i
) {
2546 if ( possibleImage
== sAllImages
[i
] ) {
2553 uint32_t getImageCount()
2555 return (uint32_t)sAllImages
.size();
2558 ImageLoader
* getIndexedImage(unsigned int index
)
2560 if ( index
< sAllImages
.size() )
2561 return sAllImages
[index
];
2565 ImageLoader
* findImageByMachHeader(const struct mach_header
* target
)
2567 return findMappedRange((uintptr_t)target
);
2571 ImageLoader
* findImageContainingAddress(const void* addr
)
2573 #if SUPPORT_ACCELERATE_TABLES
2574 if ( sAllCacheImagesProxy
!= NULL
) {
2575 const mach_header
* mh
;
2578 if ( sAllCacheImagesProxy
->addressInCache(addr
, &mh
, &path
, &index
) )
2579 return sAllCacheImagesProxy
;
2582 return findMappedRange((uintptr_t)addr
);
2586 ImageLoader
* findImageContainingSymbol(const void* symbol
)
2588 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
2589 ImageLoader
* anImage
= *it
;
2590 if ( anImage
->containsSymbol(symbol
) )
2598 void forEachImageDo( void (*callback
)(ImageLoader
*, void* userData
), void* userData
)
2600 const size_t imageCount
= sAllImages
.size();
2601 for(size_t i
=0; i
< imageCount
; ++i
) {
2602 ImageLoader
* anImage
= sAllImages
[i
];
2603 (*callback
)(anImage
, userData
);
2607 ImageLoader
* findLoadedImage(const struct stat
& stat_buf
)
2609 const size_t imageCount
= sAllImages
.size();
2610 for(size_t i
=0; i
< imageCount
; ++i
){
2611 ImageLoader
* anImage
= sAllImages
[i
];
2612 if ( anImage
->statMatch(stat_buf
) )
2618 // based on ANSI-C strstr()
2619 static const char* strrstr(const char* str
, const char* sub
)
2621 const size_t sublen
= strlen(sub
);
2622 for(const char* p
= &str
[strlen(str
)]; p
!= str
; --p
) {
2623 if ( strncmp(p
, sub
, sublen
) == 0 )
2631 // Find framework path
2633 // /path/foo.framework/foo => foo.framework/foo
2634 // /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo
2635 // /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar
2636 // /path/foo.framework/Libraries/bar.dylb => NULL
2637 // /path/foo.framework/bar => NULL
2639 // Returns NULL if not a framework path
2641 static const char* getFrameworkPartialPath(const char* path
)
2643 const char* dirDot
= strrstr(path
, ".framework/");
2644 if ( dirDot
!= NULL
) {
2645 const char* dirStart
= dirDot
;
2646 for ( ; dirStart
>= path
; --dirStart
) {
2647 if ( (*dirStart
== '/') || (dirStart
== path
) ) {
2648 const char* frameworkStart
= &dirStart
[1];
2649 if ( dirStart
== path
)
2651 size_t len
= dirDot
- frameworkStart
;
2652 char framework
[len
+1];
2653 strncpy(framework
, frameworkStart
, len
);
2654 framework
[len
] = '\0';
2655 const char* leaf
= strrchr(path
, '/');
2656 if ( leaf
!= NULL
) {
2657 if ( strcmp(framework
, &leaf
[1]) == 0 ) {
2658 return frameworkStart
;
2660 if ( gLinkContext
.imageSuffix
!= NULL
) {
2661 // some debug frameworks have install names that end in _debug
2662 if ( strncmp(framework
, &leaf
[1], len
) == 0 ) {
2663 for (const char* const* suffix
=gLinkContext
.imageSuffix
; *suffix
!= NULL
; ++suffix
) {
2664 if ( strcmp(*suffix
, &leaf
[len
+1]) == 0 )
2665 return frameworkStart
;
2677 static const char* getLibraryLeafName(const char* path
)
2679 const char* start
= strrchr(path
, '/');
2680 if ( start
!= NULL
)
2687 // only for architectures that use cpu-sub-types
2688 #if CPU_SUBTYPES_SUPPORTED
2690 const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST
= -1;
2694 // A fat file may contain multiple sub-images for the same CPU type.
2695 // In that case, dyld picks which sub-image to use by scanning a table
2696 // of preferred cpu-sub-types for the running cpu.
2698 // There is one row in the table for each cpu-sub-type on which dyld might run.
2699 // The first entry in a row is that cpu-sub-type. It is followed by all
2700 // cpu-sub-types that can run on that cpu, if preferred order. Each row ends with
2701 // a "SUBTYPE_ALL" (to denote that images written to run on any cpu-sub-type are usable),
2702 // followed by one or more CPU_SUBTYPE_END_OF_LIST to pad out this row.
2708 // ARM sub-type lists
2710 const int kARM_RowCount
= 8;
2711 static const cpu_subtype_t kARM
[kARM_RowCount
][9] = {
2713 // armv7f can run: v7f, v7, v6, v5, and v4
2714 { CPU_SUBTYPE_ARM_V7F
, CPU_SUBTYPE_ARM_V7
, CPU_SUBTYPE_ARM_V6
, CPU_SUBTYPE_ARM_V5TEJ
, CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
},
2716 // armv7k can run: v7k
2717 { CPU_SUBTYPE_ARM_V7K
, CPU_SUBTYPE_END_OF_LIST
},
2719 // armv7s can run: v7s, v7, v7f, v7k, v6, v5, and v4
2720 { CPU_SUBTYPE_ARM_V7S
, CPU_SUBTYPE_ARM_V7
, CPU_SUBTYPE_ARM_V7F
, CPU_SUBTYPE_ARM_V6
, CPU_SUBTYPE_ARM_V5TEJ
, CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
},
2722 // armv7 can run: v7, v6, v5, and v4
2723 { CPU_SUBTYPE_ARM_V7
, CPU_SUBTYPE_ARM_V6
, CPU_SUBTYPE_ARM_V5TEJ
, CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
},
2725 // armv6 can run: v6, v5, and v4
2726 { CPU_SUBTYPE_ARM_V6
, CPU_SUBTYPE_ARM_V5TEJ
, CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
},
2728 // xscale can run: xscale, v5, and v4
2729 { CPU_SUBTYPE_ARM_XSCALE
, CPU_SUBTYPE_ARM_V5TEJ
, CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
},
2731 // armv5 can run: v5 and v4
2732 { CPU_SUBTYPE_ARM_V5TEJ
, CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
},
2734 // armv4 can run: v4
2735 { CPU_SUBTYPE_ARM_V4T
, CPU_SUBTYPE_ARM_ALL
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
, CPU_SUBTYPE_END_OF_LIST
},
2739 #if __ARM64_ARCH_8_32__
2741 // arm64_32 sub-type lists
2743 static const cpu_subtype_t kARM64_32
[] = { CPU_SUBTYPE_ARM64_32_V8
, CPU_SUBTYPE_END_OF_LIST
};
2746 #if __arm64__ && __LP64__
2748 // arm64[e] sub-type handing
2751 // arm64e with keys on
2752 static const cpu_subtype_t kARM64e
[] = { CPU_SUBTYPE_ARM64E
, CPU_SUBTYPE_END_OF_LIST
};
2753 // arm64 or arm64e with keys off
2754 static const cpu_subtype_t kARM64eKeysOff
[] = { CPU_SUBTYPE_ARM64E
, CPU_SUBTYPE_ARM64_V8
, CPU_SUBTYPE_ARM64_ALL
, CPU_SUBTYPE_END_OF_LIST
};
2756 // arm64 main binary
2757 static const cpu_subtype_t kARM64
[] = { CPU_SUBTYPE_ARM64_V8
, CPU_SUBTYPE_ARM64_ALL
, CPU_SUBTYPE_END_OF_LIST
};
2758 #endif // __arm64e__
2764 // x86_64 sub-type lists
2766 const int kX86_64_RowCount
= 2;
2767 static const cpu_subtype_t kX86_64
[kX86_64_RowCount
][5] = {
2769 // x86_64h can run: x86_64h, x86_64h(lib), x86_64(lib), and x86_64
2770 { CPU_SUBTYPE_X86_64_H
, (cpu_subtype_t
)(CPU_SUBTYPE_LIB64
|CPU_SUBTYPE_X86_64_H
), (cpu_subtype_t
)(CPU_SUBTYPE_LIB64
|CPU_SUBTYPE_X86_64_ALL
), CPU_SUBTYPE_X86_64_ALL
, CPU_SUBTYPE_END_OF_LIST
},
2772 // x86_64 can run: x86_64(lib) and x86_64
2773 { CPU_SUBTYPE_X86_64_ALL
, (cpu_subtype_t
)(CPU_SUBTYPE_LIB64
|CPU_SUBTYPE_X86_64_ALL
), CPU_SUBTYPE_END_OF_LIST
},
2779 // scan the tables above to find the cpu-sub-type-list for this machine
2780 static const cpu_subtype_t
* findCPUSubtypeList(cpu_type_t cpu
, cpu_subtype_t subtype
)
2785 for (int i
=0; i
< kARM_RowCount
; ++i
) {
2786 if ( kARM
[i
][0] == subtype
)
2793 case CPU_TYPE_ARM64
:
2795 return ( sKeysDisabled
? kARM64eKeysOff
: kARM64e
);
2803 case CPU_TYPE_ARM64_32
:
2809 case CPU_TYPE_X86_64
:
2810 for (int i
=0; i
< kX86_64_RowCount
; ++i
) {
2811 if ( kX86_64
[i
][0] == subtype
)
2821 // scan fat table-of-contents for best most preferred subtype
2822 static bool fatFindBestFromOrderedList(cpu_type_t cpu
, const cpu_subtype_t list
[], const fat_header
* fh
, int fd
, uint64_t* offset
, uint64_t* len
)
2824 const fat_arch
* const archs
= (fat_arch
*)(((char*)fh
)+sizeof(fat_header
));
2825 for (uint32_t subTypeIndex
=0; list
[subTypeIndex
] != CPU_SUBTYPE_END_OF_LIST
; ++subTypeIndex
) {
2826 for(uint32_t fatIndex
=0; fatIndex
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++fatIndex
) {
2827 cpu_type_t sliceCpuType
= OSSwapBigToHostInt32(archs
[fatIndex
].cputype
);
2828 cpu_subtype_t sliceCpuSubType
= OSSwapBigToHostInt32(archs
[fatIndex
].cpusubtype
) & ~CPU_SUBTYPE_MASK
;
2829 uint64_t sliceOffset
= OSSwapBigToHostInt32(archs
[fatIndex
].offset
);
2830 uint64_t sliceLen
= OSSwapBigToHostInt32(archs
[fatIndex
].size
);
2831 if ( (sliceCpuType
== cpu
) && ((list
[subTypeIndex
] & ~CPU_SUBTYPE_MASK
) == sliceCpuSubType
) ) {
2832 #if TARGET_OS_OSX && __has_feature(ptrauth_calls)
2833 if ( sOnlyPlatformArm64e
&& (sliceCpuType
== CPU_TYPE_ARM64
) && (sliceCpuSubType
== CPU_SUBTYPE_ARM64E
) ) {
2834 // if we can only load arm64e slices that are platform binaries, skip over slices that are not
2835 if ( !dyld3::MachOAnalyzer::sliceIsOSBinary(fd
, sliceOffset
, sliceLen
) )
2839 *offset
= sliceOffset
;
2848 #if !TARGET_OS_OSX || !__has_feature(ptrauth_calls)
2849 // scan fat table-of-contents for exact match of cpu and cpu-sub-type
2850 static bool fatFindExactMatch(cpu_type_t cpu
, cpu_subtype_t subtype
, const fat_header
* fh
, uint64_t* offset
, uint64_t* len
)
2852 const fat_arch
* archs
= (fat_arch
*)(((char*)fh
)+sizeof(fat_header
));
2853 for(uint32_t i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
2854 if ( ((cpu_type_t
)OSSwapBigToHostInt32(archs
[i
].cputype
) == cpu
)
2855 && ((cpu_subtype_t
)OSSwapBigToHostInt32(archs
[i
].cpusubtype
) == subtype
) ) {
2856 *offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
2857 *len
= OSSwapBigToHostInt32(archs
[i
].size
);
2865 // scan fat table-of-contents for image with matching cpu-type and runs-on-all-sub-types
2866 static bool fatFindRunsOnAllCPUs(cpu_type_t cpu
, const fat_header
* fh
, uint64_t* offset
, uint64_t* len
)
2868 const fat_arch
* archs
= (fat_arch
*)(((char*)fh
)+sizeof(fat_header
));
2869 for(uint32_t i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
2870 if ( (cpu_type_t
)OSSwapBigToHostInt32(archs
[i
].cputype
) == cpu
) {
2874 if ( (cpu_subtype_t
)OSSwapBigToHostInt32(archs
[i
].cpusubtype
) == CPU_SUBTYPE_ARM_ALL
) {
2875 *offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
2876 *len
= OSSwapBigToHostInt32(archs
[i
].size
);
2882 case CPU_TYPE_ARM64
:
2883 if ( (cpu_subtype_t
)OSSwapBigToHostInt32(archs
[i
].cpusubtype
) == CPU_SUBTYPE_ARM64_ALL
) {
2884 *offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
2885 *len
= OSSwapBigToHostInt32(archs
[i
].size
);
2891 case CPU_TYPE_X86_64
:
2892 if ( (cpu_subtype_t
)OSSwapBigToHostInt32(archs
[i
].cpusubtype
) == CPU_SUBTYPE_X86_64_ALL
) {
2893 *offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
2894 *len
= OSSwapBigToHostInt32(archs
[i
].size
);
2905 #endif // CPU_SUBTYPES_SUPPORTED
2909 // Validate the fat_header and fat_arch array:
2911 // 1) arch count would not cause array to extend past 4096 byte read buffer
2912 // 2) no slice overlaps the fat_header and arch array
2913 // 3) arch list does not contain duplicate cputype/cpusubtype tuples
2914 // 4) arch list does not have two overlapping slices.
2916 static bool fatValidate(const fat_header
* fh
)
2918 if ( fh
->magic
!= OSSwapBigToHostInt32(FAT_MAGIC
) )
2921 // since only first 4096 bytes of file read, we can only handle up to 204 slices.
2922 const uint32_t sliceCount
= OSSwapBigToHostInt32(fh
->nfat_arch
);
2923 if ( sliceCount
> 204 )
2926 // compare all slices looking for conflicts
2927 const fat_arch
* archs
= (fat_arch
*)(((char*)fh
)+sizeof(fat_header
));
2928 for (uint32_t i
=0; i
< sliceCount
; ++i
) {
2929 uint32_t i_offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
2930 uint32_t i_size
= OSSwapBigToHostInt32(archs
[i
].size
);
2931 uint32_t i_cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
2932 uint32_t i_cpusubtype
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
2933 uint32_t i_end
= i_offset
+ i_size
;
2934 // slice cannot overlap with header
2935 if ( i_offset
< 4096 )
2937 // slice size cannot overflow
2938 if ( i_end
< i_offset
)
2940 for (uint32_t j
=i
+1; j
< sliceCount
; ++j
) {
2941 uint32_t j_offset
= OSSwapBigToHostInt32(archs
[j
].offset
);
2942 uint32_t j_size
= OSSwapBigToHostInt32(archs
[j
].size
);
2943 uint32_t j_cputype
= OSSwapBigToHostInt32(archs
[j
].cputype
);
2944 uint32_t j_cpusubtype
= OSSwapBigToHostInt32(archs
[j
].cpusubtype
);
2945 uint32_t j_end
= j_offset
+ j_size
;
2946 // duplicate slices types not allowed
2947 if ( (i_cputype
== j_cputype
) && (i_cpusubtype
== j_cpusubtype
) )
2949 // slice size cannot overflow
2950 if ( j_end
< j_offset
)
2952 // check for overlap of slices
2953 if ( i_offset
<= j_offset
) {
2954 if ( j_offset
< i_end
)
2955 return false; // j overlaps end of i
2958 // j overlaps end of i
2959 if ( i_offset
< j_end
)
2960 return false; // i overlaps end of j
2968 // A fat file may contain multiple sub-images for the same cpu-type,
2969 // each optimized for a different cpu-sub-type (e.g G3 or G5).
2970 // This routine picks the optimal sub-image.
2972 static bool fatFindBest(const fat_header
* fh
, uint64_t* offset
, uint64_t* len
, int fd
=-1)
2974 if ( !fatValidate(fh
) )
2977 #if CPU_SUBTYPES_SUPPORTED
2978 // assume all dylibs loaded must have same cpu type as main executable
2979 const cpu_type_t cpu
= sMainExecutableMachHeader
->cputype
;
2981 // We only know the subtype to use if the main executable cpu type matches the host
2982 if ( cpu
== sHostCPU
) {
2983 // get preference ordered list of subtypes
2984 const cpu_subtype_t
* subTypePreferenceList
= findCPUSubtypeList(cpu
, sHostCPUsubtype
);
2986 // use ordered list to find best sub-image in fat file
2987 if ( subTypePreferenceList
!= NULL
) {
2988 if ( fatFindBestFromOrderedList(cpu
, subTypePreferenceList
, fh
, fd
, offset
, len
) )
2991 #if TARGET_OS_OSX && __has_feature(ptrauth_calls)
2992 // don't use fallbacks for macOS arm64e to ensure only compatible binaries are loaded
2995 // if running cpu is not in list, try for an exact match
2996 if ( fatFindExactMatch(cpu
, sHostCPUsubtype
, fh
, offset
, len
) )
3001 // running on an uknown cpu, can only load generic code
3002 return fatFindRunsOnAllCPUs(cpu
, fh
, offset
, len
);
3004 // just find first slice with matching architecture
3005 const fat_arch
* archs
= (fat_arch
*)(((char*)fh
)+sizeof(fat_header
));
3006 for(uint32_t i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
3007 if ( (cpu_type_t
)OSSwapBigToHostInt32(archs
[i
].cputype
) == sMainExecutableMachHeader
->cputype
) {
3008 *offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
3009 *len
= OSSwapBigToHostInt32(archs
[i
].size
);
3017 #if defined(__x86_64__) && !TARGET_OS_SIMULATOR
3018 #ifndef kIsTranslated
3019 #define kIsTranslated 0x4000000000000000ULL
3023 return ((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64
) & kIsTranslated
);
3029 // This is used to validate if a non-fat (aka thin or raw) mach-o file can be used
3030 // on the current processor. //
3031 bool isCompatibleMachO(const uint8_t* firstPage
, const char* path
, int fd
=-1, uint64_t sliceOffset
=0, uint64_t sliceLen
=-1)
3033 #if CPU_SUBTYPES_SUPPORTED
3034 // It is deemed compatible if any of the following are true:
3035 // 1) mach_header subtype is in list of compatible subtypes for running processor
3036 // 2) mach_header subtype is same as running processor subtype
3037 // 3) mach_header subtype runs on all processor variants
3038 const mach_header
* mh
= (mach_header
*)firstPage
;
3039 if ( mh
->magic
== sMainExecutableMachHeader
->magic
) {
3040 if ( mh
->cputype
== sMainExecutableMachHeader
->cputype
) {
3041 if ( mh
->cputype
== sHostCPU
) {
3042 const cpu_subtype_t mhCPUSubtype
= mh
->cpusubtype
& ~CPU_SUBTYPE_MASK
;
3043 // get preference ordered list of subtypes that this machine can use
3044 const cpu_subtype_t
* subTypePreferenceList
= findCPUSubtypeList(mh
->cputype
, sHostCPUsubtype
);
3045 if ( subTypePreferenceList
!= NULL
) {
3046 // if image's subtype is in the list, it is compatible
3047 for (const cpu_subtype_t
* p
= subTypePreferenceList
; *p
!= CPU_SUBTYPE_END_OF_LIST
; ++p
) {
3048 if ( *p
== mhCPUSubtype
) {
3049 #if TARGET_OS_OSX && __has_feature(ptrauth_calls)
3050 if ( mhCPUSubtype
== CPU_SUBTYPE_ARM64E
) {
3051 if ( !sOnlyPlatformArm64e
|| dyld3::MachOAnalyzer::sliceIsOSBinary(fd
, sliceOffset
, sliceLen
) )
3059 // have list and not in list, so not compatible
3060 throwf("incompatible cpu-subtype: 0x%08X in %s", mhCPUSubtype
, path
);
3062 // unknown cpu sub-type, but if exact match for current subtype then ok to use
3063 if ( mhCPUSubtype
== sHostCPUsubtype
)
3067 // cpu type has no ordered list of subtypes
3068 switch (mh
->cputype
) {
3070 case CPU_TYPE_X86_64
:
3071 // subtypes are not used or these architectures
3077 // For architectures that don't support cpu-sub-types
3078 // this just check the cpu type.
3079 const mach_header
* mh
= (mach_header
*)firstPage
;
3080 if ( mh
->magic
== sMainExecutableMachHeader
->magic
) {
3081 if ( mh
->cputype
== sMainExecutableMachHeader
->cputype
) {
3092 // The kernel maps in main executable before dyld gets control. We need to
3093 // make an ImageLoader* for the already mapped in main executable.
3094 static ImageLoaderMachO
* instantiateFromLoadedImage(const macho_header
* mh
, uintptr_t slide
, const char* path
)
3096 // try mach-o loader
3097 // if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
3098 ImageLoader
* image
= ImageLoaderMachO::instantiateMainExecutable(mh
, slide
, path
, gLinkContext
);
3100 return (ImageLoaderMachO
*)image
;
3103 // throw "main executable not a known format";
3106 #if SUPPORT_ACCELERATE_TABLES
3107 static bool dylibsCanOverrideCache()
3109 if ( !dyld3::internalInstall() )
3111 return ( (sSharedCacheLoadInfo
.loadAddress
!= nullptr) && (sSharedCacheLoadInfo
.loadAddress
->header
.cacheType
== kDyldSharedCacheTypeDevelopment
) );
3115 const void* imMemorySharedCacheHeader()
3117 return sSharedCacheLoadInfo
.loadAddress
;
3121 const char* getStandardSharedCacheFilePath()
3123 if ( sSharedCacheLoadInfo
.loadAddress
!= nullptr )
3124 return sSharedCacheLoadInfo
.path
;
3129 bool hasInsertedOrInterposingLibraries() {
3130 return (sInsertedDylibCount
> 0) || ImageLoader::haveInterposingTuples();
3134 #if SUPPORT_VERSIONED_PATHS
3135 static bool findInSharedCacheImage(const char* path
, bool searchByPath
, const struct stat
* stat_buf
, const macho_header
** mh
, const char** pathInCache
, long* slide
)
3137 dyld3::SharedCacheFindDylibResults results
;
3138 if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, path
, &results
) ) {
3139 *mh
= (macho_header
*)results
.mhInCache
;
3140 *pathInCache
= results
.pathInCache
;
3141 *slide
= results
.slideInCache
;
3148 bool inSharedCache(const char* path
)
3150 return dyld3::pathIsInSharedCacheImage(sSharedCacheLoadInfo
, path
);
3154 static ImageLoader
* checkandAddImage(ImageLoader
* image
, const LoadContext
& context
)
3156 // now sanity check that this loaded image does not have the same install path as any existing image
3157 const char* loadedImageInstallPath
= image
->getInstallPath();
3158 if ( image
->isDylib() && (loadedImageInstallPath
!= NULL
) && (loadedImageInstallPath
[0] == '/') ) {
3159 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
3160 ImageLoader
* anImage
= *it
;
3161 const char* installPath
= anImage
->getInstallPath();
3162 if ( installPath
!= NULL
) {
3163 if ( strcmp(loadedImageInstallPath
, installPath
) == 0 ) {
3164 //dyld::log("duplicate(%s) => %p\n", installPath, anImage);
3166 ImageLoader::deleteImage(image
);
3173 // some API's restrict what they can load
3174 if ( context
.mustBeBundle
&& !image
->isBundle() )
3175 throw "not a bundle";
3176 if ( context
.mustBeDylib
&& !image
->isDylib() )
3177 throw "not a dylib";
3179 // regular main executables cannot be loaded
3180 if ( image
->isExecutable() ) {
3181 if ( !context
.canBePIE
|| !image
->isPositionIndependentExecutable() )
3182 throw "can't load a main executable";
3185 // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list
3186 if ( ! image
->isBundle() )
3192 #if TARGET_OS_SIMULATOR
3193 static bool isSimulatorBinary(const uint8_t* firstPages
, const char* path
)
3195 const macho_header
* mh
= (macho_header
*)firstPages
;
3196 const uint32_t cmd_count
= mh
->ncmds
;
3197 const load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
3198 const load_command
* const cmdsEnd
= (load_command
*)((char*)cmds
+ mh
->sizeofcmds
);
3199 const struct load_command
* cmd
= cmds
;
3200 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
3203 case LC_VERSION_MIN_WATCHOS
:
3206 case LC_VERSION_MIN_TVOS
:
3209 case LC_VERSION_MIN_IPHONEOS
:
3212 case LC_VERSION_MIN_MACOSX
:
3213 // grandfather in a few libSystem dylibs
3214 if ((strcmp(path
, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
3215 (strcmp(path
, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
3216 (strcmp(path
, "/usr/lib/system/libsystem_pthread.dylib") == 0) ||
3217 (strcmp(path
, "/usr/lib/system/libsystem_platform_debug.dylib") == 0) ||
3218 (strcmp(path
, "/usr/lib/system/libsystem_pthread_debug.dylib") == 0) ||
3219 (strcmp(path
, "/sbin/launchd_sim_trampoline") == 0) ||
3220 (strcmp(path
, "/usr/sbin/iokitsimd") == 0) ||
3221 (strcmp(path
, "/usr/lib/system/host/liblaunch_sim.dylib") == 0))
3224 case LC_BUILD_VERSION
:
3226 // Same logic as above, but for LC_BUILD_VERSION instead of legacy load commands
3227 const struct build_version_command
* buildVersionCmd
= (build_version_command
*)cmd
;
3228 switch(buildVersionCmd
->platform
) {
3229 case PLATFORM_IOSSIMULATOR
:
3230 case PLATFORM_TVOSSIMULATOR
:
3231 case PLATFORM_WATCHOSSIMULATOR
:
3232 case PLATFORM_WATCHOS
:
3234 case PLATFORM_MACOS
:
3235 if ((strcmp(path
, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
3236 (strcmp(path
, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
3237 (strcmp(path
, "/usr/lib/system/libsystem_pthread.dylib") == 0) ||
3238 (strcmp(path
, "/usr/lib/system/libsystem_platform_debug.dylib") == 0) ||
3239 (strcmp(path
, "/usr/lib/system/libsystem_pthread_debug.dylib") == 0) ||
3240 (strcmp(path
, "/sbin/launchd_sim_trampoline") == 0) ||
3241 (strcmp(path
, "/usr/sbin/iokitsimd") == 0) ||
3242 (strcmp(path
, "/usr/lib/system/host/liblaunch_sim.dylib") == 0))
3247 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
3248 if ( cmd
> cmdsEnd
)
3256 // map in file and instantiate an ImageLoader
3257 static ImageLoader
* loadPhase6(int fd
, const struct stat
& stat_buf
, const char* path
, const LoadContext
& context
)
3259 //dyld::log("%s(%s)\n", __func__ , path);
3260 uint64_t fileOffset
= 0;
3261 uint64_t fileLength
= stat_buf
.st_size
;
3263 // validate it is a file (not directory)
3264 if ( (stat_buf
.st_mode
& S_IFMT
) != S_IFREG
)
3267 uint8_t firstPages
[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE
];
3268 bool shortPage
= false;
3270 // min mach-o file is 4K
3271 if ( fileLength
< 4096 ) {
3272 if ( pread(fd
, firstPages
, (size_t)fileLength
, 0) != (ssize_t
)fileLength
)
3273 throwf("pread of short file failed: %d", errno
);
3277 // optimistically read only first 4KB
3278 if ( pread(fd
, firstPages
, 4096, 0) != 4096 )
3279 throwf("pread of first 4K failed: %d", errno
);
3282 // if fat wrapper, find usable sub-file
3283 const fat_header
* fileStartAsFat
= (fat_header
*)firstPages
;
3284 if ( fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
3285 if ( OSSwapBigToHostInt32(fileStartAsFat
->nfat_arch
) > ((4096 - sizeof(fat_header
)) / sizeof(fat_arch
)) )
3286 throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat
->nfat_arch
));
3287 if ( fatFindBest(fileStartAsFat
, &fileOffset
, &fileLength
, fd
) ) {
3288 if ( (fileOffset
+fileLength
) > (uint64_t)(stat_buf
.st_size
) )
3289 throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf
.st_size
, fileOffset
+fileLength
);
3290 if (pread(fd
, firstPages
, 4096, fileOffset
) != 4096)
3291 throwf("pread of fat file failed: %d", errno
);
3294 throw "no matching architecture in universal wrapper";
3298 // try mach-o loader
3300 throw "file too short";
3302 if ( isCompatibleMachO(firstPages
, path
, fd
, fileOffset
, fileLength
) ) {
3304 // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
3305 const mach_header
* mh
= (mach_header
*)firstPages
;
3306 switch ( mh
->filetype
) {
3312 throw "mach-o, but wrong filetype";
3315 uint32_t headerAndLoadCommandsSize
= sizeof(macho_header
) + mh
->sizeofcmds
;
3316 if ( headerAndLoadCommandsSize
> fileLength
)
3317 dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize
, fileLength
);
3319 vm_address_t vmAllocatedFirstPages
= 0;
3320 if ( headerAndLoadCommandsSize
> MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE
) {
3321 if ( ::vm_allocate(mach_task_self(), &vmAllocatedFirstPages
, headerAndLoadCommandsSize
, VM_FLAGS_ANYWHERE
) == 0 ) {
3322 if ( ::pread(fd
, (void*)vmAllocatedFirstPages
, headerAndLoadCommandsSize
, fileOffset
) != headerAndLoadCommandsSize
)
3323 throwf("pread of all load commands failed: %d", errno
);
3324 mh
= (mach_header
*)vmAllocatedFirstPages
;
3327 throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize
, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE
);
3330 else if ( headerAndLoadCommandsSize
> 4096 ) {
3332 unsigned readAmount
= headerAndLoadCommandsSize
- 4096;
3333 if ( pread(fd
, &firstPages
[4096], readAmount
, fileOffset
+4096) != readAmount
)
3334 throwf("pread of extra load commands past 4KB failed: %d", errno
);
3337 if ( !((dyld3::MachOFile
*)mh
)->loadableIntoProcess((dyld3::Platform
)gProcessInfo
->platform
, path
) ) {
3338 throwf("mach-o, but not built for platform %s", dyld3::MachOFile::platformName((dyld3::Platform
)gProcessInfo
->platform
));
3341 #if __has_feature(ptrauth_calls)
3342 if ( !sKeysDisabled
&& ((sMainExecutableMachHeader
->cpusubtype
& ~CPU_SUBTYPE_MASK
) == CPU_SUBTYPE_ARM64E
) && ((mh
->cpusubtype
& ~CPU_SUBTYPE_MASK
) != CPU_SUBTYPE_ARM64E
) )
3343 throw "arm64 dylibs cannot be loaded into arm64e processes";
3345 ImageLoader
* image
= nullptr;
3347 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_MAP_IMAGE
, path
, 0, 0);
3348 image
= ImageLoaderMachO::instantiateFromFile(path
, fd
, (uint8_t*)mh
, headerAndLoadCommandsSize
, fileOffset
, fileLength
, stat_buf
, gLinkContext
);
3349 timer
.setData4((uint64_t)image
->machHeader());
3352 if ( vmAllocatedFirstPages
!= 0 )
3353 ::vm_deallocate(mach_task_self(), (vm_address_t
)vmAllocatedFirstPages
, headerAndLoadCommandsSize
);
3356 return checkandAddImage(image
, context
);
3359 // try other file formats here...
3362 // throw error about what was found
3363 switch (*(uint32_t*)firstPages
) {
3368 throw "mach-o, but wrong architecture";
3370 throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
3371 firstPages
[0], firstPages
[1], firstPages
[2], firstPages
[3], firstPages
[4], firstPages
[5], firstPages
[6],firstPages
[7]);
3376 static ImageLoader
* loadPhase5open(const char* path
, const LoadContext
& context
, const struct stat
& stat_buf
, std::vector
<const char*>* exceptions
)
3378 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3380 // open file (automagically closed when this function exits)
3381 FileOpener
file(path
);
3383 // just return NULL if file not found, but record any other errors
3384 if ( file
.getFileDescriptor() == -1 ) {
3386 if ( err
!= ENOENT
) {
3388 if ( (err
== EPERM
) && sandboxBlockedOpen(path
) )
3389 newMsg
= dyld::mkstringf("file system sandbox blocked open() of '%s'", path
);
3391 newMsg
= dyld::mkstringf("%s: open() failed with errno=%d", path
, err
);
3392 exceptions
->push_back(newMsg
);
3398 return loadPhase6(file
.getFileDescriptor(), stat_buf
, path
, context
);
3400 catch (const char* msg
) {
3401 const char* newMsg
= dyld::mkstringf("%s: %s", path
, msg
);
3402 exceptions
->push_back(newMsg
);
3408 static bool isFileRelativePath(const char* path
)
3410 if ( path
[0] == '/' )
3412 if ( path
[0] != '.' )
3414 if ( path
[1] == '/' )
3416 if ( (path
[1] == '.') && (path
[2] == '/') )
3421 static ImageLoader
* loadPhase5check(const char* path
, const char* orgPath
, const LoadContext
& context
);
3425 static ImageLoader
* loadPhase5load(const char* path
, const char* orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3427 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3429 // <rdar://problem/47682983> don't allow file system relative paths in hardened programs
3430 if ( (exceptions
!= NULL
) && !gLinkContext
.allowEnvVarsPath
&& isFileRelativePath(path
) ) {
3431 exceptions
->push_back("file system relative paths not allowed in hardened programs");
3435 #if SUPPORT_ACCELERATE_TABLES
3436 if ( sAllCacheImagesProxy
!= NULL
) {
3437 if ( sAllCacheImagesProxy
->hasDylib(path
, &cacheIndex
) )
3438 return sAllCacheImagesProxy
;
3442 struct stat statBuf
;
3443 bool didStat
= false;
3445 __block
dyld3::SharedCacheFindDylibResults shareCacheResults
;
3446 shareCacheResults
.image
= nullptr;
3448 #if TARGET_OS_SIMULATOR
3450 auto findSharedCacheImage
= ^() {
3451 // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
3452 return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, orgPath
, &shareCacheResults
);
3457 auto findSharedCacheImage
= ^() {
3458 return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, path
, &shareCacheResults
);
3463 if ( findSharedCacheImage() ) {
3464 // see if this image in the cache was already loaded via a different path
3465 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); ++it
) {
3466 ImageLoader
* anImage
= *it
;
3467 if ( (const mach_header
*)anImage
->machHeader() == shareCacheResults
.mhInCache
)
3470 // if RTLD_NOLOAD, do nothing if not already loaded
3471 if ( context
.dontLoad
) {
3472 // <rdar://33412890> possible that there is an override of cache
3473 if ( dyld3::stat(path
, &statBuf
) == 0 ) {
3474 ImageLoader
* imageLoader
= findLoadedImage(statBuf
);
3475 if ( imageLoader
!= NULL
)
3480 bool useCache
= false;
3481 if ( shareCacheResults
.image
== nullptr ) {
3482 // HACK to support old caches
3483 existsOnDisk
= ( dyld3::stat(path
, &statBuf
) == 0 );
3486 useCache
= !existsOnDisk
;
3489 // <rdar://problem/7014995> zero out stat buffer so mtime, etc are zero for items from the shared cache
3490 bzero(&statBuf
, sizeof(statBuf
));
3491 if ( shareCacheResults
.image
->overridableDylib() ) {
3492 existsOnDisk
= ( dyld3::stat(path
, &statBuf
) == 0 );
3494 if ( sSharedCacheLoadInfo
.loadAddress
->header
.dylibsExpectedOnDisk
) {
3495 // old style macOS with dylibs on disk
3496 uint64_t expectedINode
;
3497 uint64_t expectedMtime
;
3498 if ( shareCacheResults
.image
->hasFileModTimeAndInode(expectedINode
, expectedMtime
) ) {
3499 // if dylib found has same inode/mtime as one in cache, use one in cache
3500 if ( (expectedMtime
== statBuf
.st_mtime
) && (expectedINode
== statBuf
.st_ino
) )
3505 // MRM style where dylibs are not on disk
3506 if ( !existsOnDisk
) {
3507 // looking at path where dylib should be, and we expect it to not be there but rather in the cache
3508 // Its possible we are looking at a deleted symlink path. For example, we are trying to open .../AppKit but
3509 // there's already a loaded root of .../Versions/C/AppKit. That used to work when the symlink was on-disk as
3510 // we'd realpath to find the shared cache path. Now we record the aliases in the cache and delete the symlinks.
3511 const char* pathInSharedCache
= shareCacheResults
.image
->path();
3512 if ( strcmp(path
, pathInSharedCache
) != 0 ) {
3513 ImageLoader
* imageLoader
= loadPhase5check(pathInSharedCache
, orgPath
, context
);
3514 if ( imageLoader
!= NULL
)
3519 else if ( !sRootsChecker
.onDiskFileIsRoot(path
, sSharedCacheLoadInfo
.loadAddress
,
3520 shareCacheResults
.image
, nullptr, statBuf
.st_ino
, statBuf
.st_mtime
) ) {
3521 // we found a file on disk, at the same path as the dyld cache has a dylib and it is one of the magic three
3527 // we are trying to override a dylib in the cache that does not allow overrides, ignore override and use cache
3532 const dyld3::MachOFile
* cacheDylibMH
= (dyld3::MachOFile
*)shareCacheResults
.mhInCache
;
3533 if ( !cacheDylibMH
->loadableIntoProcess((dyld3::Platform
)gProcessInfo
->platform
, path
) )
3534 throwf("mach-o, but not built for platform %s", dyld3::MachOFile::platformName((dyld3::Platform
)gProcessInfo
->platform
));
3536 ImageLoader
* imageLoader
= ImageLoaderMachO::instantiateFromCache((macho_header
*)cacheDylibMH
, shareCacheResults
.pathInCache
, shareCacheResults
.slideInCache
, statBuf
, gLinkContext
);
3537 return checkandAddImage(imageLoader
, context
);
3541 // not in cache or cache not usable
3543 existsOnDisk
= ( dyld3::stat(path
, &statBuf
) == 0 );
3546 if ( existsOnDisk
) {
3547 // in case image was renamed or found via symlinks, check for inode match
3548 ImageLoader
* imageLoader
= findLoadedImage(statBuf
);
3549 if ( imageLoader
!= NULL
)
3551 // do nothing if not already loaded and if RTLD_NOLOAD
3552 if ( context
.dontLoad
)
3555 imageLoader
= loadPhase5open(path
, context
, statBuf
, exceptions
);
3556 if ( imageLoader
!= NULL
) {
3557 if ( shareCacheResults
.image
!= nullptr ) {
3558 // if image was found in cache, but is overridden by a newer file on disk, record what the image overrides
3559 imageLoader
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
3565 // just return NULL if file not found, but record any other errors
3566 if ( (statErrNo
!= ENOENT
) && (statErrNo
!= 0) ) {
3567 if ( (statErrNo
== EPERM
) && sandboxBlockedStat(path
) )
3568 exceptions
->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path
));
3570 exceptions
->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path
, statErrNo
));
3575 // look for path match with existing loaded images
3576 static ImageLoader
* loadPhase5check(const char* path
, const char* orgPath
, const LoadContext
& context
)
3578 //dyld::log("%s(%s, %s)\n", __func__ , path, orgPath);
3579 // search path against load-path and install-path of all already loaded images
3580 uint32_t hash
= ImageLoader::hash(path
);
3581 //dyld::log("check() hash=%d, path=%s\n", hash, path);
3582 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
3583 ImageLoader
* anImage
= *it
;
3584 // check hash first to cut down on strcmp calls
3585 //dyld::log(" check() hash=%d, path=%s\n", anImage->getPathHash(), anImage->getPath());
3586 if ( anImage
->getPathHash() == hash
) {
3587 if ( strcmp(path
, anImage
->getPath()) == 0 ) {
3588 // if we are looking for a dylib don't return something else
3589 if ( !context
.mustBeDylib
|| anImage
->isDylib() )
3593 if ( context
.matchByInstallName
|| anImage
->matchInstallPath() ) {
3594 const char* installPath
= anImage
->getInstallPath();
3595 if ( installPath
!= NULL
) {
3596 if ( strcmp(path
, installPath
) == 0 ) {
3597 // if we are looking for a dylib don't return something else
3598 if ( !context
.mustBeDylib
|| anImage
->isDylib() )
3603 // an install name starting with @rpath should match by install name, not just real path
3604 if ( (orgPath
[0] == '@') && (strncmp(orgPath
, "@rpath/", 7) == 0) ) {
3605 const char* installPath
= anImage
->getInstallPath();
3606 if ( installPath
!= NULL
) {
3607 if ( !context
.mustBeDylib
|| anImage
->isDylib() ) {
3608 if ( strcmp(orgPath
, installPath
) == 0 )
3615 //dyld::log("%s(%s) => NULL\n", __func__, path);
3620 // open or check existing
3621 static ImageLoader
* loadPhase5(const char* path
, const char* orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3623 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3625 // check for specific dylib overrides
3626 for (std::vector
<DylibOverride
>::iterator it
= sDylibOverrides
.begin(); it
!= sDylibOverrides
.end(); ++it
) {
3627 if ( strcmp(it
->installName
, path
) == 0 ) {
3628 path
= it
->override
;
3633 if ( exceptions
!= NULL
)
3634 return loadPhase5load(path
, orgPath
, context
, cacheIndex
, exceptions
);
3636 return loadPhase5check(path
, orgPath
, context
);
3639 // try with and without image suffix
3640 static ImageLoader
* loadPhase4(const char* path
, const char* orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3642 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3643 ImageLoader
* image
= NULL
;
3644 if ( gLinkContext
.imageSuffix
!= NULL
) {
3645 for (const char* const* suffix
=gLinkContext
.imageSuffix
; *suffix
!= NULL
; ++suffix
) {
3646 char pathWithSuffix
[strlen(path
)+strlen(*suffix
)+2];
3647 ImageLoader::addSuffix(path
, *suffix
, pathWithSuffix
);
3648 image
= loadPhase5(pathWithSuffix
, orgPath
, context
, cacheIndex
, exceptions
);
3649 if ( image
!= NULL
)
3652 if ( image
!= NULL
) {
3653 // if original path is in the dyld cache, then mark this one found as an override
3654 dyld3::SharedCacheFindDylibResults shareCacheResults
;
3655 if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, path
, &shareCacheResults
) && (shareCacheResults
.image
!= nullptr) )
3656 image
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
3659 if ( image
== NULL
)
3660 image
= loadPhase5(path
, orgPath
, context
, cacheIndex
, exceptions
);
3664 static ImageLoader
* loadPhase2(const char* path
, const char* orgPath
, const LoadContext
& context
,
3665 const char* const frameworkPaths
[], const char* const libraryPaths
[],
3666 unsigned& cacheIndex
, std::vector
<const char*>* exceptions
); // forward reference
3669 // expand @ variables
3670 static ImageLoader
* loadPhase3(const char* path
, const char* orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3672 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3673 ImageLoader
* image
= NULL
;
3674 if ( strncmp(path
, "@executable_path/", 17) == 0 ) {
3675 // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305
3676 if ( !gLinkContext
.allowAtPaths
)
3677 throwf("unsafe use of @executable_path in %s with restricted binary (Codesign main executable with Library Validation to allow @ paths)", context
.origin
);
3678 // handle @executable_path path prefix
3679 const char* executablePath
= sExecPath
;
3680 char newPath
[strlen(executablePath
) + strlen(path
)];
3681 strcpy(newPath
, executablePath
);
3682 char* addPoint
= strrchr(newPath
,'/');
3683 if ( addPoint
!= NULL
)
3684 strcpy(&addPoint
[1], &path
[17]);
3686 strcpy(newPath
, &path
[17]);
3687 image
= loadPhase4(newPath
, orgPath
, context
, cacheIndex
, exceptions
);
3688 if ( image
!= NULL
)
3691 // perhaps main executable path is a sym link, find realpath and retry
3692 char resolvedPath
[PATH_MAX
];
3693 if ( realpath(sExecPath
, resolvedPath
) != NULL
) {
3694 char newRealPath
[strlen(resolvedPath
) + strlen(path
)];
3695 strcpy(newRealPath
, resolvedPath
);
3696 addPoint
= strrchr(newRealPath
,'/');
3697 if ( addPoint
!= NULL
)
3698 strcpy(&addPoint
[1], &path
[17]);
3700 strcpy(newRealPath
, &path
[17]);
3701 image
= loadPhase4(newRealPath
, orgPath
, context
, cacheIndex
, exceptions
);
3702 if ( image
!= NULL
)
3706 else if ( (strncmp(path
, "@loader_path/", 13) == 0) && (context
.origin
!= NULL
) ) {
3707 // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305
3708 if ( !gLinkContext
.allowAtPaths
&& (strcmp(context
.origin
, sExecPath
) == 0) )
3709 throwf("unsafe use of @loader_path in %s with restricted binary (Codesign main executable with Library Validation to allow @ paths)", context
.origin
);
3710 // handle @loader_path path prefix
3711 char newPath
[strlen(context
.origin
) + strlen(path
)];
3712 strcpy(newPath
, context
.origin
);
3713 char* addPoint
= strrchr(newPath
,'/');
3714 if ( addPoint
!= NULL
)
3715 strcpy(&addPoint
[1], &path
[13]);
3717 strcpy(newPath
, &path
[13]);
3718 image
= loadPhase4(newPath
, orgPath
, context
, cacheIndex
, exceptions
);
3719 if ( image
!= NULL
)
3722 // perhaps loader path is a sym link, find realpath and retry
3723 char resolvedPath
[PATH_MAX
];
3724 if ( realpath(context
.origin
, resolvedPath
) != NULL
) {
3725 char newRealPath
[strlen(resolvedPath
) + strlen(path
)];
3726 strcpy(newRealPath
, resolvedPath
);
3727 addPoint
= strrchr(newRealPath
,'/');
3728 if ( addPoint
!= NULL
)
3729 strcpy(&addPoint
[1], &path
[13]);
3731 strcpy(newRealPath
, &path
[13]);
3732 image
= loadPhase4(newRealPath
, orgPath
, context
, cacheIndex
, exceptions
);
3733 if ( image
!= NULL
)
3737 else if ( context
.implicitRPath
|| (strncmp(path
, "@rpath/", 7) == 0) ) {
3738 const char* trailingPath
= (strncmp(path
, "@rpath/", 7) == 0) ? &path
[7] : path
;
3739 // substitute @rpath with all -rpath paths up the load chain
3740 for(const ImageLoader::RPathChain
* rp
=context
.rpath
; rp
!= NULL
; rp
=rp
->next
) {
3741 if (rp
->paths
!= NULL
) {
3742 for(std::vector
<const char*>::iterator it
=rp
->paths
->begin(); it
!= rp
->paths
->end(); ++it
) {
3743 const char* anRPath
= *it
;
3744 char newPath
[strlen(anRPath
) + strlen(trailingPath
)+2];
3745 strcpy(newPath
, anRPath
);
3746 if ( newPath
[strlen(newPath
)-1] != '/' )
3747 strcat(newPath
, "/");
3748 strcat(newPath
, trailingPath
);
3749 image
= loadPhase4(newPath
, orgPath
, context
, cacheIndex
, exceptions
);
3750 if ( gLinkContext
.verboseRPaths
&& (exceptions
!= NULL
) ) {
3751 if ( image
!= NULL
)
3752 dyld::log("RPATH successful expansion of %s to: %s\n", orgPath
, newPath
);
3754 dyld::log("RPATH failed expanding %s to: %s\n", orgPath
, newPath
);
3756 if ( image
!= NULL
)
3762 // substitute @rpath with LD_LIBRARY_PATH
3763 if ( sEnv
.LD_LIBRARY_PATH
!= NULL
) {
3764 image
= loadPhase2(trailingPath
, orgPath
, context
, NULL
, sEnv
.LD_LIBRARY_PATH
, cacheIndex
, exceptions
);
3765 if ( image
!= NULL
)
3769 // if this is the "open" pass, don't try to open @rpath/... as a relative path
3770 if ( (exceptions
!= NULL
) && (trailingPath
!= path
) )
3773 else if ( !gLinkContext
.allowEnvVarsPath
&& (path
[0] != '/' ) ) {
3774 throwf("unsafe use of relative rpath %s in %s with restricted binary", path
, context
.origin
);
3777 return loadPhase4(path
, orgPath
, context
, cacheIndex
, exceptions
);
3780 static ImageLoader
* loadPhase2cache(const char* path
, const char *orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
) {
3781 ImageLoader
* image
= NULL
;
3782 #if !TARGET_OS_SIMULATOR
3783 if ( (exceptions
!= NULL
) && (gLinkContext
.allowEnvVarsPath
|| !isFileRelativePath(path
)) && (path
[0] != '@') ) {
3784 char resolvedPath
[PATH_MAX
];
3785 realpath(path
, resolvedPath
);
3787 // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
3788 if ( (myerr
== ENOENT
) || (myerr
== 0) )
3790 image
= loadPhase4(resolvedPath
, orgPath
, context
, cacheIndex
, exceptions
);
3799 static ImageLoader
* loadPhase2(const char* path
, const char* orgPath
, const LoadContext
& context
,
3800 const char* const frameworkPaths
[], const char* const libraryPaths
[],
3801 unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3803 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3804 ImageLoader
* image
= NULL
;
3805 const char* frameworkPartialPath
= getFrameworkPartialPath(path
);
3806 if ( frameworkPaths
!= NULL
) {
3807 if ( frameworkPartialPath
!= NULL
) {
3808 const size_t frameworkPartialPathLen
= strlen(frameworkPartialPath
);
3809 for(const char* const* fp
= frameworkPaths
; *fp
!= NULL
; ++fp
) {
3810 char npath
[strlen(*fp
)+frameworkPartialPathLen
+8];
3813 strcat(npath
, frameworkPartialPath
);
3814 //dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath);
3815 image
= loadPhase4(npath
, orgPath
, context
, cacheIndex
, exceptions
);
3816 // Look in the cache if appropriate
3818 image
= loadPhase2cache(npath
, orgPath
, context
, cacheIndex
, exceptions
);
3819 if ( image
!= NULL
) {
3820 // if original path is in the dyld cache, then mark this one found as an override
3821 dyld3::SharedCacheFindDylibResults shareCacheResults
;
3822 if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, path
, &shareCacheResults
) && (shareCacheResults
.image
!= nullptr) ) {
3823 image
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
3825 #if SUPPORT_ROOT_PATH
3826 else if ( (gLinkContext
.rootPaths
!= nullptr)
3827 && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, orgPath
, &shareCacheResults
)
3828 && (shareCacheResults
.image
!= nullptr) ) {
3829 // DYLD_ROOT_PATH, ie, iOSMac, also needs to check if the original path is overridden
3830 // as the root prefix has been applied to 'path', but the framework path searches without a root path prefix
3831 image
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
3839 // <rdar://problem/12649639> An executable with the same name as a framework & DYLD_LIBRARY_PATH pointing to it gets loaded twice
3840 // <rdar://problem/14160846> Some apps depend on frameworks being found via library paths
3841 if ( (libraryPaths
!= NULL
) && ((frameworkPartialPath
== NULL
) || sFrameworksFoundAsDylibs
) ) {
3842 const char* libraryLeafName
= getLibraryLeafName(path
);
3843 const size_t libraryLeafNameLen
= strlen(libraryLeafName
);
3844 for(const char* const* lp
= libraryPaths
; *lp
!= NULL
; ++lp
) {
3845 char libpath
[strlen(*lp
)+libraryLeafNameLen
+8];
3846 strcpy(libpath
, *lp
);
3847 strcat(libpath
, "/");
3848 strcat(libpath
, libraryLeafName
);
3849 //dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath);
3850 image
= loadPhase4(libpath
, orgPath
, context
, cacheIndex
, exceptions
);
3851 // Look in the cache if appropriate
3853 image
= loadPhase2cache(libpath
, orgPath
, context
, cacheIndex
, exceptions
);
3854 if ( image
!= NULL
) {
3855 // if original path is in the dyld cache, then mark this one found as an override
3856 dyld3::SharedCacheFindDylibResults shareCacheResults
;
3857 if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, path
, &shareCacheResults
) && (shareCacheResults
.image
!= nullptr) ) {
3858 image
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
3860 #if SUPPORT_ROOT_PATH
3861 else if ( (gLinkContext
.rootPaths
!= nullptr)
3862 && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, orgPath
, &shareCacheResults
)
3863 && (shareCacheResults
.image
!= nullptr) ) {
3864 // DYLD_ROOT_PATH, ie, iOSMac, also needs to check if the original path is overridden
3865 // as the root prefix has been applied to 'path', but the library path searches without a root path prefix
3866 image
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
3876 // try search overrides and fallbacks
3877 static ImageLoader
* loadPhase1(const char* path
, const char* orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3879 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3880 ImageLoader
* image
= NULL
;
3882 bool pathIsInDyldCacheWhichCannotBeOverridden
= false;
3883 if ( sSharedCacheLoadInfo
.loadAddress
!= nullptr ) {
3884 pathIsInDyldCacheWhichCannotBeOverridden
= sSharedCacheLoadInfo
.loadAddress
->hasNonOverridablePath(path
);
3887 // <rdar://problem/48490116> dyld customer cache cannot be overridden
3888 if ( !pathIsInDyldCacheWhichCannotBeOverridden
) {
3889 // handle LD_LIBRARY_PATH environment variables that force searching
3890 if ( context
.useLdLibraryPath
&& (sEnv
.LD_LIBRARY_PATH
!= NULL
) ) {
3891 image
= loadPhase2(path
, orgPath
, context
, NULL
, sEnv
.LD_LIBRARY_PATH
, cacheIndex
,exceptions
);
3892 if ( image
!= NULL
)
3896 // handle DYLD_ environment variables that force searching
3897 if ( context
.useSearchPaths
&& ((sEnv
.DYLD_FRAMEWORK_PATH
!= NULL
) || (sEnv
.DYLD_LIBRARY_PATH
!= NULL
)) ) {
3898 image
= loadPhase2(path
, orgPath
, context
, sEnv
.DYLD_FRAMEWORK_PATH
, sEnv
.DYLD_LIBRARY_PATH
, cacheIndex
, exceptions
);
3899 if ( image
!= NULL
)
3905 image
= loadPhase3(path
, orgPath
, context
, cacheIndex
, exceptions
);
3906 if ( image
!= NULL
)
3909 // try fallback paths during second time (will open file)
3910 const char* const* fallbackLibraryPaths
= sEnv
.DYLD_FALLBACK_LIBRARY_PATH
;
3911 if ( (fallbackLibraryPaths
!= NULL
) && !context
.useFallbackPaths
)
3912 fallbackLibraryPaths
= NULL
;
3913 if ( !context
.dontLoad
&& (exceptions
!= NULL
) && ((sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
!= NULL
) || (fallbackLibraryPaths
!= NULL
)) ) {
3914 image
= loadPhase2(path
, orgPath
, context
, sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
, fallbackLibraryPaths
, cacheIndex
, exceptions
);
3915 if ( image
!= NULL
)
3919 // <rdar://problem/47682983> if hardened app calls dlopen() with a leaf path, dyld should only look in /usr/lib
3920 if ( context
.useLdLibraryPath
&& (fallbackLibraryPaths
== NULL
) ) {
3921 const char* stdPaths
[2] = { "/usr/lib", NULL
};
3922 image
= loadPhase2(path
, orgPath
, context
, NULL
, stdPaths
, cacheIndex
, exceptions
);
3923 if ( image
!= NULL
)
3927 #if SUPPORT_VERSIONED_PATHS
3928 // <rdar://problem/53215116> DYLD_VERSIONED_FRAMEWORK_PATH fails to load a framework if it does not also exist at the system install path
3929 // Scan to see if the dylib appears in a versioned path. Don't worry if we find the newest, that will handled later
3930 if ( !context
.dontLoad
&& (exceptions
!= NULL
) && ((sEnv
.DYLD_VERSIONED_FRAMEWORK_PATH
!= NULL
) || (sEnv
.DYLD_VERSIONED_LIBRARY_PATH
!= NULL
)) ) {
3931 image
= loadPhase2(path
, orgPath
, context
, sEnv
.DYLD_VERSIONED_FRAMEWORK_PATH
, sEnv
.DYLD_VERSIONED_LIBRARY_PATH
, cacheIndex
, exceptions
);
3932 if ( image
!= NULL
)
3940 // try root substitutions
3941 static ImageLoader
* loadPhase0(const char* path
, const char* orgPath
, const LoadContext
& context
, unsigned& cacheIndex
, std::vector
<const char*>* exceptions
)
3943 //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
3946 // handle macOS dylibs dlopen()ing versioned path which needs to map to flat path in mazipan simulator
3947 if ( gLinkContext
.iOSonMac
&& strstr(path
, ".framework/Versions/")) {
3948 uintptr_t sourceOffset
= 0;
3949 uintptr_t destOffset
= 0;
3950 size_t sourceLangth
= strlen(path
);
3951 char flatPath
[sourceLangth
];
3953 const char* frameworkBase
= NULL
;
3954 while ((frameworkBase
= strstr(&path
[sourceOffset
], ".framework/Versions/"))) {
3955 uintptr_t foundLength
= (frameworkBase
- &path
[sourceOffset
]) + strlen(".framework/") ;
3956 strlcat(&flatPath
[destOffset
], &path
[sourceOffset
], foundLength
);
3957 sourceOffset
+= foundLength
+ strlen("Versions/") + 1;
3958 destOffset
+= foundLength
- 1;
3960 strlcat(&flatPath
[destOffset
], &path
[sourceOffset
], sourceLangth
);
3961 ImageLoader
* image
= loadPhase0(flatPath
, orgPath
, context
, cacheIndex
, exceptions
);
3962 if ( image
!= NULL
)
3967 #if SUPPORT_ROOT_PATH
3968 // handle DYLD_ROOT_PATH which forces absolute paths to use a new root
3969 if ( (gLinkContext
.rootPaths
!= NULL
) && (path
[0] == '/') ) {
3970 for(const char* const* rootPath
= gLinkContext
.rootPaths
; *rootPath
!= NULL
; ++rootPath
) {
3971 size_t rootLen
= strlen(*rootPath
);
3972 if ( strncmp(path
, *rootPath
, rootLen
) != 0 ) {
3973 char newPath
[rootLen
+ strlen(path
)+2];
3974 strcpy(newPath
, *rootPath
);
3975 strcat(newPath
, path
);
3976 ImageLoader
* image
= loadPhase1(newPath
, orgPath
, context
, cacheIndex
, exceptions
);
3977 if ( image
!= NULL
)
3985 return loadPhase1(path
, orgPath
, context
, cacheIndex
, exceptions
);
3989 // Given all the DYLD_ environment variables, the general case for loading libraries
3990 // is that any given path expands into a list of possible locations to load. We
3991 // also must take care to ensure two copies of the "same" library are never loaded.
3993 // The algorithm used here is that there is a separate function for each "phase" of the
3994 // path expansion. Each phase function calls the next phase with each possible expansion
3995 // of that phase. The result is the last phase is called with all possible paths.
3997 // To catch duplicates the algorithm is run twice. The first time, the last phase checks
3998 // the path against all loaded images. The second time, the last phase calls open() on
3999 // the path. Either time, if an image is found, the phases all unwind without checking
4002 ImageLoader
* load(const char* path
, const LoadContext
& context
, unsigned& cacheIndex
)
4004 CRSetCrashLogMessage2(path
);
4005 const char* orgPath
= path
;
4006 cacheIndex
= UINT32_MAX
;
4008 //dyld::log("%s(%s)\n", __func__ , path);
4009 char realPath
[PATH_MAX
];
4010 // when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match
4011 if ( context
.useSearchPaths
&& ( gLinkContext
.imageSuffix
!= NULL
&& *gLinkContext
.imageSuffix
!= NULL
) ) {
4012 if ( realpath(path
, realPath
) != NULL
)
4016 // try all path permutations and check against existing loaded images
4018 ImageLoader
* image
= loadPhase0(path
, orgPath
, context
, cacheIndex
, NULL
);
4019 if ( image
!= NULL
) {
4020 CRSetCrashLogMessage2(NULL
);
4024 // try all path permutations and try open() until first success
4025 std::vector
<const char*> exceptions
;
4026 image
= loadPhase0(path
, orgPath
, context
, cacheIndex
, &exceptions
);
4027 #if !TARGET_OS_SIMULATOR
4028 // <rdar://problem/16704628> support symlinks on disk to a path in dyld shared cache
4030 image
= loadPhase2cache(path
, orgPath
, context
, cacheIndex
, &exceptions
);
4032 CRSetCrashLogMessage2(NULL
);
4033 if ( image
!= NULL
) {
4034 // <rdar://problem/6916014> leak in dyld during dlopen when using DYLD_ variables
4035 for (std::vector
<const char*>::iterator it
= exceptions
.begin(); it
!= exceptions
.end(); ++it
) {
4038 // if loaded image is not from cache, but original path is in cache
4039 // set gSharedCacheOverridden flag to disable some ObjC optimizations
4040 if ( !gSharedCacheOverridden
&& !image
->inSharedCache() && image
->isDylib() && dyld3::MachOFile::isSharedCacheEligiblePath(path
) && inSharedCache(path
) ) {
4041 gSharedCacheOverridden
= true;
4043 // <rdar://problem/59327556> if file loaded via symlink to a root of something in dyld cache, mark it as an override
4044 dyld3::SharedCacheFindDylibResults shareCacheResults
;
4045 if ( !image
->inSharedCache() && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo
, image
->getRealPath(), &shareCacheResults
) && (shareCacheResults
.image
!= nullptr) )
4046 image
->setOverridesCachedDylib(shareCacheResults
.image
->imageNum());
4050 else if ( exceptions
.size() == 0 ) {
4051 if ( context
.dontLoad
) {
4055 throw "image not found";
4058 const char* msgStart
= "no suitable image found. Did find:";
4059 const char* delim
= "\n\t";
4060 size_t allsizes
= strlen(msgStart
)+8;
4061 for (size_t i
=0; i
< exceptions
.size(); ++i
)
4062 allsizes
+= (strlen(exceptions
[i
]) + strlen(delim
));
4063 char* fullMsg
= new char[allsizes
];
4064 strcpy(fullMsg
, msgStart
);
4065 for (size_t i
=0; i
< exceptions
.size(); ++i
) {
4066 strcat(fullMsg
, delim
);
4067 strcat(fullMsg
, exceptions
[i
]);
4068 free((void*)exceptions
[i
]);
4070 throw (const char*)fullMsg
;
4078 static void mapSharedCache(uintptr_t mainExecutableSlide
)
4080 dyld3::SharedCacheOptions opts
;
4081 opts
.cacheDirOverride
= sSharedCacheOverrideDir
;
4082 opts
.forcePrivate
= (gLinkContext
.sharedRegionMode
== ImageLoader::kUsePrivateSharedRegion
);
4083 #if __x86_64__ && !TARGET_OS_SIMULATOR
4084 opts
.useHaswell
= sHaswell
;
4086 opts
.useHaswell
= false;
4088 opts
.verbose
= gLinkContext
.verboseMapping
;
4089 // <rdar://problem/32031197> respect -disable_aslr boot-arg
4090 // <rdar://problem/56299169> kern.bootargs is now blocked
4091 opts
.disableASLR
= (mainExecutableSlide
== 0) && dyld3::internalInstall(); // infer ASLR is off if main executable is not slid
4092 loadDyldCache(opts
, &sSharedCacheLoadInfo
);
4094 // update global state
4095 if ( sSharedCacheLoadInfo
.loadAddress
!= nullptr ) {
4096 gLinkContext
.dyldCache
= sSharedCacheLoadInfo
.loadAddress
;
4097 dyld::gProcessInfo
->processDetachedFromSharedRegion
= opts
.forcePrivate
;
4098 dyld::gProcessInfo
->sharedCacheSlide
= sSharedCacheLoadInfo
.slide
;
4099 dyld::gProcessInfo
->sharedCacheBaseAddress
= (unsigned long)sSharedCacheLoadInfo
.loadAddress
;
4100 sSharedCacheLoadInfo
.loadAddress
->getUUID(dyld::gProcessInfo
->sharedCacheUUID
);
4101 dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A
, sSharedCacheLoadInfo
.path
, (const uuid_t
*)&dyld::gProcessInfo
->sharedCacheUUID
[0], {0,0}, {{ 0, 0 }}, (const mach_header
*)sSharedCacheLoadInfo
.loadAddress
);
4104 //#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
4105 // RAM disk booting does not have shared cache yet
4106 // Don't make lack of a shared cache fatal in that case
4107 // if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
4108 // if ( sSharedCacheLoadInfo.errorMessage != nullptr )
4109 // halt(sSharedCacheLoadInfo.errorMessage);
4111 // halt("error loading dyld shared cache");
4118 // create when NSLinkModule is called for a second time on a bundle
4119 ImageLoader
* cloneImage(ImageLoader
* image
)
4121 // open file (automagically closed when this function exits)
4122 FileOpener
file(image
->getPath());
4124 struct stat stat_buf
;
4125 if ( fstat(file
.getFileDescriptor(), &stat_buf
) == -1)
4128 dyld::LoadContext context
;
4129 context
.useSearchPaths
= false;
4130 context
.useFallbackPaths
= false;
4131 context
.useLdLibraryPath
= false;
4132 context
.implicitRPath
= false;
4133 context
.matchByInstallName
= false;
4134 context
.dontLoad
= false;
4135 context
.mustBeBundle
= true;
4136 context
.mustBeDylib
= false;
4137 context
.canBePIE
= false;
4138 context
.origin
= NULL
;
4139 context
.rpath
= NULL
;
4140 return loadPhase6(file
.getFileDescriptor(), stat_buf
, image
->getPath(), context
);
4144 ImageLoader
* loadFromMemory(const uint8_t* mem
, uint64_t len
, const char* moduleName
)
4146 // if fat wrapper, find usable sub-file
4147 const fat_header
* memStartAsFat
= (fat_header
*)mem
;
4148 uint64_t fileOffset
= 0;
4149 uint64_t fileLength
= len
;
4150 if ( memStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
4151 if ( fatFindBest(memStartAsFat
, &fileOffset
, &fileLength
) ) {
4152 mem
= &mem
[fileOffset
];
4156 throw "no matching architecture in universal wrapper";
4161 if ( isCompatibleMachO(mem
, moduleName
) ) {
4162 ImageLoader
* image
= ImageLoaderMachO::instantiateFromMemory(moduleName
, (macho_header
*)mem
, len
, gLinkContext
);
4163 // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list
4164 if ( ! image
->isBundle() )
4169 // try other file formats here...
4171 // throw error about what was found
4172 switch (*(uint32_t*)mem
) {
4177 throw "mach-o, but wrong architecture";
4179 throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
4180 mem
[0], mem
[1], mem
[2], mem
[3], mem
[4], mem
[5], mem
[6],mem
[7]);
4185 void registerAddCallback(ImageCallback func
)
4187 // now add to list to get notified when any more images are added
4188 sAddImageCallbacks
.push_back(func
);
4190 // call callback with all existing images
4191 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
4192 ImageLoader
* image
= *it
;
4193 if ( image
->getState() >= dyld_image_state_bound
&& image
->getState() < dyld_image_state_terminated
) {
4194 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)image
->machHeader(), (uint64_t)(*func
), 0);
4195 (*func
)(image
->machHeader(), image
->getSlide());
4198 #if SUPPORT_ACCELERATE_TABLES
4199 if ( sAllCacheImagesProxy
!= NULL
) {
4200 dyld_image_info infos
[allImagesCount()+1];
4201 unsigned cacheCount
= sAllCacheImagesProxy
->appendImagesToNotify(dyld_image_state_bound
, true, infos
);
4202 for (unsigned i
=0; i
< cacheCount
; ++i
) {
4203 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)infos
[i
].imageLoadAddress
, (uint64_t)(*func
), 0);
4204 (*func
)(infos
[i
].imageLoadAddress
, sSharedCacheLoadInfo
.slide
);
4210 void registerLoadCallback(LoadImageCallback func
)
4212 // now add to list to get notified when any more images are added
4213 sAddLoadImageCallbacks
.push_back(func
);
4215 // call callback with all existing images
4216 for (ImageLoader
* image
: sAllImages
) {
4217 if ( image
->getState() >= dyld_image_state_bound
&& image
->getState() < dyld_image_state_terminated
) {
4218 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)image
->machHeader(), (uint64_t)(*func
), 0);
4219 (*func
)(image
->machHeader(), image
->getPath(), !image
->neverUnload());
4222 #if SUPPORT_ACCELERATE_TABLES
4223 if ( sAllCacheImagesProxy
!= NULL
) {
4224 dyld_image_info infos
[allImagesCount()+1];
4225 unsigned cacheCount
= sAllCacheImagesProxy
->appendImagesToNotify(dyld_image_state_bound
, true, infos
);
4226 for (unsigned i
=0; i
< cacheCount
; ++i
) {
4227 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)infos
[i
].imageLoadAddress
, (uint64_t)(*func
), 0);
4228 (*func
)(infos
[i
].imageLoadAddress
, infos
[i
].imageFilePath
, false);
4234 void registerBulkLoadCallback(LoadImageBulkCallback func
)
4236 // call callback with all existing images
4237 unsigned count
= dyld::gProcessInfo
->infoArrayCount
;
4238 const dyld_image_info
* infoArray
= dyld::gProcessInfo
->infoArray
;
4239 if ( infoArray
!= NULL
) {
4240 const mach_header
* mhs
[count
];
4241 const char* paths
[count
];
4242 for (unsigned i
=0; i
< count
; ++i
) {
4243 mhs
[i
] = infoArray
[i
].imageLoadAddress
;
4244 paths
[i
] = infoArray
[i
].imageFilePath
;
4246 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
, (uint64_t)mhs
[0], (uint64_t)func
, 0);
4247 func(count
, mhs
, paths
);
4250 // now add to list to get notified when any more images are added
4251 sAddBulkLoadImageCallbacks
.push_back(func
);
4254 void registerRemoveCallback(ImageCallback func
)
4256 // <rdar://problem/15025198> ignore calls to register a notification during a notification
4257 if ( sRemoveImageCallbacksInUse
)
4259 sRemoveImageCallbacks
.push_back(func
);
4262 void clearErrorMessage()
4264 error_string
[0] = '\0';
4267 void setErrorMessage(const char* message
)
4269 // save off error message in global buffer for CrashReporter to find
4270 strlcpy(error_string
, message
, sizeof(error_string
));
4273 const char* getErrorMessage()
4275 return error_string
;
4278 void halt(const char* message
)
4280 if ( sSharedCacheLoadInfo
.errorMessage
!= nullptr ) {
4281 // <rdar://problem/45957449> if dyld fails with a missing dylib and there is no shared cache, display the shared cache load error message
4282 dyld::log("dyld: dyld cache load error: %s\n", sSharedCacheLoadInfo
.errorMessage
);
4283 dyld::log("dyld: %s\n", message
);
4284 strlcpy(error_string
, "dyld cache load error: ", sizeof(error_string
));
4285 strlcat(error_string
, sSharedCacheLoadInfo
.errorMessage
, sizeof(error_string
));
4286 strlcat(error_string
, "\n", sizeof(error_string
));
4287 strlcat(error_string
, message
, sizeof(error_string
));
4288 } else if ( dyld::gProcessInfo
->errorKind
== DYLD_EXIT_REASON_DYLIB_MISSING
) {
4289 // If a dylib is missing, but we have the cache, print the cache UUID to make it easier
4290 // to see what might have gone wrong
4291 if ( sSharedCacheLoadInfo
.loadAddress
== nullptr ) {
4292 strlcpy(error_string
, "dyld: No shared cache present\n", sizeof(error_string
));
4295 sSharedCacheLoadInfo
.loadAddress
->getUUID(cacheUUID
);
4296 uuid_string_t uuidStr
;
4297 uuid_unparse_upper(cacheUUID
, uuidStr
);
4299 strlcpy(error_string
, "dyld: Using shared cache: ", sizeof(error_string
));
4300 strlcat(error_string
, uuidStr
, sizeof(error_string
));
4301 strlcat(error_string
, "\n", sizeof(error_string
));
4304 dyld::log("dyld: %s\n", message
);
4305 strlcat(error_string
, message
, sizeof(error_string
));
4308 dyld::log("dyld: %s\n", message
);
4309 strlcpy(error_string
, message
, sizeof(error_string
));
4312 dyld::gProcessInfo
->errorMessage
= error_string
;
4313 if ( !gLinkContext
.startedInitializingMainExecutable
)
4314 dyld::gProcessInfo
->terminationFlags
= 1;
4316 dyld::gProcessInfo
->terminationFlags
= 0;
4318 char payloadBuffer
[EXIT_REASON_PAYLOAD_MAX_LEN
];
4319 dyld_abort_payload
* payload
= (dyld_abort_payload
*)payloadBuffer
;
4320 payload
->version
= 1;
4321 payload
->flags
= gLinkContext
.startedInitializingMainExecutable
? 0 : 1;
4322 payload
->targetDylibPathOffset
= 0;
4323 payload
->clientPathOffset
= 0;
4324 payload
->symbolOffset
= 0;
4325 int payloadSize
= sizeof(dyld_abort_payload
);
4327 if ( dyld::gProcessInfo
->errorTargetDylibPath
!= NULL
) {
4328 payload
->targetDylibPathOffset
= payloadSize
;
4329 payloadSize
+= strlcpy(&payloadBuffer
[payloadSize
], dyld::gProcessInfo
->errorTargetDylibPath
, sizeof(payloadBuffer
)-payloadSize
) + 1;
4331 if ( dyld::gProcessInfo
->errorClientOfDylibPath
!= NULL
) {
4332 payload
->clientPathOffset
= payloadSize
;
4333 payloadSize
+= strlcpy(&payloadBuffer
[payloadSize
], dyld::gProcessInfo
->errorClientOfDylibPath
, sizeof(payloadBuffer
)-payloadSize
) + 1;
4335 if ( dyld::gProcessInfo
->errorSymbol
!= NULL
) {
4336 payload
->symbolOffset
= payloadSize
;
4337 payloadSize
+= strlcpy(&payloadBuffer
[payloadSize
], dyld::gProcessInfo
->errorSymbol
, sizeof(payloadBuffer
)-payloadSize
) + 1;
4339 char truncMessage
[EXIT_REASON_USER_DESC_MAX_LEN
];
4340 strlcpy(truncMessage
, error_string
, EXIT_REASON_USER_DESC_MAX_LEN
);
4341 abort_with_payload(OS_REASON_DYLD
, dyld::gProcessInfo
->errorKind
? dyld::gProcessInfo
->errorKind
: DYLD_EXIT_REASON_OTHER
, payloadBuffer
, payloadSize
, truncMessage
, 0);
4344 static void setErrorStrings(unsigned errorCode
, const char* errorClientOfDylibPath
,
4345 const char* errorTargetDylibPath
, const char* errorSymbol
)
4347 dyld::gProcessInfo
->errorKind
= errorCode
;
4348 dyld::gProcessInfo
->errorClientOfDylibPath
= errorClientOfDylibPath
;
4349 dyld::gProcessInfo
->errorTargetDylibPath
= errorTargetDylibPath
;
4350 dyld::gProcessInfo
->errorSymbol
= errorSymbol
;
4354 uintptr_t bindLazySymbol(const mach_header
* mh
, uintptr_t* lazyPointer
)
4356 uintptr_t result
= 0;
4357 // acquire read-lock on dyld's data structures
4358 #if 0 // rdar://problem/3811777 turn off locking until deadlock is resolved
4359 if ( gLibSystemHelpers
!= NULL
)
4360 (*gLibSystemHelpers
->lockForReading
)();
4362 // lookup and bind lazy pointer and get target address
4364 ImageLoader
* target
;
4366 // fast stubs pass NULL for mh and image is instead found via the location of stub (aka lazyPointer)
4368 target
= dyld::findImageContainingAddress(lazyPointer
);
4370 target
= dyld::findImageByMachHeader(mh
);
4372 // note, target should always be mach-o, because only mach-o lazy handler wired up to this
4373 target
= dyld::findImageByMachHeader(mh
);
4375 if ( target
== NULL
)
4376 throwf("image not found for lazy pointer at %p", lazyPointer
);
4377 result
= target
->doBindLazySymbol(lazyPointer
, gLinkContext
);
4379 catch (const char* message
) {
4380 dyld::log("dyld: lazy symbol binding failed: %s\n", message
);
4383 // release read-lock on dyld's data structures
4385 if ( gLibSystemHelpers
!= NULL
)
4386 (*gLibSystemHelpers
->unlockForReading
)();
4388 // return target address to glue which jumps to it with real parameters restored
4393 uintptr_t fastBindLazySymbol(ImageLoader
** imageLoaderCache
, uintptr_t lazyBindingInfoOffset
)
4395 uintptr_t result
= 0;
4397 if ( *imageLoaderCache
== NULL
) {
4399 *imageLoaderCache
= dyld::findMappedRange((uintptr_t)imageLoaderCache
);
4400 if ( *imageLoaderCache
== NULL
) {
4401 #if SUPPORT_ACCELERATE_TABLES
4402 if ( sAllCacheImagesProxy
!= NULL
) {
4403 const mach_header
* mh
;
4406 if ( sAllCacheImagesProxy
->addressInCache(imageLoaderCache
, &mh
, &path
, &index
) ) {
4407 result
= sAllCacheImagesProxy
->bindLazy(lazyBindingInfoOffset
, gLinkContext
, mh
, index
);
4408 if ( result
== 0 ) {
4409 halt("dyld: lazy symbol binding failed for image in dyld shared\n");
4415 const char* message
= "fast lazy binding from unknown image";
4416 dyld::log("dyld: %s\n", message
);
4421 // bind lazy pointer and return it
4423 result
= (*imageLoaderCache
)->doBindFastLazySymbol((uint32_t)lazyBindingInfoOffset
, gLinkContext
,
4424 (dyld::gLibSystemHelpers
!= NULL
) ? dyld::gLibSystemHelpers
->acquireGlobalDyldLock
: NULL
,
4425 (dyld::gLibSystemHelpers
!= NULL
) ? dyld::gLibSystemHelpers
->releaseGlobalDyldLock
: NULL
);
4427 catch (const char* message
) {
4428 dyld::log("dyld: lazy symbol binding failed: %s\n", message
);
4432 // return target address to glue which jumps to it with real parameters restored
4438 void registerUndefinedHandler(UndefinedHandler handler
)
4440 sUndefinedHandler
= handler
;
4443 static void undefinedHandler(const char* symboName
)
4445 if ( sUndefinedHandler
!= NULL
) {
4446 (*sUndefinedHandler
)(symboName
);
4450 static bool findExportedSymbol(const char* name
, bool onlyInCoalesced
, const ImageLoader::Symbol
** sym
, const ImageLoader
** image
, ImageLoader::CoalesceNotifier notifier
=NULL
)
4452 // search all images in order
4453 const ImageLoader
* firstWeakImage
= NULL
;
4454 const ImageLoader::Symbol
* firstWeakSym
= NULL
;
4455 const ImageLoader
* firstNonWeakImage
= NULL
;
4456 const ImageLoader::Symbol
* firstNonWeakSym
= NULL
;
4457 const size_t imageCount
= sAllImages
.size();
4458 for(size_t i
=0; i
< imageCount
; ++i
) {
4459 ImageLoader
* anImage
= sAllImages
[i
];
4460 // the use of inserted libraries alters search order
4461 // so that inserted libraries are found before the main executable
4462 if ( sInsertedDylibCount
> 0 ) {
4463 if ( i
< sInsertedDylibCount
)
4464 anImage
= sAllImages
[i
+1];
4465 else if ( i
== sInsertedDylibCount
)
4466 anImage
= sAllImages
[0];
4468 //dyld::log("findExportedSymbol(%s) looking at %s\n", name, anImage->getPath());
4469 if ( ! anImage
->hasHiddenExports() && (!onlyInCoalesced
|| anImage
->hasCoalescedExports()) ) {
4470 const ImageLoader
* foundInImage
;
4471 *sym
= anImage
->findExportedSymbol(name
, false, &foundInImage
);
4472 //dyld::log("findExportedSymbol(%s) found: sym=%p, anImage=%p, foundInImage=%p\n", name, *sym, anImage, foundInImage /*, (foundInImage ? foundInImage->getPath() : "")*/);
4473 if ( *sym
!= NULL
) {
4474 if ( notifier
&& (foundInImage
== anImage
) )
4475 notifier(*sym
, foundInImage
, foundInImage
->machHeader());
4476 // if weak definition found, record first one found
4477 if ( (foundInImage
->getExportedSymbolInfo(*sym
) & ImageLoader::kWeakDefinition
) != 0 ) {
4478 if ( firstWeakImage
== NULL
) {
4479 firstWeakImage
= foundInImage
;
4480 firstWeakSym
= *sym
;
4485 if ( !onlyInCoalesced
) {
4486 // for flat lookups, return first found
4487 *image
= foundInImage
;
4490 if ( firstNonWeakImage
== NULL
) {
4491 firstNonWeakImage
= foundInImage
;
4492 firstNonWeakSym
= *sym
;
4498 if ( firstNonWeakImage
!= NULL
) {
4499 // found a weak definition, but no non-weak, so return first weak found
4500 *sym
= firstNonWeakSym
;
4501 *image
= firstNonWeakImage
;
4504 if ( firstWeakSym
!= NULL
) {
4505 // found a weak definition, but no non-weak, so return first weak found
4506 *sym
= firstWeakSym
;
4507 *image
= firstWeakImage
;
4510 #if SUPPORT_ACCELERATE_TABLES
4511 if ( sAllCacheImagesProxy
!= NULL
) {
4512 if ( sAllCacheImagesProxy
->flatFindSymbol(name
, onlyInCoalesced
, sym
, image
, notifier
) )
4520 bool flatFindExportedSymbol(const char* name
, const ImageLoader::Symbol
** sym
, const ImageLoader
** image
)
4522 return findExportedSymbol(name
, false, sym
, image
);
4525 bool findCoalescedExportedSymbol(const char* name
, const ImageLoader::Symbol
** sym
, const ImageLoader
** image
, ImageLoader::CoalesceNotifier notifier
)
4527 return findExportedSymbol(name
, true, sym
, image
, notifier
);
4531 bool flatFindExportedSymbolWithHint(const char* name
, const char* librarySubstring
, const ImageLoader::Symbol
** sym
, const ImageLoader
** image
)
4533 // search all images in order
4534 const size_t imageCount
= sAllImages
.size();
4535 for(size_t i
=0; i
< imageCount
; ++i
){
4536 ImageLoader
* anImage
= sAllImages
[i
];
4537 // only look at images whose paths contain the hint string (NULL hint string is wildcard)
4538 if ( ! anImage
->isBundle() && ((librarySubstring
==NULL
) || (strstr(anImage
->getPath(), librarySubstring
) != NULL
)) ) {
4539 *sym
= anImage
->findExportedSymbol(name
, false, image
);
4540 if ( *sym
!= NULL
) {
4549 unsigned int getCoalescedImages(ImageLoader
* images
[], unsigned imageIndex
[])
4551 unsigned int count
= 0;
4552 const size_t imageCount
= sAllImages
.size();
4553 for(size_t i
=0; i
< imageCount
; ++i
) {
4554 ImageLoader
* anImage
= sAllImages
[i
];
4555 // the use of inserted libraries alters search order
4556 // so that inserted libraries are found before the main executable
4557 if ( sInsertedDylibCount
> 0 ) {
4558 if ( i
< sInsertedDylibCount
)
4559 anImage
= sAllImages
[i
+1];
4560 else if ( i
== sInsertedDylibCount
)
4561 anImage
= sAllImages
[0];
4563 if ( anImage
->participatesInCoalescing() ) {
4564 images
[count
] = anImage
;
4565 imageIndex
[count
] = 0;
4569 #if SUPPORT_ACCELERATE_TABLES
4570 if ( sAllCacheImagesProxy
!= NULL
) {
4571 sAllCacheImagesProxy
->appendImagesNeedingCoalescing(images
, imageIndex
, count
);
4578 static ImageLoader::MappedRegion
* getMappedRegions(ImageLoader::MappedRegion
* regions
)
4580 ImageLoader::MappedRegion
* end
= regions
;
4581 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
4582 (*it
)->getMappedRegions(end
);
4587 void registerImageStateSingleChangeHandler(dyld_image_states state
, dyld_image_state_change_handler handler
)
4589 // mark the image that the handler is in as never-unload because dyld has a reference into it
4590 ImageLoader
* handlerImage
= findImageContainingAddress((void*)handler
);
4591 if ( handlerImage
!= NULL
)
4592 handlerImage
->setNeverUnload();
4594 // add to list of handlers
4595 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(state
, sSingleHandlers
);
4596 if ( handlers
!= NULL
) {
4597 // <rdar://problem/10332417> need updateAllImages() to be last in dyld_image_state_mapped list
4598 // so that if ObjC adds a handler that prevents a load, it happens before the gdb list is updated
4599 if ( state
== dyld_image_state_mapped
)
4600 handlers
->insert(handlers
->begin(), handler
);
4602 handlers
->push_back(handler
);
4604 // call callback with all existing images
4605 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
4606 ImageLoader
* image
= *it
;
4607 dyld_image_info info
;
4608 info
.imageLoadAddress
= image
->machHeader();
4609 info
.imageFilePath
= image
->getRealPath();
4610 info
.imageFileModDate
= image
->lastModified();
4611 // should only call handler if state == image->state
4612 if ( image
->getState() == state
)
4613 (*handler
)(state
, 1, &info
);
4614 // ignore returned string, too late to do anything
4619 void registerImageStateBatchChangeHandler(dyld_image_states state
, dyld_image_state_change_handler handler
)
4621 // mark the image that the handler is in as never-unload because dyld has a reference into it
4622 ImageLoader
* handlerImage
= findImageContainingAddress((void*)handler
);
4623 if ( handlerImage
!= NULL
)
4624 handlerImage
->setNeverUnload();
4626 // add to list of handlers
4627 std::vector
<dyld_image_state_change_handler
>* handlers
= stateToHandlers(state
, sBatchHandlers
);
4628 if ( handlers
!= NULL
) {
4629 // insert at front, so that gdb handler is always last
4630 handlers
->insert(handlers
->begin(), handler
);
4632 // call callback with all existing images
4634 notifyBatchPartial(state
, true, handler
, false, false);
4636 catch (const char* msg
) {
4637 // ignore request to abort during registration
4643 void registerObjCNotifiers(_dyld_objc_notify_mapped mapped
, _dyld_objc_notify_init init
, _dyld_objc_notify_unmapped unmapped
)
4645 // record functions to call
4646 sNotifyObjCMapped
= mapped
;
4647 sNotifyObjCInit
= init
;
4648 sNotifyObjCUnmapped
= unmapped
;
4650 // call 'mapped' function with all images mapped so far
4652 notifyBatchPartial(dyld_image_state_bound
, true, NULL
, false, true);
4654 catch (const char* msg
) {
4655 // ignore request to abort during registration
4658 // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
4659 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
4660 ImageLoader
* image
= *it
;
4661 if ( (image
->getState() == dyld_image_state_initialized
) && image
->notifyObjC() ) {
4662 dyld3::ScopedTimer
timer(DBG_DYLD_TIMING_OBJC_INIT
, (uint64_t)image
->machHeader(), 0, 0);
4663 (*sNotifyObjCInit
)(image
->getRealPath(), image
->machHeader());
4668 bool sharedCacheUUID(uuid_t uuid
)
4670 if ( sSharedCacheLoadInfo
.loadAddress
== nullptr )
4673 sSharedCacheLoadInfo
.loadAddress
->getUUID(uuid
);
4677 #if SUPPORT_ACCELERATE_TABLES
4679 bool dlopenFromCache(const char* path
, int mode
, void** handle
)
4681 if ( sAllCacheImagesProxy
== NULL
)
4683 char fallbackPath
[PATH_MAX
];
4684 bool result
= sAllCacheImagesProxy
->dlopenFromCache(gLinkContext
, path
, mode
, handle
);
4685 if ( !result
&& (strchr(path
, '/') == NULL
) ) {
4686 // POSIX says you can call dlopen() with a leaf name (e.g. dlopen("libz.dylb"))
4687 strcpy(fallbackPath
, "/usr/lib/");
4688 strlcat(fallbackPath
, path
, PATH_MAX
);
4689 result
= sAllCacheImagesProxy
->dlopenFromCache(gLinkContext
, fallbackPath
, mode
, handle
);
4691 path
= fallbackPath
;
4694 // leaf name could be a symlink
4695 char resolvedPath
[PATH_MAX
];
4696 realpath(path
, resolvedPath
);
4697 int realpathErrno
= errno
;
4698 // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
4699 if ( (realpathErrno
== ENOENT
) || (realpathErrno
== 0) ) {
4700 result
= sAllCacheImagesProxy
->dlopenFromCache(gLinkContext
, resolvedPath
, mode
, handle
);
4707 bool makeCacheHandle(ImageLoader
* image
, unsigned cacheIndex
, int mode
, void** result
)
4709 if ( sAllCacheImagesProxy
== NULL
)
4711 return sAllCacheImagesProxy
->makeCacheHandle(gLinkContext
, cacheIndex
, mode
, result
);
4714 bool isCacheHandle(void* handle
)
4716 if ( sAllCacheImagesProxy
== NULL
)
4718 return sAllCacheImagesProxy
->isCacheHandle(handle
, NULL
, NULL
);
4721 bool isPathInCache(const char* path
)
4723 if ( sAllCacheImagesProxy
== NULL
)
4726 return sAllCacheImagesProxy
->hasDylib(path
, &index
);
4729 const char* getPathFromIndex(unsigned cacheIndex
)
4731 if ( sAllCacheImagesProxy
== NULL
)
4733 return sAllCacheImagesProxy
->getIndexedPath(cacheIndex
);
4736 void* dlsymFromCache(void* handle
, const char* symName
, unsigned index
)
4738 if ( sAllCacheImagesProxy
== NULL
)
4740 return sAllCacheImagesProxy
->dlsymFromCache(gLinkContext
, handle
, symName
, index
);
4743 bool addressInCache(const void* address
, const mach_header
** mh
, const char** path
, unsigned* index
)
4745 if ( sAllCacheImagesProxy
== NULL
)
4748 return sAllCacheImagesProxy
->addressInCache(address
, mh
, path
, index
? index
: &ignore
);
4751 bool findUnwindSections(const void* addr
, dyld_unwind_sections
* info
)
4753 if ( sAllCacheImagesProxy
== NULL
)
4755 return sAllCacheImagesProxy
->findUnwindSections(addr
, info
);
4758 bool dladdrFromCache(const void* address
, Dl_info
* info
)
4760 if ( sAllCacheImagesProxy
== NULL
)
4762 return sAllCacheImagesProxy
->dladdrFromCache(address
, info
);
4766 static ImageLoader
* libraryLocator(const char* libraryName
, bool search
, const char* origin
, const ImageLoader::RPathChain
* rpaths
, unsigned& cacheIndex
)
4768 dyld::LoadContext context
;
4769 context
.useSearchPaths
= search
;
4770 context
.useFallbackPaths
= search
;
4771 context
.useLdLibraryPath
= false;
4772 context
.implicitRPath
= false;
4773 context
.matchByInstallName
= false;
4774 context
.dontLoad
= false;
4775 context
.mustBeBundle
= false;
4776 context
.mustBeDylib
= true;
4777 context
.canBePIE
= false;
4778 context
.origin
= origin
;
4779 context
.rpath
= rpaths
;
4780 return load(libraryName
, context
, cacheIndex
);
4783 static const char* basename(const char* path
)
4785 const char* last
= path
;
4786 for (const char* s
= path
; *s
!= '\0'; s
++) {
4793 static void setContext(const macho_header
* mainExecutableMH
, int argc
, const char* argv
[], const char* envp
[], const char* apple
[])
4795 gLinkContext
.loadLibrary
= &libraryLocator
;
4796 gLinkContext
.terminationRecorder
= &terminationRecorder
;
4797 gLinkContext
.flatExportFinder
= &flatFindExportedSymbol
;
4798 gLinkContext
.coalescedExportFinder
= &findCoalescedExportedSymbol
;
4799 gLinkContext
.getCoalescedImages
= &getCoalescedImages
;
4800 gLinkContext
.undefinedHandler
= &undefinedHandler
;
4801 gLinkContext
.getAllMappedRegions
= &getMappedRegions
;
4802 gLinkContext
.bindingHandler
= NULL
;
4803 gLinkContext
.notifySingle
= ¬ifySingle
;
4804 gLinkContext
.notifyBatch
= ¬ifyBatch
;
4805 gLinkContext
.removeImage
= &removeImage
;
4806 gLinkContext
.registerDOFs
= dyld3::Loader::dtraceUserProbesEnabled() ? ®isterDOFs
: NULL
;
4807 gLinkContext
.clearAllDepths
= &clearAllDepths
;
4808 gLinkContext
.printAllDepths
= &printAllDepths
;
4809 gLinkContext
.imageCount
= &imageCount
;
4810 gLinkContext
.setNewProgramVars
= &setNewProgramVars
;
4811 gLinkContext
.inSharedCache
= &inSharedCache
;
4812 gLinkContext
.setErrorStrings
= &setErrorStrings
;
4813 #if SUPPORT_OLD_CRT_INITIALIZATION
4814 gLinkContext
.setRunInitialzersOldWay
= &setRunInitialzersOldWay
;
4816 gLinkContext
.findImageContainingAddress
= &findImageContainingAddress
;
4817 gLinkContext
.addDynamicReference
= &addDynamicReference
;
4818 #if SUPPORT_ACCELERATE_TABLES
4819 gLinkContext
.notifySingleFromCache
= ¬ifySingleFromCache
;
4820 gLinkContext
.getPreInitNotifyHandler
= &getPreInitNotifyHandler
;
4821 gLinkContext
.getBoundBatchHandler
= &getBoundBatchHandler
;
4823 gLinkContext
.bindingOptions
= ImageLoader::kBindingNone
;
4824 gLinkContext
.argc
= argc
;
4825 gLinkContext
.argv
= argv
;
4826 gLinkContext
.envp
= envp
;
4827 gLinkContext
.apple
= apple
;
4828 gLinkContext
.progname
= (argv
[0] != NULL
) ? basename(argv
[0]) : "";
4829 gLinkContext
.programVars
.mh
= mainExecutableMH
;
4830 gLinkContext
.programVars
.NXArgcPtr
= &gLinkContext
.argc
;
4831 gLinkContext
.programVars
.NXArgvPtr
= &gLinkContext
.argv
;
4832 gLinkContext
.programVars
.environPtr
= &gLinkContext
.envp
;
4833 gLinkContext
.programVars
.__prognamePtr
=&gLinkContext
.progname
;
4834 gLinkContext
.mainExecutable
= NULL
;
4835 gLinkContext
.imageSuffix
= NULL
;
4836 gLinkContext
.dynamicInterposeArray
= NULL
;
4837 gLinkContext
.dynamicInterposeCount
= 0;
4838 gLinkContext
.prebindUsage
= ImageLoader::kUseAllPrebinding
;
4839 gLinkContext
.sharedRegionMode
= ImageLoader::kUseSharedRegion
;
4845 // Look for a special segment in the mach header.
4846 // Its presences means that the binary wants to have DYLD ignore
4847 // DYLD_ environment variables.
4850 static bool hasRestrictedSegment(const macho_header
* mh
)
4852 const uint32_t cmd_count
= mh
->ncmds
;
4853 const struct load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
4854 const struct load_command
* cmd
= cmds
;
4855 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
4857 case LC_SEGMENT_COMMAND
:
4859 const struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
4861 //dyld::log("seg name: %s\n", seg->segname);
4862 if (strcmp(seg
->segname
, "__RESTRICT") == 0) {
4863 const struct macho_section
* const sectionsStart
= (struct macho_section
*)((char*)seg
+ sizeof(struct macho_segment_command
));
4864 const struct macho_section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
4865 for (const struct macho_section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
4866 if (strcmp(sect
->sectname
, "__restrict") == 0)
4873 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
4880 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
4881 static bool isFairPlayEncrypted(const macho_header
* mh
)
4883 const uint32_t cmd_count
= mh
->ncmds
;
4884 const struct load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
4885 const struct load_command
* cmd
= cmds
;
4886 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
4887 if ( cmd
->cmd
== LC_ENCRYPT_COMMAND
) {
4888 const encryption_info_command
* enc
= (encryption_info_command
*)cmd
;
4889 return (enc
->cryptid
!= 0);
4891 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
4898 #if SUPPORT_VERSIONED_PATHS
4900 static bool readFirstPage(const char* dylibPath
, uint8_t firstPage
[4096])
4903 // open file (automagically closed when this function exits)
4904 FileOpener
file(dylibPath
);
4906 if ( file
.getFileDescriptor() == -1 )
4909 if ( pread(file
.getFileDescriptor(), firstPage
, 4096, 0) != 4096 )
4912 // if fat wrapper, find usable sub-file
4913 const fat_header
* fileStartAsFat
= (fat_header
*)firstPage
;
4914 if ( fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
4915 uint64_t fileOffset
;
4916 uint64_t fileLength
;
4917 if ( fatFindBest(fileStartAsFat
, &fileOffset
, &fileLength
) ) {
4918 if ( pread(file
.getFileDescriptor(), firstPage
, 4096, fileOffset
) != 4096 )
4930 // Peeks at a dylib file and returns its current_version and install_name.
4931 // Returns false on error.
4933 static bool getDylibVersionAndInstallname(const char* dylibPath
, uint32_t* version
, char* installName
)
4935 uint8_t firstPage
[4096];
4936 const macho_header
* mh
= (macho_header
*)firstPage
;
4937 if ( !readFirstPage(dylibPath
, firstPage
) ) {
4938 // If file cannot be read, check to see if path is in shared cache
4939 const macho_header
* mhInCache
;
4940 const char* pathInCache
;
4942 if ( !findInSharedCacheImage(dylibPath
, true, NULL
, &mhInCache
, &pathInCache
, &slideInCache
) )
4947 // check mach-o header
4948 if ( mh
->magic
!= sMainExecutableMachHeader
->magic
)
4950 if ( mh
->cputype
!= sMainExecutableMachHeader
->cputype
)
4953 // scan load commands for LC_ID_DYLIB
4954 const uint32_t cmd_count
= mh
->ncmds
;
4955 const struct load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
4956 const struct load_command
* const cmdsReadEnd
= (struct load_command
*)(((char*)mh
)+4096);
4957 const struct load_command
* cmd
= cmds
;
4958 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
4962 const struct dylib_command
* id
= (struct dylib_command
*)cmd
;
4963 *version
= id
->dylib
.current_version
;
4964 if ( installName
!= NULL
)
4965 strlcpy(installName
, (char *)id
+ id
->dylib
.name
.offset
, PATH_MAX
);
4970 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
4971 if ( cmd
> cmdsReadEnd
)
4977 #endif // SUPPORT_VERSIONED_PATHS
4981 static void printAllImages()
4983 dyld::log("printAllImages()\n");
4984 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
4985 ImageLoader
* image
= *it
;
4986 dyld_image_states imageState
= image
->getState();
4987 dyld::log(" state=%d, dlopen-count=%d, never-unload=%d, in-use=%d, name=%s\n",
4988 imageState
, image
->dlopenCount(), image
->neverUnload(), image
->isMarkedInUse(), image
->getShortName());
4993 void link(ImageLoader
* image
, bool forceLazysBound
, bool neverUnload
, const ImageLoader::RPathChain
& loaderRPaths
, unsigned cacheIndex
)
4995 // add to list of known images. This did not happen at creation time for bundles
4996 if ( image
->isBundle() && !image
->isLinked() )
4999 // we detect root images as those not linked in yet
5000 if ( !image
->isLinked() )
5001 addRootImage(image
);
5005 const char* path
= image
->getPath();
5006 #if SUPPORT_ACCELERATE_TABLES
5007 if ( image
== sAllCacheImagesProxy
)
5008 path
= sAllCacheImagesProxy
->getIndexedPath(cacheIndex
);
5010 image
->link(gLinkContext
, forceLazysBound
, false, neverUnload
, loaderRPaths
, path
);
5012 catch (const char* msg
) {
5013 garbageCollectImages();
5019 void runInitializers(ImageLoader
* image
)
5021 // do bottom up initialization
5022 ImageLoader::InitializerTimingList initializerTimes
[allImagesCount()];
5023 initializerTimes
[0].count
= 0;
5024 image
->runInitializers(gLinkContext
, initializerTimes
[0]);
5027 // This function is called at the end of dlclose() when the reference count goes to zero.
5028 // The dylib being unloaded may have brought in other dependent dylibs when it was loaded.
5029 // Those dependent dylibs need to be unloaded, but only if they are not referenced by
5030 // something else. We use a standard mark and sweep garbage collection.
5032 // The tricky part is that when a dylib is unloaded it may have a termination function that
5033 // can run and itself call dlclose() on yet another dylib. The problem is that this
5034 // sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose()
5035 // which calls garbageCollectImages() will just set a flag to re-do the garbage collection
5036 // when the current pass is done.
5038 // Also note that this is done within the dyld global lock, so it is always single threaded.
5040 void garbageCollectImages()
5042 static bool sDoingGC
= false;
5043 static bool sRedo
= false;
5046 // GC is currently being run, just set a flag to have it run again.
5055 // mark phase: mark all images not-in-use
5056 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
5057 ImageLoader
* image
= *it
;
5058 //dyld::log("gc: neverUnload=%d name=%s\n", image->neverUnload(), image->getShortName());
5059 image
->markNotUsed();
5062 #pragma clang diagnostic push
5063 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
5064 // sweep phase: mark as in-use, images reachable from never-unload or in-use image
5065 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
5066 ImageLoader
* image
= *it
;
5067 if ( (image
->dlopenCount() != 0) || (image
->neverUnload() && (image
->getState() >= dyld_image_state_bound
)) || (image
== sMainExecutable
) ) {
5068 OSSpinLockLock(&sDynamicReferencesLock
);
5069 image
->markedUsedRecursive(sDynamicReferences
);
5070 OSSpinLockUnlock(&sDynamicReferencesLock
);
5073 #pragma clang diagnostic pop
5075 // collect phase: build array of images not marked in-use
5076 ImageLoader
* deadImages
[sAllImages
.size()];
5077 unsigned deadCount
= 0;
5078 int maxRangeCount
= 0;
5079 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
5080 ImageLoader
* image
= *it
;
5081 if ( ! image
->isMarkedInUse() ) {
5082 deadImages
[deadCount
++] = image
;
5083 if (gLogAPIs
) dyld::log("dlclose(), found unused image %p %s\n", image
, image
->getShortName());
5084 maxRangeCount
+= image
->segmentCount();
5088 // collect phase: run termination routines for images not marked in-use
5089 if ( maxRangeCount
!= 0 ) {
5090 __cxa_range_t ranges
[maxRangeCount
];
5092 for (unsigned i
=0; i
< deadCount
; ++i
) {
5093 ImageLoader
* image
= deadImages
[i
];
5094 for (unsigned int j
=0; j
< image
->segmentCount(); ++j
) {
5095 if ( !image
->segExecutable(j
) )
5097 if ( rangeCount
< maxRangeCount
) {
5098 ranges
[rangeCount
].addr
= (const void*)image
->segActualLoadAddress(j
);
5099 ranges
[rangeCount
].length
= image
->segSize(j
);
5104 runImageStaticTerminators(image
);
5106 catch (const char* msg
) {
5107 dyld::warn("problem running terminators for image: %s\n", msg
);
5111 // <rdar://problem/14718598> dyld should call __cxa_finalize_ranges()
5112 if ( (rangeCount
> 0) && (gLibSystemHelpers
!= NULL
) && (gLibSystemHelpers
->version
>= 13) )
5113 (*gLibSystemHelpers
->cxa_finalize_ranges
)(ranges
, rangeCount
);
5116 // collect phase: delete all images which are not marked in-use
5119 mightBeMore
= false;
5120 for (std::vector
<ImageLoader
*>::iterator it
=sAllImages
.begin(); it
!= sAllImages
.end(); it
++) {
5121 ImageLoader
* image
= *it
;
5122 if ( ! image
->isMarkedInUse() ) {
5124 if (gLogAPIs
) dyld::log("dlclose(), deleting %p %s\n", image
, image
->getShortName());
5126 ImageLoader::deleteImage(image
);
5128 break; // interator in invalidated by this removal
5130 catch (const char* msg
) {
5131 dyld::warn("problem deleting image: %s\n", msg
);
5135 } while ( mightBeMore
);
5144 static void preflight_finally(ImageLoader
* image
)
5146 if ( image
->isBundle() ) {
5147 removeImageFromAllImages(image
->machHeader());
5148 ImageLoader::deleteImage(image
);
5150 sBundleBeingLoaded
= NULL
;
5151 dyld::garbageCollectImages();
5155 void preflight(ImageLoader
* image
, const ImageLoader::RPathChain
& loaderRPaths
, unsigned cacheIndex
)
5158 if ( image
->isBundle() )
5159 sBundleBeingLoaded
= image
; // hack
5160 const char* path
= image
->getPath();
5161 #if SUPPORT_ACCELERATE_TABLES
5162 if ( image
== sAllCacheImagesProxy
)
5163 path
= sAllCacheImagesProxy
->getIndexedPath(cacheIndex
);
5165 image
->link(gLinkContext
, false, true, false, loaderRPaths
, path
);
5167 catch (const char* msg
) {
5168 preflight_finally(image
);
5171 preflight_finally(image
);
5174 static void loadInsertedDylib(const char* path
)
5176 unsigned cacheIndex
;
5178 LoadContext context
;
5179 context
.useSearchPaths
= false;
5180 context
.useFallbackPaths
= false;
5181 context
.useLdLibraryPath
= false;
5182 context
.implicitRPath
= false;
5183 context
.matchByInstallName
= false;
5184 context
.dontLoad
= false;
5185 context
.mustBeBundle
= false;
5186 context
.mustBeDylib
= true;
5187 context
.canBePIE
= false;
5188 context
.origin
= NULL
; // can't use @loader_path with DYLD_INSERT_LIBRARIES
5189 context
.rpath
= NULL
;
5190 load(path
, context
, cacheIndex
);
5192 catch (const char* msg
) {
5193 if ( gLinkContext
.allowInsertFailures
)
5194 dyld::log("dyld: warning: could not load inserted library '%s' into hardened process because %s\n", path
, msg
);
5196 halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path
, msg
));
5199 halt(dyld::mkstringf("could not load inserted library '%s'\n", path
));
5204 static void configureProcessRestrictions(const macho_header
* mainExecutableMH
, const char* envp
[])
5206 uint64_t amfiInputFlags
= 0;
5207 #if TARGET_OS_SIMULATOR
5208 amfiInputFlags
|= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR
;
5210 if ( hasRestrictedSegment(mainExecutableMH
) )
5211 amfiInputFlags
|= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG
;
5212 #elif TARGET_OS_IPHONE
5213 if ( isFairPlayEncrypted(mainExecutableMH
) )
5214 amfiInputFlags
|= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED
;
5216 uint64_t amfiOutputFlags
= 0;
5217 const char* amfiFake
= nullptr;
5218 if constexpr(BUILD_FOR_TESTING
== 1) {
5219 amfiFake
= _simple_getenv(envp
, "DYLD_AMFI_FAKE");
5220 } else if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) {
5221 amfiFake
= _simple_getenv(envp
, "DYLD_AMFI_FAKE");
5224 if ( amfiFake
!= nullptr ) {
5225 amfiOutputFlags
= hexToUInt64(amfiFake
, nullptr);
5227 if ( (amfiFake
!= nullptr) || (amfi_check_dyld_policy_self(amfiInputFlags
, &amfiOutputFlags
) == 0) ) {
5228 gLinkContext
.allowAtPaths
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_AT_PATH
);
5229 gLinkContext
.allowEnvVarsPrint
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS
);
5230 gLinkContext
.allowEnvVarsPath
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS
);
5231 gLinkContext
.allowEnvVarsSharedCache
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE
);
5232 gLinkContext
.allowClassicFallbackPaths
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS
);
5233 gLinkContext
.allowInsertFailures
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION
);
5234 #ifdef AMFI_RETURNS_INTERPOSING_FLAG
5235 gLinkContext
.allowInterposing
= (amfiOutputFlags
& AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING
);
5237 gLinkContext
.allowInterposing
= true;
5242 // support chrooting from old kernel
5243 bool isRestricted
= false;
5244 bool libraryValidation
= false;
5245 // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
5246 if ( issetugid() || hasRestrictedSegment(mainExecutableMH
) ) {
5247 isRestricted
= true;
5249 bool usingSIP
= (csr_check(CSR_ALLOW_TASK_FOR_PID
) != 0);
5251 if ( csops(0, CS_OPS_STATUS
, &flags
, sizeof(flags
)) != -1 ) {
5252 // On OS X CS_RESTRICT means the program was signed with entitlements
5253 if ( ((flags
& CS_RESTRICT
) == CS_RESTRICT
) && usingSIP
) {
5254 isRestricted
= true;
5256 // Library Validation loosens searching but requires everything to be code signed
5257 if ( flags
& CS_REQUIRE_LV
) {
5258 isRestricted
= false;
5259 libraryValidation
= true;
5262 gLinkContext
.allowAtPaths
= !isRestricted
;
5263 gLinkContext
.allowEnvVarsPrint
= !isRestricted
;
5264 gLinkContext
.allowEnvVarsPath
= !isRestricted
;
5265 gLinkContext
.allowEnvVarsSharedCache
= !libraryValidation
|| !usingSIP
;
5266 gLinkContext
.allowClassicFallbackPaths
= !isRestricted
;
5267 gLinkContext
.allowInsertFailures
= false;
5268 gLinkContext
.allowInterposing
= true;
5270 halt("amfi_check_dyld_policy_self() failed\n");
5275 // called by _dyld_register_driverkit_main()
5276 void setMainEntry(void (*main
)())
5278 if ( sEntryOverride
== nullptr )
5279 sEntryOverride
= main
;
5281 halt("_dyld_register_driverkit_main() may only be called once");
5284 bool processIsRestricted()
5287 return !gLinkContext
.allowEnvVarsPath
;
5294 // <rdar://problem/10583252> Add dyld to uuidArray to enable symbolication of stackshots
5295 static void addDyldImageToUUIDList()
5297 const struct macho_header
* mh
= (macho_header
*)&__dso_handle
;
5298 const uint32_t cmd_count
= mh
->ncmds
;
5299 const struct load_command
* const cmds
= (struct load_command
*)((char*)mh
+ sizeof(macho_header
));
5300 const struct load_command
* cmd
= cmds
;
5301 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
5304 uuid_command
* uc
= (uuid_command
*)cmd
;
5305 dyld_uuid_info info
;
5306 info
.imageLoadAddress
= (mach_header
*)mh
;
5307 memcpy(info
.imageUUID
, uc
->uuid
, 16);
5308 addNonSharedCacheImageUUID(info
);
5312 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
5316 void notifyKernelAboutImage(const struct macho_header
* mh
, const char* fileInfo
)
5318 const char *endptr
= nullptr;
5319 uint64_t tmp
= hexToUInt64(fileInfo
, &endptr
);
5320 fsid_t fsid
= *reinterpret_cast<fsid_t
*>(&tmp
);
5321 uint64_t fsobj_id_scalar
= 0;
5322 fsobj_id_t fsobj_id
= {0};
5323 if (endptr
!= nullptr) {
5324 fsobj_id_scalar
= hexToUInt64(endptr
+1, &endptr
);
5325 fsobj_id
= *reinterpret_cast<fsobj_id_t
*>(&fsobj_id_scalar
);
5327 const uint32_t cmd_count
= mh
->ncmds
;
5328 const struct load_command
* const cmds
= (struct load_command
*)((char*)mh
+ sizeof(macho_header
));
5329 const struct load_command
* cmd
= cmds
;
5330 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
5333 // Add dyld to the kernel image info
5334 uuid_command
* uc
= (uuid_command
*)cmd
;
5335 char path
[MAXPATHLEN
];
5336 if (fsgetpath(path
, MAXPATHLEN
, &fsid
, fsobj_id_scalar
) < 0) {
5339 dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A
, path
, (const uuid_t
*)&uc
->uuid
[0], fsobj_id
, fsid
, (const mach_header
*)mh
);
5343 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
5348 static void* getProcessInfo() { return dyld::gProcessInfo
; }
5349 static const SyscallHelpers sSysCalls
= {
5351 // added in version 1
5370 &pthread_mutex_lock
,
5371 &pthread_mutex_unlock
,
5373 &mach_port_deallocate
,
5375 &mach_timebase_info
,
5376 &OSAtomicCompareAndSwapPtrBarrier
,
5380 &mach_absolute_time
,
5381 // added in version 2
5383 // added in version 3
5387 // added in version 4
5388 &coresymbolication_load_notifier
,
5389 &coresymbolication_unload_notifier
,
5390 // Added in version 5
5391 &proc_regionfilename
,
5393 &mach_port_insert_right
,
5394 &mach_port_allocate
,
5396 // Added in version 6
5397 &abort_with_payload
,
5398 // Added in version 7
5399 &legacy_task_register_dyld_image_infos
,
5400 &legacy_task_unregister_dyld_image_infos
,
5401 &legacy_task_get_dyld_image_infos
,
5402 &legacy_task_register_dyld_shared_cache_image_info
,
5403 &legacy_task_register_dyld_set_dyld_state
,
5404 &legacy_task_register_dyld_get_process_state
,
5405 // Added in version 8
5410 // Added in version 9
5411 &kdebug_trace_string
,
5412 // Added in version 10
5413 &amfi_check_dyld_policy_self
,
5414 // Added in version 11
5415 ¬ifyMonitoringDyldMain
,
5416 ¬ifyMonitoringDyld
,
5417 // Add in version 12
5419 &mach_port_construct
,
5420 &mach_port_destruct
,
5421 // Add in version 13
5426 __attribute__((noinline
))
5427 static const char* useSimulatorDyld(int fd
, const macho_header
* mainExecutableMH
, const char* dyldPath
,
5428 int argc
, const char* argv
[], const char* envp
[], const char* apple
[],
5429 uintptr_t* startGlue
, uintptr_t* mainAddr
)
5434 // <rdar://problem/25311921> simulator does not support restricted processes
5436 if ( csops(0, CS_OPS_STATUS
, &flags
, sizeof(flags
)) == -1 )
5437 return "csops() failed";
5438 if ( (flags
& CS_RESTRICT
) == CS_RESTRICT
)
5439 return "dyld_sim cannot be loaded in a restricted process";
5441 return "dyld_sim cannot be loaded in a setuid process";
5442 if ( hasRestrictedSegment(mainExecutableMH
) )
5443 return "dyld_sim cannot be loaded in a restricted process";
5445 // get file size of dyld_sim
5447 if ( fstat(fd
, &sb
) == -1 )
5448 return "stat(dyld_sim) failed";
5450 // read first page of dyld_sim file
5451 uint8_t firstPage
[4096];
5452 if ( pread(fd
, firstPage
, 4096, 0) != 4096 )
5453 return "pread(dyld_sim) failed";
5455 // if fat file, pick matching slice
5456 uint64_t fileOffset
= 0;
5457 uint64_t fileLength
= sb
.st_size
;
5458 const fat_header
* fileStartAsFat
= (fat_header
*)firstPage
;
5459 if ( fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
5460 if ( !fatFindBest(fileStartAsFat
, &fileOffset
, &fileLength
) )
5461 return "no matching arch in dyld_sim";
5462 // re-read buffer from start of mach-o slice in fat file
5463 if ( pread(fd
, firstPage
, 4096, fileOffset
) != 4096 )
5464 return "pread(dyld_sim) failed";
5466 else if ( !isCompatibleMachO(firstPage
, dyldPath
, fd
, fileOffset
, fileLength
) ) {
5467 return "dyld_sim is not compatible with the loaded process, likely due to architecture mismatch";
5470 // calculate total size of dyld segments
5471 const macho_header
* mh
= (const macho_header
*)firstPage
;
5472 struct macho_segment_command
* lastSeg
= NULL
;
5473 struct macho_segment_command
* firstSeg
= NULL
;
5474 uintptr_t mappingSize
= 0;
5475 uintptr_t preferredLoadAddress
= 0;
5476 const uint32_t cmd_count
= mh
->ncmds
;
5477 if ( mh
->sizeofcmds
> 4096 )
5478 return "dyld_sim load commands to large";
5479 if ( (sizeof(macho_header
) + mh
->sizeofcmds
) > 4096 )
5480 return "dyld_sim load commands to large";
5481 struct linkedit_data_command
* codeSigCmd
= NULL
;
5482 const struct load_command
* const cmds
= (struct load_command
*)(((char*)mh
)+sizeof(macho_header
));
5483 const struct load_command
* const endCmds
= (struct load_command
*)(((char*)mh
) + sizeof(macho_header
) + mh
->sizeofcmds
);
5484 const struct load_command
* cmd
= cmds
;
5485 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
5486 uint32_t cmdLength
= cmd
->cmdsize
;
5487 if ( cmdLength
< 8 )
5488 return "dyld_sim load command too small";
5489 const struct load_command
* const nextCmd
= (const struct load_command
*)(((char*)cmd
)+cmdLength
);
5490 if ( (nextCmd
> endCmds
) || (nextCmd
< cmd
) )
5491 return "dyld_sim load command too large";
5493 case LC_SEGMENT_COMMAND
:
5495 struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
5496 if ( seg
->vmaddr
+ seg
->vmsize
< seg
->vmaddr
)
5497 return "dyld_sim seg wraps address space";
5498 if ( seg
->vmsize
< seg
->filesize
)
5499 return "dyld_sim seg vmsize too small";
5500 if ( (seg
->fileoff
+ seg
->filesize
) < seg
->fileoff
)
5501 return "dyld_sim seg size wraps address space";
5502 if ( lastSeg
== NULL
) {
5503 // first segment must be __TEXT and start at beginning of file/slice
5505 if ( strcmp(seg
->segname
, "__TEXT") != 0 )
5506 return "dyld_sim first segment not __TEXT";
5507 if ( seg
->fileoff
!= 0 )
5508 return "dyld_sim first segment not at file offset zero";
5509 if ( seg
->filesize
< (sizeof(macho_header
) + mh
->sizeofcmds
) )
5510 return "dyld_sim first segment smaller than load commands";
5511 preferredLoadAddress
= seg
->vmaddr
;
5514 // other sements must be continguous with previous segment and not executable
5515 if ( lastSeg
->fileoff
+ lastSeg
->filesize
!= seg
->fileoff
)
5516 return "dyld_sim segments not contiguous";
5517 if ( lastSeg
->vmaddr
+ lastSeg
->vmsize
!= seg
->vmaddr
)
5518 return "dyld_sim segments not address contiguous";
5519 if ( (seg
->initprot
& VM_PROT_EXECUTE
) != 0 )
5520 return "dyld_sim non-first segment is executable";
5522 mappingSize
+= seg
->vmsize
;
5526 case LC_SEGMENT_COMMAND_WRONG
:
5527 return "dyld_sim wrong load segment load command";
5528 case LC_CODE_SIGNATURE
:
5529 codeSigCmd
= (struct linkedit_data_command
*)cmd
;
5534 // last segment must be named __LINKEDIT and not writable
5535 if ( lastSeg
== NULL
)
5536 return "dyld_sim has no segments";
5537 if ( strcmp(lastSeg
->segname
, "__LINKEDIT") != 0 )
5538 return "dyld_sim last segment not __LINKEDIT";
5539 if ( lastSeg
->initprot
& VM_PROT_WRITE
)
5540 return "dyld_sim __LINKEDIT segment writable";
5542 // must have code signature which is contained within LINKEDIT segment
5543 if ( codeSigCmd
== NULL
)
5544 return "dyld_sim not code signed";
5545 if ( codeSigCmd
->dataoff
< lastSeg
->fileoff
)
5546 return "dyld_sim code signature not in __LINKEDIT";
5547 if ( (codeSigCmd
->dataoff
+ codeSigCmd
->datasize
) < codeSigCmd
->dataoff
)
5548 return "dyld_sim code signature size wraps";
5549 if ( (codeSigCmd
->dataoff
+ codeSigCmd
->datasize
) > (lastSeg
->fileoff
+ lastSeg
->filesize
) )
5550 return "dyld_sim code signature extends beyond __LINKEDIT";
5552 // register code signature with kernel before mmap()ing segments
5553 fsignatures_t siginfo
;
5554 siginfo
.fs_file_start
=fileOffset
; // start of mach-o slice in fat file
5555 siginfo
.fs_blob_start
=(void*)(long)(codeSigCmd
->dataoff
); // start of code-signature in mach-o file
5556 siginfo
.fs_blob_size
=codeSigCmd
->datasize
; // size of code-signature
5557 int result
= fcntl(fd
, F_ADDFILESIGS_FOR_DYLD_SIM
, &siginfo
);
5558 if ( result
== -1 ) {
5559 return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno
);
5561 // file range covered by code signature must extend up to code signature itself
5562 if ( siginfo
.fs_file_start
< codeSigCmd
->dataoff
)
5563 return mkstringf("dyld_sim code signature does not cover all of dyld_sim. Signature covers up to 0x%08lX. Signature starts at 0x%08X", (unsigned long)siginfo
.fs_file_start
, codeSigCmd
->dataoff
);
5565 // reserve space, then mmap each segment
5566 vm_address_t loadAddress
= 0;
5567 if ( ::vm_allocate(mach_task_self(), &loadAddress
, mappingSize
, VM_FLAGS_ANYWHERE
) != 0 )
5568 return "dyld_sim cannot allocate space";
5570 struct source_version_command
* dyldVersionCmd
= NULL
;
5571 struct uuid_command
* uuidCmd
= NULL
;
5572 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
5574 case LC_SEGMENT_COMMAND
:
5576 struct macho_segment_command
* seg
= (struct macho_segment_command
*)cmd
;
5577 uintptr_t requestedLoadAddress
= seg
->vmaddr
- preferredLoadAddress
+ loadAddress
;
5578 void* segAddress
= ::mmap((void*)requestedLoadAddress
, seg
->filesize
, seg
->initprot
, MAP_FIXED
| MAP_PRIVATE
, fd
, fileOffset
+ seg
->fileoff
);
5579 //dyld::log("dyld_sim %s mapped at %p\n", seg->segname, segAddress);
5580 if ( segAddress
== (void*)(-1) )
5581 return "dyld_sim mmap() of segment failed";
5582 if ( ((uintptr_t)segAddress
< loadAddress
) || ((uintptr_t)segAddress
+seg
->filesize
> loadAddress
+mappingSize
) )
5583 return "dyld_sim mmap() to wrong location";
5586 case LC_SOURCE_VERSION
:
5587 dyldVersionCmd
= (struct source_version_command
*)cmd
;
5590 uuidCmd
= (uuid_command
*)cmd
;
5594 cmd
= (const struct load_command
*)(((char*)cmd
)+cmd
->cmdsize
);
5598 // Walk newly mapped dyld_sim load commands to find entry point
5599 uintptr_t entry
= 0;
5600 bool unusedUsesCRT
= false;
5601 uint64_t entryOffset
= 0;
5602 if ( !((dyld3::MachOAnalyzer
*)loadAddress
)->getEntry(entryOffset
, unusedUsesCRT
) ) {
5603 return "dyld_sim entry not found";
5606 // Translate the load address by the entry offset in order to get the runtime address.
5607 entry
= (uintptr_t)loadAddress
;
5608 entry
+= entryOffset
;
5611 // It's necessary to sign the entry pointer.
5612 entry
= (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)entry
, ptrauth_key_asia
, 0);
5615 // notify debugger that dyld_sim is loaded
5616 dyld_image_info info
;
5617 info
.imageLoadAddress
= (mach_header
*)loadAddress
;
5618 info
.imageFilePath
= strdup(dyldPath
);
5619 info
.imageFileModDate
= sb
.st_mtime
;
5620 addImagesToAllImages(1, &info
);
5621 dyld::gProcessInfo
->notification(dyld_image_adding
, 1, &info
);
5623 fsid_t fsid
= {{0, 0}};
5624 fsobj_id_t fsobj
= {0};
5625 ino_t inode
= sb
.st_ino
;
5626 fsobj
.fid_objno
= (uint32_t)inode
;
5627 fsobj
.fid_generation
= (uint32_t)(inode
>>32);
5628 fsid
.val
[0] = sb
.st_dev
;
5629 dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A
, dyldPath
, (const uuid_t
*)&uuidCmd
->uuid
[0], fsobj
, fsid
, (const mach_header
*)loadAddress
);
5631 const char** appleParams
= apple
;
5633 // <rdar://problem/5077374> have host dyld detach macOS shared cache from process before jumping into dyld_sim
5634 dyld3::deallocateExistingSharedCache();
5636 // jump into new simulator dyld
5637 typedef uintptr_t (*sim_entry_proc_t
)(int argc
, const char* argv
[], const char* envp
[], const char* apple
[],
5638 const macho_header
* mainExecutableMH
, const macho_header
* dyldMH
, uintptr_t dyldSlide
,
5639 const dyld::SyscallHelpers
* vtable
, uintptr_t* startGlue
);
5640 sim_entry_proc_t newDyld
= (sim_entry_proc_t
)entry
;
5641 *mainAddr
= (*newDyld
)(argc
, argv
, envp
, appleParams
, mainExecutableMH
, (macho_header
*)loadAddress
,
5642 loadAddress
- preferredLoadAddress
,
5643 &sSysCalls
, startGlue
);
5649 // If the DYLD_SKIP_MAIN environment is set to 1, dyld will return the
5650 // address of this function instead of main() in the target program which
5651 // __dyld_start jumps to. Useful for qualifying dyld itself.
5661 #if !TARGET_OS_SIMULATOR
5663 static bool envVarMatches(const dyld3::closure::LaunchClosure
* mainClosure
, const char* envp
[], const char* varName
)
5665 __block
const char* valueFromClosure
= nullptr;
5666 mainClosure
->forEachEnvVar(^(const char* keyEqualValue
, bool& stop
) {
5667 size_t keyLen
= strlen(varName
);
5668 if ( (strncmp(varName
, keyEqualValue
, keyLen
) == 0) && (keyEqualValue
[keyLen
] == '=') ) {
5669 valueFromClosure
= &keyEqualValue
[keyLen
+1];
5674 const char* valueFromEnv
= _simple_getenv(envp
, varName
);
5676 bool inClosure
= (valueFromClosure
!= nullptr);
5677 bool inEnv
= (valueFromEnv
!= nullptr);
5678 if ( inClosure
!= inEnv
)
5680 if ( !inClosure
&& !inEnv
)
5682 return ( strcmp(valueFromClosure
, valueFromEnv
) == 0 );
5685 static const char* const sEnvVarsToCheck
[] = {
5686 "DYLD_LIBRARY_PATH",
5687 "DYLD_FRAMEWORK_PATH",
5688 "DYLD_FALLBACK_LIBRARY_PATH",
5689 "DYLD_FALLBACK_FRAMEWORK_PATH",
5690 "DYLD_INSERT_LIBRARIES",
5691 "DYLD_IMAGE_SUFFIX",
5692 "DYLD_VERSIONED_FRAMEWORK_PATH",
5693 "DYLD_VERSIONED_LIBRARY_PATH",
5697 static bool envVarsMatch(const dyld3::closure::LaunchClosure
* mainClosure
, const char* envp
[])
5699 for (const char* envVar
: sEnvVarsToCheck
) {
5700 if ( !envVarMatches(mainClosure
, envp
, envVar
) ) {
5701 if ( gLinkContext
.verboseWarnings
)
5702 dyld::log("dyld: closure %p not used because %s changed\n", mainClosure
, envVar
);
5707 // FIXME: dyld3 doesn't support versioned paths so we need to fall back to dyld2 if we have them.
5708 // <rdar://problem/37004660> dyld3: support DYLD_VERSIONED_*_PATHs ?
5709 if ( sEnv
.DYLD_VERSIONED_LIBRARY_PATH
!= nullptr ) {
5710 if ( gLinkContext
.verboseWarnings
)
5711 dyld::log("dyld: closure %p not used because DYLD_VERSIONED_LIBRARY_PATH used\n", mainClosure
);
5714 if ( sEnv
.DYLD_VERSIONED_FRAMEWORK_PATH
!= nullptr ) {
5715 if ( gLinkContext
.verboseWarnings
)
5716 dyld::log("dyld: closure %p not used because DYLD_VERSIONED_FRAMEWORK_PATH used\n", mainClosure
);
5723 static bool closureValid(const dyld3::closure::LaunchClosure
* mainClosure
, const dyld3::closure::LoadedFileInfo
& mainFileInfo
,
5724 const uint8_t* mainExecutableCDHash
, bool closureInCache
, const char* envp
[])
5726 if ( closureInCache
) {
5727 // We can only use the cache closure if the cache version is the same as dyld
5728 if (sSharedCacheLoadInfo
.loadAddress
->header
.formatVersion
!= dyld3::closure::kFormatVersion
) {
5729 if ( gLinkContext
.verboseWarnings
)
5730 dyld::log("dyld: dyld closure version 0x%08X does not match dyld cache version 0x%08X\n",
5731 dyld3::closure::kFormatVersion
, sSharedCacheLoadInfo
.loadAddress
->header
.formatVersion
);
5734 if (sForceInvalidSharedCacheClosureFormat
) {
5735 if ( gLinkContext
.verboseWarnings
)
5736 dyld::log("dyld: closure %p dyld cache version forced invalid\n", mainClosure
);
5740 // verify current dyld cache is same as expected
5741 uuid_t expectedCacheUUID
;
5742 if ( mainClosure
->builtAgainstDyldCache(expectedCacheUUID
) ) {
5743 if ( sSharedCacheLoadInfo
.loadAddress
== nullptr ) {
5744 if ( gLinkContext
.verboseWarnings
)
5745 dyld::log("dyld: closure %p dyld cache not loaded\n", mainClosure
);
5749 uuid_t actualCacheUUID
;
5750 sSharedCacheLoadInfo
.loadAddress
->getUUID(actualCacheUUID
);
5751 if ( memcmp(expectedCacheUUID
, actualCacheUUID
, sizeof(uuid_t
)) != 0 ) {
5752 if ( gLinkContext
.verboseWarnings
)
5753 dyld::log("dyld: closure %p not used because built against different dyld cache\n", mainClosure
);
5759 // closure built assume there is no dyld cache
5760 if ( sSharedCacheLoadInfo
.loadAddress
!= nullptr ) {
5761 if ( gLinkContext
.verboseWarnings
)
5762 dyld::log("dyld: closure %p built expecting no dyld cache\n", mainClosure
);
5768 // verify all mach-o files have not changed since closure was built
5769 __block
bool foundFileThatInvalidatesClosure
= false;
5770 mainClosure
->images()->forEachImage(^(const dyld3::closure::Image
* image
, bool& stop
) {
5771 __block
uint64_t expectedInode
;
5772 __block
uint64_t expectedMtime
;
5773 if ( image
->hasFileModTimeAndInode(expectedInode
, expectedMtime
) ) {
5774 struct stat statBuf
;
5775 if ( dyld3::stat(image
->path(), &statBuf
) == 0 ) {
5776 if ( (statBuf
.st_mtime
!= expectedMtime
) || (statBuf
.st_ino
!= expectedInode
) ) {
5777 if ( gLinkContext
.verboseWarnings
)
5778 dyld::log("dyld: closure %p not used because mtime/inode for '%s' has changed since closure was built\n", mainClosure
, image
->path());
5779 foundFileThatInvalidatesClosure
= true;
5784 if ( gLinkContext
.verboseWarnings
)
5785 dyld::log("dyld: closure %p not used because '%s' is needed by closure but is missing\n", mainClosure
, image
->path());
5786 foundFileThatInvalidatesClosure
= true;
5791 if ( foundFileThatInvalidatesClosure
)
5794 // verify cdHash of main executable is same as recorded in closure
5795 const dyld3::closure::Image
* mainImage
= mainClosure
->topImage();
5797 __block
bool foundCDHash
= false;
5798 __block
bool foundValidCDHash
= false;
5799 mainImage
->forEachCDHash(^(const uint8_t *expectedHash
, bool& stop
) {
5800 if ( mainExecutableCDHash
== nullptr ) {
5801 if ( gLinkContext
.verboseWarnings
)
5802 dyld::log("dyld: closure %p not used because main executable is not code signed but was expected to be\n", mainClosure
);
5807 if ( memcmp(mainExecutableCDHash
, expectedHash
, 20) == 0 ) {
5808 // found a match, so lets use this one.
5809 foundValidCDHash
= true;
5815 // If we found cd hashes, but they were all invalid, then print them out
5816 if ( foundCDHash
&& !foundValidCDHash
) {
5817 auto getCDHashString
= [](const uint8_t cdHash
[20], char* cdHashBuffer
) {
5818 for (int i
=0; i
< 20; ++i
) {
5819 uint8_t byte
= cdHash
[i
];
5820 uint8_t nibbleL
= byte
& 0x0F;
5821 uint8_t nibbleH
= byte
>> 4;
5822 if ( nibbleH
< 10 ) {
5823 *cdHashBuffer
= '0' + nibbleH
;
5826 *cdHashBuffer
= 'a' + (nibbleH
-10);
5829 if ( nibbleL
< 10 ) {
5830 *cdHashBuffer
= '0' + nibbleL
;
5833 *cdHashBuffer
= 'a' + (nibbleL
-10);
5838 if ( gLinkContext
.verboseWarnings
) {
5839 mainImage
->forEachCDHash(^(const uint8_t *expectedHash
, bool &stop
) {
5840 char mainExecutableCDHashBuffer
[128] = { '\0' };
5841 char expectedCDHashBuffer
[128] = { '\0' };
5843 getCDHashString(mainExecutableCDHash
, mainExecutableCDHashBuffer
);
5844 getCDHashString(expectedHash
, expectedCDHashBuffer
);
5846 dyld::log("dyld: closure %p not used because main executable cd-hash (%s) changed since closure was built with (%s)\n",
5847 mainClosure
, mainExecutableCDHashBuffer
, expectedCDHashBuffer
);
5854 // verify UUID of main executable is same as recorded in closure
5855 uuid_t expectedUUID
;
5856 bool hasExpect
= mainImage
->getUuid(expectedUUID
);
5858 const dyld3::MachOLoaded
* mainExecutableMH
= (const dyld3::MachOLoaded
*)mainFileInfo
.fileContent
;
5859 bool hasActual
= mainExecutableMH
->getUuid(actualUUID
);
5860 if ( hasExpect
!= hasActual
) {
5861 if ( gLinkContext
.verboseWarnings
)
5862 dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosure
);
5865 if ( hasExpect
&& hasActual
&& memcmp(actualUUID
, expectedUUID
, sizeof(uuid_t
)) != 0 ) {
5866 if ( gLinkContext
.verboseWarnings
)
5867 dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosure
);
5871 // verify DYLD_* env vars are same as when closure was built
5872 if ( !envVarsMatch(mainClosure
, envp
) ) {
5876 // verify files that are supposed to be missing actually are missing
5877 mainClosure
->forEachMustBeMissingFile(^(const char* path
, bool& stop
) {
5878 struct stat statBuf
;
5879 if ( dyld3::stat(path
, &statBuf
) == 0 ) {
5881 foundFileThatInvalidatesClosure
= true;
5882 if ( gLinkContext
.verboseWarnings
)
5883 dyld::log("dyld: closure %p not used because found unexpected file '%s'\n", mainClosure
, path
);
5887 // verify files that are supposed to exist are there with the
5888 mainClosure
->forEachSkipIfExistsFile(^(const dyld3::closure::LaunchClosure::SkippedFile
&file
, bool &stop
) {
5889 struct stat statBuf
;
5890 if ( dyld3::stat(file
.path
, &statBuf
) == 0 ) {
5891 if ( (statBuf
.st_mtime
!= file
.mtime
) || (statBuf
.st_ino
!= file
.inode
) ) {
5892 if ( gLinkContext
.verboseWarnings
)
5893 dyld::log("dyld: closure %p not used because mtime/inode for '%s' has changed since closure was built\n", mainClosure
, file
.path
);
5894 foundFileThatInvalidatesClosure
= true;
5900 // verify closure did not require anything unavailable
5901 if ( mainClosure
->usedAtPaths() && !gLinkContext
.allowAtPaths
) {
5902 if ( gLinkContext
.verboseWarnings
)
5903 dyld::log("dyld: closure %p not used because is used @paths, but process does not allow that\n", mainClosure
);
5906 if ( mainClosure
->usedFallbackPaths() && !gLinkContext
.allowClassicFallbackPaths
) {
5907 if ( gLinkContext
.verboseWarnings
)
5908 dyld::log("dyld: closure %p not used because is used default fallback paths, but process does not allow that\n", mainClosure
);
5911 if ( mainClosure
->usedInterposing() && !gLinkContext
.allowInterposing
) {
5912 if ( gLinkContext
.verboseWarnings
)
5913 dyld::log("dyld: closure %p not used because is uses interposing, but process does not allow that\n", mainClosure
);
5916 return !foundFileThatInvalidatesClosure
;
5919 static bool nolog(const char* format
, ...)
5924 static bool dolog(const char* format
, ...)
5927 va_start(list
, format
);
5933 static bool launchWithClosure(const dyld3::closure::LaunchClosure
* mainClosure
,
5934 const DyldSharedCache
* dyldCache
,
5935 const dyld3::MachOLoaded
* mainExecutableMH
, uintptr_t mainExecutableSlide
,
5936 int argc
, const char* argv
[], const char* envp
[], const char* apple
[], Diagnostics
& diag
,
5937 uintptr_t* entry
, uintptr_t* startGlue
, bool* closureOutOfDate
, bool* recoverable
)
5939 *closureOutOfDate
= false;
5940 *recoverable
= true;
5942 // build list of all known ImageArrays (at most three: cached dylibs, other OS dylibs, and main prog)
5943 STACK_ALLOC_ARRAY(const dyld3::closure::ImageArray
*, imagesArrays
, 3);
5944 const dyld3::closure::ImageArray
* mainClosureImages
= mainClosure
->images();
5945 if ( dyldCache
!= nullptr ) {
5946 imagesArrays
.push_back(dyldCache
->cachedDylibsImageArray());
5947 if ( auto others
= dyldCache
->otherOSImageArray() )
5948 imagesArrays
.push_back(others
);
5950 imagesArrays
.push_back(mainClosureImages
);
5952 // allocate space for Array<LoadedImage>
5953 STACK_ALLOC_ARRAY(dyld3::LoadedImage
, allImages
, mainClosure
->initialLoadCount());
5954 STACK_ALLOC_ARRAY(dyld3::LoadedImage
, noImages
, 1);
5956 // Get the pre-optimized Objective-C so that we can bind the selectors
5957 const dyld3::closure::ObjCSelectorOpt
* selectorOpt
= nullptr;
5958 dyld3::Array
<dyld3::closure::Image::ObjCSelectorImage
> selectorImages
;
5959 mainClosure
->selectorHashTable(selectorImages
, selectorOpt
);
5961 __block
dyld3::Loader
loader(noImages
, allImages
, dyldCache
, imagesArrays
,
5962 selectorOpt
, selectorImages
, sRootsChecker
,
5963 (dyld3::Platform
)gProcessInfo
->platform
,
5964 (gLinkContext
.verboseLoading
? &dolog
: &nolog
),
5965 (gLinkContext
.verboseMapping
? &dolog
: &nolog
),
5966 (gLinkContext
.verboseBind
? &dolog
: &nolog
),
5967 (gLinkContext
.verboseDOF
? &dolog
: &nolog
),
5968 (sClosureKind
== ClosureKind::minimal
),
5969 (dyld3::LaunchErrorInfo
*)&gProcessInfo
->errorKind
);
5970 dyld3::closure::ImageNum mainImageNum
= mainClosure
->topImageNum();
5971 mainClosureImages
->forEachImage(^(const dyld3::closure::Image
* image
, bool& stop
) {
5972 if ( image
->imageNum() == mainImageNum
) {
5973 // add main executable (which is already mapped by kernel) to list
5974 dyld3::LoadedImage mainLoadedImage
= dyld3::LoadedImage::make(image
, mainExecutableMH
);
5975 mainLoadedImage
.setState(dyld3::LoadedImage::State::mapped
);
5976 mainLoadedImage
.markLeaveMapped();
5977 loader
.addImage(mainLoadedImage
);
5981 // add inserted library to initial list
5982 loader
.addImage(dyld3::LoadedImage::make(image
));
5986 // recursively load all dependents and fill in allImages array
5987 bool someCacheImageOverridden
= false;
5988 loader
.completeAllDependents(diag
, someCacheImageOverridden
);
5989 if ( diag
.noError() )
5990 loader
.mapAndFixupAllImages(diag
, dyld3::Loader::dtraceUserProbesEnabled(), false, closureOutOfDate
, recoverable
);
5991 if ( diag
.hasError() ) {
5992 if ( gLinkContext
.verboseWarnings
)
5993 dyld::log("dyld: %s\n", diag
.errorMessage());
5994 if ( !*recoverable
) {
5995 // we won't make it to libDyldEntry, so the image list will never be set up
5996 // hack together an image list here so crash reports show the binaries involved
5997 __block
unsigned loadImageCount
= 0;
5998 loader
.forEachImage(^(const dyld3::LoadedImage
& li
, bool& stop
) {
6001 dyld_image_info
* tempArray
= new dyld_image_info
[loadImageCount
];
6002 __block
unsigned i
= 0;
6003 loader
.forEachImage(^(const dyld3::LoadedImage
& li
, bool& stop
) {
6004 tempArray
[i
].imageFilePath
= li
.image()->path();
6005 tempArray
[i
].imageLoadAddress
= li
.loadedAddress();
6006 tempArray
[i
].imageFileModDate
= 0;
6009 dyld::gProcessInfo
->infoArray
= tempArray
;
6010 dyld::gProcessInfo
->infoArrayCount
= loadImageCount
;
6011 dyld::gProcessInfo
->initialImageCount
= loadImageCount
;
6012 dyld::gProcessInfo
->infoArrayChangeTimestamp
= mach_absolute_time();
6017 //dyld::log("loaded image list:\n");
6018 //for (const dyld3::LoadedImage& info : allImages) {
6019 // dyld::log("mh=%p, path=%s\n", info.loadedAddress(), info.image()->path());
6022 // find libdyld entry
6023 dyld3::closure::Image::ResolvedSymbolTarget dyldEntry
;
6024 mainClosure
->libDyldEntry(dyldEntry
);
6025 const dyld3::LibDyldEntryVector
* libDyldEntry
= (dyld3::LibDyldEntryVector
*)loader
.resolveTarget(dyldEntry
);
6027 // send info on all images to libdyld.dylb
6028 libDyldEntry
->setVars(mainExecutableMH
, argc
, argv
, envp
, apple
, sKeysDisabled
, sOnlyPlatformArm64e
);
6030 uint32_t progVarsOffset
;
6031 if ( mainClosure
->hasProgramVars(progVarsOffset
) ) {
6032 if ( libDyldEntry
->vectorVersion
>= 8 ) {
6033 // main executable contains globals to hold argc, argv, envp, and progname, but they need to be filled in
6034 ProgramVars
* vars
= (ProgramVars
*)((uint8_t*)mainExecutableMH
+ progVarsOffset
);
6035 *vars
->NXArgcPtr
= argc
;
6036 *vars
->NXArgvPtr
= argv
;
6037 *vars
->environPtr
= envp
;
6038 *vars
->__prognamePtr
= (argv
[0] != NULL
) ? basename(argv
[0]) : "";
6039 // set up so libSystem gets ProgramVars struct embedded in main executable
6040 libDyldEntry
->setProgramVars(vars
);
6044 if ( libDyldEntry
->vectorVersion
> 4 )
6045 libDyldEntry
->setRestrictions(gLinkContext
.allowAtPaths
, gLinkContext
.allowEnvVarsPath
, gLinkContext
.allowClassicFallbackPaths
);
6046 libDyldEntry
->setHaltFunction(&halt
);
6047 if ( libDyldEntry
->vectorVersion
> 5 ) {
6048 libDyldEntry
->setNotifyMonitoringDyldMain(¬ifyMonitoringDyldMain
);
6049 libDyldEntry
->setNotifyMonitoringDyld(¬ifyMonitoringDyld
);
6052 if ( libDyldEntry
->vectorVersion
> 6 )
6053 libDyldEntry
->setHasCacheOverrides(someCacheImageOverridden
);
6055 if ( libDyldEntry
->vectorVersion
> 2 )
6056 libDyldEntry
->setChildForkFunction(&_dyld_fork_child
);
6057 #if !TARGET_OS_SIMULATOR
6058 if ( libDyldEntry
->vectorVersion
> 3 )
6059 libDyldEntry
->setLogFunction(&dyld::vlog
);
6061 if ( libDyldEntry
->vectorVersion
>= 9 )
6062 libDyldEntry
->setLaunchMode(sLaunchModeUsed
);
6065 libDyldEntry
->setOldAllImageInfo(gProcessInfo
);
6066 dyld3::LoadedImage
* libSys
= loader
.findImage(mainClosure
->libSystemImageNum());
6067 libDyldEntry
->setInitialImageList(mainClosure
, dyldCache
, sSharedCacheLoadInfo
.path
, allImages
, *libSys
);
6069 CRSetCrashLogMessage("dyld3: launch, running initializers");
6070 libDyldEntry
->runInitialzersBottomUp((mach_header
*)mainExecutableMH
);
6071 //dyld::log("returned from runInitialzersBottomUp()\n");
6073 if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
)) {
6074 dyld3::kdebug_trace_dyld_duration_end(launchTraceID
, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
, 0, 0, 3);
6077 if ( gLinkContext
.driverKit
) {
6078 if (libDyldEntry
->vectorVersion
>= 10)
6079 *entry
= (uintptr_t)libDyldEntry
->getDriverkitMain();
6081 halt("no entry point registered");
6082 if ( sClosureKind
!= ClosureKind::minimal
)
6083 halt("driverkit process should run with minimal closures");
6084 *startGlue
= (uintptr_t)(libDyldEntry
->startFunc
);
6089 dyld3::closure::Image::ResolvedSymbolTarget progEntry
;
6090 if ( mainClosure
->mainEntry(progEntry
) ) {
6091 // modern app with LC_MAIN
6092 // set startGlue to "start" function in libdyld.dylib
6093 // set entry to "main" function in program
6094 *startGlue
= (uintptr_t)(libDyldEntry
->startFunc
);
6095 *entry
= loader
.resolveTarget(progEntry
);
6096 #if __has_feature(ptrauth_calls)
6097 // start() calls the result pointer as a function pointer so we need to sign it.
6098 *entry
= (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)*entry
, 0, 0);
6101 else if ( mainClosure
->startEntry(progEntry
) ) {
6102 // old style app linked with crt1.o
6103 // entry is "start" function in program
6105 *entry
= loader
.resolveTarget(progEntry
);
6111 CRSetCrashLogMessage("dyld3 mode");
6116 static const dyld3::closure::LaunchClosure
* mapClosureFile(const char* closurePath
)
6118 struct stat statbuf
;
6119 if ( dyld3::stat(closurePath
, &statbuf
) == -1 )
6122 // check for tombstone file
6123 if ( statbuf
.st_size
== 0 )
6126 int fd
= dyld3::open(closurePath
, O_RDONLY
, 0);
6130 const dyld3::closure::LaunchClosure
* closure
= (dyld3::closure::LaunchClosure
*)::mmap(NULL
, (size_t)statbuf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
6133 if ( closure
== MAP_FAILED
)
6139 static bool needsDyld2ErrorMessage(const char* msg
)
6141 if ( strcmp(msg
, "lazy bind opcodes missing binds") == 0 )
6146 // Note: buildLaunchClosure calls halt() if there is an error building the closure
6147 static const dyld3::closure::LaunchClosure
* buildLaunchClosure(const uint8_t* mainExecutableCDHash
,
6148 const dyld3::closure::LoadedFileInfo
& mainFileInfo
,
6150 const dyld3::Array
<uint8_t>& bootToken
)
6152 const dyld3::MachOLoaded
* mainExecutableMH
= (const dyld3::MachOLoaded
*)mainFileInfo
.fileContent
;
6153 dyld3::closure::PathOverrides pathOverrides
;
6154 pathOverrides
.setFallbackPathHandling(gLinkContext
.allowClassicFallbackPaths
? dyld3::closure::PathOverrides::FallbackPathMode::classic
: dyld3::closure::PathOverrides::FallbackPathMode::restricted
);
6155 pathOverrides
.setEnvVars(envp
, mainExecutableMH
, mainFileInfo
.path
);
6156 STACK_ALLOC_ARRAY(const dyld3::closure::ImageArray
*, imagesArrays
, 3);
6157 if ( sSharedCacheLoadInfo
.loadAddress
!= nullptr ) {
6158 imagesArrays
.push_back(sSharedCacheLoadInfo
.loadAddress
->cachedDylibsImageArray());
6159 if ( auto others
= sSharedCacheLoadInfo
.loadAddress
->otherOSImageArray() )
6160 imagesArrays
.push_back(others
);
6163 char closurePath
[PATH_MAX
];
6164 bool canSaveClosureToDisk
= !bootToken
.empty() && dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo
.path
, envp
, true, closurePath
);
6165 dyld3::LaunchErrorInfo
* errorInfo
= (dyld3::LaunchErrorInfo
*)&gProcessInfo
->errorKind
;
6166 const dyld3::GradedArchs
& archs
= dyld3::GradedArchs::forCurrentOS(sKeysDisabled
, sOnlyPlatformArm64e
);
6167 dyld3::closure::FileSystemPhysical fileSystem
;
6168 dyld3::closure::ClosureBuilder::AtPath atPathHanding
= (gLinkContext
.allowAtPaths
? dyld3::closure::ClosureBuilder::AtPath::all
: dyld3::closure::ClosureBuilder::AtPath::none
);
6169 dyld3::closure::ClosureBuilder
builder(dyld3::closure::kFirstLaunchClosureImageNum
, fileSystem
, sRootsChecker
, sSharedCacheLoadInfo
.loadAddress
, true,
6170 archs
, pathOverrides
, atPathHanding
, gLinkContext
.allowEnvVarsPath
, errorInfo
, (dyld3::Platform
)gProcessInfo
->platform
);
6171 if (sForceInvalidSharedCacheClosureFormat
)
6172 builder
.setDyldCacheInvalidFormatVersion();
6173 if (sClosureKind
== ClosureKind::minimal
)
6174 builder
.makeMinimalClosures();
6175 else if ( canSaveClosureToDisk
)
6176 builder
.setCanSkipEncodingRebases(); // <rdar://problem/56172089> large iOS apps with massive number of rebases can overflow 16MB closure file limit
6177 if ( !gLinkContext
.allowInterposing
)
6178 builder
.disableInterposing();
6180 const dyld3::closure::LaunchClosure
* result
= builder
.makeLaunchClosure(mainFileInfo
, gLinkContext
.allowInsertFailures
);
6181 if ( builder
.diagnostics().hasError() ) {
6182 const char* errMsg
= builder
.diagnostics().errorMessage();
6183 // let apps with this error fallback to dyld2 mode
6184 if ( needsDyld2ErrorMessage(errMsg
) ) {
6185 if ( canSaveClosureToDisk
) {
6186 // create empty file as a tombstone to not keep trying
6187 int fd
= dyld3::open(closurePath
, O_WRONLY
|O_CREAT
, S_IRUSR
|S_IWUSR
);
6189 ::fchmod(fd
, S_IRUSR
);
6191 if ( gLinkContext
.verboseWarnings
)
6192 dyld::log("dyld: just built tombstone closure for %s\n", sExecPath
);
6193 // We only care about closure failures that do not also cause dyld2 to fail, so defer logging
6194 // until after dyld2 has tried to launch the binary
6195 sLogClosureFailure
= true;
6200 // terminate process
6204 if ( result
== nullptr )
6207 if ( !closureValid(result
, mainFileInfo
, mainExecutableCDHash
, false, envp
) ) {
6208 // some how the freshly generated closure is invalid...
6209 result
->deallocate();
6210 if ( gLinkContext
.verboseWarnings
)
6211 dyld::log("dyld: somehow just built closure is invalid\n");
6215 // write closure file but only if we have boot-token
6216 if ( canSaveClosureToDisk
) {
6217 if ( const dyld3::closure::LaunchClosure
* existingClosure
= mapClosureFile(closurePath
) ) {
6218 if ( (existingClosure
->size() == result
->size()) && (memcmp(existingClosure
, result
, result
->size()) == 0) ) {
6219 // closure file already exists and has same content, so re-use file by altering boot-token
6220 ::chmod(closurePath
, S_IRUSR
|S_IWUSR
); // file has to be writable to alter attributes
6221 // handle both attribute size change and missing attribute
6222 if ( ::setxattr(closurePath
, DYLD_CLOSURE_XATTR_NAME
, bootToken
.begin(), bootToken
.count(), 0, XATTR_REPLACE
) != 0 )
6223 ::setxattr(closurePath
, DYLD_CLOSURE_XATTR_NAME
, bootToken
.begin(), bootToken
.count(), 0, 0);
6224 ::chmod(closurePath
, S_IRUSR
);
6225 result
->deallocate();
6226 if ( gLinkContext
.verboseWarnings
)
6227 dyld::log("dyld: reusing previous boot %s closure %p (size=%lu) for %s\n", existingClosure
->topImage()->variantString(), existingClosure
, existingClosure
->size(), sExecPath
);
6228 return existingClosure
;
6232 char closurePathTemp
[PATH_MAX
];
6233 strlcpy(closurePathTemp
, closurePath
, PATH_MAX
);
6234 int mypid
= getpid();
6238 putHexByte(mypid
>> 24, s
);
6239 putHexByte(mypid
>> 16, s
);
6240 putHexByte(mypid
>> 8, s
);
6241 putHexByte(mypid
, s
);
6243 strlcat(closurePathTemp
, pidBuf
, PATH_MAX
);
6245 int fd
= dyld3::open(closurePathTemp
, O_WRONLY
|O_CREAT
, S_IRUSR
|S_IWUSR
);
6247 int fd
= ::open_dprotected_np(closurePathTemp
, O_WRONLY
|O_CREAT
, PROTECTION_CLASS_D
, 0, S_IRUSR
|S_IWUSR
);
6250 ::ftruncate(fd
, result
->size());
6251 ::write(fd
, result
, result
->size());
6252 ::fsetxattr(fd
, DYLD_CLOSURE_XATTR_NAME
, bootToken
.begin(), bootToken
.count(), 0, 0);
6253 ::fchmod(fd
, S_IRUSR
);
6255 ::rename(closurePathTemp
, closurePath
);
6256 // free built closure and mmap file() to reduce dirty memory
6257 result
->deallocate();
6258 result
= mapClosureFile(closurePath
);
6259 sLaunchModeUsed
|= DYLD_LAUNCH_MODE_CLOSURE_SAVED_TO_FILE
;
6261 else if ( gLinkContext
.verboseWarnings
) {
6262 dyld::log("could not save closure (errno=%d) to: %s\n", errno
, closurePathTemp
);
6266 if ( gLinkContext
.verboseWarnings
)
6267 dyld::log("dyld: just built %s closure %p (size=%lu) for %s\n", result
->topImage()->variantString(), result
, result
->size(), sExecPath
);
6272 static const dyld3::closure::LaunchClosure
* findCachedLaunchClosure(const uint8_t* mainExecutableCDHash
,
6273 const dyld3::closure::LoadedFileInfo
& mainFileInfo
,
6275 const dyld3::Array
<uint8_t>& bootToken
)
6277 // get path to where closure file will be store for this program
6278 char closurePath
[PATH_MAX
];
6279 if ( !dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo
.path
, envp
, false, closurePath
) ) {
6280 // if cannot construct path to use/store closure file, then use minimal closures
6281 if ( sClosureKind
== ClosureKind::unset
)
6282 sClosureKind
= ClosureKind::minimal
;
6286 // if file exists, but extended attribute is wrong, ignore file (might be re-used later)
6287 if ( bootToken
.empty() )
6289 uint8_t filesBootToken
[bootToken
.count()];
6290 ssize_t attrSize
= ::getxattr(closurePath
, DYLD_CLOSURE_XATTR_NAME
, filesBootToken
, bootToken
.count(), 0, 0);
6291 if ( attrSize
!= bootToken
.count() )
6293 if ( memcmp(bootToken
.begin(), filesBootToken
, bootToken
.count()) != 0 )
6296 const dyld3::closure::LaunchClosure
* closure
= mapClosureFile(closurePath
);
6297 if ( closure
== nullptr )
6300 if ( !closureValid(closure
, mainFileInfo
, mainExecutableCDHash
, false, envp
) ) {
6301 ::munmap((void*)closure
, closure
->size());
6305 if ( gLinkContext
.verboseWarnings
)
6306 dyld::log("dyld: used cached %s closure %p (size=%lu) for %s\n", closure
->topImage()->variantString(), closure
, closure
->size(), sExecPath
);
6311 #endif // !TARGET_OS_SIMULATOR
6314 static ClosureMode
getPlatformDefaultClosureMode() {
6317 // rdar://problem/32701418: Don't use dyld3 for i386 for now.
6318 return ClosureMode::Off
;
6320 // x86_64 defaults to using the shared cache closures
6321 return ClosureMode::PreBuiltOnly
;
6325 // <rdar://problem/33171968> enable dyld3 mode for all OS programs when using customer dyld cache (no roots)
6326 if ( (sSharedCacheLoadInfo
.loadAddress
!= nullptr) && (sSharedCacheLoadInfo
.loadAddress
->header
.cacheType
== kDyldSharedCacheTypeProduction
) )
6327 return ClosureMode::On
;
6329 return ClosureMode::Off
;
6330 #endif // TARGET_OS_OSX
6334 // Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which
6335 // sets up some registers and call this function.
6337 // Returns address of main() in target program which __dyld_start jumps to
6340 _main(const macho_header
* mainExecutableMH
, uintptr_t mainExecutableSlide
,
6341 int argc
, const char* argv
[], const char* envp
[], const char* apple
[],
6342 uintptr_t* startGlue
)
6344 if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
)) {
6345 launchTraceID
= dyld3::kdebug_trace_dyld_duration_start(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
, (uint64_t)mainExecutableMH
, 0, 0);
6348 //Check and see if there are any kernel flags
6349 dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple
, "dyld_flags"), nullptr));
6351 #if __has_feature(ptrauth_calls)
6352 // Check and see if kernel disabled JOP pointer signing (which lets us load plain arm64 binaries)
6353 if ( const char* disableStr
= _simple_getenv(apple
, "ptrauth_disabled") ) {
6354 if ( strcmp(disableStr
, "1") == 0 )
6355 sKeysDisabled
= true;
6358 // needed until kernel passes ptrauth_disabled for arm64 main executables
6359 if ( (mainExecutableMH
->cpusubtype
== CPU_SUBTYPE_ARM64_V8
) || (mainExecutableMH
->cpusubtype
== CPU_SUBTYPE_ARM64_ALL
) )
6360 sKeysDisabled
= true;
6364 // Grab the cdHash of the main executable from the environment
6365 uint8_t mainExecutableCDHashBuffer
[20];
6366 const uint8_t* mainExecutableCDHash
= nullptr;
6367 if ( const char* mainExeCdHashStr
= _simple_getenv(apple
, "executable_cdhash") ) {
6368 unsigned bufferLenUsed
;
6369 if ( hexStringToBytes(mainExeCdHashStr
, mainExecutableCDHashBuffer
, sizeof(mainExecutableCDHashBuffer
), bufferLenUsed
) )
6370 mainExecutableCDHash
= mainExecutableCDHashBuffer
;
6373 getHostInfo(mainExecutableMH
, mainExecutableSlide
);
6375 #if !TARGET_OS_SIMULATOR
6376 // Trace dyld's load
6377 notifyKernelAboutImage((macho_header
*)&__dso_handle
, _simple_getenv(apple
, "dyld_file"));
6378 // Trace the main executable's load
6379 notifyKernelAboutImage(mainExecutableMH
, _simple_getenv(apple
, "executable_file"));
6382 uintptr_t result
= 0;
6383 sMainExecutableMachHeader
= mainExecutableMH
;
6384 sMainExecutableSlide
= mainExecutableSlide
;
6387 // Set the platform ID in the all image infos so debuggers can tell the process type
6388 // FIXME: This can all be removed once we make the kernel handle it in rdar://43369446
6389 // The host may not have the platform field in its struct, but there's space for it in the padding, so always set it
6391 __block
bool platformFound
= false;
6392 ((dyld3::MachOFile
*)mainExecutableMH
)->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
6393 if (platformFound
) {
6394 halt("MH_EXECUTE binaries may only specify one platform");
6396 gProcessInfo
->platform
= (uint32_t)platform
;
6397 platformFound
= true;
6399 if (gProcessInfo
->platform
== (uint32_t)dyld3::Platform::unknown
) {
6400 // There were no platforms found in the binary. This may occur on macOS for alternate toolchains and old binaries.
6401 // It should never occur on any of our embedded platforms.
6403 gProcessInfo
->platform
= (uint32_t)dyld3::Platform::macOS
;
6405 halt("MH_EXECUTE binaries must specify a minimum supported OS version");
6411 // Check to see if we need to override the platform
6412 const char* forcedPlatform
= _simple_getenv(envp
, "DYLD_FORCE_PLATFORM");
6413 if (forcedPlatform
) {
6414 dyld_platform_t forcedPlatformType
= 0;
6415 if (strncmp(forcedPlatform
, "6", 1) == 0) {
6416 forcedPlatformType
= PLATFORM_MACCATALYST
;
6417 } else if (strncmp(forcedPlatform
, "2", 1) == 0) {
6418 forcedPlatformType
= PLATFORM_IOS
;
6420 halt("DYLD_FORCE_PLATFORM is only supported for platform 2 or 6.");
6422 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)sMainExecutableMachHeader
;
6423 if (mf
->allowsAlternatePlatform()) {
6424 gProcessInfo
->platform
= forcedPlatformType
;
6428 // if this is host dyld, check to see if iOS simulator is being run
6429 const char* rootPath
= _simple_getenv(envp
, "DYLD_ROOT_PATH");
6430 if ( (rootPath
!= NULL
) ) {
6431 // look to see if simulator has its own dyld
6432 char simDyldPath
[PATH_MAX
];
6433 strlcpy(simDyldPath
, rootPath
, PATH_MAX
);
6434 strlcat(simDyldPath
, "/usr/lib/dyld_sim", PATH_MAX
);
6435 int fd
= dyld3::open(simDyldPath
, O_RDONLY
, 0);
6437 const char* errMessage
= useSimulatorDyld(fd
, mainExecutableMH
, simDyldPath
, argc
, argv
, envp
, apple
, startGlue
, &result
);
6438 if ( errMessage
!= NULL
)
6444 ((dyld3::MachOFile
*)mainExecutableMH
)->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
6445 if ( dyld3::MachOFile::isSimulatorPlatform(platform
) )
6446 halt("attempt to run simulator program outside simulator (DYLD_ROOT_PATH not set)");
6451 CRSetCrashLogMessage("dyld: launch started");
6453 setContext(mainExecutableMH
, argc
, argv
, envp
, apple
);
6455 // Pickup the pointer to the exec path.
6456 sExecPath
= _simple_getenv(apple
, "executable_path");
6458 // <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
6459 if (!sExecPath
) sExecPath
= apple
[0];
6461 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
6462 // <rdar://54095622> kernel is not passing a real path for main executable
6463 if ( strncmp(sExecPath
, "/var/containers/Bundle/Application/", 35) == 0 ) {
6464 if ( char* newPath
= (char*)malloc(strlen(sExecPath
)+10) ) {
6465 strcpy(newPath
, "/private");
6466 strcat(newPath
, sExecPath
);
6467 sExecPath
= newPath
;
6472 if ( sExecPath
[0] != '/' ) {
6473 // have relative path, use cwd to make absolute
6474 char cwdbuff
[MAXPATHLEN
];
6475 if ( getcwd(cwdbuff
, MAXPATHLEN
) != NULL
) {
6476 // maybe use static buffer to avoid calling malloc so early...
6477 char* s
= new char[strlen(cwdbuff
) + strlen(sExecPath
) + 2];
6480 strcat(s
, sExecPath
);
6485 // Remember short name of process for later logging
6486 sExecShortName
= ::strrchr(sExecPath
, '/');
6487 if ( sExecShortName
!= NULL
)
6490 sExecShortName
= sExecPath
;
6492 #if TARGET_OS_OSX && __has_feature(ptrauth_calls)
6493 // on Apple Silicon macOS, only Apple signed ("platform binary") arm64e can be loaded
6494 sOnlyPlatformArm64e
= true;
6496 // internal builds, or if boot-arg is set, then non-platform-binary arm64e slices can be run
6497 if ( const char* abiMode
= _simple_getenv(apple
, "arm64e_abi") ) {
6498 if ( strcmp(abiMode
, "all") == 0 )
6499 sOnlyPlatformArm64e
= false;
6503 configureProcessRestrictions(mainExecutableMH
, envp
);
6505 // Check if we should force dyld3. Note we have to do this outside of the regular env parsing due to AMFI
6506 if ( dyld3::internalInstall() ) {
6507 if (const char* useClosures
= _simple_getenv(envp
, "DYLD_USE_CLOSURES")) {
6508 if ( strcmp(useClosures
, "0") == 0 ) {
6509 sClosureMode
= ClosureMode::Off
;
6510 } else if ( strcmp(useClosures
, "1") == 0 ) {
6511 #if !__i386__ // don't support dyld3 for 32-bit macOS
6512 sClosureMode
= ClosureMode::On
;
6513 sClosureKind
= ClosureKind::full
;
6515 } else if ( strcmp(useClosures
, "2") == 0 ) {
6516 sClosureMode
= ClosureMode::On
;
6517 sClosureKind
= ClosureKind::minimal
;
6519 dyld::warn("unknown option to DYLD_USE_CLOSURES. Valid options are: 0 and 1\n");
6526 if ( !gLinkContext
.allowEnvVarsPrint
&& !gLinkContext
.allowEnvVarsPath
&& !gLinkContext
.allowEnvVarsSharedCache
) {
6527 pruneEnvironmentVariables(envp
, &apple
);
6528 // set again because envp and apple may have changed or moved
6529 setContext(mainExecutableMH
, argc
, argv
, envp
, apple
);
6534 checkEnvironmentVariables(envp
);
6535 defaultUninitializedFallbackPaths(envp
);
6538 switch (gProcessInfo
->platform
) {
6539 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
6541 sClosureMode
= ClosureMode::On
; // <rdar://problem/56792308> Run iOS apps on macOS in dyld3 mode
6542 [[clang::fallthrough]];
6544 case PLATFORM_MACCATALYST
:
6545 gLinkContext
.rootPaths
= parseColonList("/System/iOSSupport", NULL
);
6546 gLinkContext
.iOSonMac
= true;
6547 if ( sEnv
.DYLD_FALLBACK_LIBRARY_PATH
== sLibraryFallbackPaths
)
6548 sEnv
.DYLD_FALLBACK_LIBRARY_PATH
= sRestrictedLibraryFallbackPaths
;
6549 if ( sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
== sFrameworkFallbackPaths
)
6550 sEnv
.DYLD_FALLBACK_FRAMEWORK_PATH
= sRestrictedFrameworkFallbackPaths
;
6552 case PLATFORM_DRIVERKIT
:
6553 gLinkContext
.driverKit
= true;
6554 gLinkContext
.sharedRegionMode
= ImageLoader::kDontUseSharedRegion
;
6558 if ( sEnv
.DYLD_PRINT_OPTS
)
6560 if ( sEnv
.DYLD_PRINT_ENV
)
6561 printEnvironmentVariables(envp
);
6563 // Parse this envirionment variable outside of the regular logic as we want to accept
6564 // this on binaries without an entitelment
6565 #if !TARGET_OS_SIMULATOR
6566 if ( _simple_getenv(envp
, "DYLD_JUST_BUILD_CLOSURE") != nullptr ) {
6567 #if TARGET_OS_IPHONE
6568 char tempClosurePath
[PATH_MAX
];
6569 if ( dyld3::closure::LaunchClosure::buildClosureCachePath(sExecPath
, envp
, false, tempClosurePath
) )
6570 sJustBuildClosure
= true;
6572 // If the env vars for the data contain look wrong, don't want to launch the app as that would bring up the UI
6573 if (!sJustBuildClosure
) {
6574 _exit(EXIT_SUCCESS
);
6579 if ( sJustBuildClosure
)
6580 sClosureMode
= ClosureMode::On
;
6582 // load shared cache
6583 checkSharedRegionDisable((dyld3::MachOLoaded
*)mainExecutableMH
, mainExecutableSlide
);
6584 if ( gLinkContext
.sharedRegionMode
!= ImageLoader::kDontUseSharedRegion
) {
6585 #if TARGET_OS_SIMULATOR
6586 if ( sSharedCacheOverrideDir
)
6587 mapSharedCache(mainExecutableSlide
);
6589 mapSharedCache(mainExecutableSlide
);
6593 #if !TARGET_OS_SIMULATOR
6594 if ( getpid() == 1 ) {
6595 // Get the value as set by the boot-args
6596 uint64_t commPageValue
= 0;
6597 size_t commPageValueSize
= sizeof(commPageValue
);
6598 if ( sysctlbyname("kern.dyld_flags", &commPageValue
, &commPageValueSize
, nullptr, 0) != 0 ) {
6599 // Try again with the old name
6600 // TODO: Remove this when we are always on new enough kernels
6601 sysctlbyname("kern.dyld_system_flags", &commPageValue
, &commPageValueSize
, nullptr, 0);
6604 commPageValue
&= CommPageBootArgMask
;
6605 // logToConsole("dyld: got comm page flags 0x%llx\n", commPageValue);
6607 // If we are PID 1 (launchd) and on macOS, then we should check if the simulator support dylibs
6608 // are roots or not.
6609 // If they are not roots at launchd time, and the file system is read-only, then we know for sure
6610 // they will not be roots later
6611 #if DYLD_SIMULATOR_ROOTS_SUPPORT
6612 bool fileSystemIsWritable
= true;
6614 // logToConsole("dyld: in launchd\n");
6615 struct statfs statBuffer
;
6616 int statResult
= statfs("/", &statBuffer
);
6617 if ( statResult
== 0 ) {
6618 if ( !strcmp(statBuffer
.f_fstypename
, "apfs") ) {
6619 if ( (statBuffer
.f_flags
& (MNT_RDONLY
| MNT_SNAPSHOT
)) == (MNT_RDONLY
| MNT_SNAPSHOT
) ) {
6620 // logToConsole("dyld: got statfs flags 0x%llx\n", statBuffer.f_flags);
6621 fileSystemIsWritable
= false;
6626 logToConsole("dyld: could not stat '/', errno = %d\n", error
);
6629 // If the file system is read-only, then we can check now whether any of the simulator support
6631 bool libsystemKernelIsRoot
= false;
6632 bool libsystemPlatformIsRoot
= false;
6633 bool libsystemPThreadIsRoot
= false;
6634 if ( !fileSystemIsWritable
&& (sSharedCacheLoadInfo
.loadAddress
!= nullptr)) {
6635 dyld3::closure::FileSystemPhysical fileSystem
;
6636 libsystemKernelIsRoot
= !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_kernel.dylib",
6637 &fileSystem
, sSharedCacheLoadInfo
.loadAddress
);
6638 libsystemPlatformIsRoot
= !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_platform.dylib",
6639 &fileSystem
, sSharedCacheLoadInfo
.loadAddress
);
6640 libsystemPThreadIsRoot
= !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_pthread.dylib",
6641 &fileSystem
, sSharedCacheLoadInfo
.loadAddress
);
6643 commPageValue
|= (fileSystemIsWritable
? CommPageFlags::fileSystemCanBeModified
: CommPageFlags::None
);
6644 commPageValue
|= (libsystemKernelIsRoot
? CommPageFlags::libsystemKernelIsRoot
: CommPageFlags::None
);
6645 commPageValue
|= (libsystemPlatformIsRoot
? CommPageFlags::libsystemPlatformIsRoot
: CommPageFlags::None
);
6646 commPageValue
|= (libsystemPThreadIsRoot
? CommPageFlags::libsystemPThreadIsRoot
: CommPageFlags::None
);
6647 #endif // DYLD_SIMULATOR_ROOTS_SUPPORT
6649 logToConsole("dyld: setting comm page to 0x%llx\n", commPageValue
);
6650 if ( sysctlbyname("kern.dyld_flags", nullptr, 0, &commPageValue
, sizeof(commPageValue
)) != 0 ) {
6651 // Try again with the old name
6652 // TODO: Remove this when we are always on new enough kernels
6653 sysctlbyname("kern.dyld_system_flags", nullptr, 0, &commPageValue
, sizeof(commPageValue
));
6657 #if DYLD_SIMULATOR_ROOTS_SUPPORT
6658 // Set the roots checker to the state from the comm page
6660 uint64_t dyldFlags
= *((uint64_t*)_COMM_PAGE_DYLD_SYSTEM_FLAGS
);
6661 bool fileSystemCanBeModified
= dyldFlags
& CommPageFlags::fileSystemCanBeModified
;
6662 bool libsystemKernelIsRoot
= dyldFlags
& CommPageFlags::libsystemKernelIsRoot
;
6663 bool libsystemPlatformIsRoot
= dyldFlags
& CommPageFlags::libsystemPlatformIsRoot
;
6664 bool libsystemPThreadIsRoot
= dyldFlags
& CommPageFlags::libsystemPThreadIsRoot
;
6665 sRootsChecker
.setFileSystemCanBeModified(fileSystemCanBeModified
);
6666 sRootsChecker
.setLibsystemKernelIsRoot(libsystemKernelIsRoot
);
6667 sRootsChecker
.setLibsystemPlatformIsRoot(libsystemPlatformIsRoot
);
6668 sRootsChecker
.setLibsystemPThreadIsRoot(libsystemPThreadIsRoot
);
6670 #endif // DYLD_SIMULATOR_ROOTS_SUPPORT
6672 #endif // !TARGET_OS_SIMULATOR
6674 // If we haven't got a closure mode yet, then check the environment and cache type
6675 if ( sClosureMode
== ClosureMode::Unset
) {
6676 // First test to see if we forced in dyld2 via a kernel boot-arg
6677 if ( dyld3::BootArgs::forceDyld2() ) {
6678 sClosureMode
= ClosureMode::Off
;
6679 } else if ( inDenyList(sExecPath
) ) {
6680 sClosureMode
= ClosureMode::Off
;
6681 } else if ( sEnv
.hasOverride
) {
6682 sClosureMode
= ClosureMode::Off
;
6683 } else if ( dyld3::BootArgs::forceDyld3() ) {
6684 sClosureMode
= ClosureMode::On
;
6686 sClosureMode
= getPlatformDefaultClosureMode();
6690 #if !TARGET_OS_SIMULATOR
6691 if ( sClosureMode
== ClosureMode::Off
) {
6692 if ( gLinkContext
.verboseWarnings
)
6693 dyld::log("dyld: not using closures\n");
6695 sLaunchModeUsed
= DYLD_LAUNCH_MODE_USING_CLOSURE
;
6696 const dyld3::closure::LaunchClosure
* mainClosure
= nullptr;
6697 dyld3::closure::LoadedFileInfo mainFileInfo
;
6698 mainFileInfo
.fileContent
= mainExecutableMH
;
6699 mainFileInfo
.path
= sExecPath
;
6700 // FIXME: If we are saving this closure, this slice offset/length is probably wrong in the case of FAT files.
6701 mainFileInfo
.sliceOffset
= 0;
6702 mainFileInfo
.sliceLen
= -1;
6703 struct stat mainExeStatBuf
;
6704 if ( dyld3::stat(sExecPath
, &mainExeStatBuf
) == 0 ) {
6705 mainFileInfo
.inode
= mainExeStatBuf
.st_ino
;
6706 mainFileInfo
.mtime
= mainExeStatBuf
.st_mtime
;
6708 // check for closure in cache first
6709 if ( sSharedCacheLoadInfo
.loadAddress
!= nullptr ) {
6710 mainClosure
= sSharedCacheLoadInfo
.loadAddress
->findClosure(sExecPath
);
6711 if ( gLinkContext
.verboseWarnings
&& (mainClosure
!= nullptr) )
6712 dyld::log("dyld: found closure %p (size=%lu) in dyld shared cache\n", mainClosure
, mainClosure
->size());
6713 if ( mainClosure
!= nullptr )
6714 sLaunchModeUsed
|= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS
;
6717 // We only want to try build a closure at runtime if its an iOS third party binary, or a macOS binary from the shared cache
6718 bool allowClosureRebuilds
= false;
6719 if ( sClosureMode
== ClosureMode::On
) {
6720 allowClosureRebuilds
= true;
6721 } else if ( (sClosureMode
== ClosureMode::PreBuiltOnly
) && (mainClosure
!= nullptr) ) {
6722 allowClosureRebuilds
= true;
6725 if ( (mainClosure
!= nullptr) && !closureValid(mainClosure
, mainFileInfo
, mainExecutableCDHash
, true, envp
) ) {
6726 mainClosure
= nullptr;
6727 sLaunchModeUsed
&= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS
;
6730 // <rdar://60333505> bootToken is a concat of boot-hash kernel passes down for app and dyld's uuid
6731 uint8_t bootTokenBufer
[128];
6732 unsigned bootTokenBufferLen
= 0;
6733 if ( const char* bootHashStr
= _simple_getenv(apple
, "executable_boothash") ) {
6734 if ( hexStringToBytes(bootHashStr
, bootTokenBufer
, sizeof(bootTokenBufer
), bootTokenBufferLen
) ) {
6735 if ( ((dyld3::MachOFile
*)&__dso_handle
)->getUuid(&bootTokenBufer
[bootTokenBufferLen
]) )
6736 bootTokenBufferLen
+= sizeof(uuid_t
);
6739 dyld3::Array
<uint8_t> bootToken(bootTokenBufer
, bootTokenBufferLen
, bootTokenBufferLen
);
6741 // If we didn't find a valid cache closure then try build a new one
6742 if ( (mainClosure
== nullptr) && allowClosureRebuilds
) {
6743 // if forcing closures, and no closure in cache, or it is invalid, check for cached closure
6744 if ( !sForceInvalidSharedCacheClosureFormat
)
6745 mainClosure
= findCachedLaunchClosure(mainExecutableCDHash
, mainFileInfo
, envp
, bootToken
);
6746 if ( mainClosure
== nullptr ) {
6747 // if no cached closure found, build new one
6748 mainClosure
= buildLaunchClosure(mainExecutableCDHash
, mainFileInfo
, envp
, bootToken
);
6749 if ( mainClosure
!= nullptr )
6750 sLaunchModeUsed
|= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH
;
6754 // exit dyld after closure is built, without running program
6755 if ( sJustBuildClosure
)
6756 _exit(EXIT_SUCCESS
);
6758 // try using launch closure
6759 if ( mainClosure
!= nullptr ) {
6760 CRSetCrashLogMessage("dyld3: launch started");
6761 if ( mainClosure
->topImage()->fixupsNotEncoded() )
6762 sLaunchModeUsed
|= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE
;
6764 bool closureOutOfDate
;
6766 bool launched
= launchWithClosure(mainClosure
, sSharedCacheLoadInfo
.loadAddress
, (dyld3::MachOLoaded
*)mainExecutableMH
,
6767 mainExecutableSlide
, argc
, argv
, envp
, apple
, diag
, &result
, startGlue
, &closureOutOfDate
, &recoverable
);
6768 if ( !launched
&& closureOutOfDate
&& allowClosureRebuilds
) {
6769 // closure is out of date, build new one
6770 mainClosure
= buildLaunchClosure(mainExecutableCDHash
, mainFileInfo
, envp
, bootToken
);
6771 if ( mainClosure
!= nullptr ) {
6773 sLaunchModeUsed
|= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH
;
6774 if ( mainClosure
->topImage()->fixupsNotEncoded() )
6775 sLaunchModeUsed
|= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE
;
6777 sLaunchModeUsed
&= ~DYLD_LAUNCH_MODE_MINIMAL_CLOSURE
;
6778 launched
= launchWithClosure(mainClosure
, sSharedCacheLoadInfo
.loadAddress
, (dyld3::MachOLoaded
*)mainExecutableMH
,
6779 mainExecutableSlide
, argc
, argv
, envp
, apple
, diag
, &result
, startGlue
, &closureOutOfDate
, &recoverable
);
6783 gLinkContext
.startedInitializingMainExecutable
= true;
6785 result
= (uintptr_t)&fake_main
;
6789 if ( gLinkContext
.verboseWarnings
) {
6790 dyld::log("dyld: unable to use closure %p\n", mainClosure
);
6793 halt(diag
.errorMessage());
6797 #endif // TARGET_OS_SIMULATOR
6798 // could not use closure info, launch old way
6799 sLaunchModeUsed
= 0;
6802 // install gdb notifier
6803 stateToHandlers(dyld_image_state_dependents_mapped
, sBatchHandlers
)->push_back(notifyGDB
);
6804 stateToHandlers(dyld_image_state_mapped
, sSingleHandlers
)->push_back(updateAllImages
);
6805 // make initial allocations large enough that it is unlikely to need to be re-alloced
6806 sImageRoots
.reserve(16);
6807 sAddImageCallbacks
.reserve(4);
6808 sRemoveImageCallbacks
.reserve(4);
6809 sAddLoadImageCallbacks
.reserve(4);
6810 sImageFilesNeedingTermination
.reserve(16);
6811 sImageFilesNeedingDOFUnregistration
.reserve(8);
6813 #if !TARGET_OS_SIMULATOR
6814 #ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE
6815 // <rdar://problem/6849505> Add gating mechanism to dyld support system order file generation process
6816 WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo
->systemOrderFlag
);
6822 // add dyld itself to UUID list
6823 addDyldImageToUUIDList();
6825 #if SUPPORT_ACCELERATE_TABLES
6827 // Disable accelerator tables when we have threaded rebase/bind, which is arm64e executables only for now.
6828 if ((sMainExecutableMachHeader
->cpusubtype
& ~CPU_SUBTYPE_MASK
) == CPU_SUBTYPE_ARM64E
)
6829 sDisableAcceleratorTables
= true;
6831 bool mainExcutableAlreadyRebased
= false;
6832 if ( (sSharedCacheLoadInfo
.loadAddress
!= nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables
&& (sSharedCacheLoadInfo
.loadAddress
->header
.accelerateInfoAddr
!= 0) ) {
6833 struct stat statBuf
;
6834 if ( dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR
"no-dyld2-accelerator-tables", &statBuf
) != 0 )
6835 sAllCacheImagesProxy
= ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo
.loadAddress
->header
, sSharedCacheLoadInfo
.slide
, mainExecutableMH
, gLinkContext
);
6843 gLinkContext
.strictMachORequired
= false;
6844 // <rdar://problem/22805519> be less strict about old macOS mach-o binaries
6845 ((dyld3::MachOFile
*)mainExecutableMH
)->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
6846 if ( (platform
== dyld3::Platform::macOS
) && (sdk
>= DYLD_PACKED_VERSION(10,15,0)) ) {
6847 gLinkContext
.strictMachORequired
= true;
6850 if ( gLinkContext
.iOSonMac
)
6851 gLinkContext
.strictMachORequired
= true;
6853 // simulators, iOS, tvOS, watchOS, are always strict
6854 gLinkContext
.strictMachORequired
= true;
6858 CRSetCrashLogMessage(sLoadingCrashMessage
);
6859 // instantiate ImageLoader for main executable
6860 sMainExecutable
= instantiateFromLoadedImage(mainExecutableMH
, mainExecutableSlide
, sExecPath
);
6861 gLinkContext
.mainExecutable
= sMainExecutable
;
6862 gLinkContext
.mainExecutableCodeSigned
= hasCodeSignatureLoadCommand(mainExecutableMH
);
6864 #if TARGET_OS_SIMULATOR
6865 // check main executable is not too new for this OS
6867 if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH
, sExecPath
) ) {
6868 throwf("program was built for a platform that is not supported by this runtime");
6870 uint32_t mainMinOS
= sMainExecutable
->minOSVersion();
6872 // dyld is always built for the current OS, so we can get the current OS version
6873 // from the load command in dyld itself.
6874 uint32_t dyldMinOS
= ImageLoaderMachO::minOSVersion((const mach_header
*)&__dso_handle
);
6875 if ( mainMinOS
> dyldMinOS
) {
6877 throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d",
6878 mainMinOS
>> 16, ((mainMinOS
>> 8) & 0xFF),
6879 dyldMinOS
>> 16, ((dyldMinOS
>> 8) & 0xFF));
6881 throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d",
6882 mainMinOS
>> 16, ((mainMinOS
>> 8) & 0xFF),
6883 dyldMinOS
>> 16, ((dyldMinOS
>> 8) & 0xFF));
6885 throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d",
6886 mainMinOS
>> 16, ((mainMinOS
>> 8) & 0xFF),
6887 dyldMinOS
>> 16, ((dyldMinOS
>> 8) & 0xFF));
6894 #if SUPPORT_ACCELERATE_TABLES
6895 sAllImages
.reserve((sAllCacheImagesProxy
!= NULL
) ? 16 : INITIAL_IMAGE_COUNT
);
6897 sAllImages
.reserve(INITIAL_IMAGE_COUNT
);
6900 #if defined(__x86_64__) && !TARGET_OS_SIMULATOR
6901 if (dyld::isTranslated()) {
6902 struct dyld_all_runtime_info
{
6903 uint32_t image_count
;
6904 dyld_image_info
* images
;
6905 uint32_t uuid_count
;
6906 dyld_uuid_info
* uuids
;
6907 uint32_t aot_image_count
;
6908 dyld_aot_image_info
* aots
;
6909 dyld_aot_shared_cache_info aot_cache_info
;
6912 dyld_all_runtime_info
* runtime_info
;
6913 int ret
= syscall(0x7000004, &runtime_info
);
6915 for (int i
= 0; i
< runtime_info
->uuid_count
; i
++) {
6916 dyld_image_info image_info
= runtime_info
->images
[i
];
6917 dyld_uuid_info uuid_info
= runtime_info
->uuids
[i
];
6919 // add the arm64 cambria runtime to uuid info
6920 addNonSharedCacheImageUUID(uuid_info
);
6923 if (stat(image_info
.imageFilePath
, &sb
) == 0) {
6924 fsid_t fsid
= {{0, 0}};
6925 fsobj_id_t fsobj
= {0};
6926 ino_t inode
= sb
.st_ino
;
6927 fsobj
.fid_objno
= (uint32_t)inode
;
6928 fsobj
.fid_generation
= (uint32_t)(inode
>>32);
6929 fsid
.val
[0] = sb
.st_dev
;
6931 dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A
, image_info
.imageFilePath
, &(uuid_info
.imageUUID
), fsobj
, fsid
, image_info
.imageLoadAddress
);
6935 // add aot images to dyld_all_image_info
6936 addAotImagesToAllAotImages(runtime_info
->aot_image_count
, runtime_info
->aots
);
6938 // add the arm64 cambria runtime to dyld_all_image_info
6939 addImagesToAllImages(runtime_info
->image_count
, runtime_info
->images
);
6941 // set the aot shared cache info in dyld_all_image_info
6942 dyld::gProcessInfo
->aotSharedCacheBaseAddress
= runtime_info
->aot_cache_info
.cacheBaseAddress
;
6943 memcpy(dyld::gProcessInfo
->aotSharedCacheUUID
, runtime_info
->aot_cache_info
.cacheUUID
, sizeof(uuid_t
));
6948 // Now that shared cache is loaded, setup an versioned dylib overrides
6949 #if SUPPORT_VERSIONED_PATHS
6950 checkVersionedPaths();
6954 // dyld_all_image_infos image list does not contain dyld
6955 // add it as dyldPath field in dyld_all_image_infos
6956 // for simulator, dyld_sim is in image list, need host dyld added
6957 #if TARGET_OS_SIMULATOR
6958 // get path of host dyld from table of syscall vectors in host dyld
6959 void* addressInDyld
= gSyscallHelpers
;
6961 // get path of dyld itself
6962 void* addressInDyld
= (void*)&__dso_handle
;
6964 char dyldPathBuffer
[MAXPATHLEN
+1];
6965 int len
= proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld
, dyldPathBuffer
, MAXPATHLEN
);
6967 dyldPathBuffer
[len
] = '\0'; // proc_regionfilename() does not zero terminate returned string
6968 if ( strcmp(dyldPathBuffer
, gProcessInfo
->dyldPath
) != 0 )
6969 gProcessInfo
->dyldPath
= strdup(dyldPathBuffer
);
6972 // load any inserted libraries
6973 if ( sEnv
.DYLD_INSERT_LIBRARIES
!= NULL
) {
6974 for (const char* const* lib
= sEnv
.DYLD_INSERT_LIBRARIES
; *lib
!= NULL
; ++lib
)
6975 loadInsertedDylib(*lib
);
6977 // record count of inserted libraries so that a flat search will look at
6978 // inserted libraries, then main, then others.
6979 sInsertedDylibCount
= sAllImages
.size()-1;
6981 // link main executable
6982 gLinkContext
.linkingMainExecutable
= true;
6983 #if SUPPORT_ACCELERATE_TABLES
6984 if ( mainExcutableAlreadyRebased
) {
6985 // previous link() on main executable has already adjusted its internal pointers for ASLR
6986 // work around that by rebasing by inverse amount
6987 sMainExecutable
->rebase(gLinkContext
, -mainExecutableSlide
);
6990 link(sMainExecutable
, sEnv
.DYLD_BIND_AT_LAUNCH
, true, ImageLoader::RPathChain(NULL
, NULL
), -1);
6991 sMainExecutable
->setNeverUnloadRecursive();
6992 if ( sMainExecutable
->forceFlat() ) {
6993 gLinkContext
.bindFlat
= true;
6994 gLinkContext
.prebindUsage
= ImageLoader::kUseNoPrebinding
;
6997 // link any inserted libraries
6998 // do this after linking main executable so that any dylibs pulled in by inserted
6999 // dylibs (e.g. libSystem) will not be in front of dylibs the program uses
7000 if ( sInsertedDylibCount
> 0 ) {
7001 for(unsigned int i
=0; i
< sInsertedDylibCount
; ++i
) {
7002 ImageLoader
* image
= sAllImages
[i
+1];
7003 link(image
, sEnv
.DYLD_BIND_AT_LAUNCH
, true, ImageLoader::RPathChain(NULL
, NULL
), -1);
7004 image
->setNeverUnloadRecursive();
7006 if ( gLinkContext
.allowInterposing
) {
7007 // only INSERTED libraries can interpose
7008 // register interposing info after all inserted libraries are bound so chaining works
7009 for(unsigned int i
=0; i
< sInsertedDylibCount
; ++i
) {
7010 ImageLoader
* image
= sAllImages
[i
+1];
7011 image
->registerInterposing(gLinkContext
);
7016 if ( gLinkContext
.allowInterposing
) {
7017 // <rdar://problem/19315404> dyld should support interposition even without DYLD_INSERT_LIBRARIES
7018 for (long i
=sInsertedDylibCount
+1; i
< sAllImages
.size(); ++i
) {
7019 ImageLoader
* image
= sAllImages
[i
];
7020 if ( image
->inSharedCache() )
7022 image
->registerInterposing(gLinkContext
);
7025 #if SUPPORT_ACCELERATE_TABLES
7026 if ( (sAllCacheImagesProxy
!= NULL
) && ImageLoader::haveInterposingTuples() ) {
7027 // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled
7028 ImageLoader::clearInterposingTuples();
7029 // unmap all loaded dylibs (but not main executable)
7030 for (long i
=1; i
< sAllImages
.size(); ++i
) {
7031 ImageLoader
* image
= sAllImages
[i
];
7032 if ( image
== sMainExecutable
)
7034 if ( image
== sAllCacheImagesProxy
)
7036 image
->setCanUnload();
7037 ImageLoader::deleteImage(image
);
7039 // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table
7041 sImageRoots
.clear();
7042 sImageFilesNeedingTermination
.clear();
7043 sImageFilesNeedingDOFUnregistration
.clear();
7044 sAddImageCallbacks
.clear();
7045 sRemoveImageCallbacks
.clear();
7046 sAddLoadImageCallbacks
.clear();
7047 sAddBulkLoadImageCallbacks
.clear();
7048 sDisableAcceleratorTables
= true;
7049 sAllCacheImagesProxy
= NULL
;
7050 sMappedRangesStart
= NULL
;
7051 mainExcutableAlreadyRebased
= true;
7052 gLinkContext
.linkingMainExecutable
= false;
7054 goto reloadAllImages
;
7058 // apply interposing to initial set of images
7059 for(int i
=0; i
< sImageRoots
.size(); ++i
) {
7060 sImageRoots
[i
]->applyInterposing(gLinkContext
);
7062 ImageLoader::applyInterposingToDyldCache(gLinkContext
);
7064 // Bind and notify for the main executable now that interposing has been registered
7065 uint64_t bindMainExecutableStartTime
= mach_absolute_time();
7066 sMainExecutable
->recursiveBindWithAccounting(gLinkContext
, sEnv
.DYLD_BIND_AT_LAUNCH
, true);
7067 uint64_t bindMainExecutableEndTime
= mach_absolute_time();
7068 ImageLoaderMachO::fgTotalBindTime
+= bindMainExecutableEndTime
- bindMainExecutableStartTime
;
7069 gLinkContext
.notifyBatch(dyld_image_state_bound
, false);
7071 // Bind and notify for the inserted images now interposing has been registered
7072 if ( sInsertedDylibCount
> 0 ) {
7073 for(unsigned int i
=0; i
< sInsertedDylibCount
; ++i
) {
7074 ImageLoader
* image
= sAllImages
[i
+1];
7075 image
->recursiveBind(gLinkContext
, sEnv
.DYLD_BIND_AT_LAUNCH
, true, nullptr);
7079 // <rdar://problem/12186933> do weak binding only after all inserted images linked
7080 sMainExecutable
->weakBind(gLinkContext
);
7081 gLinkContext
.linkingMainExecutable
= false;
7083 sMainExecutable
->recursiveMakeDataReadOnly(gLinkContext
);
7085 CRSetCrashLogMessage("dyld: launch, running initializers");
7086 #if SUPPORT_OLD_CRT_INITIALIZATION
7087 // Old way is to run initializers via a callback from crt1.o
7088 if ( ! gRunInitializersOldWay
)
7089 initializeMainExecutable();
7091 // run all initializers
7092 initializeMainExecutable();
7095 // notify any montoring proccesses that this process is about to enter main()
7096 notifyMonitoringDyldMain();
7097 if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
)) {
7098 dyld3::kdebug_trace_dyld_duration_end(launchTraceID
, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
, 0, 0, 2);
7100 ARIADNEDBG_CODE(220, 1);
7103 if ( gLinkContext
.driverKit
) {
7104 result
= (uintptr_t)sEntryOverride
;
7106 halt("no entry point registered");
7107 *startGlue
= (uintptr_t)gLibSystemHelpers
->startGlueToCallExit
;
7112 // find entry point for main executable
7113 result
= (uintptr_t)sMainExecutable
->getEntryFromLC_MAIN();
7114 if ( result
!= 0 ) {
7115 // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
7116 if ( (gLibSystemHelpers
!= NULL
) && (gLibSystemHelpers
->version
>= 9) )
7117 *startGlue
= (uintptr_t)gLibSystemHelpers
->startGlueToCallExit
;
7119 halt("libdyld.dylib support not present for LC_MAIN");
7122 // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
7123 result
= (uintptr_t)sMainExecutable
->getEntryFromLC_UNIXTHREAD();
7128 catch(const char* message
) {
7133 dyld::log("dyld: launch failed\n");
7136 CRSetCrashLogMessage("dyld2 mode");
7137 #if !TARGET_OS_SIMULATOR
7138 if (sLogClosureFailure
) {
7139 // We failed to launch in dyld3, but dyld2 can handle it. synthesize a crash report for analytics
7140 dyld3::syntheticBacktrace("Could not generate launchClosure, falling back to dyld2", true);
7145 notifyMonitoringDyldMain();
7146 if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
)) {
7147 dyld3::kdebug_trace_dyld_duration_end(launchTraceID
, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
, 0, 0, 2);
7149 ARIADNEDBG_CODE(220, 1);
7150 result
= (uintptr_t)&fake_main
;
7151 *startGlue
= (uintptr_t)gLibSystemHelpers
->startGlueToCallExit
;