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)
61 extern mach_header __dso_handle
;
64 extern dyld_all_image_infos dyld_all_image_infos
;
71 static const void *stripPointer(const void *ptr
) {
72 #if __has_feature(ptrauth_calls)
73 return __builtin_ptrauth_strip(ptr
, ptrauth_key_asia
);
79 pthread_mutex_t
RecursiveAutoLock::_sMutex
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER
;
81 // forward declaration
82 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
));
85 uint32_t _dyld_image_count(void)
87 log_apis("_dyld_image_count()\n");
89 return gAllImages
.count();
92 const mach_header
* _dyld_get_image_header(uint32_t imageIndex
)
94 log_apis("_dyld_get_image_header(%d)\n", imageIndex
);
95 return gAllImages
.imageLoadAddressByIndex(imageIndex
);
98 intptr_t _dyld_get_image_slide(const mach_header
* mh
)
100 log_apis("_dyld_get_image_slide(%p)\n", mh
);
102 const MachOLoaded
* mf
= (MachOLoaded
*)mh
;
103 if ( !mf
->hasMachOMagic() )
106 return mf
->getSlide();
109 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex
)
111 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex
);
113 const mach_header
* mh
= gAllImages
.imageLoadAddressByIndex(imageIndex
);
115 return dyld3::_dyld_get_image_slide(mh
);
119 const char* _dyld_get_image_name(uint32_t imageIndex
)
121 log_apis("_dyld_get_image_name(%d)\n", imageIndex
);
122 return gAllImages
.imagePathByIndex(imageIndex
);
126 static bool nameMatch(const char* installName
, const char* libraryName
)
128 const char* leafName
= strrchr(installName
, '/');
129 if ( leafName
== NULL
)
130 leafName
= installName
;
134 // -framework case is exact match of leaf name
135 if ( strcmp(leafName
, libraryName
) == 0 )
138 // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
139 size_t leafNameLen
= strlen(leafName
);
140 size_t libraryNameLen
= strlen(libraryName
);
141 if ( leafNameLen
< (libraryNameLen
+9) )
143 if ( strncmp(leafName
, "lib", 3) != 0 )
145 if ( strcmp(&leafName
[leafNameLen
-6], ".dylib") != 0 )
147 if ( strncmp(&leafName
[3], libraryName
, libraryNameLen
) != 0 )
149 return (leafName
[libraryNameLen
+3] == '.');
154 // BETTER, USE: dyld_get_program_sdk_version()
156 // Scans the main executable and returns the version of the specified dylib the program was built against.
158 // The library to find is the leaf name that would have been passed to linker tool
159 // (e.g. -lfoo or -framework foo would use "foo").
161 // Returns -1 if the main executable did not link against the specified library, or is malformed.
163 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName
)
165 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName
);
167 __block
int32_t result
= -1;
168 gAllImages
.mainExecutable()->forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
169 if ( nameMatch(loadPath
, libraryName
) )
170 result
= currentVersion
;
172 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result
);
178 // Searches loaded images for the requested dylib and returns its current version.
180 // The library to find is the leaf name that would have been passed to linker tool
181 // (e.g. -lfoo or -framework foo would use "foo").
183 // If the specified library is not loaded, -1 is returned.
185 int32_t NSVersionOfRunTimeLibrary(const char* libraryName
)
187 log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName
);
188 __block
int32_t result
= -1;
189 gAllImages
.forEachImage(^(const dyld3::LoadedImage
& loadedImage
, bool &stop
) {
190 const char* installName
;
191 uint32_t currentVersion
;
192 uint32_t compatVersion
;
193 if ( loadedImage
.loadedAddress()->getDylibInstallName(&installName
, &compatVersion
, ¤tVersion
) && nameMatch(installName
, libraryName
) ) {
194 result
= currentVersion
;
198 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", result
);
203 uint32_t dyld_get_program_sdk_watch_os_version()
205 log_apis("dyld_get_program_sdk_watch_os_version()\n");
207 __block
uint32_t retval
= 0;
208 __block
bool versionFound
= false;
209 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
210 if (versionFound
) return;
212 if (dyld_get_base_platform(platform
) == PLATFORM_WATCHOS
) {
214 retval
= sdk_version
;
221 uint32_t dyld_get_program_min_watch_os_version()
223 log_apis("dyld_get_program_min_watch_os_version()\n");
225 __block
uint32_t retval
= 0;
226 __block
bool versionFound
= false;
227 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
228 if (versionFound
) return;
230 if (dyld_get_base_platform(platform
) == PLATFORM_WATCHOS
) {
232 retval
= min_version
;
239 uint32_t dyld_get_program_sdk_bridge_os_version()
241 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
243 __block
uint32_t retval
= 0;
244 __block
bool versionFound
= false;
245 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
246 if (versionFound
) return;
248 if (dyld_get_base_platform(platform
) == PLATFORM_BRIDGEOS
) {
250 retval
= sdk_version
;
257 uint32_t dyld_get_program_min_bridge_os_version()
259 log_apis("dyld_get_program_min_bridge_os_version()\n");
261 __block
uint32_t retval
= 0;
262 __block
bool versionFound
= false;
263 dyld3::dyld_get_image_versions(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
264 if (versionFound
) return;
266 if (dyld_get_base_platform(platform
) == PLATFORM_BRIDGEOS
) {
268 retval
= min_version
;
276 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
277 // specified binary was built against.
279 // First looks for LC_VERSION_MIN_* in binary and if sdk field is
280 // not zero, return that value.
281 // Otherwise, looks for the libSystem.B.dylib the binary linked
282 // against and uses a table to convert that to an sdk version.
284 uint32_t dyld_get_sdk_version(const mach_header
* mh
)
286 log_apis("dyld_get_sdk_version(%p)\n", mh
);
287 __block
bool versionFound
= false;
288 __block
uint32_t retval
= 0;
289 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
290 if (versionFound
) return;
292 if (platform
== ::dyld_get_active_platform()) {
294 switch (dyld3::dyld_get_base_platform(platform
)) {
295 case PLATFORM_BRIDGEOS
: retval
= sdk_version
+ 0x00090000; return;
296 case PLATFORM_WATCHOS
: retval
= sdk_version
+ 0x00070000; return;
297 default: retval
= sdk_version
; return;
305 uint32_t dyld_get_program_sdk_version()
307 log_apis("dyld_get_program_sdk_version()\n");
308 return dyld3::dyld_get_sdk_version(gAllImages
.mainExecutable());
311 uint32_t dyld_get_min_os_version(const mach_header
* mh
)
313 log_apis("dyld_get_min_os_version(%p)\n", mh
);
314 __block
bool versionFound
= false;
315 __block
uint32_t retval
= 0;
316 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
317 if (versionFound
) return;
319 if (platform
== ::dyld_get_active_platform()) {
321 switch (dyld3::dyld_get_base_platform(platform
)) {
322 case PLATFORM_BRIDGEOS
: retval
= min_version
+ 0x00090000; return;
323 case PLATFORM_WATCHOS
: retval
= min_version
+ 0x00070000; return;
324 default: retval
= min_version
; return;
332 dyld_platform_t
dyld_get_active_platform(void) {
333 return gAllImages
.platform();
336 dyld_platform_t
dyld_get_base_platform(dyld_platform_t platform
) {
338 case PLATFORM_IOSMAC
: return PLATFORM_IOS
;
339 case PLATFORM_IOSSIMULATOR
: return PLATFORM_IOS
;
340 case PLATFORM_WATCHOSSIMULATOR
: return PLATFORM_WATCHOS
;
341 case PLATFORM_TVOSSIMULATOR
: return PLATFORM_TVOS
;
342 default: return platform
;
346 bool dyld_is_simulator_platform(dyld_platform_t platform
) {
348 case PLATFORM_IOSSIMULATOR
:
349 case PLATFORM_WATCHOSSIMULATOR
:
350 case PLATFORM_TVOSSIMULATOR
:
357 bool dyld_sdk_at_least(const struct mach_header
* mh
, dyld_build_version_t version
) {
358 __block
bool retval
= false;
359 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
360 if (dyld3::dyld_get_base_platform(platform
) == version
.platform
&& sdk_version
>= version
.version
) {
367 bool dyld_minos_at_least(const struct mach_header
* mh
, dyld_build_version_t version
) {
368 __block
bool retval
= false;
369 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
370 if (dyld3::dyld_get_base_platform(platform
) == version
.platform
&& min_version
>= version
.version
) {
377 bool dyld_program_sdk_at_least(dyld_build_version_t version
) {
378 return dyld3::dyld_sdk_at_least(gAllImages
.mainExecutable(), version
);
381 bool dyld_program_minos_at_least(dyld_build_version_t version
) {
382 return dyld3::dyld_minos_at_least(gAllImages
.mainExecutable(), version
);
385 #if TARGET_OS_OSX || TARGET_OS_IOS
387 uint32_t linkedDylibVersion(const mach_header
* mh
, const char *installname
) {
388 __block
uint32_t retval
= 0;
389 ((MachOLoaded
*)mh
)->forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
390 if (strcmp(loadPath
, installname
) == 0) {
391 retval
= currentVersion
;
400 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
402 static uint32_t deriveVersionFromDylibs(const struct mach_header
* mh
) {
403 // This is a binary without a version load command, we need to infer things
404 struct DylibToOSMapping
{
405 uint32_t dylibVersion
;
408 uint32_t linkedVersion
= 0;
410 linkedVersion
= linkedDylibVersion(mh
, "/usr/lib/libSystem.B.dylib");
411 static const DylibToOSMapping versionMapping
[] = {
412 { PACKED_VERSION(88,1,3), 0x000A0400 },
413 { PACKED_VERSION(111,0,0), 0x000A0500 },
414 { PACKED_VERSION(123,0,0), 0x000A0600 },
415 { PACKED_VERSION(159,0,0), 0x000A0700 },
416 { PACKED_VERSION(169,3,0), 0x000A0800 },
417 { PACKED_VERSION(1197,0,0), 0x000A0900 },
418 { PACKED_VERSION(0,0,0), 0x000A0900 }
419 // We don't need to expand this table because all recent
420 // binaries have LC_VERSION_MIN_ load command.
423 linkedVersion
= linkedDylibVersion(mh
, "/System/Library/Frameworks/Foundation.framework/Foundation");
424 static const DylibToOSMapping versionMapping
[] = {
425 { PACKED_VERSION(678,24,0), 0x00020000 },
426 { PACKED_VERSION(678,26,0), 0x00020100 },
427 { PACKED_VERSION(678,29,0), 0x00020200 },
428 { PACKED_VERSION(678,47,0), 0x00030000 },
429 { PACKED_VERSION(678,51,0), 0x00030100 },
430 { PACKED_VERSION(678,60,0), 0x00030200 },
431 { PACKED_VERSION(751,32,0), 0x00040000 },
432 { PACKED_VERSION(751,37,0), 0x00040100 },
433 { PACKED_VERSION(751,49,0), 0x00040200 },
434 { PACKED_VERSION(751,58,0), 0x00040300 },
435 { PACKED_VERSION(881,0,0), 0x00050000 },
436 { PACKED_VERSION(890,1,0), 0x00050100 },
437 { PACKED_VERSION(992,0,0), 0x00060000 },
438 { PACKED_VERSION(993,0,0), 0x00060100 },
439 { PACKED_VERSION(1038,14,0),0x00070000 },
440 { PACKED_VERSION(0,0,0), 0x00070000 }
441 // We don't need to expand this table because all recent
442 // binaries have LC_VERSION_MIN_ load command.
445 static const DylibToOSMapping versionMapping
[] = {};
447 if ( linkedVersion
!= 0 ) {
448 uint32_t lastOsVersion
= 0;
449 for (const DylibToOSMapping
* p
=versionMapping
; ; ++p
) {
450 if ( p
->dylibVersion
== 0 ) {
453 if ( linkedVersion
< p
->dylibVersion
) {
454 return lastOsVersion
;
456 lastOsVersion
= p
->osVersion
;
462 // assumes mh has already been validated
463 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
))
465 const MachOFile
* mf
= (MachOFile
*)mh
;
466 __block
bool lcFound
= false;
467 mf
->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
469 // If SDK field is empty then derive the value from library linkages
471 sdk
= deriveVersionFromDylibs(mh
);
473 // HACK: We don't have time to fix all the zippered clients in the spring releases, so keep the mapping
474 if (platform
== dyld3::Platform::iOSMac
) {
475 if (sdk
>= 0x000D0400) {
481 callback((const dyld_platform_t
)platform
, sdk
, minOS
);
484 // No load command was found, so again, fallback to deriving it from library linkages
487 #if __x86_64__ || __x86__
488 dyld_platform_t platform
= PLATFORM_IOSSIMULATOR
;
490 dyld_platform_t platform
= PLATFORM_IOS
;
493 dyld_platform_t platform
= PLATFORM_MACOS
;
495 dyld_platform_t platform
= 0;
497 uint32_t derivedVersion
= deriveVersionFromDylibs(mh
);
498 if ( platform
!= 0 && derivedVersion
!= 0 ) {
499 callback(platform
, derivedVersion
, 0);
504 void dyld_get_image_versions(const struct mach_header
* mh
, void (^callback
)(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
))
507 const MachOFile
* mf
= (MachOFile
*)mh
;
508 static dyld_platform_t mainExecutablePlatform
= 0;
509 static uint32_t mainExecutableSDKVersion
= 0;
510 static uint32_t mainExecutableMinOSVersion
= 0;
512 // FIXME: Once dyld2 is gone gAllImages.mainExecutable() will be valid in all cases
513 // and we can stop calling _NSGetMachExecuteHeader()
514 if (mh
== (const struct mach_header
*)_NSGetMachExecuteHeader()) {
515 if (mainExecutablePlatform
) {
516 return callback(mainExecutablePlatform
, mainExecutableSDKVersion
, mainExecutableMinOSVersion
);
518 mainExecutablePlatform
= ::dyld_get_active_platform();
519 dyld_get_image_versions_internal(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
520 if (platform
== PLATFORM_MACOS
&& dyld_get_base_platform(mainExecutablePlatform
) == PLATFORM_IOS
) {
521 // We are running with DYLD_FORCE_PLATFORM, use the current OSes values
522 dyld_get_image_versions_internal(&__dso_handle
, ^(dyld_platform_t dyld_platform
, uint32_t dyld_sdk_version
, uint32_t dyld_min_version
) {
523 if (dyld_get_base_platform(dyld_platform
) == PLATFORM_IOS
) {
524 mainExecutableSDKVersion
= dyld_sdk_version
;
525 mainExecutableMinOSVersion
= dyld_min_version
;
529 mainExecutableSDKVersion
= sdk_version
;
530 mainExecutableMinOSVersion
= min_version
;
533 return callback(mainExecutablePlatform
, mainExecutableSDKVersion
, mainExecutableMinOSVersion
);
535 #if TARGET_OS_EMBEDDED
536 // If we are on embedded AND in the shared cache then the versions should be the same as libdyld
537 if (mf
->inDyldCache()) {
538 static dyld_platform_t libDyldPlatform
= 0;
539 static uint32_t libDyldSDKVersion
= 0;
540 static uint32_t libDyldMinOSVersion
= 0;
541 if (libDyldPlatform
== 0) {
542 dyld_get_image_versions_internal(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
543 libDyldPlatform
= platform
;
544 libDyldSDKVersion
= sdk_version
;
545 libDyldMinOSVersion
= min_version
;
546 //FIXME: Assert if more than one command?
549 return callback(libDyldPlatform
, libDyldSDKVersion
, libDyldMinOSVersion
);
552 if ( mf
->isMachO(diag
, mh
->sizeofcmds
+ sizeof(mach_header_64
)) )
553 dyld_get_image_versions_internal(mh
, callback
);
556 uint32_t dyld_get_program_min_os_version()
558 log_apis("dyld_get_program_min_os_version()\n");
559 return dyld3::dyld_get_min_os_version(gAllImages
.mainExecutable());
562 bool _dyld_get_image_uuid(const mach_header
* mh
, uuid_t uuid
)
564 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh
, uuid
);
566 const MachOFile
* mf
= (MachOFile
*)mh
;
567 if ( !mf
->hasMachOMagic() )
570 return mf
->getUuid(uuid
);
574 // _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
575 // should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
576 // and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
577 // to the size required.
579 int _NSGetExecutablePath(char* buf
, uint32_t* bufsize
)
581 log_apis("_NSGetExecutablePath(%p, %p)\n", buf
, bufsize
);
583 const closure::Image
* mainImage
= gAllImages
.mainExecutableImage();
584 const char* path
= gAllImages
.imagePath(mainImage
);
585 size_t pathSize
= strlen(path
) + 1;
586 if ( *bufsize
>= pathSize
) {
590 *bufsize
= (uint32_t)pathSize
;
594 void _dyld_register_func_for_add_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
596 log_apis("_dyld_register_func_for_add_image(%p)\n", func
);
598 gAllImages
.addLoadNotifier(func
);
601 void _dyld_register_func_for_remove_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
603 log_apis("_dyld_register_func_for_remove_image(%p)\n", func
);
605 gAllImages
.addUnloadNotifier(func
);
608 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped
,
609 _dyld_objc_notify_init init
,
610 _dyld_objc_notify_unmapped unmapped
)
612 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped
, init
, unmapped
);
614 gAllImages
.setObjCNotifiers(mapped
, init
, unmapped
);
618 const mach_header
* dyld_image_header_containing_address(const void* addr
)
620 log_apis("dyld_image_header_containing_address(%p)\n", addr
);
622 addr
= stripPointer(addr
);
624 const MachOLoaded
* ml
;
625 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, nullptr) )
632 const char* dyld_image_path_containing_address(const void* addr
)
634 log_apis("dyld_image_path_containing_address(%p)\n", addr
);
636 addr
= stripPointer(addr
);
637 const char* result
= gAllImages
.pathForImageMappedAt(addr
);
639 log_apis(" dyld_image_path_containing_address() => %s\n", result
);
645 bool _dyld_is_memory_immutable(const void* addr
, size_t length
)
647 return gAllImages
.immutableMemory(addr
, length
);
650 int dladdr(const void* addr
, Dl_info
* info
)
652 log_apis("dladdr(%p, %p)\n", addr
, info
);
654 // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
658 addr
= stripPointer(addr
);
660 __block
int result
= 0;
661 const MachOLoaded
* ml
= nullptr;
662 const char* path
= nullptr;
663 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, &path
) ) {
664 info
->dli_fname
= path
;
665 info
->dli_fbase
= (void*)ml
;
668 if ( addr
== info
->dli_fbase
) {
669 // special case lookup of header
670 info
->dli_sname
= "__dso_handle";
671 info
->dli_saddr
= info
->dli_fbase
;
673 else if ( ml
->findClosestSymbol((long)addr
, &(info
->dli_sname
), &symbolAddr
) ) {
674 info
->dli_saddr
= (void*)(long)symbolAddr
;
675 // never return the mach_header symbol
676 if ( info
->dli_saddr
== info
->dli_fbase
) {
677 info
->dli_sname
= nullptr;
678 info
->dli_saddr
= nullptr;
680 // strip off leading underscore
681 else if ( (info
->dli_sname
!= nullptr) && (info
->dli_sname
[0] == '_') ) {
682 info
->dli_sname
= info
->dli_sname
+ 1;
686 info
->dli_sname
= nullptr;
687 info
->dli_saddr
= nullptr;
693 log_apis(" dladdr() => 0\n");
695 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info
->dli_fname
, info
->dli_fbase
, info
->dli_sname
, info
->dli_saddr
);
699 #if !TARGET_OS_DRIVERKIT
701 struct PerThreadErrorMessage
703 size_t sizeAllocated
;
708 static void dlerror_perThreadKey_once(void* ctx
)
710 pthread_key_t
* dlerrorPThreadKeyPtr
= (pthread_key_t
*)ctx
;
711 pthread_key_create(dlerrorPThreadKeyPtr
, &free
);
714 static pthread_key_t
dlerror_perThreadKey()
716 static os_once_t onceToken
;
717 static pthread_key_t dlerrorPThreadKey
;
718 os_once(&onceToken
, &dlerrorPThreadKey
, dlerror_perThreadKey_once
);
719 return dlerrorPThreadKey
;
722 static void clearErrorString()
724 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
725 if ( errorBuffer
!= nullptr )
726 errorBuffer
->valid
= false;
729 __attribute__((format(printf
, 1, 2)))
730 static void setErrorString(const char* format
, ...)
732 _SIMPLE_STRING buf
= _simple_salloc();
733 if ( buf
!= nullptr ) {
735 va_start(list
, format
);
736 _simple_vsprintf(buf
, format
, list
);
738 size_t strLen
= strlen(_simple_string(buf
)) + 1;
739 size_t sizeNeeded
= sizeof(PerThreadErrorMessage
) + strLen
;
740 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
741 if ( errorBuffer
!= nullptr ) {
742 if ( errorBuffer
->sizeAllocated
< sizeNeeded
) {
744 errorBuffer
= nullptr;
747 if ( errorBuffer
== nullptr ) {
748 size_t allocSize
= std::max(sizeNeeded
, (size_t)256);
749 PerThreadErrorMessage
* p
= (PerThreadErrorMessage
*)malloc(allocSize
);
750 p
->sizeAllocated
= allocSize
;
752 pthread_setspecific(dlerror_perThreadKey(), p
);
755 strcpy(errorBuffer
->message
, _simple_string(buf
));
756 errorBuffer
->valid
= true;
763 log_apis("dlerror()\n");
765 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
766 if ( errorBuffer
!= nullptr ) {
767 if ( errorBuffer
->valid
) {
768 // you can only call dlerror() once, then the message is cleared
769 errorBuffer
->valid
= false;
770 return errorBuffer
->message
;
777 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
779 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
783 static void* makeDlHandle(const mach_header
* mh
, bool dontContinue
)
785 uintptr_t flags
= (dontContinue
? 1 : 0);
786 return (void*)((((uintptr_t)mh
) >> 5) | flags
);
790 void parseDlHandle(void* h
, const MachOLoaded
** mh
, bool* dontContinue
)
792 *dontContinue
= (((uintptr_t)h
) & 1);
793 *mh
= (const MachOLoaded
*)((((uintptr_t)h
) & (-2)) << 5);
796 int dlclose(void* handle
)
798 DYLD_LOAD_LOCK_THIS_BLOCK
799 log_apis("dlclose(%p)\n", handle
);
801 // silently accept magic handles for main executable
802 if ( handle
== RTLD_MAIN_ONLY
)
804 if ( handle
== RTLD_DEFAULT
)
807 const MachOLoaded
* mh
;
809 parseDlHandle(handle
, &mh
, &dontContinue
);
811 __block
bool unloadable
= false;
812 __block
bool validHandle
= false;
813 gAllImages
.infoForImageMappedAt(mh
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
815 if ( !foundImage
.image()->neverUnload() )
819 gAllImages
.decRefCount(mh
); // removes image if reference count went to zero
827 setErrorString("invalid handle passed to dlclose()");
833 void* dlopen_internal(const char* path
, int mode
, void* callerAddress
)
835 DYLD_LOAD_LOCK_THIS_BLOCK
836 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path
==NULL
) ? "NULL" : path
), mode
);
840 // passing NULL for path means return magic object
841 if ( path
== NULL
) {
842 // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
843 if ( (mode
& RTLD_FIRST
) != 0 )
844 return RTLD_MAIN_ONLY
;
849 const char* leafName
= strrchr(path
, '/');
850 if ( leafName
!= nullptr )
855 #if __IPHONE_OS_VERSION_MIN_REQUIRED
856 // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
857 char canonicalPath
[PATH_MAX
];
858 if ( leafName
!= path
) {
859 // make path canonical if it contains a // or ./
860 if ( (strstr(path
, "//") != NULL
) || (strstr(path
, "./") != NULL
) ) {
861 const char* lastSlash
= strrchr(path
, '/');
862 char dirPath
[PATH_MAX
];
863 if ( strlcpy(dirPath
, path
, sizeof(dirPath
)) < sizeof(dirPath
) ) {
864 dirPath
[lastSlash
-path
] = '\0';
865 if ( realpath(dirPath
, canonicalPath
) ) {
866 strlcat(canonicalPath
, "/", sizeof(canonicalPath
));
867 if ( strlcat(canonicalPath
, lastSlash
+1, sizeof(canonicalPath
)) < sizeof(canonicalPath
) ) {
868 // if all fit in buffer, use new canonical path
869 path
= canonicalPath
;
877 // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
878 const bool firstOnly
= (mode
& RTLD_FIRST
);
880 // 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
881 const bool rtldLocal
= (mode
& RTLD_LOCAL
);
883 // RTLD_NODELETE means don't unmap image during dlclose(). Leave the memory mapped, but orphan (leak) it.
884 // Note: this is a weird state and it slightly different semantics that other OSs
885 const bool rtldNoDelete
= (mode
& RTLD_NODELETE
);
887 // RTLD_NOLOAD means do nothing if image not already loaded
888 const bool rtldNoLoad
= (mode
& RTLD_NOLOAD
);
890 // RTLD_NOW means force lazy symbols bound and fail dlopen() if some cannot be bound
891 const bool rtldNow
= (mode
& RTLD_NOW
);
893 // try to load image from specified path
895 const mach_header
* topLoadAddress
= gAllImages
.dlopen(diag
, path
, rtldNoLoad
, rtldLocal
, rtldNoDelete
, rtldNow
, false, callerAddress
);
896 if ( diag
.hasError() ) {
897 setErrorString("dlopen(%s, 0x%04X): %s", path
, mode
, diag
.errorMessage());
898 log_apis(" dlopen: closure creation error: %s\n", diag
.errorMessage());
901 if ( topLoadAddress
== nullptr ) {
902 log_apis(" dlopen(%s) => NULL\n", leafName
);
905 void* result
= makeDlHandle(topLoadAddress
, firstOnly
);
906 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
911 bool dlopen_preflight_internal(const char* path
)
913 DYLD_LOAD_LOCK_THIS_BLOCK
914 log_apis("dlopen_preflight(%s)\n", path
);
916 // check if path is in dyld shared cache
917 if ( gAllImages
.dyldCacheHasPath(path
) )
920 // check if file is loadable
922 closure::FileSystemPhysical fileSystem
;
923 char realerPath
[MAXPATHLEN
];
924 closure::LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, path
, gAllImages
.archs(), (Platform
)gAllImages
.platform(), realerPath
);
925 if ( loadedFileInfo
.fileContent
!= nullptr ) {
926 fileSystem
.unloadFile(loadedFileInfo
);
930 // FIXME: may be symlink to something in dyld cache
935 static void* dlsym_search(const char* symName
, const LoadedImage
& start
, bool searchStartImage
, MachOLoaded::DependentToMachOLoaded reExportHelper
,
936 bool* resultPointsToInstructions
)
938 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
939 return gAllImages
.findDependent(mh
, depIndex
);
941 //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
943 // walk all dependents of 'start' in order looking for symbol
944 __block
void* result
= nullptr;
945 gAllImages
.visitDependentsTopDown(start
, ^(const LoadedImage
& aLoadedImage
, bool& stop
) {
946 //fprintf(stderr, " search: %s\n", aLoadedImage.image()->path());
947 if ( !searchStartImage
&& aLoadedImage
.image() == start
.image() )
949 if ( aLoadedImage
.loadedAddress()->hasExportedSymbol(symName
, finder
, &result
, resultPointsToInstructions
) ) {
950 result
= gAllImages
.interposeValue(result
);
959 void* dlsym_internal(void* handle
, const char* symbolName
, void* callerAddress
)
961 log_apis("dlsym(%p, \"%s\")\n", handle
, symbolName
);
965 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
966 return gAllImages
.findDependent(mh
, depIndex
);
969 // dlsym() assumes symbolName passed in is same as in C source code
970 // dyld assumes all symbol names have an underscore prefix
971 BLOCK_ACCCESSIBLE_ARRAY(char, underscoredName
, strlen(symbolName
)+2);
972 underscoredName
[0] = '_';
973 strcpy(&underscoredName
[1], symbolName
);
975 __block
void* result
= nullptr;
976 __block
bool resultPointsToInstructions
= false;
977 if ( handle
== RTLD_DEFAULT
) {
978 // magic "search all in load order" handle
979 gAllImages
.forEachImage(^(const LoadedImage
& loadedImage
, bool& stop
) {
980 if ( loadedImage
.hideFromFlatSearch() )
982 if ( loadedImage
.loadedAddress()->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
) ) {
986 if ( result
!= nullptr ) {
987 result
= gAllImages
.interposeValue(result
);
988 #if __has_feature(ptrauth_calls)
989 if (resultPointsToInstructions
)
990 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
992 log_apis(" dlsym() => %p\n", result
);
995 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName
);
996 log_apis(" dlsym() => NULL\n");
999 else if ( handle
== RTLD_MAIN_ONLY
) {
1000 // magic "search only main executable" handle
1001 if ( gAllImages
.mainExecutable()->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
) ) {
1002 result
= gAllImages
.interposeValue(result
);
1003 log_apis(" dlsym() => %p\n", result
);
1004 #if __has_feature(ptrauth_calls)
1005 if (resultPointsToInstructions
)
1006 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
1010 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName
);
1011 log_apis(" dlsym() => NULL\n");
1014 // rest of cases search in dependency order
1015 if ( handle
== RTLD_NEXT
) {
1016 // magic "search what I would see" handle
1017 __block
bool foundCaller
= false;
1018 gAllImages
.infoForImageMappedAt(callerAddress
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
1020 result
= dlsym_search(underscoredName
, foundImage
, false, finder
, &resultPointsToInstructions
);
1022 if ( !foundCaller
) {
1023 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
1027 else if ( handle
== RTLD_SELF
) {
1028 // magic "search me, then what I would see" handle
1029 __block
bool foundCaller
= false;
1030 gAllImages
.infoForImageMappedAt(callerAddress
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
1032 result
= dlsym_search(underscoredName
, foundImage
, true, finder
, &resultPointsToInstructions
);
1034 if ( !foundCaller
) {
1035 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
1040 // handle value was something returned by dlopen()
1041 const MachOLoaded
* mh
;
1043 parseDlHandle(handle
, &mh
, &dontContinue
);
1045 __block
bool foundCaller
= false;
1046 gAllImages
.infoForImageWithLoadAddress(mh
, ^(const LoadedImage
& foundImage
) {
1048 if ( dontContinue
) {
1049 // RTLD_FIRST only searches one place
1050 // we go through infoForImageWithLoadAddress() to validate the handle
1051 if (mh
->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
))
1052 result
= gAllImages
.interposeValue(result
);
1055 result
= dlsym_search(underscoredName
, foundImage
, true, finder
, &resultPointsToInstructions
);
1058 if ( !foundCaller
) {
1059 setErrorString("dlsym(%p, %s): invalid handle", handle
, symbolName
);
1060 log_apis(" dlsym() => NULL\n");
1065 if ( result
!= nullptr ) {
1066 #if __has_feature(ptrauth_calls)
1067 if (resultPointsToInstructions
)
1068 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
1070 log_apis(" dlsym() => %p\n", result
);
1074 setErrorString("dlsym(%p, %s): symbol not found", handle
, symbolName
);
1075 log_apis(" dlsym() => NULL\n");
1078 #endif // !TARGET_OS_DRIVERKIT
1081 const struct dyld_all_image_infos
* _dyld_get_all_image_infos()
1083 return gAllImages
.oldAllImageInfo();
1086 bool dyld_shared_cache_some_image_overridden()
1088 log_apis("dyld_shared_cache_some_image_overridden()\n");
1090 return gAllImages
.hasCacheOverrides();
1093 bool _dyld_get_shared_cache_uuid(uuid_t uuid
)
1095 log_apis("_dyld_get_shared_cache_uuid()\n");
1097 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1098 if ( sharedCache
== nullptr )
1101 if ( gAllImages
.oldAllImageInfo() != nullptr ) {
1102 memcpy(uuid
, gAllImages
.oldAllImageInfo()->sharedCacheUUID
, sizeof(uuid_t
));
1108 const void* _dyld_get_shared_cache_range(size_t* mappedSize
)
1110 log_apis("_dyld_get_shared_cache_range()\n");
1112 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1113 if ( sharedCache
!= nullptr ) {
1114 *mappedSize
= (size_t)sharedCache
->mappedSize();
1121 bool _dyld_shared_cache_optimized()
1123 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1124 if ( sharedCache
!= nullptr ) {
1125 return (sharedCache
->header
.cacheType
== kDyldSharedCacheTypeProduction
);
1130 bool _dyld_shared_cache_is_locally_built()
1132 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1133 if ( sharedCache
!= nullptr ) {
1134 return (sharedCache
->header
.locallyBuiltCache
== 1);
1139 void _dyld_images_for_addresses(unsigned count
, const void* addresses
[], dyld_image_uuid_offset infos
[])
1141 log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count
, addresses
, infos
);
1143 // in stack crawls, common for contiguous fames to be in same image, so cache
1144 // last lookup and check if next addresss in in there before doing full search
1145 const MachOLoaded
* ml
= nullptr;
1146 uint64_t textSize
= 0;
1147 const void* end
= (void*)ml
;
1148 for (unsigned i
=0; i
< count
; ++i
) {
1149 const void* addr
= stripPointer(addresses
[i
]);
1150 bzero(&infos
[i
], sizeof(dyld_image_uuid_offset
));
1151 if ( (ml
== nullptr) || (addr
< (void*)ml
) || (addr
> end
) ) {
1152 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, &textSize
, nullptr) ) {
1153 end
= (void*)((uint8_t*)ml
+ textSize
);
1160 if ( ml
!= nullptr ) {
1161 infos
[i
].image
= ml
;
1162 infos
[i
].offsetInImage
= (uintptr_t)addr
- (uintptr_t)ml
;
1163 ml
->getUuid(infos
[i
].uuid
);
1168 void _dyld_register_for_image_loads(void (*func
)(const mach_header
* mh
, const char* path
, bool unloadable
))
1170 gAllImages
.addLoadNotifier(func
);
1173 void _dyld_register_for_bulk_image_loads(void (*func
)(unsigned imageCount
, const struct mach_header
* mhs
[], const char* paths
[]))
1175 gAllImages
.addBulkLoadNotifier(func
);
1178 bool _dyld_find_unwind_sections(void* addr
, dyld_unwind_sections
* info
)
1180 log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr
, info
);
1181 addr
= (void*)stripPointer(addr
);
1183 const MachOLoaded
* ml
= nullptr;
1184 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, nullptr) ) {
1186 info
->dwarf_section
= nullptr;
1187 info
->dwarf_section_length
= 0;
1188 info
->compact_unwind_section
= nullptr;
1189 info
->compact_unwind_section_length
= 0;
1192 if ( const void* content
= ml
->findSectionContent("__TEXT", "__eh_frame", size
) ) {
1193 info
->dwarf_section
= content
;
1194 info
->dwarf_section_length
= (uintptr_t)size
;
1196 if ( const void* content
= ml
->findSectionContent("__TEXT", "__unwind_info", size
) ) {
1197 info
->compact_unwind_section
= content
;
1198 info
->compact_unwind_section_length
= (uintptr_t)size
;
1207 bool dyld_process_is_restricted()
1209 log_apis("dyld_process_is_restricted()\n");
1210 return gAllImages
.isRestricted();
1214 const char* dyld_shared_cache_file_path()
1216 log_apis("dyld_shared_cache_file_path()\n");
1218 return gAllImages
.dyldCachePath();
1222 bool dyld_has_inserted_or_interposing_libraries()
1224 log_apis("dyld_has_inserted_or_interposing_libraries()\n");
1226 return gAllImages
.hasInsertedOrInterposingLibraries();
1230 void dyld_dynamic_interpose(const mach_header
* mh
, const dyld_interpose_tuple array
[], size_t count
)
1232 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh
, array
, count
);
1237 static void* mapStartOfCache(const char* path
, size_t length
)
1239 struct stat statbuf
;
1240 if ( ::stat(path
, &statbuf
) == -1 )
1243 if ( statbuf
.st_size
< length
)
1246 int cache_fd
= ::open(path
, O_RDONLY
);
1250 void* result
= ::mmap(NULL
, length
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
1253 if ( result
== MAP_FAILED
)
1259 static const DyldSharedCache
* findCacheInDirAndMap(const uuid_t cacheUuid
, const char* dirPath
, size_t& sizeMapped
)
1261 DIR* dirp
= ::opendir(dirPath
);
1262 if ( dirp
!= NULL
) {
1264 dirent
* entp
= NULL
;
1265 char cachePath
[PATH_MAX
];
1266 while ( ::readdir_r(dirp
, &entry
, &entp
) == 0 ) {
1269 if ( entp
->d_type
!= DT_REG
)
1271 if ( strlcpy(cachePath
, dirPath
, PATH_MAX
) >= PATH_MAX
)
1273 if ( strlcat(cachePath
, "/", PATH_MAX
) >= PATH_MAX
)
1275 if ( strlcat(cachePath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1277 if ( const DyldSharedCache
* cache
= (DyldSharedCache
*)mapStartOfCache(cachePath
, 0x00100000) ) {
1279 cache
->getUUID(foundUuid
);
1280 if ( ::memcmp(foundUuid
, cacheUuid
, 16) != 0 ) {
1281 // wrong uuid, unmap and keep looking
1282 ::munmap((void*)cache
, 0x00100000);
1287 sizeMapped
= 0x00100000;
1297 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid
, const char* extraSearchDirs
[], void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1299 log_apis("dyld_shared_cache_find_iterate_text()\n");
1301 // see if requested cache is the active one in this process
1302 size_t sizeMapped
= 0;
1303 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1304 if ( sharedCache
!= nullptr ) {
1306 sharedCache
->getUUID(runningUuid
);
1307 if ( ::memcmp(runningUuid
, cacheUuid
, 16) != 0 )
1308 sharedCache
= nullptr;
1310 if ( sharedCache
== nullptr ) {
1311 // if not, look in default location for cache files
1312 #if __IPHONE_OS_VERSION_MIN_REQUIRED
1313 const char* defaultSearchDir
= IPHONE_DYLD_SHARED_CACHE_DIR
;
1315 const char* defaultSearchDir
= MACOSX_DYLD_SHARED_CACHE_DIR
;
1317 sharedCache
= findCacheInDirAndMap(cacheUuid
, defaultSearchDir
, sizeMapped
);
1318 // if not there, look in extra search locations
1319 if ( sharedCache
== nullptr ) {
1320 for (const char** p
= extraSearchDirs
; *p
!= nullptr; ++p
) {
1321 sharedCache
= findCacheInDirAndMap(cacheUuid
, *p
, sizeMapped
);
1322 if ( sharedCache
!= nullptr )
1327 if ( sharedCache
== nullptr )
1330 // get base address of cache
1331 __block
uint64_t cacheUnslidBaseAddress
= 0;
1332 sharedCache
->forEachRegion(^(const void *content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
1333 if ( cacheUnslidBaseAddress
== 0 )
1334 cacheUnslidBaseAddress
= vmAddr
;
1337 // iterate all images
1338 sharedCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
, bool& stop
) {
1339 dyld_shared_cache_dylib_text_info dylibTextInfo
;
1340 dylibTextInfo
.version
= 2;
1341 dylibTextInfo
.loadAddressUnslid
= loadAddressUnslid
;
1342 dylibTextInfo
.textSegmentSize
= textSegmentSize
;
1343 dylibTextInfo
.path
= installName
;
1344 ::memcpy(dylibTextInfo
.dylibUuid
, dylibUUID
, 16);
1345 dylibTextInfo
.textSegmentOffset
= loadAddressUnslid
- cacheUnslidBaseAddress
;
1346 callback(&dylibTextInfo
);
1349 if ( sizeMapped
!= 0 )
1350 ::munmap((void*)sharedCache
, sizeMapped
);
1355 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid
, void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1357 log_apis("dyld_shared_cache_iterate_text()\n");
1359 const char* extraSearchDirs
[] = { NULL
};
1360 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid
, extraSearchDirs
, callback
);
1363 bool dyld_need_closure(const char* execPath
, const char* tempDir
)
1365 log_apis("dyld_need_closure()\n");
1367 // We don't need to build a closure if the shared cache has it already
1368 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1369 if ( sharedCache
!= nullptr ) {
1370 if ( sharedCache
->findClosure(execPath
) != nullptr )
1375 char closurePath
[PATH_MAX
];
1376 if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath
, closurePath
, tempDir
, false) ) {
1377 struct stat statbuf
;
1378 return (::stat(closurePath
, &statbuf
) != 0);
1381 // Not containerized so no point in building a closure.
1385 void _dyld_missing_symbol_abort()
1387 // We don't know the name of the lazy symbol that is missing.
1388 // dyld3 binds all such missing symbols to this one handler.
1389 // We need the crash log to contain the backtrace so someone can
1390 // figure out the symbol.
1392 auto allImageInfos
= gAllImages
.oldAllImageInfo();
1393 allImageInfos
->errorKind
= DYLD_EXIT_REASON_SYMBOL_MISSING
;
1394 allImageInfos
->errorClientOfDylibPath
= "<unknown>";
1395 allImageInfos
->errorTargetDylibPath
= "<unknown>";
1396 allImageInfos
->errorSymbol
= "<unknown>";
1398 halt("missing lazy symbol called");
1401 const char* _dyld_get_objc_selector(const char* selName
)
1403 log_apis("dyld_get_objc_selector()\n");
1404 return gAllImages
.getObjCSelector(selName
);
1407 void _dyld_for_each_objc_class(const char* className
,
1408 void (^callback
)(void* classPtr
, bool isLoaded
, bool* stop
)) {
1409 log_apis("_dyld_for_each_objc_class()\n");
1410 gAllImages
.forEachObjCClass(className
, callback
);
1413 void _dyld_for_each_objc_protocol(const char* protocolName
,
1414 void (^callback
)(void* protocolPtr
, bool isLoaded
, bool* stop
)) {
1415 log_apis("_dyld_for_each_objc_protocol()\n");
1416 gAllImages
.forEachObjCProtocol(protocolName
, callback
);
1419 #if !TARGET_OS_DRIVERKIT
1422 void* implementation
;
1425 static const struct dyld_func dyld_funcs
[] = {
1426 {"__dyld_dlsym", (void*)dlsym
}, // needs to go through generic function to get caller address
1427 {"__dyld_dlopen", (void*)dlopen
},// needs to go through generic function to get caller address
1428 {"__dyld_dladdr", (void*)dyld3::dladdr
},
1429 {"__dyld_image_count", (void*)dyld3::_dyld_image_count
},
1430 {"__dyld_get_image_name", (void*)dyld3::_dyld_get_image_name
},
1431 {"__dyld_get_image_header", (void*)dyld3::_dyld_get_image_header
},
1432 {"__dyld_get_image_vmaddr_slide", (void*)dyld3::_dyld_get_image_vmaddr_slide
},
1436 int compatFuncLookup(const char* name
, void** address
)
1438 #if !TARGET_OS_DRIVERKIT
1439 for (const dyld_func
* p
= dyld_funcs
; p
->name
!= NULL
; ++p
) {
1440 if ( strcmp(p
->name
, name
) == 0 ) {
1441 *address
= p
->implementation
;
1452 } // namespace dyld3