dyld-640.2.tar.gz
[apple/dyld.git] / dyld3 / MachOFile.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 #include <stdlib.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <TargetConditionals.h>
29 #include <mach/host_info.h>
30 #include <mach/mach.h>
31 #include <mach/mach_host.h>
32
33 #include "MachOFile.h"
34 #include "SupportedArchs.h"
35
36 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
37 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
38 #endif
39
40 #ifndef CPU_SUBTYPE_ARM64_E
41 #define CPU_SUBTYPE_ARM64_E 2
42 #endif
43
44 #ifndef CPU_SUBTYPE_ARM64_32_V8
45 #define CPU_SUBTYPE_ARM64_32_V8 1
46 #endif
47
48 #ifndef CPU_TYPE_ARM64_32
49 #ifndef CPU_ARCH_ABI64_32
50 #define CPU_ARCH_ABI64_32 0x02000000
51 #endif
52 #define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
53 #endif
54
55 namespace dyld3 {
56
57 //////////////////////////// FatFile ////////////////////////////////////////
58
59 const FatFile* FatFile::isFatFile(const void* fileStart)
60 {
61 const FatFile* fileStartAsFat = (FatFile*)fileStart;
62 if ( (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC)) || (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC_64)) )
63 return fileStartAsFat;
64 else
65 return nullptr;
66 }
67
68 void FatFile::forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const
69 {
70 if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
71 if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
72 diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
73 return;
74 }
75 bool stop = false;
76 const fat_arch* const archs = (fat_arch*)(((char*)this)+sizeof(fat_header));
77 for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
78 uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
79 uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
80 uint32_t offset = OSSwapBigToHostInt32(archs[i].offset);
81 uint32_t len = OSSwapBigToHostInt32(archs[i].size);
82 if ( greaterThanAddOrOverflow(offset, len, fileLen) ) {
83 diag.error("slice %d extends beyond end of file", i);
84 return;
85 }
86 callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
87 if ( stop )
88 break;
89 }
90 }
91 else if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC_64) ) {
92 if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
93 diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
94 return;
95 }
96 bool stop = false;
97 const fat_arch_64* const archs = (fat_arch_64*)(((char*)this)+sizeof(fat_header));
98 for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
99 uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
100 uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
101 uint64_t offset = OSSwapBigToHostInt64(archs[i].offset);
102 uint64_t len = OSSwapBigToHostInt64(archs[i].size);
103 if ( greaterThanAddOrOverflow(offset, len, fileLen) ) {
104 diag.error("slice %d extends beyond end of file", i);
105 return;
106 }
107 callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
108 if ( stop )
109 break;
110 }
111 }
112 else {
113 diag.error("not a fat file");
114 }
115 }
116
117 bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const char* archName, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
118 {
119 missingSlice = false;
120 if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
121 return false;
122
123 __block bool found = false;
124 forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
125 const char* sliceArchName = MachOFile::archName(sliceCpuType, sliceCpuSubType);
126 if ( strcmp(sliceArchName, archName) == 0 ) {
127 sliceOffset = (char*)sliceStart - (char*)this;
128 sliceLen = sliceSize;
129 found = true;
130 stop = true;
131 }
132 });
133 if ( diag.hasError() )
134 return false;
135
136 if ( !found )
137 missingSlice = true;
138
139 // when looking for x86_64h fallback to x86_64
140 if ( !found && (strcmp(archName, "x86_64h") == 0) )
141 return isFatFileWithSlice(diag, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
142
143 return found;
144 }
145
146
147 //////////////////////////// MachOFile ////////////////////////////////////////
148
149
150 const MachOFile::ArchInfo MachOFile::_s_archInfos[] = {
151 { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
152 { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H },
153 { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
154 { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
155 #if SUPPORT_ARCH_arm64e
156 { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E },
157 #endif
158 #if SUPPORT_ARCH_arm64_32
159 { "arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8 },
160 #endif
161 { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
162 { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
163 { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }
164 };
165
166 const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
167 { "macOS", Platform::macOS, LC_VERSION_MIN_MACOSX },
168 { "iOS", Platform::iOS, LC_VERSION_MIN_IPHONEOS },
169 { "tvOS", Platform::tvOS, LC_VERSION_MIN_TVOS },
170 { "watchOS", Platform::watchOS, LC_VERSION_MIN_WATCHOS },
171 { "bridgeOS", Platform::bridgeOS, LC_BUILD_VERSION },
172 { "iOSMac", Platform::iOSMac, LC_BUILD_VERSION },
173 { "iOS-sim", Platform::iOS_simulator, LC_BUILD_VERSION },
174 { "tvOS-sim", Platform::tvOS_simulator, LC_BUILD_VERSION },
175 { "watchOS-sim", Platform::watchOS_simulator, LC_BUILD_VERSION },
176 };
177
178
179 bool MachOFile::is64() const
180 {
181 return (this->magic == MH_MAGIC_64);
182 }
183
184 uint32_t MachOFile::pointerSize() const
185 {
186 if (this->magic == MH_MAGIC_64)
187 return 8;
188 else
189 return 4;
190 }
191
192 bool MachOFile::uses16KPages() const
193 {
194 switch (this->cputype) {
195 case CPU_TYPE_ARM64:
196 case CPU_TYPE_ARM:
197 case CPU_TYPE_ARM64_32:
198 return true;
199 default:
200 return false;
201 }
202 }
203
204 bool MachOFile::isArch(const char* aName) const
205 {
206 return (strcmp(aName, archName(this->cputype, this->cpusubtype)) == 0);
207 }
208
209 const char* MachOFile::archName(uint32_t cputype, uint32_t cpusubtype)
210 {
211 for (const ArchInfo& info : _s_archInfos) {
212 if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) {
213 return info.name;
214 }
215 }
216 return "unknown";
217 }
218
219 uint32_t MachOFile::cpuTypeFromArchName(const char* archName)
220 {
221 for (const ArchInfo& info : _s_archInfos) {
222 if ( strcmp(archName, info.name) == 0 ) {
223 return info.cputype;
224 }
225 }
226 return 0;
227 }
228
229 uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName)
230 {
231 for (const ArchInfo& info : _s_archInfos) {
232 if ( strcmp(archName, info.name) == 0 ) {
233 return info.cpusubtype;
234 }
235 }
236 return 0;
237 }
238
239 const char* MachOFile::archName() const
240 {
241 return archName(this->cputype, this->cpusubtype);
242 }
243
244 static void appendDigit(char*& s, unsigned& num, unsigned place, bool& startedPrinting)
245 {
246 if ( num >= place ) {
247 unsigned dig = (num/place);
248 *s++ = '0' + dig;
249 num -= (dig*place);
250 startedPrinting = true;
251 }
252 else if ( startedPrinting ) {
253 *s++ = '0';
254 }
255 }
256
257 static void appendNumber(char*& s, unsigned num)
258 {
259 assert(num < 99999);
260 bool startedPrinting = false;
261 appendDigit(s, num, 10000, startedPrinting);
262 appendDigit(s, num, 1000, startedPrinting);
263 appendDigit(s, num, 100, startedPrinting);
264 appendDigit(s, num, 10, startedPrinting);
265 appendDigit(s, num, 1, startedPrinting);
266 if ( !startedPrinting )
267 *s++ = '0';
268 }
269
270 void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString[32])
271 {
272 // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
273 char* s = versionString;
274 appendNumber(s, (packedVersion >> 16));
275 *s++ = '.';
276 appendNumber(s, (packedVersion >> 8) & 0xFF);
277 *s++ = '.';
278 appendNumber(s, (packedVersion & 0xFF));
279 *s++ = '\0';
280 }
281
282 bool MachOFile::supportsPlatform(Platform reqPlatform) const
283 {
284 __block bool foundRequestedPlatform = false;
285 __block bool foundOtherPlatform = false;
286 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
287 if ( platform == reqPlatform )
288 foundRequestedPlatform = true;
289 else
290 foundOtherPlatform = true;
291 });
292 if ( foundRequestedPlatform )
293 return true;
294
295 // we did find some platform info, but not requested, so return false
296 if ( foundOtherPlatform )
297 return false;
298
299 // binary has no explict load command to mark platform
300 // could be an old macOS binary, look at arch
301 if ( reqPlatform == Platform::macOS ) {
302 if ( this->cputype == CPU_TYPE_X86_64 )
303 return true;
304 if ( this->cputype == CPU_TYPE_I386 )
305 return true;
306 }
307
308 return false;
309 }
310
311 Platform MachOFile::currentPlatform()
312 {
313 #if TARGET_OS_BRIDGE
314 return Platform::bridgeOS;
315 #elif TARGET_OS_WATCH
316 return Platform::watchOS;
317 #elif TARGET_OS_TV
318 return Platform::tvOS;
319 #elif TARGET_OS_IOS
320 return Platform::iOS;
321 #elif TARGET_OS_MAC
322 return Platform::macOS;
323 #else
324 #error unknown platform
325 #endif
326 }
327
328 #if __x86_64__
329 static bool isHaswell()
330 {
331 // FIXME: figure out a commpage way to check this
332 static bool sAlreadyDetermined = false;
333 static bool sHaswell = false;
334 if ( !sAlreadyDetermined ) {
335 struct host_basic_info info;
336 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
337 mach_port_t hostPort = mach_host_self();
338 kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
339 mach_port_deallocate(mach_task_self(), hostPort);
340 sHaswell = (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
341 sAlreadyDetermined = true;
342 }
343 return sHaswell;
344 }
345 #endif
346
347 const char* MachOFile::currentArchName()
348 {
349 #if __ARM_ARCH_7K__
350 return "armv7k";
351 #elif __ARM_ARCH_7A__
352 return "armv7";
353 #elif __ARM_ARCH_7S__
354 return "armv7s";
355 #elif __arm64e__
356 return "arm64e";
357 #elif __arm64__
358 #if __LP64__
359 return "arm64";
360 #else
361 return "arm64_32";
362 #endif
363 #elif __x86_64__
364 return isHaswell() ? "x86_64h" : "x86_64";
365 #elif __i386__
366 return "i386";
367 #else
368 #error unknown arch
369 #endif
370 }
371
372
373 bool MachOFile::isDylib() const
374 {
375 return (this->filetype == MH_DYLIB);
376 }
377
378 bool MachOFile::isBundle() const
379 {
380 return (this->filetype == MH_BUNDLE);
381 }
382
383 bool MachOFile::isMainExecutable() const
384 {
385 return (this->filetype == MH_EXECUTE);
386 }
387
388 bool MachOFile::isDynamicExecutable() const
389 {
390 if ( this->filetype != MH_EXECUTE )
391 return false;
392
393 // static executables do not have dyld load command
394 __block bool hasDyldLoad = false;
395 Diagnostics diag;
396 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
397 if ( cmd->cmd == LC_LOAD_DYLINKER ) {
398 hasDyldLoad = true;
399 stop = true;
400 }
401 });
402 return hasDyldLoad;
403 }
404
405 bool MachOFile::isPIE() const
406 {
407 return (this->flags & MH_PIE);
408 }
409
410 const char* MachOFile::platformName(Platform reqPlatform)
411 {
412 for (const PlatformInfo& info : _s_platformInfos) {
413 if ( info.platform == reqPlatform )
414 return info.name;
415 }
416 return "unknown platform";
417 }
418
419 void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
420 {
421 Diagnostics diag;
422 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
423 const build_version_command* buildCmd = (build_version_command *)cmd;
424 const version_min_command* versCmd = (version_min_command*)cmd;
425 switch ( cmd->cmd ) {
426 case LC_BUILD_VERSION:
427 handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
428 break;
429 case LC_VERSION_MIN_MACOSX:
430 handler(Platform::macOS, versCmd->version, versCmd->sdk);
431 break;
432 case LC_VERSION_MIN_IPHONEOS:
433 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
434 handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
435 else
436 handler(Platform::iOS, versCmd->version, versCmd->sdk);
437 break;
438 case LC_VERSION_MIN_TVOS:
439 if ( this->cputype == CPU_TYPE_X86_64 )
440 handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
441 else
442 handler(Platform::tvOS, versCmd->version, versCmd->sdk);
443 break;
444 case LC_VERSION_MIN_WATCHOS:
445 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
446 handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
447 else
448 handler(Platform::watchOS, versCmd->version, versCmd->sdk);
449 break;
450 }
451 });
452 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
453 }
454
455
456 bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
457 {
458 if ( !hasMachOMagic() ) {
459 diag.error("file does not start with MH_MAGIC[_64]");
460 return false;
461 }
462 if ( this->sizeofcmds + sizeof(mach_header_64) > fileSize ) {
463 diag.error("load commands exceed length of first segment");
464 return false;
465 }
466 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { });
467 return diag.noError();
468 }
469
470 bool MachOFile::hasMachOMagic() const
471 {
472 return ( (this->magic == MH_MAGIC) || (this->magic == MH_MAGIC_64) );
473 }
474
475 void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
476 {
477 bool stop = false;
478 const load_command* startCmds = nullptr;
479 if ( this->magic == MH_MAGIC_64 )
480 startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
481 else if ( this->magic == MH_MAGIC )
482 startCmds = (load_command*)((char *)this + sizeof(mach_header));
483 else {
484 const uint32_t* h = (uint32_t*)this;
485 diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
486 return; // not a mach-o file
487 }
488 const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
489 const load_command* cmd = startCmds;
490 for (uint32_t i = 0; i < this->ncmds; ++i) {
491 const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
492 if ( cmd->cmdsize < 8 ) {
493 diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
494 return;
495 }
496 if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
497 diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
498 return;
499 }
500 callback(cmd, stop);
501 if ( stop )
502 return;
503 cmd = nextCmd;
504 }
505 }
506
507 const char* MachOFile::installName() const
508 {
509 const char* name;
510 uint32_t compatVersion;
511 uint32_t currentVersion;
512 if ( getDylibInstallName(&name, &compatVersion, &currentVersion) )
513 return name;
514 return nullptr;
515 }
516
517 bool MachOFile::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
518 {
519 Diagnostics diag;
520 __block bool found = false;
521 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
522 if ( cmd->cmd == LC_ID_DYLIB ) {
523 const dylib_command* dylibCmd = (dylib_command*)cmd;
524 *compatVersion = dylibCmd->dylib.compatibility_version;
525 *currentVersion = dylibCmd->dylib.current_version;
526 *installName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
527 found = true;
528 stop = true;
529 }
530 });
531 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
532 return found;
533 }
534
535 bool MachOFile::getUuid(uuid_t uuid) const
536 {
537 Diagnostics diag;
538 __block bool found = false;
539 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
540 if ( cmd->cmd == LC_UUID ) {
541 const uuid_command* uc = (const uuid_command*)cmd;
542 memcpy(uuid, uc->uuid, sizeof(uuid_t));
543 found = true;
544 stop = true;
545 }
546 });
547 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
548 if ( !found )
549 bzero(uuid, sizeof(uuid_t));
550 return found;
551 }
552
553 void MachOFile::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
554 {
555 Diagnostics diag;
556 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
557 switch ( cmd->cmd ) {
558 case LC_LOAD_DYLIB:
559 case LC_LOAD_WEAK_DYLIB:
560 case LC_REEXPORT_DYLIB:
561 case LC_LOAD_UPWARD_DYLIB: {
562 const dylib_command* dylibCmd = (dylib_command*)cmd;
563 const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
564 callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB),
565 dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop);
566 }
567 break;
568 }
569 });
570 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
571 }
572
573 void MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
574 {
575 Diagnostics diag;
576 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
577 if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
578 const dylinker_command* envCmd = (dylinker_command*)cmd;
579 const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
580 // only process variables that start with DYLD_ and end in _PATH
581 if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
582 const char* equals = strchr(keyEqualsValue, '=');
583 if ( equals != NULL ) {
584 if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
585 callback(keyEqualsValue, stop);
586 }
587 }
588 }
589 }
590 });
591 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
592 }
593
594 bool MachOFile::enforceCompatVersion() const
595 {
596 __block bool result = true;
597 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
598 switch ( platform ) {
599 case Platform::macOS:
600 if ( minOS >= 0x000A0E00 ) // macOS 10.14
601 result = false;
602 break;
603 case Platform::iOS:
604 case Platform::tvOS:
605 case Platform::iOS_simulator:
606 case Platform::tvOS_simulator:
607 if ( minOS >= 0x000C0000 ) // iOS 12.0
608 result = false;
609 break;
610 case Platform::watchOS:
611 case Platform::watchOS_simulator:
612 if ( minOS >= 0x00050000 ) // watchOS 5.0
613 result = false;
614 break;
615 case Platform::bridgeOS:
616 if ( minOS >= 0x00030000 ) // bridgeOS 3.0
617 result = false;
618 break;
619 case Platform::iOSMac:
620 result = false;
621 break;
622 case Platform::unknown:
623 break;
624 }
625 });
626 return result;
627 }
628
629
630 void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
631 {
632 Diagnostics diag;
633 const bool intel32 = (this->cputype == CPU_TYPE_I386);
634 __block uint32_t segIndex = 0;
635 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
636 if ( cmd->cmd == LC_SEGMENT_64 ) {
637 const segment_command_64* segCmd = (segment_command_64*)cmd;
638 uint64_t sizeOfSections = segCmd->vmsize;
639 uint8_t p2align = 0;
640 const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
641 const section_64* const sectionsEnd = &sectionsStart[segCmd->nsects];
642 for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
643 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
644 if ( sect->align > p2align )
645 p2align = sect->align;
646 }
647 SegmentInfo info;
648 info.fileOffset = segCmd->fileoff;
649 info.fileSize = segCmd->filesize;
650 info.vmAddr = segCmd->vmaddr;
651 info.vmSize = segCmd->vmsize;
652 info.sizeOfSections = sizeOfSections;
653 info.segName = segCmd->segname;
654 info.protections = segCmd->initprot;
655 info.textRelocs = false;
656 info.p2align = p2align;
657 info.segIndex = segIndex;
658 callback(info, stop);
659 ++segIndex;
660 }
661 else if ( cmd->cmd == LC_SEGMENT ) {
662 const segment_command* segCmd = (segment_command*)cmd;
663 uint64_t sizeOfSections = segCmd->vmsize;
664 uint8_t p2align = 0;
665 bool hasTextRelocs = false;
666 const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
667 const section* const sectionsEnd = &sectionsStart[segCmd->nsects];
668 for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
669 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
670 if ( sect->align > p2align )
671 p2align = sect->align;
672 if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
673 hasTextRelocs = true;
674 }
675 SegmentInfo info;
676 info.fileOffset = segCmd->fileoff;
677 info.fileSize = segCmd->filesize;
678 info.vmAddr = segCmd->vmaddr;
679 info.vmSize = segCmd->vmsize;
680 info.sizeOfSections = sizeOfSections;
681 info.segName = segCmd->segname;
682 info.protections = segCmd->initprot;
683 info.textRelocs = intel32 && !info.writable() && hasTextRelocs;
684 info.p2align = p2align;
685 info.segIndex = segIndex;
686 callback(info, stop);
687 ++segIndex;
688 }
689 });
690 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
691 }
692
693 void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
694 {
695 Diagnostics diag;
696 const bool intel32 = (this->cputype == CPU_TYPE_I386);
697 __block uint32_t segIndex = 0;
698 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
699 SectionInfo sectInfo;
700 if ( cmd->cmd == LC_SEGMENT_64 ) {
701 const segment_command_64* segCmd = (segment_command_64*)cmd;
702 uint64_t sizeOfSections = segCmd->vmsize;
703 uint8_t p2align = 0;
704 const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
705 const section_64* const sectionsEnd = &sectionsStart[segCmd->nsects];
706 for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
707 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
708 if ( sect->align > p2align )
709 p2align = sect->align;
710 }
711 sectInfo.segInfo.fileOffset = segCmd->fileoff;
712 sectInfo.segInfo.fileSize = segCmd->filesize;
713 sectInfo.segInfo.vmAddr = segCmd->vmaddr;
714 sectInfo.segInfo.vmSize = segCmd->vmsize;
715 sectInfo.segInfo.sizeOfSections = sizeOfSections;
716 sectInfo.segInfo.segName = segCmd->segname;
717 sectInfo.segInfo.protections = segCmd->initprot;
718 sectInfo.segInfo.textRelocs = false;
719 sectInfo.segInfo.p2align = p2align;
720 sectInfo.segInfo.segIndex = segIndex;
721 for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
722 const char* sectName = sect->sectname;
723 char sectNameCopy[20];
724 if ( sectName[15] != '\0' ) {
725 strlcpy(sectNameCopy, sectName, 17);
726 sectName = sectNameCopy;
727 }
728 bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
729 sectInfo.sectName = sectName;
730 sectInfo.sectFileOffset = sect->offset;
731 sectInfo.sectFlags = sect->flags;
732 sectInfo.sectAddr = sect->addr;
733 sectInfo.sectSize = sect->size;
734 sectInfo.sectAlignP2 = sect->align;
735 sectInfo.reserved1 = sect->reserved1;
736 sectInfo.reserved2 = sect->reserved2;
737 callback(sectInfo, malformedSectionRange, stop);
738 }
739 ++segIndex;
740 }
741 else if ( cmd->cmd == LC_SEGMENT ) {
742 const segment_command* segCmd = (segment_command*)cmd;
743 uint64_t sizeOfSections = segCmd->vmsize;
744 uint8_t p2align = 0;
745 bool hasTextRelocs = false;
746 const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
747 const section* const sectionsEnd = &sectionsStart[segCmd->nsects];
748 for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
749 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
750 if ( sect->align > p2align )
751 p2align = sect->align;
752 if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
753 hasTextRelocs = true;
754 }
755 sectInfo.segInfo.fileOffset = segCmd->fileoff;
756 sectInfo.segInfo.fileSize = segCmd->filesize;
757 sectInfo.segInfo.vmAddr = segCmd->vmaddr;
758 sectInfo.segInfo.vmSize = segCmd->vmsize;
759 sectInfo.segInfo.sizeOfSections = sizeOfSections;
760 sectInfo.segInfo.segName = segCmd->segname;
761 sectInfo.segInfo.protections = segCmd->initprot;
762 sectInfo.segInfo.textRelocs = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
763 sectInfo.segInfo.p2align = p2align;
764 sectInfo.segInfo.segIndex = segIndex;
765 for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
766 const char* sectName = sect->sectname;
767 char sectNameCopy[20];
768 if ( sectName[15] != '\0' ) {
769 strlcpy(sectNameCopy, sectName, 17);
770 sectName = sectNameCopy;
771 }
772 bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
773 sectInfo.sectName = sectName;
774 sectInfo.sectFileOffset = sect->offset;
775 sectInfo.sectFlags = sect->flags;
776 sectInfo.sectAddr = sect->addr;
777 sectInfo.sectSize = sect->size;
778 sectInfo.sectAlignP2 = sect->align;
779 sectInfo.reserved1 = sect->reserved1;
780 sectInfo.reserved2 = sect->reserved2;
781 callback(sectInfo, malformedSectionRange, stop);
782 }
783 ++segIndex;
784 }
785 });
786 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
787 }
788
789 bool MachOFile::hasWeakDefs() const
790 {
791 return (this->flags & MH_WEAK_DEFINES);
792 }
793
794 bool MachOFile::hasThreadLocalVariables() const
795 {
796 return (this->flags & MH_HAS_TLV_DESCRIPTORS);
797 }
798
799 static bool endsWith(const char* str, const char* suffix)
800 {
801 size_t strLen = strlen(str);
802 size_t suffixLen = strlen(suffix);
803 if ( strLen < suffixLen )
804 return false;
805 return (strcmp(&str[strLen-suffixLen], suffix) == 0);
806 }
807
808 bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
809 {
810 // only dylibs can go in cache
811 if ( this->filetype != MH_DYLIB ) {
812 failureReason("Not MH_DYLIB");
813 return false; // cannot continue, installName() will assert() if not a dylib
814 }
815
816 // only dylibs built for /usr/lib or /System/Library can go in cache
817 bool retval = true;
818 const char* dylibName = installName();
819 if ( dylibName[0] != '/' ) {
820 retval = false;
821 failureReason("install name not an absolute path");
822 }
823 else if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
824 retval = false;
825 failureReason("Not in '/usr/lib/' or '/System/Library/'");
826 }
827
828 // flat namespace files cannot go in cache
829 if ( (this->flags & MH_TWOLEVEL) == 0 ) {
830 retval = false;
831 failureReason("Not built with two level namespaces");
832 }
833
834 // don't put debug variants into dyld cache
835 if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
836 retval = false;
837 failureReason("Variant image");
838 }
839
840 // dylib must have extra info for moving DATA and TEXT segments apart
841 __block bool hasExtraInfo = false;
842 __block bool hasDyldInfo = false;
843 Diagnostics diag;
844 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
845 if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
846 hasExtraInfo = true;
847 if ( cmd->cmd == LC_DYLD_INFO_ONLY )
848 hasDyldInfo = true;
849 });
850 if ( !hasExtraInfo ) {
851 retval = false;
852 failureReason("Missing split seg info");
853 }
854 if ( !hasDyldInfo ) {
855 retval = false;
856 failureReason("Old binary, missing dyld info");
857 }
858
859 // dylib can only depend on other dylibs in the shared cache
860 __block bool allDepPathsAreGood = true;
861 forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
862 if ( (strncmp(loadPath, "/usr/lib/", 9) != 0) && (strncmp(loadPath, "/System/Library/", 16) != 0) ) {
863 allDepPathsAreGood = false;
864 stop = true;
865 }
866 });
867 if ( !allDepPathsAreGood ) {
868 retval = false;
869 failureReason("Depends on dylibs ineligable for dyld cache");
870 }
871
872 // dylibs with interposing info cannot be in cache
873 __block bool hasInterposing = false;
874 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
875 if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
876 hasInterposing = true;
877 });
878 if ( hasInterposing ) {
879 retval = false;
880 failureReason("Has interposing tuples");
881 }
882
883 return retval;
884 }
885
886
887 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
888 {
889 if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
890 if ( encCmd->cryptid == 1 ) {
891 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
892 textOffset = encCmd->cryptoff;
893 size = encCmd->cryptsize;
894 return true;
895 }
896 }
897 textOffset = 0;
898 size = 0;
899 return false;
900 }
901
902 bool MachOFile::canBeFairPlayEncrypted() const
903 {
904 return (findFairPlayEncryptionLoadCommand() != nullptr);
905 }
906
907 const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
908 {
909 __block const encryption_info_command* result = nullptr;
910 Diagnostics diag;
911 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
912 if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
913 result = (encryption_info_command*)cmd;
914 stop = true;
915 }
916 });
917 if ( diag.noError() )
918 return result;
919 else
920 return nullptr;
921 }
922
923
924 bool MachOFile::hasChainedFixups() const
925 {
926 #if SUPPORT_ARCH_arm64e
927 // for now only arm64e uses chained fixups
928 return ( strcmp(archName(), "arm64e") == 0 );
929 #else
930 return false;
931 #endif
932 }
933
934 uint64_t MachOFile::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
935 {
936 uint64_t result = 0;
937 int bit = 0;
938 do {
939 if ( p == end ) {
940 diag.error("malformed uleb128");
941 break;
942 }
943 uint64_t slice = *p & 0x7f;
944
945 if ( bit > 63 ) {
946 diag.error("uleb128 too big for uint64");
947 break;
948 }
949 else {
950 result |= (slice << bit);
951 bit += 7;
952 }
953 }
954 while (*p++ & 0x80);
955 return result;
956 }
957
958
959 int64_t MachOFile::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
960 {
961 int64_t result = 0;
962 int bit = 0;
963 uint8_t byte = 0;
964 do {
965 if ( p == end ) {
966 diag.error("malformed sleb128");
967 break;
968 }
969 byte = *p++;
970 result |= (((int64_t)(byte & 0x7f)) << bit);
971 bit += 7;
972 } while (byte & 0x80);
973 // sign extend negative numbers
974 if ( (byte & 0x40) != 0 )
975 result |= (~0ULL) << bit;
976 return result;
977 }
978
979
980 } // namespace dyld3
981
982
983
984
985