]> git.saurik.com Git - apple/dyld.git/blob - dyld3/MachOFile.cpp
dyld-750.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 #if BUILDING_DYLDINFO
434 // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms
435 if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) )
436 return true;
437 #endif
438
439 return false;
440 }
441
442 bool MachOFile::isZippered() const
443 {
444 __block bool macOS = false;
445 __block bool iOSMac = false;
446 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
447 if ( platform == Platform::macOS )
448 macOS = true;
449 else if ( platform == Platform::iOSMac )
450 iOSMac = true;
451 });
452 return macOS && iOSMac;
453 }
454
455 bool MachOFile::inDyldCache() const {
456 return (this->flags & 0x80000000);
457 }
458
459 Platform MachOFile::currentPlatform()
460 {
461
462 #if TARGET_OS_SIMULATOR
463 #if TARGET_OS_WATCH
464 return Platform::watchOS_simulator;
465 #elif TARGET_OS_TV
466 return Platform::tvOS_simulator;
467 #else
468 return Platform::iOS_simulator;
469 #endif
470 #elif TARGET_OS_BRIDGE
471 return Platform::bridgeOS;
472 #elif TARGET_OS_WATCH
473 return Platform::watchOS;
474 #elif TARGET_OS_TV
475 return Platform::tvOS;
476 #elif TARGET_OS_IOS
477 return Platform::iOS;
478 #elif TARGET_OS_MAC
479 return Platform::macOS;
480 #else
481 #error unknown platform
482 #endif
483 }
484
485
486 const char* MachOFile::currentArchName()
487 {
488 #if __ARM_ARCH_7K__
489 return "armv7k";
490 #elif __ARM_ARCH_7A__
491 return "armv7";
492 #elif __ARM_ARCH_7S__
493 return "armv7s";
494 #elif __arm64e__
495 return "arm64e";
496 #elif __arm64__
497 #if __LP64__
498 return "arm64";
499 #else
500 return "arm64_32";
501 #endif
502 #elif __x86_64__
503 return isHaswell() ? "x86_64h" : "x86_64";
504 #elif __i386__
505 return "i386";
506 #else
507 #error unknown arch
508 #endif
509 }
510
511 bool MachOFile::isSimulatorPlatform(Platform platform)
512 {
513 return ( (platform == Platform::iOS_simulator) ||
514 (platform == Platform::watchOS_simulator) ||
515 (platform == Platform::tvOS_simulator) );
516 }
517
518 bool MachOFile::isDylib() const
519 {
520 return (this->filetype == MH_DYLIB);
521 }
522
523 bool MachOFile::isBundle() const
524 {
525 return (this->filetype == MH_BUNDLE);
526 }
527
528 bool MachOFile::isMainExecutable() const
529 {
530 return (this->filetype == MH_EXECUTE);
531 }
532
533 bool MachOFile::isDynamicExecutable() const
534 {
535 if ( this->filetype != MH_EXECUTE )
536 return false;
537
538 // static executables do not have dyld load command
539 return hasLoadCommand(LC_LOAD_DYLINKER);
540 }
541
542 bool MachOFile::isStaticExecutable() const
543 {
544 if ( this->filetype != MH_EXECUTE )
545 return false;
546
547 // static executables do not have dyld load command
548 return !hasLoadCommand(LC_LOAD_DYLINKER);
549 }
550
551 bool MachOFile::isPIE() const
552 {
553 return (this->flags & MH_PIE);
554 }
555
556 bool MachOFile::isPreload() const
557 {
558 return (this->filetype == MH_PRELOAD);
559 }
560
561 const char* MachOFile::platformName(Platform reqPlatform)
562 {
563 for (const PlatformInfo& info : _s_platformInfos) {
564 if ( info.platform == reqPlatform )
565 return info.name;
566 }
567 return "unknown platform";
568 }
569
570 void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
571 {
572 Diagnostics diag;
573 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
574 const build_version_command* buildCmd = (build_version_command *)cmd;
575 const version_min_command* versCmd = (version_min_command*)cmd;
576 switch ( cmd->cmd ) {
577 case LC_BUILD_VERSION:
578 handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
579 break;
580 case LC_VERSION_MIN_MACOSX:
581 handler(Platform::macOS, versCmd->version, versCmd->sdk);
582 break;
583 case LC_VERSION_MIN_IPHONEOS:
584 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
585 handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
586 else
587 handler(Platform::iOS, versCmd->version, versCmd->sdk);
588 break;
589 case LC_VERSION_MIN_TVOS:
590 if ( this->cputype == CPU_TYPE_X86_64 )
591 handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
592 else
593 handler(Platform::tvOS, versCmd->version, versCmd->sdk);
594 break;
595 case LC_VERSION_MIN_WATCHOS:
596 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
597 handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
598 else
599 handler(Platform::watchOS, versCmd->version, versCmd->sdk);
600 break;
601 }
602 });
603 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
604 }
605
606
607 bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
608 {
609 if ( !hasMachOMagic() ) {
610 // old PPC slices are not currently valid "mach-o" but should not cause an error
611 if ( !hasMachOBigEndianMagic() )
612 diag.error("file does not start with MH_MAGIC[_64]");
613 return false;
614 }
615 if ( this->sizeofcmds + machHeaderSize() > fileSize ) {
616 diag.error("load commands exceed length of first segment");
617 return false;
618 }
619 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { });
620 return diag.noError();
621 }
622
623 bool MachOFile::hasMachOMagic() const
624 {
625 return ( (this->magic == MH_MAGIC) || (this->magic == MH_MAGIC_64) );
626 }
627
628 bool MachOFile::hasMachOBigEndianMagic() const
629 {
630 return ( (this->magic == MH_CIGAM) || (this->magic == MH_CIGAM_64) );
631 }
632
633
634 void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
635 {
636 bool stop = false;
637 const load_command* startCmds = nullptr;
638 if ( this->magic == MH_MAGIC_64 )
639 startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
640 else if ( this->magic == MH_MAGIC )
641 startCmds = (load_command*)((char *)this + sizeof(mach_header));
642 else if ( hasMachOBigEndianMagic() )
643 return; // can't process big endian mach-o
644 else {
645 const uint32_t* h = (uint32_t*)this;
646 diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
647 return; // not a mach-o file
648 }
649 const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
650 const load_command* cmd = startCmds;
651 for (uint32_t i = 0; i < this->ncmds; ++i) {
652 const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
653 if ( cmd->cmdsize < 8 ) {
654 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);
655 return;
656 }
657 if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
658 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);
659 return;
660 }
661 callback(cmd, stop);
662 if ( stop )
663 return;
664 cmd = nextCmd;
665 }
666 }
667
668 const char* MachOFile::installName() const
669 {
670 const char* name;
671 uint32_t compatVersion;
672 uint32_t currentVersion;
673 if ( getDylibInstallName(&name, &compatVersion, &currentVersion) )
674 return name;
675 return nullptr;
676 }
677
678 bool MachOFile::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
679 {
680 Diagnostics diag;
681 __block bool found = false;
682 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
683 if ( cmd->cmd == LC_ID_DYLIB ) {
684 const dylib_command* dylibCmd = (dylib_command*)cmd;
685 *compatVersion = dylibCmd->dylib.compatibility_version;
686 *currentVersion = dylibCmd->dylib.current_version;
687 *installName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
688 found = true;
689 stop = true;
690 }
691 });
692 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
693 return found;
694 }
695
696 bool MachOFile::getUuid(uuid_t uuid) const
697 {
698 Diagnostics diag;
699 __block bool found = false;
700 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
701 if ( cmd->cmd == LC_UUID ) {
702 const uuid_command* uc = (const uuid_command*)cmd;
703 memcpy(uuid, uc->uuid, sizeof(uuid_t));
704 found = true;
705 stop = true;
706 }
707 });
708 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
709 if ( !found )
710 bzero(uuid, sizeof(uuid_t));
711 return found;
712 }
713
714 void MachOFile::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
715 {
716 Diagnostics diag;
717 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
718 switch ( cmd->cmd ) {
719 case LC_LOAD_DYLIB:
720 case LC_LOAD_WEAK_DYLIB:
721 case LC_REEXPORT_DYLIB:
722 case LC_LOAD_UPWARD_DYLIB: {
723 const dylib_command* dylibCmd = (dylib_command*)cmd;
724 const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
725 callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB),
726 dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop);
727 }
728 break;
729 }
730 });
731 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
732 }
733
734 void MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
735 {
736 Diagnostics diag;
737 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
738 if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
739 const dylinker_command* envCmd = (dylinker_command*)cmd;
740 const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
741 // only process variables that start with DYLD_ and end in _PATH
742 if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
743 const char* equals = strchr(keyEqualsValue, '=');
744 if ( equals != NULL ) {
745 if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
746 callback(keyEqualsValue, stop);
747 }
748 }
749 }
750 }
751 });
752 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
753 }
754
755 bool MachOFile::enforceCompatVersion() const
756 {
757 __block bool result = true;
758 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
759 switch ( platform ) {
760 case Platform::macOS:
761 if ( minOS >= 0x000A0E00 ) // macOS 10.14
762 result = false;
763 break;
764 case Platform::iOS:
765 case Platform::tvOS:
766 case Platform::iOS_simulator:
767 case Platform::tvOS_simulator:
768 if ( minOS >= 0x000C0000 ) // iOS 12.0
769 result = false;
770 break;
771 case Platform::watchOS:
772 case Platform::watchOS_simulator:
773 if ( minOS >= 0x00050000 ) // watchOS 5.0
774 result = false;
775 break;
776 case Platform::bridgeOS:
777 if ( minOS >= 0x00030000 ) // bridgeOS 3.0
778 result = false;
779 break;
780 case Platform::driverKit:
781 case Platform::iOSMac:
782 result = false;
783 break;
784 case Platform::unknown:
785 break;
786 }
787 });
788 return result;
789 }
790
791
792 void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
793 {
794 Diagnostics diag;
795 const bool intel32 = (this->cputype == CPU_TYPE_I386);
796 __block uint32_t segIndex = 0;
797 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
798 if ( cmd->cmd == LC_SEGMENT_64 ) {
799 const segment_command_64* segCmd = (segment_command_64*)cmd;
800 uint64_t sizeOfSections = segCmd->vmsize;
801 uint8_t p2align = 0;
802 const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
803 const section_64* const sectionsEnd = &sectionsStart[segCmd->nsects];
804 for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
805 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
806 if ( sect->align > p2align )
807 p2align = sect->align;
808 }
809 SegmentInfo info;
810 info.fileOffset = segCmd->fileoff;
811 info.fileSize = segCmd->filesize;
812 info.vmAddr = segCmd->vmaddr;
813 info.vmSize = segCmd->vmsize;
814 info.sizeOfSections = sizeOfSections;
815 info.segName = segCmd->segname;
816 info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
817 info.protections = segCmd->initprot;
818 info.textRelocs = false;
819 info.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
820 info.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
821 info.p2align = p2align;
822 info.segIndex = segIndex;
823 callback(info, stop);
824 ++segIndex;
825 }
826 else if ( cmd->cmd == LC_SEGMENT ) {
827 const segment_command* segCmd = (segment_command*)cmd;
828 uint64_t sizeOfSections = segCmd->vmsize;
829 uint8_t p2align = 0;
830 bool hasTextRelocs = false;
831 const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
832 const section* const sectionsEnd = &sectionsStart[segCmd->nsects];
833 for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
834 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
835 if ( sect->align > p2align )
836 p2align = sect->align;
837 if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
838 hasTextRelocs = true;
839 }
840 SegmentInfo info;
841 info.fileOffset = segCmd->fileoff;
842 info.fileSize = segCmd->filesize;
843 info.vmAddr = segCmd->vmaddr;
844 info.vmSize = segCmd->vmsize;
845 info.sizeOfSections = sizeOfSections;
846 info.segName = segCmd->segname;
847 info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
848 info.protections = segCmd->initprot;
849 info.textRelocs = intel32 && !info.writable() && hasTextRelocs;
850 info.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
851 info.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
852 info.p2align = p2align;
853 info.segIndex = segIndex;
854 callback(info, stop);
855 ++segIndex;
856 }
857 });
858 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
859 }
860
861 void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
862 {
863 Diagnostics diag;
864 BLOCK_ACCCESSIBLE_ARRAY(char, sectNameCopy, 20); // read as: char sectNameCopy[20];
865 const bool intel32 = (this->cputype == CPU_TYPE_I386);
866 __block uint32_t segIndex = 0;
867 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
868 SectionInfo sectInfo;
869 if ( cmd->cmd == LC_SEGMENT_64 ) {
870 const segment_command_64* segCmd = (segment_command_64*)cmd;
871 uint64_t sizeOfSections = segCmd->vmsize;
872 uint8_t p2align = 0;
873 const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
874 const section_64* const sectionsEnd = &sectionsStart[segCmd->nsects];
875 for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
876 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
877 if ( sect->align > p2align )
878 p2align = sect->align;
879 }
880 sectInfo.segInfo.fileOffset = segCmd->fileoff;
881 sectInfo.segInfo.fileSize = segCmd->filesize;
882 sectInfo.segInfo.vmAddr = segCmd->vmaddr;
883 sectInfo.segInfo.vmSize = segCmd->vmsize;
884 sectInfo.segInfo.sizeOfSections = sizeOfSections;
885 sectInfo.segInfo.segName = segCmd->segname;
886 sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
887 sectInfo.segInfo.protections = segCmd->initprot;
888 sectInfo.segInfo.textRelocs = false;
889 sectInfo.segInfo.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
890 sectInfo.segInfo.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
891 sectInfo.segInfo.p2align = p2align;
892 sectInfo.segInfo.segIndex = segIndex;
893 for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
894 const char* sectName = sect->sectname;
895 if ( sectName[15] != '\0' ) {
896 strlcpy(sectNameCopy, sectName, 17);
897 sectName = sectNameCopy;
898 }
899 bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
900 sectInfo.sectName = sectName;
901 sectInfo.sectFileOffset = sect->offset;
902 sectInfo.sectFlags = sect->flags;
903 sectInfo.sectAddr = sect->addr;
904 sectInfo.sectSize = sect->size;
905 sectInfo.sectAlignP2 = sect->align;
906 sectInfo.reserved1 = sect->reserved1;
907 sectInfo.reserved2 = sect->reserved2;
908 callback(sectInfo, malformedSectionRange, stop);
909 }
910 ++segIndex;
911 }
912 else if ( cmd->cmd == LC_SEGMENT ) {
913 const segment_command* segCmd = (segment_command*)cmd;
914 uint64_t sizeOfSections = segCmd->vmsize;
915 uint8_t p2align = 0;
916 bool hasTextRelocs = false;
917 const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
918 const section* const sectionsEnd = &sectionsStart[segCmd->nsects];
919 for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
920 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
921 if ( sect->align > p2align )
922 p2align = sect->align;
923 if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
924 hasTextRelocs = true;
925 }
926 sectInfo.segInfo.fileOffset = segCmd->fileoff;
927 sectInfo.segInfo.fileSize = segCmd->filesize;
928 sectInfo.segInfo.vmAddr = segCmd->vmaddr;
929 sectInfo.segInfo.vmSize = segCmd->vmsize;
930 sectInfo.segInfo.sizeOfSections = sizeOfSections;
931 sectInfo.segInfo.segName = segCmd->segname;
932 sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
933 sectInfo.segInfo.protections = segCmd->initprot;
934 sectInfo.segInfo.textRelocs = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
935 sectInfo.segInfo.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
936 sectInfo.segInfo.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
937 sectInfo.segInfo.p2align = p2align;
938 sectInfo.segInfo.segIndex = segIndex;
939 for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
940 const char* sectName = sect->sectname;
941 if ( sectName[15] != '\0' ) {
942 strlcpy(sectNameCopy, sectName, 17);
943 sectName = sectNameCopy;
944 }
945 bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
946 sectInfo.sectName = sectName;
947 sectInfo.sectFileOffset = sect->offset;
948 sectInfo.sectFlags = sect->flags;
949 sectInfo.sectAddr = sect->addr;
950 sectInfo.sectSize = sect->size;
951 sectInfo.sectAlignP2 = sect->align;
952 sectInfo.reserved1 = sect->reserved1;
953 sectInfo.reserved2 = sect->reserved2;
954 callback(sectInfo, malformedSectionRange, stop);
955 }
956 ++segIndex;
957 }
958 });
959 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
960 }
961
962 bool MachOFile::hasWeakDefs() const
963 {
964 return (this->flags & MH_WEAK_DEFINES);
965 }
966
967 bool MachOFile::hasThreadLocalVariables() const
968 {
969 return (this->flags & MH_HAS_TLV_DESCRIPTORS);
970 }
971
972 static bool endsWith(const char* str, const char* suffix)
973 {
974 size_t strLen = strlen(str);
975 size_t suffixLen = strlen(suffix);
976 if ( strLen < suffixLen )
977 return false;
978 return (strcmp(&str[strLen-suffixLen], suffix) == 0);
979 }
980
981 bool MachOFile::isSharedCacheEligiblePath(const char* dylibName) {
982 return ( (strncmp(dylibName, "/usr/lib/", 9) == 0)
983 || (strncmp(dylibName, "/System/Library/", 16) == 0)
984 || (strncmp(dylibName, "/System/iOSSupport/usr/lib/", 27) == 0)
985 || (strncmp(dylibName, "/System/iOSSupport/System/Library/", 34) == 0)
986 || (strncmp(dylibName, "/Library/Apple/usr/lib/", 23) == 0)
987 || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0) );
988 }
989
990 bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
991 {
992 if ( !isSharedCacheEligiblePath(path) ) {
993 // Dont spam the user with an error about paths when we know these are never eligible.
994 return false;
995 }
996
997 // only dylibs can go in cache
998 if ( this->filetype != MH_DYLIB ) {
999 failureReason("Not MH_DYLIB");
1000 return false; // cannot continue, installName() will assert() if not a dylib
1001 }
1002
1003 // only dylibs built for /usr/lib or /System/Library can go in cache
1004
1005 const char* dylibName = installName();
1006 if ( dylibName[0] != '/' ) {
1007 failureReason("install name not an absolute path");
1008 // Don't continue as we don't want to spam the log with errors we don't need.
1009 return false;
1010 }
1011 else if ( strcmp(dylibName, path) != 0 ) {
1012 failureReason("install path does not match install name");
1013 return false;
1014 }
1015
1016 bool retval = true;
1017
1018 // flat namespace files cannot go in cache
1019 if ( (this->flags & MH_TWOLEVEL) == 0 ) {
1020 retval = false;
1021 failureReason("Not built with two level namespaces");
1022 }
1023
1024 // don't put debug variants into dyld cache
1025 if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
1026 retval = false;
1027 failureReason("Variant image");
1028 }
1029
1030 // dylib must have extra info for moving DATA and TEXT segments apart
1031 __block bool hasExtraInfo = false;
1032 __block bool hasDyldInfo = false;
1033 __block bool hasExportTrie = false;
1034 Diagnostics diag;
1035 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1036 if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
1037 hasExtraInfo = true;
1038 if ( cmd->cmd == LC_DYLD_INFO_ONLY )
1039 hasDyldInfo = true;
1040 if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE )
1041 hasExportTrie = true;
1042 });
1043 if ( !hasExtraInfo ) {
1044 retval = false;
1045 failureReason("Missing split seg info");
1046 }
1047 if ( !hasDyldInfo && !hasExportTrie ) {
1048 retval = false;
1049 failureReason("Old binary, missing dyld info or export trie");
1050 }
1051
1052 // dylib can only depend on other dylibs in the shared cache
1053 __block bool allDepPathsAreGood = true;
1054 forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
1055 if ( !isSharedCacheEligiblePath(loadPath) ) {
1056 allDepPathsAreGood = false;
1057 stop = true;
1058 }
1059 });
1060 if ( !allDepPathsAreGood ) {
1061 retval = false;
1062 failureReason("Depends on dylibs ineligable for dyld cache");
1063 }
1064
1065 // dylibs with interposing info cannot be in cache
1066 __block bool hasInterposing = false;
1067 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
1068 if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
1069 hasInterposing = true;
1070 });
1071 if ( hasInterposing ) {
1072 retval = false;
1073 failureReason("Has interposing tuples");
1074 }
1075
1076 // Temporarily kick out swift binaries on watchOS simulators as they have missing split seg
1077 if ( supportsPlatform(Platform::watchOS_simulator) && isArch("i386") ) {
1078 if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
1079 retval = false;
1080 failureReason("i386 swift binary");
1081 }
1082 }
1083
1084 return retval;
1085 }
1086
1087
1088 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
1089 {
1090 if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
1091 if ( encCmd->cryptid == 1 ) {
1092 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
1093 textOffset = encCmd->cryptoff;
1094 size = encCmd->cryptsize;
1095 return true;
1096 }
1097 }
1098 textOffset = 0;
1099 size = 0;
1100 return false;
1101 }
1102
1103 bool MachOFile::canBeFairPlayEncrypted() const
1104 {
1105 return (findFairPlayEncryptionLoadCommand() != nullptr);
1106 }
1107
1108 const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
1109 {
1110 __block const encryption_info_command* result = nullptr;
1111 Diagnostics diag;
1112 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1113 if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
1114 result = (encryption_info_command*)cmd;
1115 stop = true;
1116 }
1117 });
1118 if ( diag.noError() )
1119 return result;
1120 else
1121 return nullptr;
1122 }
1123
1124
1125 bool MachOFile::hasLoadCommand(uint32_t cmdNum) const
1126 {
1127 __block bool hasLC = false;
1128 Diagnostics diag;
1129 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1130 if ( cmd->cmd == cmdNum ) {
1131 hasLC = true;
1132 stop = true;
1133 }
1134 });
1135 return hasLC;
1136 }
1137
1138 bool MachOFile::allowsAlternatePlatform() const
1139 {
1140 __block bool result = false;
1141 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
1142 if ( (strcmp(info.sectName, "__allow_alt_plat") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
1143 result = true;
1144 stop = true;
1145 }
1146 });
1147 return result;
1148 }
1149
1150 bool MachOFile::hasChainedFixups() const
1151 {
1152 #if SUPPORT_ARCH_arm64e
1153 // arm64e always uses chained fixups
1154 if ( (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) )
1155 return true;
1156 #endif
1157 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
1158 }
1159
1160 uint64_t MachOFile::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
1161 {
1162 uint64_t result = 0;
1163 int bit = 0;
1164 do {
1165 if ( p == end ) {
1166 diag.error("malformed uleb128");
1167 break;
1168 }
1169 uint64_t slice = *p & 0x7f;
1170
1171 if ( bit > 63 ) {
1172 diag.error("uleb128 too big for uint64");
1173 break;
1174 }
1175 else {
1176 result |= (slice << bit);
1177 bit += 7;
1178 }
1179 }
1180 while (*p++ & 0x80);
1181 return result;
1182 }
1183
1184
1185 int64_t MachOFile::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
1186 {
1187 int64_t result = 0;
1188 int bit = 0;
1189 uint8_t byte = 0;
1190 do {
1191 if ( p == end ) {
1192 diag.error("malformed sleb128");
1193 break;
1194 }
1195 byte = *p++;
1196 result |= (((int64_t)(byte & 0x7f)) << bit);
1197 bit += 7;
1198 } while (byte & 0x80);
1199 // sign extend negative numbers
1200 if ( (byte & 0x40) != 0 )
1201 result |= (~0ULL) << bit;
1202 return result;
1203 }
1204
1205
1206 } // namespace dyld3
1207
1208
1209
1210
1211