]> git.saurik.com Git - apple/dyld.git/blob - dyld3/APIs.cpp
dyld-519.2.1.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 <_simple.h>
28 #include <sys/errno.h>
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33 #include <TargetConditionals.h>
34 #include <CommonCrypto/CommonDigest.h>
35 #include <dispatch/dispatch.h>
36
37 #include <algorithm>
38
39 #include "dlfcn.h"
40 #include "dyld_priv.h"
41
42 #include "AllImages.h"
43 #include "MachOParser.h"
44 #include "Loading.h"
45 #include "Logging.h"
46 #include "Diagnostics.h"
47 #include "DyldSharedCache.h"
48 #include "PathOverrides.h"
49 #include "APIs.h"
50 #include "StringUtils.h"
51
52
53
54 extern "C" {
55 #include "closuredProtocol.h"
56 }
57
58
59 namespace dyld {
60 extern dyld_all_image_infos dyld_all_image_infos;
61 }
62
63
64 namespace dyld3 {
65
66
67 uint32_t _dyld_image_count(void)
68 {
69 log_apis("_dyld_image_count()\n");
70
71 return gAllImages.count();
72 }
73
74 const mach_header* _dyld_get_image_header(uint32_t imageIndex)
75 {
76 log_apis("_dyld_get_image_header(%d)\n", imageIndex);
77
78 const mach_header* loadAddress;
79 launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress);
80 if ( image.valid() )
81 return loadAddress;
82 return nullptr;
83 }
84
85 intptr_t _dyld_get_image_slide(const mach_header* mh)
86 {
87 log_apis("_dyld_get_image_slide(%p)\n", mh);
88
89 MachOParser parser(mh);
90 return parser.getSlide();
91 }
92
93 intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex)
94 {
95 log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex);
96
97 const mach_header* mh = _dyld_get_image_header(imageIndex);
98 if ( mh != nullptr )
99 return dyld3::_dyld_get_image_slide(mh);
100 return 0;
101 }
102
103 const char* _dyld_get_image_name(uint32_t imageIndex)
104 {
105 log_apis("_dyld_get_image_name(%d)\n", imageIndex);
106
107 const mach_header* loadAddress;
108 launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress);
109 if ( image.valid() )
110 return gAllImages.imagePath(image.binaryData());
111 return nullptr;
112 }
113
114
115 static bool nameMatch(const char* installName, const char* libraryName)
116 {
117 const char* leafName = strrchr(installName, '/');
118 if ( leafName == NULL )
119 leafName = installName;
120 else
121 leafName++;
122
123 // -framework case is exact match of leaf name
124 if ( strcmp(leafName, libraryName) == 0 )
125 return true;
126
127 // -lxxx case: leafName must match "lib" <libraryName> ["." ?] ".dylib"
128 size_t leafNameLen = strlen(leafName);
129 size_t libraryNameLen = strlen(libraryName);
130 if ( leafNameLen < (libraryNameLen+9) )
131 return false;
132 if ( strncmp(leafName, "lib", 3) != 0 )
133 return false;
134 if ( strcmp(&leafName[leafNameLen-6], ".dylib") != 0 )
135 return false;
136 if ( strncmp(&leafName[3], libraryName, libraryNameLen) != 0 )
137 return false;
138 return (leafName[libraryNameLen+3] == '.');
139 }
140
141
142 //
143 // BETTER, USE: dyld_get_program_sdk_version()
144 //
145 // Scans the main executable and returns the version of the specified dylib the program was built against.
146 //
147 // The library to find is the leaf name that would have been passed to linker tool
148 // (e.g. -lfoo or -framework foo would use "foo").
149 //
150 // Returns -1 if the main executable did not link against the specified library, or is malformed.
151 //
152 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
153 {
154 log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName);
155
156 __block int32_t result = -1;
157 MachOParser parser(gAllImages.mainExecutable());
158 parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
159 if ( nameMatch(loadPath, libraryName) )
160 result = currentVersion;
161 });
162 log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result);
163 return result;
164 }
165
166
167 //
168 // Searches loaded images for the requested dylib and returns its current version.
169 //
170 // The library to find is the leaf name that would have been passed to linker tool
171 // (e.g. -lfoo or -framework foo would use "foo").
172 //
173 // If the specified library is not loaded, -1 is returned.
174 //
175 int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
176 {
177 log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName);
178
179 uint32_t count = gAllImages.count();
180 for (uint32_t i=0; i < count; ++i) {
181 const mach_header* loadAddress;
182 launch_cache::Image image = gAllImages.findByLoadOrder(i, &loadAddress);
183 if ( image.valid() ) {
184 MachOParser parser(loadAddress);
185 const char* installName;
186 uint32_t currentVersion;
187 uint32_t compatVersion;
188 if ( parser.getDylibInstallName(&installName, &compatVersion, &currentVersion) && nameMatch(installName, libraryName) ) {
189 log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", currentVersion);
190 return currentVersion;
191 }
192 }
193 }
194 log_apis(" NSVersionOfRunTimeLibrary() => -1\n");
195 return -1;
196 }
197
198
199 #if __WATCH_OS_VERSION_MIN_REQUIRED
200
201 static uint32_t watchVersToIOSVers(uint32_t vers)
202 {
203 return vers + 0x00070000;
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 Platform platform;
211 uint32_t minOS;
212 uint32_t sdk;
213
214 MachOParser parser(gAllImages.mainExecutable());
215 if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
216 if ( platform == Platform::watchOS )
217 return sdk;
218 }
219 return 0;
220 }
221
222 uint32_t dyld_get_program_min_watch_os_version()
223 {
224 log_apis("dyld_get_program_min_watch_os_version()\n");
225
226 Platform platform;
227 uint32_t minOS;
228 uint32_t sdk;
229
230 MachOParser parser(gAllImages.mainExecutable());
231 if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
232 if ( platform == Platform::watchOS )
233 return minOS; // return raw minOS (not mapped to iOS version)
234 }
235 return 0;
236 }
237 #endif
238
239
240 #if TARGET_OS_BRIDGE
241
242 static uint32_t bridgeVersToIOSVers(uint32_t vers)
243 {
244 return vers + 0x00090000;
245 }
246
247 uint32_t dyld_get_program_sdk_bridge_os_version()
248 {
249 log_apis("dyld_get_program_sdk_bridge_os_version()\n");
250
251 Platform platform;
252 uint32_t minOS;
253 uint32_t sdk;
254
255 MachOParser parser(gAllImages.mainExecutable());
256 if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
257 if ( platform == Platform::bridgeOS )
258 return sdk;
259 }
260 return 0;
261 }
262
263 uint32_t dyld_get_program_min_bridge_os_version()
264 {
265 log_apis("dyld_get_program_min_bridge_os_version()\n");
266
267 Platform platform;
268 uint32_t minOS;
269 uint32_t sdk;
270
271 MachOParser parser(gAllImages.mainExecutable());
272 if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
273 if ( platform == Platform::bridgeOS )
274 return minOS; // return raw minOS (not mapped to iOS version)
275 }
276 return 0;
277 }
278
279 #endif
280
281
282 #if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE
283
284 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
285
286 static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
287 {
288 __block uint32_t foundationVers = 0;
289 __block uint32_t libSystemVers = 0;
290 MachOParser parser(mh);
291 parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
292 if ( strcmp(loadPath, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") == 0 )
293 foundationVers = currentVersion;
294 else if ( strcmp(loadPath, "/usr/lib/libSystem.B.dylib") == 0 )
295 libSystemVers = currentVersion;
296 });
297
298 struct DylibToOSMapping {
299 uint32_t dylibVersion;
300 uint32_t osVersion;
301 };
302
303 #if __IPHONE_OS_VERSION_MIN_REQUIRED
304 static const DylibToOSMapping foundationMapping[] = {
305 { PACKED_VERSION(678,24,0), 0x00020000 },
306 { PACKED_VERSION(678,26,0), 0x00020100 },
307 { PACKED_VERSION(678,29,0), 0x00020200 },
308 { PACKED_VERSION(678,47,0), 0x00030000 },
309 { PACKED_VERSION(678,51,0), 0x00030100 },
310 { PACKED_VERSION(678,60,0), 0x00030200 },
311 { PACKED_VERSION(751,32,0), 0x00040000 },
312 { PACKED_VERSION(751,37,0), 0x00040100 },
313 { PACKED_VERSION(751,49,0), 0x00040200 },
314 { PACKED_VERSION(751,58,0), 0x00040300 },
315 { PACKED_VERSION(881,0,0), 0x00050000 },
316 { PACKED_VERSION(890,1,0), 0x00050100 },
317 { PACKED_VERSION(992,0,0), 0x00060000 },
318 { PACKED_VERSION(993,0,0), 0x00060100 },
319 { PACKED_VERSION(1038,14,0),0x00070000 },
320 { PACKED_VERSION(0,0,0), 0x00070000 }
321 // We don't need to expand this table because all recent
322 // binaries have LC_VERSION_MIN_ load command.
323 };
324
325 if ( foundationVers != 0 ) {
326 uint32_t lastOsVersion = 0;
327 for (const DylibToOSMapping* p=foundationMapping; ; ++p) {
328 if ( p->dylibVersion == 0 )
329 return p->osVersion;
330 if ( foundationVers < p->dylibVersion )
331 return lastOsVersion;
332 lastOsVersion = p->osVersion;
333 }
334 }
335
336 #else
337 // Note: versions are for the GM release. The last entry should
338 // always be zero. At the start of the next major version,
339 // a new last entry needs to be added and the previous zero
340 // updated to the GM dylib version.
341 static const DylibToOSMapping libSystemMapping[] = {
342 { PACKED_VERSION(88,1,3), 0x000A0400 },
343 { PACKED_VERSION(111,0,0), 0x000A0500 },
344 { PACKED_VERSION(123,0,0), 0x000A0600 },
345 { PACKED_VERSION(159,0,0), 0x000A0700 },
346 { PACKED_VERSION(169,3,0), 0x000A0800 },
347 { PACKED_VERSION(1197,0,0), 0x000A0900 },
348 { PACKED_VERSION(0,0,0), 0x000A0900 }
349 // We don't need to expand this table because all recent
350 // binaries have LC_VERSION_MIN_ load command.
351 };
352
353 if ( libSystemVers != 0 ) {
354 uint32_t lastOsVersion = 0;
355 for (const DylibToOSMapping* p=libSystemMapping; ; ++p) {
356 if ( p->dylibVersion == 0 )
357 return p->osVersion;
358 if ( libSystemVers < p->dylibVersion )
359 return lastOsVersion;
360 lastOsVersion = p->osVersion;
361 }
362 }
363 #endif
364 return 0;
365 }
366 #endif
367
368
369 //
370 // Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the
371 // specified binary was built against.
372 //
373 // First looks for LC_VERSION_MIN_* in binary and if sdk field is
374 // not zero, return that value.
375 // Otherwise, looks for the libSystem.B.dylib the binary linked
376 // against and uses a table to convert that to an sdk version.
377 //
378 uint32_t dyld_get_sdk_version(const mach_header* mh)
379 {
380 log_apis("dyld_get_sdk_version(%p)\n", mh);
381
382 Platform platform;
383 uint32_t minOS;
384 uint32_t sdk;
385
386 if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
387 return 0;
388 MachOParser parser(mh);
389 if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
390 switch (platform) {
391 #if TARGET_OS_BRIDGE
392 case Platform::bridgeOS:
393 // new binary. sdk version looks like "2.0" but API wants "11.0"
394 return bridgeVersToIOSVers(sdk);
395 case Platform::iOS:
396 // old binary. sdk matches API semantics so can return directly.
397 return sdk;
398 #elif __WATCH_OS_VERSION_MIN_REQUIRED
399 case Platform::watchOS:
400 // new binary. sdk version looks like "2.0" but API wants "9.0"
401 return watchVersToIOSVers(sdk);
402 case Platform::iOS:
403 // old binary. sdk matches API semantics so can return directly.
404 return sdk;
405 #elif __TV_OS_VERSION_MIN_REQUIRED
406 case Platform::tvOS:
407 case Platform::iOS:
408 return sdk;
409 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
410 case Platform::iOS:
411 if ( sdk != 0 ) // old binaries might not have SDK set
412 return sdk;
413 break;
414 #else
415 case Platform::macOS:
416 if ( sdk != 0 ) // old binaries might not have SDK set
417 return sdk;
418 break;
419 #endif
420 default:
421 // wrong binary for this platform
422 break;
423 }
424 }
425
426 #if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE
427 // All watchOS and tvOS binaries should have version load command.
428 return 0;
429 #else
430 // MacOSX and iOS have old binaries without version load commmand.
431 return deriveSDKVersFromDylibs(mh);
432 #endif
433 }
434
435 uint32_t dyld_get_program_sdk_version()
436 {
437 log_apis("dyld_get_program_sdk_version()\n");
438
439 return dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
440 }
441
442 uint32_t dyld_get_min_os_version(const mach_header* mh)
443 {
444 log_apis("dyld_get_min_os_version(%p)\n", mh);
445
446 Platform platform;
447 uint32_t minOS;
448 uint32_t sdk;
449
450 if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
451 return 0;
452 MachOParser parser(mh);
453 if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
454 switch (platform) {
455 #if TARGET_OS_BRIDGE
456 case Platform::bridgeOS:
457 // new binary. sdk version looks like "2.0" but API wants "11.0"
458 return bridgeVersToIOSVers(minOS);
459 case Platform::iOS:
460 // old binary. sdk matches API semantics so can return directly.
461 return minOS;
462 #elif __WATCH_OS_VERSION_MIN_REQUIRED
463 case Platform::watchOS:
464 // new binary. OS version looks like "2.0" but API wants "9.0"
465 return watchVersToIOSVers(minOS);
466 case Platform::iOS:
467 // old binary. OS matches API semantics so can return directly.
468 return minOS;
469 #elif __TV_OS_VERSION_MIN_REQUIRED
470 case Platform::tvOS:
471 case Platform::iOS:
472 return minOS;
473 #elif __IPHONE_OS_VERSION_MIN_REQUIRED
474 case Platform::iOS:
475 return minOS;
476 #else
477 case Platform::macOS:
478 return minOS;
479 #endif
480 default:
481 // wrong binary for this platform
482 break;
483 }
484 }
485 return 0;
486 }
487
488
489 uint32_t dyld_get_program_min_os_version()
490 {
491 log_apis("dyld_get_program_min_os_version()\n");
492
493 return dyld3::dyld_get_min_os_version(gAllImages.mainExecutable());
494 }
495
496
497 bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
498 {
499 log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
500
501 if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
502 return false;
503 MachOParser parser(mh);
504 return parser.getUuid(uuid);
505 }
506
507 //
508 // _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
509 // should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
510 // and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
511 // to the size required.
512 //
513 int _NSGetExecutablePath(char* buf, uint32_t* bufsize)
514 {
515 log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
516
517 launch_cache::Image image = gAllImages.mainExecutableImage();
518 if ( image.valid() ) {
519 const char* path = gAllImages.imagePath(image.binaryData());
520 size_t pathSize = strlen(path) + 1;
521 if ( *bufsize >= pathSize ) {
522 strcpy(buf, path);
523 return 0;
524 }
525 *bufsize = (uint32_t)pathSize;
526 }
527 return -1;
528 }
529
530 void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
531 {
532 log_apis("_dyld_register_func_for_add_image(%p)\n", func);
533
534 gAllImages.addLoadNotifier(func);
535 }
536
537 void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide))
538 {
539 log_apis("_dyld_register_func_for_remove_image(%p)\n", func);
540
541 gAllImages.addUnloadNotifier(func);
542 }
543
544 void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
545 _dyld_objc_notify_init init,
546 _dyld_objc_notify_unmapped unmapped)
547 {
548 log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);
549
550 gAllImages.setObjCNotifiers(mapped, init, unmapped);
551 }
552
553
554 const mach_header* dyld_image_header_containing_address(const void* addr)
555 {
556 log_apis("dyld_image_header_containing_address(%p)\n", addr);
557
558 const mach_header* loadAddress;
559 launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
560 if ( image.valid() )
561 return loadAddress;
562 return nullptr;
563 }
564
565
566 const char* dyld_image_path_containing_address(const void* addr)
567 {
568 log_apis("dyld_image_path_containing_address(%p)\n", addr);
569
570 const mach_header* loadAddress;
571 launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
572 if ( image.valid() ) {
573 const char* path = gAllImages.imagePath(image.binaryData());
574 log_apis(" dyld_image_path_containing_address() => %s\n", path);
575 return path;
576 }
577 log_apis(" dyld_image_path_containing_address() => NULL\n");
578 return nullptr;
579 }
580
581 bool _dyld_is_memory_immutable(const void* addr, size_t length)
582 {
583 uintptr_t checkStart = (uintptr_t)addr;
584 uintptr_t checkEnd = checkStart + length;
585
586 // quick check to see if in r/o region of shared cache. If so return true.
587 const DyldSharedCache* cache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
588 if ( cache != nullptr ) {
589 __block bool firstVMAddr = 0;
590 __block bool isReadOnlyInCache = false;
591 __block bool isInCache = false;
592 cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
593 if ( firstVMAddr == 0 )
594 firstVMAddr = vmAddr;
595 uintptr_t regionStart = (uintptr_t)cache + (uintptr_t)(vmAddr - firstVMAddr);
596 uintptr_t regionEnd = regionStart + (uintptr_t)size;
597 if ( (regionStart < checkStart) && (checkEnd < regionEnd) ) {
598 isInCache = true;
599 isReadOnlyInCache = ((permissions & VM_PROT_WRITE) != 0);
600 }
601 });
602 if ( isInCache )
603 return isReadOnlyInCache;
604 }
605
606 // go slow route of looking at each image's segments
607 const mach_header* loadAddress;
608 uint8_t permissions;
609 launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress, &permissions);
610 if ( !image.valid() )
611 return false;
612 if ( (permissions & VM_PROT_WRITE) != 0 )
613 return false;
614 return !gAllImages.imageUnloadable(image, loadAddress);
615 }
616
617
618 int dladdr(const void* addr, Dl_info* info)
619 {
620 log_apis("dladdr(%p, %p)\n", addr, info);
621
622 const mach_header* loadAddress;
623 launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress);
624 if ( !image.valid() ) {
625 log_apis(" dladdr() => 0\n");
626 return 0;
627 }
628 MachOParser parser(loadAddress);
629 info->dli_fname = gAllImages.imagePath(image.binaryData());
630 info->dli_fbase = (void*)(loadAddress);
631 if ( addr == info->dli_fbase ) {
632 // special case lookup of header
633 info->dli_sname = "__dso_handle";
634 info->dli_saddr = info->dli_fbase;
635 }
636 else if ( parser.findClosestSymbol(addr, &(info->dli_sname), (const void**)&(info->dli_saddr)) ) {
637 // never return the mach_header symbol
638 if ( info->dli_saddr == info->dli_fbase ) {
639 info->dli_sname = nullptr;
640 info->dli_saddr = nullptr;
641 }
642 // strip off leading underscore
643 else if ( (info->dli_sname != nullptr) && (info->dli_sname[0] == '_') ) {
644 info->dli_sname = info->dli_sname + 1;
645 }
646 }
647 else {
648 info->dli_sname = nullptr;
649 info->dli_saddr = nullptr;
650 }
651 log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
652 return 1;
653 }
654
655
656 struct PerThreadErrorMessage
657 {
658 size_t sizeAllocated;
659 bool valid;
660 char message[1];
661 };
662
663 static pthread_key_t dlerror_perThreadKey()
664 {
665 static dispatch_once_t onceToken;
666 static pthread_key_t dlerrorPThreadKey;
667 dispatch_once(&onceToken, ^{
668 pthread_key_create(&dlerrorPThreadKey, &free);
669 });
670 return dlerrorPThreadKey;
671 }
672
673 static void clearErrorString()
674 {
675 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
676 if ( errorBuffer != nullptr )
677 errorBuffer->valid = false;
678 }
679
680 __attribute__((format(printf, 1, 2)))
681 static void setErrorString(const char* format, ...)
682 {
683 _SIMPLE_STRING buf = _simple_salloc();
684 if ( buf != nullptr ) {
685 va_list list;
686 va_start(list, format);
687 _simple_vsprintf(buf, format, list);
688 va_end(list);
689 size_t strLen = strlen(_simple_string(buf)) + 1;
690 size_t sizeNeeded = sizeof(PerThreadErrorMessage) + strLen;
691 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
692 if ( errorBuffer != nullptr ) {
693 if ( errorBuffer->sizeAllocated < sizeNeeded ) {
694 free(errorBuffer);
695 errorBuffer = nullptr;
696 }
697 }
698 if ( errorBuffer == nullptr ) {
699 size_t allocSize = std::max(sizeNeeded, (size_t)256);
700 PerThreadErrorMessage* p = (PerThreadErrorMessage*)malloc(allocSize);
701 p->sizeAllocated = allocSize;
702 p->valid = false;
703 pthread_setspecific(dlerror_perThreadKey(), p);
704 errorBuffer = p;
705 }
706 strcpy(errorBuffer->message, _simple_string(buf));
707 errorBuffer->valid = true;
708 _simple_sfree(buf);
709 }
710 }
711
712 char* dlerror()
713 {
714 log_apis("dlerror()\n");
715
716 PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey());
717 if ( errorBuffer != nullptr ) {
718 if ( errorBuffer->valid ) {
719 // you can only call dlerror() once, then the message is cleared
720 errorBuffer->valid = false;
721 return errorBuffer->message;
722 }
723 }
724 return nullptr;
725 }
726
727 #if __arm64__
728 #define CURRENT_CPU_TYPE CPU_TYPE_ARM64
729 #elif __arm__
730 #define CURRENT_CPU_TYPE CPU_TYPE_ARM
731 #endif
732
733
734 class VIS_HIDDEN RecursiveAutoLock
735 {
736 public:
737 RecursiveAutoLock() {
738 pthread_mutex_lock(&_sMutex);
739 }
740 ~RecursiveAutoLock() {
741 pthread_mutex_unlock(&_sMutex);
742 }
743 private:
744 static pthread_mutex_t _sMutex;
745 };
746
747 pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
748
749 static void* makeDlHandle(const mach_header* mh, bool dontContinue)
750 {
751 uintptr_t flags = (dontContinue ? 1 : 0);
752 return (void*)((((uintptr_t)mh) >> 5) | flags);
753 }
754
755 VIS_HIDDEN
756 void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue)
757 {
758 *dontContinue = (((uintptr_t)h) & 1);
759 *mh = (const mach_header*)((((uintptr_t)h) & (-2)) << 5);
760 }
761
762 int dlclose(void* handle)
763 {
764 log_apis("dlclose(%p)\n", handle);
765
766 // silently accept magic handles for main executable
767 if ( handle == RTLD_MAIN_ONLY )
768 return 0;
769 if ( handle == RTLD_DEFAULT )
770 return 0;
771
772 // from here on, serialize all dlopen()s
773 RecursiveAutoLock dlopenSerializer;
774
775 const mach_header* mh;
776 bool dontContinue;
777 parseDlHandle(handle, &mh, &dontContinue);
778 launch_cache::Image image = gAllImages.findByLoadAddress(mh);
779 if ( image.valid() ) {
780 // removes image if reference count went to zero
781 if ( !image.neverUnload() )
782 gAllImages.decRefCount(mh);
783 clearErrorString();
784 return 0;
785 }
786 else {
787 setErrorString("invalid handle passed to dlclose()");
788 return -1;
789 }
790 }
791
792
793
794 VIS_HIDDEN
795 const mach_header* loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount)
796 {
797 launch_cache::Image topImage(imageToLoad);
798 uint32_t maxLoad = topImage.maxLoadCount();
799 // first construct array of all BinImage* objects that dlopen'ed image depends on
800 const dyld3::launch_cache::binary_format::Image* fullImageList[maxLoad];
801 dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]);
802 imageSet.add(imageToLoad);
803 STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
804 gAllImages.copyCurrentGroups(currentGroupsList);
805 if ( !topImage.recurseAllDependentImages(currentGroupsList, imageSet, nullptr) ) {
806 diag.error("unexpected > %d images loaded", maxLoad);
807 return nullptr;
808 }
809
810 // build array of BinImage* that are not already loaded
811 const dyld3::launch_cache::binary_format::Image* toLoadImageList[maxLoad];
812 const dyld3::launch_cache::binary_format::Image** toLoadImageArray = toLoadImageList;
813 __block int needToLoadCount = 0;
814 imageSet.forEach(^(const dyld3::launch_cache::binary_format::Image* aBinImage) {
815 if ( gAllImages.findLoadAddressByImage(aBinImage) == nullptr )
816 toLoadImageArray[needToLoadCount++] = aBinImage;
817 });
818 assert(needToLoadCount > 0);
819
820 // build one array of all existing and to-be-loaded images
821 uint32_t alreadyLoadImageCount = gAllImages.count();
822 STACK_ALLOC_DYNARRAY(loader::ImageInfo, alreadyLoadImageCount + needToLoadCount, allImages);
823 loader::ImageInfo* allImagesArray = &allImages[0];
824 gAllImages.forEachImage(^(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop) {
825 launch_cache::ImageGroup grp = image.group();
826 loader::ImageInfo& info= allImagesArray[imageIndex];
827 info.imageData = image.binaryData();
828 info.loadAddress = loadAddress;
829 info.groupNum = grp.groupNum();
830 info.indexInGroup = grp.indexInGroup(info.imageData);
831 info.previouslyFixedUp = true;
832 info.justMapped = false;
833 info.justUsedFromDyldCache = false;
834 info.neverUnload = false;
835 });
836 for (int i=0; i < needToLoadCount; ++i) {
837 launch_cache::Image img(toLoadImageArray[i]);
838 launch_cache::ImageGroup grp = img.group();
839 loader::ImageInfo& info= allImages[alreadyLoadImageCount+i];
840 info.imageData = toLoadImageArray[i];
841 info.loadAddress = nullptr;
842 info.groupNum = grp.groupNum();
843 info.indexInGroup = grp.indexInGroup(img.binaryData());
844 info.previouslyFixedUp = false;
845 info.justMapped = false;
846 info.justUsedFromDyldCache = false;
847 info.neverUnload = false;
848 }
849
850 // map new images and apply all fixups
851 mapAndFixupImages(diag, allImages, (const uint8_t*)gAllImages.cacheLoadAddress(), &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs);
852 if ( diag.hasError() )
853 return nullptr;
854 const mach_header* topLoadAddress = allImages[alreadyLoadImageCount].loadAddress;
855
856 // bump dlopen refcount of image directly loaded
857 if ( bumpDlopenCount )
858 gAllImages.incRefCount(topLoadAddress);
859
860 // tell gAllImages about new images
861 dyld3::launch_cache::DynArray<loader::ImageInfo> newImages(needToLoadCount, &allImages[alreadyLoadImageCount]);
862 gAllImages.addImages(newImages);
863
864 // tell gAllImages about any old images which now have never unload set
865 for (int i=0; i < alreadyLoadImageCount; ++i) {
866 if (allImages[i].neverUnload && !allImages[i].imageData->neverUnload)
867 gAllImages.setNeverUnload(allImages[i]);
868 }
869
870 // run initializers
871 gAllImages.runInitialzersBottomUp(topLoadAddress);
872
873 return topLoadAddress;
874 }
875
876
877 void* dlopen(const char* path, int mode)
878 {
879 log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
880
881 clearErrorString();
882
883 // passing NULL for path means return magic object
884 if ( path == NULL ) {
885 // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images
886 if ( (mode & RTLD_FIRST) != 0 )
887 return RTLD_MAIN_ONLY;
888 else
889 return RTLD_DEFAULT;
890 }
891
892 // from here on, serialize all dlopen()s
893 RecursiveAutoLock dlopenSerializer;
894
895 const char* leafName = strrchr(path, '/');
896 if ( leafName != nullptr )
897 ++leafName;
898 else
899 leafName = path;
900
901 // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
902 bool dontContinue = (mode & RTLD_FIRST);
903 bool bumpRefCount = true;
904
905 // check if dylib with same inode/mtime is already loaded
906 __block const mach_header* alreadyLoadMH = nullptr;
907 struct stat statBuf;
908 if ( stat(path, &statBuf) == 0 ) {
909 alreadyLoadMH = gAllImages.alreadyLoaded(statBuf.st_ino, statBuf.st_mtime, bumpRefCount);
910 if ( alreadyLoadMH != nullptr) {
911 log_apis(" dlopen: path inode/mtime matches already loaded image\n");
912 void* result = makeDlHandle(alreadyLoadMH, dontContinue);
913 log_apis(" dlopen(%s) => %p\n", leafName, result);
914 return result;
915 }
916 }
917
918 // check if already loaded, and if so, just bump ref-count
919 gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) {
920 alreadyLoadMH = gAllImages.alreadyLoaded(possiblePath, bumpRefCount);
921 if ( alreadyLoadMH != nullptr ) {
922 log_apis(" dlopen: matches already loaded image %s\n", possiblePath);
923 stop = true;
924 }
925 });
926 if ( alreadyLoadMH != nullptr) {
927 void* result = makeDlHandle(alreadyLoadMH, dontContinue);
928 log_apis(" dlopen(%s) => %p\n", leafName, result);
929 return result;
930 }
931
932 // it may be that the path supplied is a symlink to something already loaded
933 char resolvedPath[PATH_MAX];
934 const char* realPathResult = realpath(path, resolvedPath);
935 // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
936 bool checkRealPathToo = ((realPathResult != nullptr) || (errno == ENOENT)) && (strcmp(path, resolvedPath) != 0);
937 if ( checkRealPathToo ) {
938 alreadyLoadMH = gAllImages.alreadyLoaded(resolvedPath, bumpRefCount);
939 log_apis(" dlopen: real path=%s\n", resolvedPath);
940 if ( alreadyLoadMH != nullptr) {
941 void* result = makeDlHandle(alreadyLoadMH, dontContinue);
942 log_apis(" dlopen(%s) => %p\n", leafName, result);
943 return result;
944 }
945 }
946
947 // check if image is in a known ImageGroup
948 __block const launch_cache::binary_format::Image* imageToLoad = nullptr;
949 gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) {
950 log_apis(" dlopen: checking for pre-built closure for path: %s\n", possiblePath);
951 imageToLoad = gAllImages.findImageInKnownGroups(possiblePath);
952 if ( imageToLoad != nullptr )
953 stop = true;
954 });
955 if ( (imageToLoad == nullptr) && checkRealPathToo ) {
956 gPathOverrides.forEachPathVariant(resolvedPath, ^(const char* possiblePath, bool& stop) {
957 log_apis(" dlopen: checking for pre-built closure for real path: %s\n", possiblePath);
958 imageToLoad = gAllImages.findImageInKnownGroups(possiblePath);
959 if ( imageToLoad != nullptr )
960 stop = true;
961 });
962 }
963
964 // check if image from a known ImageGroup is already loaded (via a different path)
965 if ( imageToLoad != nullptr ) {
966 alreadyLoadMH = gAllImages.alreadyLoaded(imageToLoad, bumpRefCount);
967 if ( alreadyLoadMH != nullptr) {
968 void* result = makeDlHandle(alreadyLoadMH, dontContinue);
969 log_apis(" dlopen(%s) => %p\n", leafName, result);
970 return result;
971 }
972 }
973
974 // RTLD_NOLOAD means do nothing if image not already loaded
975 if ( mode & RTLD_NOLOAD ) {
976 log_apis(" dlopen(%s) => NULL\n", leafName);
977 return nullptr;
978 }
979
980 // if we have a closure, optimistically use it. If out of date, it will fail
981 if ( imageToLoad != nullptr ) {
982 log_apis(" dlopen: trying existing closure image=%p\n", imageToLoad);
983 Diagnostics diag;
984 const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true);
985 if ( diag.noError() ) {
986 void* result = makeDlHandle(topLoadAddress, dontContinue);
987 log_apis(" dlopen(%s) => %p\n", leafName, result);
988 return result;
989 }
990 // image is no longer valid, will need to build one
991 imageToLoad = nullptr;
992 log_apis(" dlopen: existing closure no longer valid\n");
993 }
994
995 // if no existing closure, RPC to closured to create one
996 const char* closuredErrorMessages[3];
997 int closuredErrorMessagesCount = 0;
998 if ( imageToLoad == nullptr ) {
999 imageToLoad = gAllImages.messageClosured(path, "dlopen", closuredErrorMessages, closuredErrorMessagesCount);
1000 }
1001
1002 // load images using new closure
1003 if ( imageToLoad != nullptr ) {
1004 log_apis(" dlopen: using closured built image=%p\n", imageToLoad);
1005 Diagnostics diag;
1006 const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true);
1007 if ( diag.noError() ) {
1008 void* result = makeDlHandle(topLoadAddress, dontContinue);
1009 log_apis(" dlopen(%s) => %p\n", leafName, result);
1010 return result;
1011 }
1012 if ( closuredErrorMessagesCount < 3 ) {
1013 closuredErrorMessages[closuredErrorMessagesCount++] = strdup(diag.errorMessage());
1014 }
1015 }
1016
1017 // otherwise, closured failed to build needed load info
1018 switch ( closuredErrorMessagesCount ) {
1019 case 0:
1020 setErrorString("dlopen(%s, 0x%04X): closured error", path, mode);
1021 log_apis(" dlopen: closured error\n");
1022 break;
1023 case 1:
1024 setErrorString("dlopen(%s, 0x%04X): %s", path, mode, closuredErrorMessages[0]);
1025 log_apis(" dlopen: closured error: %s\n", closuredErrorMessages[0]);
1026 break;
1027 case 2:
1028 setErrorString("dlopen(%s, 0x%04X): %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1]);
1029 log_apis(" dlopen: closured error: %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1]);
1030 break;
1031 case 3:
1032 setErrorString("dlopen(%s, 0x%04X): %s %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]);
1033 log_apis(" dlopen: closured error: %s %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]);
1034 break;
1035 }
1036 for (int i=0; i < closuredErrorMessagesCount;++i)
1037 free((void*)closuredErrorMessages[i]);
1038
1039 log_apis(" dlopen(%s) => NULL\n", leafName);
1040
1041 return nullptr;
1042 }
1043
1044 bool dlopen_preflight(const char* path)
1045 {
1046 log_apis("dlopen_preflight(%s)\n", path);
1047
1048 if ( gAllImages.alreadyLoaded(path, false) != nullptr )
1049 return true;
1050
1051 if ( gAllImages.findImageInKnownGroups(path) != nullptr )
1052 return true;
1053
1054 // map whole file
1055 struct stat statBuf;
1056 if ( ::stat(path, &statBuf) != 0 )
1057 return false;
1058 int fd = ::open(path, O_RDONLY);
1059 if ( fd < 0 )
1060 return false;
1061 const void* fileBuffer = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1062 ::close(fd);
1063 if ( fileBuffer == MAP_FAILED )
1064 return false;
1065 size_t mappedSize = (size_t)statBuf.st_size;
1066
1067 // check if it is current arch mach-o or fat with slice for current arch
1068 __block bool result = false;
1069 __block Diagnostics diag;
1070 if ( MachOParser::isMachO(diag, fileBuffer, mappedSize) ) {
1071 result = true;
1072 }
1073 else {
1074 if ( FatUtil::isFatFile(fileBuffer) ) {
1075 FatUtil::forEachSlice(diag, fileBuffer, mappedSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSz, bool& stop) {
1076 if ( MachOParser::isMachO(diag, sliceStart, sliceSz) ) {
1077 result = true;
1078 stop = true;
1079 }
1080 });
1081 }
1082 }
1083 ::munmap((void*)fileBuffer, mappedSize);
1084
1085 // FIXME: may be symlink to something in dyld cache
1086
1087 // FIXME: maybe ask closured
1088
1089 return result;
1090 }
1091
1092 static void* dlsym_search(const char* symName, const mach_header* startImageLoadAddress, const launch_cache::Image& startImage, bool searchStartImage, MachOParser::DependentFinder reExportFollower)
1093 {
1094 // construct array of all BinImage* objects that dlopen'ed image depends on
1095 uint32_t maxLoad = startImage.maxLoadCount();
1096 const dyld3::launch_cache::binary_format::Image* fullImageList[maxLoad];
1097 dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]);
1098 imageSet.add(startImage.binaryData());
1099 STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
1100 gAllImages.copyCurrentGroups(currentGroupsList);
1101
1102 __block void* result = nullptr;
1103 auto handler = ^(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop) {
1104 const mach_header* loadAddress = gAllImages.findLoadAddressByImage(aBinImage);
1105 if ( !searchStartImage && (loadAddress == startImageLoadAddress) )
1106 return;
1107 if ( loadAddress != nullptr ) {
1108 MachOParser parser(loadAddress);
1109 if ( parser.hasExportedSymbol(symName, reExportFollower, &result) ) {
1110 stop = true;
1111 }
1112 }
1113 };
1114
1115 bool stop = false;
1116 handler(startImage.binaryData(), stop);
1117 if (stop)
1118 return result;
1119
1120 // check each dependent image for symbol
1121 if ( !startImage.recurseAllDependentImages(currentGroupsList, imageSet, handler) ) {
1122 setErrorString("unexpected > %d images loaded", maxLoad);
1123 return nullptr;
1124 }
1125 return result;
1126 }
1127
1128 void* dlsym(void* handle, const char* symbolName)
1129 {
1130 log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
1131
1132 clearErrorString();
1133
1134 // dlsym() assumes symbolName passed in is same as in C source code
1135 // dyld assumes all symbol names have an underscore prefix
1136 char underscoredName[strlen(symbolName)+2];
1137 underscoredName[0] = '_';
1138 strcpy(&underscoredName[1], symbolName);
1139
1140 // this block is only used if hasExportedSymbol() needs to trace re-exported dylibs to find a symbol
1141 MachOParser::DependentFinder reExportFollower = ^(uint32_t targetDepIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
1142 if ( (strncmp(depLoadPath, "@rpath/", 7) == 0) && (extra != nullptr) ) {
1143 const mach_header* parentMH = (mach_header*)extra;
1144 launch_cache::Image parentImage = gAllImages.findByLoadAddress(parentMH);
1145 if ( parentImage.valid() ) {
1146 STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
1147 gAllImages.copyCurrentGroups(currentGroupsList);
1148 parentImage.forEachDependentImage(currentGroupsList, ^(uint32_t parentDepIndex, dyld3::launch_cache::Image parentDepImage, dyld3::launch_cache::Image::LinkKind kind, bool &stop) {
1149 if ( parentDepIndex != targetDepIndex )
1150 return;
1151 const mach_header* parentDepMH = gAllImages.findLoadAddressByImage(parentDepImage.binaryData());
1152 if ( parentDepMH != nullptr ) {
1153 *foundMH = parentDepMH;
1154 stop = true;
1155 }
1156 });
1157 }
1158 }
1159 else {
1160 *foundMH = gAllImages.alreadyLoaded(depLoadPath, false);
1161 }
1162 return (*foundMH != nullptr);
1163 };
1164
1165 if ( handle == RTLD_DEFAULT ) {
1166 // magic "search all in load order" handle
1167 for (uint32_t index=0; index < gAllImages.count(); ++index) {
1168 const mach_header* loadAddress;
1169 launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress);
1170 if ( image.valid() ) {
1171 MachOParser parser(loadAddress);
1172 void* result;
1173 //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
1174 if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) {
1175 log_apis(" dlsym() => %p\n", result);
1176 return result;
1177 }
1178 }
1179 }
1180 setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
1181 log_apis(" dlsym() => NULL\n");
1182 return nullptr;
1183 }
1184 else if ( handle == RTLD_MAIN_ONLY ) {
1185 // magic "search only main executable" handle
1186 MachOParser parser(gAllImages.mainExecutable());
1187 //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress);
1188 void* result;
1189 if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) {
1190 log_apis(" dlsym() => %p\n", result);
1191 return result;
1192 }
1193 setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
1194 log_apis(" dlsym() => NULL\n");
1195 return nullptr;
1196 }
1197
1198 // rest of cases search in dependency order
1199 const mach_header* startImageLoadAddress;
1200 launch_cache::Image startImage(nullptr);
1201 void* result = nullptr;
1202 if ( handle == RTLD_NEXT ) {
1203 // magic "search what I would see" handle
1204 void* callerAddress = __builtin_return_address(0);
1205 startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress);
1206 if ( ! startImage.valid() ) {
1207 setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
1208 return nullptr;
1209 }
1210 result = dlsym_search(underscoredName, startImageLoadAddress, startImage, false, reExportFollower);
1211 }
1212 else if ( handle == RTLD_SELF ) {
1213 // magic "search me, then what I would see" handle
1214 void* callerAddress = __builtin_return_address(0);
1215 startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress);
1216 if ( ! startImage.valid() ) {
1217 setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName, callerAddress);
1218 return nullptr;
1219 }
1220 result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower);
1221 }
1222 else {
1223 // handle value was something returned by dlopen()
1224 bool dontContinue;
1225 parseDlHandle(handle, &startImageLoadAddress, &dontContinue);
1226 startImage = gAllImages.findByLoadAddress(startImageLoadAddress);
1227 if ( !startImage.valid() ) {
1228 setErrorString("dlsym(%p, %s): invalid handle", handle, symbolName);
1229 log_apis(" dlsym() => NULL\n");
1230 return nullptr;
1231 }
1232 if ( dontContinue ) {
1233 // RTLD_FIRST only searches one place
1234 MachOParser parser(startImageLoadAddress);
1235 parser.hasExportedSymbol(underscoredName, reExportFollower, &result);
1236 }
1237 else {
1238 result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower);
1239 }
1240 }
1241
1242 if ( result != nullptr ) {
1243 log_apis(" dlsym() => %p\n", result);
1244 return result;
1245 }
1246
1247 setErrorString("dlsym(%p, %s): symbol not found", handle, symbolName);
1248 log_apis(" dlsym() => NULL\n");
1249 return nullptr;
1250 }
1251
1252
1253 const struct dyld_all_image_infos* _dyld_get_all_image_infos()
1254 {
1255 return gAllImages.oldAllImageInfo();
1256 }
1257
1258 bool dyld_shared_cache_some_image_overridden()
1259 {
1260 log_apis("dyld_shared_cache_some_image_overridden()\n");
1261
1262 assert(0 && "not implemented yet");
1263 }
1264
1265 bool _dyld_get_shared_cache_uuid(uuid_t uuid)
1266 {
1267 log_apis("_dyld_get_shared_cache_uuid()\n");
1268
1269 if ( gAllImages.oldAllImageInfo() != nullptr ) {
1270 memcpy(uuid, gAllImages.oldAllImageInfo()->sharedCacheUUID, sizeof(uuid_t));
1271 return true;
1272 }
1273 return false;
1274 }
1275
1276 const void* _dyld_get_shared_cache_range(size_t* mappedSize)
1277 {
1278 log_apis("_dyld_get_shared_cache_range()\n");
1279
1280 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1281 if ( sharedCache != nullptr ) {
1282 *mappedSize = (size_t)sharedCache->mappedSize();
1283 return sharedCache;
1284 }
1285 *mappedSize = 0;
1286 return NULL;
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
1293 const mach_header* mh = dyld_image_header_containing_address(addr);
1294 if ( mh == nullptr )
1295 return false;
1296
1297 info->mh = mh;
1298 info->dwarf_section = nullptr;
1299 info->dwarf_section_length = 0;
1300 info->compact_unwind_section = nullptr;
1301 info->compact_unwind_section_length = 0;
1302
1303 MachOParser parser(mh);
1304 parser.forEachSection(^(const char* segName, const char* sectName, uint32_t flags, const void* content, size_t sectSize, bool illegalSectionSize, bool& stop) {
1305 if ( strcmp(segName, "__TEXT") == 0 ) {
1306 if ( strcmp(sectName, "__eh_frame") == 0 ) {
1307 info->dwarf_section = content;
1308 info->dwarf_section_length = sectSize;
1309 }
1310 else if ( strcmp(sectName, "__unwind_info") == 0 ) {
1311 info->compact_unwind_section = content;
1312 info->compact_unwind_section_length = sectSize;
1313 }
1314 }
1315 });
1316
1317 return true;
1318 }
1319
1320
1321 bool dyld_process_is_restricted()
1322 {
1323 log_apis("dyld_process_is_restricted()\n");
1324
1325 launch_cache::Closure closure(gAllImages.mainClosure());
1326 return closure.isRestricted();
1327 }
1328
1329
1330 const char* dyld_shared_cache_file_path()
1331 {
1332 log_apis("dyld_shared_cache_file_path()\n");
1333
1334 return gAllImages.dyldCachePath();
1335 }
1336
1337
1338 void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count)
1339 {
1340 log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh, array, count);
1341 // FIXME
1342 }
1343
1344
1345 static void* mapStartOfCache(const char* path, size_t length)
1346 {
1347 struct stat statbuf;
1348 if ( ::stat(path, &statbuf) == -1 )
1349 return NULL;
1350
1351 if ( statbuf.st_size < length )
1352 return NULL;
1353
1354 int cache_fd = ::open(path, O_RDONLY);
1355 if ( cache_fd < 0 )
1356 return NULL;
1357
1358 void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0);
1359 close(cache_fd);
1360
1361 if ( result == MAP_FAILED )
1362 return NULL;
1363
1364 return result;
1365 }
1366
1367 static const DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath, size_t& sizeMapped)
1368 {
1369 DIR* dirp = ::opendir(dirPath);
1370 if ( dirp != NULL) {
1371 dirent entry;
1372 dirent* entp = NULL;
1373 char cachePath[PATH_MAX];
1374 while ( ::readdir_r(dirp, &entry, &entp) == 0 ) {
1375 if ( entp == NULL )
1376 break;
1377 if ( entp->d_type != DT_REG )
1378 continue;
1379 if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX )
1380 continue;
1381 if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX )
1382 continue;
1383 if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX )
1384 continue;
1385 if ( const DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) {
1386 uuid_t foundUuid;
1387 cache->getUUID(foundUuid);
1388 if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) {
1389 // wrong uuid, unmap and keep looking
1390 ::munmap((void*)cache, 0x00100000);
1391 }
1392 else {
1393 // found cache
1394 closedir(dirp);
1395 sizeMapped = 0x00100000;
1396 return cache;
1397 }
1398 }
1399 }
1400 closedir(dirp);
1401 }
1402 return nullptr;
1403 }
1404
1405 int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info))
1406 {
1407 log_apis("dyld_shared_cache_find_iterate_text()\n");
1408
1409 // see if requested cache is the active one in this process
1410 size_t sizeMapped = 0;
1411 const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
1412 if ( sharedCache != nullptr ) {
1413 uuid_t runningUuid;
1414 sharedCache->getUUID(runningUuid);
1415 if ( ::memcmp(runningUuid, cacheUuid, 16) != 0 )
1416 sharedCache = nullptr;
1417 }
1418 if ( sharedCache == nullptr ) {
1419 // if not, look in default location for cache files
1420 #if __IPHONE_OS_VERSION_MIN_REQUIRED
1421 const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR;
1422 #else
1423 const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR;
1424 #endif
1425 sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped);
1426 // if not there, look in extra search locations
1427 if ( sharedCache == nullptr ) {
1428 for (const char** p = extraSearchDirs; *p != nullptr; ++p) {
1429 sharedCache = findCacheInDirAndMap(cacheUuid, *p, sizeMapped);
1430 if ( sharedCache != nullptr )
1431 break;
1432 }
1433 }
1434 }
1435 if ( sharedCache == nullptr )
1436 return -1;
1437
1438 // get base address of cache
1439 __block uint64_t cacheUnslidBaseAddress = 0;
1440 sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
1441 if ( cacheUnslidBaseAddress == 0 )
1442 cacheUnslidBaseAddress = vmAddr;
1443 });
1444
1445 // iterate all images
1446 sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName) {
1447 dyld_shared_cache_dylib_text_info dylibTextInfo;
1448 dylibTextInfo.version = 2;
1449 dylibTextInfo.loadAddressUnslid = loadAddressUnslid;
1450 dylibTextInfo.textSegmentSize = textSegmentSize;
1451 dylibTextInfo.path = installName;
1452 ::memcpy(dylibTextInfo.dylibUuid, dylibUUID, 16);
1453 dylibTextInfo.textSegmentOffset = loadAddressUnslid - cacheUnslidBaseAddress;
1454 callback(&dylibTextInfo);
1455 });
1456
1457 if ( sizeMapped != 0 )
1458 ::munmap((void*)sharedCache, sizeMapped);
1459
1460 return 0;
1461 }
1462
1463 int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info))
1464 {
1465 log_apis("dyld_shared_cache_iterate_text()\n");
1466
1467 const char* extraSearchDirs[] = { NULL };
1468 return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
1469 }
1470
1471
1472
1473 } // namespace dyld3
1474