2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/errno.h>
32 #include <libc_private.h>
33 #include <TargetConditionals.h>
35 #include <mach-o/dyld_priv.h>
36 #include <mach-o/dyld_images.h>
37 #include <crt_externs.h> // FIXME: Remove once we move off of _NSGetMainExecutable()
38 #include <os/once_private.h>
45 #include "AllImages.h"
48 #include "Diagnostics.h"
49 #include "DyldSharedCache.h"
50 #include "PathOverrides.h"
53 #include "MachOLoaded.h"
54 #include "ClosureBuilder.h"
55 #include "ClosureFileSystemPhysical.h"
57 #if __has_feature(ptrauth_calls)
63 extern dyld_all_image_infos dyld_all_image_infos
;
70 static const void *stripPointer(const void *ptr
) {
71 #if __has_feature(ptrauth_calls)
72 return __builtin_ptrauth_strip(ptr
, ptrauth_key_asia
);
78 pthread_mutex_t
RecursiveAutoLock::_sMutex
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER
;
80 // forward declaration
81 static void dyld_get_image_versions_internal(const struct mach_header
* mh
, void (^callback
)(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
));
84 uint32_t _dyld_image_count(void)
86 log_apis("_dyld_image_count()\n");
88 return gAllImages
.count();
91 const mach_header
* _dyld_get_image_header(uint32_t imageIndex
)
93 log_apis("_dyld_get_image_header(%d)\n", imageIndex
);
94 return gAllImages
.imageLoadAddressByIndex(imageIndex
);
97 intptr_t _dyld_get_image_slide(const mach_header
* mh
)
99 log_apis("_dyld_get_image_slide(%p)\n", mh
);
101 const MachOLoaded
* mf
= (MachOLoaded
*)mh
;
102 if ( !mf
->hasMachOMagic() )
105 return mf
->getSlide();
108 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex
)
110 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex
);
112 const mach_header
* mh
= gAllImages
.imageLoadAddressByIndex(imageIndex
);
114 return dyld3::_dyld_get_image_slide(mh
);
118 const char* _dyld_get_image_name(uint32_t imageIndex
)
120 log_apis("_dyld_get_image_name(%d)\n", imageIndex
);
121 return gAllImages
.imagePathByIndex(imageIndex
);
125 static bool nameMatch(const char* installName
, const char* libraryName
)
127 const char* leafName
= strrchr(installName
, '/');
128 if ( leafName
== NULL
)
129 leafName
= installName
;
133 // -framework case is exact match of leaf name
134 if ( strcmp(leafName
, libraryName
) == 0 )
137 // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
138 size_t leafNameLen
= strlen(leafName
);
139 size_t libraryNameLen
= strlen(libraryName
);
140 if ( leafNameLen
< (libraryNameLen
+9) )
142 if ( strncmp(leafName
, "lib", 3) != 0 )
144 if ( strcmp(&leafName
[leafNameLen
-6], ".dylib") != 0 )
146 if ( strncmp(&leafName
[3], libraryName
, libraryNameLen
) != 0 )
148 return (leafName
[libraryNameLen
+3] == '.');
153 // BETTER, USE: dyld_get_program_sdk_version()
155 // Scans the main executable and returns the version of the specified dylib the program was built against.
157 // The library to find is the leaf name that would have been passed to linker tool
158 // (e.g. -lfoo or -framework foo would use "foo").
160 // Returns -1 if the main executable did not link against the specified library, or is malformed.
162 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName
)
164 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName
);
166 __block
int32_t result
= -1;
167 gAllImages
.mainExecutable()->forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
168 if ( nameMatch(loadPath
, libraryName
) )
169 result
= currentVersion
;
171 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result
);
177 // Searches loaded images for the requested dylib and returns its current version.
179 // The library to find is the leaf name that would have been passed to linker tool
180 // (e.g. -lfoo or -framework foo would use "foo").
182 // If the specified library is not loaded, -1 is returned.
184 int32_t NSVersionOfRunTimeLibrary(const char* libraryName
)
186 log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName
);
187 __block
int32_t result
= -1;
188 gAllImages
.forEachImage(^(const dyld3::LoadedImage
& loadedImage
, bool &stop
) {
189 const char* installName
;
190 uint32_t currentVersion
;
191 uint32_t compatVersion
;
192 if ( loadedImage
.loadedAddress()->getDylibInstallName(&installName
, &compatVersion
, ¤tVersion
) && nameMatch(installName
, libraryName
) ) {
193 result
= currentVersion
;
197 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", result
);
202 uint32_t dyld_get_program_sdk_watch_os_version()
204 log_apis("dyld_get_program_sdk_watch_os_version()\n");
206 __block
uint32_t retval
= 0;
207 __block
bool versionFound
= false;
208 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
209 if (versionFound
) return;
211 if (dyld_get_base_platform(platform
) == PLATFORM_WATCHOS
) {
213 retval
= sdk_version
;
220 uint32_t dyld_get_program_min_watch_os_version()
222 log_apis("dyld_get_program_min_watch_os_version()\n");
224 __block
uint32_t retval
= 0;
225 __block
bool versionFound
= false;
226 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
227 if (versionFound
) return;
229 if (dyld_get_base_platform(platform
) == PLATFORM_WATCHOS
) {
231 retval
= min_version
;
238 uint32_t dyld_get_program_sdk_bridge_os_version()
240 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
242 __block
uint32_t retval
= 0;
243 __block
bool versionFound
= false;
244 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
245 if (versionFound
) return;
247 if (dyld_get_base_platform(platform
) == PLATFORM_BRIDGEOS
) {
249 retval
= sdk_version
;
256 uint32_t dyld_get_program_min_bridge_os_version()
258 log_apis("dyld_get_program_min_bridge_os_version()\n");
260 __block
uint32_t retval
= 0;
261 __block
bool versionFound
= false;
262 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
263 if (versionFound
) return;
265 if (dyld_get_base_platform(platform
) == PLATFORM_BRIDGEOS
) {
267 retval
= min_version
;
275 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
276 // specified binary was built against.
278 // First looks for LC_VERSION_MIN_* in binary and if sdk field is
279 // not zero, return that value.
280 // Otherwise, looks for the libSystem.B.dylib the binary linked
281 // against and uses a table to convert that to an sdk version.
283 uint32_t dyld_get_sdk_version(const mach_header
* mh
)
285 log_apis("dyld_get_sdk_version(%p)\n", mh
);
286 __block
bool versionFound
= false;
287 __block
uint32_t retval
= 0;
288 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
289 if (versionFound
) return;
291 if (platform
== ::dyld_get_active_platform()) {
293 switch (dyld3::dyld_get_base_platform(platform
)) {
294 case PLATFORM_BRIDGEOS
: retval
= sdk_version
+ 0x00090000; return;
295 case PLATFORM_WATCHOS
: retval
= sdk_version
+ 0x00070000; return;
296 default: retval
= sdk_version
; return;
304 uint32_t dyld_get_program_sdk_version()
306 log_apis("dyld_get_program_sdk_version()\n");
307 return dyld3::dyld_get_sdk_version(gAllImages
.mainExecutable());
310 uint32_t dyld_get_min_os_version(const mach_header
* mh
)
312 log_apis("dyld_get_min_os_version(%p)\n", mh
);
313 __block
bool versionFound
= false;
314 __block
uint32_t retval
= 0;
315 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
316 if (versionFound
) return;
318 if (platform
== ::dyld_get_active_platform()) {
320 switch (dyld3::dyld_get_base_platform(platform
)) {
321 case PLATFORM_BRIDGEOS
: retval
= min_version
+ 0x00090000; return;
322 case PLATFORM_WATCHOS
: retval
= min_version
+ 0x00070000; return;
323 default: retval
= min_version
; return;
331 dyld_platform_t
dyld_get_active_platform(void) {
332 return gAllImages
.platform();
335 dyld_platform_t
dyld_get_base_platform(dyld_platform_t platform
) {
337 case PLATFORM_IOSMAC
: return PLATFORM_IOS
;
338 case PLATFORM_IOSSIMULATOR
: return PLATFORM_IOS
;
339 case PLATFORM_WATCHOSSIMULATOR
: return PLATFORM_WATCHOS
;
340 case PLATFORM_TVOSSIMULATOR
: return PLATFORM_TVOS
;
341 default: return platform
;
345 bool dyld_is_simulator_platform(dyld_platform_t platform
) {
347 case PLATFORM_IOSSIMULATOR
:
348 case PLATFORM_WATCHOSSIMULATOR
:
349 case PLATFORM_TVOSSIMULATOR
:
356 bool dyld_sdk_at_least(const struct mach_header
* mh
, dyld_build_version_t version
) {
357 __block
bool retval
= false;
358 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
359 if (dyld3::dyld_get_base_platform(platform
) == version
.platform
&& sdk_version
>= version
.version
) {
366 bool dyld_minos_at_least(const struct mach_header
* mh
, dyld_build_version_t version
) {
367 __block
bool retval
= false;
368 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
369 if (dyld3::dyld_get_base_platform(platform
) == version
.platform
&& min_version
>= version
.version
) {
376 bool dyld_program_sdk_at_least(dyld_build_version_t version
) {
377 return dyld3::dyld_sdk_at_least(gAllImages
.mainExecutable(), version
);
380 bool dyld_program_minos_at_least(dyld_build_version_t version
) {
381 return dyld3::dyld_minos_at_least(gAllImages
.mainExecutable(), version
);
384 #if TARGET_OS_OSX || TARGET_OS_IOS
386 uint32_t linkedDylibVersion(const mach_header
* mh
, const char *installname
) {
387 __block
uint32_t retval
= 0;
388 ((MachOLoaded
*)mh
)->forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
389 if (strcmp(loadPath
, installname
) == 0) {
390 retval
= currentVersion
;
399 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
401 static uint32_t deriveVersionFromDylibs(const struct mach_header
* mh
) {
402 // This is a binary without a version load command, we need to infer things
403 struct DylibToOSMapping
{
404 uint32_t dylibVersion
;
407 uint32_t linkedVersion
= 0;
409 linkedVersion
= linkedDylibVersion(mh
, "/usr/lib/libSystem.B.dylib");
410 static const DylibToOSMapping versionMapping
[] = {
411 { PACKED_VERSION(88,1,3), 0x000A0400 },
412 { PACKED_VERSION(111,0,0), 0x000A0500 },
413 { PACKED_VERSION(123,0,0), 0x000A0600 },
414 { PACKED_VERSION(159,0,0), 0x000A0700 },
415 { PACKED_VERSION(169,3,0), 0x000A0800 },
416 { PACKED_VERSION(1197,0,0), 0x000A0900 },
417 { PACKED_VERSION(0,0,0), 0x000A0900 }
418 // We don't need to expand this table because all recent
419 // binaries have LC_VERSION_MIN_ load command.
422 linkedVersion
= linkedDylibVersion(mh
, "/System/Library/Frameworks/Foundation.framework/Foundation");
423 static const DylibToOSMapping versionMapping
[] = {
424 { PACKED_VERSION(678,24,0), 0x00020000 },
425 { PACKED_VERSION(678,26,0), 0x00020100 },
426 { PACKED_VERSION(678,29,0), 0x00020200 },
427 { PACKED_VERSION(678,47,0), 0x00030000 },
428 { PACKED_VERSION(678,51,0), 0x00030100 },
429 { PACKED_VERSION(678,60,0), 0x00030200 },
430 { PACKED_VERSION(751,32,0), 0x00040000 },
431 { PACKED_VERSION(751,37,0), 0x00040100 },
432 { PACKED_VERSION(751,49,0), 0x00040200 },
433 { PACKED_VERSION(751,58,0), 0x00040300 },
434 { PACKED_VERSION(881,0,0), 0x00050000 },
435 { PACKED_VERSION(890,1,0), 0x00050100 },
436 { PACKED_VERSION(992,0,0), 0x00060000 },
437 { PACKED_VERSION(993,0,0), 0x00060100 },
438 { PACKED_VERSION(1038,14,0),0x00070000 },
439 { PACKED_VERSION(0,0,0), 0x00070000 }
440 // We don't need to expand this table because all recent
441 // binaries have LC_VERSION_MIN_ load command.
444 static const DylibToOSMapping versionMapping
[] = {};
446 if ( linkedVersion
!= 0 ) {
447 uint32_t lastOsVersion
= 0;
448 for (const DylibToOSMapping
* p
=versionMapping
; ; ++p
) {
449 if ( p
->dylibVersion
== 0 ) {
452 if ( linkedVersion
< p
->dylibVersion
) {
453 return lastOsVersion
;
455 lastOsVersion
= p
->osVersion
;
461 // assumes mh has already been validated
462 static void dyld_get_image_versions_internal(const struct mach_header
* mh
, void (^callback
)(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
))
464 const MachOFile
* mf
= (MachOFile
*)mh
;
465 __block
bool lcFound
= false;
466 mf
->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
468 // If SDK field is empty then derive the value from library linkages
470 sdk
= deriveVersionFromDylibs(mh
);
472 if (platform
== dyld3::Platform::iOSMac
) {
475 callback((const dyld_platform_t
)platform
, sdk
, minOS
);
478 // No load command was found, so again, fallback to deriving it from library linkages
481 #if __x86_64__ || __x86__
482 dyld_platform_t platform
= PLATFORM_IOSSIMULATOR
;
484 dyld_platform_t platform
= PLATFORM_IOS
;
487 dyld_platform_t platform
= PLATFORM_MACOS
;
489 dyld_platform_t platform
= 0;
491 uint32_t derivedVersion
= deriveVersionFromDylibs(mh
);
492 if ( platform
!= 0 && derivedVersion
!= 0 ) {
493 callback(platform
, derivedVersion
, 0);
498 void dyld_get_image_versions(const struct mach_header
* mh
, void (^callback
)(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
))
501 const MachOFile
* mf
= (MachOFile
*)mh
;
502 static dyld_platform_t mainExecutablePlatform
= 0;
503 static uint32_t mainExecutableSDKVersion
= 0;
504 static uint32_t mainExecutableMinOSVersion
= 0;
506 // FIXME: Once dyld2 is gone gAllImages.mainExecutable() will be valid in all cases
507 // and we can stop calling _NSGetMachExecuteHeader()
508 if (mh
== (const struct mach_header
*)_NSGetMachExecuteHeader()) {
509 // Cache the main executable and short circuit parsing the
510 if (mainExecutablePlatform
== 0) {
511 dyld_get_image_versions_internal(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
513 //FIXME: Reenable this once Libc supports dynamic platforms.
514 if (platform
== PLATFORM_MACOS
&& dyld_get_active_platform() == PLATFORM_IOSMAC
) {
515 //FIXME: This version should be generated at link time
516 mainExecutablePlatform
= PLATFORM_IOSMAC
;
517 mainExecutableSDKVersion
= 0x000D0000;
518 mainExecutableMinOSVersion
= 0x000D0000;
520 mainExecutablePlatform
= platform
;
521 mainExecutableSDKVersion
= sdk_version
;
522 mainExecutableMinOSVersion
= min_version
;
525 mainExecutablePlatform
= platform
;
526 mainExecutableSDKVersion
= sdk_version
;
527 mainExecutableMinOSVersion
= min_version
;
529 //FIXME: Assert if more than one command?
532 return callback(mainExecutablePlatform
, mainExecutableSDKVersion
, mainExecutableMinOSVersion
);
534 #if TARGET_OS_EMBEDDED
535 // If we are on embedded AND in the shared cache then the versions should be the same as libdyld
536 if (mf
->inDyldCache()) {
537 static dyld_platform_t libDyldPlatform
= 0;
538 static uint32_t libDyldSDKVersion
= 0;
539 static uint32_t libDyldMinOSVersion
= 0;
540 if (libDyldPlatform
== 0) {
541 dyld_get_image_versions_internal(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
542 libDyldPlatform
= platform
;
543 libDyldSDKVersion
= sdk_version
;
544 libDyldMinOSVersion
= min_version
;
545 //FIXME: Assert if more than one command?
548 return callback(libDyldPlatform
, libDyldSDKVersion
, libDyldMinOSVersion
);
551 if ( mf
->isMachO(diag
, mh
->sizeofcmds
+ sizeof(mach_header_64
)) )
552 dyld_get_image_versions_internal(mh
, callback
);
555 uint32_t dyld_get_program_min_os_version()
557 log_apis("dyld_get_program_min_os_version()\n");
558 return dyld3::dyld_get_min_os_version(gAllImages
.mainExecutable());
561 bool _dyld_get_image_uuid(const mach_header
* mh
, uuid_t uuid
)
563 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh
, uuid
);
565 const MachOFile
* mf
= (MachOFile
*)mh
;
566 if ( !mf
->hasMachOMagic() )
569 return mf
->getUuid(uuid
);
573 // _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
574 // should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
575 // and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
576 // to the size required.
578 int _NSGetExecutablePath(char* buf
, uint32_t* bufsize
)
580 log_apis("_NSGetExecutablePath(%p, %p)\n", buf
, bufsize
);
582 const closure::Image
* mainImage
= gAllImages
.mainExecutableImage();
583 const char* path
= gAllImages
.imagePath(mainImage
);
584 size_t pathSize
= strlen(path
) + 1;
585 if ( *bufsize
>= pathSize
) {
589 *bufsize
= (uint32_t)pathSize
;
593 void _dyld_register_func_for_add_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
595 log_apis("_dyld_register_func_for_add_image(%p)\n", func
);
597 gAllImages
.addLoadNotifier(func
);
600 void _dyld_register_func_for_remove_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
602 log_apis("_dyld_register_func_for_remove_image(%p)\n", func
);
604 gAllImages
.addUnloadNotifier(func
);
607 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped
,
608 _dyld_objc_notify_init init
,
609 _dyld_objc_notify_unmapped unmapped
)
611 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped
, init
, unmapped
);
613 gAllImages
.setObjCNotifiers(mapped
, init
, unmapped
);
617 const mach_header
* dyld_image_header_containing_address(const void* addr
)
619 log_apis("dyld_image_header_containing_address(%p)\n", addr
);
621 addr
= stripPointer(addr
);
623 const MachOLoaded
* ml
;
624 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, nullptr) )
631 const char* dyld_image_path_containing_address(const void* addr
)
633 log_apis("dyld_image_path_containing_address(%p)\n", addr
);
635 addr
= stripPointer(addr
);
636 const char* result
= gAllImages
.pathForImageMappedAt(addr
);
638 log_apis(" dyld_image_path_containing_address() => %s\n", result
);
644 bool _dyld_is_memory_immutable(const void* addr
, size_t length
)
646 return gAllImages
.immutableMemory(addr
, length
);
649 int dladdr(const void* addr
, Dl_info
* info
)
651 log_apis("dladdr(%p, %p)\n", addr
, info
);
653 // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
657 addr
= stripPointer(addr
);
659 __block
int result
= 0;
660 const MachOLoaded
* ml
= nullptr;
661 const char* path
= nullptr;
662 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, &path
) ) {
663 info
->dli_fname
= path
;
664 info
->dli_fbase
= (void*)ml
;
667 if ( addr
== info
->dli_fbase
) {
668 // special case lookup of header
669 info
->dli_sname
= "__dso_handle";
670 info
->dli_saddr
= info
->dli_fbase
;
672 else if ( ml
->findClosestSymbol((long)addr
, &(info
->dli_sname
), &symbolAddr
) ) {
673 info
->dli_saddr
= (void*)(long)symbolAddr
;
674 // never return the mach_header symbol
675 if ( info
->dli_saddr
== info
->dli_fbase
) {
676 info
->dli_sname
= nullptr;
677 info
->dli_saddr
= nullptr;
679 // strip off leading underscore
680 else if ( (info
->dli_sname
!= nullptr) && (info
->dli_sname
[0] == '_') ) {
681 info
->dli_sname
= info
->dli_sname
+ 1;
685 info
->dli_sname
= nullptr;
686 info
->dli_saddr
= nullptr;
692 log_apis(" dladdr() => 0\n");
694 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info
->dli_fname
, info
->dli_fbase
, info
->dli_sname
, info
->dli_saddr
);
698 #if !TARGET_OS_DRIVERKIT
700 struct PerThreadErrorMessage
702 size_t sizeAllocated
;
707 static void dlerror_perThreadKey_once(void* ctx
)
709 pthread_key_t
* dlerrorPThreadKeyPtr
= (pthread_key_t
*)ctx
;
710 pthread_key_create(dlerrorPThreadKeyPtr
, &free
);
713 static pthread_key_t
dlerror_perThreadKey()
715 static os_once_t onceToken
;
716 static pthread_key_t dlerrorPThreadKey
;
717 os_once(&onceToken
, &dlerrorPThreadKey
, dlerror_perThreadKey_once
);
718 return dlerrorPThreadKey
;
721 static void clearErrorString()
723 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
724 if ( errorBuffer
!= nullptr )
725 errorBuffer
->valid
= false;
728 __attribute__((format(printf
, 1, 2)))
729 static void setErrorString(const char* format
, ...)
731 _SIMPLE_STRING buf
= _simple_salloc();
732 if ( buf
!= nullptr ) {
734 va_start(list
, format
);
735 _simple_vsprintf(buf
, format
, list
);
737 size_t strLen
= strlen(_simple_string(buf
)) + 1;
738 size_t sizeNeeded
= sizeof(PerThreadErrorMessage
) + strLen
;
739 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
740 if ( errorBuffer
!= nullptr ) {
741 if ( errorBuffer
->sizeAllocated
< sizeNeeded
) {
743 errorBuffer
= nullptr;
746 if ( errorBuffer
== nullptr ) {
747 size_t allocSize
= std::max(sizeNeeded
, (size_t)256);
748 PerThreadErrorMessage
* p
= (PerThreadErrorMessage
*)malloc(allocSize
);
749 p
->sizeAllocated
= allocSize
;
751 pthread_setspecific(dlerror_perThreadKey(), p
);
754 strcpy(errorBuffer
->message
, _simple_string(buf
));
755 errorBuffer
->valid
= true;
762 log_apis("dlerror()\n");
764 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
765 if ( errorBuffer
!= nullptr ) {
766 if ( errorBuffer
->valid
) {
767 // you can only call dlerror() once, then the message is cleared
768 errorBuffer
->valid
= false;
769 return errorBuffer
->message
;
776 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
778 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
782 static void* makeDlHandle(const mach_header
* mh
, bool dontContinue
)
784 uintptr_t flags
= (dontContinue
? 1 : 0);
785 return (void*)((((uintptr_t)mh
) >> 5) | flags
);
789 void parseDlHandle(void* h
, const MachOLoaded
** mh
, bool* dontContinue
)
791 *dontContinue
= (((uintptr_t)h
) & 1);
792 *mh
= (const MachOLoaded
*)((((uintptr_t)h
) & (-2)) << 5);
795 int dlclose(void* handle
)
797 DYLD_LOAD_LOCK_THIS_BLOCK
798 log_apis("dlclose(%p)\n", handle
);
800 // silently accept magic handles for main executable
801 if ( handle
== RTLD_MAIN_ONLY
)
803 if ( handle
== RTLD_DEFAULT
)
806 const MachOLoaded
* mh
;
808 parseDlHandle(handle
, &mh
, &dontContinue
);
810 __block
bool unloadable
= false;
811 __block
bool validHandle
= false;
812 gAllImages
.infoForImageMappedAt(mh
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
814 if ( !foundImage
.image()->neverUnload() )
818 gAllImages
.decRefCount(mh
); // removes image if reference count went to zero
826 setErrorString("invalid handle passed to dlclose()");
832 void* dlopen_internal(const char* path
, int mode
, void* callerAddress
)
834 DYLD_LOAD_LOCK_THIS_BLOCK
835 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path
==NULL
) ? "NULL" : path
), mode
);
839 // passing NULL for path means return magic object
840 if ( path
== NULL
) {
841 // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
842 if ( (mode
& RTLD_FIRST
) != 0 )
843 return RTLD_MAIN_ONLY
;
848 const char* leafName
= strrchr(path
, '/');
849 if ( leafName
!= nullptr )
854 #if __IPHONE_OS_VERSION_MIN_REQUIRED
855 // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
856 char canonicalPath
[PATH_MAX
];
857 if ( leafName
!= path
) {
858 // make path canonical if it contains a // or ./
859 if ( (strstr(path
, "//") != NULL
) || (strstr(path
, "./") != NULL
) ) {
860 const char* lastSlash
= strrchr(path
, '/');
861 char dirPath
[PATH_MAX
];
862 if ( strlcpy(dirPath
, path
, sizeof(dirPath
)) < sizeof(dirPath
) ) {
863 dirPath
[lastSlash
-path
] = '\0';
864 if ( realpath(dirPath
, canonicalPath
) ) {
865 strlcat(canonicalPath
, "/", sizeof(canonicalPath
));
866 if ( strlcat(canonicalPath
, lastSlash
+1, sizeof(canonicalPath
)) < sizeof(canonicalPath
) ) {
867 // if all fit in buffer, use new canonical path
868 path
= canonicalPath
;
876 // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
877 const bool firstOnly
= (mode
& RTLD_FIRST
);
879 // RTLD_LOCAL means when flat searches of all images (e.g. RTLD_DEFAULT) is done, this image should be skipped. But dlsym(handle, xx) can find symbols
880 const bool rtldLocal
= (mode
& RTLD_LOCAL
);
882 // RTLD_NODELETE means don't unmap image during dlclose(). Leave the memory mapped, but orphan (leak) it.
883 // Note: this is a weird state and it slightly different semantics that other OSs
884 const bool rtldNoDelete
= (mode
& RTLD_NODELETE
);
886 // RTLD_NOLOAD means do nothing if image not already loaded
887 const bool rtldNoLoad
= (mode
& RTLD_NOLOAD
);
889 // RTLD_NOW means force lazy symbols bound and fail dlopen() if some cannot be bound
890 const bool rtldNow
= (mode
& RTLD_NOW
);
892 // try to load image from specified path
894 const mach_header
* topLoadAddress
= gAllImages
.dlopen(diag
, path
, rtldNoLoad
, rtldLocal
, rtldNoDelete
, rtldNow
, false, callerAddress
);
895 if ( diag
.hasError() ) {
896 setErrorString("dlopen(%s, 0x%04X): %s", path
, mode
, diag
.errorMessage());
897 log_apis(" dlopen: closure creation error: %s\n", diag
.errorMessage());
900 if ( topLoadAddress
== nullptr ) {
901 log_apis(" dlopen(%s) => NULL\n", leafName
);
904 void* result
= makeDlHandle(topLoadAddress
, firstOnly
);
905 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
910 bool dlopen_preflight_internal(const char* path
)
912 DYLD_LOAD_LOCK_THIS_BLOCK
913 log_apis("dlopen_preflight(%s)\n", path
);
915 // check if path is in dyld shared cache
916 if ( gAllImages
.dyldCacheHasPath(path
) )
919 // check if file is loadable
921 closure::FileSystemPhysical fileSystem
;
922 char realerPath
[MAXPATHLEN
];
923 closure::LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, path
, gAllImages
.archs(), (Platform
)gAllImages
.platform(), realerPath
);
924 if ( loadedFileInfo
.fileContent
!= nullptr ) {
925 fileSystem
.unloadFile(loadedFileInfo
);
929 // FIXME: may be symlink to something in dyld cache
934 static void* dlsym_search(const char* symName
, const LoadedImage
& start
, bool searchStartImage
, MachOLoaded::DependentToMachOLoaded reExportHelper
,
935 bool* resultPointsToInstructions
)
937 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
938 return gAllImages
.findDependent(mh
, depIndex
);
940 //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
942 // walk all dependents of 'start' in order looking for symbol
943 __block
void* result
= nullptr;
944 gAllImages
.visitDependentsTopDown(start
, ^(const LoadedImage
& aLoadedImage
, bool& stop
) {
945 //fprintf(stderr, " search: %s\n", aLoadedImage.image()->path());
946 if ( !searchStartImage
&& aLoadedImage
.image() == start
.image() )
948 if ( aLoadedImage
.loadedAddress()->hasExportedSymbol(symName
, finder
, &result
, resultPointsToInstructions
) ) {
949 result
= gAllImages
.interposeValue(result
);
958 void* dlsym_internal(void* handle
, const char* symbolName
, void* callerAddress
)
960 log_apis("dlsym(%p, \"%s\")\n", handle
, symbolName
);
964 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
965 return gAllImages
.findDependent(mh
, depIndex
);
968 // dlsym() assumes symbolName passed in is same as in C source code
969 // dyld assumes all symbol names have an underscore prefix
970 BLOCK_ACCCESSIBLE_ARRAY(char, underscoredName
, strlen(symbolName
)+2);
971 underscoredName
[0] = '_';
972 strcpy(&underscoredName
[1], symbolName
);
974 __block
void* result
= nullptr;
975 __block
bool resultPointsToInstructions
= false;
976 if ( handle
== RTLD_DEFAULT
) {
977 // magic "search all in load order" handle
978 gAllImages
.forEachImage(^(const LoadedImage
& loadedImage
, bool& stop
) {
979 if ( loadedImage
.hideFromFlatSearch() )
981 if ( loadedImage
.loadedAddress()->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
) ) {
985 if ( result
!= nullptr ) {
986 result
= gAllImages
.interposeValue(result
);
987 #if __has_feature(ptrauth_calls)
988 if (resultPointsToInstructions
)
989 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
991 log_apis(" dlsym() => %p\n", result
);
994 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName
);
995 log_apis(" dlsym() => NULL\n");
998 else if ( handle
== RTLD_MAIN_ONLY
) {
999 // magic "search only main executable" handle
1000 if ( gAllImages
.mainExecutable()->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
) ) {
1001 result
= gAllImages
.interposeValue(result
);
1002 log_apis(" dlsym() => %p\n", result
);
1003 #if __has_feature(ptrauth_calls)
1004 if (resultPointsToInstructions
)
1005 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
1009 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName
);
1010 log_apis(" dlsym() => NULL\n");
1013 // rest of cases search in dependency order
1014 if ( handle
== RTLD_NEXT
) {
1015 // magic "search what I would see" handle
1016 __block
bool foundCaller
= false;
1017 gAllImages
.infoForImageMappedAt(callerAddress
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
1019 result
= dlsym_search(underscoredName
, foundImage
, false, finder
, &resultPointsToInstructions
);
1021 if ( !foundCaller
) {
1022 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
1026 else if ( handle
== RTLD_SELF
) {
1027 // magic "search me, then what I would see" handle
1028 __block
bool foundCaller
= false;
1029 gAllImages
.infoForImageMappedAt(callerAddress
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
1031 result
= dlsym_search(underscoredName
, foundImage
, true, finder
, &resultPointsToInstructions
);
1033 if ( !foundCaller
) {
1034 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
1039 // handle value was something returned by dlopen()
1040 const MachOLoaded
* mh
;
1042 parseDlHandle(handle
, &mh
, &dontContinue
);
1044 __block
bool foundCaller
= false;
1045 gAllImages
.infoForImageWithLoadAddress(mh
, ^(const LoadedImage
& foundImage
) {
1047 if ( dontContinue
) {
1048 // RTLD_FIRST only searches one place
1049 // we go through infoForImageWithLoadAddress() to validate the handle
1050 if (mh
->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
))
1051 result
= gAllImages
.interposeValue(result
);
1054 result
= dlsym_search(underscoredName
, foundImage
, true, finder
, &resultPointsToInstructions
);
1057 if ( !foundCaller
) {
1058 setErrorString("dlsym(%p, %s): invalid handle", handle
, symbolName
);
1059 log_apis(" dlsym() => NULL\n");
1064 if ( result
!= nullptr ) {
1065 #if __has_feature(ptrauth_calls)
1066 if (resultPointsToInstructions
)
1067 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
1069 log_apis(" dlsym() => %p\n", result
);
1073 setErrorString("dlsym(%p, %s): symbol not found", handle
, symbolName
);
1074 log_apis(" dlsym() => NULL\n");
1077 #endif // !TARGET_OS_DRIVERKIT
1080 const struct dyld_all_image_infos
* _dyld_get_all_image_infos()
1082 return gAllImages
.oldAllImageInfo();
1085 bool dyld_shared_cache_some_image_overridden()
1087 log_apis("dyld_shared_cache_some_image_overridden()\n");
1089 return gAllImages
.hasCacheOverrides();
1092 bool _dyld_get_shared_cache_uuid(uuid_t uuid
)
1094 log_apis("_dyld_get_shared_cache_uuid()\n");
1096 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1097 if ( sharedCache
== nullptr )
1100 if ( gAllImages
.oldAllImageInfo() != nullptr ) {
1101 memcpy(uuid
, gAllImages
.oldAllImageInfo()->sharedCacheUUID
, sizeof(uuid_t
));
1107 const void* _dyld_get_shared_cache_range(size_t* mappedSize
)
1109 log_apis("_dyld_get_shared_cache_range()\n");
1111 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1112 if ( sharedCache
!= nullptr ) {
1113 *mappedSize
= (size_t)sharedCache
->mappedSize();
1120 bool _dyld_shared_cache_optimized()
1122 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1123 if ( sharedCache
!= nullptr ) {
1124 return (sharedCache
->header
.cacheType
== kDyldSharedCacheTypeProduction
);
1129 bool _dyld_shared_cache_is_locally_built()
1131 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1132 if ( sharedCache
!= nullptr ) {
1133 return (sharedCache
->header
.locallyBuiltCache
== 1);
1138 void _dyld_images_for_addresses(unsigned count
, const void* addresses
[], dyld_image_uuid_offset infos
[])
1140 log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count
, addresses
, infos
);
1142 // in stack crawls, common for contiguous fames to be in same image, so cache
1143 // last lookup and check if next addresss in in there before doing full search
1144 const MachOLoaded
* ml
= nullptr;
1145 uint64_t textSize
= 0;
1146 const void* end
= (void*)ml
;
1147 for (unsigned i
=0; i
< count
; ++i
) {
1148 const void* addr
= stripPointer(addresses
[i
]);
1149 bzero(&infos
[i
], sizeof(dyld_image_uuid_offset
));
1150 if ( (ml
== nullptr) || (addr
< (void*)ml
) || (addr
> end
) ) {
1151 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, &textSize
, nullptr) ) {
1152 end
= (void*)((uint8_t*)ml
+ textSize
);
1159 if ( ml
!= nullptr ) {
1160 infos
[i
].image
= ml
;
1161 infos
[i
].offsetInImage
= (uintptr_t)addr
- (uintptr_t)ml
;
1162 ml
->getUuid(infos
[i
].uuid
);
1167 void _dyld_register_for_image_loads(void (*func
)(const mach_header
* mh
, const char* path
, bool unloadable
))
1169 gAllImages
.addLoadNotifier(func
);
1172 void _dyld_register_for_bulk_image_loads(void (*func
)(unsigned imageCount
, const struct mach_header
* mhs
[], const char* paths
[]))
1174 gAllImages
.addBulkLoadNotifier(func
);
1177 bool _dyld_find_unwind_sections(void* addr
, dyld_unwind_sections
* info
)
1179 log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr
, info
);
1180 addr
= (void*)stripPointer(addr
);
1182 const MachOLoaded
* ml
= nullptr;
1183 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, nullptr) ) {
1185 info
->dwarf_section
= nullptr;
1186 info
->dwarf_section_length
= 0;
1187 info
->compact_unwind_section
= nullptr;
1188 info
->compact_unwind_section_length
= 0;
1191 if ( const void* content
= ml
->findSectionContent("__TEXT", "__eh_frame", size
) ) {
1192 info
->dwarf_section
= content
;
1193 info
->dwarf_section_length
= (uintptr_t)size
;
1195 if ( const void* content
= ml
->findSectionContent("__TEXT", "__unwind_info", size
) ) {
1196 info
->compact_unwind_section
= content
;
1197 info
->compact_unwind_section_length
= (uintptr_t)size
;
1206 bool dyld_process_is_restricted()
1208 log_apis("dyld_process_is_restricted()\n");
1209 return gAllImages
.isRestricted();
1213 const char* dyld_shared_cache_file_path()
1215 log_apis("dyld_shared_cache_file_path()\n");
1217 return gAllImages
.dyldCachePath();
1221 bool dyld_has_inserted_or_interposing_libraries()
1223 log_apis("dyld_has_inserted_or_interposing_libraries()\n");
1225 return gAllImages
.hasInsertedOrInterposingLibraries();
1229 void dyld_dynamic_interpose(const mach_header
* mh
, const dyld_interpose_tuple array
[], size_t count
)
1231 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh
, array
, count
);
1236 static void* mapStartOfCache(const char* path
, size_t length
)
1238 struct stat statbuf
;
1239 if ( ::stat(path
, &statbuf
) == -1 )
1242 if ( statbuf
.st_size
< length
)
1245 int cache_fd
= ::open(path
, O_RDONLY
);
1249 void* result
= ::mmap(NULL
, length
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
1252 if ( result
== MAP_FAILED
)
1258 static const DyldSharedCache
* findCacheInDirAndMap(const uuid_t cacheUuid
, const char* dirPath
, size_t& sizeMapped
)
1260 DIR* dirp
= ::opendir(dirPath
);
1261 if ( dirp
!= NULL
) {
1263 dirent
* entp
= NULL
;
1264 char cachePath
[PATH_MAX
];
1265 while ( ::readdir_r(dirp
, &entry
, &entp
) == 0 ) {
1268 if ( entp
->d_type
!= DT_REG
)
1270 if ( strlcpy(cachePath
, dirPath
, PATH_MAX
) >= PATH_MAX
)
1272 if ( strlcat(cachePath
, "/", PATH_MAX
) >= PATH_MAX
)
1274 if ( strlcat(cachePath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1276 if ( const DyldSharedCache
* cache
= (DyldSharedCache
*)mapStartOfCache(cachePath
, 0x00100000) ) {
1278 cache
->getUUID(foundUuid
);
1279 if ( ::memcmp(foundUuid
, cacheUuid
, 16) != 0 ) {
1280 // wrong uuid, unmap and keep looking
1281 ::munmap((void*)cache
, 0x00100000);
1286 sizeMapped
= 0x00100000;
1296 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid
, const char* extraSearchDirs
[], void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1298 log_apis("dyld_shared_cache_find_iterate_text()\n");
1300 // see if requested cache is the active one in this process
1301 size_t sizeMapped
= 0;
1302 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1303 if ( sharedCache
!= nullptr ) {
1305 sharedCache
->getUUID(runningUuid
);
1306 if ( ::memcmp(runningUuid
, cacheUuid
, 16) != 0 )
1307 sharedCache
= nullptr;
1309 if ( sharedCache
== nullptr ) {
1310 // if not, look in default location for cache files
1311 #if __IPHONE_OS_VERSION_MIN_REQUIRED
1312 const char* defaultSearchDir
= IPHONE_DYLD_SHARED_CACHE_DIR
;
1314 const char* defaultSearchDir
= MACOSX_DYLD_SHARED_CACHE_DIR
;
1316 sharedCache
= findCacheInDirAndMap(cacheUuid
, defaultSearchDir
, sizeMapped
);
1317 // if not there, look in extra search locations
1318 if ( sharedCache
== nullptr ) {
1319 for (const char** p
= extraSearchDirs
; *p
!= nullptr; ++p
) {
1320 sharedCache
= findCacheInDirAndMap(cacheUuid
, *p
, sizeMapped
);
1321 if ( sharedCache
!= nullptr )
1326 if ( sharedCache
== nullptr )
1329 // get base address of cache
1330 __block
uint64_t cacheUnslidBaseAddress
= 0;
1331 sharedCache
->forEachRegion(^(const void *content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
1332 if ( cacheUnslidBaseAddress
== 0 )
1333 cacheUnslidBaseAddress
= vmAddr
;
1336 // iterate all images
1337 sharedCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
, bool& stop
) {
1338 dyld_shared_cache_dylib_text_info dylibTextInfo
;
1339 dylibTextInfo
.version
= 2;
1340 dylibTextInfo
.loadAddressUnslid
= loadAddressUnslid
;
1341 dylibTextInfo
.textSegmentSize
= textSegmentSize
;
1342 dylibTextInfo
.path
= installName
;
1343 ::memcpy(dylibTextInfo
.dylibUuid
, dylibUUID
, 16);
1344 dylibTextInfo
.textSegmentOffset
= loadAddressUnslid
- cacheUnslidBaseAddress
;
1345 callback(&dylibTextInfo
);
1348 if ( sizeMapped
!= 0 )
1349 ::munmap((void*)sharedCache
, sizeMapped
);
1354 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid
, void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1356 log_apis("dyld_shared_cache_iterate_text()\n");
1358 const char* extraSearchDirs
[] = { NULL
};
1359 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid
, extraSearchDirs
, callback
);
1362 bool dyld_need_closure(const char* execPath
, const char* tempDir
)
1364 log_apis("dyld_need_closure()\n");
1366 // We don't need to build a closure if the shared cache has it already
1367 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1368 if ( sharedCache
!= nullptr ) {
1369 if ( sharedCache
->findClosure(execPath
) != nullptr )
1374 char closurePath
[PATH_MAX
];
1375 if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath
, closurePath
, tempDir
, false) ) {
1376 struct stat statbuf
;
1377 return (::stat(closurePath
, &statbuf
) != 0);
1380 // Not containerized so no point in building a closure.
1384 void _dyld_missing_symbol_abort()
1386 // We don't know the name of the lazy symbol that is missing.
1387 // dyld3 binds all such missing symbols to this one handler.
1388 // We need the crash log to contain the backtrace so someone can
1389 // figure out the symbol.
1390 abort_report_np("missing lazy symbol called");
1393 const char* _dyld_get_objc_selector(const char* selName
)
1395 log_apis("dyld_get_objc_selector()\n");
1396 return gAllImages
.getObjCSelector(selName
);
1399 void _dyld_for_each_objc_class(const char* className
,
1400 void (^callback
)(void* classPtr
, bool isLoaded
, bool* stop
)) {
1401 log_apis("_dyld_for_each_objc_class()\n");
1402 gAllImages
.forEachObjCClass(className
, callback
);
1405 void _dyld_for_each_objc_protocol(const char* protocolName
,
1406 void (^callback
)(void* protocolPtr
, bool isLoaded
, bool* stop
)) {
1407 log_apis("_dyld_for_each_objc_protocol()\n");
1408 gAllImages
.forEachObjCProtocol(protocolName
, callback
);
1411 #if !TARGET_OS_DRIVERKIT
1414 void* implementation
;
1417 static const struct dyld_func dyld_funcs
[] = {
1418 {"__dyld_dlsym", (void*)dlsym
}, // needs to go through generic function to get caller address
1419 {"__dyld_dlopen", (void*)dlopen
},// needs to go through generic function to get caller address
1420 {"__dyld_dladdr", (void*)dyld3::dladdr
},
1421 {"__dyld_image_count", (void*)dyld3::_dyld_image_count
},
1422 {"__dyld_get_image_name", (void*)dyld3::_dyld_get_image_name
},
1423 {"__dyld_get_image_header", (void*)dyld3::_dyld_get_image_header
},
1424 {"__dyld_get_image_vmaddr_slide", (void*)dyld3::_dyld_get_image_vmaddr_slide
},
1428 int compatFuncLookup(const char* name
, void** address
)
1430 #if !TARGET_OS_DRIVERKIT
1431 for (const dyld_func
* p
= dyld_funcs
; p
->name
!= NULL
; ++p
) {
1432 if ( strcmp(p
->name
, name
) == 0 ) {
1433 *address
= p
->implementation
;
1444 } // namespace dyld3