dyld-625.13.tar.gz
[apple/dyld.git] / dyld3 / APIs.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <string.h>
26 #include <stdint.h>
27 #include <sys/errno.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <dirent.h>
31 #include <fcntl.h>
32 #include <TargetConditionals.h>
33 #include <CommonCrypto/CommonDigest.h>
34 #include <dispatch/dispatch.h>
35 #include <_simple.h>
36
37 #include <array>
38 #include <algorithm>
39
40 #include "dlfcn.h"
41 #include "dyld.h"
42 #include "dyld_priv.h"
43
44 #include "AllImages.h"
45 #include "Loading.h"
46 #include "Logging.h"
47 #include "Diagnostics.h"
48 #include "DyldSharedCache.h"
49 #include "PathOverrides.h"
50 #include "APIs.h"
51 #include "Closure.h"
52 #include "MachOLoaded.h"
53 #include "ClosureBuilder.h"
54 #include "ClosureFileSystemPhysical.h"
55
56 #if __has_feature(ptrauth_calls)
57 #include <ptrauth.h>
58 #endif
59
60
61 namespace dyld {
62 extern dyld_all_image_infos dyld_all_image_infos;
63 }
64
65
66 namespace dyld3 {
67
68
69 static const void *stripPointer(const void *ptr) {
70 #if __has_feature(ptrauth_calls)
71 return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
72 #else
73 return ptr;
74 #endif
75 }
76
77 pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
78
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));
81
82
83 uint32_t _dyld_image_count(void)
84 {
85 log_apis("_dyld_image_count()\n");
86
87 return gAllImages.count();
88 }
89
90 const mach_header* _dyld_get_image_header(uint32_t imageIndex)
91 {
92 log_apis("_dyld_get_image_header(%d)\n", imageIndex);
93 return gAllImages.imageLoadAddressByIndex(imageIndex);
94 }
95
96 intptr_t _dyld_get_image_slide(const mach_header* mh)
97 {
98 log_apis("_dyld_get_image_slide(%p)\n", mh);
99
100 const MachOLoaded* mf = (MachOLoaded*)mh;
101 if ( !mf->hasMachOMagic() )
102 return 0;
103
104 return mf->getSlide();
105 }
106
107 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex)
108 {
109 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex);
110
111 const mach_header* mh = gAllImages.imageLoadAddressByIndex(imageIndex);
112 if ( mh != nullptr )
113 return dyld3::_dyld_get_image_slide(mh);
114 return 0;
115 }
116
117 const char* _dyld_get_image_name(uint32_t imageIndex)
118 {
119 log_apis("_dyld_get_image_name(%d)\n", imageIndex);
120 return gAllImages.imagePathByIndex(imageIndex);
121 }
122
123
124 static bool nameMatch(const char* installName, const char* libraryName)
125 {
126 const char* leafName = strrchr(installName, '/');
127 if ( leafName == NULL )
128 leafName = installName;
129 else
130 leafName++;
131
132 // -framework case is exact match of leaf name
133 if ( strcmp(leafName, libraryName) == 0 )
134 return true;
135
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) )
140 return false;
141 if ( strncmp(leafName, "lib", 3) != 0 )
142 return false;
143 if ( strcmp(&leafName[leafNameLen-6], ".dylib") != 0 )
144 return false;
145 if ( strncmp(&leafName[3], libraryName, libraryNameLen) != 0 )
146 return false;
147 return (leafName[libraryNameLen+3] == '.');
148 }
149
150
151 //
152 // BETTER, USE: dyld_get_program_sdk_version()
153 //
154 // Scans the main executable and returns the version of the specified dylib the program was built against.
155 //
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").
158 //
159 // Returns -1 if the main executable did not link against the specified library, or is malformed.
160 //
161 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
162 {
163 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName);
164
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;
169 });
170 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result);
171 return result;
172 }
173
174
175 //
176 // Searches loaded images for the requested dylib and returns its current version.
177 //
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").
180 //
181 // If the specified library is not loaded, -1 is returned.
182 //
183 int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
184 {
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, &currentVersion) && nameMatch(installName, libraryName) ) {
192 result = currentVersion;
193 stop = true;
194 }
195 });
196 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", result);
197 return result;
198 }
199
200
201 uint32_t dyld_get_program_sdk_watch_os_version()
202 {
203 log_apis("dyld_get_program_sdk_watch_os_version()\n");
204
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;
209
210 if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
211 versionFound = true;
212 retval = sdk_version;
213 }
214 });
215
216 return retval;
217 }
218
219 uint32_t dyld_get_program_min_watch_os_version()
220 {
221 log_apis("dyld_get_program_min_watch_os_version()\n");
222
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;
227
228 if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
229 versionFound = true;
230 retval = min_version;
231 }
232 });
233
234 return retval;
235 }
236
237 uint32_t dyld_get_program_sdk_bridge_os_version()
238 {
239 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
240
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;
245
246 if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
247 versionFound = true;
248 retval = sdk_version;
249 }
250 });
251
252 return retval;
253 }
254
255 uint32_t dyld_get_program_min_bridge_os_version()
256 {
257 log_apis("dyld_get_program_min_bridge_os_version()\n");
258
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;
263
264 if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
265 versionFound = true;
266 retval = min_version;
267 }
268 });
269
270 return retval;
271 }
272
273 //
274 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
275 // specified binary was built against.
276 //
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.
281 //
282 uint32_t dyld_get_sdk_version(const mach_header* mh)
283 {
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;
289
290 if (platform == ::dyld_get_active_platform()) {
291 versionFound = true;
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;
296 }
297 } else if (platform == PLATFORM_IOSSIMULATOR && ::dyld_get_active_platform() == PLATFORM_IOSMAC) {
298 //FIXME bringup hack
299 versionFound = true;
300 retval = 0x000C0000;
301 }
302 });
303
304 return retval;
305 }
306
307 uint32_t dyld_get_program_sdk_version()
308 {
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());
313 }
314 return sProgramSDKVersion;
315 }
316
317 uint32_t dyld_get_min_os_version(const mach_header* mh)
318 {
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;
324
325 if (platform == ::dyld_get_active_platform()) {
326 versionFound = true;
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;
331 }
332 } else if (platform == PLATFORM_IOSSIMULATOR && ::dyld_get_active_platform() == PLATFORM_IOSMAC) {
333 //FIXME bringup hack
334 versionFound = true;
335 retval = 0x000C0000;
336 }
337 });
338
339 return retval;
340 }
341
342 dyld_platform_t dyld_get_active_platform(void) {
343 return gAllImages.platform();
344 }
345
346 dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
347 switch (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;
353 }
354 }
355
356 bool dyld_is_simulator_platform(dyld_platform_t platform) {
357 switch(platform) {
358 case PLATFORM_IOSSIMULATOR:
359 case PLATFORM_WATCHOSSIMULATOR:
360 case PLATFORM_TVOSSIMULATOR:
361 return true;
362 default:
363 return false;
364 }
365 }
366
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) {
371 retval = true;
372 }
373 });
374 return retval;
375 }
376
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) {
381 retval = true;
382 }
383 });
384 return retval;
385 }
386
387 bool dyld_program_sdk_at_least(dyld_build_version_t version) {
388 return dyld3::dyld_sdk_at_least(gAllImages.mainExecutable(), version);
389 }
390
391 bool dyld_program_minos_at_least(dyld_build_version_t version) {
392 return dyld3::dyld_minos_at_least(gAllImages.mainExecutable(), version);
393 }
394
395 static
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;
401 stop = true;
402 }
403 });
404 return retval;
405 }
406
407 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
408
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;
413 uint32_t osVersion;
414 };
415 uint32_t linkedVersion = 0;
416 #if TARGET_OS_OSX
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.
428 };
429 #elif TARGET_OS_IOS
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.
450 };
451 #else
452 static const DylibToOSMapping versionMapping[] = {};
453 #endif
454 if ( linkedVersion != 0 ) {
455 uint32_t lastOsVersion = 0;
456 for (const DylibToOSMapping* p=versionMapping; ; ++p) {
457 if ( p->dylibVersion == 0 ) {
458 return p->osVersion;
459 }
460 if ( linkedVersion < p->dylibVersion ) {
461 return lastOsVersion;
462 }
463 lastOsVersion = p->osVersion;
464 }
465 }
466 return 0;
467 }
468
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))
471 {
472 const MachOFile* mf = (MachOFile*)mh;
473 __block bool lcFound = false;
474 mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
475 lcFound = true;
476 // If SDK field is empty then derive the value from library linkages
477 if (sdk == 0) {
478 sdk = deriveVersionFromDylibs(mh);
479 }
480 callback((const dyld_platform_t)platform, sdk, minOS);
481 });
482
483 // No load command was found, so again, fallback to deriving it from library linkages
484 if (!lcFound) {
485 #if TARGET_OS_IOS
486 #if __x86_64__ || __x86__
487 dyld_platform_t platform = PLATFORM_IOSSIMULATOR;
488 #else
489 dyld_platform_t platform = PLATFORM_IOS;
490 #endif
491 #elif TARGET_OS_OSX
492 dyld_platform_t platform = PLATFORM_MACOS;
493 #else
494 dyld_platform_t platform = 0;
495 #endif
496 uint32_t derivedVersion = deriveVersionFromDylibs(mh);
497 if ( platform != 0 && derivedVersion != 0 ) {
498 callback(platform, derivedVersion, 0);
499 }
500 }
501 }
502
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))
504 {
505 Diagnostics diag;
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);
509 }
510
511 uint32_t dyld_get_program_min_os_version()
512 {
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());
517 }
518 return sProgramMinVersion;
519 }
520
521 bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
522 {
523 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
524
525 const MachOFile* mf = (MachOFile*)mh;
526 if ( !mf->hasMachOMagic() )
527 return false;
528
529 return mf->getUuid(uuid);
530 }
531
532 //
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.
537 //
538 int _NSGetExecutablePath(char* buf, uint32_t* bufsize)
539 {
540 log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
541
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 ) {
546 strcpy(buf, path);
547 return 0;
548 }
549 *bufsize = (uint32_t)pathSize;
550 return -1;
551 }
552
553 void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
554 {
555 log_apis("_dyld_register_func_for_add_image(%p)\n", func);
556
557 gAllImages.addLoadNotifier(func);
558 }
559
560 void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
561 {
562 log_apis("_dyld_register_func_for_remove_image(%p)\n", func);
563
564 gAllImages.addUnloadNotifier(func);
565 }
566
567 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
568 _dyld_objc_notify_init init,
569 _dyld_objc_notify_unmapped unmapped)
570 {
571 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
572
573 gAllImages.setObjCNotifiers(mapped, init, unmapped);
574 }
575
576
577 const mach_header* dyld_image_header_containing_address(const void* addr)
578 {
579 log_apis("dyld_image_header_containing_address(%p)\n", addr);
580
581 addr = stripPointer(addr);
582
583 const MachOLoaded* ml;
584 if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) )
585 return ml;
586
587 return nullptr;
588 }
589
590
591 const char* dyld_image_path_containing_address(const void* addr)
592 {
593 log_apis("dyld_image_path_containing_address(%p)\n", addr);
594
595 addr = stripPointer(addr);
596 const char* result = gAllImages.pathForImageMappedAt(addr);
597
598 log_apis(" dyld_image_path_containing_address() => %s\n", result);
599 return result;
600 }
601
602
603
604 bool _dyld_is_memory_immutable(const void* addr, size_t length)
605 {
606 return gAllImages.immutableMemory(addr, length);
607 }
608
609
610 int dladdr(const void* addr, Dl_info* info)
611 {
612 log_apis("dladdr(%p, %p)\n", addr, info);
613
614 // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
615 if ( info == NULL )
616 return 0; // failure
617
618 addr = stripPointer(addr);
619
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;
626
627 uint64_t symbolAddr;
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;
632 }
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;
639 }
640 // strip off leading underscore
641 else if ( (info->dli_sname != nullptr) && (info->dli_sname[0] == '_') ) {
642 info->dli_sname = info->dli_sname + 1;
643 }
644 }
645 else {
646 info->dli_sname = nullptr;
647 info->dli_saddr = nullptr;
648 }
649 result = 1;
650 }
651
652 if ( result == 0 )
653 log_apis(" dladdr() => 0\n");
654 else
655 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
656 return result;
657 }
658
659
660 struct PerThreadErrorMessage
661 {
662 size_t sizeAllocated;
663 bool valid;
664 char message[1];
665 };
666
667 static pthread_key_t dlerror_perThreadKey()
668 {
669 static dispatch_once_t onceToken;
670 static pthread_key_t dlerrorPThreadKey;
671 dispatch_once(&onceToken, ^{
672 pthread_key_create(&dlerrorPThreadKey, &free);
673 });
674 return dlerrorPThreadKey;
675 }
676
677 static void clearErrorString()
678 {
679 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
680 if ( errorBuffer != nullptr )
681 errorBuffer->valid = false;
682 }
683
684 __attribute__((format(printf, 1, 2)))
685 static void setErrorString(const char* format, ...)
686 {
687 _SIMPLE_STRING buf = _simple_salloc();
688 if ( buf != nullptr ) {
689 va_list list;
690 va_start(list, format);
691 _simple_vsprintf(buf, format, list);
692 va_end(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 ) {
698 free(errorBuffer);
699 errorBuffer = nullptr;
700 }
701 }
702 if ( errorBuffer == nullptr ) {
703 size_t allocSize = std::max(sizeNeeded, (size_t)256);
704 PerThreadErrorMessage* p = (PerThreadErrorMessage*)malloc(allocSize);
705 p->sizeAllocated = allocSize;
706 p->valid = false;
707 pthread_setspecific(dlerror_perThreadKey(), p);
708 errorBuffer = p;
709 }
710 strcpy(errorBuffer->message, _simple_string(buf));
711 errorBuffer->valid = true;
712 _simple_sfree(buf);
713 }
714 }
715
716 char* dlerror()
717 {
718 log_apis("dlerror()\n");
719
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;
726 }
727 }
728 return nullptr;
729 }
730
731 #if __arm64__
732 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
733 #elif __arm__
734 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
735 #endif
736
737
738 static void* makeDlHandle(const mach_header* mh, bool dontContinue)
739 {
740 uintptr_t flags = (dontContinue ? 1 : 0);
741 return (void*)((((uintptr_t)mh) >> 5) | flags);
742 }
743
744 VIS_HIDDEN
745 void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue)
746 {
747 *dontContinue = (((uintptr_t)h) & 1);
748 *mh = (const MachOLoaded*)((((uintptr_t)h) & (-2)) << 5);
749 }
750
751 int dlclose(void* handle)
752 {
753 DYLD_LOAD_LOCK_THIS_BLOCK
754 log_apis("dlclose(%p)\n", handle);
755
756 // silently accept magic handles for main executable
757 if ( handle == RTLD_MAIN_ONLY )
758 return 0;
759 if ( handle == RTLD_DEFAULT )
760 return 0;
761
762 const MachOLoaded* mh;
763 bool dontContinue;
764 parseDlHandle(handle, &mh, &dontContinue);
765
766 __block bool unloadable = false;
767 __block bool validHandle = false;
768 gAllImages.infoForImageMappedAt(mh, ^(const LoadedImage& foundImage, uint8_t permissions) {
769 validHandle = true;
770 if ( !foundImage.image()->neverUnload() )
771 unloadable = true;
772 });
773 if ( unloadable ) {
774 gAllImages.decRefCount(mh); // removes image if reference count went to zero
775 }
776
777 if ( validHandle ) {
778 clearErrorString();
779 return 0;
780 }
781 else {
782 setErrorString("invalid handle passed to dlclose()");
783 return -1;
784 }
785 }
786
787
788 void* dlopen_internal(const char* path, int mode, void* callerAddress)
789 {
790 DYLD_LOAD_LOCK_THIS_BLOCK
791 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
792
793 clearErrorString();
794
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;
800 else
801 return RTLD_DEFAULT;
802 }
803
804 const char* leafName = strrchr(path, '/');
805 if ( leafName != nullptr )
806 ++leafName;
807 else
808 leafName = path;
809
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;
825 }
826 }
827 }
828 }
829 }
830 #endif
831
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);
834
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);
837
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);
841
842 // RTLD_NOLOAD means do nothing if image not already loaded
843 const bool rtldNoLoad = (mode & RTLD_NOLOAD);
844
845 // try to load image from specified path
846 Diagnostics diag;
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());
851 return nullptr;
852 }
853 if ( topLoadAddress == nullptr ) {
854 log_apis(" dlopen(%s) => NULL\n", leafName);
855 return nullptr;
856 }
857 void* result = makeDlHandle(topLoadAddress, firstOnly);
858 log_apis(" dlopen(%s) => %p\n", leafName, result);
859 return result;
860
861 }
862
863 bool dlopen_preflight_internal(const char* path)
864 {
865 DYLD_LOAD_LOCK_THIS_BLOCK
866 log_apis("dlopen_preflight(%s)\n", path);
867
868 // check if path is in dyld shared cache
869 if ( gAllImages.dyldCacheHasPath(path) )
870 return true;
871
872 // check if file is loadable
873 Diagnostics diag;
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);
878 return true;
879 }
880
881 // FIXME: may be symlink to something in dyld cache
882
883 return false;
884 }
885
886 static void* dlsym_search(const char* symName, const LoadedImage& start, bool searchStartImage, MachOLoaded::DependentToMachOLoaded reExportHelper,
887 bool* resultPointsToInstructions)
888 {
889 MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
890 return gAllImages.findDependent(mh, depIndex);
891 };
892 //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
893
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() )
899 return;
900 if ( aLoadedImage.loadedAddress()->hasExportedSymbol(symName, finder, &result, resultPointsToInstructions) ) {
901 stop = true;
902 }
903 });
904
905 return result;
906 }
907
908
909 void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
910 {
911 log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
912
913 clearErrorString();
914
915 MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
916 return gAllImages.findDependent(mh, depIndex);
917 };
918
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);
924
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() )
931 return;
932 if ( loadedImage.loadedAddress()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
933 stop = true;
934 }
935 });
936 if ( result != nullptr ) {
937 #if __has_feature(ptrauth_calls)
938 if (resultPointsToInstructions)
939 result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
940 #endif
941 log_apis(" dlsym() => %p\n", result);
942 return result;
943 }
944 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
945 log_apis(" dlsym() => NULL\n");
946 return nullptr;
947 }
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);
955 #endif
956 return result;
957 }
958 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
959 log_apis(" dlsym() => NULL\n");
960 return nullptr;
961 }
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) {
967 foundCaller = true;
968 result = dlsym_search(underscoredName, foundImage, false, finder, &resultPointsToInstructions);
969 });
970 if ( !foundCaller ) {
971 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
972 return nullptr;
973 }
974 }
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) {
979 foundCaller = true;
980 result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
981 });
982 if ( !foundCaller ) {
983 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
984 return nullptr;
985 }
986 }
987 else {
988 // handle value was something returned by dlopen()
989 const MachOLoaded* mh;
990 bool dontContinue;
991 parseDlHandle(handle, &mh, &dontContinue);
992
993 __block bool foundCaller = false;
994 gAllImages.infoForImageWithLoadAddress(mh, ^(const LoadedImage& foundImage) {
995 foundCaller = true;
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);
1000 }
1001 else {
1002 result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
1003 }
1004 });
1005 if ( !foundCaller ) {
1006 setErrorString("dlsym(%p, %s): invalid handle", handle, symbolName);
1007 log_apis(" dlsym() => NULL\n");
1008 return nullptr;
1009 }
1010 }
1011
1012 if ( result != nullptr ) {
1013 #if __has_feature(ptrauth_calls)
1014 if (resultPointsToInstructions)
1015 result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
1016 #endif
1017 log_apis(" dlsym() => %p\n", result);
1018 return result;
1019 }
1020
1021 setErrorString("dlsym(%p, %s): symbol not found", handle, symbolName);
1022 log_apis(" dlsym() => NULL\n");
1023 return nullptr;
1024 }
1025
1026
1027 const struct dyld_all_image_infos* _dyld_get_all_image_infos()
1028 {
1029 return gAllImages.oldAllImageInfo();
1030 }
1031
1032 bool dyld_shared_cache_some_image_overridden()
1033 {
1034 log_apis("dyld_shared_cache_some_image_overridden()\n");
1035
1036 assert(0 && "not implemented yet");
1037 }
1038
1039 bool _dyld_get_shared_cache_uuid(uuid_t uuid)
1040 {
1041 log_apis("_dyld_get_shared_cache_uuid()\n");
1042
1043 if ( gAllImages.oldAllImageInfo() != nullptr ) {
1044 memcpy(uuid, gAllImages.oldAllImageInfo()->sharedCacheUUID, sizeof(uuid_t));
1045 return true;
1046 }
1047 return false;
1048 }
1049
1050 const void* _dyld_get_shared_cache_range(size_t* mappedSize)
1051 {
1052 log_apis("_dyld_get_shared_cache_range()\n");
1053
1054 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1055 if ( sharedCache != nullptr ) {
1056 *mappedSize = (size_t)sharedCache->mappedSize();
1057 return sharedCache;
1058 }
1059 *mappedSize = 0;
1060 return NULL;
1061 }
1062
1063 void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[])
1064 {
1065 log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count, addresses, infos);
1066
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);
1078 }
1079 else {
1080 ml = nullptr;
1081 textSize = 0;
1082 }
1083 }
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);
1088 }
1089 }
1090 }
1091
1092 void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
1093 {
1094 gAllImages.addLoadNotifier(func);
1095 }
1096
1097 bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
1098 {
1099 log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info);
1100 addr = (void*)stripPointer(addr);
1101
1102 const MachOLoaded* ml = nullptr;
1103 if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) ) {
1104 info->mh = ml;
1105 info->dwarf_section = nullptr;
1106 info->dwarf_section_length = 0;
1107 info->compact_unwind_section = nullptr;
1108 info->compact_unwind_section_length = 0;
1109
1110 uint64_t size;
1111 if ( const void* content = ml->findSectionContent("__TEXT", "__eh_frame", size) ) {
1112 info->dwarf_section = content;
1113 info->dwarf_section_length = (uintptr_t)size;
1114 }
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;
1118 }
1119 return true;
1120 }
1121
1122 return false;
1123 }
1124
1125
1126 bool dyld_process_is_restricted()
1127 {
1128 log_apis("dyld_process_is_restricted()\n");
1129 return gAllImages.isRestricted();
1130 }
1131
1132
1133 const char* dyld_shared_cache_file_path()
1134 {
1135 log_apis("dyld_shared_cache_file_path()\n");
1136
1137 return gAllImages.dyldCachePath();
1138 }
1139
1140
1141 void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count)
1142 {
1143 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh, array, count);
1144 // FIXME
1145 }
1146
1147
1148 static void* mapStartOfCache(const char* path, size_t length)
1149 {
1150 struct stat statbuf;
1151 if ( ::stat(path, &statbuf) == -1 )
1152 return NULL;
1153
1154 if ( statbuf.st_size < length )
1155 return NULL;
1156
1157 int cache_fd = ::open(path, O_RDONLY);
1158 if ( cache_fd < 0 )
1159 return NULL;
1160
1161 void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0);
1162 close(cache_fd);
1163
1164 if ( result == MAP_FAILED )
1165 return NULL;
1166
1167 return result;
1168 }
1169
1170 static const DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath, size_t& sizeMapped)
1171 {
1172 DIR* dirp = ::opendir(dirPath);
1173 if ( dirp != NULL) {
1174 dirent entry;
1175 dirent* entp = NULL;
1176 char cachePath[PATH_MAX];
1177 while ( ::readdir_r(dirp, &entry, &entp) == 0 ) {
1178 if ( entp == NULL )
1179 break;
1180 if ( entp->d_type != DT_REG )
1181 continue;
1182 if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX )
1183 continue;
1184 if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX )
1185 continue;
1186 if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX )
1187 continue;
1188 if ( const DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) {
1189 uuid_t foundUuid;
1190 cache->getUUID(foundUuid);
1191 if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) {
1192 // wrong uuid, unmap and keep looking
1193 ::munmap((void*)cache, 0x00100000);
1194 }
1195 else {
1196 // found cache
1197 closedir(dirp);
1198 sizeMapped = 0x00100000;
1199 return cache;
1200 }
1201 }
1202 }
1203 closedir(dirp);
1204 }
1205 return nullptr;
1206 }
1207
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))
1209 {
1210 log_apis("dyld_shared_cache_find_iterate_text()\n");
1211
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 ) {
1216 uuid_t runningUuid;
1217 sharedCache->getUUID(runningUuid);
1218 if ( ::memcmp(runningUuid, cacheUuid, 16) != 0 )
1219 sharedCache = nullptr;
1220 }
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;
1225 #else
1226 const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR;
1227 #endif
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 )
1234 break;
1235 }
1236 }
1237 }
1238 if ( sharedCache == nullptr )
1239 return -1;
1240
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;
1246 });
1247
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);
1258 });
1259
1260 if ( sizeMapped != 0 )
1261 ::munmap((void*)sharedCache, sizeMapped);
1262
1263 return 0;
1264 }
1265
1266 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
1267 {
1268 log_apis("dyld_shared_cache_iterate_text()\n");
1269
1270 const char* extraSearchDirs[] = { NULL };
1271 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
1272 }
1273
1274
1275
1276 } // namespace dyld3
1277