2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/fcntl.h>
33 #include <TargetConditionals.h>
34 #include <mach/host_info.h>
35 #include <mach/mach.h>
36 #include <mach/mach_host.h>
39 #include "MachOFile.h"
40 #include "SupportedArchs.h"
42 #if BUILDING_DYLD || BUILDING_LIBDYLD
43 // define away restrict until rdar://60166935 is fixed
45 #include <subsystem.h>
50 //////////////////////////// posix wrappers ////////////////////////////////////////
52 // <rdar://problem/10111032> wrap calls to stat() with check for EAGAIN
53 int stat(const char* path
, struct stat
* buf
)
57 #if BUILDING_DYLD || BUILDING_LIBDYLD
58 result
= ::stat_with_subsystem(path
, buf
);
60 result
= ::stat(path
, buf
);
62 } while ((result
== -1) && ((errno
== EAGAIN
) || (errno
== EINTR
)));
67 // <rdar://problem/13805025> dyld should retry open() if it gets an EGAIN
68 int open(const char* path
, int flag
, int other
)
72 #if BUILDING_DYLD || BUILDING_LIBDYLD
74 result
= ::open(path
, flag
, other
);
76 result
= ::open_with_subsystem(path
, flag
);
78 result
= ::open(path
, flag
, other
);
80 } while ((result
== -1) && ((errno
== EAGAIN
) || (errno
== EINTR
)));
86 //////////////////////////// FatFile ////////////////////////////////////////
88 const FatFile
* FatFile::isFatFile(const void* fileStart
)
90 const FatFile
* fileStartAsFat
= (FatFile
*)fileStart
;
91 if ( (fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
)) || (fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC_64
)) )
92 return fileStartAsFat
;
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
);
103 const dyld3::MachOFile
* mf
= (const dyld3::MachOFile
*)((uint8_t*)this+sliceOffset
);
104 if (!mf
->isMachO(diag
, sliceLen
))
106 if ( (mf
->cputype
!= (cpu_type_t
)sliceCpuType
) || (mf
->cpusubtype
!= (cpu_subtype_t
)sliceCpuSubType
) ) {
107 diag
.error("cpu type/subtype mismatch");
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");
116 diag
.error("slice is not page aligned");
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
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
);
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
);
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
);
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
));
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
);
175 diag
.error("not a fat file");
179 bool FatFile::isFatFileWithSlice(Diagnostics
& diag
, uint64_t fileLen
, const GradedArchs
& archs
, bool isOSBinary
,
180 uint64_t& sliceOffset
, uint64_t& sliceLen
, bool& missingSlice
) const
182 missingSlice
= false;
183 if ( (this->magic
!= OSSwapBigToHostInt32(FAT_MAGIC
)) && (this->magic
!= OSSwapBigToHostInt32(FAT_MAGIC_64
)) )
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
;
196 if ( diag
.hasError() )
199 if ( bestGrade
== 0 )
202 return (bestGrade
!= 0);
206 //////////////////////////// GradedArchs ////////////////////////////////////////
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
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}} };
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}} };
237 int GradedArchs::grade(uint32_t cputype
, uint32_t cpusubtype
, bool isOSBinary
) const
239 for (const CpuGrade
* p
= _orderedCpuTypes
; p
->type
!= 0; ++p
) {
240 if ( (p
->type
== cputype
) && (p
->subtype
== (cpusubtype
& ~CPU_SUBTYPE_MASK
)) ) {
253 const char* GradedArchs::name() const
255 return MachOFile::archName(_orderedCpuTypes
[0].type
, _orderedCpuTypes
[0].subtype
);
259 static bool isHaswell()
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;
277 const GradedArchs
& GradedArchs::forCurrentOS(bool keysOff
, bool osBinariesOnly
)
280 if ( osBinariesOnly
)
281 return (keysOff
? arm64e_keysoff_pb
: arm64e_pb
);
283 return (keysOff
? arm64e_keysoff
: arm64e
);
284 #elif __ARM64_ARCH_8_32__
288 #elif __ARM_ARCH_7K__
290 #elif __ARM_ARCH_7S__
292 #elif __ARM_ARCH_7A__
295 return isHaswell() ? x86_64h
: x86_64
;
299 #error unknown platform
303 const GradedArchs
& GradedArchs::forName(const char* archName
, bool keysOff
)
305 if (strcmp(archName
, "x86_64h") == 0 )
307 else if (strcmp(archName
, "x86_64") == 0 )
309 #if SUPPORT_ARCH_arm64e
310 else if (strcmp(archName
, "arm64e") == 0 )
311 return keysOff
? arm64e_keysoff
: arm64e
;
313 else if (strcmp(archName
, "arm64") == 0 )
315 else if (strcmp(archName
, "armv7k") == 0 )
317 else if (strcmp(archName
, "armv7s") == 0 )
319 else if (strcmp(archName
, "armv7") == 0 )
321 #if SUPPORT_ARCH_arm64_32
322 else if (strcmp(archName
, "arm64_32") == 0 )
325 else if (strcmp(archName
, "i386") == 0 )
327 assert(0 && "unknown arch name");
332 //////////////////////////// MachOFile ////////////////////////////////////////
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
},
343 #if SUPPORT_ARCH_arm64_32
344 { "arm64_32", CPU_TYPE_ARM64_32
, CPU_SUBTYPE_ARM64_32_V8
},
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
}
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
},
365 bool MachOFile::is64() const
367 return (this->magic
== MH_MAGIC_64
);
370 size_t MachOFile::machHeaderSize() const
372 return is64() ? sizeof(mach_header_64
) : sizeof(mach_header
);
375 uint32_t MachOFile::maskedCpuSubtype() const
377 return (this->cpusubtype
& ~CPU_SUBTYPE_MASK
);
380 uint32_t MachOFile::pointerSize() const
382 if (this->magic
== MH_MAGIC_64
)
388 bool MachOFile::uses16KPages() const
390 switch (this->cputype
) {
392 case CPU_TYPE_ARM64_32
:
395 // iOS is 16k aligned for armv7/armv7s and watchOS armv7k is 16k aligned
396 return this->cpusubtype
== CPU_SUBTYPE_ARM_V7K
;
402 bool MachOFile::isArch(const char* aName
) const
404 return (strcmp(aName
, archName(this->cputype
, this->cpusubtype
)) == 0);
407 const char* MachOFile::archName(uint32_t cputype
, uint32_t cpusubtype
)
409 for (const ArchInfo
& info
: _s_archInfos
) {
410 if ( (cputype
== info
.cputype
) && ((cpusubtype
& ~CPU_SUBTYPE_MASK
) == info
.cpusubtype
) ) {
417 uint32_t MachOFile::cpuTypeFromArchName(const char* archName
)
419 for (const ArchInfo
& info
: _s_archInfos
) {
420 if ( strcmp(archName
, info
.name
) == 0 ) {
427 uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName
)
429 for (const ArchInfo
& info
: _s_archInfos
) {
430 if ( strcmp(archName
, info
.name
) == 0 ) {
431 return info
.cpusubtype
;
437 const char* MachOFile::archName() const
439 return archName(this->cputype
, this->cpusubtype
);
442 static void appendDigit(char*& s
, unsigned& num
, unsigned place
, bool& startedPrinting
)
444 if ( num
>= place
) {
445 unsigned dig
= (num
/place
);
448 startedPrinting
= true;
450 else if ( startedPrinting
) {
455 static void appendNumber(char*& s
, unsigned num
)
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
)
468 void MachOFile::packedVersionToString(uint32_t packedVersion
, char versionString
[32])
470 // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
471 char* s
= versionString
;
472 appendNumber(s
, (packedVersion
>> 16));
474 appendNumber(s
, (packedVersion
>> 8) & 0xFF);
476 appendNumber(s
, (packedVersion
& 0xFF));
480 bool MachOFile::builtForPlatform(Platform reqPlatform
, bool onlyOnePlatform
) const
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;
488 foundOtherPlatform
= true;
490 // if checking that this binary is built for exactly one platform, fail if more
491 if ( foundOtherPlatform
&& onlyOnePlatform
)
493 if ( foundRequestedPlatform
)
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
)
501 if ( this->cputype
== CPU_TYPE_I386
)
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
) )
514 bool MachOFile::loadableIntoProcess(Platform processPlatform
, const char* path
) const
516 if ( this->builtForPlatform(processPlatform
) )
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",
529 for (const char* libPath
: macOSHost
) {
530 if (strcmp(libPath
, path
) == 0)
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) )
538 #if (TARGET_OS_OSX && TARGET_CPU_ARM64)
539 if ( (this->filetype
== MH_EXECUTE
) && (processPlatform
== Platform::macOS
) && this->builtForPlatform(Platform::iOS
, true) )
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
) )
553 // macOS dylibs can be loaded into iOSMac processes
554 if ( (iOSonMac
) && this->builtForPlatform(Platform::macOS
, true) )
560 bool MachOFile::isZippered() const
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
)
567 else if ( platform
== Platform::iOSMac
)
570 return macOS
&& iOSMac
;
573 bool MachOFile::inDyldCache() const {
574 return (this->flags
& 0x80000000);
577 Platform
MachOFile::currentPlatform()
580 #if TARGET_OS_SIMULATOR
582 return Platform::watchOS_simulator
;
584 return Platform::tvOS_simulator
;
586 return Platform::iOS_simulator
;
588 #elif TARGET_OS_BRIDGE
589 return Platform::bridgeOS
;
590 #elif TARGET_OS_WATCH
591 return Platform::watchOS
;
593 return Platform::tvOS
;
595 return Platform::iOS
;
597 return Platform::macOS
;
598 #elif TARGET_OS_DRIVERKIT
599 return Platform::driverKit
;
601 #error unknown platform
606 const char* MachOFile::currentArchName()
610 #elif __ARM_ARCH_7A__
612 #elif __ARM_ARCH_7S__
623 return isHaswell() ? "x86_64h" : "x86_64";
631 bool MachOFile::isSimulatorPlatform(Platform platform
)
633 return ( (platform
== Platform::iOS_simulator
) ||
634 (platform
== Platform::watchOS_simulator
) ||
635 (platform
== Platform::tvOS_simulator
) );
638 bool MachOFile::isDylib() const
640 return (this->filetype
== MH_DYLIB
);
643 bool MachOFile::isBundle() const
645 return (this->filetype
== MH_BUNDLE
);
648 bool MachOFile::isMainExecutable() const
650 return (this->filetype
== MH_EXECUTE
);
653 bool MachOFile::isDynamicExecutable() const
655 if ( this->filetype
!= MH_EXECUTE
)
658 // static executables do not have dyld load command
659 return hasLoadCommand(LC_LOAD_DYLINKER
);
662 bool MachOFile::isStaticExecutable() const
664 if ( this->filetype
!= MH_EXECUTE
)
667 // static executables do not have dyld load command
668 return !hasLoadCommand(LC_LOAD_DYLINKER
);
671 bool MachOFile::isKextBundle() const
673 return (this->filetype
== MH_KEXT_BUNDLE
);
676 bool MachOFile::isFileSet() const
678 return (this->filetype
== MH_FILESET
);
681 bool MachOFile::isPIE() const
683 return (this->flags
& MH_PIE
);
686 bool MachOFile::isPreload() const
688 return (this->filetype
== MH_PRELOAD
);
691 const char* MachOFile::platformName(Platform reqPlatform
)
693 for (const PlatformInfo
& info
: _s_platformInfos
) {
694 if ( info
.platform
== reqPlatform
)
697 return "unknown platform";
700 void MachOFile::forEachSupportedPlatform(void (^handler
)(Platform platform
, uint32_t minOS
, uint32_t sdk
)) const
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
);
710 case LC_VERSION_MIN_MACOSX
:
711 handler(Platform::macOS
, versCmd
->version
, versCmd
->sdk
);
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
717 handler(Platform::iOS
, versCmd
->version
, versCmd
->sdk
);
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
723 handler(Platform::tvOS
, versCmd
->version
, versCmd
->sdk
);
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
729 handler(Platform::watchOS
, versCmd
->version
, versCmd
->sdk
);
733 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
737 bool MachOFile::isMachO(Diagnostics
& diag
, uint64_t fileSize
) const
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]");
745 if ( this->sizeofcmds
+ machHeaderSize() > fileSize
) {
746 diag
.error("load commands exceed length of first segment");
749 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { });
750 return diag
.noError();
753 bool MachOFile::hasMachOMagic() const
755 return ( (this->magic
== MH_MAGIC
) || (this->magic
== MH_MAGIC_64
) );
758 bool MachOFile::hasMachOBigEndianMagic() const
760 return ( (this->magic
== MH_CIGAM
) || (this->magic
== MH_CIGAM_64
) );
764 void MachOFile::forEachLoadCommand(Diagnostics
& diag
, void (^callback
)(const load_command
* cmd
, bool& stop
)) const
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
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
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
);
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
);
798 void MachOFile::removeLoadCommand(Diagnostics
& diag
, void (^callback
)(const load_command
* cmd
, bool& remove
, bool& stop
))
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
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
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
) {
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
);
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
);
828 callback(cmd
, remove
, stop
);
830 this->sizeofcmds
-= cmd
->cmdsize
;
831 ::memmove((void*)cmd
, (void*)nextCmd
, bytesRemaining
);
834 bytesRemaining
-= cmd
->cmdsize
;
841 ::bzero(cmd
, bytesRemaining
);
844 const char* MachOFile::installName() const
847 uint32_t compatVersion
;
848 uint32_t currentVersion
;
849 if ( getDylibInstallName(&name
, &compatVersion
, ¤tVersion
) )
854 bool MachOFile::getDylibInstallName(const char** installName
, uint32_t* compatVersion
, uint32_t* currentVersion
) const
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
;
868 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
872 bool MachOFile::getUuid(uuid_t uuid
) const
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
));
884 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
886 bzero(uuid
, sizeof(uuid_t
));
890 void MachOFile::forEachDependentDylib(void (^callback
)(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
)) const
893 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
894 switch ( cmd
->cmd
) {
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
);
907 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
910 void MachOFile::forDyldEnv(void (^callback
)(const char* envVar
, bool& stop
)) const
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
);
928 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
931 bool MachOFile::enforceCompatVersion() const
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
942 case Platform::iOS_simulator
:
943 case Platform::tvOS_simulator
:
944 if ( minOS
>= 0x000C0000 ) // iOS 12.0
947 case Platform::watchOS
:
948 case Platform::watchOS_simulator
:
949 if ( minOS
>= 0x00050000 ) // watchOS 5.0
952 case Platform::bridgeOS
:
953 if ( minOS
>= 0x00030000 ) // bridgeOS 3.0
956 case Platform::driverKit
:
957 case Platform::iOSMac
:
960 case Platform::unknown
:
967 const thread_command
* MachOFile::unixThreadLoadCommand() const {
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
;
980 uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const
982 switch ( this->cputype
) {
984 return 10; // i386_thread_state_t.eip
985 case CPU_TYPE_X86_64
:
986 return 16; // x86_thread_state64_t.rip
988 return 15; // arm_thread_state_t.pc
990 return 32; // arm_thread_state64_t.__pc
996 uint64_t MachOFile::entryAddrFromThreadCmd(const thread_command
* cmd
) const
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);
1002 uint32_t index
= entryAddrRegisterIndexForThreadCmd();
1006 return is64() ? regs64
[index
] : regs32
[index
];
1010 void MachOFile::forEachSegment(void (^callback
)(const SegmentInfo
& info
, bool& stop
)) const
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
= §ionsStart
[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
;
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
);
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
= §ionsStart
[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;
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
);
1076 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
1079 void MachOFile::forEachSection(void (^callback
)(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
)) const
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
= §ionsStart
[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
;
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
;
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
);
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
= §ionsStart
[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;
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
;
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
);
1177 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
1180 bool MachOFile::hasWeakDefs() const
1182 return (this->flags
& MH_WEAK_DEFINES
);
1185 bool MachOFile::hasThreadLocalVariables() const
1187 return (this->flags
& MH_HAS_TLV_DESCRIPTORS
);
1190 static bool endsWith(const char* str
, const char* suffix
)
1192 size_t strLen
= strlen(str
);
1193 size_t suffixLen
= strlen(suffix
);
1194 if ( strLen
< suffixLen
)
1196 return (strcmp(&str
[strLen
-suffixLen
], suffix
) == 0);
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) );
1208 static bool startsWith(const char* buffer
, const char* valueToFind
) {
1209 return strncmp(buffer
, valueToFind
, strlen(valueToFind
)) == 0;
1212 static bool platformExcludesSharedCache_macOS(const char* installName
) {
1213 // Note: This function basically matches dontCache() from update dyld shared cache
1215 if ( startsWith(installName
, "/usr/lib/system/introspection/") )
1217 if ( startsWith(installName
, "/System/Library/QuickTime/") )
1219 if ( startsWith(installName
, "/System/Library/Tcl/") )
1221 if ( startsWith(installName
, "/System/Library/Perl/") )
1223 if ( startsWith(installName
, "/System/Library/MonitorPanels/") )
1225 if ( startsWith(installName
, "/System/Library/Accessibility/") )
1227 if ( startsWith(installName
, "/usr/local/") )
1229 if ( startsWith(installName
, "/usr/lib/pam/") )
1231 // We no longer support ROSP, so skip all paths which start with the special prefix
1232 if ( startsWith(installName
, "/System/Library/Templates/Data/") )
1235 // anything inside a .app bundle is specific to app, so should not be in shared cache
1236 if ( strstr(installName
, ".app/") != NULL
)
1242 static bool platformExcludesSharedCache_iOS(const char* installName
) {
1243 if ( strcmp(installName
, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
1245 if ( strcmp(installName
, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
1250 static bool platformExcludesSharedCache_tvOS(const char* installName
) {
1251 return platformExcludesSharedCache_iOS(installName
);
1254 static bool platformExcludesSharedCache_watchOS(const char* installName
) {
1255 return platformExcludesSharedCache_iOS(installName
);
1258 static bool platformExcludesSharedCache_bridgeOS(const char* installName
) {
1259 return platformExcludesSharedCache_iOS(installName
);
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
) {
1266 case dyld3::Platform::unknown
:
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
:
1282 case dyld3::Platform::tvOS_simulator
:
1284 case dyld3::Platform::watchOS_simulator
:
1286 case dyld3::Platform::driverKit
:
1293 bool MachOFile::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const
1296 if ( !isSharedCacheEligiblePath(path
) ) {
1297 // Dont spam the user with an error about paths when we know these are never eligible.
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
1307 // only dylibs built for /usr/lib or /System/Library can go in cache
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.
1315 else if ( strcmp(dylibName
, path
) != 0 ) {
1316 failureReason("install path does not match install name");
1319 else if ( strstr(dylibName
, "//") != 0 ) {
1320 failureReason("install name should not include //");
1323 else if ( strstr(dylibName
, "./") != 0 ) {
1324 failureReason("install name should not include ./");
1328 __block
bool platformExcludedFile
= false;
1329 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
1330 if ( platformExcludedFile
)
1332 if ( platformExcludesSharedCache(platform
, dylibName
) ) {
1333 platformExcludedFile
= true;
1337 if ( platformExcludedFile
) {
1338 failureReason("install name is not shared cache eligible on platform");
1344 // flat namespace files cannot go in cache
1345 if ( (this->flags
& MH_TWOLEVEL
) == 0 ) {
1347 failureReason("Not built with two level namespaces");
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") ) {
1353 failureReason("Variant image");
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;
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
)
1367 if ( cmd
->cmd
== LC_DYLD_EXPORTS_TRIE
)
1368 hasExportTrie
= true;
1369 if ( cmd
->cmd
== LC_LAZY_LOAD_DYLIB
)
1372 if ( !hasExtraInfo
) {
1374 failureReason("Missing split seg info");
1376 if ( !hasDyldInfo
&& !hasExportTrie
) {
1378 failureReason("Old binary, missing dyld info or export trie");
1380 if ( hasLazyLoad
) {
1382 failureReason("Has lazy load");
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;
1393 if ( !allDepPathsAreGood
) {
1395 failureReason("Depends on dylibs ineligable for dyld cache");
1398 // dylibs with interposing info cannot be in cache
1399 if ( hasInterposingTuples() ) {
1401 failureReason("Has interposing tuples");
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 ) {
1408 failureReason("i386 swift binary");
1415 #if BUILDING_APP_CACHE_UTIL
1416 bool MachOFile::canBePlacedInKernelCollection(const char* path
, void (^failureReason
)(const char*)) const
1418 // only dylibs and the kernel itself can go in cache
1419 if ( this->filetype
== MH_EXECUTE
) {
1421 } else if ( this->isKextBundle() ) {
1424 failureReason("Not MH_KEXT_BUNDLE");
1428 if ( this->filetype
== MH_EXECUTE
) {
1431 // two-level namespace binaries cannot go in cache
1432 if ( (this->flags
& MH_TWOLEVEL
) != 0 ) {
1433 failureReason("Built with two level namespaces");
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;
1445 if (foundPageZero
) {
1446 failureReason("Has __PAGEZERO");
1450 // xnu must have an LC_UNIXTHREAD to point to the entry point
1451 __block
bool foundMainLC
= false;
1452 __block
bool foundUnixThreadLC
= false;
1454 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1455 if ( cmd
->cmd
== LC_MAIN
) {
1459 else if ( cmd
->cmd
== LC_UNIXTHREAD
) {
1460 foundUnixThreadLC
= true;
1464 failureReason("Found LC_MAIN");
1467 if (!foundUnixThreadLC
) {
1468 failureReason("Expected LC_UNIXTHREAD");
1472 if (diag
.hasError()) {
1473 failureReason("Error parsing load commands");
1477 // The kernel should be a static executable, not a dynamic one
1478 if ( !isStaticExecutable() ) {
1479 failureReason("Expected static executable");
1483 // The kernel must be built with -pie
1485 failureReason("Expected pie");
1490 if ( isArch("arm64e") && isKextBundle() && !hasChainedFixups() ) {
1491 failureReason("Missing fixup information");
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;
1501 if ( hasInterposing
) {
1502 failureReason("Has interposing tuples");
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/") )
1515 // anything inside a .app bundle is specific to app, so should not get a prebuilt closure
1516 if ( strstr(path
, ".app/") != NULL
)
1522 static bool platformExcludesPrebuiltClosure_iOS(const char* path
) {
1523 if ( strcmp(path
, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
1525 if ( strcmp(path
, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
1530 static bool platformExcludesPrebuiltClosure_tvOS(const char* path
) {
1531 return platformExcludesPrebuiltClosure_iOS(path
);
1534 static bool platformExcludesPrebuiltClosure_watchOS(const char* path
) {
1535 return platformExcludesPrebuiltClosure_iOS(path
);
1538 static bool platformExcludesPrebuiltClosure_bridgeOS(const char* path
) {
1539 return platformExcludesPrebuiltClosure_iOS(path
);
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
) {
1546 case dyld3::Platform::unknown
:
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
:
1562 case dyld3::Platform::tvOS_simulator
:
1564 case dyld3::Platform::watchOS_simulator
:
1566 case dyld3::Platform::driverKit
:
1571 bool MachOFile::canHavePrecomputedDlopenClosure(const char* path
, void (^failureReason
)(const char*)) const
1573 __block
bool retval
= true;
1575 // only dylibs can go in cache
1576 if ( (this->filetype
!= MH_DYLIB
) && (this->filetype
!= MH_BUNDLE
) ) {
1578 failureReason("not MH_DYLIB or MH_BUNDLE");
1581 // flat namespace files cannot go in cache
1582 if ( (this->flags
& MH_TWOLEVEL
) == 0 ) {
1584 failureReason("not built with two level namespaces");
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;
1595 if ( !allDepPathsAreGood
) {
1597 failureReason("depends on dylibs that are not absolute paths");
1600 __block
bool platformExcludedFile
= false;
1601 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
1602 if ( platformExcludedFile
)
1604 if ( platformExcludesPrebuiltClosure(platform
, path
) ) {
1605 platformExcludedFile
= true;
1609 if ( platformExcludedFile
) {
1610 failureReason("file cannot get a prebuilt closure on this platform");
1614 // dylibs with interposing info cannot have dlopen closure pre-computed
1615 if ( hasInterposingTuples() ) {
1617 failureReason("has interposing tuples");
1620 // special system dylib overrides cannot have closure pre-computed
1621 if ( strncmp(path
, "/usr/lib/system/introspection/", 30) == 0 ) {
1623 failureReason("override of OS dylib");
1629 bool MachOFile::hasInterposingTuples() const
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;
1636 return hasInterposing
;
1639 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset
, uint32_t& size
) const
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
;
1654 bool MachOFile::canBeFairPlayEncrypted() const
1656 return (findFairPlayEncryptionLoadCommand() != nullptr);
1659 const encryption_info_command
* MachOFile::findFairPlayEncryptionLoadCommand() const
1661 __block
const encryption_info_command
* result
= nullptr;
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
;
1669 if ( diag
.noError() )
1676 bool MachOFile::hasLoadCommand(uint32_t cmdNum
) const
1678 __block
bool hasLC
= false;
1680 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1681 if ( cmd
->cmd
== cmdNum
) {
1689 bool MachOFile::allowsAlternatePlatform() const
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) ) {
1701 bool MachOFile::hasChainedFixups() const
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
);
1710 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS
);
1713 bool MachOFile::hasChainedFixupsLoadCommand() const
1715 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS
);
1718 uint64_t MachOFile::read_uleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
1720 uint64_t result
= 0;
1724 diag
.error("malformed uleb128");
1727 uint64_t slice
= *p
& 0x7f;
1730 diag
.error("uleb128 too big for uint64");
1734 result
|= (slice
<< bit
);
1738 while (*p
++ & 0x80);
1743 int64_t MachOFile::read_sleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
1750 diag
.error("malformed sleb128");
1754 result
|= (((int64_t)(byte
& 0x7f)) << bit
);
1756 } while (byte
& 0x80);
1757 // sign extend negative numbers
1758 if ( ((byte
& 0x40) != 0) && (bit
< 64) )
1759 result
|= (~0ULL) << bit
;
1764 } // namespace dyld3