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 const uint32_t* h
= (uint32_t*)this;
485 diag
.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h
[0], h
[1]);
486 return; // not a mach-o file
488 const load_command
* const cmdsEnd
= (load_command
*)((char*)startCmds
+ this->sizeofcmds
);
489 const load_command
* cmd
= startCmds
;
490 for (uint32_t i
= 0; i
< this->ncmds
; ++i
) {
491 const load_command
* nextCmd
= (load_command
*)((char *)cmd
+ cmd
->cmdsize
);
492 if ( cmd
->cmdsize
< 8 ) {
493 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
);
496 if ( (nextCmd
> cmdsEnd
) || (nextCmd
< startCmds
) ) {
497 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
);
507 const char* MachOFile::installName() const
510 uint32_t compatVersion
;
511 uint32_t currentVersion
;
512 if ( getDylibInstallName(&name
, &compatVersion
, ¤tVersion
) )
517 bool MachOFile::getDylibInstallName(const char** installName
, uint32_t* compatVersion
, uint32_t* currentVersion
) const
520 __block
bool found
= false;
521 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
522 if ( cmd
->cmd
== LC_ID_DYLIB
) {
523 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
524 *compatVersion
= dylibCmd
->dylib
.compatibility_version
;
525 *currentVersion
= dylibCmd
->dylib
.current_version
;
526 *installName
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
531 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
535 bool MachOFile::getUuid(uuid_t uuid
) const
538 __block
bool found
= false;
539 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
540 if ( cmd
->cmd
== LC_UUID
) {
541 const uuid_command
* uc
= (const uuid_command
*)cmd
;
542 memcpy(uuid
, uc
->uuid
, sizeof(uuid_t
));
547 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
549 bzero(uuid
, sizeof(uuid_t
));
553 void MachOFile::forEachDependentDylib(void (^callback
)(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
)) const
556 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
557 switch ( cmd
->cmd
) {
559 case LC_LOAD_WEAK_DYLIB
:
560 case LC_REEXPORT_DYLIB
:
561 case LC_LOAD_UPWARD_DYLIB
: {
562 const dylib_command
* dylibCmd
= (dylib_command
*)cmd
;
563 const char* loadPath
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
564 callback(loadPath
, (cmd
->cmd
== LC_LOAD_WEAK_DYLIB
), (cmd
->cmd
== LC_REEXPORT_DYLIB
), (cmd
->cmd
== LC_LOAD_UPWARD_DYLIB
),
565 dylibCmd
->dylib
.compatibility_version
, dylibCmd
->dylib
.current_version
, stop
);
570 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
573 void MachOFile::forDyldEnv(void (^callback
)(const char* envVar
, bool& stop
)) const
576 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
577 if ( cmd
->cmd
== LC_DYLD_ENVIRONMENT
) {
578 const dylinker_command
* envCmd
= (dylinker_command
*)cmd
;
579 const char* keyEqualsValue
= (char*)envCmd
+ envCmd
->name
.offset
;
580 // only process variables that start with DYLD_ and end in _PATH
581 if ( (strncmp(keyEqualsValue
, "DYLD_", 5) == 0) ) {
582 const char* equals
= strchr(keyEqualsValue
, '=');
583 if ( equals
!= NULL
) {
584 if ( strncmp(&equals
[-5], "_PATH", 5) == 0 ) {
585 callback(keyEqualsValue
, stop
);
591 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
594 bool MachOFile::enforceCompatVersion() const
596 __block
bool result
= true;
597 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
598 switch ( platform
) {
599 case Platform::macOS
:
600 if ( minOS
>= 0x000A0E00 ) // macOS 10.14
605 case Platform::iOS_simulator
:
606 case Platform::tvOS_simulator
:
607 if ( minOS
>= 0x000C0000 ) // iOS 12.0
610 case Platform::watchOS
:
611 case Platform::watchOS_simulator
:
612 if ( minOS
>= 0x00050000 ) // watchOS 5.0
615 case Platform::bridgeOS
:
616 if ( minOS
>= 0x00030000 ) // bridgeOS 3.0
619 case Platform::iOSMac
:
622 case Platform::unknown
:
630 void MachOFile::forEachSegment(void (^callback
)(const SegmentInfo
& info
, bool& stop
)) const
633 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
634 __block
uint32_t segIndex
= 0;
635 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
636 if ( cmd
->cmd
== LC_SEGMENT_64
) {
637 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
638 uint64_t sizeOfSections
= segCmd
->vmsize
;
640 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
641 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
642 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
643 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
644 if ( sect
->align
> p2align
)
645 p2align
= sect
->align
;
648 info
.fileOffset
= segCmd
->fileoff
;
649 info
.fileSize
= segCmd
->filesize
;
650 info
.vmAddr
= segCmd
->vmaddr
;
651 info
.vmSize
= segCmd
->vmsize
;
652 info
.sizeOfSections
= sizeOfSections
;
653 info
.segName
= segCmd
->segname
;
654 info
.protections
= segCmd
->initprot
;
655 info
.textRelocs
= false;
656 info
.p2align
= p2align
;
657 info
.segIndex
= segIndex
;
658 callback(info
, stop
);
661 else if ( cmd
->cmd
== LC_SEGMENT
) {
662 const segment_command
* segCmd
= (segment_command
*)cmd
;
663 uint64_t sizeOfSections
= segCmd
->vmsize
;
665 bool hasTextRelocs
= false;
666 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
667 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
668 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
669 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
670 if ( sect
->align
> p2align
)
671 p2align
= sect
->align
;
672 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
673 hasTextRelocs
= true;
676 info
.fileOffset
= segCmd
->fileoff
;
677 info
.fileSize
= segCmd
->filesize
;
678 info
.vmAddr
= segCmd
->vmaddr
;
679 info
.vmSize
= segCmd
->vmsize
;
680 info
.sizeOfSections
= sizeOfSections
;
681 info
.segName
= segCmd
->segname
;
682 info
.protections
= segCmd
->initprot
;
683 info
.textRelocs
= intel32
&& !info
.writable() && hasTextRelocs
;
684 info
.p2align
= p2align
;
685 info
.segIndex
= segIndex
;
686 callback(info
, stop
);
690 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
693 void MachOFile::forEachSection(void (^callback
)(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
)) const
696 const bool intel32
= (this->cputype
== CPU_TYPE_I386
);
697 __block
uint32_t segIndex
= 0;
698 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
699 SectionInfo sectInfo
;
700 if ( cmd
->cmd
== LC_SEGMENT_64
) {
701 const segment_command_64
* segCmd
= (segment_command_64
*)cmd
;
702 uint64_t sizeOfSections
= segCmd
->vmsize
;
704 const section_64
* const sectionsStart
= (section_64
*)((char*)segCmd
+ sizeof(struct segment_command_64
));
705 const section_64
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
706 for (const section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
707 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
708 if ( sect
->align
> p2align
)
709 p2align
= sect
->align
;
711 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
712 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
713 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
714 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
715 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
716 sectInfo
.segInfo
.segName
= segCmd
->segname
;
717 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
718 sectInfo
.segInfo
.textRelocs
= false;
719 sectInfo
.segInfo
.p2align
= p2align
;
720 sectInfo
.segInfo
.segIndex
= segIndex
;
721 for (const section_64
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
722 const char* sectName
= sect
->sectname
;
723 char sectNameCopy
[20];
724 if ( sectName
[15] != '\0' ) {
725 strlcpy(sectNameCopy
, sectName
, 17);
726 sectName
= sectNameCopy
;
728 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
729 sectInfo
.sectName
= sectName
;
730 sectInfo
.sectFileOffset
= sect
->offset
;
731 sectInfo
.sectFlags
= sect
->flags
;
732 sectInfo
.sectAddr
= sect
->addr
;
733 sectInfo
.sectSize
= sect
->size
;
734 sectInfo
.sectAlignP2
= sect
->align
;
735 sectInfo
.reserved1
= sect
->reserved1
;
736 sectInfo
.reserved2
= sect
->reserved2
;
737 callback(sectInfo
, malformedSectionRange
, stop
);
741 else if ( cmd
->cmd
== LC_SEGMENT
) {
742 const segment_command
* segCmd
= (segment_command
*)cmd
;
743 uint64_t sizeOfSections
= segCmd
->vmsize
;
745 bool hasTextRelocs
= false;
746 const section
* const sectionsStart
= (section
*)((char*)segCmd
+ sizeof(struct segment_command
));
747 const section
* const sectionsEnd
= §ionsStart
[segCmd
->nsects
];
748 for (const section
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
749 sizeOfSections
= sect
->addr
+ sect
->size
- segCmd
->vmaddr
;
750 if ( sect
->align
> p2align
)
751 p2align
= sect
->align
;
752 if ( sect
->flags
& (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
) )
753 hasTextRelocs
= true;
755 sectInfo
.segInfo
.fileOffset
= segCmd
->fileoff
;
756 sectInfo
.segInfo
.fileSize
= segCmd
->filesize
;
757 sectInfo
.segInfo
.vmAddr
= segCmd
->vmaddr
;
758 sectInfo
.segInfo
.vmSize
= segCmd
->vmsize
;
759 sectInfo
.segInfo
.sizeOfSections
= sizeOfSections
;
760 sectInfo
.segInfo
.segName
= segCmd
->segname
;
761 sectInfo
.segInfo
.protections
= segCmd
->initprot
;
762 sectInfo
.segInfo
.textRelocs
= intel32
&& !sectInfo
.segInfo
.writable() && hasTextRelocs
;
763 sectInfo
.segInfo
.p2align
= p2align
;
764 sectInfo
.segInfo
.segIndex
= segIndex
;
765 for (const section
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
766 const char* sectName
= sect
->sectname
;
767 char sectNameCopy
[20];
768 if ( sectName
[15] != '\0' ) {
769 strlcpy(sectNameCopy
, sectName
, 17);
770 sectName
= sectNameCopy
;
772 bool malformedSectionRange
= (sect
->addr
< segCmd
->vmaddr
) || greaterThanAddOrOverflow(sect
->addr
, sect
->size
, segCmd
->vmaddr
+ segCmd
->filesize
);
773 sectInfo
.sectName
= sectName
;
774 sectInfo
.sectFileOffset
= sect
->offset
;
775 sectInfo
.sectFlags
= sect
->flags
;
776 sectInfo
.sectAddr
= sect
->addr
;
777 sectInfo
.sectSize
= sect
->size
;
778 sectInfo
.sectAlignP2
= sect
->align
;
779 sectInfo
.reserved1
= sect
->reserved1
;
780 sectInfo
.reserved2
= sect
->reserved2
;
781 callback(sectInfo
, malformedSectionRange
, stop
);
786 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
789 bool MachOFile::hasWeakDefs() const
791 return (this->flags
& MH_WEAK_DEFINES
);
794 bool MachOFile::hasThreadLocalVariables() const
796 return (this->flags
& MH_HAS_TLV_DESCRIPTORS
);
799 static bool endsWith(const char* str
, const char* suffix
)
801 size_t strLen
= strlen(str
);
802 size_t suffixLen
= strlen(suffix
);
803 if ( strLen
< suffixLen
)
805 return (strcmp(&str
[strLen
-suffixLen
], suffix
) == 0);
808 bool MachOFile::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const
810 // only dylibs can go in cache
811 if ( this->filetype
!= MH_DYLIB
) {
812 failureReason("Not MH_DYLIB");
813 return false; // cannot continue, installName() will assert() if not a dylib
816 // only dylibs built for /usr/lib or /System/Library can go in cache
818 const char* dylibName
= installName();
819 if ( dylibName
[0] != '/' ) {
821 failureReason("install name not an absolute path");
823 else if ( (strncmp(dylibName
, "/usr/lib/", 9) != 0) && (strncmp(dylibName
, "/System/Library/", 16) != 0) ) {
825 failureReason("Not in '/usr/lib/' or '/System/Library/'");
828 // flat namespace files cannot go in cache
829 if ( (this->flags
& MH_TWOLEVEL
) == 0 ) {
831 failureReason("Not built with two level namespaces");
834 // don't put debug variants into dyld cache
835 if ( endsWith(path
, "_profile.dylib") || endsWith(path
, "_debug.dylib") || endsWith(path
, "_profile") || endsWith(path
, "_debug") || endsWith(path
, "/CoreADI") ) {
837 failureReason("Variant image");
840 // dylib must have extra info for moving DATA and TEXT segments apart
841 __block
bool hasExtraInfo
= false;
842 __block
bool hasDyldInfo
= false;
844 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
845 if ( cmd
->cmd
== LC_SEGMENT_SPLIT_INFO
)
847 if ( cmd
->cmd
== LC_DYLD_INFO_ONLY
)
850 if ( !hasExtraInfo
) {
852 failureReason("Missing split seg info");
854 if ( !hasDyldInfo
) {
856 failureReason("Old binary, missing dyld info");
859 // dylib can only depend on other dylibs in the shared cache
860 __block
bool allDepPathsAreGood
= true;
861 forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
862 if ( (strncmp(loadPath
, "/usr/lib/", 9) != 0) && (strncmp(loadPath
, "/System/Library/", 16) != 0) ) {
863 allDepPathsAreGood
= false;
867 if ( !allDepPathsAreGood
) {
869 failureReason("Depends on dylibs ineligable for dyld cache");
872 // dylibs with interposing info cannot be in cache
873 __block
bool hasInterposing
= false;
874 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
875 if ( ((info
.sectFlags
& SECTION_TYPE
) == S_INTERPOSING
) || ((strcmp(info
.sectName
, "__interpose") == 0) && (strcmp(info
.segInfo
.segName
, "__DATA") == 0)) )
876 hasInterposing
= true;
878 if ( hasInterposing
) {
880 failureReason("Has interposing tuples");
887 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset
, uint32_t& size
) const
889 if ( const encryption_info_command
* encCmd
= findFairPlayEncryptionLoadCommand() ) {
890 if ( encCmd
->cryptid
== 1 ) {
891 // Note: cryptid is 0 in just-built apps. The AppStore sets cryptid to 1
892 textOffset
= encCmd
->cryptoff
;
893 size
= encCmd
->cryptsize
;
902 bool MachOFile::canBeFairPlayEncrypted() const
904 return (findFairPlayEncryptionLoadCommand() != nullptr);
907 const encryption_info_command
* MachOFile::findFairPlayEncryptionLoadCommand() const
909 __block
const encryption_info_command
* result
= nullptr;
911 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
912 if ( (cmd
->cmd
== LC_ENCRYPTION_INFO
) || (cmd
->cmd
== LC_ENCRYPTION_INFO_64
) ) {
913 result
= (encryption_info_command
*)cmd
;
917 if ( diag
.noError() )
924 bool MachOFile::hasChainedFixups() const
926 #if SUPPORT_ARCH_arm64e
927 // for now only arm64e uses chained fixups
928 return ( strcmp(archName(), "arm64e") == 0 );
934 uint64_t MachOFile::read_uleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
940 diag
.error("malformed uleb128");
943 uint64_t slice
= *p
& 0x7f;
946 diag
.error("uleb128 too big for uint64");
950 result
|= (slice
<< bit
);
959 int64_t MachOFile::read_sleb128(Diagnostics
& diag
, const uint8_t*& p
, const uint8_t* end
)
966 diag
.error("malformed sleb128");
970 result
|= (((int64_t)(byte
& 0x7f)) << bit
);
972 } while (byte
& 0x80);
973 // sign extend negative numbers
974 if ( (byte
& 0x40) != 0 )
975 result
|= (~0ULL) << bit
;