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