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@
28 #include <sys/errno.h>
33 #include <TargetConditionals.h>
34 #include <CommonCrypto/CommonDigest.h>
35 #include <dispatch/dispatch.h>
40 #include "dyld_priv.h"
42 #include "AllImages.h"
43 #include "MachOParser.h"
46 #include "Diagnostics.h"
47 #include "DyldSharedCache.h"
48 #include "PathOverrides.h"
50 #include "StringUtils.h"
55 #include "closuredProtocol.h"
60 extern dyld_all_image_infos dyld_all_image_infos
;
67 uint32_t _dyld_image_count(void)
69 log_apis("_dyld_image_count()\n");
71 return gAllImages
.count();
74 const mach_header
* _dyld_get_image_header(uint32_t imageIndex
)
76 log_apis("_dyld_get_image_header(%d)\n", imageIndex
);
78 const mach_header
* loadAddress
;
79 launch_cache::Image image
= gAllImages
.findByLoadOrder(imageIndex
, &loadAddress
);
85 intptr_t _dyld_get_image_slide(const mach_header
* mh
)
87 log_apis("_dyld_get_image_slide(%p)\n", mh
);
89 MachOParser
parser(mh
);
90 return parser
.getSlide();
93 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex
)
95 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex
);
97 const mach_header
* mh
= _dyld_get_image_header(imageIndex
);
99 return dyld3::_dyld_get_image_slide(mh
);
103 const char* _dyld_get_image_name(uint32_t imageIndex
)
105 log_apis("_dyld_get_image_name(%d)\n", imageIndex
);
107 const mach_header
* loadAddress
;
108 launch_cache::Image image
= gAllImages
.findByLoadOrder(imageIndex
, &loadAddress
);
110 return gAllImages
.imagePath(image
.binaryData());
115 static bool nameMatch(const char* installName
, const char* libraryName
)
117 const char* leafName
= strrchr(installName
, '/');
118 if ( leafName
== NULL
)
119 leafName
= installName
;
123 // -framework case is exact match of leaf name
124 if ( strcmp(leafName
, libraryName
) == 0 )
127 // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
128 size_t leafNameLen
= strlen(leafName
);
129 size_t libraryNameLen
= strlen(libraryName
);
130 if ( leafNameLen
< (libraryNameLen
+9) )
132 if ( strncmp(leafName
, "lib", 3) != 0 )
134 if ( strcmp(&leafName
[leafNameLen
-6], ".dylib") != 0 )
136 if ( strncmp(&leafName
[3], libraryName
, libraryNameLen
) != 0 )
138 return (leafName
[libraryNameLen
+3] == '.');
143 // BETTER, USE: dyld_get_program_sdk_version()
145 // Scans the main executable and returns the version of the specified dylib the program was built against.
147 // The library to find is the leaf name that would have been passed to linker tool
148 // (e.g. -lfoo or -framework foo would use "foo").
150 // Returns -1 if the main executable did not link against the specified library, or is malformed.
152 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName
)
154 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName
);
156 __block
int32_t result
= -1;
157 MachOParser
parser(gAllImages
.mainExecutable());
158 parser
.forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
159 if ( nameMatch(loadPath
, libraryName
) )
160 result
= currentVersion
;
162 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result
);
168 // Searches loaded images for the requested dylib and returns its current version.
170 // The library to find is the leaf name that would have been passed to linker tool
171 // (e.g. -lfoo or -framework foo would use "foo").
173 // If the specified library is not loaded, -1 is returned.
175 int32_t NSVersionOfRunTimeLibrary(const char* libraryName
)
177 log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName
);
179 uint32_t count
= gAllImages
.count();
180 for (uint32_t i
=0; i
< count
; ++i
) {
181 const mach_header
* loadAddress
;
182 launch_cache::Image image
= gAllImages
.findByLoadOrder(i
, &loadAddress
);
183 if ( image
.valid() ) {
184 MachOParser
parser(loadAddress
);
185 const char* installName
;
186 uint32_t currentVersion
;
187 uint32_t compatVersion
;
188 if ( parser
.getDylibInstallName(&installName
, &compatVersion
, ¤tVersion
) && nameMatch(installName
, libraryName
) ) {
189 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", currentVersion
);
190 return currentVersion
;
194 log_apis(" NSVersionOfRunTimeLibrary() => -1\n");
199 #if __WATCH_OS_VERSION_MIN_REQUIRED
201 static uint32_t watchVersToIOSVers(uint32_t vers
)
203 return vers
+ 0x00070000;
206 uint32_t dyld_get_program_sdk_watch_os_version()
208 log_apis("dyld_get_program_sdk_watch_os_version()\n");
214 MachOParser
parser(gAllImages
.mainExecutable());
215 if ( parser
.getPlatformAndVersion(&platform
, &minOS
, &sdk
) ) {
216 if ( platform
== Platform::watchOS
)
222 uint32_t dyld_get_program_min_watch_os_version()
224 log_apis("dyld_get_program_min_watch_os_version()\n");
230 MachOParser
parser(gAllImages
.mainExecutable());
231 if ( parser
.getPlatformAndVersion(&platform
, &minOS
, &sdk
) ) {
232 if ( platform
== Platform::watchOS
)
233 return minOS
; // return raw minOS (not mapped to iOS version)
242 static uint32_t bridgeVersToIOSVers(uint32_t vers
)
244 return vers
+ 0x00090000;
247 uint32_t dyld_get_program_sdk_bridge_os_version()
249 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
255 MachOParser
parser(gAllImages
.mainExecutable());
256 if ( parser
.getPlatformAndVersion(&platform
, &minOS
, &sdk
) ) {
257 if ( platform
== Platform::bridgeOS
)
263 uint32_t dyld_get_program_min_bridge_os_version()
265 log_apis("dyld_get_program_min_bridge_os_version()\n");
271 MachOParser
parser(gAllImages
.mainExecutable());
272 if ( parser
.getPlatformAndVersion(&platform
, &minOS
, &sdk
) ) {
273 if ( platform
== Platform::bridgeOS
)
274 return minOS
; // return raw minOS (not mapped to iOS version)
282 #if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE
284 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
286 static uint32_t deriveSDKVersFromDylibs(const mach_header
* mh
)
288 __block
uint32_t foundationVers
= 0;
289 __block
uint32_t libSystemVers
= 0;
290 MachOParser
parser(mh
);
291 parser
.forEachDependentDylib(^(const char* loadPath
, bool, bool, bool, uint32_t compatVersion
, uint32_t currentVersion
, bool& stop
) {
292 if ( strcmp(loadPath
, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") == 0 )
293 foundationVers
= currentVersion
;
294 else if ( strcmp(loadPath
, "/usr/lib/libSystem.B.dylib") == 0 )
295 libSystemVers
= currentVersion
;
298 struct DylibToOSMapping
{
299 uint32_t dylibVersion
;
303 #if __IPHONE_OS_VERSION_MIN_REQUIRED
304 static const DylibToOSMapping foundationMapping
[] = {
305 { PACKED_VERSION(678,24,0), 0x00020000 },
306 { PACKED_VERSION(678,26,0), 0x00020100 },
307 { PACKED_VERSION(678,29,0), 0x00020200 },
308 { PACKED_VERSION(678,47,0), 0x00030000 },
309 { PACKED_VERSION(678,51,0), 0x00030100 },
310 { PACKED_VERSION(678,60,0), 0x00030200 },
311 { PACKED_VERSION(751,32,0), 0x00040000 },
312 { PACKED_VERSION(751,37,0), 0x00040100 },
313 { PACKED_VERSION(751,49,0), 0x00040200 },
314 { PACKED_VERSION(751,58,0), 0x00040300 },
315 { PACKED_VERSION(881,0,0), 0x00050000 },
316 { PACKED_VERSION(890,1,0), 0x00050100 },
317 { PACKED_VERSION(992,0,0), 0x00060000 },
318 { PACKED_VERSION(993,0,0), 0x00060100 },
319 { PACKED_VERSION(1038,14,0),0x00070000 },
320 { PACKED_VERSION(0,0,0), 0x00070000 }
321 // We don't need to expand this table because all recent
322 // binaries have LC_VERSION_MIN_ load command.
325 if ( foundationVers
!= 0 ) {
326 uint32_t lastOsVersion
= 0;
327 for (const DylibToOSMapping
* p
=foundationMapping
; ; ++p
) {
328 if ( p
->dylibVersion
== 0 )
330 if ( foundationVers
< p
->dylibVersion
)
331 return lastOsVersion
;
332 lastOsVersion
= p
->osVersion
;
337 // Note: versions are for the GM release. The last entry should
338 // always be zero. At the start of the next major version,
339 // a new last entry needs to be added and the previous zero
340 // updated to the GM dylib version.
341 static const DylibToOSMapping libSystemMapping
[] = {
342 { PACKED_VERSION(88,1,3), 0x000A0400 },
343 { PACKED_VERSION(111,0,0), 0x000A0500 },
344 { PACKED_VERSION(123,0,0), 0x000A0600 },
345 { PACKED_VERSION(159,0,0), 0x000A0700 },
346 { PACKED_VERSION(169,3,0), 0x000A0800 },
347 { PACKED_VERSION(1197,0,0), 0x000A0900 },
348 { PACKED_VERSION(0,0,0), 0x000A0900 }
349 // We don't need to expand this table because all recent
350 // binaries have LC_VERSION_MIN_ load command.
353 if ( libSystemVers
!= 0 ) {
354 uint32_t lastOsVersion
= 0;
355 for (const DylibToOSMapping
* p
=libSystemMapping
; ; ++p
) {
356 if ( p
->dylibVersion
== 0 )
358 if ( libSystemVers
< p
->dylibVersion
)
359 return lastOsVersion
;
360 lastOsVersion
= p
->osVersion
;
370 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
371 // specified binary was built against.
373 // First looks for LC_VERSION_MIN_* in binary and if sdk field is
374 // not zero, return that value.
375 // Otherwise, looks for the libSystem.B.dylib the binary linked
376 // against and uses a table to convert that to an sdk version.
378 uint32_t dyld_get_sdk_version(const mach_header
* mh
)
380 log_apis("dyld_get_sdk_version(%p)\n", mh
);
386 if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh
) )
388 MachOParser
parser(mh
);
389 if ( parser
.getPlatformAndVersion(&platform
, &minOS
, &sdk
) ) {
392 case Platform::bridgeOS
:
393 // new binary. sdk version looks like "2.0" but API wants "11.0"
394 return bridgeVersToIOSVers(sdk
);
396 // old binary. sdk matches API semantics so can return directly.
398 #elif __WATCH_OS_VERSION_MIN_REQUIRED
399 case Platform::watchOS
:
400 // new binary. sdk version looks like "2.0" but API wants "9.0"
401 return watchVersToIOSVers(sdk
);
403 // old binary. sdk matches API semantics so can return directly.
405 #elif __TV_OS_VERSION_MIN_REQUIRED
409 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
411 if ( sdk
!= 0 ) // old binaries might not have SDK set
415 case Platform::macOS
:
416 if ( sdk
!= 0 ) // old binaries might not have SDK set
421 // wrong binary for this platform
426 #if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE
427 // All watchOS and tvOS binaries should have version load command.
430 // MacOSX and iOS have old binaries without version load commmand.
431 return deriveSDKVersFromDylibs(mh
);
435 uint32_t dyld_get_program_sdk_version()
437 log_apis("dyld_get_program_sdk_version()\n");
439 return dyld3::dyld_get_sdk_version(gAllImages
.mainExecutable());
442 uint32_t dyld_get_min_os_version(const mach_header
* mh
)
444 log_apis("dyld_get_min_os_version(%p)\n", mh
);
450 if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh
) )
452 MachOParser
parser(mh
);
453 if ( parser
.getPlatformAndVersion(&platform
, &minOS
, &sdk
) ) {
456 case Platform::bridgeOS
:
457 // new binary. sdk version looks like "2.0" but API wants "11.0"
458 return bridgeVersToIOSVers(minOS
);
460 // old binary. sdk matches API semantics so can return directly.
462 #elif __WATCH_OS_VERSION_MIN_REQUIRED
463 case Platform::watchOS
:
464 // new binary. OS version looks like "2.0" but API wants "9.0"
465 return watchVersToIOSVers(minOS
);
467 // old binary. OS matches API semantics so can return directly.
469 #elif __TV_OS_VERSION_MIN_REQUIRED
473 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
477 case Platform::macOS
:
481 // wrong binary for this platform
489 uint32_t dyld_get_program_min_os_version()
491 log_apis("dyld_get_program_min_os_version()\n");
493 return dyld3::dyld_get_min_os_version(gAllImages
.mainExecutable());
497 bool _dyld_get_image_uuid(const mach_header
* mh
, uuid_t uuid
)
499 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh
, uuid
);
501 if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh
) )
503 MachOParser
parser(mh
);
504 return parser
.getUuid(uuid
);
508 // _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
509 // should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
510 // and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
511 // to the size required.
513 int _NSGetExecutablePath(char* buf
, uint32_t* bufsize
)
515 log_apis("_NSGetExecutablePath(%p, %p)\n", buf
, bufsize
);
517 launch_cache::Image image
= gAllImages
.mainExecutableImage();
518 if ( image
.valid() ) {
519 const char* path
= gAllImages
.imagePath(image
.binaryData());
520 size_t pathSize
= strlen(path
) + 1;
521 if ( *bufsize
>= pathSize
) {
525 *bufsize
= (uint32_t)pathSize
;
530 void _dyld_register_func_for_add_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
532 log_apis("_dyld_register_func_for_add_image(%p)\n", func
);
534 gAllImages
.addLoadNotifier(func
);
537 void _dyld_register_func_for_remove_image(void (*func
)(const mach_header
*mh
, intptr_t vmaddr_slide
))
539 log_apis("_dyld_register_func_for_remove_image(%p)\n", func
);
541 gAllImages
.addUnloadNotifier(func
);
544 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped
,
545 _dyld_objc_notify_init init
,
546 _dyld_objc_notify_unmapped unmapped
)
548 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped
, init
, unmapped
);
550 gAllImages
.setObjCNotifiers(mapped
, init
, unmapped
);
554 const mach_header
* dyld_image_header_containing_address(const void* addr
)
556 log_apis("dyld_image_header_containing_address(%p)\n", addr
);
558 const mach_header
* loadAddress
;
559 launch_cache::Image image
= gAllImages
.findByOwnedAddress(addr
, &loadAddress
);
566 const char* dyld_image_path_containing_address(const void* addr
)
568 log_apis("dyld_image_path_containing_address(%p)\n", addr
);
570 const mach_header
* loadAddress
;
571 launch_cache::Image image
= gAllImages
.findByOwnedAddress(addr
, &loadAddress
);
572 if ( image
.valid() ) {
573 const char* path
= gAllImages
.imagePath(image
.binaryData());
574 log_apis(" dyld_image_path_containing_address() => %s\n", path
);
577 log_apis(" dyld_image_path_containing_address() => NULL\n");
581 bool _dyld_is_memory_immutable(const void* addr
, size_t length
)
583 uintptr_t checkStart
= (uintptr_t)addr
;
584 uintptr_t checkEnd
= checkStart
+ length
;
586 // quick check to see if in r/o region of shared cache. If so return true.
587 const DyldSharedCache
* cache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
588 if ( cache
!= nullptr ) {
589 __block
bool firstVMAddr
= 0;
590 __block
bool isReadOnlyInCache
= false;
591 __block
bool isInCache
= false;
592 cache
->forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
593 if ( firstVMAddr
== 0 )
594 firstVMAddr
= vmAddr
;
595 uintptr_t regionStart
= (uintptr_t)cache
+ (uintptr_t)(vmAddr
- firstVMAddr
);
596 uintptr_t regionEnd
= regionStart
+ (uintptr_t)size
;
597 if ( (regionStart
< checkStart
) && (checkEnd
< regionEnd
) ) {
599 isReadOnlyInCache
= ((permissions
& VM_PROT_WRITE
) != 0);
603 return isReadOnlyInCache
;
606 // go slow route of looking at each image's segments
607 const mach_header
* loadAddress
;
609 launch_cache::Image image
= gAllImages
.findByOwnedAddress(addr
, &loadAddress
, &permissions
);
610 if ( !image
.valid() )
612 if ( (permissions
& VM_PROT_WRITE
) != 0 )
614 return !gAllImages
.imageUnloadable(image
, loadAddress
);
618 int dladdr(const void* addr
, Dl_info
* info
)
620 log_apis("dladdr(%p, %p)\n", addr
, info
);
622 const mach_header
* loadAddress
;
623 launch_cache::Image image
= gAllImages
.findByOwnedAddress(addr
, &loadAddress
);
624 if ( !image
.valid() ) {
625 log_apis(" dladdr() => 0\n");
628 MachOParser
parser(loadAddress
);
629 info
->dli_fname
= gAllImages
.imagePath(image
.binaryData());
630 info
->dli_fbase
= (void*)(loadAddress
);
631 if ( addr
== info
->dli_fbase
) {
632 // special case lookup of header
633 info
->dli_sname
= "__dso_handle";
634 info
->dli_saddr
= info
->dli_fbase
;
636 else if ( parser
.findClosestSymbol(addr
, &(info
->dli_sname
), (const void**)&(info
->dli_saddr
)) ) {
637 // never return the mach_header symbol
638 if ( info
->dli_saddr
== info
->dli_fbase
) {
639 info
->dli_sname
= nullptr;
640 info
->dli_saddr
= nullptr;
642 // strip off leading underscore
643 else if ( (info
->dli_sname
!= nullptr) && (info
->dli_sname
[0] == '_') ) {
644 info
->dli_sname
= info
->dli_sname
+ 1;
648 info
->dli_sname
= nullptr;
649 info
->dli_saddr
= nullptr;
651 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info
->dli_fname
, info
->dli_fbase
, info
->dli_sname
, info
->dli_saddr
);
656 struct PerThreadErrorMessage
658 size_t sizeAllocated
;
663 static pthread_key_t
dlerror_perThreadKey()
665 static dispatch_once_t onceToken
;
666 static pthread_key_t dlerrorPThreadKey
;
667 dispatch_once(&onceToken
, ^{
668 pthread_key_create(&dlerrorPThreadKey
, &free
);
670 return dlerrorPThreadKey
;
673 static void clearErrorString()
675 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
676 if ( errorBuffer
!= nullptr )
677 errorBuffer
->valid
= false;
680 __attribute__((format(printf
, 1, 2)))
681 static void setErrorString(const char* format
, ...)
683 _SIMPLE_STRING buf
= _simple_salloc();
684 if ( buf
!= nullptr ) {
686 va_start(list
, format
);
687 _simple_vsprintf(buf
, format
, list
);
689 size_t strLen
= strlen(_simple_string(buf
)) + 1;
690 size_t sizeNeeded
= sizeof(PerThreadErrorMessage
) + strLen
;
691 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
692 if ( errorBuffer
!= nullptr ) {
693 if ( errorBuffer
->sizeAllocated
< sizeNeeded
) {
695 errorBuffer
= nullptr;
698 if ( errorBuffer
== nullptr ) {
699 size_t allocSize
= std::max(sizeNeeded
, (size_t)256);
700 PerThreadErrorMessage
* p
= (PerThreadErrorMessage
*)malloc(allocSize
);
701 p
->sizeAllocated
= allocSize
;
703 pthread_setspecific(dlerror_perThreadKey(), p
);
706 strcpy(errorBuffer
->message
, _simple_string(buf
));
707 errorBuffer
->valid
= true;
714 log_apis("dlerror()\n");
716 PerThreadErrorMessage
* errorBuffer
= (PerThreadErrorMessage
*)pthread_getspecific(dlerror_perThreadKey());
717 if ( errorBuffer
!= nullptr ) {
718 if ( errorBuffer
->valid
) {
719 // you can only call dlerror() once, then the message is cleared
720 errorBuffer
->valid
= false;
721 return errorBuffer
->message
;
728 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
730 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
734 class VIS_HIDDEN RecursiveAutoLock
737 RecursiveAutoLock() {
738 pthread_mutex_lock(&_sMutex
);
740 ~RecursiveAutoLock() {
741 pthread_mutex_unlock(&_sMutex
);
744 static pthread_mutex_t _sMutex
;
747 pthread_mutex_t
RecursiveAutoLock::_sMutex
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER
;
749 static void* makeDlHandle(const mach_header
* mh
, bool dontContinue
)
751 uintptr_t flags
= (dontContinue
? 1 : 0);
752 return (void*)((((uintptr_t)mh
) >> 5) | flags
);
756 void parseDlHandle(void* h
, const mach_header
** mh
, bool* dontContinue
)
758 *dontContinue
= (((uintptr_t)h
) & 1);
759 *mh
= (const mach_header
*)((((uintptr_t)h
) & (-2)) << 5);
762 int dlclose(void* handle
)
764 log_apis("dlclose(%p)\n", handle
);
766 // silently accept magic handles for main executable
767 if ( handle
== RTLD_MAIN_ONLY
)
769 if ( handle
== RTLD_DEFAULT
)
772 // from here on, serialize all dlopen()s
773 RecursiveAutoLock dlopenSerializer
;
775 const mach_header
* mh
;
777 parseDlHandle(handle
, &mh
, &dontContinue
);
778 launch_cache::Image image
= gAllImages
.findByLoadAddress(mh
);
779 if ( image
.valid() ) {
780 // removes image if reference count went to zero
781 if ( !image
.neverUnload() )
782 gAllImages
.decRefCount(mh
);
787 setErrorString("invalid handle passed to dlclose()");
795 const mach_header
* loadImageAndDependents(Diagnostics
& diag
, const launch_cache::binary_format::Image
* imageToLoad
, bool bumpDlopenCount
)
797 launch_cache::Image
topImage(imageToLoad
);
798 uint32_t maxLoad
= topImage
.maxLoadCount();
799 // first construct array of all BinImage* objects that dlopen'ed image depends on
800 const dyld3::launch_cache::binary_format::Image
* fullImageList
[maxLoad
];
801 dyld3::launch_cache::SlowLoadSet
imageSet(&fullImageList
[0], &fullImageList
[maxLoad
]);
802 imageSet
.add(imageToLoad
);
803 STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData
*, gAllImages
.currentGroupsCount(), currentGroupsList
);
804 gAllImages
.copyCurrentGroups(currentGroupsList
);
805 if ( !topImage
.recurseAllDependentImages(currentGroupsList
, imageSet
, nullptr) ) {
806 diag
.error("unexpected > %d images loaded", maxLoad
);
810 // build array of BinImage* that are not already loaded
811 const dyld3::launch_cache::binary_format::Image
* toLoadImageList
[maxLoad
];
812 const dyld3::launch_cache::binary_format::Image
** toLoadImageArray
= toLoadImageList
;
813 __block
int needToLoadCount
= 0;
814 imageSet
.forEach(^(const dyld3::launch_cache::binary_format::Image
* aBinImage
) {
815 if ( gAllImages
.findLoadAddressByImage(aBinImage
) == nullptr )
816 toLoadImageArray
[needToLoadCount
++] = aBinImage
;
818 assert(needToLoadCount
> 0);
820 // build one array of all existing and to-be-loaded images
821 uint32_t alreadyLoadImageCount
= gAllImages
.count();
822 STACK_ALLOC_DYNARRAY(loader::ImageInfo
, alreadyLoadImageCount
+ needToLoadCount
, allImages
);
823 loader::ImageInfo
* allImagesArray
= &allImages
[0];
824 gAllImages
.forEachImage(^(uint32_t imageIndex
, const mach_header
* loadAddress
, const launch_cache::Image image
, bool& stop
) {
825 launch_cache::ImageGroup grp
= image
.group();
826 loader::ImageInfo
& info
= allImagesArray
[imageIndex
];
827 info
.imageData
= image
.binaryData();
828 info
.loadAddress
= loadAddress
;
829 info
.groupNum
= grp
.groupNum();
830 info
.indexInGroup
= grp
.indexInGroup(info
.imageData
);
831 info
.previouslyFixedUp
= true;
832 info
.justMapped
= false;
833 info
.justUsedFromDyldCache
= false;
834 info
.neverUnload
= false;
836 for (int i
=0; i
< needToLoadCount
; ++i
) {
837 launch_cache::Image
img(toLoadImageArray
[i
]);
838 launch_cache::ImageGroup grp
= img
.group();
839 loader::ImageInfo
& info
= allImages
[alreadyLoadImageCount
+i
];
840 info
.imageData
= toLoadImageArray
[i
];
841 info
.loadAddress
= nullptr;
842 info
.groupNum
= grp
.groupNum();
843 info
.indexInGroup
= grp
.indexInGroup(img
.binaryData());
844 info
.previouslyFixedUp
= false;
845 info
.justMapped
= false;
846 info
.justUsedFromDyldCache
= false;
847 info
.neverUnload
= false;
850 // map new images and apply all fixups
851 mapAndFixupImages(diag
, allImages
, (const uint8_t*)gAllImages
.cacheLoadAddress(), &dyld3::log_loads
, &dyld3::log_segments
, &dyld3::log_fixups
, &dyld3::log_dofs
);
852 if ( diag
.hasError() )
854 const mach_header
* topLoadAddress
= allImages
[alreadyLoadImageCount
].loadAddress
;
856 // bump dlopen refcount of image directly loaded
857 if ( bumpDlopenCount
)
858 gAllImages
.incRefCount(topLoadAddress
);
860 // tell gAllImages about new images
861 dyld3::launch_cache::DynArray
<loader::ImageInfo
> newImages(needToLoadCount
, &allImages
[alreadyLoadImageCount
]);
862 gAllImages
.addImages(newImages
);
864 // tell gAllImages about any old images which now have never unload set
865 for (int i
=0; i
< alreadyLoadImageCount
; ++i
) {
866 if (allImages
[i
].neverUnload
&& !allImages
[i
].imageData
->neverUnload
)
867 gAllImages
.setNeverUnload(allImages
[i
]);
871 gAllImages
.runInitialzersBottomUp(topLoadAddress
);
873 return topLoadAddress
;
877 void* dlopen(const char* path
, int mode
)
879 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path
==NULL
) ? "NULL" : path
), mode
);
883 // passing NULL for path means return magic object
884 if ( path
== NULL
) {
885 // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
886 if ( (mode
& RTLD_FIRST
) != 0 )
887 return RTLD_MAIN_ONLY
;
892 // from here on, serialize all dlopen()s
893 RecursiveAutoLock dlopenSerializer
;
895 const char* leafName
= strrchr(path
, '/');
896 if ( leafName
!= nullptr )
901 // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
902 bool dontContinue
= (mode
& RTLD_FIRST
);
903 bool bumpRefCount
= true;
905 // check if dylib with same inode/mtime is already loaded
906 __block
const mach_header
* alreadyLoadMH
= nullptr;
908 if ( stat(path
, &statBuf
) == 0 ) {
909 alreadyLoadMH
= gAllImages
.alreadyLoaded(statBuf
.st_ino
, statBuf
.st_mtime
, bumpRefCount
);
910 if ( alreadyLoadMH
!= nullptr) {
911 log_apis(" dlopen: path inode/mtime matches already loaded image\n");
912 void* result
= makeDlHandle(alreadyLoadMH
, dontContinue
);
913 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
918 // check if already loaded, and if so, just bump ref-count
919 gPathOverrides
.forEachPathVariant(path
, ^(const char* possiblePath
, bool& stop
) {
920 alreadyLoadMH
= gAllImages
.alreadyLoaded(possiblePath
, bumpRefCount
);
921 if ( alreadyLoadMH
!= nullptr ) {
922 log_apis(" dlopen: matches already loaded image %s\n", possiblePath
);
926 if ( alreadyLoadMH
!= nullptr) {
927 void* result
= makeDlHandle(alreadyLoadMH
, dontContinue
);
928 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
932 // it may be that the path supplied is a symlink to something already loaded
933 char resolvedPath
[PATH_MAX
];
934 const char* realPathResult
= realpath(path
, resolvedPath
);
935 // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
936 bool checkRealPathToo
= ((realPathResult
!= nullptr) || (errno
== ENOENT
)) && (strcmp(path
, resolvedPath
) != 0);
937 if ( checkRealPathToo
) {
938 alreadyLoadMH
= gAllImages
.alreadyLoaded(resolvedPath
, bumpRefCount
);
939 log_apis(" dlopen: real path=%s\n", resolvedPath
);
940 if ( alreadyLoadMH
!= nullptr) {
941 void* result
= makeDlHandle(alreadyLoadMH
, dontContinue
);
942 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
947 // check if image is in a known ImageGroup
948 __block
const launch_cache::binary_format::Image
* imageToLoad
= nullptr;
949 gPathOverrides
.forEachPathVariant(path
, ^(const char* possiblePath
, bool& stop
) {
950 log_apis(" dlopen: checking for pre-built closure for path: %s\n", possiblePath
);
951 imageToLoad
= gAllImages
.findImageInKnownGroups(possiblePath
);
952 if ( imageToLoad
!= nullptr )
955 if ( (imageToLoad
== nullptr) && checkRealPathToo
) {
956 gPathOverrides
.forEachPathVariant(resolvedPath
, ^(const char* possiblePath
, bool& stop
) {
957 log_apis(" dlopen: checking for pre-built closure for real path: %s\n", possiblePath
);
958 imageToLoad
= gAllImages
.findImageInKnownGroups(possiblePath
);
959 if ( imageToLoad
!= nullptr )
964 // check if image from a known ImageGroup is already loaded (via a different path)
965 if ( imageToLoad
!= nullptr ) {
966 alreadyLoadMH
= gAllImages
.alreadyLoaded(imageToLoad
, bumpRefCount
);
967 if ( alreadyLoadMH
!= nullptr) {
968 void* result
= makeDlHandle(alreadyLoadMH
, dontContinue
);
969 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
974 // RTLD_NOLOAD means do nothing if image not already loaded
975 if ( mode
& RTLD_NOLOAD
) {
976 log_apis(" dlopen(%s) => NULL\n", leafName
);
980 // if we have a closure, optimistically use it. If out of date, it will fail
981 if ( imageToLoad
!= nullptr ) {
982 log_apis(" dlopen: trying existing closure image=%p\n", imageToLoad
);
984 const mach_header
* topLoadAddress
= loadImageAndDependents(diag
, imageToLoad
, true);
985 if ( diag
.noError() ) {
986 void* result
= makeDlHandle(topLoadAddress
, dontContinue
);
987 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
990 // image is no longer valid, will need to build one
991 imageToLoad
= nullptr;
992 log_apis(" dlopen: existing closure no longer valid\n");
995 // if no existing closure, RPC to closured to create one
996 const char* closuredErrorMessages
[3];
997 int closuredErrorMessagesCount
= 0;
998 if ( imageToLoad
== nullptr ) {
999 imageToLoad
= gAllImages
.messageClosured(path
, "dlopen", closuredErrorMessages
, closuredErrorMessagesCount
);
1002 // load images using new closure
1003 if ( imageToLoad
!= nullptr ) {
1004 log_apis(" dlopen: using closured built image=%p\n", imageToLoad
);
1006 const mach_header
* topLoadAddress
= loadImageAndDependents(diag
, imageToLoad
, true);
1007 if ( diag
.noError() ) {
1008 void* result
= makeDlHandle(topLoadAddress
, dontContinue
);
1009 log_apis(" dlopen(%s) => %p\n", leafName
, result
);
1012 if ( closuredErrorMessagesCount
< 3 ) {
1013 closuredErrorMessages
[closuredErrorMessagesCount
++] = strdup(diag
.errorMessage());
1017 // otherwise, closured failed to build needed load info
1018 switch ( closuredErrorMessagesCount
) {
1020 setErrorString("dlopen(%s, 0x%04X): closured error", path
, mode
);
1021 log_apis(" dlopen: closured error\n");
1024 setErrorString("dlopen(%s, 0x%04X): %s", path
, mode
, closuredErrorMessages
[0]);
1025 log_apis(" dlopen: closured error: %s\n", closuredErrorMessages
[0]);
1028 setErrorString("dlopen(%s, 0x%04X): %s %s", path
, mode
, closuredErrorMessages
[0], closuredErrorMessages
[1]);
1029 log_apis(" dlopen: closured error: %s %s\n", closuredErrorMessages
[0], closuredErrorMessages
[1]);
1032 setErrorString("dlopen(%s, 0x%04X): %s %s %s", path
, mode
, closuredErrorMessages
[0], closuredErrorMessages
[1], closuredErrorMessages
[2]);
1033 log_apis(" dlopen: closured error: %s %s %s\n", closuredErrorMessages
[0], closuredErrorMessages
[1], closuredErrorMessages
[2]);
1036 for (int i
=0; i
< closuredErrorMessagesCount
;++i
)
1037 free((void*)closuredErrorMessages
[i
]);
1039 log_apis(" dlopen(%s) => NULL\n", leafName
);
1044 bool dlopen_preflight(const char* path
)
1046 log_apis("dlopen_preflight(%s)\n", path
);
1048 if ( gAllImages
.alreadyLoaded(path
, false) != nullptr )
1051 if ( gAllImages
.findImageInKnownGroups(path
) != nullptr )
1055 struct stat statBuf
;
1056 if ( ::stat(path
, &statBuf
) != 0 )
1058 int fd
= ::open(path
, O_RDONLY
);
1061 const void* fileBuffer
= ::mmap(NULL
, (size_t)statBuf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
1063 if ( fileBuffer
== MAP_FAILED
)
1065 size_t mappedSize
= (size_t)statBuf
.st_size
;
1067 // check if it is current arch mach-o or fat with slice for current arch
1068 __block
bool result
= false;
1069 __block Diagnostics diag
;
1070 if ( MachOParser::isMachO(diag
, fileBuffer
, mappedSize
) ) {
1074 if ( FatUtil::isFatFile(fileBuffer
) ) {
1075 FatUtil::forEachSlice(diag
, fileBuffer
, mappedSize
, ^(uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, const void* sliceStart
, size_t sliceSz
, bool& stop
) {
1076 if ( MachOParser::isMachO(diag
, sliceStart
, sliceSz
) ) {
1083 ::munmap((void*)fileBuffer
, mappedSize
);
1085 // FIXME: may be symlink to something in dyld cache
1087 // FIXME: maybe ask closured
1092 static void* dlsym_search(const char* symName
, const mach_header
* startImageLoadAddress
, const launch_cache::Image
& startImage
, bool searchStartImage
, MachOParser::DependentFinder reExportFollower
)
1094 // construct array of all BinImage* objects that dlopen'ed image depends on
1095 uint32_t maxLoad
= startImage
.maxLoadCount();
1096 const dyld3::launch_cache::binary_format::Image
* fullImageList
[maxLoad
];
1097 dyld3::launch_cache::SlowLoadSet
imageSet(&fullImageList
[0], &fullImageList
[maxLoad
]);
1098 imageSet
.add(startImage
.binaryData());
1099 STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData
*, gAllImages
.currentGroupsCount(), currentGroupsList
);
1100 gAllImages
.copyCurrentGroups(currentGroupsList
);
1102 __block
void* result
= nullptr;
1103 auto handler
= ^(const dyld3::launch_cache::binary_format::Image
* aBinImage
, bool& stop
) {
1104 const mach_header
* loadAddress
= gAllImages
.findLoadAddressByImage(aBinImage
);
1105 if ( !searchStartImage
&& (loadAddress
== startImageLoadAddress
) )
1107 if ( loadAddress
!= nullptr ) {
1108 MachOParser
parser(loadAddress
);
1109 if ( parser
.hasExportedSymbol(symName
, reExportFollower
, &result
) ) {
1116 handler(startImage
.binaryData(), stop
);
1120 // check each dependent image for symbol
1121 if ( !startImage
.recurseAllDependentImages(currentGroupsList
, imageSet
, handler
) ) {
1122 setErrorString("unexpected > %d images loaded", maxLoad
);
1128 void* dlsym(void* handle
, const char* symbolName
)
1130 log_apis("dlsym(%p, \"%s\")\n", handle
, symbolName
);
1134 // dlsym() assumes symbolName passed in is same as in C source code
1135 // dyld assumes all symbol names have an underscore prefix
1136 char underscoredName
[strlen(symbolName
)+2];
1137 underscoredName
[0] = '_';
1138 strcpy(&underscoredName
[1], symbolName
);
1140 // this block is only used if hasExportedSymbol() needs to trace re-exported dylibs to find a symbol
1141 MachOParser::DependentFinder reExportFollower
= ^(uint32_t targetDepIndex
, const char* depLoadPath
, void* extra
, const mach_header
** foundMH
, void** foundExtra
) {
1142 if ( (strncmp(depLoadPath
, "@rpath/", 7) == 0) && (extra
!= nullptr) ) {
1143 const mach_header
* parentMH
= (mach_header
*)extra
;
1144 launch_cache::Image parentImage
= gAllImages
.findByLoadAddress(parentMH
);
1145 if ( parentImage
.valid() ) {
1146 STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData
*, gAllImages
.currentGroupsCount(), currentGroupsList
);
1147 gAllImages
.copyCurrentGroups(currentGroupsList
);
1148 parentImage
.forEachDependentImage(currentGroupsList
, ^(uint32_t parentDepIndex
, dyld3::launch_cache::Image parentDepImage
, dyld3::launch_cache::Image::LinkKind kind
, bool &stop
) {
1149 if ( parentDepIndex
!= targetDepIndex
)
1151 const mach_header
* parentDepMH
= gAllImages
.findLoadAddressByImage(parentDepImage
.binaryData());
1152 if ( parentDepMH
!= nullptr ) {
1153 *foundMH
= parentDepMH
;
1160 *foundMH
= gAllImages
.alreadyLoaded(depLoadPath
, false);
1162 return (*foundMH
!= nullptr);
1165 if ( handle
== RTLD_DEFAULT
) {
1166 // magic "search all in load order" handle
1167 for (uint32_t index
=0; index
< gAllImages
.count(); ++index
) {
1168 const mach_header
* loadAddress
;
1169 launch_cache::Image image
= gAllImages
.findByLoadOrder(index
, &loadAddress
);
1170 if ( image
.valid() ) {
1171 MachOParser
parser(loadAddress
);
1173 //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
1174 if ( parser
.hasExportedSymbol(underscoredName
, reExportFollower
, &result
) ) {
1175 log_apis(" dlsym() => %p\n", result
);
1180 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName
);
1181 log_apis(" dlsym() => NULL\n");
1184 else if ( handle
== RTLD_MAIN_ONLY
) {
1185 // magic "search only main executable" handle
1186 MachOParser
parser(gAllImages
.mainExecutable());
1187 //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
1189 if ( parser
.hasExportedSymbol(underscoredName
, reExportFollower
, &result
) ) {
1190 log_apis(" dlsym() => %p\n", result
);
1193 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName
);
1194 log_apis(" dlsym() => NULL\n");
1198 // rest of cases search in dependency order
1199 const mach_header
* startImageLoadAddress
;
1200 launch_cache::Image
startImage(nullptr);
1201 void* result
= nullptr;
1202 if ( handle
== RTLD_NEXT
) {
1203 // magic "search what I would see" handle
1204 void* callerAddress
= __builtin_return_address(0);
1205 startImage
= gAllImages
.findByOwnedAddress(callerAddress
, &startImageLoadAddress
);
1206 if ( ! startImage
.valid() ) {
1207 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
1210 result
= dlsym_search(underscoredName
, startImageLoadAddress
, startImage
, false, reExportFollower
);
1212 else if ( handle
== RTLD_SELF
) {
1213 // magic "search me, then what I would see" handle
1214 void* callerAddress
= __builtin_return_address(0);
1215 startImage
= gAllImages
.findByOwnedAddress(callerAddress
, &startImageLoadAddress
);
1216 if ( ! startImage
.valid() ) {
1217 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName
, callerAddress
);
1220 result
= dlsym_search(underscoredName
, startImageLoadAddress
, startImage
, true, reExportFollower
);
1223 // handle value was something returned by dlopen()
1225 parseDlHandle(handle
, &startImageLoadAddress
, &dontContinue
);
1226 startImage
= gAllImages
.findByLoadAddress(startImageLoadAddress
);
1227 if ( !startImage
.valid() ) {
1228 setErrorString("dlsym(%p, %s): invalid handle", handle
, symbolName
);
1229 log_apis(" dlsym() => NULL\n");
1232 if ( dontContinue
) {
1233 // RTLD_FIRST only searches one place
1234 MachOParser
parser(startImageLoadAddress
);
1235 parser
.hasExportedSymbol(underscoredName
, reExportFollower
, &result
);
1238 result
= dlsym_search(underscoredName
, startImageLoadAddress
, startImage
, true, reExportFollower
);
1242 if ( result
!= nullptr ) {
1243 log_apis(" dlsym() => %p\n", result
);
1247 setErrorString("dlsym(%p, %s): symbol not found", handle
, symbolName
);
1248 log_apis(" dlsym() => NULL\n");
1253 const struct dyld_all_image_infos
* _dyld_get_all_image_infos()
1255 return gAllImages
.oldAllImageInfo();
1258 bool dyld_shared_cache_some_image_overridden()
1260 log_apis("dyld_shared_cache_some_image_overridden()\n");
1262 assert(0 && "not implemented yet");
1265 bool _dyld_get_shared_cache_uuid(uuid_t uuid
)
1267 log_apis("_dyld_get_shared_cache_uuid()\n");
1269 if ( gAllImages
.oldAllImageInfo() != nullptr ) {
1270 memcpy(uuid
, gAllImages
.oldAllImageInfo()->sharedCacheUUID
, sizeof(uuid_t
));
1276 const void* _dyld_get_shared_cache_range(size_t* mappedSize
)
1278 log_apis("_dyld_get_shared_cache_range()\n");
1280 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1281 if ( sharedCache
!= nullptr ) {
1282 *mappedSize
= (size_t)sharedCache
->mappedSize();
1289 bool _dyld_find_unwind_sections(void* addr
, dyld_unwind_sections
* info
)
1291 log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr
, info
);
1293 const mach_header
* mh
= dyld_image_header_containing_address(addr
);
1294 if ( mh
== nullptr )
1298 info
->dwarf_section
= nullptr;
1299 info
->dwarf_section_length
= 0;
1300 info
->compact_unwind_section
= nullptr;
1301 info
->compact_unwind_section_length
= 0;
1303 MachOParser
parser(mh
);
1304 parser
.forEachSection(^(const char* segName
, const char* sectName
, uint32_t flags
, const void* content
, size_t sectSize
, bool illegalSectionSize
, bool& stop
) {
1305 if ( strcmp(segName
, "__TEXT") == 0 ) {
1306 if ( strcmp(sectName
, "__eh_frame") == 0 ) {
1307 info
->dwarf_section
= content
;
1308 info
->dwarf_section_length
= sectSize
;
1310 else if ( strcmp(sectName
, "__unwind_info") == 0 ) {
1311 info
->compact_unwind_section
= content
;
1312 info
->compact_unwind_section_length
= sectSize
;
1321 bool dyld_process_is_restricted()
1323 log_apis("dyld_process_is_restricted()\n");
1325 launch_cache::Closure
closure(gAllImages
.mainClosure());
1326 return closure
.isRestricted();
1330 const char* dyld_shared_cache_file_path()
1332 log_apis("dyld_shared_cache_file_path()\n");
1334 return gAllImages
.dyldCachePath();
1338 void dyld_dynamic_interpose(const mach_header
* mh
, const dyld_interpose_tuple array
[], size_t count
)
1340 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh
, array
, count
);
1345 static void* mapStartOfCache(const char* path
, size_t length
)
1347 struct stat statbuf
;
1348 if ( ::stat(path
, &statbuf
) == -1 )
1351 if ( statbuf
.st_size
< length
)
1354 int cache_fd
= ::open(path
, O_RDONLY
);
1358 void* result
= ::mmap(NULL
, length
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
1361 if ( result
== MAP_FAILED
)
1367 static const DyldSharedCache
* findCacheInDirAndMap(const uuid_t cacheUuid
, const char* dirPath
, size_t& sizeMapped
)
1369 DIR* dirp
= ::opendir(dirPath
);
1370 if ( dirp
!= NULL
) {
1372 dirent
* entp
= NULL
;
1373 char cachePath
[PATH_MAX
];
1374 while ( ::readdir_r(dirp
, &entry
, &entp
) == 0 ) {
1377 if ( entp
->d_type
!= DT_REG
)
1379 if ( strlcpy(cachePath
, dirPath
, PATH_MAX
) >= PATH_MAX
)
1381 if ( strlcat(cachePath
, "/", PATH_MAX
) >= PATH_MAX
)
1383 if ( strlcat(cachePath
, entp
->d_name
, PATH_MAX
) >= PATH_MAX
)
1385 if ( const DyldSharedCache
* cache
= (DyldSharedCache
*)mapStartOfCache(cachePath
, 0x00100000) ) {
1387 cache
->getUUID(foundUuid
);
1388 if ( ::memcmp(foundUuid
, cacheUuid
, 16) != 0 ) {
1389 // wrong uuid, unmap and keep looking
1390 ::munmap((void*)cache
, 0x00100000);
1395 sizeMapped
= 0x00100000;
1405 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid
, const char* extraSearchDirs
[], void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1407 log_apis("dyld_shared_cache_find_iterate_text()\n");
1409 // see if requested cache is the active one in this process
1410 size_t sizeMapped
= 0;
1411 const DyldSharedCache
* sharedCache
= (DyldSharedCache
*)gAllImages
.cacheLoadAddress();
1412 if ( sharedCache
!= nullptr ) {
1414 sharedCache
->getUUID(runningUuid
);
1415 if ( ::memcmp(runningUuid
, cacheUuid
, 16) != 0 )
1416 sharedCache
= nullptr;
1418 if ( sharedCache
== nullptr ) {
1419 // if not, look in default location for cache files
1420 #if __IPHONE_OS_VERSION_MIN_REQUIRED
1421 const char* defaultSearchDir
= IPHONE_DYLD_SHARED_CACHE_DIR
;
1423 const char* defaultSearchDir
= MACOSX_DYLD_SHARED_CACHE_DIR
;
1425 sharedCache
= findCacheInDirAndMap(cacheUuid
, defaultSearchDir
, sizeMapped
);
1426 // if not there, look in extra search locations
1427 if ( sharedCache
== nullptr ) {
1428 for (const char** p
= extraSearchDirs
; *p
!= nullptr; ++p
) {
1429 sharedCache
= findCacheInDirAndMap(cacheUuid
, *p
, sizeMapped
);
1430 if ( sharedCache
!= nullptr )
1435 if ( sharedCache
== nullptr )
1438 // get base address of cache
1439 __block
uint64_t cacheUnslidBaseAddress
= 0;
1440 sharedCache
->forEachRegion(^(const void *content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
1441 if ( cacheUnslidBaseAddress
== 0 )
1442 cacheUnslidBaseAddress
= vmAddr
;
1445 // iterate all images
1446 sharedCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
) {
1447 dyld_shared_cache_dylib_text_info dylibTextInfo
;
1448 dylibTextInfo
.version
= 2;
1449 dylibTextInfo
.loadAddressUnslid
= loadAddressUnslid
;
1450 dylibTextInfo
.textSegmentSize
= textSegmentSize
;
1451 dylibTextInfo
.path
= installName
;
1452 ::memcpy(dylibTextInfo
.dylibUuid
, dylibUUID
, 16);
1453 dylibTextInfo
.textSegmentOffset
= loadAddressUnslid
- cacheUnslidBaseAddress
;
1454 callback(&dylibTextInfo
);
1457 if ( sizeMapped
!= 0 )
1458 ::munmap((void*)sharedCache
, sizeMapped
);
1463 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid
, void (^callback
)(const dyld_shared_cache_dylib_text_info
* info
))
1465 log_apis("dyld_shared_cache_iterate_text()\n");
1467 const char* extraSearchDirs
[] = { NULL
};
1468 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid
, extraSearchDirs
, callback
);
1473 } // namespace dyld3