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