]> git.saurik.com Git - apple/dyld.git/blob - dyld3/APIs.cpp
8ed4a6dfb7771bc65e020ca7630630a55f8808c6
[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 <libc_private.h>
33 #include <TargetConditionals.h>
34 #include <_simple.h>
35 #include <mach-o/dyld_priv.h>
36 #include <mach-o/dyld_images.h>
37 #include <crt_externs.h> // FIXME: Remove once we move off of _NSGetMainExecutable()
38 #include <os/once_private.h>
39
40 #include <array>
41 #include <algorithm>
42
43 #include "dlfcn.h"
44
45 #include "AllImages.h"
46 #include "Loading.h"
47 #include "Logging.h"
48 #include "Diagnostics.h"
49 #include "DyldSharedCache.h"
50 #include "PathOverrides.h"
51 #include "APIs.h"
52 #include "Closure.h"
53 #include "MachOLoaded.h"
54 #include "ClosureBuilder.h"
55 #include "ClosureFileSystemPhysical.h"
56
57 #if __has_feature(ptrauth_calls)
58 #include <ptrauth.h>
59 #endif
60
61 extern mach_header __dso_handle;
62
63 namespace dyld {
64 extern dyld_all_image_infos dyld_all_image_infos;
65 }
66
67
68 namespace dyld3 {
69
70
71 static const void *stripPointer(const void *ptr) {
72 #if __has_feature(ptrauth_calls)
73 return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
74 #else
75 return ptr;
76 #endif
77 }
78
79 pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
80
81 // forward declaration
82 static void dyld_get_image_versions_internal(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
83
84
85 uint32_t _dyld_image_count(void)
86 {
87 log_apis("_dyld_image_count()\n");
88
89 return gAllImages.count();
90 }
91
92 const mach_header* _dyld_get_image_header(uint32_t imageIndex)
93 {
94 log_apis("_dyld_get_image_header(%d)\n", imageIndex);
95 return gAllImages.imageLoadAddressByIndex(imageIndex);
96 }
97
98 intptr_t _dyld_get_image_slide(const mach_header* mh)
99 {
100 log_apis("_dyld_get_image_slide(%p)\n", mh);
101
102 const MachOLoaded* mf = (MachOLoaded*)mh;
103 if ( !mf->hasMachOMagic() )
104 return 0;
105
106 return mf->getSlide();
107 }
108
109 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex)
110 {
111 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex);
112
113 const mach_header* mh = gAllImages.imageLoadAddressByIndex(imageIndex);
114 if ( mh != nullptr )
115 return dyld3::_dyld_get_image_slide(mh);
116 return 0;
117 }
118
119 const char* _dyld_get_image_name(uint32_t imageIndex)
120 {
121 log_apis("_dyld_get_image_name(%d)\n", imageIndex);
122 return gAllImages.imagePathByIndex(imageIndex);
123 }
124
125
126 static bool nameMatch(const char* installName, const char* libraryName)
127 {
128 const char* leafName = strrchr(installName, '/');
129 if ( leafName == NULL )
130 leafName = installName;
131 else
132 leafName++;
133
134 // -framework case is exact match of leaf name
135 if ( strcmp(leafName, libraryName) == 0 )
136 return true;
137
138 // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
139 size_t leafNameLen = strlen(leafName);
140 size_t libraryNameLen = strlen(libraryName);
141 if ( leafNameLen < (libraryNameLen+9) )
142 return false;
143 if ( strncmp(leafName, "lib", 3) != 0 )
144 return false;
145 if ( strcmp(&leafName[leafNameLen-6], ".dylib") != 0 )
146 return false;
147 if ( strncmp(&leafName[3], libraryName, libraryNameLen) != 0 )
148 return false;
149 return (leafName[libraryNameLen+3] == '.');
150 }
151
152
153 //
154 // BETTER, USE: dyld_get_program_sdk_version()
155 //
156 // Scans the main executable and returns the version of the specified dylib the program was built against.
157 //
158 // The library to find is the leaf name that would have been passed to linker tool
159 // (e.g. -lfoo or -framework foo would use "foo").
160 //
161 // Returns -1 if the main executable did not link against the specified library, or is malformed.
162 //
163 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
164 {
165 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName);
166
167 __block int32_t result = -1;
168 gAllImages.mainExecutable()->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
169 if ( nameMatch(loadPath, libraryName) )
170 result = currentVersion;
171 });
172 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result);
173 return result;
174 }
175
176
177 //
178 // Searches loaded images for the requested dylib and returns its current version.
179 //
180 // The library to find is the leaf name that would have been passed to linker tool
181 // (e.g. -lfoo or -framework foo would use "foo").
182 //
183 // If the specified library is not loaded, -1 is returned.
184 //
185 int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
186 {
187 log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName);
188 __block int32_t result = -1;
189 gAllImages.forEachImage(^(const dyld3::LoadedImage& loadedImage, bool &stop) {
190 const char* installName;
191 uint32_t currentVersion;
192 uint32_t compatVersion;
193 if ( loadedImage.loadedAddress()->getDylibInstallName(&installName, &compatVersion, &currentVersion) && nameMatch(installName, libraryName) ) {
194 result = currentVersion;
195 stop = true;
196 }
197 });
198 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", result);
199 return result;
200 }
201
202
203 uint32_t dyld_get_program_sdk_watch_os_version()
204 {
205 log_apis("dyld_get_program_sdk_watch_os_version()\n");
206
207 __block uint32_t retval = 0;
208 __block bool versionFound = false;
209 dyld3::dyld_get_image_versions(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
210 if (versionFound) return;
211
212 if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
213 versionFound = true;
214 retval = sdk_version;
215 }
216 });
217
218 return retval;
219 }
220
221 uint32_t dyld_get_program_min_watch_os_version()
222 {
223 log_apis("dyld_get_program_min_watch_os_version()\n");
224
225 __block uint32_t retval = 0;
226 __block bool versionFound = false;
227 dyld3::dyld_get_image_versions(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
228 if (versionFound) return;
229
230 if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
231 versionFound = true;
232 retval = min_version;
233 }
234 });
235
236 return retval;
237 }
238
239 uint32_t dyld_get_program_sdk_bridge_os_version()
240 {
241 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
242
243 __block uint32_t retval = 0;
244 __block bool versionFound = false;
245 dyld3::dyld_get_image_versions(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
246 if (versionFound) return;
247
248 if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
249 versionFound = true;
250 retval = sdk_version;
251 }
252 });
253
254 return retval;
255 }
256
257 uint32_t dyld_get_program_min_bridge_os_version()
258 {
259 log_apis("dyld_get_program_min_bridge_os_version()\n");
260
261 __block uint32_t retval = 0;
262 __block bool versionFound = false;
263 dyld3::dyld_get_image_versions(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
264 if (versionFound) return;
265
266 if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
267 versionFound = true;
268 retval = min_version;
269 }
270 });
271
272 return retval;
273 }
274
275 //
276 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
277 // specified binary was built against.
278 //
279 // First looks for LC_VERSION_MIN_* in binary and if sdk field is
280 // not zero, return that value.
281 // Otherwise, looks for the libSystem.B.dylib the binary linked
282 // against and uses a table to convert that to an sdk version.
283 //
284 uint32_t dyld_get_sdk_version(const mach_header* mh)
285 {
286 log_apis("dyld_get_sdk_version(%p)\n", mh);
287 __block bool versionFound = false;
288 __block uint32_t retval = 0;
289 dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
290 if (versionFound) return;
291
292 if (platform == ::dyld_get_active_platform()) {
293 versionFound = true;
294 switch (dyld3::dyld_get_base_platform(platform)) {
295 case PLATFORM_BRIDGEOS: retval = sdk_version + 0x00090000; return;
296 case PLATFORM_WATCHOS: retval = sdk_version + 0x00070000; return;
297 default: retval = sdk_version; return;
298 }
299 }
300 });
301
302 return retval;
303 }
304
305 uint32_t dyld_get_program_sdk_version()
306 {
307 log_apis("dyld_get_program_sdk_version()\n");
308 return dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
309 }
310
311 uint32_t dyld_get_min_os_version(const mach_header* mh)
312 {
313 log_apis("dyld_get_min_os_version(%p)\n", mh);
314 __block bool versionFound = false;
315 __block uint32_t retval = 0;
316 dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
317 if (versionFound) return;
318
319 if (platform == ::dyld_get_active_platform()) {
320 versionFound = true;
321 switch (dyld3::dyld_get_base_platform(platform)) {
322 case PLATFORM_BRIDGEOS: retval = min_version + 0x00090000; return;
323 case PLATFORM_WATCHOS: retval = min_version + 0x00070000; return;
324 default: retval = min_version; return;
325 }
326 }
327 });
328
329 return retval;
330 }
331
332 dyld_platform_t dyld_get_active_platform(void) {
333 return gAllImages.platform();
334 }
335
336 dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
337 switch (platform) {
338 case PLATFORM_IOSMAC: return PLATFORM_IOS;
339 case PLATFORM_IOSSIMULATOR: return PLATFORM_IOS;
340 case PLATFORM_WATCHOSSIMULATOR: return PLATFORM_WATCHOS;
341 case PLATFORM_TVOSSIMULATOR: return PLATFORM_TVOS;
342 default: return platform;
343 }
344 }
345
346 bool dyld_is_simulator_platform(dyld_platform_t platform) {
347 switch(platform) {
348 case PLATFORM_IOSSIMULATOR:
349 case PLATFORM_WATCHOSSIMULATOR:
350 case PLATFORM_TVOSSIMULATOR:
351 return true;
352 default:
353 return false;
354 }
355 }
356
357 bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) {
358 __block bool retval = false;
359 dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
360 if (dyld3::dyld_get_base_platform(platform) == version.platform && sdk_version >= version.version) {
361 retval = true;
362 }
363 });
364 return retval;
365 }
366
367 bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) {
368 __block bool retval = false;
369 dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
370 if (dyld3::dyld_get_base_platform(platform) == version.platform && min_version >= version.version) {
371 retval = true;
372 }
373 });
374 return retval;
375 }
376
377 bool dyld_program_sdk_at_least(dyld_build_version_t version) {
378 return dyld3::dyld_sdk_at_least(gAllImages.mainExecutable(), version);
379 }
380
381 bool dyld_program_minos_at_least(dyld_build_version_t version) {
382 return dyld3::dyld_minos_at_least(gAllImages.mainExecutable(), version);
383 }
384
385 #if TARGET_OS_OSX || TARGET_OS_IOS
386 static
387 uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) {
388 __block uint32_t retval = 0;
389 ((MachOLoaded*)mh)->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
390 if (strcmp(loadPath, installname) == 0) {
391 retval = currentVersion;
392 stop = true;
393 }
394 });
395 return retval;
396 }
397 #endif
398
399
400 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
401
402 static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) {
403 // This is a binary without a version load command, we need to infer things
404 struct DylibToOSMapping {
405 uint32_t dylibVersion;
406 uint32_t osVersion;
407 };
408 uint32_t linkedVersion = 0;
409 #if TARGET_OS_OSX
410 linkedVersion = linkedDylibVersion(mh, "/usr/lib/libSystem.B.dylib");
411 static const DylibToOSMapping versionMapping[] = {
412 { PACKED_VERSION(88,1,3), 0x000A0400 },
413 { PACKED_VERSION(111,0,0), 0x000A0500 },
414 { PACKED_VERSION(123,0,0), 0x000A0600 },
415 { PACKED_VERSION(159,0,0), 0x000A0700 },
416 { PACKED_VERSION(169,3,0), 0x000A0800 },
417 { PACKED_VERSION(1197,0,0), 0x000A0900 },
418 { PACKED_VERSION(0,0,0), 0x000A0900 }
419 // We don't need to expand this table because all recent
420 // binaries have LC_VERSION_MIN_ load command.
421 };
422 #elif TARGET_OS_IOS
423 linkedVersion = linkedDylibVersion(mh, "/System/Library/Frameworks/Foundation.framework/Foundation");
424 static const DylibToOSMapping versionMapping[] = {
425 { PACKED_VERSION(678,24,0), 0x00020000 },
426 { PACKED_VERSION(678,26,0), 0x00020100 },
427 { PACKED_VERSION(678,29,0), 0x00020200 },
428 { PACKED_VERSION(678,47,0), 0x00030000 },
429 { PACKED_VERSION(678,51,0), 0x00030100 },
430 { PACKED_VERSION(678,60,0), 0x00030200 },
431 { PACKED_VERSION(751,32,0), 0x00040000 },
432 { PACKED_VERSION(751,37,0), 0x00040100 },
433 { PACKED_VERSION(751,49,0), 0x00040200 },
434 { PACKED_VERSION(751,58,0), 0x00040300 },
435 { PACKED_VERSION(881,0,0), 0x00050000 },
436 { PACKED_VERSION(890,1,0), 0x00050100 },
437 { PACKED_VERSION(992,0,0), 0x00060000 },
438 { PACKED_VERSION(993,0,0), 0x00060100 },
439 { PACKED_VERSION(1038,14,0),0x00070000 },
440 { PACKED_VERSION(0,0,0), 0x00070000 }
441 // We don't need to expand this table because all recent
442 // binaries have LC_VERSION_MIN_ load command.
443 };
444 #else
445 static const DylibToOSMapping versionMapping[] = {};
446 #endif
447 if ( linkedVersion != 0 ) {
448 uint32_t lastOsVersion = 0;
449 for (const DylibToOSMapping* p=versionMapping; ; ++p) {
450 if ( p->dylibVersion == 0 ) {
451 return p->osVersion;
452 }
453 if ( linkedVersion < p->dylibVersion ) {
454 return lastOsVersion;
455 }
456 lastOsVersion = p->osVersion;
457 }
458 }
459 return 0;
460 }
461
462 // assumes mh has already been validated
463 static void dyld_get_image_versions_internal(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version))
464 {
465 const MachOFile* mf = (MachOFile*)mh;
466 __block bool lcFound = false;
467 mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
468 lcFound = true;
469 // If SDK field is empty then derive the value from library linkages
470 if (sdk == 0) {
471 sdk = deriveVersionFromDylibs(mh);
472 }
473 // HACK: We don't have time to fix all the zippered clients in the spring releases, so keep the mapping
474 if (platform == dyld3::Platform::iOSMac) {
475 if (sdk >= 0x000D0400) {
476 sdk = 0x000A0F04;
477 } else {
478 sdk = 0x000A0F00;
479 }
480 }
481 callback((const dyld_platform_t)platform, sdk, minOS);
482 });
483
484 // No load command was found, so again, fallback to deriving it from library linkages
485 if (!lcFound) {
486 #if TARGET_OS_IOS
487 #if __x86_64__ || __x86__
488 dyld_platform_t platform = PLATFORM_IOSSIMULATOR;
489 #else
490 dyld_platform_t platform = PLATFORM_IOS;
491 #endif
492 #elif TARGET_OS_OSX
493 dyld_platform_t platform = PLATFORM_MACOS;
494 #else
495 dyld_platform_t platform = 0;
496 #endif
497 uint32_t derivedVersion = deriveVersionFromDylibs(mh);
498 if ( platform != 0 && derivedVersion != 0 ) {
499 callback(platform, derivedVersion, 0);
500 }
501 }
502 }
503
504 void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version))
505 {
506 Diagnostics diag;
507 const MachOFile* mf = (MachOFile*)mh;
508 static dyld_platform_t mainExecutablePlatform = 0;
509 static uint32_t mainExecutableSDKVersion = 0;
510 static uint32_t mainExecutableMinOSVersion = 0;
511
512 // FIXME: Once dyld2 is gone gAllImages.mainExecutable() will be valid in all cases
513 // and we can stop calling _NSGetMachExecuteHeader()
514 if (mh == (const struct mach_header*)_NSGetMachExecuteHeader()) {
515 if (mainExecutablePlatform) {
516 return callback(mainExecutablePlatform, mainExecutableSDKVersion, mainExecutableMinOSVersion);
517 }
518 mainExecutablePlatform = ::dyld_get_active_platform();
519 dyld_get_image_versions_internal(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
520 if (platform == PLATFORM_MACOS && dyld_get_base_platform(mainExecutablePlatform) == PLATFORM_IOS) {
521 // We are running with DYLD_FORCE_PLATFORM, use the current OSes values
522 dyld_get_image_versions_internal(&__dso_handle, ^(dyld_platform_t dyld_platform, uint32_t dyld_sdk_version, uint32_t dyld_min_version) {
523 if (dyld_get_base_platform(dyld_platform) == PLATFORM_IOS) {
524 mainExecutableSDKVersion = dyld_sdk_version;
525 mainExecutableMinOSVersion = dyld_min_version;
526 }
527 });
528 } else {
529 mainExecutableSDKVersion = sdk_version;
530 mainExecutableMinOSVersion = min_version;
531 }
532 });
533 return callback(mainExecutablePlatform, mainExecutableSDKVersion, mainExecutableMinOSVersion);
534 }
535 #if TARGET_OS_EMBEDDED
536 // If we are on embedded AND in the shared cache then the versions should be the same as libdyld
537 if (mf->inDyldCache()) {
538 static dyld_platform_t libDyldPlatform = 0;
539 static uint32_t libDyldSDKVersion = 0;
540 static uint32_t libDyldMinOSVersion = 0;
541 if (libDyldPlatform == 0) {
542 dyld_get_image_versions_internal(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
543 libDyldPlatform = platform;
544 libDyldSDKVersion = sdk_version;
545 libDyldMinOSVersion = min_version;
546 //FIXME: Assert if more than one command?
547 });
548 }
549 return callback(libDyldPlatform, libDyldSDKVersion, libDyldMinOSVersion);
550 }
551 #endif
552 if ( mf->isMachO(diag, mh->sizeofcmds + sizeof(mach_header_64)) )
553 dyld_get_image_versions_internal(mh, callback);
554 }
555
556 uint32_t dyld_get_program_min_os_version()
557 {
558 log_apis("dyld_get_program_min_os_version()\n");
559 return dyld3::dyld_get_min_os_version(gAllImages.mainExecutable());
560 }
561
562 bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
563 {
564 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
565
566 const MachOFile* mf = (MachOFile*)mh;
567 if ( !mf->hasMachOMagic() )
568 return false;
569
570 return mf->getUuid(uuid);
571 }
572
573 //
574 // _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
575 // should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
576 // and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
577 // to the size required.
578 //
579 int _NSGetExecutablePath(char* buf, uint32_t* bufsize)
580 {
581 log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
582
583 const closure::Image* mainImage = gAllImages.mainExecutableImage();
584 const char* path = gAllImages.imagePath(mainImage);
585 size_t pathSize = strlen(path) + 1;
586 if ( *bufsize >= pathSize ) {
587 strcpy(buf, path);
588 return 0;
589 }
590 *bufsize = (uint32_t)pathSize;
591 return -1;
592 }
593
594 void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
595 {
596 log_apis("_dyld_register_func_for_add_image(%p)\n", func);
597
598 gAllImages.addLoadNotifier(func);
599 }
600
601 void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
602 {
603 log_apis("_dyld_register_func_for_remove_image(%p)\n", func);
604
605 gAllImages.addUnloadNotifier(func);
606 }
607
608 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
609 _dyld_objc_notify_init init,
610 _dyld_objc_notify_unmapped unmapped)
611 {
612 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
613
614 gAllImages.setObjCNotifiers(mapped, init, unmapped);
615 }
616
617
618 const mach_header* dyld_image_header_containing_address(const void* addr)
619 {
620 log_apis("dyld_image_header_containing_address(%p)\n", addr);
621
622 addr = stripPointer(addr);
623
624 const MachOLoaded* ml;
625 if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) )
626 return ml;
627
628 return nullptr;
629 }
630
631
632 const char* dyld_image_path_containing_address(const void* addr)
633 {
634 log_apis("dyld_image_path_containing_address(%p)\n", addr);
635
636 addr = stripPointer(addr);
637 const char* result = gAllImages.pathForImageMappedAt(addr);
638
639 log_apis(" dyld_image_path_containing_address() => %s\n", result);
640 return result;
641 }
642
643
644
645 bool _dyld_is_memory_immutable(const void* addr, size_t length)
646 {
647 return gAllImages.immutableMemory(addr, length);
648 }
649
650 int dladdr(const void* addr, Dl_info* info)
651 {
652 log_apis("dladdr(%p, %p)\n", addr, info);
653
654 // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
655 if ( info == NULL )
656 return 0; // failure
657
658 addr = stripPointer(addr);
659
660 __block int result = 0;
661 const MachOLoaded* ml = nullptr;
662 const char* path = nullptr;
663 if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, &path) ) {
664 info->dli_fname = path;
665 info->dli_fbase = (void*)ml;
666
667 uint64_t symbolAddr;
668 if ( addr == info->dli_fbase ) {
669 // special case lookup of header
670 info->dli_sname = "__dso_handle";
671 info->dli_saddr = info->dli_fbase;
672 }
673 else if ( ml->findClosestSymbol((long)addr, &(info->dli_sname), &symbolAddr) ) {
674 info->dli_saddr = (void*)(long)symbolAddr;
675 // never return the mach_header symbol
676 if ( info->dli_saddr == info->dli_fbase ) {
677 info->dli_sname = nullptr;
678 info->dli_saddr = nullptr;
679 }
680 // strip off leading underscore
681 else if ( (info->dli_sname != nullptr) && (info->dli_sname[0] == '_') ) {
682 info->dli_sname = info->dli_sname + 1;
683 }
684 }
685 else {
686 info->dli_sname = nullptr;
687 info->dli_saddr = nullptr;
688 }
689 result = 1;
690 }
691
692 if ( result == 0 )
693 log_apis(" dladdr() => 0\n");
694 else
695 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
696 return result;
697 }
698
699 #if !TARGET_OS_DRIVERKIT
700
701 struct PerThreadErrorMessage
702 {
703 size_t sizeAllocated;
704 bool valid;
705 char message[1];
706 };
707
708 static void dlerror_perThreadKey_once(void* ctx)
709 {
710 pthread_key_t* dlerrorPThreadKeyPtr = (pthread_key_t*)ctx;
711 pthread_key_create(dlerrorPThreadKeyPtr, &free);
712 }
713
714 static pthread_key_t dlerror_perThreadKey()
715 {
716 static os_once_t onceToken;
717 static pthread_key_t dlerrorPThreadKey;
718 os_once(&onceToken, &dlerrorPThreadKey, dlerror_perThreadKey_once);
719 return dlerrorPThreadKey;
720 }
721
722 static void clearErrorString()
723 {
724 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
725 if ( errorBuffer != nullptr )
726 errorBuffer->valid = false;
727 }
728
729 __attribute__((format(printf, 1, 2)))
730 static void setErrorString(const char* format, ...)
731 {
732 _SIMPLE_STRING buf = _simple_salloc();
733 if ( buf != nullptr ) {
734 va_list list;
735 va_start(list, format);
736 _simple_vsprintf(buf, format, list);
737 va_end(list);
738 size_t strLen = strlen(_simple_string(buf)) + 1;
739 size_t sizeNeeded = sizeof(PerThreadErrorMessage) + strLen;
740 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
741 if ( errorBuffer != nullptr ) {
742 if ( errorBuffer->sizeAllocated < sizeNeeded ) {
743 free(errorBuffer);
744 errorBuffer = nullptr;
745 }
746 }
747 if ( errorBuffer == nullptr ) {
748 size_t allocSize = std::max(sizeNeeded, (size_t)256);
749 PerThreadErrorMessage* p = (PerThreadErrorMessage*)malloc(allocSize);
750 p->sizeAllocated = allocSize;
751 p->valid = false;
752 pthread_setspecific(dlerror_perThreadKey(), p);
753 errorBuffer = p;
754 }
755 strcpy(errorBuffer->message, _simple_string(buf));
756 errorBuffer->valid = true;
757 _simple_sfree(buf);
758 }
759 }
760
761 char* dlerror()
762 {
763 log_apis("dlerror()\n");
764
765 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
766 if ( errorBuffer != nullptr ) {
767 if ( errorBuffer->valid ) {
768 // you can only call dlerror() once, then the message is cleared
769 errorBuffer->valid = false;
770 return errorBuffer->message;
771 }
772 }
773 return nullptr;
774 }
775
776 #if __arm64__
777 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
778 #elif __arm__
779 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
780 #endif
781
782
783 static void* makeDlHandle(const mach_header* mh, bool dontContinue)
784 {
785 uintptr_t flags = (dontContinue ? 1 : 0);
786 return (void*)((((uintptr_t)mh) >> 5) | flags);
787 }
788
789 VIS_HIDDEN
790 void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue)
791 {
792 *dontContinue = (((uintptr_t)h) & 1);
793 *mh = (const MachOLoaded*)((((uintptr_t)h) & (-2)) << 5);
794 }
795
796 int dlclose(void* handle)
797 {
798 DYLD_LOAD_LOCK_THIS_BLOCK
799 log_apis("dlclose(%p)\n", handle);
800
801 // silently accept magic handles for main executable
802 if ( handle == RTLD_MAIN_ONLY )
803 return 0;
804 if ( handle == RTLD_DEFAULT )
805 return 0;
806
807 const MachOLoaded* mh;
808 bool dontContinue;
809 parseDlHandle(handle, &mh, &dontContinue);
810
811 __block bool unloadable = false;
812 __block bool validHandle = false;
813 gAllImages.infoForImageMappedAt(mh, ^(const LoadedImage& foundImage, uint8_t permissions) {
814 validHandle = true;
815 if ( !foundImage.image()->neverUnload() )
816 unloadable = true;
817 });
818 if ( unloadable ) {
819 gAllImages.decRefCount(mh); // removes image if reference count went to zero
820 }
821
822 if ( validHandle ) {
823 clearErrorString();
824 return 0;
825 }
826 else {
827 setErrorString("invalid handle passed to dlclose()");
828 return -1;
829 }
830 }
831
832
833 void* dlopen_internal(const char* path, int mode, void* callerAddress)
834 {
835 DYLD_LOAD_LOCK_THIS_BLOCK
836 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
837
838 clearErrorString();
839
840 // passing NULL for path means return magic object
841 if ( path == NULL ) {
842 // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
843 if ( (mode & RTLD_FIRST) != 0 )
844 return RTLD_MAIN_ONLY;
845 else
846 return RTLD_DEFAULT;
847 }
848
849 const char* leafName = strrchr(path, '/');
850 if ( leafName != nullptr )
851 ++leafName;
852 else
853 leafName = path;
854
855 #if __IPHONE_OS_VERSION_MIN_REQUIRED
856 // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
857 char canonicalPath[PATH_MAX];
858 if ( leafName != path ) {
859 // make path canonical if it contains a // or ./
860 if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
861 const char* lastSlash = strrchr(path, '/');
862 char dirPath[PATH_MAX];
863 if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
864 dirPath[lastSlash-path] = '\0';
865 if ( realpath(dirPath, canonicalPath) ) {
866 strlcat(canonicalPath, "/", sizeof(canonicalPath));
867 if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
868 // if all fit in buffer, use new canonical path
869 path = canonicalPath;
870 }
871 }
872 }
873 }
874 }
875 #endif
876
877 // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
878 const bool firstOnly = (mode & RTLD_FIRST);
879
880 // RTLD_LOCAL means when flat searches of all images (e.g. RTLD_DEFAULT) is done, this image should be skipped. But dlsym(handle, xx) can find symbols
881 const bool rtldLocal = (mode & RTLD_LOCAL);
882
883 // RTLD_NODELETE means don't unmap image during dlclose(). Leave the memory mapped, but orphan (leak) it.
884 // Note: this is a weird state and it slightly different semantics that other OSs
885 const bool rtldNoDelete = (mode & RTLD_NODELETE);
886
887 // RTLD_NOLOAD means do nothing if image not already loaded
888 const bool rtldNoLoad = (mode & RTLD_NOLOAD);
889
890 // RTLD_NOW means force lazy symbols bound and fail dlopen() if some cannot be bound
891 const bool rtldNow = (mode & RTLD_NOW);
892
893 // try to load image from specified path
894 Diagnostics diag;
895 const mach_header* topLoadAddress = gAllImages.dlopen(diag, path, rtldNoLoad, rtldLocal, rtldNoDelete, rtldNow, false, callerAddress);
896 if ( diag.hasError() ) {
897 setErrorString("dlopen(%s, 0x%04X): %s", path, mode, diag.errorMessage());
898 log_apis(" dlopen: closure creation error: %s\n", diag.errorMessage());
899 return nullptr;
900 }
901 if ( topLoadAddress == nullptr ) {
902 log_apis(" dlopen(%s) => NULL\n", leafName);
903 return nullptr;
904 }
905 void* result = makeDlHandle(topLoadAddress, firstOnly);
906 log_apis(" dlopen(%s) => %p\n", leafName, result);
907 return result;
908
909 }
910
911 bool dlopen_preflight_internal(const char* path)
912 {
913 DYLD_LOAD_LOCK_THIS_BLOCK
914 log_apis("dlopen_preflight(%s)\n", path);
915
916 // check if path is in dyld shared cache
917 if ( gAllImages.dyldCacheHasPath(path) )
918 return true;
919
920 // check if file is loadable
921 Diagnostics diag;
922 closure::FileSystemPhysical fileSystem;
923 char realerPath[MAXPATHLEN];
924 closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, gAllImages.archs(), (Platform)gAllImages.platform(), realerPath);
925 if ( loadedFileInfo.fileContent != nullptr ) {
926 fileSystem.unloadFile(loadedFileInfo);
927 return true;
928 }
929
930 // FIXME: may be symlink to something in dyld cache
931
932 return false;
933 }
934
935 static void* dlsym_search(const char* symName, const LoadedImage& start, bool searchStartImage, MachOLoaded::DependentToMachOLoaded reExportHelper,
936 bool* resultPointsToInstructions)
937 {
938 MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
939 return gAllImages.findDependent(mh, depIndex);
940 };
941 //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
942
943 // walk all dependents of 'start' in order looking for symbol
944 __block void* result = nullptr;
945 gAllImages.visitDependentsTopDown(start, ^(const LoadedImage& aLoadedImage, bool& stop) {
946 //fprintf(stderr, " search: %s\n", aLoadedImage.image()->path());
947 if ( !searchStartImage && aLoadedImage.image() == start.image() )
948 return;
949 if ( aLoadedImage.loadedAddress()->hasExportedSymbol(symName, finder, &result, resultPointsToInstructions) ) {
950 result = gAllImages.interposeValue(result);
951 stop = true;
952 }
953 });
954
955 return result;
956 }
957
958
959 void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
960 {
961 log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
962
963 clearErrorString();
964
965 MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
966 return gAllImages.findDependent(mh, depIndex);
967 };
968
969 // dlsym() assumes symbolName passed in is same as in C source code
970 // dyld assumes all symbol names have an underscore prefix
971 BLOCK_ACCCESSIBLE_ARRAY(char, underscoredName, strlen(symbolName)+2);
972 underscoredName[0] = '_';
973 strcpy(&underscoredName[1], symbolName);
974
975 __block void* result = nullptr;
976 __block bool resultPointsToInstructions = false;
977 if ( handle == RTLD_DEFAULT ) {
978 // magic "search all in load order" handle
979 gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
980 if ( loadedImage.hideFromFlatSearch() )
981 return;
982 if ( loadedImage.loadedAddress()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
983 stop = true;
984 }
985 });
986 if ( result != nullptr ) {
987 result = gAllImages.interposeValue(result);
988 #if __has_feature(ptrauth_calls)
989 if (resultPointsToInstructions)
990 result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
991 #endif
992 log_apis(" dlsym() => %p\n", result);
993 return result;
994 }
995 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
996 log_apis(" dlsym() => NULL\n");
997 return nullptr;
998 }
999 else if ( handle == RTLD_MAIN_ONLY ) {
1000 // magic "search only main executable" handle
1001 if ( gAllImages.mainExecutable()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
1002 result = gAllImages.interposeValue(result);
1003 log_apis(" dlsym() => %p\n", result);
1004 #if __has_feature(ptrauth_calls)
1005 if (resultPointsToInstructions)
1006 result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
1007 #endif
1008 return result;
1009 }
1010 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
1011 log_apis(" dlsym() => NULL\n");
1012 return nullptr;
1013 }
1014 // rest of cases search in dependency order
1015 if ( handle == RTLD_NEXT ) {
1016 // magic "search what I would see" handle
1017 __block bool foundCaller = false;
1018 gAllImages.infoForImageMappedAt(callerAddress, ^(const LoadedImage& foundImage, uint8_t permissions) {
1019 foundCaller = true;
1020 result = dlsym_search(underscoredName, foundImage, false, finder, &resultPointsToInstructions);
1021 });
1022 if ( !foundCaller ) {
1023 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
1024 return nullptr;
1025 }
1026 }
1027 else if ( handle == RTLD_SELF ) {
1028 // magic "search me, then what I would see" handle
1029 __block bool foundCaller = false;
1030 gAllImages.infoForImageMappedAt(callerAddress, ^(const LoadedImage& foundImage, uint8_t permissions) {
1031 foundCaller = true;
1032 result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
1033 });
1034 if ( !foundCaller ) {
1035 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
1036 return nullptr;
1037 }
1038 }
1039 else {
1040 // handle value was something returned by dlopen()
1041 const MachOLoaded* mh;
1042 bool dontContinue;
1043 parseDlHandle(handle, &mh, &dontContinue);
1044
1045 __block bool foundCaller = false;
1046 gAllImages.infoForImageWithLoadAddress(mh, ^(const LoadedImage& foundImage) {
1047 foundCaller = true;
1048 if ( dontContinue ) {
1049 // RTLD_FIRST only searches one place
1050 // we go through infoForImageWithLoadAddress() to validate the handle
1051 if (mh->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions))
1052 result = gAllImages.interposeValue(result);
1053 }
1054 else {
1055 result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
1056 }
1057 });
1058 if ( !foundCaller ) {
1059 setErrorString("dlsym(%p, %s): invalid handle", handle, symbolName);
1060 log_apis(" dlsym() => NULL\n");
1061 return nullptr;
1062 }
1063 }
1064
1065 if ( result != nullptr ) {
1066 #if __has_feature(ptrauth_calls)
1067 if (resultPointsToInstructions)
1068 result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
1069 #endif
1070 log_apis(" dlsym() => %p\n", result);
1071 return result;
1072 }
1073
1074 setErrorString("dlsym(%p, %s): symbol not found", handle, symbolName);
1075 log_apis(" dlsym() => NULL\n");
1076 return nullptr;
1077 }
1078 #endif // !TARGET_OS_DRIVERKIT
1079
1080
1081 const struct dyld_all_image_infos* _dyld_get_all_image_infos()
1082 {
1083 return gAllImages.oldAllImageInfo();
1084 }
1085
1086 bool dyld_shared_cache_some_image_overridden()
1087 {
1088 log_apis("dyld_shared_cache_some_image_overridden()\n");
1089
1090 return gAllImages.hasCacheOverrides();
1091 }
1092
1093 bool _dyld_get_shared_cache_uuid(uuid_t uuid)
1094 {
1095 log_apis("_dyld_get_shared_cache_uuid()\n");
1096
1097 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1098 if ( sharedCache == nullptr )
1099 return false;
1100
1101 if ( gAllImages.oldAllImageInfo() != nullptr ) {
1102 memcpy(uuid, gAllImages.oldAllImageInfo()->sharedCacheUUID, sizeof(uuid_t));
1103 return true;
1104 }
1105 return false;
1106 }
1107
1108 const void* _dyld_get_shared_cache_range(size_t* mappedSize)
1109 {
1110 log_apis("_dyld_get_shared_cache_range()\n");
1111
1112 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1113 if ( sharedCache != nullptr ) {
1114 *mappedSize = (size_t)sharedCache->mappedSize();
1115 return sharedCache;
1116 }
1117 *mappedSize = 0;
1118 return NULL;
1119 }
1120
1121 bool _dyld_shared_cache_optimized()
1122 {
1123 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1124 if ( sharedCache != nullptr ) {
1125 return (sharedCache->header.cacheType == kDyldSharedCacheTypeProduction);
1126 }
1127 return false;
1128 }
1129
1130 bool _dyld_shared_cache_is_locally_built()
1131 {
1132 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1133 if ( sharedCache != nullptr ) {
1134 return (sharedCache->header.locallyBuiltCache == 1);
1135 }
1136 return false;
1137 }
1138
1139 void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[])
1140 {
1141 log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count, addresses, infos);
1142
1143 // in stack crawls, common for contiguous fames to be in same image, so cache
1144 // last lookup and check if next addresss in in there before doing full search
1145 const MachOLoaded* ml = nullptr;
1146 uint64_t textSize = 0;
1147 const void* end = (void*)ml;
1148 for (unsigned i=0; i < count; ++i) {
1149 const void* addr = stripPointer(addresses[i]);
1150 bzero(&infos[i], sizeof(dyld_image_uuid_offset));
1151 if ( (ml == nullptr) || (addr < (void*)ml) || (addr > end) ) {
1152 if ( gAllImages.infoForImageMappedAt(addr, &ml, &textSize, nullptr) ) {
1153 end = (void*)((uint8_t*)ml + textSize);
1154 }
1155 else {
1156 ml = nullptr;
1157 textSize = 0;
1158 }
1159 }
1160 if ( ml != nullptr ) {
1161 infos[i].image = ml;
1162 infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)ml;
1163 ml->getUuid(infos[i].uuid);
1164 }
1165 }
1166 }
1167
1168 void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
1169 {
1170 gAllImages.addLoadNotifier(func);
1171 }
1172
1173 void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount, const struct mach_header* mhs[], const char* paths[]))
1174 {
1175 gAllImages.addBulkLoadNotifier(func);
1176 }
1177
1178 bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
1179 {
1180 log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info);
1181 addr = (void*)stripPointer(addr);
1182
1183 const MachOLoaded* ml = nullptr;
1184 if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) ) {
1185 info->mh = ml;
1186 info->dwarf_section = nullptr;
1187 info->dwarf_section_length = 0;
1188 info->compact_unwind_section = nullptr;
1189 info->compact_unwind_section_length = 0;
1190
1191 uint64_t size;
1192 if ( const void* content = ml->findSectionContent("__TEXT", "__eh_frame", size) ) {
1193 info->dwarf_section = content;
1194 info->dwarf_section_length = (uintptr_t)size;
1195 }
1196 if ( const void* content = ml->findSectionContent("__TEXT", "__unwind_info", size) ) {
1197 info->compact_unwind_section = content;
1198 info->compact_unwind_section_length = (uintptr_t)size;
1199 }
1200 return true;
1201 }
1202
1203 return false;
1204 }
1205
1206
1207 bool dyld_process_is_restricted()
1208 {
1209 log_apis("dyld_process_is_restricted()\n");
1210 return gAllImages.isRestricted();
1211 }
1212
1213
1214 const char* dyld_shared_cache_file_path()
1215 {
1216 log_apis("dyld_shared_cache_file_path()\n");
1217
1218 return gAllImages.dyldCachePath();
1219 }
1220
1221
1222 bool dyld_has_inserted_or_interposing_libraries()
1223 {
1224 log_apis("dyld_has_inserted_or_interposing_libraries()\n");
1225
1226 return gAllImages.hasInsertedOrInterposingLibraries();
1227 }
1228
1229
1230 void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count)
1231 {
1232 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh, array, count);
1233 // FIXME
1234 }
1235
1236
1237 static void* mapStartOfCache(const char* path, size_t length)
1238 {
1239 struct stat statbuf;
1240 if ( ::stat(path, &statbuf) == -1 )
1241 return NULL;
1242
1243 if ( statbuf.st_size < length )
1244 return NULL;
1245
1246 int cache_fd = ::open(path, O_RDONLY);
1247 if ( cache_fd < 0 )
1248 return NULL;
1249
1250 void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0);
1251 close(cache_fd);
1252
1253 if ( result == MAP_FAILED )
1254 return NULL;
1255
1256 return result;
1257 }
1258
1259 static const DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath, size_t& sizeMapped)
1260 {
1261 DIR* dirp = ::opendir(dirPath);
1262 if ( dirp != NULL) {
1263 dirent entry;
1264 dirent* entp = NULL;
1265 char cachePath[PATH_MAX];
1266 while ( ::readdir_r(dirp, &entry, &entp) == 0 ) {
1267 if ( entp == NULL )
1268 break;
1269 if ( entp->d_type != DT_REG )
1270 continue;
1271 if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX )
1272 continue;
1273 if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX )
1274 continue;
1275 if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX )
1276 continue;
1277 if ( const DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) {
1278 uuid_t foundUuid;
1279 cache->getUUID(foundUuid);
1280 if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) {
1281 // wrong uuid, unmap and keep looking
1282 ::munmap((void*)cache, 0x00100000);
1283 }
1284 else {
1285 // found cache
1286 closedir(dirp);
1287 sizeMapped = 0x00100000;
1288 return cache;
1289 }
1290 }
1291 }
1292 closedir(dirp);
1293 }
1294 return nullptr;
1295 }
1296
1297 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
1298 {
1299 log_apis("dyld_shared_cache_find_iterate_text()\n");
1300
1301 // see if requested cache is the active one in this process
1302 size_t sizeMapped = 0;
1303 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1304 if ( sharedCache != nullptr ) {
1305 uuid_t runningUuid;
1306 sharedCache->getUUID(runningUuid);
1307 if ( ::memcmp(runningUuid, cacheUuid, 16) != 0 )
1308 sharedCache = nullptr;
1309 }
1310 if ( sharedCache == nullptr ) {
1311 // if not, look in default location for cache files
1312 #if __IPHONE_OS_VERSION_MIN_REQUIRED
1313 const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR;
1314 #else
1315 const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR;
1316 #endif
1317 sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped);
1318 // if not there, look in extra search locations
1319 if ( sharedCache == nullptr ) {
1320 for (const char** p = extraSearchDirs; *p != nullptr; ++p) {
1321 sharedCache = findCacheInDirAndMap(cacheUuid, *p, sizeMapped);
1322 if ( sharedCache != nullptr )
1323 break;
1324 }
1325 }
1326 }
1327 if ( sharedCache == nullptr )
1328 return -1;
1329
1330 // get base address of cache
1331 __block uint64_t cacheUnslidBaseAddress = 0;
1332 sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
1333 if ( cacheUnslidBaseAddress == 0 )
1334 cacheUnslidBaseAddress = vmAddr;
1335 });
1336
1337 // iterate all images
1338 sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop) {
1339 dyld_shared_cache_dylib_text_info dylibTextInfo;
1340 dylibTextInfo.version = 2;
1341 dylibTextInfo.loadAddressUnslid = loadAddressUnslid;
1342 dylibTextInfo.textSegmentSize = textSegmentSize;
1343 dylibTextInfo.path = installName;
1344 ::memcpy(dylibTextInfo.dylibUuid, dylibUUID, 16);
1345 dylibTextInfo.textSegmentOffset = loadAddressUnslid - cacheUnslidBaseAddress;
1346 callback(&dylibTextInfo);
1347 });
1348
1349 if ( sizeMapped != 0 )
1350 ::munmap((void*)sharedCache, sizeMapped);
1351
1352 return 0;
1353 }
1354
1355 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
1356 {
1357 log_apis("dyld_shared_cache_iterate_text()\n");
1358
1359 const char* extraSearchDirs[] = { NULL };
1360 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
1361 }
1362
1363 bool dyld_need_closure(const char* execPath, const char* tempDir)
1364 {
1365 log_apis("dyld_need_closure()\n");
1366
1367 // We don't need to build a closure if the shared cache has it already
1368 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1369 if ( sharedCache != nullptr ) {
1370 if ( sharedCache->findClosure(execPath) != nullptr )
1371 return false;
1372 }
1373
1374
1375 char closurePath[PATH_MAX];
1376 if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath, closurePath, tempDir, false) ) {
1377 struct stat statbuf;
1378 return (::stat(closurePath, &statbuf) != 0);
1379 }
1380
1381 // Not containerized so no point in building a closure.
1382 return false;
1383 }
1384
1385 void _dyld_missing_symbol_abort()
1386 {
1387 // We don't know the name of the lazy symbol that is missing.
1388 // dyld3 binds all such missing symbols to this one handler.
1389 // We need the crash log to contain the backtrace so someone can
1390 // figure out the symbol.
1391
1392 auto allImageInfos = gAllImages.oldAllImageInfo();
1393 allImageInfos->errorKind = DYLD_EXIT_REASON_SYMBOL_MISSING;
1394 allImageInfos->errorClientOfDylibPath = "<unknown>";
1395 allImageInfos->errorTargetDylibPath = "<unknown>";
1396 allImageInfos->errorSymbol = "<unknown>";
1397
1398 halt("missing lazy symbol called");
1399 }
1400
1401 const char* _dyld_get_objc_selector(const char* selName)
1402 {
1403 log_apis("dyld_get_objc_selector()\n");
1404 return gAllImages.getObjCSelector(selName);
1405 }
1406
1407 void _dyld_for_each_objc_class(const char* className,
1408 void (^callback)(void* classPtr, bool isLoaded, bool* stop)) {
1409 log_apis("_dyld_for_each_objc_class()\n");
1410 gAllImages.forEachObjCClass(className, callback);
1411 }
1412
1413 void _dyld_for_each_objc_protocol(const char* protocolName,
1414 void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) {
1415 log_apis("_dyld_for_each_objc_protocol()\n");
1416 gAllImages.forEachObjCProtocol(protocolName, callback);
1417 }
1418
1419 #if !TARGET_OS_DRIVERKIT
1420 struct dyld_func {
1421 const char* name;
1422 void* implementation;
1423 };
1424
1425 static const struct dyld_func dyld_funcs[] = {
1426 {"__dyld_dlsym", (void*)dlsym }, // needs to go through generic function to get caller address
1427 {"__dyld_dlopen", (void*)dlopen },// needs to go through generic function to get caller address
1428 {"__dyld_dladdr", (void*)dyld3::dladdr },
1429 {"__dyld_image_count", (void*)dyld3::_dyld_image_count },
1430 {"__dyld_get_image_name", (void*)dyld3::_dyld_get_image_name },
1431 {"__dyld_get_image_header", (void*)dyld3::_dyld_get_image_header },
1432 {"__dyld_get_image_vmaddr_slide", (void*)dyld3::_dyld_get_image_vmaddr_slide },
1433 };
1434 #endif
1435
1436 int compatFuncLookup(const char* name, void** address)
1437 {
1438 #if !TARGET_OS_DRIVERKIT
1439 for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) {
1440 if ( strcmp(p->name, name) == 0 ) {
1441 *address = p->implementation;
1442 return true;
1443 }
1444 }
1445 *address = 0;
1446 #endif
1447 return false;
1448 }
1449
1450
1451
1452 } // namespace dyld3
1453