dyld-832.7.1.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 <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/fcntl.h>
32 #include <unistd.h>
33 #include <TargetConditionals.h>
34 #include <mach/host_info.h>
35 #include <mach/mach.h>
36 #include <mach/mach_host.h>
37
38 #include "Array.h"
39 #include "MachOFile.h"
40 #include "SupportedArchs.h"
41
42 #if BUILDING_DYLD || BUILDING_LIBDYLD
43 // define away restrict until rdar://60166935 is fixed
44 #define restrict
45 #include <subsystem.h>
46 #endif
47
48 namespace dyld3 {
49
50 //////////////////////////// posix wrappers ////////////////////////////////////////
51
52 // <rdar://problem/10111032> wrap calls to stat() with check for EAGAIN
53 int stat(const char* path, struct stat* buf)
54 {
55 int result;
56 do {
57 #if BUILDING_DYLD || BUILDING_LIBDYLD
58 result = ::stat_with_subsystem(path, buf);
59 #else
60 result = ::stat(path, buf);
61 #endif
62 } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
63
64 return result;
65 }
66
67 // <rdar://problem/13805025> dyld should retry open() if it gets an EGAIN
68 int open(const char* path, int flag, int other)
69 {
70 int result;
71 do {
72 #if BUILDING_DYLD || BUILDING_LIBDYLD
73 if (flag & O_CREAT)
74 result = ::open(path, flag, other);
75 else
76 result = ::open_with_subsystem(path, flag);
77 #else
78 result = ::open(path, flag, other);
79 #endif
80 } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
81
82 return result;
83 }
84
85
86 //////////////////////////// FatFile ////////////////////////////////////////
87
88 const FatFile* FatFile::isFatFile(const void* fileStart)
89 {
90 const FatFile* fileStartAsFat = (FatFile*)fileStart;
91 if ( (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC)) || (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC_64)) )
92 return fileStartAsFat;
93 else
94 return nullptr;
95 }
96
97 bool FatFile::isValidSlice(Diagnostics& diag, uint64_t fileLen, uint32_t sliceIndex,
98 uint32_t sliceCpuType, uint32_t sliceCpuSubType, uint64_t sliceOffset, uint64_t sliceLen) const {
99 if ( greaterThanAddOrOverflow(sliceOffset, sliceLen, fileLen) ) {
100 diag.error("slice %d extends beyond end of file", sliceIndex);
101 return false;
102 }
103 const dyld3::MachOFile* mf = (const dyld3::MachOFile*)((uint8_t*)this+sliceOffset);
104 if (!mf->isMachO(diag, sliceLen))
105 return false;
106 if ( (mf->cputype != (cpu_type_t)sliceCpuType) || (mf->cpusubtype != (cpu_subtype_t)sliceCpuSubType) ) {
107 diag.error("cpu type/subtype mismatch");
108 return false;
109 }
110 uint32_t pageSizeMask = mf->uses16KPages() ? 0x3FFF : 0xFFF;
111 if ( (sliceOffset & pageSizeMask) != 0 ) {
112 // slice not page aligned
113 if ( strncmp((char*)this+sliceOffset, "!<arch>", 7) == 0 )
114 diag.error("file is static library");
115 else
116 diag.error("slice is not page aligned");
117 return false;
118 }
119 return true;
120 }
121
122 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
123 {
124 if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
125 const uint64_t maxArchs = ((4096 - sizeof(fat_header)) / sizeof(fat_arch));
126 const uint32_t numArchs = OSSwapBigToHostInt32(nfat_arch);
127 if ( numArchs > maxArchs ) {
128 diag.error("fat header too large: %u entries", numArchs);
129 return;
130 }
131 bool stop = false;
132 const fat_arch* const archs = (fat_arch*)(((char*)this)+sizeof(fat_header));
133 for (uint32_t i=0; i < numArchs; ++i) {
134 uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
135 uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
136 uint32_t offset = OSSwapBigToHostInt32(archs[i].offset);
137 uint32_t len = OSSwapBigToHostInt32(archs[i].size);
138 if (isValidSlice(diag, fileLen, i, cpuType, cpuSubType, offset, len))
139 callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
140 if ( stop )
141 break;
142 }
143
144 // Look for one more slice
145 if ( numArchs != maxArchs ) {
146 uint32_t cpuType = OSSwapBigToHostInt32(archs[numArchs].cputype);
147 uint32_t cpuSubType = OSSwapBigToHostInt32(archs[numArchs].cpusubtype);
148 uint32_t offset = OSSwapBigToHostInt32(archs[numArchs].offset);
149 uint32_t len = OSSwapBigToHostInt32(archs[numArchs].size);
150 if ((cpuType == CPU_TYPE_ARM64) && ((cpuSubType == CPU_SUBTYPE_ARM64_ALL || cpuSubType == CPU_SUBTYPE_ARM64_V8))) {
151 if (isValidSlice(diag, fileLen, numArchs, cpuType, cpuSubType, offset, len))
152 callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
153 }
154 }
155 }
156 else if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC_64) ) {
157 if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
158 diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
159 return;
160 }
161 bool stop = false;
162 const fat_arch_64* const archs = (fat_arch_64*)(((char*)this)+sizeof(fat_header));
163 for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
164 uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype);
165 uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
166 uint64_t offset = OSSwapBigToHostInt64(archs[i].offset);
167 uint64_t len = OSSwapBigToHostInt64(archs[i].size);
168 if (isValidSlice(diag, fileLen, i, cpuType, cpuSubType, offset, len))
169 callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
170 if ( stop )
171 break;
172 }
173 }
174 else {
175 diag.error("not a fat file");
176 }
177 }
178
179 bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool isOSBinary,
180 uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
181 {
182 missingSlice = false;
183 if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
184 return false;
185
186 __block int bestGrade = 0;
187 forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
188 if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType, isOSBinary)) {
189 if ( sliceGrade > bestGrade ) {
190 sliceOffset = (char*)sliceStart - (char*)this;
191 sliceLen = sliceSize;
192 bestGrade = sliceGrade;
193 }
194 }
195 });
196 if ( diag.hasError() )
197 return false;
198
199 if ( bestGrade == 0 )
200 missingSlice = true;
201
202 return (bestGrade != 0);
203 }
204
205
206 //////////////////////////// GradedArchs ////////////////////////////////////////
207
208
209 #define GRADE_i386 CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, false
210 #define GRADE_x86_64 CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, false
211 #define GRADE_x86_64h CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, false
212 #define GRADE_armv7 CPU_TYPE_ARM, CPU_SUBTYPE_ARM64_ALL, false
213 #define GRADE_armv7s CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, false
214 #define GRADE_armv7k CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K, false
215 #define GRADE_arm64 CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, false
216 #define GRADE_arm64e CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, false
217 #define GRADE_arm64e_pb CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, true
218 #define GRADE_arm64_32 CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8, false
219
220 const GradedArchs GradedArchs::i386 = { {{GRADE_i386, 1}} };
221 const GradedArchs GradedArchs::x86_64 = { {{GRADE_x86_64, 1}} };
222 const GradedArchs GradedArchs::x86_64h = { {{GRADE_x86_64h, 2}, {GRADE_x86_64, 1}} };
223 const GradedArchs GradedArchs::arm64 = { {{GRADE_arm64, 1}} };
224 #if SUPPORT_ARCH_arm64e
225 const GradedArchs GradedArchs::arm64e_keysoff = { {{GRADE_arm64e, 2}, {GRADE_arm64, 1}} };
226 const GradedArchs GradedArchs::arm64e_keysoff_pb = { {{GRADE_arm64e_pb, 2}, {GRADE_arm64, 1}} };
227 const GradedArchs GradedArchs::arm64e = { {{GRADE_arm64e, 1}} };
228 const GradedArchs GradedArchs::arm64e_pb = { {{GRADE_arm64e_pb, 1}} };
229 #endif
230 const GradedArchs GradedArchs::armv7 = { {{GRADE_armv7, 1}} };
231 const GradedArchs GradedArchs::armv7s = { {{GRADE_armv7s, 2}, {GRADE_armv7, 1}} };
232 const GradedArchs GradedArchs::armv7k = { {{GRADE_armv7k, 1}} };
233 #if SUPPORT_ARCH_arm64_32
234 const GradedArchs GradedArchs::arm64_32 = { {{GRADE_arm64_32, 1}} };
235 #endif
236
237 int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype, bool isOSBinary) const
238 {
239 for (const CpuGrade* p = _orderedCpuTypes; p->type != 0; ++p) {
240 if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) {
241 if ( p->osBinary ) {
242 if ( isOSBinary )
243 return p->grade;
244 }
245 else {
246 return p->grade;
247 }
248 }
249 }
250 return 0;
251 }
252
253 const char* GradedArchs::name() const
254 {
255 return MachOFile::archName(_orderedCpuTypes[0].type, _orderedCpuTypes[0].subtype);
256 }
257
258 #if __x86_64__
259 static bool isHaswell()
260 {
261 // FIXME: figure out a commpage way to check this
262 static bool sAlreadyDetermined = false;
263 static bool sHaswell = false;
264 if ( !sAlreadyDetermined ) {
265 struct host_basic_info info;
266 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
267 mach_port_t hostPort = mach_host_self();
268 kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
269 mach_port_deallocate(mach_task_self(), hostPort);
270 sHaswell = (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
271 sAlreadyDetermined = true;
272 }
273 return sHaswell;
274 }
275 #endif
276
277 const GradedArchs& GradedArchs::forCurrentOS(bool keysOff, bool osBinariesOnly)
278 {
279 #if __arm64e__
280 if ( osBinariesOnly )
281 return (keysOff ? arm64e_keysoff_pb : arm64e_pb);
282 else
283 return (keysOff ? arm64e_keysoff : arm64e);
284 #elif __ARM64_ARCH_8_32__
285 return arm64_32;
286 #elif __arm64__
287 return arm64;
288 #elif __ARM_ARCH_7K__
289 return armv7k;
290 #elif __ARM_ARCH_7S__
291 return armv7s;
292 #elif __ARM_ARCH_7A__
293 return armv7;
294 #elif __x86_64__
295 return isHaswell() ? x86_64h : x86_64;
296 #elif __i386__
297 return i386;
298 #else
299 #error unknown platform
300 #endif
301 }
302
303 const GradedArchs& GradedArchs::forName(const char* archName, bool keysOff)
304 {
305 if (strcmp(archName, "x86_64h") == 0 )
306 return x86_64h;
307 else if (strcmp(archName, "x86_64") == 0 )
308 return x86_64;
309 #if SUPPORT_ARCH_arm64e
310 else if (strcmp(archName, "arm64e") == 0 )
311 return keysOff ? arm64e_keysoff : arm64e;
312 #endif
313 else if (strcmp(archName, "arm64") == 0 )
314 return arm64;
315 else if (strcmp(archName, "armv7k") == 0 )
316 return armv7k;
317 else if (strcmp(archName, "armv7s") == 0 )
318 return armv7s;
319 else if (strcmp(archName, "armv7") == 0 )
320 return armv7;
321 #if SUPPORT_ARCH_arm64_32
322 else if (strcmp(archName, "arm64_32") == 0 )
323 return arm64_32;
324 #endif
325 else if (strcmp(archName, "i386") == 0 )
326 return i386;
327 assert(0 && "unknown arch name");
328 }
329
330
331
332 //////////////////////////// MachOFile ////////////////////////////////////////
333
334
335 const MachOFile::ArchInfo MachOFile::_s_archInfos[] = {
336 { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
337 { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H },
338 { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL },
339 { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
340 #if SUPPORT_ARCH_arm64e
341 { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E },
342 #endif
343 #if SUPPORT_ARCH_arm64_32
344 { "arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8 },
345 #endif
346 { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K },
347 { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
348 { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }
349 };
350
351 const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
352 { "macOS", Platform::macOS, LC_VERSION_MIN_MACOSX },
353 { "iOS", Platform::iOS, LC_VERSION_MIN_IPHONEOS },
354 { "tvOS", Platform::tvOS, LC_VERSION_MIN_TVOS },
355 { "watchOS", Platform::watchOS, LC_VERSION_MIN_WATCHOS },
356 { "bridgeOS", Platform::bridgeOS, LC_BUILD_VERSION },
357 { "MacCatalyst", Platform::iOSMac, LC_BUILD_VERSION },
358 { "iOS-sim", Platform::iOS_simulator, LC_BUILD_VERSION },
359 { "tvOS-sim", Platform::tvOS_simulator, LC_BUILD_VERSION },
360 { "watchOS-sim", Platform::watchOS_simulator, LC_BUILD_VERSION },
361 };
362
363
364
365 bool MachOFile::is64() const
366 {
367 return (this->magic == MH_MAGIC_64);
368 }
369
370 size_t MachOFile::machHeaderSize() const
371 {
372 return is64() ? sizeof(mach_header_64) : sizeof(mach_header);
373 }
374
375 uint32_t MachOFile::maskedCpuSubtype() const
376 {
377 return (this->cpusubtype & ~CPU_SUBTYPE_MASK);
378 }
379
380 uint32_t MachOFile::pointerSize() const
381 {
382 if (this->magic == MH_MAGIC_64)
383 return 8;
384 else
385 return 4;
386 }
387
388 bool MachOFile::uses16KPages() const
389 {
390 switch (this->cputype) {
391 case CPU_TYPE_ARM64:
392 case CPU_TYPE_ARM64_32:
393 return true;
394 case CPU_TYPE_ARM:
395 // iOS is 16k aligned for armv7/armv7s and watchOS armv7k is 16k aligned
396 return this->cpusubtype == CPU_SUBTYPE_ARM_V7K;
397 default:
398 return false;
399 }
400 }
401
402 bool MachOFile::isArch(const char* aName) const
403 {
404 return (strcmp(aName, archName(this->cputype, this->cpusubtype)) == 0);
405 }
406
407 const char* MachOFile::archName(uint32_t cputype, uint32_t cpusubtype)
408 {
409 for (const ArchInfo& info : _s_archInfos) {
410 if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) {
411 return info.name;
412 }
413 }
414 return "unknown";
415 }
416
417 uint32_t MachOFile::cpuTypeFromArchName(const char* archName)
418 {
419 for (const ArchInfo& info : _s_archInfos) {
420 if ( strcmp(archName, info.name) == 0 ) {
421 return info.cputype;
422 }
423 }
424 return 0;
425 }
426
427 uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName)
428 {
429 for (const ArchInfo& info : _s_archInfos) {
430 if ( strcmp(archName, info.name) == 0 ) {
431 return info.cpusubtype;
432 }
433 }
434 return 0;
435 }
436
437 const char* MachOFile::archName() const
438 {
439 return archName(this->cputype, this->cpusubtype);
440 }
441
442 static void appendDigit(char*& s, unsigned& num, unsigned place, bool& startedPrinting)
443 {
444 if ( num >= place ) {
445 unsigned dig = (num/place);
446 *s++ = '0' + dig;
447 num -= (dig*place);
448 startedPrinting = true;
449 }
450 else if ( startedPrinting ) {
451 *s++ = '0';
452 }
453 }
454
455 static void appendNumber(char*& s, unsigned num)
456 {
457 assert(num < 99999);
458 bool startedPrinting = false;
459 appendDigit(s, num, 10000, startedPrinting);
460 appendDigit(s, num, 1000, startedPrinting);
461 appendDigit(s, num, 100, startedPrinting);
462 appendDigit(s, num, 10, startedPrinting);
463 appendDigit(s, num, 1, startedPrinting);
464 if ( !startedPrinting )
465 *s++ = '0';
466 }
467
468 void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString[32])
469 {
470 // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
471 char* s = versionString;
472 appendNumber(s, (packedVersion >> 16));
473 *s++ = '.';
474 appendNumber(s, (packedVersion >> 8) & 0xFF);
475 *s++ = '.';
476 appendNumber(s, (packedVersion & 0xFF));
477 *s++ = '\0';
478 }
479
480 bool MachOFile::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
481 {
482 __block bool foundRequestedPlatform = false;
483 __block bool foundOtherPlatform = false;
484 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
485 if ( platform == reqPlatform )
486 foundRequestedPlatform = true;
487 else
488 foundOtherPlatform = true;
489 });
490 // if checking that this binary is built for exactly one platform, fail if more
491 if ( foundOtherPlatform && onlyOnePlatform )
492 return false;
493 if ( foundRequestedPlatform )
494 return true;
495
496 // binary has no explict load command to mark platform
497 // could be an old macOS binary, look at arch
498 if ( !foundOtherPlatform && (reqPlatform == Platform::macOS) ) {
499 if ( this->cputype == CPU_TYPE_X86_64 )
500 return true;
501 if ( this->cputype == CPU_TYPE_I386 )
502 return true;
503 }
504
505 #if BUILDING_DYLDINFO
506 // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms
507 if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) )
508 return true;
509 #endif
510
511 return false;
512 }
513
514 bool MachOFile::loadableIntoProcess(Platform processPlatform, const char* path) const
515 {
516 if ( this->builtForPlatform(processPlatform) )
517 return true;
518
519 // Some host macOS dylibs can be loaded into simulator processes
520 if ( MachOFile::isSimulatorPlatform(processPlatform) && this->builtForPlatform(Platform::macOS)) {
521 static const char* macOSHost[] = {
522 "/usr/lib/system/libsystem_kernel.dylib",
523 "/usr/lib/system/libsystem_platform.dylib",
524 "/usr/lib/system/libsystem_pthread.dylib",
525 "/usr/lib/system/libsystem_platform_debug.dylib",
526 "/usr/lib/system/libsystem_pthread_debug.dylib",
527 "/usr/lib/system/host/liblaunch_sim.dylib",
528 };
529 for (const char* libPath : macOSHost) {
530 if (strcmp(libPath, path) == 0)
531 return true;
532 }
533 }
534
535 // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable
536 if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOSMac, true) )
537 return true;
538 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
539 if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) )
540 return true;
541 #endif
542
543 bool iOSonMac = (processPlatform == Platform::iOSMac);
544 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
545 // allow iOS binaries in iOSApp
546 if ( processPlatform == Platform::iOS ) {
547 // can load Catalyst binaries into iOS process
548 if ( this->builtForPlatform(Platform::iOSMac) )
549 return true;
550 iOSonMac = true;
551 }
552 #endif
553 // macOS dylibs can be loaded into iOSMac processes
554 if ( (iOSonMac) && this->builtForPlatform(Platform::macOS, true) )
555 return true;
556
557 return false;
558 }
559
560 bool MachOFile::isZippered() const
561 {
562 __block bool macOS = false;
563 __block bool iOSMac = false;
564 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
565 if ( platform == Platform::macOS )
566 macOS = true;
567 else if ( platform == Platform::iOSMac )
568 iOSMac = true;
569 });
570 return macOS && iOSMac;
571 }
572
573 bool MachOFile::inDyldCache() const {
574 return (this->flags & 0x80000000);
575 }
576
577 Platform MachOFile::currentPlatform()
578 {
579
580 #if TARGET_OS_SIMULATOR
581 #if TARGET_OS_WATCH
582 return Platform::watchOS_simulator;
583 #elif TARGET_OS_TV
584 return Platform::tvOS_simulator;
585 #else
586 return Platform::iOS_simulator;
587 #endif
588 #elif TARGET_OS_BRIDGE
589 return Platform::bridgeOS;
590 #elif TARGET_OS_WATCH
591 return Platform::watchOS;
592 #elif TARGET_OS_TV
593 return Platform::tvOS;
594 #elif TARGET_OS_IOS
595 return Platform::iOS;
596 #elif TARGET_OS_OSX
597 return Platform::macOS;
598 #elif TARGET_OS_DRIVERKIT
599 return Platform::driverKit;
600 #else
601 #error unknown platform
602 #endif
603 }
604
605
606 const char* MachOFile::currentArchName()
607 {
608 #if __ARM_ARCH_7K__
609 return "armv7k";
610 #elif __ARM_ARCH_7A__
611 return "armv7";
612 #elif __ARM_ARCH_7S__
613 return "armv7s";
614 #elif __arm64e__
615 return "arm64e";
616 #elif __arm64__
617 #if __LP64__
618 return "arm64";
619 #else
620 return "arm64_32";
621 #endif
622 #elif __x86_64__
623 return isHaswell() ? "x86_64h" : "x86_64";
624 #elif __i386__
625 return "i386";
626 #else
627 #error unknown arch
628 #endif
629 }
630
631 bool MachOFile::isSimulatorPlatform(Platform platform)
632 {
633 return ( (platform == Platform::iOS_simulator) ||
634 (platform == Platform::watchOS_simulator) ||
635 (platform == Platform::tvOS_simulator) );
636 }
637
638 bool MachOFile::isDylib() const
639 {
640 return (this->filetype == MH_DYLIB);
641 }
642
643 bool MachOFile::isBundle() const
644 {
645 return (this->filetype == MH_BUNDLE);
646 }
647
648 bool MachOFile::isMainExecutable() const
649 {
650 return (this->filetype == MH_EXECUTE);
651 }
652
653 bool MachOFile::isDynamicExecutable() const
654 {
655 if ( this->filetype != MH_EXECUTE )
656 return false;
657
658 // static executables do not have dyld load command
659 return hasLoadCommand(LC_LOAD_DYLINKER);
660 }
661
662 bool MachOFile::isStaticExecutable() const
663 {
664 if ( this->filetype != MH_EXECUTE )
665 return false;
666
667 // static executables do not have dyld load command
668 return !hasLoadCommand(LC_LOAD_DYLINKER);
669 }
670
671 bool MachOFile::isKextBundle() const
672 {
673 return (this->filetype == MH_KEXT_BUNDLE);
674 }
675
676 bool MachOFile::isFileSet() const
677 {
678 return (this->filetype == MH_FILESET);
679 }
680
681 bool MachOFile::isPIE() const
682 {
683 return (this->flags & MH_PIE);
684 }
685
686 bool MachOFile::isPreload() const
687 {
688 return (this->filetype == MH_PRELOAD);
689 }
690
691 const char* MachOFile::platformName(Platform reqPlatform)
692 {
693 for (const PlatformInfo& info : _s_platformInfos) {
694 if ( info.platform == reqPlatform )
695 return info.name;
696 }
697 return "unknown platform";
698 }
699
700 void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
701 {
702 Diagnostics diag;
703 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
704 const build_version_command* buildCmd = (build_version_command *)cmd;
705 const version_min_command* versCmd = (version_min_command*)cmd;
706 switch ( cmd->cmd ) {
707 case LC_BUILD_VERSION:
708 handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
709 break;
710 case LC_VERSION_MIN_MACOSX:
711 handler(Platform::macOS, versCmd->version, versCmd->sdk);
712 break;
713 case LC_VERSION_MIN_IPHONEOS:
714 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
715 handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
716 else
717 handler(Platform::iOS, versCmd->version, versCmd->sdk);
718 break;
719 case LC_VERSION_MIN_TVOS:
720 if ( this->cputype == CPU_TYPE_X86_64 )
721 handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
722 else
723 handler(Platform::tvOS, versCmd->version, versCmd->sdk);
724 break;
725 case LC_VERSION_MIN_WATCHOS:
726 if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
727 handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
728 else
729 handler(Platform::watchOS, versCmd->version, versCmd->sdk);
730 break;
731 }
732 });
733 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
734 }
735
736
737 bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
738 {
739 if ( !hasMachOMagic() ) {
740 // old PPC slices are not currently valid "mach-o" but should not cause an error
741 if ( !hasMachOBigEndianMagic() )
742 diag.error("file does not start with MH_MAGIC[_64]");
743 return false;
744 }
745 if ( this->sizeofcmds + machHeaderSize() > fileSize ) {
746 diag.error("load commands exceed length of first segment");
747 return false;
748 }
749 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { });
750 return diag.noError();
751 }
752
753 bool MachOFile::hasMachOMagic() const
754 {
755 return ( (this->magic == MH_MAGIC) || (this->magic == MH_MAGIC_64) );
756 }
757
758 bool MachOFile::hasMachOBigEndianMagic() const
759 {
760 return ( (this->magic == MH_CIGAM) || (this->magic == MH_CIGAM_64) );
761 }
762
763
764 void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
765 {
766 bool stop = false;
767 const load_command* startCmds = nullptr;
768 if ( this->magic == MH_MAGIC_64 )
769 startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
770 else if ( this->magic == MH_MAGIC )
771 startCmds = (load_command*)((char *)this + sizeof(mach_header));
772 else if ( hasMachOBigEndianMagic() )
773 return; // can't process big endian mach-o
774 else {
775 const uint32_t* h = (uint32_t*)this;
776 diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
777 return; // not a mach-o file
778 }
779 const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
780 const load_command* cmd = startCmds;
781 for (uint32_t i = 0; i < this->ncmds; ++i) {
782 const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
783 if ( cmd->cmdsize < 8 ) {
784 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);
785 return;
786 }
787 if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
788 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);
789 return;
790 }
791 callback(cmd, stop);
792 if ( stop )
793 return;
794 cmd = nextCmd;
795 }
796 }
797
798 void MachOFile::removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop))
799 {
800 bool stop = false;
801 const load_command* startCmds = nullptr;
802 if ( this->magic == MH_MAGIC_64 )
803 startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
804 else if ( this->magic == MH_MAGIC )
805 startCmds = (load_command*)((char *)this + sizeof(mach_header));
806 else if ( hasMachOBigEndianMagic() )
807 return; // can't process big endian mach-o
808 else {
809 const uint32_t* h = (uint32_t*)this;
810 diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
811 return; // not a mach-o file
812 }
813 const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
814 auto cmd = (load_command*)startCmds;
815 const uint32_t origNcmds = this->ncmds;
816 unsigned bytesRemaining = this->sizeofcmds;
817 for (uint32_t i = 0; i < origNcmds; ++i) {
818 bool remove = false;
819 auto nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
820 if ( cmd->cmdsize < 8 ) {
821 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);
822 return;
823 }
824 if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
825 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);
826 return;
827 }
828 callback(cmd, remove, stop);
829 if ( remove ) {
830 this->sizeofcmds -= cmd->cmdsize;
831 ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
832 this->ncmds--;
833 } else {
834 bytesRemaining -= cmd->cmdsize;
835 cmd = nextCmd;
836 }
837 if ( stop )
838 break;
839 }
840 if ( cmd )
841 ::bzero(cmd, bytesRemaining);
842 }
843
844 const char* MachOFile::installName() const
845 {
846 const char* name;
847 uint32_t compatVersion;
848 uint32_t currentVersion;
849 if ( getDylibInstallName(&name, &compatVersion, &currentVersion) )
850 return name;
851 return nullptr;
852 }
853
854 bool MachOFile::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
855 {
856 Diagnostics diag;
857 __block bool found = false;
858 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
859 if ( cmd->cmd == LC_ID_DYLIB ) {
860 const dylib_command* dylibCmd = (dylib_command*)cmd;
861 *compatVersion = dylibCmd->dylib.compatibility_version;
862 *currentVersion = dylibCmd->dylib.current_version;
863 *installName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
864 found = true;
865 stop = true;
866 }
867 });
868 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
869 return found;
870 }
871
872 bool MachOFile::getUuid(uuid_t uuid) const
873 {
874 Diagnostics diag;
875 __block bool found = false;
876 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
877 if ( cmd->cmd == LC_UUID ) {
878 const uuid_command* uc = (const uuid_command*)cmd;
879 memcpy(uuid, uc->uuid, sizeof(uuid_t));
880 found = true;
881 stop = true;
882 }
883 });
884 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
885 if ( !found )
886 bzero(uuid, sizeof(uuid_t));
887 return found;
888 }
889
890 void MachOFile::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
891 {
892 Diagnostics diag;
893 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
894 switch ( cmd->cmd ) {
895 case LC_LOAD_DYLIB:
896 case LC_LOAD_WEAK_DYLIB:
897 case LC_REEXPORT_DYLIB:
898 case LC_LOAD_UPWARD_DYLIB: {
899 const dylib_command* dylibCmd = (dylib_command*)cmd;
900 const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
901 callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB),
902 dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop);
903 }
904 break;
905 }
906 });
907 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
908 }
909
910 void MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
911 {
912 Diagnostics diag;
913 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
914 if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
915 const dylinker_command* envCmd = (dylinker_command*)cmd;
916 const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
917 // only process variables that start with DYLD_ and end in _PATH
918 if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
919 const char* equals = strchr(keyEqualsValue, '=');
920 if ( equals != NULL ) {
921 if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
922 callback(keyEqualsValue, stop);
923 }
924 }
925 }
926 }
927 });
928 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
929 }
930
931 bool MachOFile::enforceCompatVersion() const
932 {
933 __block bool result = true;
934 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
935 switch ( platform ) {
936 case Platform::macOS:
937 if ( minOS >= 0x000A0E00 ) // macOS 10.14
938 result = false;
939 break;
940 case Platform::iOS:
941 case Platform::tvOS:
942 case Platform::iOS_simulator:
943 case Platform::tvOS_simulator:
944 if ( minOS >= 0x000C0000 ) // iOS 12.0
945 result = false;
946 break;
947 case Platform::watchOS:
948 case Platform::watchOS_simulator:
949 if ( minOS >= 0x00050000 ) // watchOS 5.0
950 result = false;
951 break;
952 case Platform::bridgeOS:
953 if ( minOS >= 0x00030000 ) // bridgeOS 3.0
954 result = false;
955 break;
956 case Platform::driverKit:
957 case Platform::iOSMac:
958 result = false;
959 break;
960 case Platform::unknown:
961 break;
962 }
963 });
964 return result;
965 }
966
967 const thread_command* MachOFile::unixThreadLoadCommand() const {
968 Diagnostics diag;
969 __block const thread_command* command = nullptr;
970 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
971 if ( cmd->cmd == LC_UNIXTHREAD ) {
972 command = (const thread_command*)cmd;
973 stop = true;
974 }
975 });
976 return command;
977 }
978
979
980 uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const
981 {
982 switch ( this->cputype ) {
983 case CPU_TYPE_I386:
984 return 10; // i386_thread_state_t.eip
985 case CPU_TYPE_X86_64:
986 return 16; // x86_thread_state64_t.rip
987 case CPU_TYPE_ARM:
988 return 15; // arm_thread_state_t.pc
989 case CPU_TYPE_ARM64:
990 return 32; // arm_thread_state64_t.__pc
991 }
992 return ~0U;
993 }
994
995
996 uint64_t MachOFile::entryAddrFromThreadCmd(const thread_command* cmd) const
997 {
998 assert(cmd->cmd == LC_UNIXTHREAD);
999 const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
1000 const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
1001
1002 uint32_t index = entryAddrRegisterIndexForThreadCmd();
1003 if (index == ~0U)
1004 return 0;
1005
1006 return is64() ? regs64[index] : regs32[index];
1007 }
1008
1009
1010 void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
1011 {
1012 Diagnostics diag;
1013 const bool intel32 = (this->cputype == CPU_TYPE_I386);
1014 __block uint32_t segIndex = 0;
1015 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1016 if ( cmd->cmd == LC_SEGMENT_64 ) {
1017 const segment_command_64* segCmd = (segment_command_64*)cmd;
1018 uint64_t sizeOfSections = segCmd->vmsize;
1019 uint8_t p2align = 0;
1020 const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
1021 const section_64* const sectionsEnd = &sectionsStart[segCmd->nsects];
1022 for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
1023 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
1024 if ( sect->align > p2align )
1025 p2align = sect->align;
1026 }
1027 SegmentInfo info;
1028 info.fileOffset = segCmd->fileoff;
1029 info.fileSize = segCmd->filesize;
1030 info.vmAddr = segCmd->vmaddr;
1031 info.vmSize = segCmd->vmsize;
1032 info.sizeOfSections = sizeOfSections;
1033 info.segName = segCmd->segname;
1034 info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
1035 info.protections = segCmd->initprot;
1036 info.textRelocs = false;
1037 info.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
1038 info.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
1039 info.p2align = p2align;
1040 info.segIndex = segIndex;
1041 callback(info, stop);
1042 ++segIndex;
1043 }
1044 else if ( cmd->cmd == LC_SEGMENT ) {
1045 const segment_command* segCmd = (segment_command*)cmd;
1046 uint64_t sizeOfSections = segCmd->vmsize;
1047 uint8_t p2align = 0;
1048 bool hasTextRelocs = false;
1049 const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
1050 const section* const sectionsEnd = &sectionsStart[segCmd->nsects];
1051 for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
1052 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
1053 if ( sect->align > p2align )
1054 p2align = sect->align;
1055 if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
1056 hasTextRelocs = true;
1057 }
1058 SegmentInfo info;
1059 info.fileOffset = segCmd->fileoff;
1060 info.fileSize = segCmd->filesize;
1061 info.vmAddr = segCmd->vmaddr;
1062 info.vmSize = segCmd->vmsize;
1063 info.sizeOfSections = sizeOfSections;
1064 info.segName = segCmd->segname;
1065 info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
1066 info.protections = segCmd->initprot;
1067 info.textRelocs = intel32 && !info.writable() && hasTextRelocs;
1068 info.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
1069 info.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
1070 info.p2align = p2align;
1071 info.segIndex = segIndex;
1072 callback(info, stop);
1073 ++segIndex;
1074 }
1075 });
1076 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
1077 }
1078
1079 void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
1080 {
1081 Diagnostics diag;
1082 BLOCK_ACCCESSIBLE_ARRAY(char, sectNameCopy, 20); // read as: char sectNameCopy[20];
1083 const bool intel32 = (this->cputype == CPU_TYPE_I386);
1084 __block uint32_t segIndex = 0;
1085 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1086 SectionInfo sectInfo;
1087 if ( cmd->cmd == LC_SEGMENT_64 ) {
1088 const segment_command_64* segCmd = (segment_command_64*)cmd;
1089 uint64_t sizeOfSections = segCmd->vmsize;
1090 uint8_t p2align = 0;
1091 const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
1092 const section_64* const sectionsEnd = &sectionsStart[segCmd->nsects];
1093 for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
1094 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
1095 if ( sect->align > p2align )
1096 p2align = sect->align;
1097 }
1098 sectInfo.segInfo.fileOffset = segCmd->fileoff;
1099 sectInfo.segInfo.fileSize = segCmd->filesize;
1100 sectInfo.segInfo.vmAddr = segCmd->vmaddr;
1101 sectInfo.segInfo.vmSize = segCmd->vmsize;
1102 sectInfo.segInfo.sizeOfSections = sizeOfSections;
1103 sectInfo.segInfo.segName = segCmd->segname;
1104 sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
1105 sectInfo.segInfo.protections = segCmd->initprot;
1106 sectInfo.segInfo.textRelocs = false;
1107 sectInfo.segInfo.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
1108 sectInfo.segInfo.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
1109 sectInfo.segInfo.p2align = p2align;
1110 sectInfo.segInfo.segIndex = segIndex;
1111 for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
1112 const char* sectName = sect->sectname;
1113 if ( sectName[15] != '\0' ) {
1114 strlcpy(sectNameCopy, sectName, 17);
1115 sectName = sectNameCopy;
1116 }
1117 bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
1118 sectInfo.sectName = sectName;
1119 sectInfo.sectFileOffset = sect->offset;
1120 sectInfo.sectFlags = sect->flags;
1121 sectInfo.sectAddr = sect->addr;
1122 sectInfo.sectSize = sect->size;
1123 sectInfo.sectAlignP2 = sect->align;
1124 sectInfo.reserved1 = sect->reserved1;
1125 sectInfo.reserved2 = sect->reserved2;
1126 callback(sectInfo, malformedSectionRange, stop);
1127 }
1128 ++segIndex;
1129 }
1130 else if ( cmd->cmd == LC_SEGMENT ) {
1131 const segment_command* segCmd = (segment_command*)cmd;
1132 uint64_t sizeOfSections = segCmd->vmsize;
1133 uint8_t p2align = 0;
1134 bool hasTextRelocs = false;
1135 const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
1136 const section* const sectionsEnd = &sectionsStart[segCmd->nsects];
1137 for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
1138 sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
1139 if ( sect->align > p2align )
1140 p2align = sect->align;
1141 if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
1142 hasTextRelocs = true;
1143 }
1144 sectInfo.segInfo.fileOffset = segCmd->fileoff;
1145 sectInfo.segInfo.fileSize = segCmd->filesize;
1146 sectInfo.segInfo.vmAddr = segCmd->vmaddr;
1147 sectInfo.segInfo.vmSize = segCmd->vmsize;
1148 sectInfo.segInfo.sizeOfSections = sizeOfSections;
1149 sectInfo.segInfo.segName = segCmd->segname;
1150 sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
1151 sectInfo.segInfo.protections = segCmd->initprot;
1152 sectInfo.segInfo.textRelocs = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
1153 sectInfo.segInfo.readOnlyData = ((segCmd->flags & SG_READ_ONLY) != 0);
1154 sectInfo.segInfo.isProtected = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
1155 sectInfo.segInfo.p2align = p2align;
1156 sectInfo.segInfo.segIndex = segIndex;
1157 for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
1158 const char* sectName = sect->sectname;
1159 if ( sectName[15] != '\0' ) {
1160 strlcpy(sectNameCopy, sectName, 17);
1161 sectName = sectNameCopy;
1162 }
1163 bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
1164 sectInfo.sectName = sectName;
1165 sectInfo.sectFileOffset = sect->offset;
1166 sectInfo.sectFlags = sect->flags;
1167 sectInfo.sectAddr = sect->addr;
1168 sectInfo.sectSize = sect->size;
1169 sectInfo.sectAlignP2 = sect->align;
1170 sectInfo.reserved1 = sect->reserved1;
1171 sectInfo.reserved2 = sect->reserved2;
1172 callback(sectInfo, malformedSectionRange, stop);
1173 }
1174 ++segIndex;
1175 }
1176 });
1177 diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
1178 }
1179
1180 bool MachOFile::hasWeakDefs() const
1181 {
1182 return (this->flags & MH_WEAK_DEFINES);
1183 }
1184
1185 bool MachOFile::hasThreadLocalVariables() const
1186 {
1187 return (this->flags & MH_HAS_TLV_DESCRIPTORS);
1188 }
1189
1190 static bool endsWith(const char* str, const char* suffix)
1191 {
1192 size_t strLen = strlen(str);
1193 size_t suffixLen = strlen(suffix);
1194 if ( strLen < suffixLen )
1195 return false;
1196 return (strcmp(&str[strLen-suffixLen], suffix) == 0);
1197 }
1198
1199 bool MachOFile::isSharedCacheEligiblePath(const char* dylibName) {
1200 return ( (strncmp(dylibName, "/usr/lib/", 9) == 0)
1201 || (strncmp(dylibName, "/System/Library/", 16) == 0)
1202 || (strncmp(dylibName, "/System/iOSSupport/usr/lib/", 27) == 0)
1203 || (strncmp(dylibName, "/System/iOSSupport/System/Library/", 34) == 0)
1204 || (strncmp(dylibName, "/Library/Apple/usr/lib/", 23) == 0)
1205 || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0) );
1206 }
1207
1208 static bool startsWith(const char* buffer, const char* valueToFind) {
1209 return strncmp(buffer, valueToFind, strlen(valueToFind)) == 0;
1210 }
1211
1212 static bool platformExcludesSharedCache_macOS(const char* installName) {
1213 // Note: This function basically matches dontCache() from update dyld shared cache
1214
1215 if ( startsWith(installName, "/usr/lib/system/introspection/") )
1216 return true;
1217 if ( startsWith(installName, "/System/Library/QuickTime/") )
1218 return true;
1219 if ( startsWith(installName, "/System/Library/Tcl/") )
1220 return true;
1221 if ( startsWith(installName, "/System/Library/Perl/") )
1222 return true;
1223 if ( startsWith(installName, "/System/Library/MonitorPanels/") )
1224 return true;
1225 if ( startsWith(installName, "/System/Library/Accessibility/") )
1226 return true;
1227 if ( startsWith(installName, "/usr/local/") )
1228 return true;
1229 if ( startsWith(installName, "/usr/lib/pam/") )
1230 return true;
1231 // We no longer support ROSP, so skip all paths which start with the special prefix
1232 if ( startsWith(installName, "/System/Library/Templates/Data/") )
1233 return true;
1234
1235 // anything inside a .app bundle is specific to app, so should not be in shared cache
1236 if ( strstr(installName, ".app/") != NULL )
1237 return true;
1238
1239 return false;
1240 }
1241
1242 static bool platformExcludesSharedCache_iOS(const char* installName) {
1243 if ( strcmp(installName, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
1244 return true;
1245 if ( strcmp(installName, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
1246 return true;
1247 return false;
1248 }
1249
1250 static bool platformExcludesSharedCache_tvOS(const char* installName) {
1251 return platformExcludesSharedCache_iOS(installName);
1252 }
1253
1254 static bool platformExcludesSharedCache_watchOS(const char* installName) {
1255 return platformExcludesSharedCache_iOS(installName);
1256 }
1257
1258 static bool platformExcludesSharedCache_bridgeOS(const char* installName) {
1259 return platformExcludesSharedCache_iOS(installName);
1260 }
1261
1262 // Returns true if the current platform requires that this install name be excluded from the shared cache
1263 // Note that this overrides any exclusion from anywhere else.
1264 static bool platformExcludesSharedCache(Platform platform, const char* installName) {
1265 switch (platform) {
1266 case dyld3::Platform::unknown:
1267 return false;
1268 case dyld3::Platform::macOS:
1269 return platformExcludesSharedCache_macOS(installName);
1270 case dyld3::Platform::iOS:
1271 return platformExcludesSharedCache_iOS(installName);
1272 case dyld3::Platform::tvOS:
1273 return platformExcludesSharedCache_tvOS(installName);
1274 case dyld3::Platform::watchOS:
1275 return platformExcludesSharedCache_watchOS(installName);
1276 case dyld3::Platform::bridgeOS:
1277 return platformExcludesSharedCache_bridgeOS(installName);
1278 case dyld3::Platform::iOSMac:
1279 return platformExcludesSharedCache_macOS(installName);
1280 case dyld3::Platform::iOS_simulator:
1281 return false;
1282 case dyld3::Platform::tvOS_simulator:
1283 return false;
1284 case dyld3::Platform::watchOS_simulator:
1285 return false;
1286 case dyld3::Platform::driverKit:
1287 return false;
1288 }
1289 }
1290
1291
1292
1293 bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
1294 {
1295
1296 if ( !isSharedCacheEligiblePath(path) ) {
1297 // Dont spam the user with an error about paths when we know these are never eligible.
1298 return false;
1299 }
1300
1301 // only dylibs can go in cache
1302 if ( this->filetype != MH_DYLIB ) {
1303 failureReason("Not MH_DYLIB");
1304 return false; // cannot continue, installName() will assert() if not a dylib
1305 }
1306
1307 // only dylibs built for /usr/lib or /System/Library can go in cache
1308
1309 const char* dylibName = installName();
1310 if ( dylibName[0] != '/' ) {
1311 failureReason("install name not an absolute path");
1312 // Don't continue as we don't want to spam the log with errors we don't need.
1313 return false;
1314 }
1315 else if ( strcmp(dylibName, path) != 0 ) {
1316 failureReason("install path does not match install name");
1317 return false;
1318 }
1319 else if ( strstr(dylibName, "//") != 0 ) {
1320 failureReason("install name should not include //");
1321 return false;
1322 }
1323 else if ( strstr(dylibName, "./") != 0 ) {
1324 failureReason("install name should not include ./");
1325 return false;
1326 }
1327
1328 __block bool platformExcludedFile = false;
1329 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
1330 if ( platformExcludedFile )
1331 return;
1332 if ( platformExcludesSharedCache(platform, dylibName) ) {
1333 platformExcludedFile = true;
1334 return;
1335 }
1336 });
1337 if ( platformExcludedFile ) {
1338 failureReason("install name is not shared cache eligible on platform");
1339 return false;
1340 }
1341
1342 bool retval = true;
1343
1344 // flat namespace files cannot go in cache
1345 if ( (this->flags & MH_TWOLEVEL) == 0 ) {
1346 retval = false;
1347 failureReason("Not built with two level namespaces");
1348 }
1349
1350 // don't put debug variants into dyld cache
1351 if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
1352 retval = false;
1353 failureReason("Variant image");
1354 }
1355
1356 // dylib must have extra info for moving DATA and TEXT segments apart
1357 __block bool hasExtraInfo = false;
1358 __block bool hasDyldInfo = false;
1359 __block bool hasExportTrie = false;
1360 __block bool hasLazyLoad = false;
1361 Diagnostics diag;
1362 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1363 if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
1364 hasExtraInfo = true;
1365 if ( cmd->cmd == LC_DYLD_INFO_ONLY )
1366 hasDyldInfo = true;
1367 if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE )
1368 hasExportTrie = true;
1369 if ( cmd->cmd == LC_LAZY_LOAD_DYLIB )
1370 hasLazyLoad = true;
1371 });
1372 if ( !hasExtraInfo ) {
1373 retval = false;
1374 failureReason("Missing split seg info");
1375 }
1376 if ( !hasDyldInfo && !hasExportTrie ) {
1377 retval = false;
1378 failureReason("Old binary, missing dyld info or export trie");
1379 }
1380 if ( hasLazyLoad ) {
1381 retval = false;
1382 failureReason("Has lazy load");
1383 }
1384
1385 // dylib can only depend on other dylibs in the shared cache
1386 __block bool allDepPathsAreGood = true;
1387 forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
1388 if ( !isSharedCacheEligiblePath(loadPath) ) {
1389 allDepPathsAreGood = false;
1390 stop = true;
1391 }
1392 });
1393 if ( !allDepPathsAreGood ) {
1394 retval = false;
1395 failureReason("Depends on dylibs ineligable for dyld cache");
1396 }
1397
1398 // dylibs with interposing info cannot be in cache
1399 if ( hasInterposingTuples() ) {
1400 retval = false;
1401 failureReason("Has interposing tuples");
1402 }
1403
1404 // Temporarily kick out swift binaries out of dyld cache on watchOS simulators as they have missing split seg
1405 if ( (this->cputype == CPU_TYPE_I386) && builtForPlatform(Platform::watchOS_simulator) ) {
1406 if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
1407 retval = false;
1408 failureReason("i386 swift binary");
1409 }
1410 }
1411
1412 return retval;
1413 }
1414
1415 #if BUILDING_APP_CACHE_UTIL
1416 bool MachOFile::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const
1417 {
1418 // only dylibs and the kernel itself can go in cache
1419 if ( this->filetype == MH_EXECUTE ) {
1420 // xnu
1421 } else if ( this->isKextBundle() ) {
1422 // kext's
1423 } else {
1424 failureReason("Not MH_KEXT_BUNDLE");
1425 return false;
1426 }
1427
1428 if ( this->filetype == MH_EXECUTE ) {
1429 // xnu
1430
1431 // two-level namespace binaries cannot go in cache
1432 if ( (this->flags & MH_TWOLEVEL) != 0 ) {
1433 failureReason("Built with two level namespaces");
1434 return false;
1435 }
1436
1437 // xnu kernel cannot have a page zero
1438 __block bool foundPageZero = false;
1439 forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
1440 if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 ) {
1441 foundPageZero = true;
1442 stop = true;
1443 }
1444 });
1445 if (foundPageZero) {
1446 failureReason("Has __PAGEZERO");
1447 return false;
1448 }
1449
1450 // xnu must have an LC_UNIXTHREAD to point to the entry point
1451 __block bool foundMainLC = false;
1452 __block bool foundUnixThreadLC = false;
1453 Diagnostics diag;
1454 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1455 if ( cmd->cmd == LC_MAIN ) {
1456 foundMainLC = true;
1457 stop = true;
1458 }
1459 else if ( cmd->cmd == LC_UNIXTHREAD ) {
1460 foundUnixThreadLC = true;
1461 }
1462 });
1463 if (foundMainLC) {
1464 failureReason("Found LC_MAIN");
1465 return false;
1466 }
1467 if (!foundUnixThreadLC) {
1468 failureReason("Expected LC_UNIXTHREAD");
1469 return false;
1470 }
1471
1472 if (diag.hasError()) {
1473 failureReason("Error parsing load commands");
1474 return false;
1475 }
1476
1477 // The kernel should be a static executable, not a dynamic one
1478 if ( !isStaticExecutable() ) {
1479 failureReason("Expected static executable");
1480 return false;
1481 }
1482
1483 // The kernel must be built with -pie
1484 if ( !isPIE() ) {
1485 failureReason("Expected pie");
1486 return false;
1487 }
1488 }
1489
1490 if ( isArch("arm64e") && isKextBundle() && !hasChainedFixups() ) {
1491 failureReason("Missing fixup information");
1492 return false;
1493 }
1494
1495 // dylibs with interposing info cannot be in cache
1496 __block bool hasInterposing = false;
1497 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
1498 if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
1499 hasInterposing = true;
1500 });
1501 if ( hasInterposing ) {
1502 failureReason("Has interposing tuples");
1503 return false;
1504 }
1505
1506 return true;
1507 }
1508 #endif
1509
1510 static bool platformExcludesPrebuiltClosure_macOS(const char* path) {
1511 // We no longer support ROSP, so skip all paths which start with the special prefix
1512 if ( startsWith(path, "/System/Library/Templates/Data/") )
1513 return true;
1514
1515 // anything inside a .app bundle is specific to app, so should not get a prebuilt closure
1516 if ( strstr(path, ".app/") != NULL )
1517 return true;
1518
1519 return false;
1520 }
1521
1522 static bool platformExcludesPrebuiltClosure_iOS(const char* path) {
1523 if ( strcmp(path, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
1524 return true;
1525 if ( strcmp(path, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
1526 return true;
1527 return false;
1528 }
1529
1530 static bool platformExcludesPrebuiltClosure_tvOS(const char* path) {
1531 return platformExcludesPrebuiltClosure_iOS(path);
1532 }
1533
1534 static bool platformExcludesPrebuiltClosure_watchOS(const char* path) {
1535 return platformExcludesPrebuiltClosure_iOS(path);
1536 }
1537
1538 static bool platformExcludesPrebuiltClosure_bridgeOS(const char* path) {
1539 return platformExcludesPrebuiltClosure_iOS(path);
1540 }
1541
1542 // Returns true if the current platform requires that this install name be excluded from the shared cache
1543 // Note that this overrides any exclusion from anywhere else.
1544 static bool platformExcludesPrebuiltClosure(Platform platform, const char* path) {
1545 switch (platform) {
1546 case dyld3::Platform::unknown:
1547 return false;
1548 case dyld3::Platform::macOS:
1549 return platformExcludesPrebuiltClosure_macOS(path);
1550 case dyld3::Platform::iOS:
1551 return platformExcludesPrebuiltClosure_iOS(path);
1552 case dyld3::Platform::tvOS:
1553 return platformExcludesPrebuiltClosure_tvOS(path);
1554 case dyld3::Platform::watchOS:
1555 return platformExcludesPrebuiltClosure_watchOS(path);
1556 case dyld3::Platform::bridgeOS:
1557 return platformExcludesPrebuiltClosure_bridgeOS(path);
1558 case dyld3::Platform::iOSMac:
1559 return platformExcludesPrebuiltClosure_macOS(path);
1560 case dyld3::Platform::iOS_simulator:
1561 return false;
1562 case dyld3::Platform::tvOS_simulator:
1563 return false;
1564 case dyld3::Platform::watchOS_simulator:
1565 return false;
1566 case dyld3::Platform::driverKit:
1567 return false;
1568 }
1569 }
1570
1571 bool MachOFile::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
1572 {
1573 __block bool retval = true;
1574
1575 // only dylibs can go in cache
1576 if ( (this->filetype != MH_DYLIB) && (this->filetype != MH_BUNDLE) ) {
1577 retval = false;
1578 failureReason("not MH_DYLIB or MH_BUNDLE");
1579 }
1580
1581 // flat namespace files cannot go in cache
1582 if ( (this->flags & MH_TWOLEVEL) == 0 ) {
1583 retval = false;
1584 failureReason("not built with two level namespaces");
1585 }
1586
1587 // can only depend on other dylibs with absolute paths
1588 __block bool allDepPathsAreGood = true;
1589 forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
1590 if ( loadPath[0] != '/' ) {
1591 allDepPathsAreGood = false;
1592 stop = true;
1593 }
1594 });
1595 if ( !allDepPathsAreGood ) {
1596 retval = false;
1597 failureReason("depends on dylibs that are not absolute paths");
1598 }
1599
1600 __block bool platformExcludedFile = false;
1601 forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
1602 if ( platformExcludedFile )
1603 return;
1604 if ( platformExcludesPrebuiltClosure(platform, path) ) {
1605 platformExcludedFile = true;
1606 return;
1607 }
1608 });
1609 if ( platformExcludedFile ) {
1610 failureReason("file cannot get a prebuilt closure on this platform");
1611 return false;
1612 }
1613
1614 // dylibs with interposing info cannot have dlopen closure pre-computed
1615 if ( hasInterposingTuples() ) {
1616 retval = false;
1617 failureReason("has interposing tuples");
1618 }
1619
1620 // special system dylib overrides cannot have closure pre-computed
1621 if ( strncmp(path, "/usr/lib/system/introspection/", 30) == 0 ) {
1622 retval = false;
1623 failureReason("override of OS dylib");
1624 }
1625
1626 return retval;
1627 }
1628
1629 bool MachOFile::hasInterposingTuples() const
1630 {
1631 __block bool hasInterposing = false;
1632 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
1633 if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
1634 hasInterposing = true;
1635 });
1636 return hasInterposing;
1637 }
1638
1639 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
1640 {
1641 if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
1642 if ( encCmd->cryptid == 1 ) {
1643 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
1644 textOffset = encCmd->cryptoff;
1645 size = encCmd->cryptsize;
1646 return true;
1647 }
1648 }
1649 textOffset = 0;
1650 size = 0;
1651 return false;
1652 }
1653
1654 bool MachOFile::canBeFairPlayEncrypted() const
1655 {
1656 return (findFairPlayEncryptionLoadCommand() != nullptr);
1657 }
1658
1659 const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
1660 {
1661 __block const encryption_info_command* result = nullptr;
1662 Diagnostics diag;
1663 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1664 if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
1665 result = (encryption_info_command*)cmd;
1666 stop = true;
1667 }
1668 });
1669 if ( diag.noError() )
1670 return result;
1671 else
1672 return nullptr;
1673 }
1674
1675
1676 bool MachOFile::hasLoadCommand(uint32_t cmdNum) const
1677 {
1678 __block bool hasLC = false;
1679 Diagnostics diag;
1680 forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
1681 if ( cmd->cmd == cmdNum ) {
1682 hasLC = true;
1683 stop = true;
1684 }
1685 });
1686 return hasLC;
1687 }
1688
1689 bool MachOFile::allowsAlternatePlatform() const
1690 {
1691 __block bool result = false;
1692 forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
1693 if ( (strcmp(info.sectName, "__allow_alt_plat") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
1694 result = true;
1695 stop = true;
1696 }
1697 });
1698 return result;
1699 }
1700
1701 bool MachOFile::hasChainedFixups() const
1702 {
1703 #if SUPPORT_ARCH_arm64e
1704 // arm64e always uses chained fixups
1705 if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
1706 // Not all binaries have fixups at all so check for the load commands
1707 return hasLoadCommand(LC_DYLD_INFO_ONLY) || hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
1708 }
1709 #endif
1710 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
1711 }
1712
1713 bool MachOFile::hasChainedFixupsLoadCommand() const
1714 {
1715 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
1716 }
1717
1718 uint64_t MachOFile::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
1719 {
1720 uint64_t result = 0;
1721 int bit = 0;
1722 do {
1723 if ( p == end ) {
1724 diag.error("malformed uleb128");
1725 break;
1726 }
1727 uint64_t slice = *p & 0x7f;
1728
1729 if ( bit > 63 ) {
1730 diag.error("uleb128 too big for uint64");
1731 break;
1732 }
1733 else {
1734 result |= (slice << bit);
1735 bit += 7;
1736 }
1737 }
1738 while (*p++ & 0x80);
1739 return result;
1740 }
1741
1742
1743 int64_t MachOFile::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
1744 {
1745 int64_t result = 0;
1746 int bit = 0;
1747 uint8_t byte = 0;
1748 do {
1749 if ( p == end ) {
1750 diag.error("malformed sleb128");
1751 break;
1752 }
1753 byte = *p++;
1754 result |= (((int64_t)(byte & 0x7f)) << bit);
1755 bit += 7;
1756 } while (byte & 0x80);
1757 // sign extend negative numbers
1758 if ( ((byte & 0x40) != 0) && (bit < 64) )
1759 result |= (~0ULL) << bit;
1760 return result;
1761 }
1762
1763
1764 } // namespace dyld3
1765
1766
1767
1768
1769