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
)
433 #if BUILDING_DYLDINFO
434 // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms
435 if ( !foundOtherPlatform
&& (reqPlatform
== Platform::unknown
) )
442 bool MachOFile::isZippered() const
444 __block
bool macOS
= false;
445 __block
bool iOSMac
= false;
446 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
447 if ( platform
== Platform::macOS
)
449 else if ( platform
== Platform::iOSMac
)
452 return macOS
&& iOSMac
;
455 bool MachOFile::inDyldCache() const {
456 return (this->flags
& 0x80000000);
459 Platform
MachOFile::currentPlatform()
462 #if TARGET_OS_SIMULATOR
464 return Platform::watchOS_simulator
;
466 return Platform::tvOS_simulator
;
468 return Platform::iOS_simulator
;
470 #elif TARGET_OS_BRIDGE
471 return Platform::bridgeOS
;
472 #elif TARGET_OS_WATCH
473 return Platform::watchOS
;
475 return Platform::tvOS
;
477 return Platform::iOS
;
479 return Platform::macOS
;
481 #error unknown platform
486 const char* MachOFile::currentArchName()
490 #elif __ARM_ARCH_7A__
492 #elif __ARM_ARCH_7S__
503 return isHaswell() ? "x86_64h" : "x86_64";
511 bool MachOFile::isSimulatorPlatform(Platform platform
)
513 return ( (platform
== Platform::iOS_simulator
) ||
514 (platform
== Platform::watchOS_simulator
) ||
515 (platform
== Platform::tvOS_simulator
) );
518 bool MachOFile::isDylib() const
520 return (this->filetype
== MH_DYLIB
);
523 bool MachOFile::isBundle() const
525 return (this->filetype
== MH_BUNDLE
);
528 bool MachOFile::isMainExecutable() const
530 return (this->filetype
== MH_EXECUTE
);
533 bool MachOFile::isDynamicExecutable() const
535 if ( this->filetype
!= MH_EXECUTE
)
538 // static executables do not have dyld load command
539 return hasLoadCommand(LC_LOAD_DYLINKER
);
542 bool MachOFile::isStaticExecutable() const
544 if ( this->filetype
!= MH_EXECUTE
)
547 // static executables do not have dyld load command
548 return !hasLoadCommand(LC_LOAD_DYLINKER
);
551 bool MachOFile::isPIE() const
553 return (this->flags
& MH_PIE
);
556 bool MachOFile::isPreload() const
558 return (this->filetype
== MH_PRELOAD
);
561 const char* MachOFile::platformName(Platform reqPlatform
)
563 for (const PlatformInfo
& info
: _s_platformInfos
) {
564 if ( info
.platform
== reqPlatform
)
567 return "unknown platform";
570 void MachOFile::forEachSupportedPlatform(void (^handler
)(Platform platform
, uint32_t minOS
, uint32_t sdk
)) const
573 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
574 const build_version_command
* buildCmd
= (build_version_command
*)cmd
;
575 const version_min_command
* versCmd
= (version_min_command
*)cmd
;
576 switch ( cmd
->cmd
) {
577 case LC_BUILD_VERSION
:
578 handler((Platform
)(buildCmd
->platform
), buildCmd
->minos
, buildCmd
->sdk
);
580 case LC_VERSION_MIN_MACOSX
:
581 handler(Platform::macOS
, versCmd
->version
, versCmd
->sdk
);
583 case LC_VERSION_MIN_IPHONEOS
:
584 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) )
585 handler(Platform::iOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
587 handler(Platform::iOS
, versCmd
->version
, versCmd
->sdk
);
589 case LC_VERSION_MIN_TVOS
:
590 if ( this->cputype
== CPU_TYPE_X86_64
)
591 handler(Platform::tvOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
593 handler(Platform::tvOS
, versCmd
->version
, versCmd
->sdk
);
595 case LC_VERSION_MIN_WATCHOS
:
596 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) )
597 handler(Platform::watchOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
599 handler(Platform::watchOS
, versCmd
->version
, versCmd
->sdk
);
603 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
607 bool MachOFile::isMachO(Diagnostics
& diag
, uint64_t fileSize
) const
609 if ( !hasMachOMagic() ) {
610 // old PPC slices are not currently valid "mach-o" but should not cause an error
611 if ( !hasMachOBigEndianMagic() )
612 diag
.error("file does not start with MH_MAGIC[_64]");
615 if ( this->sizeofcmds
+ machHeaderSize() > fileSize
) {
616 diag
.error("load commands exceed length of first segment");
619 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { });
620 return diag
.noError();
623 bool MachOFile::hasMachOMagic() const
625 return ( (this->magic
== MH_MAGIC
) || (this->magic
== MH_MAGIC_64
) );
628 bool MachOFile::hasMachOBigEndianMagic() const
630 return ( (this->magic
== MH_CIGAM
) || (this->magic
== MH_CIGAM_64
) );
634 void MachOFile::forEachLoadCommand(Diagnostics
& diag
, void (^callback
)(const load_command
* cmd
, bool& stop
)) const
637 const load_command
* startCmds
= nullptr;
638 if ( this->magic
== MH_MAGIC_64
)
639 startCmds
= (load_command
*)((char *)this + sizeof(mach_header_64
));
640 else if ( this->magic
== MH_MAGIC
)
641 startCmds
= (load_command
*)((char *)this + sizeof(mach_header
));
642 else if ( hasMachOBigEndianMagic() )
643 return; // can't process big endian mach-o
645 const uint32_t* h
= (uint32_t*)this;
646 diag
.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h
[0], h
[1]);
647 return; // not a mach-o file
649 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ this->sizeofcmds
);
650 const load_command
* cmd
= startCmds
;
651 for (uint32_t i
= 0; i
< this->ncmds
; ++i
) {
652 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
653 if ( cmd
->cmdsize
< 8 ) {
654 diag
.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i
, this->ncmds
, cmd
, this, cmd
->cmdsize
);
657 if ( (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
658 diag
.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i
, this->ncmds
, cmd
, this, cmd
->cmdsize
, cmdsEnd
);
668 const char* MachOFile::installName() const
671 uint32_t compatVersion
;
672 uint32_t currentVersion
;
673 if ( getDylibInstallName(&name
, &compatVersion
, ¤tVersion
) )
678 bool MachOFile::getDylibInstallName(const char** installName
, uint32_t* compatVersion
, uint32_t* currentVersion
) const
681 __block
bool found
= false;
682 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
683 if ( cmd
->cmd
== LC_ID_DYLIB
) {
684 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
685 *compatVersion
= dylibCmd
->dylib
.compatibility_version
;
686 *currentVersion
= dylibCmd
->dylib
.current_version
;
687 *installName
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
692 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
696 bool MachOFile::getUuid(uuid_t uuid
) const
699 __block
bool found
= false;
700 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
701 if ( cmd
->cmd
== LC_UUID
) {
702 const uuid_command
* uc
= (const uuid_command
*)cmd
;
703 memcpy(uuid
, uc
->uuid
, sizeof(uuid_t
));
708 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
710 bzero(uuid
, sizeof(uuid_t
));
714 void MachOFile::forEachDependentDylib(void (^callback
)(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
)) const
717 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
718 switch ( cmd
->cmd
) {
720 case LC_LOAD_WEAK_DYLIB
:
721 case LC_REEXPORT_DYLIB
:
722 case LC_LOAD_UPWARD_DYLIB
: {
723 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
724 const char* loadPath
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
725 callback(loadPath
, (cmd
->cmd
== LC_LOAD_WEAK_DYLIB
), (cmd
->cmd
== LC_REEXPORT_DYLIB
), (cmd
->cmd
== LC_LOAD_UPWARD_DYLIB
),
726 dylibCmd
->dylib
.compatibility_version
, dylibCmd
->dylib
.current_version
, stop
);
731 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
734 void MachOFile::forDyldEnv(void (^callback
)(const char* envVar
, bool& stop
)) const
737 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
738 if ( cmd
->cmd
== LC_DYLD_ENVIRONMENT
) {
739 const dylinker_command
* envCmd
= (dylinker_command
*)cmd
;
740 const char* keyEqualsValue
= (char*)envCmd
+ envCmd
->name
.offset
;
741 // only process variables that start with DYLD_ and end in _PATH
742 if ( (strncmp(keyEqualsValue
, "DYLD_", 5) == 0) ) {
743 const char* equals
= strchr(keyEqualsValue
, '=');
744 if ( equals
!= NULL
) {
745 if ( strncmp(&equals
[-5], "_PATH", 5) == 0 ) {
746 callback(keyEqualsValue
, stop
);
752 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
755 bool MachOFile::enforceCompatVersion() const
757 __block
bool result
= true;
758 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
759 switch ( platform
) {
760 case Platform::macOS
:
761 if ( minOS
>= 0x000A0E00 ) // macOS 10.14
766 case Platform::iOS_simulator
:
767 case Platform::tvOS_simulator
:
768 if ( minOS
>= 0x000C0000 ) // iOS 12.0
771 case Platform::watchOS
:
772 case Platform::watchOS_simulator
:
773 if ( minOS
>= 0x00050000 ) // watchOS 5.0
776 case Platform::bridgeOS
:
777 if ( minOS
>= 0x00030000 ) // bridgeOS 3.0
780 case Platform::driverKit
:
781 case Platform::iOSMac
:
784 case Platform::unknown
:
792 void MachOFile::forEachSegment(void (^callback
)(const SegmentInfo
& info
, bool& stop
)) const
795 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
796 __block
uint32_t segIndex
= 0;
797 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
798 if ( cmd
->cmd
== LC_SEGMENT_64
) {
799 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
800 uint64_t sizeOfSections
= segCmd
->vmsize
;
802 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
803 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
804 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
805 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
806 if ( sect
->align
> p2align
)
807 p2align
= sect
->align
;
810 info
.fileOffset
= segCmd
->fileoff
;
811 info
.fileSize
= segCmd
->filesize
;
812 info
.vmAddr
= segCmd
->vmaddr
;
813 info
.vmSize
= segCmd
->vmsize
;
814 info
.sizeOfSections
= sizeOfSections
;
815 info
.segName
= segCmd
->segname
;
816 info
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
817 info
.protections
= segCmd
->initprot
;
818 info
.textRelocs
= false;
819 info
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
820 info
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
821 info
.p2align
= p2align
;
822 info
.segIndex
= segIndex
;
823 callback(info
, stop
);
826 else if ( cmd
->cmd
== LC_SEGMENT
) {
827 const segment_command
* segCmd
= (segment_command
*)cmd
;
828 uint64_t sizeOfSections
= segCmd
->vmsize
;
830 bool hasTextRelocs
= false;
831 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
832 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
833 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
834 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
835 if ( sect
->align
> p2align
)
836 p2align
= sect
->align
;
837 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
838 hasTextRelocs
= true;
841 info
.fileOffset
= segCmd
->fileoff
;
842 info
.fileSize
= segCmd
->filesize
;
843 info
.vmAddr
= segCmd
->vmaddr
;
844 info
.vmSize
= segCmd
->vmsize
;
845 info
.sizeOfSections
= sizeOfSections
;
846 info
.segName
= segCmd
->segname
;
847 info
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
848 info
.protections
= segCmd
->initprot
;
849 info
.textRelocs
= intel32
&& !info
.writable() && hasTextRelocs
;
850 info
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
851 info
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
852 info
.p2align
= p2align
;
853 info
.segIndex
= segIndex
;
854 callback(info
, stop
);
858 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
861 void MachOFile::forEachSection(void (^callback
)(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
)) const
864 BLOCK_ACCCESSIBLE_ARRAY(char, sectNameCopy
, 20); // read as: char sectNameCopy[20];
865 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
866 __block
uint32_t segIndex
= 0;
867 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
868 SectionInfo sectInfo
;
869 if ( cmd
->cmd
== LC_SEGMENT_64
) {
870 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
871 uint64_t sizeOfSections
= segCmd
->vmsize
;
873 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
874 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
875 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
876 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
877 if ( sect
->align
> p2align
)
878 p2align
= sect
->align
;
880 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
881 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
882 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
883 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
884 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
885 sectInfo
.segInfo
.segName
= segCmd
->segname
;
886 sectInfo
.segInfo
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
887 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
888 sectInfo
.segInfo
.textRelocs
= false;
889 sectInfo
.segInfo
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
890 sectInfo
.segInfo
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
891 sectInfo
.segInfo
.p2align
= p2align
;
892 sectInfo
.segInfo
.segIndex
= segIndex
;
893 for (const section_64
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
894 const char* sectName
= sect
->sectname
;
895 if ( sectName
[15] != '\0' ) {
896 strlcpy(sectNameCopy
, sectName
, 17);
897 sectName
= sectNameCopy
;
899 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
900 sectInfo
.sectName
= sectName
;
901 sectInfo
.sectFileOffset
= sect
->offset
;
902 sectInfo
.sectFlags
= sect
->flags
;
903 sectInfo
.sectAddr
= sect
->addr
;
904 sectInfo
.sectSize
= sect
->size
;
905 sectInfo
.sectAlignP2
= sect
->align
;
906 sectInfo
.reserved1
= sect
->reserved1
;
907 sectInfo
.reserved2
= sect
->reserved2
;
908 callback(sectInfo
, malformedSectionRange
, stop
);
912 else if ( cmd
->cmd
== LC_SEGMENT
) {
913 const segment_command
* segCmd
= (segment_command
*)cmd
;
914 uint64_t sizeOfSections
= segCmd
->vmsize
;
916 bool hasTextRelocs
= false;
917 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
918 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
919 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
920 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
921 if ( sect
->align
> p2align
)
922 p2align
= sect
->align
;
923 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
924 hasTextRelocs
= true;
926 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
927 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
928 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
929 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
930 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
931 sectInfo
.segInfo
.segName
= segCmd
->segname
;
932 sectInfo
.segInfo
.loadCommandOffset
= (uint32_t)((uint8_t*)segCmd
- (uint8_t*)this);
933 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
934 sectInfo
.segInfo
.textRelocs
= intel32
&& !sectInfo
.segInfo
.writable() && hasTextRelocs
;
935 sectInfo
.segInfo
.readOnlyData
= ((segCmd
->flags
& SG_READ_ONLY
) != 0);
936 sectInfo
.segInfo
.isProtected
= (segCmd
->flags
& SG_PROTECTED_VERSION_1
) ? 1 : 0;
937 sectInfo
.segInfo
.p2align
= p2align
;
938 sectInfo
.segInfo
.segIndex
= segIndex
;
939 for (const section
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
940 const char* sectName
= sect
->sectname
;
941 if ( sectName
[15] != '\0' ) {
942 strlcpy(sectNameCopy
, sectName
, 17);
943 sectName
= sectNameCopy
;
945 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
946 sectInfo
.sectName
= sectName
;
947 sectInfo
.sectFileOffset
= sect
->offset
;
948 sectInfo
.sectFlags
= sect
->flags
;
949 sectInfo
.sectAddr
= sect
->addr
;
950 sectInfo
.sectSize
= sect
->size
;
951 sectInfo
.sectAlignP2
= sect
->align
;
952 sectInfo
.reserved1
= sect
->reserved1
;
953 sectInfo
.reserved2
= sect
->reserved2
;
954 callback(sectInfo
, malformedSectionRange
, stop
);
959 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
962 bool MachOFile::hasWeakDefs() const
964 return (this->flags
& MH_WEAK_DEFINES
);
967 bool MachOFile::hasThreadLocalVariables() const
969 return (this->flags
& MH_HAS_TLV_DESCRIPTORS
);
972 static bool endsWith(const char* str
, const char* suffix
)
974 size_t strLen
= strlen(str
);
975 size_t suffixLen
= strlen(suffix
);
976 if ( strLen
< suffixLen
)
978 return (strcmp(&str
[strLen
-suffixLen
], suffix
) == 0);
981 bool MachOFile::isSharedCacheEligiblePath(const char* dylibName
) {
982 return ( (strncmp(dylibName
, "/usr/lib/", 9) == 0)
983 || (strncmp(dylibName
, "/System/Library/", 16) == 0)
984 || (strncmp(dylibName
, "/System/iOSSupport/usr/lib/", 27) == 0)
985 || (strncmp(dylibName
, "/System/iOSSupport/System/Library/", 34) == 0)
986 || (strncmp(dylibName
, "/Library/Apple/usr/lib/", 23) == 0)
987 || (strncmp(dylibName
, "/Library/Apple/System/Library/", 30) == 0) );
990 bool MachOFile::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const
992 if ( !isSharedCacheEligiblePath(path
) ) {
993 // Dont spam the user with an error about paths when we know these are never eligible.
997 // only dylibs can go in cache
998 if ( this->filetype
!= MH_DYLIB
) {
999 failureReason("Not MH_DYLIB");
1000 return false; // cannot continue, installName() will assert() if not a dylib
1003 // only dylibs built for /usr/lib or /System/Library can go in cache
1005 const char* dylibName
= installName();
1006 if ( dylibName
[0] != '/' ) {
1007 failureReason("install name not an absolute path");
1008 // Don't continue as we don't want to spam the log with errors we don't need.
1011 else if ( strcmp(dylibName
, path
) != 0 ) {
1012 failureReason("install path does not match install name");
1018 // flat namespace files cannot go in cache
1019 if ( (this->flags
& MH_TWOLEVEL
) == 0 ) {
1021 failureReason("Not built with two level namespaces");
1024 // don't put debug variants into dyld cache
1025 if ( endsWith(path
, "_profile.dylib") || endsWith(path
, "_debug.dylib") || endsWith(path
, "_profile") || endsWith(path
, "_debug") || endsWith(path
, "/CoreADI") ) {
1027 failureReason("Variant image");
1030 // dylib must have extra info for moving DATA and TEXT segments apart
1031 __block
bool hasExtraInfo
= false;
1032 __block
bool hasDyldInfo
= false;
1033 __block
bool hasExportTrie
= false;
1035 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1036 if ( cmd
->cmd
== LC_SEGMENT_SPLIT_INFO
)
1037 hasExtraInfo
= true;
1038 if ( cmd
->cmd
== LC_DYLD_INFO_ONLY
)
1040 if ( cmd
->cmd
== LC_DYLD_EXPORTS_TRIE
)
1041 hasExportTrie
= true;
1043 if ( !hasExtraInfo
) {
1045 failureReason("Missing split seg info");
1047 if ( !hasDyldInfo
&& !hasExportTrie
) {
1049 failureReason("Old binary, missing dyld info or export trie");
1052 // dylib can only depend on other dylibs in the shared cache
1053 __block
bool allDepPathsAreGood
= true;
1054 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
1055 if ( !isSharedCacheEligiblePath(loadPath
) ) {
1056 allDepPathsAreGood
= false;
1060 if ( !allDepPathsAreGood
) {
1062 failureReason("Depends on dylibs ineligable for dyld cache");
1065 // dylibs with interposing info cannot be in cache
1066 __block
bool hasInterposing
= false;
1067 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
1068 if ( ((info
.sectFlags
& SECTION_TYPE
) == S_INTERPOSING
) || ((strcmp(info
.sectName
, "__interpose") == 0) && (strcmp(info
.segInfo
.segName
, "__DATA") == 0)) )
1069 hasInterposing
= true;
1071 if ( hasInterposing
) {
1073 failureReason("Has interposing tuples");
1076 // Temporarily kick out swift binaries on watchOS simulators as they have missing split seg
1077 if ( supportsPlatform(Platform::watchOS_simulator
) && isArch("i386") ) {
1078 if ( strncmp(dylibName
, "/usr/lib/swift/", 15) == 0 ) {
1080 failureReason("i386 swift binary");
1088 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset
, uint32_t& size
) const
1090 if ( const encryption_info_command
* encCmd
= findFairPlayEncryptionLoadCommand() ) {
1091 if ( encCmd
->cryptid
== 1 ) {
1092 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
1093 textOffset
= encCmd
->cryptoff
;
1094 size
= encCmd
->cryptsize
;
1103 bool MachOFile::canBeFairPlayEncrypted() const
1105 return (findFairPlayEncryptionLoadCommand() != nullptr);
1108 const encryption_info_command
* MachOFile::findFairPlayEncryptionLoadCommand() const
1110 __block
const encryption_info_command
* result
= nullptr;
1112 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1113 if ( (cmd
->cmd
== LC_ENCRYPTION_INFO
) || (cmd
->cmd
== LC_ENCRYPTION_INFO_64
) ) {
1114 result
= (encryption_info_command
*)cmd
;
1118 if ( diag
.noError() )
1125 bool MachOFile::hasLoadCommand(uint32_t cmdNum
) const
1127 __block
bool hasLC
= false;
1129 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
1130 if ( cmd
->cmd
== cmdNum
) {
1138 bool MachOFile::allowsAlternatePlatform() const
1140 __block
bool result
= false;
1141 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
1142 if ( (strcmp(info
.sectName
, "__allow_alt_plat") == 0) && (strncmp(info
.segInfo
.segName
, "__DATA", 6) == 0) ) {
1150 bool MachOFile::hasChainedFixups() const
1152 #if SUPPORT_ARCH_arm64e
1153 // arm64e always uses chained fixups
1154 if ( (this->cputype
== CPU_TYPE_ARM64
) && (this->cpusubtype
== CPU_SUBTYPE_ARM64E
) )
1157 return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS
);
1160 uint64_t MachOFile::read_uleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
1162 uint64_t result
= 0;
1166 diag
.error("malformed uleb128");
1169 uint64_t slice
= *p
& 0x7f;
1172 diag
.error("uleb128 too big for uint64");
1176 result
|= (slice
<< bit
);
1180 while (*p
++ & 0x80);
1185 int64_t MachOFile::read_sleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
1192 diag
.error("malformed sleb128");
1196 result
|= (((int64_t)(byte
& 0x7f)) << bit
);
1198 } while (byte
& 0x80);
1199 // sign extend negative numbers
1200 if ( (byte
& 0x40) != 0 )
1201 result
|= (~0ULL) << bit
;
1206 } // namespace dyld3