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@
28 #include <TargetConditionals.h>
29 #include <mach/host_info.h>
30 #include <mach/mach.h>
31 #include <mach/mach_host.h>
34 #include "MachOFile.h"
35 #include "SupportedArchs.h"
40 //////////////////////////// FatFile ////////////////////////////////////////
42 const FatFile
* FatFile::isFatFile(const void* fileStart
)
44 const FatFile
* fileStartAsFat
= (FatFile
*)fileStart
;
45 if ( (fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
)) || (fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC_64
)) )
46 return fileStartAsFat
;
51 bool FatFile::isValidSlice(Diagnostics
& diag
, uint64_t fileLen
, uint32_t sliceIndex
,
52 uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, uint64_t sliceOffset
, uint64_t sliceLen
) const {
53 if ( greaterThanAddOrOverflow(sliceOffset
, sliceLen
, fileLen
) ) {
54 diag
.error("slice %d extends beyond end of file", sliceIndex
);
57 const dyld3::MachOFile
* mf
= (const dyld3::MachOFile
*)((uint8_t*)this+sliceOffset
);
58 if (!mf
->isMachO(diag
, sliceLen
))
60 if ( (mf
->cputype
!= (cpu_type_t
)sliceCpuType
) || (mf
->cpusubtype
!= (cpu_subtype_t
)sliceCpuSubType
) ) {
61 diag
.error("cpu type/subtype mismatch");
64 uint32_t pageSizeMask
= mf
->uses16KPages() ? 0x3FFF : 0xFFF;
65 if ( (sliceOffset
& pageSizeMask
) != 0 ) {
66 // slice not page aligned
67 if ( strncmp((char*)this+sliceOffset
, "!<arch>", 7) == 0 )
68 diag
.error("file is static library");
70 diag
.error("slice is not page aligned");
76 void FatFile::forEachSlice(Diagnostics
& diag
, uint64_t fileLen
, void (^callback
)(uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, const void* sliceStart
, uint64_t sliceSize
, bool& stop
)) const
78 if ( this->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
79 const uint64_t maxArchs
= ((4096 - sizeof(fat_header
)) / sizeof(fat_arch
));
80 const uint32_t numArchs
= OSSwapBigToHostInt32(nfat_arch
);
81 if ( numArchs
> maxArchs
) {
82 diag
.error("fat header too large: %u entries", numArchs
);
86 const fat_arch
* const archs
= (fat_arch
*)(((char*)this)+sizeof(fat_header
));
87 for (uint32_t i
=0; i
< numArchs
; ++i
) {
88 uint32_t cpuType
= OSSwapBigToHostInt32(archs
[i
].cputype
);
89 uint32_t cpuSubType
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
90 uint32_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
91 uint32_t len
= OSSwapBigToHostInt32(archs
[i
].size
);
92 if (isValidSlice(diag
, fileLen
, i
, cpuType
, cpuSubType
, offset
, len
))
93 callback(cpuType
, cpuSubType
, (uint8_t*)this+offset
, len
, stop
);
98 // Look for one more slice
99 if ( numArchs
!= maxArchs
) {
100 uint32_t cpuType
= OSSwapBigToHostInt32(archs
[numArchs
].cputype
);
101 uint32_t cpuSubType
= OSSwapBigToHostInt32(archs
[numArchs
].cpusubtype
);
102 uint32_t offset
= OSSwapBigToHostInt32(archs
[numArchs
].offset
);
103 uint32_t len
= OSSwapBigToHostInt32(archs
[numArchs
].size
);
104 if ((cpuType
== CPU_TYPE_ARM64
) && ((cpuSubType
== CPU_SUBTYPE_ARM64_ALL
|| cpuSubType
== CPU_SUBTYPE_ARM64_V8
))) {
105 if (isValidSlice(diag
, fileLen
, numArchs
, cpuType
, cpuSubType
, offset
, len
))
106 callback(cpuType
, cpuSubType
, (uint8_t*)this+offset
, len
, stop
);
110 else if ( this->magic
== OSSwapBigToHostInt32(FAT_MAGIC_64
) ) {
111 if ( OSSwapBigToHostInt32(nfat_arch
) > ((4096 - sizeof(fat_header
)) / sizeof(fat_arch
)) ) {
112 diag
.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch
));
116 const fat_arch_64
* const archs
= (fat_arch_64
*)(((char*)this)+sizeof(fat_header
));
117 for (uint32_t i
=0; i
< OSSwapBigToHostInt32(nfat_arch
); ++i
) {
118 uint32_t cpuType
= OSSwapBigToHostInt32(archs
[i
].cputype
);
119 uint32_t cpuSubType
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
120 uint64_t offset
= OSSwapBigToHostInt64(archs
[i
].offset
);
121 uint64_t len
= OSSwapBigToHostInt64(archs
[i
].size
);
122 if (isValidSlice(diag
, fileLen
, i
, cpuType
, cpuSubType
, offset
, len
))
123 callback(cpuType
, cpuSubType
, (uint8_t*)this+offset
, len
, stop
);
129 diag
.error("not a fat file");
133 bool FatFile::isFatFileWithSlice(Diagnostics
& diag
, uint64_t fileLen
, const GradedArchs
& archs
, uint64_t& sliceOffset
, uint64_t& sliceLen
, bool& missingSlice
) const
135 missingSlice
= false;
136 if ( (this->magic
!= OSSwapBigToHostInt32(FAT_MAGIC
)) && (this->magic
!= OSSwapBigToHostInt32(FAT_MAGIC_64
)) )
139 __block
int bestGrade
= 0;
140 forEachSlice(diag
, fileLen
, ^(uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, const void* sliceStart
, uint64_t sliceSize
, bool& stop
) {
141 if (int sliceGrade
= archs
.grade(sliceCpuType
, sliceCpuSubType
)) {
142 if ( sliceGrade
> bestGrade
) {
143 sliceOffset
= (char*)sliceStart
- (char*)this;
144 sliceLen
= sliceSize
;
145 bestGrade
= sliceGrade
;
149 if ( diag
.hasError() )
152 if ( bestGrade
== 0 )
155 return (bestGrade
!= 0);
159 //////////////////////////// GradedArchs ////////////////////////////////////////
161 const GradedArchs
GradedArchs::i386
= { {{CPU_TYPE_I386
, CPU_SUBTYPE_I386_ALL
, 1}} };
162 const GradedArchs
GradedArchs::x86_64
= { {{CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_ALL
, 1}} };
163 const GradedArchs
GradedArchs::x86_64h
= { {{CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_H
, 2}, {CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_ALL
, 1}}, };
164 const GradedArchs
GradedArchs::arm64
= { {{CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64_ALL
, 1}} };
165 #if SUPPORT_ARCH_arm64e
166 const GradedArchs
GradedArchs::arm64e_compat
= { {{CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64E
, 2}, {CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64_ALL
, 1}} };
167 const GradedArchs
GradedArchs::arm64e
= { {{CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64E
, 1}} };
169 const GradedArchs
GradedArchs::armv7k
= { {{CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7K
, 1}} };
170 const GradedArchs
GradedArchs::armv7
= { {{CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7
, 1}} };
171 const GradedArchs
GradedArchs::armv7s
= { {{CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7S
, 2}, {CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7
, 1}} };
172 #if SUPPORT_ARCH_arm64_32
173 const GradedArchs
GradedArchs::arm64_32
= { {{CPU_TYPE_ARM64_32
, CPU_SUBTYPE_ARM64_32_V8
, 1}} };
176 int GradedArchs::grade(uint32_t cputype
, uint32_t cpusubtype
) const
178 for (const CpuGrade
* p
= _orderedCpuTypes
; p
->type
!= 0; ++p
) {
179 if ( (p
->type
== cputype
) && (p
->subtype
== (cpusubtype
& ~CPU_SUBTYPE_MASK
)) )
185 const char* GradedArchs::name() const
187 return MachOFile::archName(_orderedCpuTypes
[0].type
, _orderedCpuTypes
[0].subtype
);
191 static bool isHaswell()
193 // FIXME: figure out a commpage way to check this
194 static bool sAlreadyDetermined
= false;
195 static bool sHaswell
= false;
196 if ( !sAlreadyDetermined
) {
197 struct host_basic_info info
;
198 mach_msg_type_number_t count
= HOST_BASIC_INFO_COUNT
;
199 mach_port_t hostPort
= mach_host_self();
200 kern_return_t result
= host_info(hostPort
, HOST_BASIC_INFO
, (host_info_t
)&info
, &count
);
201 mach_port_deallocate(mach_task_self(), hostPort
);
202 sHaswell
= (result
== KERN_SUCCESS
) && (info
.cpu_subtype
== CPU_SUBTYPE_X86_64_H
);
203 sAlreadyDetermined
= true;
209 const GradedArchs
& GradedArchs::forCurrentOS(const MachOFile
* mainExecutable
)
212 if ( mainExecutable
->cpusubtype
< CPU_SUBTYPE_ARM64E
)
213 return arm64e_compat
;
216 #elif __ARM64_ARCH_8_32__
220 #elif __ARM_ARCH_7K__
222 #elif __ARM_ARCH_7S__
224 #elif __ARM_ARCH_7A__
227 return isHaswell() ? x86_64h
: x86_64
;
231 #error unknown platform
235 const GradedArchs
& GradedArchs::forName(const char* archName
, bool forMainExecutable
)
237 if (strcmp(archName
, "x86_64h") == 0 )
239 else if (strcmp(archName
, "x86_64") == 0 )
241 #if SUPPORT_ARCH_arm64e
242 else if (strcmp(archName
, "arm64e") == 0 )
243 return forMainExecutable
? arm64e_compat
: arm64e
;
245 else if (strcmp(archName
, "arm64") == 0 )
247 else if (strcmp(archName
, "armv7k") == 0 )
249 else if (strcmp(archName
, "armv7s") == 0 )
251 else if (strcmp(archName
, "armv7") == 0 )
253 #if SUPPORT_ARCH_arm64_32
254 else if (strcmp(archName
, "arm64_32") == 0 )
257 else if (strcmp(archName
, "i386") == 0 )
259 assert(0 && "unknown arch name");
263 //////////////////////////// MachOFile ////////////////////////////////////////
266 const MachOFile::ArchInfo
MachOFile::_s_archInfos
[] = {
267 { "x86_64", CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_ALL
},
268 { "x86_64h", CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_H
},
269 { "i386", CPU_TYPE_I386
, CPU_SUBTYPE_I386_ALL
},
270 { "arm64", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64_ALL
},
271 #if SUPPORT_ARCH_arm64e
272 { "arm64e", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64E
},
274 #if SUPPORT_ARCH_arm64_32
275 { "arm64_32", CPU_TYPE_ARM64_32
, CPU_SUBTYPE_ARM64_32_V8
},
277 { "armv7k", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7K
},
278 { "armv7s", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7S
},
279 { "armv7", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7
}
282 const MachOFile::PlatformInfo
MachOFile::_s_platformInfos
[] = {
283 { "macOS", Platform::macOS
, LC_VERSION_MIN_MACOSX
},
284 { "iOS", Platform::iOS
, LC_VERSION_MIN_IPHONEOS
},
285 { "tvOS", Platform::tvOS
, LC_VERSION_MIN_TVOS
},
286 { "watchOS", Platform::watchOS
, LC_VERSION_MIN_WATCHOS
},
287 { "bridgeOS", Platform::bridgeOS
, LC_BUILD_VERSION
},
288 { "UIKitForMac", Platform::iOSMac
, LC_BUILD_VERSION
},
289 { "iOS-sim", Platform::iOS_simulator
, LC_BUILD_VERSION
},
290 { "tvOS-sim", Platform::tvOS_simulator
, LC_BUILD_VERSION
},
291 { "watchOS-sim", Platform::watchOS_simulator
, LC_BUILD_VERSION
},
296 bool MachOFile::is64() const
298 return (this->magic
== MH_MAGIC_64
);
301 size_t MachOFile::machHeaderSize() const
303 return is64() ? sizeof(mach_header_64
) : sizeof(mach_header
);
307 uint32_t MachOFile::pointerSize() const
309 if (this->magic
== MH_MAGIC_64
)
315 bool MachOFile::uses16KPages() const
317 switch (this->cputype
) {
319 case CPU_TYPE_ARM64_32
:
322 // iOS is 16k aligned for armv7/armv7s and watchOS armv7k is 16k aligned
323 return this->cpusubtype
== CPU_SUBTYPE_ARM_V7K
;
329 bool MachOFile::isArch(const char* aName
) const
331 return (strcmp(aName
, archName(this->cputype
, this->cpusubtype
)) == 0);
334 const char* MachOFile::archName(uint32_t cputype
, uint32_t cpusubtype
)
336 for (const ArchInfo
& info
: _s_archInfos
) {
337 if ( (cputype
== info
.cputype
) && ((cpusubtype
& ~CPU_SUBTYPE_MASK
) == info
.cpusubtype
) ) {
344 uint32_t MachOFile::cpuTypeFromArchName(const char* archName
)
346 for (const ArchInfo
& info
: _s_archInfos
) {
347 if ( strcmp(archName
, info
.name
) == 0 ) {
354 uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName
)
356 for (const ArchInfo
& info
: _s_archInfos
) {
357 if ( strcmp(archName
, info
.name
) == 0 ) {
358 return info
.cpusubtype
;
364 const char* MachOFile::archName() const
366 return archName(this->cputype
, this->cpusubtype
);
369 static void appendDigit(char*& s
, unsigned& num
, unsigned place
, bool& startedPrinting
)
371 if ( num
>= place
) {
372 unsigned dig
= (num
/place
);
375 startedPrinting
= true;
377 else if ( startedPrinting
) {
382 static void appendNumber(char*& s
, unsigned num
)
385 bool startedPrinting
= false;
386 appendDigit(s
, num
, 10000, startedPrinting
);
387 appendDigit(s
, num
, 1000, startedPrinting
);
388 appendDigit(s
, num
, 100, startedPrinting
);
389 appendDigit(s
, num
, 10, startedPrinting
);
390 appendDigit(s
, num
, 1, startedPrinting
);
391 if ( !startedPrinting
)
395 void MachOFile::packedVersionToString(uint32_t packedVersion
, char versionString
[32])
397 // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
398 char* s
= versionString
;
399 appendNumber(s
, (packedVersion
>> 16));
401 appendNumber(s
, (packedVersion
>> 8) & 0xFF);
403 appendNumber(s
, (packedVersion
& 0xFF));
407 bool MachOFile::supportsPlatform(Platform reqPlatform
) const
409 __block
bool foundRequestedPlatform
= false;
410 __block
bool foundOtherPlatform
= false;
411 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
412 if ( platform
== reqPlatform
)
413 foundRequestedPlatform
= true;
415 foundOtherPlatform
= true;
417 if ( foundRequestedPlatform
)
420 // we did find some platform info, but not requested, so return false
421 if ( foundOtherPlatform
)
424 // binary has no explict load command to mark platform
425 // could be an old macOS binary, look at arch
426 if ( reqPlatform
== Platform::macOS
) {
427 if ( this->cputype
== CPU_TYPE_X86_64
)
429 if ( this->cputype
== CPU_TYPE_I386
)
436 bool MachOFile::isZippered() const
438 __block
bool macOS
= false;
439 __block
bool iOSMac
= false;
440 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
441 if ( platform
== Platform::macOS
)
443 else if ( platform
== Platform::iOSMac
)
446 return macOS
&& iOSMac
;
449 bool MachOFile::inDyldCache() const {
450 return (this->flags
& 0x80000000);
453 Platform
MachOFile::currentPlatform()
456 #if TARGET_OS_SIMULATOR
458 return Platform::watchOS_simulator
;
460 return Platform::tvOS_simulator
;
462 return Platform::iOS_simulator
;
464 #elif TARGET_OS_BRIDGE
465 return Platform::bridgeOS
;
466 #elif TARGET_OS_WATCH
467 return Platform::watchOS
;
469 return Platform::tvOS
;
471 return Platform::iOS
;
473 return Platform::macOS
;
475 #error unknown platform
480 const char* MachOFile::currentArchName()
484 #elif __ARM_ARCH_7A__
486 #elif __ARM_ARCH_7S__
497 return isHaswell() ? "x86_64h" : "x86_64";
505 bool MachOFile::isSimulatorPlatform(Platform platform
)
507 return ( (platform
== Platform::iOS_simulator
) ||
508 (platform
== Platform::watchOS_simulator
) ||
509 (platform
== Platform::tvOS_simulator
) );
512 bool MachOFile::isDylib() const
514 return (this->filetype
== MH_DYLIB
);
517 bool MachOFile::isBundle() const
519 return (this->filetype
== MH_BUNDLE
);
522 bool MachOFile::isMainExecutable() const
524 return (this->filetype
== MH_EXECUTE
);
527 bool MachOFile::isDynamicExecutable() const
529 if ( this->filetype
!= MH_EXECUTE
)
532 // static executables do not have dyld load command
533 return hasLoadCommand(LC_LOAD_DYLINKER
);
536 bool MachOFile::isStaticExecutable() const
538 if ( this->filetype
!= MH_EXECUTE
)
541 // static executables do not have dyld load command
542 return !hasLoadCommand(LC_LOAD_DYLINKER
);
545 bool MachOFile::isPIE() const
547 return (this->flags
& MH_PIE
);
550 bool MachOFile::isPreload() const
552 return (this->filetype
== MH_PRELOAD
);
555 const char* MachOFile::platformName(Platform reqPlatform
)
557 for (const PlatformInfo
& info
: _s_platformInfos
) {
558 if ( info
.platform
== reqPlatform
)
561 return "unknown platform";
564 void MachOFile::forEachSupportedPlatform(void (^handler
)(Platform platform
, uint32_t minOS
, uint32_t sdk
)) const
567 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
568 const build_version_command
* buildCmd
= (build_version_command
*)cmd
;
569 const version_min_command
* versCmd
= (version_min_command
*)cmd
;
570 switch ( cmd
->cmd
) {
571 case LC_BUILD_VERSION
:
572 handler((Platform
)(buildCmd
->platform
), buildCmd
->minos
, buildCmd
->sdk
);
574 case LC_VERSION_MIN_MACOSX
:
575 handler(Platform::macOS
, versCmd
->version
, versCmd
->sdk
);
577 case LC_VERSION_MIN_IPHONEOS
:
578 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) )
579 handler(Platform::iOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
581 handler(Platform::iOS
, versCmd
->version
, versCmd
->sdk
);
583 case LC_VERSION_MIN_TVOS
:
584 if ( this->cputype
== CPU_TYPE_X86_64
)
585 handler(Platform::tvOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
587 handler(Platform::tvOS
, versCmd
->version
, versCmd
->sdk
);
589 case LC_VERSION_MIN_WATCHOS
:
590 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) )
591 handler(Platform::watchOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
593 handler(Platform::watchOS
, versCmd
->version
, versCmd
->sdk
);
597 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
601 bool MachOFile::isMachO(Diagnostics
& diag
, uint64_t fileSize
) const
603 if ( !hasMachOMagic() ) {
604 // old PPC slices are not currently valid "mach-o" but should not cause an error
605 if ( !hasMachOBigEndianMagic() )
606 diag
.error("file does not start with MH_MAGIC[_64]");
609 if ( this->sizeofcmds
+ machHeaderSize() > fileSize
) {
610 diag
.error("load commands exceed length of first segment");
613 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { });
614 return diag
.noError();
617 bool MachOFile::hasMachOMagic() const
619 return ( (this->magic
== MH_MAGIC
) || (this->magic
== MH_MAGIC_64
) );
622 bool MachOFile::hasMachOBigEndianMagic() const
624 return ( (this->magic
== MH_CIGAM
) || (this->magic
== MH_CIGAM_64
) );
628 void MachOFile::forEachLoadCommand(Diagnostics
& diag
, void (^callback
)(const load_command
* cmd
, bool& stop
)) const
631 const load_command
* startCmds
= nullptr;
632 if ( this->magic
== MH_MAGIC_64
)
633 startCmds
= (load_command
*)((char *)this + sizeof(mach_header_64
));
634 else if ( this->magic
== MH_MAGIC
)
635 startCmds
= (load_command
*)((char *)this + sizeof(mach_header
));
636 else if ( hasMachOBigEndianMagic() )
637 return; // can't process big endian mach-o
639 const uint32_t* h
= (uint32_t*)this;
640 diag
.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h
[0], h
[1]);
641 return; // not a mach-o file
643 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ this->sizeofcmds
);
644 const load_command
* cmd
= startCmds
;
645 for (uint32_t i
= 0; i
< this->ncmds
; ++i
) {
646 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
647 if ( cmd
->cmdsize
< 8 ) {
648 diag
.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i
, this->ncmds
, cmd
, this, cmd
->cmdsize
);
651 if ( (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
652 diag
.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i
, this->ncmds
, cmd
, this, cmd
->cmdsize
, cmdsEnd
);
662 const char* MachOFile::installName() const
665 uint32_t compatVersion
;
666 uint32_t currentVersion
;
667 if ( getDylibInstallName(&name
, &compatVersion
, ¤tVersion
) )
672 bool MachOFile::getDylibInstallName(const char** installName
, uint32_t* compatVersion
, uint32_t* currentVersion
) const
675 __block
bool found
= false;
676 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
677 if ( cmd
->cmd
== LC_ID_DYLIB
) {
678 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
679 *compatVersion
= dylibCmd
->dylib
.compatibility_version
;
680 *currentVersion
= dylibCmd
->dylib
.current_version
;
681 *installName
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
686 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
690 bool MachOFile::getUuid(uuid_t uuid
) const
693 __block
bool found
= false;
694 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
695 if ( cmd
->cmd
== LC_UUID
) {
696 const uuid_command
* uc
= (const uuid_command
*)cmd
;
697 memcpy(uuid
, uc
->uuid
, sizeof(uuid_t
));
702 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
704 bzero(uuid
, sizeof(uuid_t
));
708 void MachOFile::forEachDependentDylib(void (^callback
)(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
)) const
711 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
712 switch ( cmd
->cmd
) {
714 case LC_LOAD_WEAK_DYLIB
:
715 case LC_REEXPORT_DYLIB
:
716 case LC_LOAD_UPWARD_DYLIB
: {
717 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
718 const char* loadPath
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
719 callback(loadPath
, (cmd
->cmd
== LC_LOAD_WEAK_DYLIB
), (cmd
->cmd
== LC_REEXPORT_DYLIB
), (cmd
->cmd
== LC_LOAD_UPWARD_DYLIB
),
720 dylibCmd
->dylib
.compatibility_version
, dylibCmd
->dylib
.current_version
, stop
);
725 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
728 void MachOFile::forDyldEnv(void (^callback
)(const char* envVar
, bool& stop
)) const
731 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
732 if ( cmd
->cmd
== LC_DYLD_ENVIRONMENT
) {
733 const dylinker_command
* envCmd
= (dylinker_command
*)cmd
;
734 const char* keyEqualsValue
= (char*)envCmd
+ envCmd
->name
.offset
;
735 // only process variables that start with DYLD_ and end in _PATH
736 if ( (strncmp(keyEqualsValue
, "DYLD_", 5) == 0) ) {
737 const char* equals
= strchr(keyEqualsValue
, '=');
738 if ( equals
!= NULL
) {
739 if ( strncmp(&equals
[-5], "_PATH", 5) == 0 ) {
740 callback(keyEqualsValue
, stop
);
746 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
749 bool MachOFile::enforceCompatVersion() const
751 __block
bool result
= true;
752 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
753 switch ( platform
) {
754 case Platform::macOS
:
755 if ( minOS
>= 0x000A0E00 ) // macOS 10.14
760 case Platform::iOS_simulator
:
761 case Platform::tvOS_simulator
:
762 if ( minOS
>= 0x000C0000 ) // iOS 12.0
765 case Platform::watchOS
:
766 case Platform::watchOS_simulator
:
767 if ( minOS
>= 0x00050000 ) // watchOS 5.0
770 case Platform::bridgeOS
:
771 if ( minOS
>= 0x00030000 ) // bridgeOS 3.0
774 case Platform::driverKit
:
775 case Platform::iOSMac
:
778 case Platform::unknown
:
786 void MachOFile::forEachSegment(void (^callback
)(const SegmentInfo
& info
, bool& stop
)) const
789 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
790 __block
uint32_t segIndex
= 0;
791 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
792 if ( cmd
->cmd
== LC_SEGMENT_64
) {
793 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
794 uint64_t sizeOfSections
= segCmd
->vmsize
;
796 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
797 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
798 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
799 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
800 if ( sect
->align
> p2align
)
801 p2align
= sect
->align
;
804 info
.fileOffset
= segCmd
->fileoff
;
805 info
.fileSize
= segCmd
->filesize
;
806 info
.vmAddr
= segCmd
->vmaddr
;
807 info
.vmSize
= segCmd
->vmsize
;
808 info
.sizeOfSections
= sizeOfSections
;
809 info
.segName
= segCmd
->segname
;
810 info
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
811 info
.protections
= segCmd
->initprot
;
812 info
.textRelocs
= false;
813 info
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
814 info
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
815 info
.p2align
= p2align
;
816 info
.segIndex
= segIndex
;
817 callback(info
, stop
);
820 else if ( cmd
->cmd
== LC_SEGMENT
) {
821 const segment_command
* segCmd
= (segment_command
*)cmd
;
822 uint64_t sizeOfSections
= segCmd
->vmsize
;
824 bool hasTextRelocs
= false;
825 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
826 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
827 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
828 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
829 if ( sect
->align
> p2align
)
830 p2align
= sect
->align
;
831 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
832 hasTextRelocs
= true;
835 info
.fileOffset
= segCmd
->fileoff
;
836 info
.fileSize
= segCmd
->filesize
;
837 info
.vmAddr
= segCmd
->vmaddr
;
838 info
.vmSize
= segCmd
->vmsize
;
839 info
.sizeOfSections
= sizeOfSections
;
840 info
.segName
= segCmd
->segname
;
841 info
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
842 info
.protections
= segCmd
->initprot
;
843 info
.textRelocs
= intel32
&& !info
.writable() && hasTextRelocs
;
844 info
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
845 info
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
846 info
.p2align
= p2align
;
847 info
.segIndex
= segIndex
;
848 callback(info
, stop
);
852 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
855 void MachOFile::forEachSection(void (^callback
)(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
)) const
858 BLOCK_ACCCESSIBLE_ARRAY(char, sectNameCopy
, 20); // read as: char sectNameCopy[20];
859 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
860 __block
uint32_t segIndex
= 0;
861 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
862 SectionInfo sectInfo
;
863 if ( cmd
->cmd
== LC_SEGMENT_64
) {
864 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
865 uint64_t sizeOfSections
= segCmd
->vmsize
;
867 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
868 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
869 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
870 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
871 if ( sect
->align
> p2align
)
872 p2align
= sect
->align
;
874 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
875 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
876 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
877 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
878 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
879 sectInfo
.segInfo
.segName
= segCmd
->segname
;
880 sectInfo
.segInfo
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
881 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
882 sectInfo
.segInfo
.textRelocs
= false;
883 sectInfo
.segInfo
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
884 sectInfo
.segInfo
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
885 sectInfo
.segInfo
.p2align
= p2align
;
886 sectInfo
.segInfo
.segIndex
= segIndex
;
887 for (const section_64
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
888 const char* sectName
= sect
->sectname
;
889 if ( sectName
[15] != '\0' ) {
890 strlcpy(sectNameCopy
, sectName
, 17);
891 sectName
= sectNameCopy
;
893 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
894 sectInfo
.sectName
= sectName
;
895 sectInfo
.sectFileOffset
= sect
->offset
;
896 sectInfo
.sectFlags
= sect
->flags
;
897 sectInfo
.sectAddr
= sect
->addr
;
898 sectInfo
.sectSize
= sect
->size
;
899 sectInfo
.sectAlignP2
= sect
->align
;
900 sectInfo
.reserved1
= sect
->reserved1
;
901 sectInfo
.reserved2
= sect
->reserved2
;
902 callback(sectInfo
, malformedSectionRange
, stop
);
906 else if ( cmd
->cmd
== LC_SEGMENT
) {
907 const segment_command
* segCmd
= (segment_command
*)cmd
;
908 uint64_t sizeOfSections
= segCmd
->vmsize
;
910 bool hasTextRelocs
= false;
911 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
912 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
913 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
914 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
915 if ( sect
->align
> p2align
)
916 p2align
= sect
->align
;
917 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
918 hasTextRelocs
= true;
920 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
921 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
922 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
923 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
924 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
925 sectInfo
.segInfo
.segName
= segCmd
->segname
;
926 sectInfo
.segInfo
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
927 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
928 sectInfo
.segInfo
.textRelocs
= intel32
&& !sectInfo
.segInfo
.writable() && hasTextRelocs
;
929 sectInfo
.segInfo
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
930 sectInfo
.segInfo
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
931 sectInfo
.segInfo
.p2align
= p2align
;
932 sectInfo
.segInfo
.segIndex
= segIndex
;
933 for (const section
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
934 const char* sectName
= sect
->sectname
;
935 if ( sectName
[15] != '\0' ) {
936 strlcpy(sectNameCopy
, sectName
, 17);
937 sectName
= sectNameCopy
;
939 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
940 sectInfo
.sectName
= sectName
;
941 sectInfo
.sectFileOffset
= sect
->offset
;
942 sectInfo
.sectFlags
= sect
->flags
;
943 sectInfo
.sectAddr
= sect
->addr
;
944 sectInfo
.sectSize
= sect
->size
;
945 sectInfo
.sectAlignP2
= sect
->align
;
946 sectInfo
.reserved1
= sect
->reserved1
;
947 sectInfo
.reserved2
= sect
->reserved2
;
948 callback(sectInfo
, malformedSectionRange
, stop
);
953 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
956 bool MachOFile::hasWeakDefs() const
958 return (this->flags
& MH_WEAK_DEFINES
);
961 bool MachOFile::hasThreadLocalVariables() const
963 return (this->flags
& MH_HAS_TLV_DESCRIPTORS
);
966 static bool endsWith(const char* str
, const char* suffix
)
968 size_t strLen
= strlen(str
);
969 size_t suffixLen
= strlen(suffix
);
970 if ( strLen
< suffixLen
)
972 return (strcmp(&str
[strLen
-suffixLen
], suffix
) == 0);
975 bool MachOFile::isSharedCacheEligiblePath(const char* dylibName
) {
976 return ( (strncmp(dylibName
, "/usr/lib/", 9) == 0)
977 || (strncmp(dylibName
, "/System/Library/", 16) == 0)
978 || (strncmp(dylibName
, "/System/iOSSupport/usr/lib/", 27) == 0)
979 || (strncmp(dylibName
, "/System/iOSSupport/System/Library/", 34) == 0)
980 || (strncmp(dylibName
, "/Library/Apple/usr/lib/", 23) == 0)
981 || (strncmp(dylibName
, "/Library/Apple/System/Library/", 30) == 0) );
984 bool MachOFile::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const
986 if ( !isSharedCacheEligiblePath(path
) ) {
987 // Dont spam the user with an error about paths when we know these are never eligible.
991 // only dylibs can go in cache
992 if ( this->filetype
!= MH_DYLIB
) {
993 failureReason("Not MH_DYLIB");
994 return false; // cannot continue, installName() will assert() if not a dylib
997 // only dylibs built for /usr/lib or /System/Library can go in cache
999 const char* dylibName
= installName();
1000 if ( dylibName
[0] != '/' ) {
1001 failureReason("install name not an absolute path");
1002 // Don't continue as we don't want to spam the log with errors we don't need.
1005 else if ( strcmp(dylibName
, path
) != 0 ) {
1006 failureReason("install path does not match install name");
1012 // flat namespace files cannot go in cache
1013 if ( (this->flags
& MH_TWOLEVEL
) == 0 ) {
1015 failureReason("Not built with two level namespaces");
1018 // don't put debug variants into dyld cache
1019 if ( endsWith(path
, "_profile.dylib") || endsWith(path
, "_debug.dylib") || endsWith(path
, "_profile") || endsWith(path
, "_debug") || endsWith(path
, "/CoreADI") ) {
1021 failureReason("Variant image");
1024 // dylib must have extra info for moving DATA and TEXT segments apart
1025 __block
bool hasExtraInfo
= false;
1026 __block
bool hasDyldInfo
= false;
1027 __block
bool hasExportTrie
= false;
1029 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1030 if ( cmd
->cmd
== LC_SEGMENT_SPLIT_INFO
)
1031 hasExtraInfo
= true;
1032 if ( cmd
->cmd
== LC_DYLD_INFO_ONLY
)
1034 if ( cmd
->cmd
== LC_DYLD_EXPORTS_TRIE
)
1035 hasExportTrie
= true;
1037 if ( !hasExtraInfo
) {
1039 failureReason("Missing split seg info");
1041 if ( !hasDyldInfo
&& !hasExportTrie
) {
1043 failureReason("Old binary, missing dyld info or export trie");
1046 // dylib can only depend on other dylibs in the shared cache
1047 __block
bool allDepPathsAreGood
= true;
1048 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
1049 if ( !isSharedCacheEligiblePath(loadPath
) ) {
1050 allDepPathsAreGood
= false;
1054 if ( !allDepPathsAreGood
) {
1056 failureReason("Depends on dylibs ineligable for dyld cache");
1059 // dylibs with interposing info cannot be in cache
1060 __block
bool hasInterposing
= false;
1061 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
1062 if ( ((info
.sectFlags
& SECTION_TYPE
) == S_INTERPOSING
) || ((strcmp(info
.sectName
, "__interpose") == 0) && (strcmp(info
.segInfo
.segName
, "__DATA") == 0)) )
1063 hasInterposing
= true;
1065 if ( hasInterposing
) {
1067 failureReason("Has interposing tuples");
1070 // Temporarily kick out swift binaries on watchOS simulators as they have missing split seg
1071 if ( supportsPlatform(Platform::watchOS_simulator
) && isArch("i386") ) {
1072 if ( strncmp(dylibName
, "/usr/lib/swift/", 15) == 0 ) {
1074 failureReason("i386 swift binary");
1082 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset
, uint32_t& size
) const
1084 if ( const encryption_info_command
* encCmd
= findFairPlayEncryptionLoadCommand() ) {
1085 if ( encCmd
->cryptid
== 1 ) {
1086 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
1087 textOffset
= encCmd
->cryptoff
;
1088 size
= encCmd
->cryptsize
;
1097 bool MachOFile::canBeFairPlayEncrypted() const
1099 return (findFairPlayEncryptionLoadCommand() != nullptr);
1102 const encryption_info_command
* MachOFile::findFairPlayEncryptionLoadCommand() const
1104 __block
const encryption_info_command
* result
= nullptr;
1106 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1107 if ( (cmd
->cmd
== LC_ENCRYPTION_INFO
) || (cmd
->cmd
== LC_ENCRYPTION_INFO_64
) ) {
1108 result
= (encryption_info_command
*)cmd
;
1112 if ( diag
.noError() )
1119 bool MachOFile::hasLoadCommand(uint32_t cmdNum
) const
1121 __block
bool hasLC
= false;
1123 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1124 if ( cmd
->cmd
== cmdNum
) {
1132 bool MachOFile::allowsAlternatePlatform() const
1134 __block
bool result
= false;
1135 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
1136 if ( (strcmp(info
.sectName
, "__allow_alt_plat") == 0) && (strncmp(info
.segInfo
.segName
, "__DATA", 6) == 0) ) {
1144 bool MachOFile::hasChainedFixups() const
1146 #if SUPPORT_ARCH_arm64e
1147 // arm64e always uses chained fixups
1148 if ( (this->cputype
== CPU_TYPE_ARM64
) && (this->cpusubtype
== CPU_SUBTYPE_ARM64E
) )
1151 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS
);
1154 uint64_t MachOFile::read_uleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
1156 uint64_t result
= 0;
1160 diag
.error("malformed uleb128");
1163 uint64_t slice
= *p
& 0x7f;
1166 diag
.error("uleb128 too big for uint64");
1170 result
|= (slice
<< bit
);
1174 while (*p
++ & 0x80);
1179 int64_t MachOFile::read_sleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
1186 diag
.error("malformed sleb128");
1190 result
|= (((int64_t)(byte
& 0x7f)) << bit
);
1192 } while (byte
& 0x80);
1193 // sign extend negative numbers
1194 if ( (byte
& 0x40) != 0 )
1195 result
|= (~0ULL) << bit
;
1200 } // namespace dyld3