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>
33 #include "MachOFile.h"
34 #include "SupportedArchs.h"
36 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
37 #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
40 #ifndef CPU_SUBTYPE_ARM64_E
41 #define CPU_SUBTYPE_ARM64_E 2
44 #ifndef CPU_SUBTYPE_ARM64_32_V8
45 #define CPU_SUBTYPE_ARM64_32_V8 1
48 #ifndef CPU_TYPE_ARM64_32
49 #ifndef CPU_ARCH_ABI64_32
50 #define CPU_ARCH_ABI64_32 0x02000000
52 #define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
57 //////////////////////////// FatFile ////////////////////////////////////////
59 const FatFile
* FatFile::isFatFile(const void* fileStart
)
61 const FatFile
* fileStartAsFat
= (FatFile
*)fileStart
;
62 if ( (fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
)) || (fileStartAsFat
->magic
== OSSwapBigToHostInt32(FAT_MAGIC_64
)) )
63 return fileStartAsFat
;
68 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
70 if ( this->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
71 if ( OSSwapBigToHostInt32(nfat_arch
) > ((4096 - sizeof(fat_header
)) / sizeof(fat_arch
)) ) {
72 diag
.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch
));
76 const fat_arch
* const archs
= (fat_arch
*)(((char*)this)+sizeof(fat_header
));
77 for (uint32_t i
=0; i
< OSSwapBigToHostInt32(nfat_arch
); ++i
) {
78 uint32_t cpuType
= OSSwapBigToHostInt32(archs
[i
].cputype
);
79 uint32_t cpuSubType
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
80 uint32_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
81 uint32_t len
= OSSwapBigToHostInt32(archs
[i
].size
);
82 if ( greaterThanAddOrOverflow(offset
, len
, fileLen
) ) {
83 diag
.error("slice %d extends beyond end of file", i
);
86 callback(cpuType
, cpuSubType
, (uint8_t*)this+offset
, len
, stop
);
91 else if ( this->magic
== OSSwapBigToHostInt32(FAT_MAGIC_64
) ) {
92 if ( OSSwapBigToHostInt32(nfat_arch
) > ((4096 - sizeof(fat_header
)) / sizeof(fat_arch
)) ) {
93 diag
.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch
));
97 const fat_arch_64
* const archs
= (fat_arch_64
*)(((char*)this)+sizeof(fat_header
));
98 for (uint32_t i
=0; i
< OSSwapBigToHostInt32(nfat_arch
); ++i
) {
99 uint32_t cpuType
= OSSwapBigToHostInt32(archs
[i
].cputype
);
100 uint32_t cpuSubType
= OSSwapBigToHostInt32(archs
[i
].cpusubtype
);
101 uint64_t offset
= OSSwapBigToHostInt64(archs
[i
].offset
);
102 uint64_t len
= OSSwapBigToHostInt64(archs
[i
].size
);
103 if ( greaterThanAddOrOverflow(offset
, len
, fileLen
) ) {
104 diag
.error("slice %d extends beyond end of file", i
);
107 callback(cpuType
, cpuSubType
, (uint8_t*)this+offset
, len
, stop
);
113 diag
.error("not a fat file");
117 bool FatFile::isFatFileWithSlice(Diagnostics
& diag
, uint64_t fileLen
, const char* archName
, uint64_t& sliceOffset
, uint64_t& sliceLen
, bool& missingSlice
) const
119 missingSlice
= false;
120 if ( (this->magic
!= OSSwapBigToHostInt32(FAT_MAGIC
)) && (this->magic
!= OSSwapBigToHostInt32(FAT_MAGIC_64
)) )
123 __block
bool found
= false;
124 forEachSlice(diag
, fileLen
, ^(uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, const void* sliceStart
, uint64_t sliceSize
, bool& stop
) {
125 const char* sliceArchName
= MachOFile::archName(sliceCpuType
, sliceCpuSubType
);
126 if ( strcmp(sliceArchName
, archName
) == 0 ) {
127 sliceOffset
= (char*)sliceStart
- (char*)this;
128 sliceLen
= sliceSize
;
133 if ( diag
.hasError() )
139 // when looking for x86_64h fallback to x86_64
140 if ( !found
&& (strcmp(archName
, "x86_64h") == 0) )
141 return isFatFileWithSlice(diag
, fileLen
, "x86_64", sliceOffset
, sliceLen
, missingSlice
);
147 //////////////////////////// MachOFile ////////////////////////////////////////
150 const MachOFile::ArchInfo
MachOFile::_s_archInfos
[] = {
151 { "x86_64", CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_ALL
},
152 { "x86_64h", CPU_TYPE_X86_64
, CPU_SUBTYPE_X86_64_H
},
153 { "i386", CPU_TYPE_I386
, CPU_SUBTYPE_I386_ALL
},
154 { "arm64", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64_ALL
},
155 #if SUPPORT_ARCH_arm64e
156 { "arm64e", CPU_TYPE_ARM64
, CPU_SUBTYPE_ARM64_E
},
158 #if SUPPORT_ARCH_arm64_32
159 { "arm64_32", CPU_TYPE_ARM64_32
, CPU_SUBTYPE_ARM64_32_V8
},
161 { "armv7k", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7K
},
162 { "armv7s", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7S
},
163 { "armv7", CPU_TYPE_ARM
, CPU_SUBTYPE_ARM_V7
}
166 const MachOFile::PlatformInfo
MachOFile::_s_platformInfos
[] = {
167 { "macOS", Platform::macOS
, LC_VERSION_MIN_MACOSX
},
168 { "iOS", Platform::iOS
, LC_VERSION_MIN_IPHONEOS
},
169 { "tvOS", Platform::tvOS
, LC_VERSION_MIN_TVOS
},
170 { "watchOS", Platform::watchOS
, LC_VERSION_MIN_WATCHOS
},
171 { "bridgeOS", Platform::bridgeOS
, LC_BUILD_VERSION
},
172 { "iOSMac", Platform::iOSMac
, LC_BUILD_VERSION
},
173 { "iOS-sim", Platform::iOS_simulator
, LC_BUILD_VERSION
},
174 { "tvOS-sim", Platform::tvOS_simulator
, LC_BUILD_VERSION
},
175 { "watchOS-sim", Platform::watchOS_simulator
, LC_BUILD_VERSION
},
179 bool MachOFile::is64() const
181 return (this->magic
== MH_MAGIC_64
);
184 uint32_t MachOFile::pointerSize() const
186 if (this->magic
== MH_MAGIC_64
)
192 bool MachOFile::uses16KPages() const
194 switch (this->cputype
) {
197 case CPU_TYPE_ARM64_32
:
204 bool MachOFile::isArch(const char* aName
) const
206 return (strcmp(aName
, archName(this->cputype
, this->cpusubtype
)) == 0);
209 const char* MachOFile::archName(uint32_t cputype
, uint32_t cpusubtype
)
211 for (const ArchInfo
& info
: _s_archInfos
) {
212 if ( (cputype
== info
.cputype
) && ((cpusubtype
& ~CPU_SUBTYPE_MASK
) == info
.cpusubtype
) ) {
219 uint32_t MachOFile::cpuTypeFromArchName(const char* archName
)
221 for (const ArchInfo
& info
: _s_archInfos
) {
222 if ( strcmp(archName
, info
.name
) == 0 ) {
229 uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName
)
231 for (const ArchInfo
& info
: _s_archInfos
) {
232 if ( strcmp(archName
, info
.name
) == 0 ) {
233 return info
.cpusubtype
;
239 const char* MachOFile::archName() const
241 return archName(this->cputype
, this->cpusubtype
);
244 static void appendDigit(char*& s
, unsigned& num
, unsigned place
, bool& startedPrinting
)
246 if ( num
>= place
) {
247 unsigned dig
= (num
/place
);
250 startedPrinting
= true;
252 else if ( startedPrinting
) {
257 static void appendNumber(char*& s
, unsigned num
)
260 bool startedPrinting
= false;
261 appendDigit(s
, num
, 10000, startedPrinting
);
262 appendDigit(s
, num
, 1000, startedPrinting
);
263 appendDigit(s
, num
, 100, startedPrinting
);
264 appendDigit(s
, num
, 10, startedPrinting
);
265 appendDigit(s
, num
, 1, startedPrinting
);
266 if ( !startedPrinting
)
270 void MachOFile::packedVersionToString(uint32_t packedVersion
, char versionString
[32])
272 // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
273 char* s
= versionString
;
274 appendNumber(s
, (packedVersion
>> 16));
276 appendNumber(s
, (packedVersion
>> 8) & 0xFF);
278 appendNumber(s
, (packedVersion
& 0xFF));
282 bool MachOFile::supportsPlatform(Platform reqPlatform
) const
284 __block
bool foundRequestedPlatform
= false;
285 __block
bool foundOtherPlatform
= false;
286 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
287 if ( platform
== reqPlatform
)
288 foundRequestedPlatform
= true;
290 foundOtherPlatform
= true;
292 if ( foundRequestedPlatform
)
295 // we did find some platform info, but not requested, so return false
296 if ( foundOtherPlatform
)
299 // binary has no explict load command to mark platform
300 // could be an old macOS binary, look at arch
301 if ( reqPlatform
== Platform::macOS
) {
302 if ( this->cputype
== CPU_TYPE_X86_64
)
304 if ( this->cputype
== CPU_TYPE_I386
)
311 Platform
MachOFile::currentPlatform()
314 return Platform::bridgeOS
;
315 #elif TARGET_OS_WATCH
316 return Platform::watchOS
;
318 return Platform::tvOS
;
320 return Platform::iOS
;
322 return Platform::macOS
;
324 #error unknown platform
329 static bool isHaswell()
331 // FIXME: figure out a commpage way to check this
332 static bool sAlreadyDetermined
= false;
333 static bool sHaswell
= false;
334 if ( !sAlreadyDetermined
) {
335 struct host_basic_info info
;
336 mach_msg_type_number_t count
= HOST_BASIC_INFO_COUNT
;
337 mach_port_t hostPort
= mach_host_self();
338 kern_return_t result
= host_info(hostPort
, HOST_BASIC_INFO
, (host_info_t
)&info
, &count
);
339 mach_port_deallocate(mach_task_self(), hostPort
);
340 sHaswell
= (result
== KERN_SUCCESS
) && (info
.cpu_subtype
== CPU_SUBTYPE_X86_64_H
);
341 sAlreadyDetermined
= true;
347 const char* MachOFile::currentArchName()
351 #elif __ARM_ARCH_7A__
353 #elif __ARM_ARCH_7S__
364 return isHaswell() ? "x86_64h" : "x86_64";
373 bool MachOFile::isDylib() const
375 return (this->filetype
== MH_DYLIB
);
378 bool MachOFile::isBundle() const
380 return (this->filetype
== MH_BUNDLE
);
383 bool MachOFile::isMainExecutable() const
385 return (this->filetype
== MH_EXECUTE
);
388 bool MachOFile::isDynamicExecutable() const
390 if ( this->filetype
!= MH_EXECUTE
)
393 // static executables do not have dyld load command
394 __block
bool hasDyldLoad
= false;
396 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
397 if ( cmd
->cmd
== LC_LOAD_DYLINKER
) {
405 bool MachOFile::isPIE() const
407 return (this->flags
& MH_PIE
);
410 const char* MachOFile::platformName(Platform reqPlatform
)
412 for (const PlatformInfo
& info
: _s_platformInfos
) {
413 if ( info
.platform
== reqPlatform
)
416 return "unknown platform";
419 void MachOFile::forEachSupportedPlatform(void (^handler
)(Platform platform
, uint32_t minOS
, uint32_t sdk
)) const
422 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
423 const build_version_command
* buildCmd
= (build_version_command
*)cmd
;
424 const version_min_command
* versCmd
= (version_min_command
*)cmd
;
425 switch ( cmd
->cmd
) {
426 case LC_BUILD_VERSION
:
427 handler((Platform
)(buildCmd
->platform
), buildCmd
->minos
, buildCmd
->sdk
);
429 case LC_VERSION_MIN_MACOSX
:
430 handler(Platform::macOS
, versCmd
->version
, versCmd
->sdk
);
432 case LC_VERSION_MIN_IPHONEOS
:
433 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) )
434 handler(Platform::iOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
436 handler(Platform::iOS
, versCmd
->version
, versCmd
->sdk
);
438 case LC_VERSION_MIN_TVOS
:
439 if ( this->cputype
== CPU_TYPE_X86_64
)
440 handler(Platform::tvOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
442 handler(Platform::tvOS
, versCmd
->version
, versCmd
->sdk
);
444 case LC_VERSION_MIN_WATCHOS
:
445 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) )
446 handler(Platform::watchOS_simulator
, versCmd
->version
, versCmd
->sdk
); // old sim binary
448 handler(Platform::watchOS
, versCmd
->version
, versCmd
->sdk
);
452 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
456 bool MachOFile::isMachO(Diagnostics
& diag
, uint64_t fileSize
) const
458 if ( !hasMachOMagic() ) {
459 diag
.error("file does not start with MH_MAGIC[_64]");
462 if ( this->sizeofcmds
+ sizeof(mach_header_64
) > fileSize
) {
463 diag
.error("load commands exceed length of first segment");
466 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { });
467 return diag
.noError();
470 bool MachOFile::hasMachOMagic() const
472 return ( (this->magic
== MH_MAGIC
) || (this->magic
== MH_MAGIC_64
) );
475 void MachOFile::forEachLoadCommand(Diagnostics
& diag
, void (^callback
)(const load_command
* cmd
, bool& stop
)) const
478 const load_command
* startCmds
= nullptr;
479 if ( this->magic
== MH_MAGIC_64
)
480 startCmds
= (load_command
*)((char *)this + sizeof(mach_header_64
));
481 else if ( this->magic
== MH_MAGIC
)
482 startCmds
= (load_command
*)((char *)this + sizeof(mach_header
));
484 diag
.error("file does not start with MH_MAGIC[_64]");
485 return; // not a mach-o file
487 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ this->sizeofcmds
);
488 const load_command
* cmd
= startCmds
;
489 for (uint32_t i
= 0; i
< this->ncmds
; ++i
) {
490 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
491 if ( cmd
->cmdsize
< 8 ) {
492 diag
.error("malformed load command #%d, size too small %d", i
, cmd
->cmdsize
);
495 if ( (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
496 diag
.error("malformed load command #%d, size too large 0x%X", i
, cmd
->cmdsize
);
506 const char* MachOFile::installName() const
509 uint32_t compatVersion
;
510 uint32_t currentVersion
;
511 if ( getDylibInstallName(&name
, &compatVersion
, ¤tVersion
) )
516 bool MachOFile::getDylibInstallName(const char** installName
, uint32_t* compatVersion
, uint32_t* currentVersion
) const
519 __block
bool found
= false;
520 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
521 if ( cmd
->cmd
== LC_ID_DYLIB
) {
522 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
523 *compatVersion
= dylibCmd
->dylib
.compatibility_version
;
524 *currentVersion
= dylibCmd
->dylib
.current_version
;
525 *installName
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
530 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
534 bool MachOFile::getUuid(uuid_t uuid
) const
537 __block
bool found
= false;
538 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
539 if ( cmd
->cmd
== LC_UUID
) {
540 const uuid_command
* uc
= (const uuid_command
*)cmd
;
541 memcpy(uuid
, uc
->uuid
, sizeof(uuid_t
));
546 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
548 bzero(uuid
, sizeof(uuid_t
));
552 void MachOFile::forEachDependentDylib(void (^callback
)(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
)) const
555 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
556 switch ( cmd
->cmd
) {
558 case LC_LOAD_WEAK_DYLIB
:
559 case LC_REEXPORT_DYLIB
:
560 case LC_LOAD_UPWARD_DYLIB
: {
561 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
562 const char* loadPath
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
563 callback(loadPath
, (cmd
->cmd
== LC_LOAD_WEAK_DYLIB
), (cmd
->cmd
== LC_REEXPORT_DYLIB
), (cmd
->cmd
== LC_LOAD_UPWARD_DYLIB
),
564 dylibCmd
->dylib
.compatibility_version
, dylibCmd
->dylib
.current_version
, stop
);
569 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
572 void MachOFile::forDyldEnv(void (^callback
)(const char* envVar
, bool& stop
)) const
575 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
576 if ( cmd
->cmd
== LC_DYLD_ENVIRONMENT
) {
577 const dylinker_command
* envCmd
= (dylinker_command
*)cmd
;
578 const char* keyEqualsValue
= (char*)envCmd
+ envCmd
->name
.offset
;
579 // only process variables that start with DYLD_ and end in _PATH
580 if ( (strncmp(keyEqualsValue
, "DYLD_", 5) == 0) ) {
581 const char* equals
= strchr(keyEqualsValue
, '=');
582 if ( equals
!= NULL
) {
583 if ( strncmp(&equals
[-5], "_PATH", 5) == 0 ) {
584 callback(keyEqualsValue
, stop
);
590 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
593 bool MachOFile::enforceCompatVersion() const
595 __block
bool result
= true;
596 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
597 switch ( platform
) {
598 case Platform::macOS
:
599 if ( minOS
>= 0x000A0E00 ) // macOS 10.14
604 case Platform::iOS_simulator
:
605 case Platform::tvOS_simulator
:
606 if ( minOS
>= 0x000C0000 ) // iOS 12.0
609 case Platform::watchOS
:
610 case Platform::watchOS_simulator
:
611 if ( minOS
>= 0x00050000 ) // watchOS 5.0
614 case Platform::bridgeOS
:
615 if ( minOS
>= 0x00030000 ) // bridgeOS 3.0
618 case Platform::iOSMac
:
621 case Platform::unknown
:
629 void MachOFile::forEachSegment(void (^callback
)(const SegmentInfo
& info
, bool& stop
)) const
632 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
633 __block
uint32_t segIndex
= 0;
634 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
635 if ( cmd
->cmd
== LC_SEGMENT_64
) {
636 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
637 uint64_t sizeOfSections
= segCmd
->vmsize
;
639 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
640 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
641 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
642 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
643 if ( sect
->align
> p2align
)
644 p2align
= sect
->align
;
647 info
.fileOffset
= segCmd
->fileoff
;
648 info
.fileSize
= segCmd
->filesize
;
649 info
.vmAddr
= segCmd
->vmaddr
;
650 info
.vmSize
= segCmd
->vmsize
;
651 info
.sizeOfSections
= sizeOfSections
;
652 info
.segName
= segCmd
->segname
;
653 info
.protections
= segCmd
->initprot
;
654 info
.textRelocs
= false;
655 info
.p2align
= p2align
;
656 info
.segIndex
= segIndex
;
657 callback(info
, stop
);
660 else if ( cmd
->cmd
== LC_SEGMENT
) {
661 const segment_command
* segCmd
= (segment_command
*)cmd
;
662 uint64_t sizeOfSections
= segCmd
->vmsize
;
664 bool hasTextRelocs
= false;
665 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
666 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
667 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
668 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
669 if ( sect
->align
> p2align
)
670 p2align
= sect
->align
;
671 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
672 hasTextRelocs
= true;
675 info
.fileOffset
= segCmd
->fileoff
;
676 info
.fileSize
= segCmd
->filesize
;
677 info
.vmAddr
= segCmd
->vmaddr
;
678 info
.vmSize
= segCmd
->vmsize
;
679 info
.sizeOfSections
= sizeOfSections
;
680 info
.segName
= segCmd
->segname
;
681 info
.protections
= segCmd
->initprot
;
682 info
.textRelocs
= intel32
&& !info
.writable() && hasTextRelocs
;
683 info
.p2align
= p2align
;
684 info
.segIndex
= segIndex
;
685 callback(info
, stop
);
689 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
692 void MachOFile::forEachSection(void (^callback
)(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
)) const
695 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
696 __block
uint32_t segIndex
= 0;
697 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
698 SectionInfo sectInfo
;
699 if ( cmd
->cmd
== LC_SEGMENT_64
) {
700 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
701 uint64_t sizeOfSections
= segCmd
->vmsize
;
703 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
704 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
705 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
706 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
707 if ( sect
->align
> p2align
)
708 p2align
= sect
->align
;
710 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
711 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
712 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
713 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
714 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
715 sectInfo
.segInfo
.segName
= segCmd
->segname
;
716 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
717 sectInfo
.segInfo
.textRelocs
= false;
718 sectInfo
.segInfo
.p2align
= p2align
;
719 sectInfo
.segInfo
.segIndex
= segIndex
;
720 for (const section_64
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
721 const char* sectName
= sect
->sectname
;
722 char sectNameCopy
[20];
723 if ( sectName
[15] != '\0' ) {
724 strlcpy(sectNameCopy
, sectName
, 17);
725 sectName
= sectNameCopy
;
727 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
728 sectInfo
.sectName
= sectName
;
729 sectInfo
.sectFileOffset
= sect
->offset
;
730 sectInfo
.sectFlags
= sect
->flags
;
731 sectInfo
.sectAddr
= sect
->addr
;
732 sectInfo
.sectSize
= sect
->size
;
733 sectInfo
.sectAlignP2
= sect
->align
;
734 sectInfo
.reserved1
= sect
->reserved1
;
735 sectInfo
.reserved2
= sect
->reserved2
;
736 callback(sectInfo
, malformedSectionRange
, stop
);
740 else if ( cmd
->cmd
== LC_SEGMENT
) {
741 const segment_command
* segCmd
= (segment_command
*)cmd
;
742 uint64_t sizeOfSections
= segCmd
->vmsize
;
744 bool hasTextRelocs
= false;
745 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
746 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
747 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
748 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
749 if ( sect
->align
> p2align
)
750 p2align
= sect
->align
;
751 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
752 hasTextRelocs
= true;
754 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
755 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
756 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
757 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
758 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
759 sectInfo
.segInfo
.segName
= segCmd
->segname
;
760 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
761 sectInfo
.segInfo
.textRelocs
= intel32
&& !sectInfo
.segInfo
.writable() && hasTextRelocs
;
762 sectInfo
.segInfo
.p2align
= p2align
;
763 sectInfo
.segInfo
.segIndex
= segIndex
;
764 for (const section
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
765 const char* sectName
= sect
->sectname
;
766 char sectNameCopy
[20];
767 if ( sectName
[15] != '\0' ) {
768 strlcpy(sectNameCopy
, sectName
, 17);
769 sectName
= sectNameCopy
;
771 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
772 sectInfo
.sectName
= sectName
;
773 sectInfo
.sectFileOffset
= sect
->offset
;
774 sectInfo
.sectFlags
= sect
->flags
;
775 sectInfo
.sectAddr
= sect
->addr
;
776 sectInfo
.sectSize
= sect
->size
;
777 sectInfo
.sectAlignP2
= sect
->align
;
778 sectInfo
.reserved1
= sect
->reserved1
;
779 sectInfo
.reserved2
= sect
->reserved2
;
780 callback(sectInfo
, malformedSectionRange
, stop
);
785 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
788 bool MachOFile::hasWeakDefs() const
790 return (this->flags
& MH_WEAK_DEFINES
);
793 bool MachOFile::hasThreadLocalVariables() const
795 return (this->flags
& MH_HAS_TLV_DESCRIPTORS
);
798 static bool endsWith(const char* str
, const char* suffix
)
800 size_t strLen
= strlen(str
);
801 size_t suffixLen
= strlen(suffix
);
802 if ( strLen
< suffixLen
)
804 return (strcmp(&str
[strLen
-suffixLen
], suffix
) == 0);
807 bool MachOFile::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const
809 // only dylibs can go in cache
810 if ( this->filetype
!= MH_DYLIB
) {
811 failureReason("Not MH_DYLIB");
812 return false; // cannot continue, installName() will assert() if not a dylib
815 // only dylibs built for /usr/lib or /System/Library can go in cache
817 const char* dylibName
= installName();
818 if ( dylibName
[0] != '/' ) {
820 failureReason("install name not an absolute path");
822 else if ( (strncmp(dylibName
, "/usr/lib/", 9) != 0) && (strncmp(dylibName
, "/System/Library/", 16) != 0) ) {
824 failureReason("Not in '/usr/lib/' or '/System/Library/'");
827 // flat namespace files cannot go in cache
828 if ( (this->flags
& MH_TWOLEVEL
) == 0 ) {
830 failureReason("Not built with two level namespaces");
833 // don't put debug variants into dyld cache
834 if ( endsWith(path
, "_profile.dylib") || endsWith(path
, "_debug.dylib") || endsWith(path
, "_profile") || endsWith(path
, "_debug") || endsWith(path
, "/CoreADI") ) {
836 failureReason("Variant image");
839 // dylib must have extra info for moving DATA and TEXT segments apart
840 __block
bool hasExtraInfo
= false;
841 __block
bool hasDyldInfo
= false;
843 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
844 if ( cmd
->cmd
== LC_SEGMENT_SPLIT_INFO
)
846 if ( cmd
->cmd
== LC_DYLD_INFO_ONLY
)
849 if ( !hasExtraInfo
) {
851 failureReason("Missing split seg info");
853 if ( !hasDyldInfo
) {
855 failureReason("Old binary, missing dyld info");
858 // dylib can only depend on other dylibs in the shared cache
859 __block
bool allDepPathsAreGood
= true;
860 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
861 if ( (strncmp(loadPath
, "/usr/lib/", 9) != 0) && (strncmp(loadPath
, "/System/Library/", 16) != 0) ) {
862 allDepPathsAreGood
= false;
866 if ( !allDepPathsAreGood
) {
868 failureReason("Depends on dylibs ineligable for dyld cache");
871 // dylibs with interposing info cannot be in cache
872 __block
bool hasInterposing
= false;
873 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
874 if ( ((info
.sectFlags
& SECTION_TYPE
) == S_INTERPOSING
) || ((strcmp(info
.sectName
, "__interpose") == 0) && (strcmp(info
.segInfo
.segName
, "__DATA") == 0)) )
875 hasInterposing
= true;
877 if ( hasInterposing
) {
879 failureReason("Has interposing tuples");
886 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset
, uint32_t& size
) const
888 if ( const encryption_info_command
* encCmd
= findFairPlayEncryptionLoadCommand() ) {
889 if ( encCmd
->cryptid
== 1 ) {
890 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
891 textOffset
= encCmd
->cryptoff
;
892 size
= encCmd
->cryptsize
;
901 bool MachOFile::canBeFairPlayEncrypted() const
903 return (findFairPlayEncryptionLoadCommand() != nullptr);
906 const encryption_info_command
* MachOFile::findFairPlayEncryptionLoadCommand() const
908 __block
const encryption_info_command
* result
= nullptr;
910 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
911 if ( (cmd
->cmd
== LC_ENCRYPTION_INFO
) || (cmd
->cmd
== LC_ENCRYPTION_INFO_64
) ) {
912 result
= (encryption_info_command
*)cmd
;
916 if ( diag
.noError() )
923 bool MachOFile::hasChainedFixups() const
925 #if SUPPORT_ARCH_arm64e
926 // for now only arm64e uses chained fixups
927 return ( strcmp(archName(), "arm64e") == 0 );
933 uint64_t MachOFile::read_uleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
939 diag
.error("malformed uleb128");
942 uint64_t slice
= *p
& 0x7f;
945 diag
.error("uleb128 too big for uint64");
949 result
|= (slice
<< bit
);
958 int64_t MachOFile::read_sleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
965 diag
.error("malformed sleb128");
969 result
|= (((int64_t)(byte
& 0x7f)) << bit
);
971 } while (byte
& 0x80);
972 // sign extend negative numbers
973 if ( (byte
& 0x40) != 0 )
974 result
|= (~0ULL) << bit
;