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 <TargetConditionals.h>
33 #include <CommonCrypto/CommonDigest.h>
34 #include <dispatch/dispatch.h>
42 #include "dyld_priv.h"
44 #include "AllImages.h"
47 #include "Diagnostics.h"
48 #include "DyldSharedCache.h"
49 #include "PathOverrides.h"
52 #include "MachOLoaded.h"
53 #include "ClosureBuilder.h"
54 #include "ClosureFileSystemPhysical.h"
56 #if __has_feature(ptrauth_calls)
62 extern dyld_all_image_infos dyld_all_image_infos
;
69 static const void *stripPointer(const void *ptr
) {
70 #if __has_feature(ptrauth_calls)
71 return __builtin_ptrauth_strip(ptr
, ptrauth_key_asia
);
77 pthread_mutex_t
RecursiveAutoLock::_sMutex
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER
;
79 // forward declaration
80 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
));
83 uint32_t _dyld_image_count(void)
85 log_apis("_dyld_image_count()\n");
87 return gAllImages
.count();
90 const mach_header
* _dyld_get_image_header(uint32_t imageIndex
)
92 log_apis("_dyld_get_image_header(%d)\n", imageIndex
);
93 return gAllImages
.imageLoadAddressByIndex(imageIndex
);
96 intptr_t _dyld_get_image_slide(const mach_header
* mh
)
98 log_apis("_dyld_get_image_slide(%p)\n", mh
);
100 const MachOLoaded
* mf
= (MachOLoaded
*)mh
;
101 if ( !mf
->hasMachOMagic() )
104 return mf
->getSlide();
107 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex
)
109 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex
);
111 const mach_header
* mh
= gAllImages
.imageLoadAddressByIndex(imageIndex
);
113 return dyld3::_dyld_get_image_slide(mh
);
117 const char* _dyld_get_image_name(uint32_t imageIndex
)
119 log_apis("_dyld_get_image_name(%d)\n", imageIndex
);
120 return gAllImages
.imagePathByIndex(imageIndex
);
124 static bool nameMatch(const char* installName
, const char* libraryName
)
126 const char* leafName
= strrchr(installName
, '/');
127 if ( leafName
== NULL
)
128 leafName
= installName
;
132 // -framework case is exact match of leaf name
133 if ( strcmp(leafName
, libraryName
) == 0 )
136 // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
137 size_t leafNameLen
= strlen(leafName
);
138 size_t libraryNameLen
= strlen(libraryName
);
139 if ( leafNameLen
< (libraryNameLen
+9) )
141 if ( strncmp(leafName
, "lib", 3) != 0 )
143 if ( strcmp(&leafName
[leafNameLen
-6], ".dylib") != 0 )
145 if ( strncmp(&leafName
[3], libraryName
, libraryNameLen
) != 0 )
147 return (leafName
[libraryNameLen
+3] == '.');
152 // BETTER, USE: dyld_get_program_sdk_version()
154 // Scans the main executable and returns the version of the specified dylib the program was built against.
156 // The library to find is the leaf name that would have been passed to linker tool
157 // (e.g. -lfoo or -framework foo would use "foo").
159 // Returns -1 if the main executable did not link against the specified library, or is malformed.
161 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName
)
163 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName
);
165 __block
int32_t result
= -1;
166 gAllImages
.mainExecutable()->forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
167 if ( nameMatch(loadPath
, libraryName
) )
168 result
= currentVersion
;
170 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result
);
176 // Searches loaded images for the requested dylib and returns its current version.
178 // The library to find is the leaf name that would have been passed to linker tool
179 // (e.g. -lfoo or -framework foo would use "foo").
181 // If the specified library is not loaded, -1 is returned.
183 int32_t NSVersionOfRunTimeLibrary(const char* libraryName
)
185 log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName
);
186 __block
int32_t result
= -1;
187 gAllImages
.forEachImage(^(const dyld3::LoadedImage
& loadedImage
, bool &stop
) {
188 const char* installName
;
189 uint32_t currentVersion
;
190 uint32_t compatVersion
;
191 if ( loadedImage
.loadedAddress()->getDylibInstallName(&installName
, &compatVersion
, ¤tVersion
) && nameMatch(installName
, libraryName
) ) {
192 result
= currentVersion
;
196 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", result
);
201 uint32_t dyld_get_program_sdk_watch_os_version()
203 log_apis("dyld_get_program_sdk_watch_os_version()\n");
205 __block
uint32_t retval
= 0;
206 __block
bool versionFound
= false;
207 dyld3::dyld_get_image_versions_internal(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
208 if (versionFound
) return;
210 if (dyld_get_base_platform(platform
) == PLATFORM_WATCHOS
) {
212 retval
= sdk_version
;
219 uint32_t dyld_get_program_min_watch_os_version()
221 log_apis("dyld_get_program_min_watch_os_version()\n");
223 __block
uint32_t retval
= 0;
224 __block
bool versionFound
= false;
225 dyld3::dyld_get_image_versions_internal(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
226 if (versionFound
) return;
228 if (dyld_get_base_platform(platform
) == PLATFORM_WATCHOS
) {
230 retval
= min_version
;
237 uint32_t dyld_get_program_sdk_bridge_os_version()
239 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
241 __block
uint32_t retval
= 0;
242 __block
bool versionFound
= false;
243 dyld3::dyld_get_image_versions_internal(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
244 if (versionFound
) return;
246 if (dyld_get_base_platform(platform
) == PLATFORM_BRIDGEOS
) {
248 retval
= sdk_version
;
255 uint32_t dyld_get_program_min_bridge_os_version()
257 log_apis("dyld_get_program_min_bridge_os_version()\n");
259 __block
uint32_t retval
= 0;
260 __block
bool versionFound
= false;
261 dyld3::dyld_get_image_versions_internal(gAllImages
.mainExecutable(), ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
262 if (versionFound
) return;
264 if (dyld_get_base_platform(platform
) == PLATFORM_BRIDGEOS
) {
266 retval
= min_version
;
274 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
275 // specified binary was built against.
277 // First looks for LC_VERSION_MIN_* in binary and if sdk field is
278 // not zero, return that value.
279 // Otherwise, looks for the libSystem.B.dylib the binary linked
280 // against and uses a table to convert that to an sdk version.
282 uint32_t dyld_get_sdk_version(const mach_header
* mh
)
284 log_apis("dyld_get_sdk_version(%p)\n", mh
);
285 __block
bool versionFound
= false;
286 __block
uint32_t retval
= 0;
287 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
288 if (versionFound
) return;
290 if (platform
== ::dyld_get_active_platform()) {
292 switch (dyld3::dyld_get_base_platform(platform
)) {
293 case PLATFORM_BRIDGEOS
: retval
= sdk_version
+ 0x00090000; return;
294 case PLATFORM_WATCHOS
: retval
= sdk_version
+ 0x00070000; return;
295 default: retval
= sdk_version
; return;
297 } else if (platform
== PLATFORM_IOSSIMULATOR
&& ::dyld_get_active_platform() == PLATFORM_IOSMAC
) {
307 uint32_t dyld_get_program_sdk_version()
309 log_apis("dyld_get_program_sdk_version()\n");
310 static uint32_t sProgramSDKVersion
= 0;
311 if (sProgramSDKVersion
== 0) {
312 sProgramSDKVersion
= dyld3::dyld_get_sdk_version(gAllImages
.mainExecutable());
314 return sProgramSDKVersion
;
317 uint32_t dyld_get_min_os_version(const mach_header
* mh
)
319 log_apis("dyld_get_min_os_version(%p)\n", mh
);
320 __block
bool versionFound
= false;
321 __block
uint32_t retval
= 0;
322 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
323 if (versionFound
) return;
325 if (platform
== ::dyld_get_active_platform()) {
327 switch (dyld3::dyld_get_base_platform(platform
)) {
328 case PLATFORM_BRIDGEOS
: retval
= min_version
+ 0x00090000; return;
329 case PLATFORM_WATCHOS
: retval
= min_version
+ 0x00070000; return;
330 default: retval
= min_version
; return;
332 } else if (platform
== PLATFORM_IOSSIMULATOR
&& ::dyld_get_active_platform() == PLATFORM_IOSMAC
) {
342 dyld_platform_t
dyld_get_active_platform(void) {
343 return gAllImages
.platform();
346 dyld_platform_t
dyld_get_base_platform(dyld_platform_t platform
) {
348 case PLATFORM_IOSMAC
: return PLATFORM_IOS
;
349 case PLATFORM_IOSSIMULATOR
: return PLATFORM_IOS
;
350 case PLATFORM_WATCHOSSIMULATOR
: return PLATFORM_WATCHOS
;
351 case PLATFORM_TVOSSIMULATOR
: return PLATFORM_TVOS
;
352 default: return platform
;
356 bool dyld_is_simulator_platform(dyld_platform_t platform
) {
358 case PLATFORM_IOSSIMULATOR
:
359 case PLATFORM_WATCHOSSIMULATOR
:
360 case PLATFORM_TVOSSIMULATOR
:
367 bool dyld_sdk_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
&& sdk_version
>= version
.version
) {
377 bool dyld_minos_at_least(const struct mach_header
* mh
, dyld_build_version_t version
) {
378 __block
bool retval
= false;
379 dyld3::dyld_get_image_versions(mh
, ^(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
) {
380 if (dyld3::dyld_get_base_platform(platform
) == version
.platform
&& min_version
>= version
.version
) {
387 bool dyld_program_sdk_at_least(dyld_build_version_t version
) {
388 return dyld3::dyld_sdk_at_least(gAllImages
.mainExecutable(), version
);
391 bool dyld_program_minos_at_least(dyld_build_version_t version
) {
392 return dyld3::dyld_minos_at_least(gAllImages
.mainExecutable(), version
);
396 uint32_t linkedDylibVersion(const mach_header
* mh
, const char *installname
) {
397 __block
uint32_t retval
= 0;
398 ((MachOLoaded
*)mh
)->forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
399 if (strcmp(loadPath
, installname
) == 0) {
400 retval
= currentVersion
;
407 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
409 static uint32_t deriveVersionFromDylibs(const struct mach_header
* mh
) {
410 // This is a binary without a version load command, we need to infer things
411 struct DylibToOSMapping
{
412 uint32_t dylibVersion
;
415 uint32_t linkedVersion
= 0;
417 linkedVersion
= linkedDylibVersion(mh
, "/usr/lib/libSystem.B.dylib");
418 static const DylibToOSMapping versionMapping
[] = {
419 { PACKED_VERSION(88,1,3), 0x000A0400 },
420 { PACKED_VERSION(111,0,0), 0x000A0500 },
421 { PACKED_VERSION(123,0,0), 0x000A0600 },
422 { PACKED_VERSION(159,0,0), 0x000A0700 },
423 { PACKED_VERSION(169,3,0), 0x000A0800 },
424 { PACKED_VERSION(1197,0,0), 0x000A0900 },
425 { PACKED_VERSION(0,0,0), 0x000A0900 }
426 // We don't need to expand this table because all recent
427 // binaries have LC_VERSION_MIN_ load command.
430 linkedVersion
= linkedDylibVersion(mh
, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation");
431 static const DylibToOSMapping versionMapping
[] = {
432 { PACKED_VERSION(678,24,0), 0x00020000 },
433 { PACKED_VERSION(678,26,0), 0x00020100 },
434 { PACKED_VERSION(678,29,0), 0x00020200 },
435 { PACKED_VERSION(678,47,0), 0x00030000 },
436 { PACKED_VERSION(678,51,0), 0x00030100 },
437 { PACKED_VERSION(678,60,0), 0x00030200 },
438 { PACKED_VERSION(751,32,0), 0x00040000 },
439 { PACKED_VERSION(751,37,0), 0x00040100 },
440 { PACKED_VERSION(751,49,0), 0x00040200 },
441 { PACKED_VERSION(751,58,0), 0x00040300 },
442 { PACKED_VERSION(881,0,0), 0x00050000 },
443 { PACKED_VERSION(890,1,0), 0x00050100 },
444 { PACKED_VERSION(992,0,0), 0x00060000 },
445 { PACKED_VERSION(993,0,0), 0x00060100 },
446 { PACKED_VERSION(1038,14,0),0x00070000 },
447 { PACKED_VERSION(0,0,0), 0x00070000 }
448 // We don't need to expand this table because all recent
449 // binaries have LC_VERSION_MIN_ load command.
452 static const DylibToOSMapping versionMapping
[] = {};
454 if ( linkedVersion
!= 0 ) {
455 uint32_t lastOsVersion
= 0;
456 for (const DylibToOSMapping
* p
=versionMapping
; ; ++p
) {
457 if ( p
->dylibVersion
== 0 ) {
460 if ( linkedVersion
< p
->dylibVersion
) {
461 return lastOsVersion
;
463 lastOsVersion
= p
->osVersion
;
469 // assumes mh has already been validated
470 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
))
472 const MachOFile
* mf
= (MachOFile
*)mh
;
473 __block
bool lcFound
= false;
474 mf
->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
476 // If SDK field is empty then derive the value from library linkages
478 sdk
= deriveVersionFromDylibs(mh
);
480 callback((const dyld_platform_t
)platform
, sdk
, minOS
);
483 // No load command was found, so again, fallback to deriving it from library linkages
486 #if __x86_64__ || __x86__
487 dyld_platform_t platform
= PLATFORM_IOSSIMULATOR
;
489 dyld_platform_t platform
= PLATFORM_IOS
;
492 dyld_platform_t platform
= PLATFORM_MACOS
;
494 dyld_platform_t platform
= 0;
496 uint32_t derivedVersion
= deriveVersionFromDylibs(mh
);
497 if ( platform
!= 0 && derivedVersion
!= 0 ) {
498 callback(platform
, derivedVersion
, 0);
503 void dyld_get_image_versions(const struct mach_header
* mh
, void (^callback
)(dyld_platform_t platform
, uint32_t sdk_version
, uint32_t min_version
))
506 const MachOFile
* mf
= (MachOFile
*)mh
;
507 if ( mf
->isMachO(diag
, mh
->sizeofcmds
+ sizeof(mach_header_64
)) )
508 dyld_get_image_versions_internal(mh
, callback
);
511 uint32_t dyld_get_program_min_os_version()
513 log_apis("dyld_get_program_min_os_version()\n");
514 static uint32_t sProgramMinVersion
= 0;
515 if (sProgramMinVersion
== 0) {
516 sProgramMinVersion
= dyld3::dyld_get_min_os_version(gAllImages
.mainExecutable());
518 return sProgramMinVersion
;
521 bool _dyld_get_image_uuid(const mach_header
* mh
, uuid_t uuid
)
523 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh
, uuid
);
525 const MachOFile
* mf
= (MachOFile
*)mh
;
526 if ( !mf
->hasMachOMagic() )
529 return mf
->getUuid(uuid
);
533 // _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
534 // should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
535 // and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
536 // to the size required.
538 int _NSGetExecutablePath(char* buf
, uint32_t* bufsize
)
540 log_apis("_NSGetExecutablePath(%p, %p)\n", buf
, bufsize
);
542 const closure::Image
* mainImage
= gAllImages
.mainExecutableImage();
543 const char* path
= gAllImages
.imagePath(mainImage
);
544 size_t pathSize
= strlen(path
) + 1;
545 if ( *bufsize
>= pathSize
) {
549 *bufsize
= (uint32_t)pathSize
;
553 void _dyld_register_func_for_add_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
555 log_apis("_dyld_register_func_for_add_image(%p)\n", func
);
557 gAllImages
.addLoadNotifier(func
);
560 void _dyld_register_func_for_remove_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
562 log_apis("_dyld_register_func_for_remove_image(%p)\n", func
);
564 gAllImages
.addUnloadNotifier(func
);
567 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped
,
568 _dyld_objc_notify_init init
,
569 _dyld_objc_notify_unmapped unmapped
)
571 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped
, init
, unmapped
);
573 gAllImages
.setObjCNotifiers(mapped
, init
, unmapped
);
577 const mach_header
* dyld_image_header_containing_address(const void* addr
)
579 log_apis("dyld_image_header_containing_address(%p)\n", addr
);
581 addr
= stripPointer(addr
);
583 const MachOLoaded
* ml
;
584 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, nullptr) )
591 const char* dyld_image_path_containing_address(const void* addr
)
593 log_apis("dyld_image_path_containing_address(%p)\n", addr
);
595 addr
= stripPointer(addr
);
596 const char* result
= gAllImages
.pathForImageMappedAt(addr
);
598 log_apis(" dyld_image_path_containing_address() => %s\n", result
);
604 bool _dyld_is_memory_immutable(const void* addr
, size_t length
)
606 return gAllImages
.immutableMemory(addr
, length
);
610 int dladdr(const void* addr
, Dl_info
* info
)
612 log_apis("dladdr(%p, %p)\n", addr
, info
);
614 // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
618 addr
= stripPointer(addr
);
620 __block
int result
= 0;
621 const MachOLoaded
* ml
= nullptr;
622 const char* path
= nullptr;
623 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, &path
) ) {
624 info
->dli_fname
= path
;
625 info
->dli_fbase
= (void*)ml
;
628 if ( addr
== info
->dli_fbase
) {
629 // special case lookup of header
630 info
->dli_sname
= "__dso_handle";
631 info
->dli_saddr
= info
->dli_fbase
;
633 else if ( ml
->findClosestSymbol((long)addr
, &(info
->dli_sname
), &symbolAddr
) ) {
634 info
->dli_saddr
= (void*)(long)symbolAddr
;
635 // never return the mach_header symbol
636 if ( info
->dli_saddr
== info
->dli_fbase
) {
637 info
->dli_sname
= nullptr;
638 info
->dli_saddr
= nullptr;
640 // strip off leading underscore
641 else if ( (info
->dli_sname
!= nullptr) && (info
->dli_sname
[0] == '_') ) {
642 info
->dli_sname
= info
->dli_sname
+ 1;
646 info
->dli_sname
= nullptr;
647 info
->dli_saddr
= nullptr;
653 log_apis(" dladdr() => 0\n");
655 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info
->dli_fname
, info
->dli_fbase
, info
->dli_sname
, info
->dli_saddr
);
660 struct PerThreadErrorMessage
662 size_t sizeAllocated
;
667 static pthread_key_t
dlerror_perThreadKey()
669 static dispatch_once_t onceToken
;
670 static pthread_key_t dlerrorPThreadKey
;
671 dispatch_once(&onceToken
, ^{
672 pthread_key_create(&dlerrorPThreadKey
, &free
);
674 return dlerrorPThreadKey
;
677 static void clearErrorString()
679 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
680 if ( errorBuffer
!= nullptr )
681 errorBuffer
->valid
= false;
684 __attribute__((format(printf
, 1, 2)))
685 static void setErrorString(const char* format
, ...)
687 _SIMPLE_STRING buf
= _simple_salloc();
688 if ( buf
!= nullptr ) {
690 va_start(list
, format
);
691 _simple_vsprintf(buf
, format
, list
);
693 size_t strLen
= strlen(_simple_string(buf
)) + 1;
694 size_t sizeNeeded
= sizeof(PerThreadErrorMessage
) + strLen
;
695 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
696 if ( errorBuffer
!= nullptr ) {
697 if ( errorBuffer
->sizeAllocated
< sizeNeeded
) {
699 errorBuffer
= nullptr;
702 if ( errorBuffer
== nullptr ) {
703 size_t allocSize
= std::max(sizeNeeded
, (size_t)256);
704 PerThreadErrorMessage
* p
= (PerThreadErrorMessage
*)malloc(allocSize
);
705 p
->sizeAllocated
= allocSize
;
707 pthread_setspecific(dlerror_perThreadKey(), p
);
710 strcpy(errorBuffer
->message
, _simple_string(buf
));
711 errorBuffer
->valid
= true;
718 log_apis("dlerror()\n");
720 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
721 if ( errorBuffer
!= nullptr ) {
722 if ( errorBuffer
->valid
) {
723 // you can only call dlerror() once, then the message is cleared
724 errorBuffer
->valid
= false;
725 return errorBuffer
->message
;
732 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
734 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
738 static void* makeDlHandle(const mach_header
* mh
, bool dontContinue
)
740 uintptr_t flags
= (dontContinue
? 1 : 0);
741 return (void*)((((uintptr_t)mh
) >> 5) | flags
);
745 void parseDlHandle(void* h
, const MachOLoaded
** mh
, bool* dontContinue
)
747 *dontContinue
= (((uintptr_t)h
) & 1);
748 *mh
= (const MachOLoaded
*)((((uintptr_t)h
) & (-2)) << 5);
751 int dlclose(void* handle
)
753 DYLD_LOAD_LOCK_THIS_BLOCK
754 log_apis("dlclose(%p)\n", handle
);
756 // silently accept magic handles for main executable
757 if ( handle
== RTLD_MAIN_ONLY
)
759 if ( handle
== RTLD_DEFAULT
)
762 const MachOLoaded
* mh
;
764 parseDlHandle(handle
, &mh
, &dontContinue
);
766 __block
bool unloadable
= false;
767 __block
bool validHandle
= false;
768 gAllImages
.infoForImageMappedAt(mh
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
770 if ( !foundImage
.image()->neverUnload() )
774 gAllImages
.decRefCount(mh
); // removes image if reference count went to zero
782 setErrorString("invalid handle passed to dlclose()");
788 void* dlopen_internal(const char* path
, int mode
, void* callerAddress
)
790 DYLD_LOAD_LOCK_THIS_BLOCK
791 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path
==NULL
) ? "NULL" : path
), mode
);
795 // passing NULL for path means return magic object
796 if ( path
== NULL
) {
797 // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
798 if ( (mode
& RTLD_FIRST
) != 0 )
799 return RTLD_MAIN_ONLY
;
804 const char* leafName
= strrchr(path
, '/');
805 if ( leafName
!= nullptr )
810 #if __IPHONE_OS_VERSION_MIN_REQUIRED
811 // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
812 char canonicalPath
[PATH_MAX
];
813 if ( leafName
!= path
) {
814 // make path canonical if it contains a // or ./
815 if ( (strstr(path
, "//") != NULL
) || (strstr(path
, "./") != NULL
) ) {
816 const char* lastSlash
= strrchr(path
, '/');
817 char dirPath
[PATH_MAX
];
818 if ( strlcpy(dirPath
, path
, sizeof(dirPath
)) < sizeof(dirPath
) ) {
819 dirPath
[lastSlash
-path
] = '\0';
820 if ( realpath(dirPath
, canonicalPath
) ) {
821 strlcat(canonicalPath
, "/", sizeof(canonicalPath
));
822 if ( strlcat(canonicalPath
, lastSlash
+1, sizeof(canonicalPath
)) < sizeof(canonicalPath
) ) {
823 // if all fit in buffer, use new canonical path
824 path
= canonicalPath
;
832 // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
833 const bool firstOnly
= (mode
& RTLD_FIRST
);
835 // 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
836 const bool rtldLocal
= (mode
& RTLD_LOCAL
);
838 // RTLD_NODELETE means don't unmap image during dlclose(). Leave the memory mapped, but orphan (leak) it.
839 // Note: this is a weird state and it slightly different semantics that other OSs
840 const bool rtldNoDelete
= (mode
& RTLD_NODELETE
);
842 // RTLD_NOLOAD means do nothing if image not already loaded
843 const bool rtldNoLoad
= (mode
& RTLD_NOLOAD
);
845 // try to load image from specified path
847 const mach_header
* topLoadAddress
= gAllImages
.dlopen(diag
, path
, rtldNoLoad
, rtldLocal
, rtldNoDelete
, false, callerAddress
);
848 if ( diag
.hasError() ) {
849 setErrorString("dlopen(%s, 0x%04X): %s", path
, mode
, diag
.errorMessage());
850 log_apis(" dlopen: closure creation error: %s\n", diag
.errorMessage());
853 if ( topLoadAddress
== nullptr ) {
854 log_apis(" dlopen(%s) => NULL\n", leafName
);
857 void* result
= makeDlHandle(topLoadAddress
, firstOnly
);
858 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
863 bool dlopen_preflight_internal(const char* path
)
865 DYLD_LOAD_LOCK_THIS_BLOCK
866 log_apis("dlopen_preflight(%s)\n", path
);
868 // check if path is in dyld shared cache
869 if ( gAllImages
.dyldCacheHasPath(path
) )
872 // check if file is loadable
874 closure::FileSystemPhysical fileSystem
;
875 closure::LoadedFileInfo loadedFileInfo
= MachOAnalyzer::load(diag
, fileSystem
, path
, MachOFile::currentArchName(), MachOFile::currentPlatform());
876 if ( loadedFileInfo
.fileContent
!= nullptr ) {
877 fileSystem
.unloadFile(loadedFileInfo
);
881 // FIXME: may be symlink to something in dyld cache
886 static void* dlsym_search(const char* symName
, const LoadedImage
& start
, bool searchStartImage
, MachOLoaded::DependentToMachOLoaded reExportHelper
,
887 bool* resultPointsToInstructions
)
889 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
890 return gAllImages
.findDependent(mh
, depIndex
);
892 //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
894 // walk all dependents of 'start' in order looking for symbol
895 __block
void* result
= nullptr;
896 gAllImages
.visitDependentsTopDown(start
, ^(const LoadedImage
& aLoadedImage
, bool& stop
) {
897 //fprintf(stderr, " search: %s\n", aLoadedImage.image()->path());
898 if ( !searchStartImage
&& aLoadedImage
.image() == start
.image() )
900 if ( aLoadedImage
.loadedAddress()->hasExportedSymbol(symName
, finder
, &result
, resultPointsToInstructions
) ) {
909 void* dlsym_internal(void* handle
, const char* symbolName
, void* callerAddress
)
911 log_apis("dlsym(%p, \"%s\")\n", handle
, symbolName
);
915 MachOLoaded::DependentToMachOLoaded finder
= ^(const MachOLoaded
* mh
, uint32_t depIndex
) {
916 return gAllImages
.findDependent(mh
, depIndex
);
919 // dlsym() assumes symbolName passed in is same as in C source code
920 // dyld assumes all symbol names have an underscore prefix
921 BLOCK_ACCCESSIBLE_ARRAY(char, underscoredName
, strlen(symbolName
)+2);
922 underscoredName
[0] = '_';
923 strcpy(&underscoredName
[1], symbolName
);
925 __block
void* result
= nullptr;
926 __block
bool resultPointsToInstructions
= false;
927 if ( handle
== RTLD_DEFAULT
) {
928 // magic "search all in load order" handle
929 gAllImages
.forEachImage(^(const LoadedImage
& loadedImage
, bool& stop
) {
930 if ( loadedImage
.hideFromFlatSearch() )
932 if ( loadedImage
.loadedAddress()->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
) ) {
936 if ( result
!= nullptr ) {
937 #if __has_feature(ptrauth_calls)
938 if (resultPointsToInstructions
)
939 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
941 log_apis(" dlsym() => %p\n", result
);
944 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName
);
945 log_apis(" dlsym() => NULL\n");
948 else if ( handle
== RTLD_MAIN_ONLY
) {
949 // magic "search only main executable" handle
950 if ( gAllImages
.mainExecutable()->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
) ) {
951 log_apis(" dlsym() => %p\n", result
);
952 #if __has_feature(ptrauth_calls)
953 if (resultPointsToInstructions
)
954 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
958 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName
);
959 log_apis(" dlsym() => NULL\n");
962 // rest of cases search in dependency order
963 if ( handle
== RTLD_NEXT
) {
964 // magic "search what I would see" handle
965 __block
bool foundCaller
= false;
966 gAllImages
.infoForImageMappedAt(callerAddress
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
968 result
= dlsym_search(underscoredName
, foundImage
, false, finder
, &resultPointsToInstructions
);
970 if ( !foundCaller
) {
971 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
975 else if ( handle
== RTLD_SELF
) {
976 // magic "search me, then what I would see" handle
977 __block
bool foundCaller
= false;
978 gAllImages
.infoForImageMappedAt(callerAddress
, ^(const LoadedImage
& foundImage
, uint8_t permissions
) {
980 result
= dlsym_search(underscoredName
, foundImage
, true, finder
, &resultPointsToInstructions
);
982 if ( !foundCaller
) {
983 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
988 // handle value was something returned by dlopen()
989 const MachOLoaded
* mh
;
991 parseDlHandle(handle
, &mh
, &dontContinue
);
993 __block
bool foundCaller
= false;
994 gAllImages
.infoForImageWithLoadAddress(mh
, ^(const LoadedImage
& foundImage
) {
996 if ( dontContinue
) {
997 // RTLD_FIRST only searches one place
998 // we go through infoForImageWithLoadAddress() to validate the handle
999 mh
->hasExportedSymbol(underscoredName
, finder
, &result
, &resultPointsToInstructions
);
1002 result
= dlsym_search(underscoredName
, foundImage
, true, finder
, &resultPointsToInstructions
);
1005 if ( !foundCaller
) {
1006 setErrorString("dlsym(%p, %s): invalid handle", handle
, symbolName
);
1007 log_apis(" dlsym() => NULL\n");
1012 if ( result
!= nullptr ) {
1013 #if __has_feature(ptrauth_calls)
1014 if (resultPointsToInstructions
)
1015 result
= __builtin_ptrauth_sign_unauthenticated(result
, ptrauth_key_asia
, 0);
1017 log_apis(" dlsym() => %p\n", result
);
1021 setErrorString("dlsym(%p, %s): symbol not found", handle
, symbolName
);
1022 log_apis(" dlsym() => NULL\n");
1027 const struct dyld_all_image_infos
* _dyld_get_all_image_infos()
1029 return gAllImages
.oldAllImageInfo();
1032 bool dyld_shared_cache_some_image_overridden()
1034 log_apis("dyld_shared_cache_some_image_overridden()\n");
1036 assert(0 && "not implemented yet");
1039 bool _dyld_get_shared_cache_uuid(uuid_t uuid
)
1041 log_apis("_dyld_get_shared_cache_uuid()\n");
1043 if ( gAllImages
.oldAllImageInfo() != nullptr ) {
1044 memcpy(uuid
, gAllImages
.oldAllImageInfo()->sharedCacheUUID
, sizeof(uuid_t
));
1050 const void* _dyld_get_shared_cache_range(size_t* mappedSize
)
1052 log_apis("_dyld_get_shared_cache_range()\n");
1054 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1055 if ( sharedCache
!= nullptr ) {
1056 *mappedSize
= (size_t)sharedCache
->mappedSize();
1063 void _dyld_images_for_addresses(unsigned count
, const void* addresses
[], dyld_image_uuid_offset infos
[])
1065 log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count
, addresses
, infos
);
1067 // in stack crawls, common for contiguous fames to be in same image, so cache
1068 // last lookup and check if next addresss in in there before doing full search
1069 const MachOLoaded
* ml
= nullptr;
1070 uint64_t textSize
= 0;
1071 const void* end
= (void*)ml
;
1072 for (unsigned i
=0; i
< count
; ++i
) {
1073 const void* addr
= stripPointer(addresses
[i
]);
1074 bzero(&infos
[i
], sizeof(dyld_image_uuid_offset
));
1075 if ( (ml
== nullptr) || (addr
< (void*)ml
) || (addr
> end
) ) {
1076 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, &textSize
, nullptr) ) {
1077 end
= (void*)((uint8_t*)ml
+ textSize
);
1084 if ( ml
!= nullptr ) {
1085 infos
[i
].image
= ml
;
1086 infos
[i
].offsetInImage
= (uintptr_t)addr
- (uintptr_t)ml
;
1087 ml
->getUuid(infos
[i
].uuid
);
1092 void _dyld_register_for_image_loads(void (*func
)(const mach_header
* mh
, const char* path
, bool unloadable
))
1094 gAllImages
.addLoadNotifier(func
);
1097 bool _dyld_find_unwind_sections(void* addr
, dyld_unwind_sections
* info
)
1099 log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr
, info
);
1100 addr
= (void*)stripPointer(addr
);
1102 const MachOLoaded
* ml
= nullptr;
1103 if ( gAllImages
.infoForImageMappedAt(addr
, &ml
, nullptr, nullptr) ) {
1105 info
->dwarf_section
= nullptr;
1106 info
->dwarf_section_length
= 0;
1107 info
->compact_unwind_section
= nullptr;
1108 info
->compact_unwind_section_length
= 0;
1111 if ( const void* content
= ml
->findSectionContent("__TEXT", "__eh_frame", size
) ) {
1112 info
->dwarf_section
= content
;
1113 info
->dwarf_section_length
= (uintptr_t)size
;
1115 if ( const void* content
= ml
->findSectionContent("__TEXT", "__unwind_info", size
) ) {
1116 info
->compact_unwind_section
= content
;
1117 info
->compact_unwind_section_length
= (uintptr_t)size
;
1126 bool dyld_process_is_restricted()
1128 log_apis("dyld_process_is_restricted()\n");
1129 return gAllImages
.isRestricted();
1133 const char* dyld_shared_cache_file_path()
1135 log_apis("dyld_shared_cache_file_path()\n");
1137 return gAllImages
.dyldCachePath();
1141 void dyld_dynamic_interpose(const mach_header
* mh
, const dyld_interpose_tuple array
[], size_t count
)
1143 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh
, array
, count
);
1148 static void* mapStartOfCache(const char* path
, size_t length
)
1150 struct stat statbuf
;
1151 if ( ::stat(path
, &statbuf
) == -1 )
1154 if ( statbuf
.st_size
< length
)
1157 int cache_fd
= ::open(path
, O_RDONLY
);
1161 void* result
= ::mmap(NULL
, length
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
1164 if ( result
== MAP_FAILED
)
1170 static const DyldSharedCache
* findCacheInDirAndMap(const uuid_t cacheUuid
, const char* dirPath
, size_t& sizeMapped
)
1172 DIR* dirp
= ::opendir(dirPath
);
1173 if ( dirp
!= NULL
) {
1175 dirent
* entp
= NULL
;
1176 char cachePath
[PATH_MAX
];
1177 while ( ::readdir_r(dirp
, &entry
, &entp
) == 0 ) {
1180 if ( entp
->d_type
!= DT_REG
)
1182 if ( strlcpy(cachePath
, dirPath
, PATH_MAX
) >= PATH_MAX
)
1184 if ( strlcat(cachePath
, "/", PATH_MAX
) >= PATH_MAX
)
1186 if ( strlcat(cachePath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1188 if ( const DyldSharedCache
* cache
= (DyldSharedCache
*)mapStartOfCache(cachePath
, 0x00100000) ) {
1190 cache
->getUUID(foundUuid
);
1191 if ( ::memcmp(foundUuid
, cacheUuid
, 16) != 0 ) {
1192 // wrong uuid, unmap and keep looking
1193 ::munmap((void*)cache
, 0x00100000);
1198 sizeMapped
= 0x00100000;
1208 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid
, const char* extraSearchDirs
[], void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1210 log_apis("dyld_shared_cache_find_iterate_text()\n");
1212 // see if requested cache is the active one in this process
1213 size_t sizeMapped
= 0;
1214 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1215 if ( sharedCache
!= nullptr ) {
1217 sharedCache
->getUUID(runningUuid
);
1218 if ( ::memcmp(runningUuid
, cacheUuid
, 16) != 0 )
1219 sharedCache
= nullptr;
1221 if ( sharedCache
== nullptr ) {
1222 // if not, look in default location for cache files
1223 #if __IPHONE_OS_VERSION_MIN_REQUIRED
1224 const char* defaultSearchDir
= IPHONE_DYLD_SHARED_CACHE_DIR
;
1226 const char* defaultSearchDir
= MACOSX_DYLD_SHARED_CACHE_DIR
;
1228 sharedCache
= findCacheInDirAndMap(cacheUuid
, defaultSearchDir
, sizeMapped
);
1229 // if not there, look in extra search locations
1230 if ( sharedCache
== nullptr ) {
1231 for (const char** p
= extraSearchDirs
; *p
!= nullptr; ++p
) {
1232 sharedCache
= findCacheInDirAndMap(cacheUuid
, *p
, sizeMapped
);
1233 if ( sharedCache
!= nullptr )
1238 if ( sharedCache
== nullptr )
1241 // get base address of cache
1242 __block
uint64_t cacheUnslidBaseAddress
= 0;
1243 sharedCache
->forEachRegion(^(const void *content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
1244 if ( cacheUnslidBaseAddress
== 0 )
1245 cacheUnslidBaseAddress
= vmAddr
;
1248 // iterate all images
1249 sharedCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
, bool& stop
) {
1250 dyld_shared_cache_dylib_text_info dylibTextInfo
;
1251 dylibTextInfo
.version
= 2;
1252 dylibTextInfo
.loadAddressUnslid
= loadAddressUnslid
;
1253 dylibTextInfo
.textSegmentSize
= textSegmentSize
;
1254 dylibTextInfo
.path
= installName
;
1255 ::memcpy(dylibTextInfo
.dylibUuid
, dylibUUID
, 16);
1256 dylibTextInfo
.textSegmentOffset
= loadAddressUnslid
- cacheUnslidBaseAddress
;
1257 callback(&dylibTextInfo
);
1260 if ( sizeMapped
!= 0 )
1261 ::munmap((void*)sharedCache
, sizeMapped
);
1266 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid
, void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1268 log_apis("dyld_shared_cache_iterate_text()\n");
1270 const char* extraSearchDirs
[] = { NULL
};
1271 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid
, extraSearchDirs
, callback
);
1276 } // namespace dyld3