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@
24 #include <sys/types.h>
26 #include <sys/errno.h>
27 #include <sys/fcntl.h>
29 #include <mach/mach.h>
35 #include <mach-o/reloc.h>
36 #include <mach-o/x86_64/reloc.h>
37 #include <mach-o/nlist.h>
38 #include <TargetConditionals.h>
40 #include "MachOAnalyzer.h"
41 #include "CodeSigningTypes.h"
44 // FIXME: We should get this from cctools
45 #define DYLD_CACHE_ADJ_V2_FORMAT 0x7F
50 const MachOAnalyzer
* MachOAnalyzer::validMainExecutable(Diagnostics
& diag
, const mach_header
* mh
, const char* path
, uint64_t sliceLength
,
51 const GradedArchs
& archs
, Platform platform
)
53 const MachOAnalyzer
* result
= (const MachOAnalyzer
*)mh
;
54 if ( !result
->validMachOForArchAndPlatform(diag
, (size_t)sliceLength
, path
, archs
, platform
, true) )
56 if ( !result
->isDynamicExecutable() )
62 bool MachOAnalyzer::loadFromBuffer(Diagnostics
& diag
, const closure::FileSystem
& fileSystem
,
63 const char* path
, const GradedArchs
& archs
, Platform platform
,
64 closure::LoadedFileInfo
& info
)
66 // if fat, remap just slice needed
67 bool fatButMissingSlice
;
68 const FatFile
* fh
= (FatFile
*)info
.fileContent
;
69 uint64_t sliceOffset
= info
.sliceOffset
;
70 uint64_t sliceLen
= info
.sliceLen
;
71 if ( fh
->isFatFileWithSlice(diag
, info
.fileContentLen
, archs
, info
.isOSBinary
, sliceOffset
, sliceLen
, fatButMissingSlice
) ) {
72 // unmap anything before slice
73 fileSystem
.unloadPartialFile(info
, sliceOffset
, sliceLen
);
74 // Update the info to keep track of the new slice offset.
75 info
.sliceOffset
= sliceOffset
;
76 info
.sliceLen
= sliceLen
;
78 else if ( diag
.hasError() ) {
79 // We must have generated an error in the fat file parsing so use that error
80 fileSystem
.unloadFile(info
);
83 else if ( fatButMissingSlice
) {
84 diag
.error("missing compatible arch in %s", path
);
85 fileSystem
.unloadFile(info
);
89 const MachOAnalyzer
* mh
= (MachOAnalyzer
*)info
.fileContent
;
91 // validate is mach-o of requested arch and platform
92 if ( !mh
->validMachOForArchAndPlatform(diag
, (size_t)info
.sliceLen
, path
, archs
, platform
, info
.isOSBinary
) ) {
93 fileSystem
.unloadFile(info
);
97 // if has zero-fill expansion, re-map
98 mh
= mh
->remapIfZeroFill(diag
, fileSystem
, info
);
100 // on error, remove mappings and return nullptr
101 if ( diag
.hasError() ) {
102 fileSystem
.unloadFile(info
);
106 // now that LINKEDIT is at expected offset, finish validation
107 mh
->validLinkedit(diag
, path
);
109 // on error, remove mappings and return nullptr
110 if ( diag
.hasError() ) {
111 fileSystem
.unloadFile(info
);
119 closure::LoadedFileInfo
MachOAnalyzer::load(Diagnostics
& diag
, const closure::FileSystem
& fileSystem
,
120 const char* path
, const GradedArchs
& archs
, Platform platform
, char realerPath
[MAXPATHLEN
])
122 // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong
123 // above us and we should quickly return instead of doing unnecessary work.
125 return closure::LoadedFileInfo();
127 closure::LoadedFileInfo info
;
128 if (!fileSystem
.loadFile(path
, info
, realerPath
, ^(const char *format
, ...) {
130 va_start(list
, format
);
131 diag
.error(format
, list
);
134 return closure::LoadedFileInfo();
137 // If we now have an error, but succeeded, then we must have tried multiple paths, one of which errored, but
138 // then succeeded on a later path. So clear the error.
142 bool loaded
= loadFromBuffer(diag
, fileSystem
, path
, archs
, platform
, info
);
148 // for use with already mmap()ed file
149 bool MachOAnalyzer::isOSBinary(int fd
, uint64_t sliceOffset
, uint64_t sliceSize
) const
157 if ( !this->hasCodeSignature(sigOffset
, sigSize
) )
160 // register code signature
161 fsignatures_t sigreg
;
162 sigreg
.fs_file_start
= sliceOffset
; // start of mach-o slice in fat file
163 sigreg
.fs_blob_start
= (void*)(long)sigOffset
; // start of CD in mach-o file
164 sigreg
.fs_blob_size
= sigSize
; // size of CD
165 if ( ::fcntl(fd
, F_ADDFILESIGS_RETURN
, &sigreg
) == -1 )
168 // ask if code signature is for something in the OS
169 fgetsigsinfo siginfo
= { (off_t
)sliceOffset
, GETSIGSINFO_PLATFORM_BINARY
, 0 };
170 if ( ::fcntl(fd
, F_GETSIGSINFO
, &siginfo
) == -1 )
173 return (siginfo
.fg_sig_is_platform
);
179 // for use when just the fat_header has been read
180 bool MachOAnalyzer::sliceIsOSBinary(int fd
, uint64_t sliceOffset
, uint64_t sliceSize
)
185 // need to mmap() slice so we can find the code signature
186 void* mappedSlice
= ::mmap(nullptr, sliceSize
, PROT_READ
, MAP_PRIVATE
, fd
, sliceOffset
);
187 if ( mappedSlice
== MAP_FAILED
)
190 const MachOAnalyzer
* ma
= (MachOAnalyzer
*)mappedSlice
;
191 bool result
= ma
->isOSBinary(fd
, sliceOffset
, sliceSize
);
192 ::munmap(mappedSlice
, sliceSize
);
200 // only used in debug builds of cache builder to verify segment moves are valid
201 void MachOAnalyzer::validateDyldCacheDylib(Diagnostics
& diag
, const char* path
) const
203 validLinkedit(diag
, path
);
204 validSegments(diag
, path
, 0xffffffff);
208 uint64_t MachOAnalyzer::mappedSize() const
212 analyzeSegmentsLayout(vmSpace
, hasZeroFill
);
216 bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics
& diag
, size_t sliceLength
, const char* path
, const GradedArchs
& archs
, Platform reqPlatform
, bool isOSBinary
) const
218 // must start with mach-o magic value
219 if ( (this->magic
!= MH_MAGIC
) && (this->magic
!= MH_MAGIC_64
) ) {
220 diag
.error("could not use '%s' because it is not a mach-o file: 0x%08X 0x%08X", path
, this->magic
, this->cputype
);
224 if ( !archs
.grade(this->cputype
, this->cpusubtype
, isOSBinary
) ) {
225 diag
.error("could not use '%s' because it is not a compatible arch", path
);
229 // must be a filetype dyld can load
230 switch ( this->filetype
) {
235 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
236 // Allow offline tools to analyze binaries dyld doesn't load
243 diag
.error("could not use '%s' because it is not a dylib, bundle, or executable, filetype=0x%08X", path
, this->filetype
);
247 // validate load commands structure
248 if ( !this->validLoadCommands(diag
, path
, sliceLength
) ) {
252 // filter out static executables
253 if ( (this->filetype
== MH_EXECUTE
) && !isDynamicExecutable() ) {
254 #if !BUILDING_DYLDINFO && !BUILDING_APP_CACHE_UTIL
255 // dyldinfo should be able to inspect static executables such as the kernel
256 diag
.error("could not use '%s' because it is a static executable", path
);
261 // HACK: If we are asking for no platform, then make sure the binary doesn't have one
262 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
264 // A statically linked kernel collection should contain a 0 platform
265 __block
bool foundPlatform
= false;
266 __block
bool foundBadPlatform
= false;
267 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
268 foundPlatform
= true;
269 if ( platform
!= Platform::unknown
) {
270 foundBadPlatform
= true;
273 if (!foundPlatform
) {
274 diag
.error("could not use '%s' because we expected it to have a platform", path
);
277 if (foundBadPlatform
) {
278 diag
.error("could not use '%s' because is has the wrong platform", path
);
281 } else if ( reqPlatform
== Platform::unknown
) {
282 // Unfortunately the static kernel has a platform, but kext's don't, so we can't
283 // verify the platform of the kernel.
284 if ( !isStaticExecutable() ) {
285 __block
bool foundPlatform
= false;
286 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
287 foundPlatform
= true;
290 diag
.error("could not use '%s' because we expected it to have no platform", path
);
296 if ( !this->loadableIntoProcess(reqPlatform
, path
) ) {
297 diag
.error("could not use '%s' because it was not built for platform %s", path
, MachOFile::platformName(reqPlatform
));
301 // validate dylib loads
302 if ( !validEmbeddedPaths(diag
, reqPlatform
, path
) )
306 if ( !validSegments(diag
, path
, sliceLength
) )
310 if ( this->filetype
== MH_EXECUTE
) {
311 if ( !validMain(diag
, path
) )
315 // further validations done in validLinkedit()
320 bool MachOAnalyzer::validLinkedit(Diagnostics
& diag
, const char* path
) const
322 // validate LINKEDIT layout
323 if ( !validLinkeditLayout(diag
, path
) )
326 if ( hasLoadCommand(LC_DYLD_CHAINED_FIXUPS
) ) {
327 if ( !validChainedFixupsInfo(diag
, path
) )
330 #if SUPPORT_ARCH_arm64e
331 else if ( (this->cputype
== CPU_TYPE_ARM64
) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E
) ) {
332 if ( !validChainedFixupsInfoOldArm64e(diag
, path
) )
337 // validate rebasing info
338 if ( !validRebaseInfo(diag
, path
) )
341 // validate binding info
342 if ( !validBindInfo(diag
, path
) )
349 bool MachOAnalyzer::validLoadCommands(Diagnostics
& diag
, const char* path
, size_t fileLen
) const
351 // check load command don't exceed file length
352 if ( this->sizeofcmds
+ machHeaderSize() > fileLen
) {
353 diag
.error("in '%s' load commands exceed length of file", path
);
357 // walk all load commands and sanity check them
358 Diagnostics walkDiag
;
359 forEachLoadCommand(walkDiag
, ^(const load_command
* cmd
, bool& stop
) {});
360 if ( walkDiag
.hasError() ) {
361 #if BUILDING_CACHE_BUILDER
362 diag
.error("in '%s' %s", path
, walkDiag
.errorMessage().c_str());
364 diag
.error("in '%s' %s", path
, walkDiag
.errorMessage());
369 // check load commands fit in TEXT segment
370 __block
bool foundTEXT
= false;
371 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
372 if ( strcmp(info
.segName
, "__TEXT") == 0 ) {
374 if ( this->sizeofcmds
+ machHeaderSize() > info
.fileSize
) {
375 diag
.error("in '%s' load commands exceed length of __TEXT segment", path
);
377 if ( info
.fileOffset
!= 0 ) {
378 diag
.error("in '%s' __TEXT segment not start of mach-o", path
);
383 if ( !diag
.noError() && !foundTEXT
) {
384 diag
.error("in '%s' __TEXT segment not found", path
);
391 const MachOAnalyzer
* MachOAnalyzer::remapIfZeroFill(Diagnostics
& diag
, const closure::FileSystem
& fileSystem
, closure::LoadedFileInfo
& info
) const
393 uint64_t vmSpaceRequired
;
395 analyzeSegmentsLayout(vmSpaceRequired
, hasZeroFill
);
398 vm_address_t newMappedAddr
;
399 if ( ::vm_allocate(mach_task_self(), &newMappedAddr
, (size_t)vmSpaceRequired
, VM_FLAGS_ANYWHERE
) != 0 ) {
400 diag
.error("vm_allocate failure");
404 // re-map each segment read-only, with runtime layout
405 #if BUILDING_APP_CACHE_UTIL
406 // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
407 __block
uint64_t baseAddress
= ~0ULL;
408 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
409 baseAddress
= std::min(baseAddress
, info
.vmAddr
);
411 uint64_t textSegVMAddr
= preferredLoadAddress();
413 uint64_t baseAddress
= preferredLoadAddress();
416 forEachSegment(^(const SegmentInfo
& segmentInfo
, bool& stop
) {
417 if ( (segmentInfo
.fileSize
!= 0) && (segmentInfo
.vmSize
!= 0) ) {
418 kern_return_t r
= vm_copy(mach_task_self(), (vm_address_t
)((long)info
.fileContent
+segmentInfo
.fileOffset
), (vm_size_t
)segmentInfo
.fileSize
, (vm_address_t
)(newMappedAddr
+segmentInfo
.vmAddr
-baseAddress
));
419 if ( r
!= KERN_SUCCESS
) {
420 diag
.error("vm_copy() failure");
425 if ( diag
.noError() ) {
426 // remove original mapping and return new mapping
427 fileSystem
.unloadFile(info
);
429 // make the new mapping read-only
430 ::vm_protect(mach_task_self(), newMappedAddr
, (vm_size_t
)vmSpaceRequired
, false, VM_PROT_READ
);
432 #if BUILDING_APP_CACHE_UTIL
433 if ( textSegVMAddr
!= baseAddress
) {
434 info
.unload
= [](const closure::LoadedFileInfo
& info
) {
435 // Unloading binaries where __DATA is first requires working out the real range of the binary
436 // The fileContent points at the mach_header, not the actaul start of the file content, unfortunately.
437 const MachOAnalyzer
* ma
= (const MachOAnalyzer
*)info
.fileContent
;
438 __block
uint64_t baseAddress
= ~0ULL;
439 ma
->forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
440 baseAddress
= std::min(baseAddress
, info
.vmAddr
);
442 uint64_t textSegVMAddr
= ma
->preferredLoadAddress();
444 uint64_t basePointerOffset
= textSegVMAddr
- baseAddress
;
445 uint8_t* bufferStart
= (uint8_t*)info
.fileContent
- basePointerOffset
;
446 ::vm_deallocate(mach_task_self(), (vm_address_t
)bufferStart
, (size_t)info
.fileContentLen
);
449 // And update the file content to the new location
450 info
.fileContent
= (const void*)(newMappedAddr
+ textSegVMAddr
- baseAddress
);
451 info
.fileContentLen
= vmSpaceRequired
;
452 return (const MachOAnalyzer
*)info
.fileContent
;
456 // Set vm_deallocate as the unload method.
457 info
.unload
= [](const closure::LoadedFileInfo
& info
) {
458 ::vm_deallocate(mach_task_self(), (vm_address_t
)info
.fileContent
, (size_t)info
.fileContentLen
);
461 // And update the file content to the new location
462 info
.fileContent
= (const void*)newMappedAddr
;
463 info
.fileContentLen
= vmSpaceRequired
;
464 return (const MachOAnalyzer
*)info
.fileContent
;
467 // new mapping failed, return old mapping with an error in diag
468 ::vm_deallocate(mach_task_self(), newMappedAddr
, (size_t)vmSpaceRequired
);
476 void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace
, bool& hasZeroFill
) const
478 __block
bool writeExpansion
= false;
479 __block
uint64_t lowestVmAddr
= 0xFFFFFFFFFFFFFFFFULL
;
480 __block
uint64_t highestVmAddr
= 0;
481 __block
uint64_t sumVmSizes
= 0;
482 forEachSegment(^(const SegmentInfo
& segmentInfo
, bool& stop
) {
483 if ( strcmp(segmentInfo
.segName
, "__PAGEZERO") == 0 )
485 if ( segmentInfo
.writable() && (segmentInfo
.fileSize
!= segmentInfo
.vmSize
) )
486 writeExpansion
= true; // zerofill at end of __DATA
487 if ( segmentInfo
.vmSize
== 0 ) {
488 // Always zero fill if we have zero-sized segments
489 writeExpansion
= true;
491 if ( segmentInfo
.vmAddr
< lowestVmAddr
)
492 lowestVmAddr
= segmentInfo
.vmAddr
;
493 if ( segmentInfo
.vmAddr
+segmentInfo
.vmSize
> highestVmAddr
)
494 highestVmAddr
= segmentInfo
.vmAddr
+segmentInfo
.vmSize
;
495 sumVmSizes
+= segmentInfo
.vmSize
;
497 uint64_t totalVmSpace
= (highestVmAddr
- lowestVmAddr
);
498 // LINKEDIT vmSize is not required to be a multiple of page size. Round up if that is the case
499 const uint64_t pageSize
= uses16KPages() ? 0x4000 : 0x1000;
500 totalVmSpace
= (totalVmSpace
+ (pageSize
- 1)) & ~(pageSize
- 1);
501 bool hasHole
= (totalVmSpace
!= sumVmSizes
); // segments not contiguous
503 // The aux KC may have __DATA first, in which case we always want to vm_copy to the right place
504 bool hasOutOfOrderSegments
= false;
505 #if BUILDING_APP_CACHE_UTIL
506 uint64_t textSegVMAddr
= preferredLoadAddress();
507 hasOutOfOrderSegments
= textSegVMAddr
!= lowestVmAddr
;
510 vmSpace
= totalVmSpace
;
511 hasZeroFill
= writeExpansion
|| hasHole
|| hasOutOfOrderSegments
;
514 bool MachOAnalyzer::enforceFormat(Malformed kind
) const
516 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
517 // HACK: If we are the kernel, we have a different format to enforce
521 case Malformed::linkeditOrder
:
522 case Malformed::linkeditAlignment
:
523 case Malformed::dyldInfoAndlocalRelocs
:
526 case Malformed::segmentOrder
:
527 // The aux KC has __DATA first
530 case Malformed::linkeditPermissions
:
531 case Malformed::executableData
:
532 case Malformed::writableData
:
533 case Malformed::codeSigAlignment
:
534 case Malformed::sectionsAddrRangeWithinSegment
:
537 case Malformed::textPermissions
:
538 // The kernel has its own __TEXT_EXEC for executable memory
545 if ( isStaticExecutable() ) {
548 case Malformed::linkeditOrder
:
549 case Malformed::linkeditAlignment
:
550 case Malformed::dyldInfoAndlocalRelocs
:
553 case Malformed::segmentOrder
:
556 case Malformed::linkeditPermissions
:
557 case Malformed::executableData
:
558 case Malformed::codeSigAlignment
:
559 case Malformed::textPermissions
:
560 case Malformed::sectionsAddrRangeWithinSegment
:
563 case Malformed::writableData
:
564 // The kernel has __DATA_CONST marked as r/o
573 __block
bool result
= false;
574 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
576 case Platform::macOS
:
578 case Malformed::linkeditOrder
:
579 case Malformed::linkeditAlignment
:
580 case Malformed::dyldInfoAndlocalRelocs
:
581 // enforce these checks on new binaries only
582 if (sdk
>= 0x000A0E00) // macOS 10.14
585 case Malformed::segmentOrder
:
586 case Malformed::linkeditPermissions
:
587 case Malformed::textPermissions
:
588 case Malformed::executableData
:
589 case Malformed::writableData
:
590 case Malformed::codeSigAlignment
:
591 // enforce these checks on new binaries only
592 if (sdk
>= 0x000A0F00) // macOS 10.15
595 case Malformed::sectionsAddrRangeWithinSegment
:
596 // enforce these checks on new binaries only
597 if (sdk
>= 0x000A1000) // macOS 10.16
604 case Malformed::linkeditOrder
:
605 case Malformed::dyldInfoAndlocalRelocs
:
606 case Malformed::textPermissions
:
607 case Malformed::executableData
:
608 case Malformed::writableData
:
611 case Malformed::linkeditAlignment
:
612 case Malformed::segmentOrder
:
613 case Malformed::linkeditPermissions
:
614 case Malformed::codeSigAlignment
:
615 // enforce these checks on new binaries only
616 if (sdk
>= 0x000D0000) // iOS 13
619 case Malformed::sectionsAddrRangeWithinSegment
:
620 // enforce these checks on new binaries only
621 if (sdk
>= 0x000E0000) // iOS 14
631 // if binary is so old, there is no platform info, don't enforce malformed errors
635 bool MachOAnalyzer::validEmbeddedPaths(Diagnostics
& diag
, Platform platform
, const char* path
) const
637 __block
int index
= 1;
638 __block
bool allGood
= true;
639 __block
bool foundInstallName
= false;
640 __block
int dependentsCount
= 0;
641 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
642 const dylib_command
* dylibCmd
;
643 const rpath_command
* rpathCmd
;
644 switch ( cmd
->cmd
) {
646 foundInstallName
= true;
648 [[clang::fallthrough]];
650 case LC_LOAD_WEAK_DYLIB
:
651 case LC_REEXPORT_DYLIB
:
652 case LC_LOAD_UPWARD_DYLIB
:
653 dylibCmd
= (dylib_command
*)cmd
;
654 if ( dylibCmd
->dylib
.name
.offset
> cmd
->cmdsize
) {
655 diag
.error("in '%s' load command #%d name offset (%u) outside its size (%u)", path
, index
, dylibCmd
->dylib
.name
.offset
, cmd
->cmdsize
);
660 bool foundEnd
= false;
661 const char* start
= (char*)dylibCmd
+ dylibCmd
->dylib
.name
.offset
;
662 const char* end
= (char*)dylibCmd
+ cmd
->cmdsize
;
663 for (const char* s
=start
; s
< end
; ++s
) {
670 diag
.error("in '%s' load command #%d string extends beyond end of load command", path
, index
);
675 if ( cmd
->cmd
!= LC_ID_DYLIB
)
679 rpathCmd
= (rpath_command
*)cmd
;
680 if ( rpathCmd
->path
.offset
> cmd
->cmdsize
) {
681 diag
.error("in '%s' load command #%d path offset (%u) outside its size (%u)", path
, index
, rpathCmd
->path
.offset
, cmd
->cmdsize
);
686 bool foundEnd
= false;
687 const char* start
= (char*)rpathCmd
+ rpathCmd
->path
.offset
;
688 const char* end
= (char*)rpathCmd
+ cmd
->cmdsize
;
689 for (const char* s
=start
; s
< end
; ++s
) {
696 diag
.error("in '%s' load command #%d string extends beyond end of load command", path
, index
);
708 if ( this->filetype
== MH_DYLIB
) {
709 if ( !foundInstallName
) {
710 diag
.error("in '%s' MH_DYLIB is missing LC_ID_DYLIB", path
);
715 if ( foundInstallName
) {
716 diag
.error("in '%s' LC_ID_DYLIB found in non-MH_DYLIB", path
);
721 if ( (dependentsCount
== 0) && (this->filetype
== MH_EXECUTE
) && isDynamicExecutable() ) {
722 diag
.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path
);
729 bool MachOAnalyzer::validSegments(Diagnostics
& diag
, const char* path
, size_t fileLen
) const
731 // check segment load command size
732 __block
bool badSegmentLoadCommand
= false;
733 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
734 if ( cmd
->cmd
== LC_SEGMENT_64
) {
735 const segment_command_64
* seg
= (segment_command_64
*)cmd
;
736 int32_t sectionsSpace
= cmd
->cmdsize
- sizeof(segment_command_64
);
737 if ( sectionsSpace
< 0 ) {
738 diag
.error("in '%s' load command size too small for LC_SEGMENT_64", path
);
739 badSegmentLoadCommand
= true;
742 else if ( (sectionsSpace
% sizeof(section_64
)) != 0 ) {
743 diag
.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path
, cmd
->cmdsize
);
744 badSegmentLoadCommand
= true;
747 else if ( sectionsSpace
!= (seg
->nsects
* sizeof(section_64
)) ) {
748 diag
.error("in '%s' load command size 0x%X does not match nsects %d", path
, cmd
->cmdsize
, seg
->nsects
);
749 badSegmentLoadCommand
= true;
752 else if ( greaterThanAddOrOverflow(seg
->fileoff
, seg
->filesize
, fileLen
) ) {
753 diag
.error("in '%s' segment load command content extends beyond end of file", path
);
754 badSegmentLoadCommand
= true;
757 else if ( (seg
->filesize
> seg
->vmsize
) && ((seg
->vmsize
!= 0) || ((seg
->flags
& SG_NORELOC
) == 0)) ) {
758 // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
759 diag
.error("in '%s' segment filesize exceeds vmsize", path
);
760 badSegmentLoadCommand
= true;
764 else if ( cmd
->cmd
== LC_SEGMENT
) {
765 const segment_command
* seg
= (segment_command
*)cmd
;
766 int32_t sectionsSpace
= cmd
->cmdsize
- sizeof(segment_command
);
767 if ( sectionsSpace
< 0 ) {
768 diag
.error("in '%s' load command size too small for LC_SEGMENT", path
);
769 badSegmentLoadCommand
= true;
772 else if ( (sectionsSpace
% sizeof(section
)) != 0 ) {
773 diag
.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path
, cmd
->cmdsize
);
774 badSegmentLoadCommand
= true;
777 else if ( sectionsSpace
!= (seg
->nsects
* sizeof(section
)) ) {
778 diag
.error("in '%s' load command size 0x%X does not match nsects %d", path
, cmd
->cmdsize
, seg
->nsects
);
779 badSegmentLoadCommand
= true;
782 else if ( (seg
->filesize
> seg
->vmsize
) && ((seg
->vmsize
!= 0) || ((seg
->flags
& SG_NORELOC
) == 0)) ) {
783 // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
784 diag
.error("in '%s' segment filesize exceeds vmsize", path
);
785 badSegmentLoadCommand
= true;
790 if ( badSegmentLoadCommand
)
793 // check mapping permissions of segments
794 __block
bool badPermissions
= false;
795 __block
bool badSize
= false;
796 __block
bool hasTEXT
= false;
797 __block
bool hasLINKEDIT
= false;
798 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
799 if ( strcmp(info
.segName
, "__TEXT") == 0 ) {
800 if ( (info
.protections
!= (VM_PROT_READ
|VM_PROT_EXECUTE
)) && enforceFormat(Malformed::textPermissions
) ) {
801 diag
.error("in '%s' __TEXT segment permissions is not 'r-x'", path
);
802 badPermissions
= true;
807 else if ( strcmp(info
.segName
, "__LINKEDIT") == 0 ) {
808 if ( (info
.protections
!= VM_PROT_READ
) && enforceFormat(Malformed::linkeditPermissions
) ) {
809 diag
.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path
);
810 badPermissions
= true;
815 else if ( (info
.protections
& 0xFFFFFFF8) != 0 ) {
816 diag
.error("in '%s' %s segment permissions has invalid bits set", path
, info
.segName
);
817 badPermissions
= true;
820 if ( greaterThanAddOrOverflow(info
.fileOffset
, info
.fileSize
, fileLen
) ) {
821 diag
.error("in '%s' %s segment content extends beyond end of file", path
, info
.segName
);
826 if ( info
.vmAddr
+info
.vmSize
< info
.vmAddr
) {
827 diag
.error("in '%s' %s segment vm range wraps", path
, info
.segName
);
833 if ( (uint32_t)(info
.vmAddr
+info
.vmSize
) < (uint32_t)(info
.vmAddr
) ) {
834 diag
.error("in '%s' %s segment vm range wraps", path
, info
.segName
);
840 if ( badPermissions
|| badSize
)
843 diag
.error("in '%s' missing __TEXT segment", path
);
846 if ( !hasLINKEDIT
) {
847 diag
.error("in '%s' missing __LINKEDIT segment", path
);
851 // check for overlapping segments
852 __block
bool badSegments
= false;
853 forEachSegment(^(const SegmentInfo
& info1
, bool& stop1
) {
854 uint64_t seg1vmEnd
= info1
.vmAddr
+ info1
.vmSize
;
855 uint64_t seg1FileEnd
= info1
.fileOffset
+ info1
.fileSize
;
856 forEachSegment(^(const SegmentInfo
& info2
, bool& stop2
) {
857 if ( info1
.segIndex
== info2
.segIndex
)
859 uint64_t seg2vmEnd
= info2
.vmAddr
+ info2
.vmSize
;
860 uint64_t seg2FileEnd
= info2
.fileOffset
+ info2
.fileSize
;
861 if ( ((info2
.vmAddr
<= info1
.vmAddr
) && (seg2vmEnd
> info1
.vmAddr
) && (seg1vmEnd
> info1
.vmAddr
)) || ((info2
.vmAddr
>= info1
.vmAddr
) && (info2
.vmAddr
< seg1vmEnd
) && (seg2vmEnd
> info2
.vmAddr
)) ) {
862 diag
.error("in '%s' segment %s vm range overlaps segment %s", path
, info1
.segName
, info2
.segName
);
867 if ( ((info2
.fileOffset
<= info1
.fileOffset
) && (seg2FileEnd
> info1
.fileOffset
) && (seg1FileEnd
> info1
.fileOffset
)) || ((info2
.fileOffset
>= info1
.fileOffset
) && (info2
.fileOffset
< seg1FileEnd
) && (seg2FileEnd
> info2
.fileOffset
)) ) {
868 diag
.error("in '%s' segment %s file content overlaps segment %s", path
, info1
.segName
, info2
.segName
);
873 if ( (info1
.segIndex
< info2
.segIndex
) && !stop1
) {
874 if ( (info1
.vmAddr
> info2
.vmAddr
) || ((info1
.fileOffset
> info2
.fileOffset
) && (info1
.fileOffset
!= 0) && (info2
.fileOffset
!= 0)) ){
875 if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder
) && !isStaticExecutable() ) {
876 // dyld cache __DATA_* segments are moved around
877 // The static kernel also has segments with vmAddr's before __TEXT
878 diag
.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path
, info1
.segName
, info2
.segName
);
890 // check sections are within segment
891 __block
bool badSections
= false;
892 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
893 if ( cmd
->cmd
== LC_SEGMENT_64
) {
894 const segment_command_64
* seg
= (segment_command_64
*)cmd
;
895 const section_64
* const sectionsStart
= (section_64
*)((char*)seg
+ sizeof(struct segment_command_64
));
896 const section_64
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
897 for (const section_64
* sect
=sectionsStart
; (sect
< sectionsEnd
); ++sect
) {
898 if ( (int64_t)(sect
->size
) < 0 ) {
899 diag
.error("in '%s' section '%s' size too large 0x%llX", path
, sect
->sectname
, sect
->size
);
902 else if ( sect
->addr
< seg
->vmaddr
) {
903 diag
.error("in '%s' section '%s' start address 0x%llX is before containing segment's address 0x%0llX", path
, sect
->sectname
, sect
->addr
, seg
->vmaddr
);
906 else if ( sect
->addr
+sect
->size
> seg
->vmaddr
+seg
->vmsize
) {
907 bool ignoreError
= !enforceFormat(Malformed::sectionsAddrRangeWithinSegment
);
908 #if BUILDING_APP_CACHE_UTIL
909 if ( (seg
->vmsize
== 0) && !strcmp(seg
->segname
, "__CTF") )
912 if ( !ignoreError
) {
913 diag
.error("in '%s' section '%s' end address 0x%llX is beyond containing segment's end address 0x%0llX", path
, sect
->sectname
, sect
->addr
+sect
->size
, seg
->vmaddr
+seg
->vmsize
);
919 else if ( cmd
->cmd
== LC_SEGMENT
) {
920 const segment_command
* seg
= (segment_command
*)cmd
;
921 const section
* const sectionsStart
= (section
*)((char*)seg
+ sizeof(struct segment_command
));
922 const section
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
923 for (const section
* sect
=sectionsStart
; !stop
&& (sect
< sectionsEnd
); ++sect
) {
924 if ( (int64_t)(sect
->size
) < 0 ) {
925 diag
.error("in '%s' section %s size too large 0x%X", path
, sect
->sectname
, sect
->size
);
928 else if ( sect
->addr
< seg
->vmaddr
) {
929 diag
.error("in '%s' section %s start address 0x%X is before containing segment's address 0x%0X", path
, sect
->sectname
, sect
->addr
, seg
->vmaddr
);
932 else if ( sect
->addr
+sect
->size
> seg
->vmaddr
+seg
->vmsize
) {
933 diag
.error("in '%s' section %s end address 0x%X is beyond containing segment's end address 0x%0X", path
, sect
->sectname
, sect
->addr
+sect
->size
, seg
->vmaddr
+seg
->vmsize
);
944 bool MachOAnalyzer::validMain(Diagnostics
& diag
, const char* path
) const
946 const char* executableTextSegmentName
= "__TEXT";
947 #if BUILDING_APP_CACHE_UTIL
948 // The kernel has __start in __TEXT_EXEC, or for x86_64 it's __HIB
949 if ( isStaticExecutable() ) {
950 if ( isArch("x86_64") || isArch("x86_64h") )
951 executableTextSegmentName
= "__HIB";
953 executableTextSegmentName
= "__TEXT_EXEC";
957 __block
uint64_t textSegStartAddr
= 0;
958 __block
uint64_t textSegStartSize
= 0;
959 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
960 if ( strcmp(info
.segName
, executableTextSegmentName
) == 0 ) {
961 textSegStartAddr
= info
.vmAddr
;
962 textSegStartSize
= info
.vmSize
;
967 __block
int mainCount
= 0;
968 __block
int threadCount
= 0;
969 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
970 entry_point_command
* mainCmd
;
971 uint64_t startAddress
;
975 mainCmd
= (entry_point_command
*)cmd
;
976 if ( mainCmd
->entryoff
>= textSegStartSize
) {
977 startAddress
= preferredLoadAddress() + mainCmd
->entryoff
;
978 __block
bool foundSegment
= false;
979 forEachSegment(^(const SegmentInfo
& info
, bool& stopSegment
) {
980 // Skip segments which don't contain this address
981 if ( (startAddress
< info
.vmAddr
) || (startAddress
>= info
.vmAddr
+info
.vmSize
) )
984 if ( (info
.protections
& VM_PROT_EXECUTE
) == 0 )
985 diag
.error("LC_MAIN points to non-executable segment");
989 diag
.error("LC_MAIN entryoff is out of range");
995 startAddress
= entryAddrFromThreadCmd((thread_command
*)cmd
);
996 if ( startAddress
== 0 ) {
997 diag
.error("LC_UNIXTHREAD not valid for arch %s", archName());
1000 #if BUILDING_DYLDINFO
1001 else if ( isStaticExecutable() ) {
1002 __block
bool foundSegment
= false;
1003 forEachSegment(^(const SegmentInfo
& info
, bool& stopSegment
) {
1004 // Skip segments which don't contain this address
1005 if ( (startAddress
< info
.vmAddr
) || (startAddress
>= info
.vmAddr
+info
.vmSize
) )
1007 foundSegment
= true;
1008 if ( (info
.protections
& VM_PROT_EXECUTE
) == 0 )
1009 diag
.error("LC_UNIXTHREAD points to non-executable segment");
1013 diag
.error("LC_UNIXTHREAD entry is out of range");
1017 else if ( (startAddress
< textSegStartAddr
) || (startAddress
>= textSegStartAddr
+textSegStartSize
) ) {
1018 diag
.error("LC_UNIXTHREAD entry not in %s segment", executableTextSegmentName
);
1024 if ( diag
.hasError() )
1027 if ( this->builtForPlatform(Platform::driverKit
) ) {
1028 if ( mainCount
+ threadCount
== 0 )
1030 diag
.error("no LC_MAIN allowed for driverkit");
1034 if ( mainCount
+threadCount
== 1 )
1037 if ( mainCount
+ threadCount
== 0 )
1038 diag
.error("missing LC_MAIN or LC_UNIXTHREAD");
1040 diag
.error("only one LC_MAIN or LC_UNIXTHREAD is allowed");
1046 struct LinkEditContentChunk
1050 uint32_t fileOffsetStart
;
1053 static int compareByFileOffset(const void* l
, const void* r
) {
1054 if ( ((LinkEditContentChunk
*)l
)->fileOffsetStart
< ((LinkEditContentChunk
*)r
)->fileOffsetStart
)
1060 } // anonymous namespace
1064 bool MachOAnalyzer::validLinkeditLayout(Diagnostics
& diag
, const char* path
) const
1066 LinkEditInfo leInfo
;
1067 getLinkEditPointers(diag
, leInfo
);
1068 if ( diag
.hasError() )
1070 const uint32_t ptrSize
= pointerSize();
1072 // build vector of all blobs in LINKEDIT
1073 LinkEditContentChunk blobs
[32];
1074 LinkEditContentChunk
* bp
= blobs
;
1075 if ( leInfo
.dyldInfo
!= nullptr ) {
1076 if ( leInfo
.dyldInfo
->rebase_size
!= 0 )
1077 *bp
++ = {"rebase opcodes", ptrSize
, leInfo
.dyldInfo
->rebase_off
, leInfo
.dyldInfo
->rebase_size
};
1078 if ( leInfo
.dyldInfo
->bind_size
!= 0 )
1079 *bp
++ = {"bind opcodes", ptrSize
, leInfo
.dyldInfo
->bind_off
, leInfo
.dyldInfo
->bind_size
};
1080 if ( leInfo
.dyldInfo
->weak_bind_size
!= 0 )
1081 *bp
++ = {"weak bind opcodes", ptrSize
, leInfo
.dyldInfo
->weak_bind_off
, leInfo
.dyldInfo
->weak_bind_size
};
1082 if ( leInfo
.dyldInfo
->lazy_bind_size
!= 0 )
1083 *bp
++ = {"lazy bind opcodes", ptrSize
, leInfo
.dyldInfo
->lazy_bind_off
, leInfo
.dyldInfo
->lazy_bind_size
};
1084 if ( leInfo
.dyldInfo
->export_size
!= 0 )
1085 *bp
++ = {"exports trie", ptrSize
, leInfo
.dyldInfo
->export_off
, leInfo
.dyldInfo
->export_size
};
1087 if ( leInfo
.exportsTrie
!= nullptr ) {
1088 if ( leInfo
.exportsTrie
->datasize
!= 0 )
1089 *bp
++ = {"exports trie", ptrSize
, leInfo
.exportsTrie
->dataoff
, leInfo
.exportsTrie
->datasize
};
1091 if ( leInfo
.chainedFixups
!= nullptr ) {
1092 if ( leInfo
.chainedFixups
->datasize
!= 0 )
1093 *bp
++ = {"chained fixups", ptrSize
, leInfo
.chainedFixups
->dataoff
, leInfo
.chainedFixups
->datasize
};
1096 if ( leInfo
.dynSymTab
!= nullptr ) {
1097 if ( leInfo
.dynSymTab
->nlocrel
!= 0 )
1098 *bp
++ = {"local relocations", ptrSize
, leInfo
.dynSymTab
->locreloff
, static_cast<uint32_t>(leInfo
.dynSymTab
->nlocrel
*sizeof(relocation_info
))};
1099 if ( leInfo
.dynSymTab
->nextrel
!= 0 )
1100 *bp
++ = {"external relocations", ptrSize
, leInfo
.dynSymTab
->extreloff
, static_cast<uint32_t>(leInfo
.dynSymTab
->nextrel
*sizeof(relocation_info
))};
1101 if ( leInfo
.dynSymTab
->nindirectsyms
!= 0 )
1102 *bp
++ = {"indirect symbol table", 4, leInfo
.dynSymTab
->indirectsymoff
, leInfo
.dynSymTab
->nindirectsyms
*4};
1104 if ( leInfo
.splitSegInfo
!= nullptr ) {
1105 if ( leInfo
.splitSegInfo
->datasize
!= 0 )
1106 *bp
++ = {"shared cache info", ptrSize
, leInfo
.splitSegInfo
->dataoff
, leInfo
.splitSegInfo
->datasize
};
1108 if ( leInfo
.functionStarts
!= nullptr ) {
1109 if ( leInfo
.functionStarts
->datasize
!= 0 )
1110 *bp
++ = {"function starts", ptrSize
, leInfo
.functionStarts
->dataoff
, leInfo
.functionStarts
->datasize
};
1112 if ( leInfo
.dataInCode
!= nullptr ) {
1113 if ( leInfo
.dataInCode
->datasize
!= 0 )
1114 *bp
++ = {"data in code", ptrSize
, leInfo
.dataInCode
->dataoff
, leInfo
.dataInCode
->datasize
};
1116 if ( leInfo
.symTab
!= nullptr ) {
1117 if ( leInfo
.symTab
->nsyms
!= 0 )
1118 *bp
++ = {"symbol table", ptrSize
, leInfo
.symTab
->symoff
, static_cast<uint32_t>(leInfo
.symTab
->nsyms
*(ptrSize
== 8 ? sizeof(nlist_64
) : sizeof(struct nlist
)))};
1119 if ( leInfo
.symTab
->strsize
!= 0 )
1120 *bp
++ = {"symbol table strings", 1, leInfo
.symTab
->stroff
, leInfo
.symTab
->strsize
};
1122 if ( leInfo
.codeSig
!= nullptr ) {
1123 if ( leInfo
.codeSig
->datasize
!= 0 )
1124 *bp
++ = {"code signature", ptrSize
, leInfo
.codeSig
->dataoff
, leInfo
.codeSig
->datasize
};
1127 // check for bad combinations
1128 if ( (leInfo
.dyldInfo
!= nullptr) && (leInfo
.dyldInfo
->cmd
== LC_DYLD_INFO_ONLY
) && (leInfo
.dynSymTab
!= nullptr) ) {
1129 if ( (leInfo
.dynSymTab
->nlocrel
!= 0) && enforceFormat(Malformed::dyldInfoAndlocalRelocs
) ) {
1130 diag
.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations", path
);
1133 if ( leInfo
.dynSymTab
->nextrel
!= 0 ) {
1134 diag
.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations", path
);
1139 bool checkMissingDyldInfo
= true;
1140 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
1141 checkMissingDyldInfo
= !isFileSet() && !isStaticExecutable() && !isKextBundle();
1143 if ( (leInfo
.dyldInfo
== nullptr) && (leInfo
.dynSymTab
== nullptr) && checkMissingDyldInfo
) {
1144 diag
.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path
);
1148 // FIXME: Remove this hack
1149 #if BUILDING_APP_CACHE_UTIL
1154 const unsigned long blobCount
= bp
- blobs
;
1155 if ( blobCount
== 0 ) {
1156 diag
.error("in '%s' malformed mach-o missing LINKEDIT", path
);
1160 uint32_t linkeditFileEnd
= leInfo
.layout
.linkeditFileOffset
+ leInfo
.layout
.linkeditFileSize
;
1163 // sort blobs by file-offset and error on overlaps
1164 ::qsort(blobs
, blobCount
, sizeof(LinkEditContentChunk
), &LinkEditContentChunk::compareByFileOffset
);
1165 uint32_t prevEnd
= leInfo
.layout
.linkeditFileOffset
;
1166 const char* prevName
= "start of LINKEDIT";
1167 for (unsigned long i
=0; i
< blobCount
; ++i
) {
1168 const LinkEditContentChunk
& blob
= blobs
[i
];
1169 if ( blob
.fileOffsetStart
< prevEnd
) {
1170 diag
.error("in '%s' LINKEDIT overlap of %s and %s", path
, prevName
, blob
.name
);
1173 if (greaterThanAddOrOverflow(blob
.fileOffsetStart
, blob
.size
, linkeditFileEnd
)) {
1174 diag
.error("in '%s' LINKEDIT content '%s' extends beyond end of segment", path
, blob
.name
);
1177 if ( (blob
.fileOffsetStart
& (blob
.alignment
-1)) != 0 ) {
1178 // <rdar://problem/51115705> relax code sig alignment for pre iOS13
1179 Malformed kind
= (strcmp(blob
.name
, "code signature") == 0) ? Malformed::codeSigAlignment
: Malformed::linkeditAlignment
;
1180 if ( enforceFormat(kind
) )
1181 diag
.error("in '%s' mis-aligned LINKEDIT content '%s'", path
, blob
.name
);
1183 prevEnd
= blob
.fileOffsetStart
+ blob
.size
;
1184 prevName
= blob
.name
;
1187 // Check for invalid symbol table sizes
1188 if ( leInfo
.symTab
!= nullptr ) {
1189 if ( leInfo
.symTab
->nsyms
> 0x10000000 ) {
1190 diag
.error("in '%s' malformed mach-o image: symbol table too large", path
);
1193 if ( leInfo
.dynSymTab
!= nullptr ) {
1194 // validate indirect symbol table
1195 if ( leInfo
.dynSymTab
->nindirectsyms
!= 0 ) {
1196 if ( leInfo
.dynSymTab
->nindirectsyms
> 0x10000000 ) {
1197 diag
.error("in '%s' malformed mach-o image: indirect symbol table too large", path
);
1201 if ( (leInfo
.dynSymTab
->nlocalsym
> leInfo
.symTab
->nsyms
) || (leInfo
.dynSymTab
->ilocalsym
> leInfo
.symTab
->nsyms
) ) {
1202 diag
.error("in '%s' malformed mach-o image: indirect symbol table local symbol count exceeds total symbols", path
);
1205 if ( leInfo
.dynSymTab
->ilocalsym
+ leInfo
.dynSymTab
->nlocalsym
< leInfo
.dynSymTab
->ilocalsym
) {
1206 diag
.error("in '%s' malformed mach-o image: indirect symbol table local symbol count wraps", path
);
1209 if ( (leInfo
.dynSymTab
->nextdefsym
> leInfo
.symTab
->nsyms
) || (leInfo
.dynSymTab
->iextdefsym
> leInfo
.symTab
->nsyms
) ) {
1210 diag
.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols", path
);
1213 if ( leInfo
.dynSymTab
->iextdefsym
+ leInfo
.dynSymTab
->nextdefsym
< leInfo
.dynSymTab
->iextdefsym
) {
1214 diag
.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count wraps", path
);
1217 if ( (leInfo
.dynSymTab
->nundefsym
> leInfo
.symTab
->nsyms
) || (leInfo
.dynSymTab
->iundefsym
> leInfo
.symTab
->nsyms
) ) {
1218 diag
.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols", path
);
1221 if ( leInfo
.dynSymTab
->iundefsym
+ leInfo
.dynSymTab
->nundefsym
< leInfo
.dynSymTab
->iundefsym
) {
1222 diag
.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count wraps", path
);
1233 bool MachOAnalyzer::invalidRebaseState(Diagnostics
& diag
, const char* opcodeName
, const char* path
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1234 bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
) const
1236 if ( !segIndexSet
) {
1237 diag
.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path
, opcodeName
);
1240 if ( segmentIndex
>= leInfo
.layout
.linkeditSegIndex
) {
1241 diag
.error("in '%s' %s segment index %d too large", path
, opcodeName
, segmentIndex
);
1244 if ( segmentOffset
> (segments
[segmentIndex
].vmSize
-ptrSize
) ) {
1245 diag
.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path
, opcodeName
, segmentOffset
, segments
[segmentIndex
].vmSize
);
1249 case Rebase::pointer32
:
1250 case Rebase::pointer64
:
1251 if ( !segments
[segmentIndex
].writable() && enforceFormat(Malformed::writableData
) ) {
1252 diag
.error("in '%s' %s pointer rebase is in non-writable segment", path
, opcodeName
);
1255 if ( segments
[segmentIndex
].executable() && enforceFormat(Malformed::executableData
) ) {
1256 diag
.error("in '%s' %s pointer rebase is in executable segment", path
, opcodeName
);
1260 case Rebase::textAbsolute32
:
1261 case Rebase::textPCrel32
:
1262 if ( !segments
[segmentIndex
].textRelocs
) {
1263 diag
.error("in '%s' %s text rebase is in segment that does not support text relocations", path
, opcodeName
);
1266 if ( segments
[segmentIndex
].writable() ) {
1267 diag
.error("in '%s' %s text rebase is in writable segment", path
, opcodeName
);
1270 if ( !segments
[segmentIndex
].executable() ) {
1271 diag
.error("in '%s' %s pointer rebase is in non-executable segment", path
, opcodeName
);
1275 case Rebase::unknown
:
1276 diag
.error("in '%s' %s unknown rebase type", path
, opcodeName
);
1283 void MachOAnalyzer::getAllSegmentsInfos(Diagnostics
& diag
, SegmentInfo segments
[]) const
1285 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
1286 segments
[info
.segIndex
] = info
;
1291 bool MachOAnalyzer::validRebaseInfo(Diagnostics
& diag
, const char* path
) const
1293 forEachRebase(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1294 bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
, bool& stop
) {
1295 if ( invalidRebaseState(diag
, opcodeName
, path
, leInfo
, segments
, segIndexSet
, ptrSize
, segmentIndex
, segmentOffset
, kind
) )
1298 return diag
.noError();
1302 void MachOAnalyzer::forEachTextRebase(Diagnostics
& diag
, void (^handler
)(uint64_t runtimeOffset
, bool& stop
)) const
1304 __block
bool startVmAddrSet
= false;
1305 __block
uint64_t startVmAddr
= 0;
1306 forEachRebase(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1307 bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
, bool& stop
) {
1308 if ( kind
!= Rebase::textAbsolute32
)
1310 if ( !startVmAddrSet
) {
1311 for (int i
=0; i
<= segmentIndex
; ++i
) {
1312 if ( strcmp(segments
[i
].segName
, "__TEXT") == 0 ) {
1313 startVmAddr
= segments
[i
].vmAddr
;
1314 startVmAddrSet
= true;
1319 uint64_t rebaseVmAddr
= segments
[segmentIndex
].vmAddr
+ segmentOffset
;
1320 uint64_t runtimeOffset
= rebaseVmAddr
- startVmAddr
;
1321 handler(runtimeOffset
, stop
);
1325 void MachOAnalyzer::forEachRebase(Diagnostics
& diag
, void (^callback
)(uint64_t runtimeOffset
, bool isLazyPointerRebase
, bool& stop
)) const
1327 __block
bool startVmAddrSet
= false;
1328 __block
uint64_t startVmAddr
= 0;
1329 __block
uint64_t lpVmAddr
= 0;
1330 __block
uint64_t lpEndVmAddr
= 0;
1331 __block
uint64_t shVmAddr
= 0;
1332 __block
uint64_t shEndVmAddr
= 0;
1333 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
1334 if ( (info
.sectFlags
& SECTION_TYPE
) == S_LAZY_SYMBOL_POINTERS
) {
1335 lpVmAddr
= info
.sectAddr
;
1336 lpEndVmAddr
= info
.sectAddr
+ info
.sectSize
;
1338 else if ( (info
.sectFlags
& S_ATTR_PURE_INSTRUCTIONS
) && (strcmp(info
.sectName
, "__stub_helper") == 0) ) {
1339 shVmAddr
= info
.sectAddr
;
1340 shEndVmAddr
= info
.sectAddr
+ info
.sectSize
;
1343 forEachRebase(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1344 bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
, bool& stop
) {
1346 case Rebase::unknown
:
1348 case Rebase::pointer32
:
1349 case Rebase::pointer64
:
1350 // We only handle these kinds for now.
1352 case Rebase::textPCrel32
:
1353 case Rebase::textAbsolute32
:
1356 if ( !startVmAddrSet
) {
1357 for (int i
=0; i
< segmentIndex
; ++i
) {
1358 if ( strcmp(segments
[i
].segName
, "__TEXT") == 0 ) {
1359 startVmAddr
= segments
[i
].vmAddr
;
1360 startVmAddrSet
= true;
1365 uint64_t rebaseVmAddr
= segments
[segmentIndex
].vmAddr
+ segmentOffset
;
1366 bool isLazyPointerRebase
= false;
1367 if ( (rebaseVmAddr
>= lpVmAddr
) && (rebaseVmAddr
< lpEndVmAddr
) ) {
1368 // rebase is in lazy pointer section
1369 uint64_t lpValue
= 0;
1371 lpValue
= *((uint64_t*)(rebaseVmAddr
-startVmAddr
+(uint8_t*)this));
1373 lpValue
= *((uint32_t*)(rebaseVmAddr
-startVmAddr
+(uint8_t*)this));
1374 if ( (lpValue
>= shVmAddr
) && (lpValue
< shEndVmAddr
) ) {
1375 // content is into stub_helper section
1376 uint64_t lpTargetImageOffset
= lpValue
- startVmAddr
;
1377 const uint8_t* helperContent
= (uint8_t*)this + lpTargetImageOffset
;
1378 bool isLazyStub
= contentIsRegularStub(helperContent
);
1379 // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub
1381 isLazyPointerRebase
= true;
1384 // if lazy pointer does not point into stub_helper, then it points to weak-def symbol and we need rebase
1387 uint64_t runtimeOffset
= rebaseVmAddr
- startVmAddr
;
1388 callback(runtimeOffset
, isLazyPointerRebase
, stop
);
1394 void MachOAnalyzer::forEachRebase(Diagnostics
& diag
, bool ignoreLazyPointers
, void (^handler
)(uint64_t runtimeOffset
, bool& stop
)) const
1396 forEachRebase(diag
, ^(uint64_t runtimeOffset
, bool isLazyPointerRebase
, bool& stop
) {
1397 if ( isLazyPointerRebase
&& ignoreLazyPointers
)
1399 handler(runtimeOffset
, stop
);
1403 bool MachOAnalyzer::hasStompedLazyOpcodes() const
1405 // if first eight bytes of lazy opcodes are zeros, then the opcodes have been stomped
1406 bool result
= false;
1408 if ( const uint8_t* p
= (uint8_t*)getLazyBindOpcodes(size
) ) {
1411 memcpy(&content
, p
, 8);
1420 bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent
) const
1422 switch (this->cputype
) {
1423 case CPU_TYPE_X86_64
:
1424 return ( (helperContent
[0] == 0x68) && (helperContent
[5] == 0xE9) ); // push $xxx / JMP pcRel
1426 return ( (helperContent
[0] == 0x68) && (helperContent
[5] == 0xFF) && (helperContent
[2] == 0x26) ); // push $xxx / JMP *pcRel
1428 return ( (helperContent
[0] == 0x00) && (helperContent
[1] == 0xC0) && (helperContent
[2] == 0x9F) && (helperContent
[3] == 0xE5) ); // ldr ip, [pc, #0]
1429 case CPU_TYPE_ARM64
:
1430 return ( (helperContent
[0] == 0x50) && (helperContent
[1] == 0x00) && (helperContent
[2] == 0x00) && (helperContent
[3] == 0x18) ); // ldr w16, L0
1436 static int relocSorter(const void* l
, const void* r
) {
1437 if ( ((relocation_info
*)l
)->r_address
< ((relocation_info
*)r
)->r_address
)
1444 void MachOAnalyzer::forEachRebase(Diagnostics
& diag
,
1445 void (^handler
)(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1446 bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
,
1447 Rebase kind
, bool& stop
)) const
1449 LinkEditInfo leInfo
;
1450 getLinkEditPointers(diag
, leInfo
);
1451 if ( diag
.hasError() )
1454 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1);
1455 getAllSegmentsInfos(diag
, segmentsInfo
);
1456 if ( diag
.hasError() )
1459 const Rebase pointerRebaseKind
= is64() ? Rebase::pointer64
: Rebase::pointer32
;
1461 if ( leInfo
.dyldInfo
!= nullptr ) {
1462 const uint8_t* const start
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->rebase_off
);
1463 const uint8_t* const end
= start
+ leInfo
.dyldInfo
->rebase_size
;
1464 const uint8_t* p
= start
;
1465 const uint32_t ptrSize
= pointerSize();
1466 Rebase kind
= Rebase::unknown
;
1468 uint64_t segOffset
= 0;
1471 bool segIndexSet
= false;
1473 while ( !stop
&& diag
.noError() && (p
< end
) ) {
1474 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1475 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1478 case REBASE_OPCODE_DONE
:
1479 if ( (end
- p
) > 8 )
1480 diag
.error("rebase opcodes terminated early at offset %d of %d", (int)(p
-start
), (int)(end
-start
));
1483 case REBASE_OPCODE_SET_TYPE_IMM
:
1484 switch ( immediate
) {
1485 case REBASE_TYPE_POINTER
:
1486 kind
= pointerRebaseKind
;
1488 case REBASE_TYPE_TEXT_ABSOLUTE32
:
1489 kind
= Rebase::textAbsolute32
;
1491 case REBASE_TYPE_TEXT_PCREL32
:
1492 kind
= Rebase::textPCrel32
;
1495 kind
= Rebase::unknown
;
1499 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1500 segIndex
= immediate
;
1501 segOffset
= read_uleb128(diag
, p
, end
);
1504 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1505 segOffset
+= read_uleb128(diag
, p
, end
);
1507 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1508 segOffset
+= immediate
*ptrSize
;
1510 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1511 for (int i
=0; i
< immediate
; ++i
) {
1512 handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
);
1513 segOffset
+= ptrSize
;
1518 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1519 count
= read_uleb128(diag
, p
, end
);
1520 for (uint32_t i
=0; i
< count
; ++i
) {
1521 handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
);
1522 segOffset
+= ptrSize
;
1527 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1528 handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
);
1529 segOffset
+= read_uleb128(diag
, p
, end
) + ptrSize
;
1531 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1532 count
= read_uleb128(diag
, p
, end
);
1533 if ( diag
.hasError() )
1535 skip
= read_uleb128(diag
, p
, end
);
1536 for (uint32_t i
=0; i
< count
; ++i
) {
1537 handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
);
1538 segOffset
+= skip
+ ptrSize
;
1544 diag
.error("unknown rebase opcode 0x%02X", opcode
);
1550 if ( leInfo
.chainedFixups
!= nullptr ) {
1551 // binary uses chained fixups, so do nothing
1552 // The kernel collections need to support both chained and classic relocations
1553 // If we are anything other than a kernel collection, then return here as we won't have
1554 // anything else to do.
1559 if ( leInfo
.dynSymTab
!= nullptr ) {
1560 // old binary, walk relocations
1561 const uint64_t relocsStartAddress
= localRelocBaseAddress(segmentsInfo
, leInfo
.layout
.linkeditSegIndex
);
1562 const relocation_info
* const relocsStart
= (relocation_info
*)getLinkEditContent(leInfo
.layout
, leInfo
.dynSymTab
->locreloff
);
1563 const relocation_info
* const relocsEnd
= &relocsStart
[leInfo
.dynSymTab
->nlocrel
];
1565 const uint8_t relocSize
= (is64() ? 3 : 2);
1566 const uint8_t ptrSize
= pointerSize();
1567 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(relocation_info
, relocs
, 2048);
1568 for (const relocation_info
* reloc
=relocsStart
; (reloc
< relocsEnd
) && !stop
; ++reloc
) {
1569 if ( reloc
->r_length
!= relocSize
) {
1570 bool shouldEmitError
= true;
1571 #if BUILDING_APP_CACHE_UTIL
1572 if ( usesClassicRelocationsInKernelCollection() && (reloc
->r_length
== 2) && (relocSize
== 3) )
1573 shouldEmitError
= false;
1575 if ( shouldEmitError
) {
1576 diag
.error("local relocation has wrong r_length");
1580 if ( reloc
->r_type
!= 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
1581 diag
.error("local relocation has wrong r_type");
1584 relocs
.push_back(*reloc
);
1586 if ( !relocs
.empty() ) {
1587 ::qsort(&relocs
[0], relocs
.count(), sizeof(relocation_info
), &relocSorter
);
1588 for (relocation_info reloc
: relocs
) {
1589 uint32_t addrOff
= reloc
.r_address
;
1590 uint32_t segIndex
= 0;
1591 uint64_t segOffset
= 0;
1593 #if BUILDING_APP_CACHE_UTIL
1594 // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
1596 if ( isStaticExecutable() || isFileSet() ) {
1597 addr
= relocsStartAddress
+ (int32_t)addrOff
;
1599 addr
= relocsStartAddress
+ addrOff
;
1602 addr
= relocsStartAddress
+ addrOff
;
1604 if ( segIndexAndOffsetForAddress(addr
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) {
1605 Rebase kind
= (reloc
.r_length
== 2) ? Rebase::pointer32
: Rebase::pointer64
;
1606 if ( this->cputype
== CPU_TYPE_I386
) {
1607 if ( segmentsInfo
[segIndex
].executable() )
1608 kind
= Rebase::textAbsolute32
;
1610 handler("local relocation", leInfo
, segmentsInfo
, true, ptrSize
, segIndex
, segOffset
, kind
, stop
);
1613 diag
.error("local relocation has out of range r_address");
1618 // then process indirect symbols
1619 forEachIndirectPointer(diag
, ^(uint64_t address
, bool bind
, int bindLibOrdinal
,
1620 const char* bindSymbolName
, bool bindWeakImport
, bool bindLazy
, bool selfModifyingStub
, bool& indStop
) {
1623 uint32_t segIndex
= 0;
1624 uint64_t segOffset
= 0;
1625 if ( segIndexAndOffsetForAddress(address
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) {
1626 handler("local relocation", leInfo
, segmentsInfo
, true, ptrSize
, segIndex
, segOffset
, pointerRebaseKind
, indStop
);
1629 diag
.error("local relocation has out of range r_address");
1636 bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr
, const SegmentInfo segmentsInfos
[], uint32_t segCount
, uint32_t& segIndex
, uint64_t& segOffset
) const
1638 for (uint32_t i
=0; i
< segCount
; ++i
) {
1639 if ( (segmentsInfos
[i
].vmAddr
<= addr
) && (addr
< segmentsInfos
[i
].vmAddr
+segmentsInfos
[i
].vmSize
) ) {
1641 segOffset
= addr
- segmentsInfos
[i
].vmAddr
;
1648 uint64_t MachOAnalyzer::localRelocBaseAddress(const SegmentInfo segmentsInfos
[], uint32_t segCount
) const
1650 if ( isArch("x86_64") || isArch("x86_64h") ) {
1651 #if BUILDING_APP_CACHE_UTIL
1652 if ( isKextBundle() ) {
1653 // for kext bundles the reloc base address starts at __TEXT segment
1654 return segmentsInfos
[0].vmAddr
;
1657 // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
1658 for (uint32_t i
=0; i
< segCount
; ++i
) {
1659 if ( segmentsInfos
[i
].writable() )
1660 return segmentsInfos
[i
].vmAddr
;
1663 return segmentsInfos
[0].vmAddr
;
1666 uint64_t MachOAnalyzer::externalRelocBaseAddress(const SegmentInfo segmentsInfos
[], uint32_t segCount
) const
1668 // Dyld caches are too large for a raw r_address, so everything is an offset from the base address
1669 if ( inDyldCache() ) {
1670 return preferredLoadAddress();
1673 #if BUILDING_APP_CACHE_UTIL
1674 if ( isKextBundle() ) {
1675 // for kext bundles the reloc base address starts at __TEXT segment
1676 return preferredLoadAddress();
1680 if ( isArch("x86_64") || isArch("x86_64h") ) {
1681 // for x86_64 reloc base address starts at first writable segment (usually __DATA)
1682 for (uint32_t i
=0; i
< segCount
; ++i
) {
1683 if ( segmentsInfos
[i
].writable() )
1684 return segmentsInfos
[i
].vmAddr
;
1687 // For everyone else we start at 0
1693 void MachOAnalyzer::forEachIndirectPointer(Diagnostics
& diag
, void (^handler
)(uint64_t pointerAddress
, bool bind
, int bindLibOrdinal
, const char* bindSymbolName
,
1694 bool bindWeakImport
, bool bindLazy
, bool selfModifyingStub
, bool& stop
)) const
1696 LinkEditInfo leInfo
;
1697 getLinkEditPointers(diag
, leInfo
);
1698 if ( diag
.hasError() )
1701 // find lazy and non-lazy pointer sections
1702 const bool is64Bit
= is64();
1703 const uint32_t* const indirectSymbolTable
= (uint32_t*)getLinkEditContent(leInfo
.layout
, leInfo
.dynSymTab
->indirectsymoff
);
1704 const uint32_t indirectSymbolTableCount
= leInfo
.dynSymTab
->nindirectsyms
;
1705 const uint32_t ptrSize
= pointerSize();
1706 const void* symbolTable
= getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
);
1707 const struct nlist_64
* symbols64
= (nlist_64
*)symbolTable
;
1708 const struct nlist
* symbols32
= (struct nlist
*)symbolTable
;
1709 const char* stringPool
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
);
1710 uint32_t symCount
= leInfo
.symTab
->nsyms
;
1711 uint32_t poolSize
= leInfo
.symTab
->strsize
;
1712 __block
bool stop
= false;
1714 // Old kexts put S_LAZY_SYMBOL_POINTERS on the __got section, even if they didn't have indirect symbols to prcess.
1715 // In that case, skip the loop as there shouldn't be anything to process
1716 if ( (indirectSymbolTableCount
== 0) && isKextBundle() )
1719 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& sectionStop
) {
1720 uint8_t sectionType
= (sectInfo
.sectFlags
& SECTION_TYPE
);
1721 bool selfModifyingStub
= (sectionType
== S_SYMBOL_STUBS
) && (sectInfo
.sectFlags
& S_ATTR_SELF_MODIFYING_CODE
) && (sectInfo
.reserved2
== 5) && (this->cputype
== CPU_TYPE_I386
);
1722 if ( (sectionType
!= S_LAZY_SYMBOL_POINTERS
) && (sectionType
!= S_NON_LAZY_SYMBOL_POINTERS
) && !selfModifyingStub
)
1724 if ( (flags
& S_ATTR_SELF_MODIFYING_CODE
) && !selfModifyingStub
) {
1725 diag
.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries");
1729 uint32_t elementSize
= selfModifyingStub
? sectInfo
.reserved2
: ptrSize
;
1730 uint32_t elementCount
= (uint32_t)(sectInfo
.sectSize
/elementSize
);
1731 if ( greaterThanAddOrOverflow(sectInfo
.reserved1
, elementCount
, indirectSymbolTableCount
) ) {
1732 diag
.error("section %s overflows indirect symbol table", sectInfo
.sectName
);
1737 for (uint32_t i
=0; (i
< elementCount
) && !stop
; ++i
) {
1738 uint32_t symNum
= indirectSymbolTable
[sectInfo
.reserved1
+ i
];
1739 if ( symNum
== INDIRECT_SYMBOL_ABS
)
1741 if ( symNum
== INDIRECT_SYMBOL_LOCAL
) {
1742 handler(sectInfo
.sectAddr
+i
*elementSize
, false, 0, "", false, false, false, stop
);
1745 if ( symNum
> symCount
) {
1746 diag
.error("indirect symbol[%d] = %d which is invalid symbol index", sectInfo
.reserved1
+ i
, symNum
);
1750 uint16_t n_desc
= is64Bit
? symbols64
[symNum
].n_desc
: symbols32
[symNum
].n_desc
;
1751 uint8_t n_type
= is64Bit
? symbols64
[symNum
].n_type
: symbols32
[symNum
].n_type
;
1752 uint32_t libOrdinal
= libOrdinalFromDesc(n_desc
);
1753 uint32_t strOffset
= is64Bit
? symbols64
[symNum
].n_un
.n_strx
: symbols32
[symNum
].n_un
.n_strx
;
1754 if ( strOffset
> poolSize
) {
1755 diag
.error("symbol[%d] string offset out of range", sectInfo
.reserved1
+ i
);
1759 const char* symbolName
= stringPool
+ strOffset
;
1760 bool weakImport
= (n_desc
& N_WEAK_REF
);
1761 bool lazy
= (sectionType
== S_LAZY_SYMBOL_POINTERS
);
1762 // Handle defined weak def symbols which need to get a special ordinal
1763 if ( ((n_type
& N_TYPE
) == N_SECT
) && ((n_type
& N_EXT
) != 0) && ((n_desc
& N_WEAK_DEF
) != 0) )
1764 libOrdinal
= BIND_SPECIAL_DYLIB_WEAK_LOOKUP
;
1765 handler(sectInfo
.sectAddr
+i
*elementSize
, true, libOrdinal
, symbolName
, weakImport
, lazy
, selfModifyingStub
, stop
);
1771 int MachOAnalyzer::libOrdinalFromDesc(uint16_t n_desc
) const
1773 // -flat_namespace is always flat lookup
1774 if ( (this->flags
& MH_TWOLEVEL
) == 0 )
1775 return BIND_SPECIAL_DYLIB_FLAT_LOOKUP
;
1777 // extract byte from undefined symbol entry
1778 int libIndex
= GET_LIBRARY_ORDINAL(n_desc
);
1779 switch ( libIndex
) {
1780 case SELF_LIBRARY_ORDINAL
:
1781 return BIND_SPECIAL_DYLIB_SELF
;
1783 case DYNAMIC_LOOKUP_ORDINAL
:
1784 return BIND_SPECIAL_DYLIB_FLAT_LOOKUP
;
1786 case EXECUTABLE_ORDINAL
:
1787 return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
;
1793 bool MachOAnalyzer::validBindInfo(Diagnostics
& diag
, const char* path
) const
1795 forEachBind(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1796 bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
,
1797 uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
,
1798 uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
1799 if ( invalidBindState(diag
, opcodeName
, path
, leInfo
, segments
, segIndexSet
, libraryOrdinalSet
, dylibCount
,
1800 libOrdinal
, ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
) ) {
1803 }, ^(const char* symbolName
) {
1805 return diag
.noError();
1808 bool MachOAnalyzer::invalidBindState(Diagnostics
& diag
, const char* opcodeName
, const char* path
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1809 bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, uint32_t ptrSize
,
1810 uint8_t segmentIndex
, uint64_t segmentOffset
, uint8_t type
, const char* symbolName
) const
1812 if ( !segIndexSet
) {
1813 diag
.error("in '%s' %s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path
, opcodeName
);
1816 if ( segmentIndex
>= leInfo
.layout
.linkeditSegIndex
) {
1817 diag
.error("in '%s' %s segment index %d too large", path
, opcodeName
, segmentIndex
);
1820 if ( segmentOffset
> (segments
[segmentIndex
].vmSize
-ptrSize
) ) {
1821 diag
.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path
, opcodeName
, segmentOffset
, segments
[segmentIndex
].vmSize
);
1824 if ( symbolName
== NULL
) {
1825 diag
.error("in '%s' %s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path
, opcodeName
);
1828 if ( !libraryOrdinalSet
) {
1829 diag
.error("in '%s' %s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", path
, opcodeName
);
1832 if ( libOrdinal
> (int)dylibCount
) {
1833 diag
.error("in '%s' %s has library ordinal too large (%d) max (%d)", path
, opcodeName
, libOrdinal
, dylibCount
);
1836 if ( libOrdinal
< BIND_SPECIAL_DYLIB_WEAK_LOOKUP
) {
1837 diag
.error("in '%s' %s has unknown library special ordinal (%d)", path
, opcodeName
, libOrdinal
);
1841 case BIND_TYPE_POINTER
:
1842 if ( !segments
[segmentIndex
].writable() ) {
1843 diag
.error("in '%s' %s pointer bind is in non-writable segment", path
, opcodeName
);
1846 if ( segments
[segmentIndex
].executable() && enforceFormat(Malformed::executableData
) ) {
1847 diag
.error("in '%s' %s pointer bind is in executable segment", path
, opcodeName
);
1851 case BIND_TYPE_TEXT_ABSOLUTE32
:
1852 case BIND_TYPE_TEXT_PCREL32
: {
1853 // Text relocations are permitted in x86_64 kexts
1854 bool forceAllowTextRelocs
= false;
1855 #if BUILDING_APP_CACHE_UTIL
1856 if ( isKextBundle() && (isArch("x86_64") || isArch("x86_64h")) )
1857 forceAllowTextRelocs
= true;
1859 if ( !forceAllowTextRelocs
&& !segments
[segmentIndex
].textRelocs
) {
1860 diag
.error("in '%s' %s text bind is in segment that does not support text relocations", path
, opcodeName
);
1863 if ( segments
[segmentIndex
].writable() ) {
1864 diag
.error("in '%s' %s text bind is in writable segment", path
, opcodeName
);
1867 if ( !segments
[segmentIndex
].executable() ) {
1868 diag
.error("in '%s' %s pointer bind is in non-executable segment", path
, opcodeName
);
1874 diag
.error("in '%s' %s unknown bind type %d", path
, opcodeName
, type
);
1880 void MachOAnalyzer::forEachBind(Diagnostics
& diag
, void (^handler
)(uint64_t runtimeOffset
, int libOrdinal
, uint8_t type
, const char* symbolName
,
1881 bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
),
1882 void (^strongHandler
)(const char* symbolName
)) const
1884 __block
bool startVmAddrSet
= false;
1885 __block
uint64_t startVmAddr
= 0;
1886 forEachBind(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1887 bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
,
1888 uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
,
1889 uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
1890 if ( !startVmAddrSet
) {
1891 for (int i
=0; i
<= segmentIndex
; ++i
) {
1892 if ( strcmp(segments
[i
].segName
, "__TEXT") == 0 ) {
1893 startVmAddr
= segments
[i
].vmAddr
;
1894 startVmAddrSet
= true;
1899 uint64_t bindVmOffset
= segments
[segmentIndex
].vmAddr
+ segmentOffset
;
1900 uint64_t runtimeOffset
= bindVmOffset
- startVmAddr
;
1901 handler(runtimeOffset
, libOrdinal
, type
, symbolName
, weakImport
, lazyBind
, addend
, stop
);
1902 }, ^(const char* symbolName
) {
1903 strongHandler(symbolName
);
1907 void MachOAnalyzer::forEachBind(Diagnostics
& diag
, void (^handler
)(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
,
1908 bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
),
1909 void (^strongHandler
)(const char* symbolName
)) const
1911 forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, uint8_t type
, const char* symbolName
,
1912 bool weakImport
, bool lazyBind
, uint64_t addend
, bool &stop
) {
1913 handler(runtimeOffset
, libOrdinal
, symbolName
, weakImport
, lazyBind
, addend
, stop
);
1917 void MachOAnalyzer::forEachBind(Diagnostics
& diag
,
1918 void (^handler
)(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[],
1919 bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
,
1920 uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, uint8_t type
,
1921 const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
),
1922 void (^strongHandler
)(const char* symbolName
)) const
1924 const uint32_t ptrSize
= this->pointerSize();
1927 LinkEditInfo leInfo
;
1928 getLinkEditPointers(diag
, leInfo
);
1929 if ( diag
.hasError() )
1932 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1);
1933 getAllSegmentsInfos(diag
, segmentsInfo
);
1934 if ( diag
.hasError() )
1939 const uint32_t dylibCount
= dependentDylibCount();
1941 if ( leInfo
.dyldInfo
!= nullptr ) {
1942 // process bind opcodes
1943 const uint8_t* p
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->bind_off
);
1944 const uint8_t* end
= p
+ leInfo
.dyldInfo
->bind_size
;
1946 uint64_t segmentOffset
= 0;
1947 uint8_t segmentIndex
= 0;
1948 const char* symbolName
= NULL
;
1949 int libraryOrdinal
= 0;
1950 bool segIndexSet
= false;
1951 bool libraryOrdinalSet
= false;
1956 bool weakImport
= false;
1957 while ( !stop
&& diag
.noError() && (p
< end
) ) {
1958 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1959 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1962 case BIND_OPCODE_DONE
:
1965 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1966 libraryOrdinal
= immediate
;
1967 libraryOrdinalSet
= true;
1969 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1970 libraryOrdinal
= (int)read_uleb128(diag
, p
, end
);
1971 libraryOrdinalSet
= true;
1973 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1974 // the special ordinals are negative numbers
1975 if ( immediate
== 0 )
1978 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1979 libraryOrdinal
= signExtended
;
1981 libraryOrdinalSet
= true;
1983 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1984 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
1985 symbolName
= (char*)p
;
1990 case BIND_OPCODE_SET_TYPE_IMM
:
1993 case BIND_OPCODE_SET_ADDEND_SLEB
:
1994 addend
= read_sleb128(diag
, p
, end
);
1996 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1997 segmentIndex
= immediate
;
1998 segmentOffset
= read_uleb128(diag
, p
, end
);
2001 case BIND_OPCODE_ADD_ADDR_ULEB
:
2002 segmentOffset
+= read_uleb128(diag
, p
, end
);
2004 case BIND_OPCODE_DO_BIND
:
2005 handler("BIND_OPCODE_DO_BIND", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2006 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2007 segmentOffset
+= ptrSize
;
2009 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
2010 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2011 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2012 segmentOffset
+= read_uleb128(diag
, p
, end
) + ptrSize
;
2014 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
2015 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2016 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2017 segmentOffset
+= immediate
*ptrSize
+ ptrSize
;
2019 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
2020 count
= read_uleb128(diag
, p
, end
);
2021 skip
= read_uleb128(diag
, p
, end
);
2022 for (uint32_t i
=0; i
< count
; ++i
) {
2023 handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2024 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2025 segmentOffset
+= skip
+ ptrSize
;
2031 diag
.error("bad bind opcode 0x%02X", *p
);
2034 if ( diag
.hasError() )
2037 // process lazy bind opcodes
2038 uint32_t lazyDoneCount
= 0;
2039 uint32_t lazyBindCount
= 0;
2040 if ( leInfo
.dyldInfo
->lazy_bind_size
!= 0 ) {
2041 p
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->lazy_bind_off
);
2042 end
= p
+ leInfo
.dyldInfo
->lazy_bind_size
;
2043 type
= BIND_TYPE_POINTER
;
2048 segIndexSet
= false;
2049 libraryOrdinalSet
= false;
2053 while ( !stop
&& diag
.noError() && (p
< end
) ) {
2054 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
2055 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
2058 case BIND_OPCODE_DONE
:
2059 // this opcode marks the end of each lazy pointer binding
2062 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
2063 libraryOrdinal
= immediate
;
2064 libraryOrdinalSet
= true;
2066 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
2067 libraryOrdinal
= (int)read_uleb128(diag
, p
, end
);
2068 libraryOrdinalSet
= true;
2070 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
2071 // the special ordinals are negative numbers
2072 if ( immediate
== 0 )
2075 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
2076 libraryOrdinal
= signExtended
;
2078 libraryOrdinalSet
= true;
2080 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
2081 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
2082 symbolName
= (char*)p
;
2087 case BIND_OPCODE_SET_ADDEND_SLEB
:
2088 addend
= read_sleb128(diag
, p
, end
);
2090 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
2091 segmentIndex
= immediate
;
2092 segmentOffset
= read_uleb128(diag
, p
, end
);
2095 case BIND_OPCODE_DO_BIND
:
2096 handler("BIND_OPCODE_DO_BIND", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2097 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, true, addend
, stop
);
2098 segmentOffset
+= ptrSize
;
2101 case BIND_OPCODE_SET_TYPE_IMM
:
2102 case BIND_OPCODE_ADD_ADDR_ULEB
:
2103 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
2104 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
2105 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
2107 diag
.error("bad lazy bind opcode 0x%02X", opcode
);
2111 if ( lazyDoneCount
> lazyBindCount
+7 ) {
2112 // diag.error("lazy bind opcodes missing binds");
2115 if ( diag
.hasError() )
2118 // process weak bind info
2119 if ( leInfo
.dyldInfo
->weak_bind_size
!= 0 ) {
2120 p
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->weak_bind_off
);
2121 end
= p
+ leInfo
.dyldInfo
->weak_bind_size
;
2122 type
= BIND_TYPE_POINTER
;
2126 libraryOrdinal
= BIND_SPECIAL_DYLIB_WEAK_LOOKUP
;
2127 segIndexSet
= false;
2128 libraryOrdinalSet
= true;
2132 while ( !stop
&& diag
.noError() && (p
< end
) ) {
2133 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
2134 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
2137 case BIND_OPCODE_DONE
:
2140 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
2141 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
2142 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
2143 diag
.error("unexpected dylib ordinal in weak_bind");
2145 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
2146 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
2147 symbolName
= (char*)p
;
2151 if ( immediate
& BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION
) {
2152 strongHandler(symbolName
);
2155 case BIND_OPCODE_SET_TYPE_IMM
:
2158 case BIND_OPCODE_SET_ADDEND_SLEB
:
2159 addend
= read_sleb128(diag
, p
, end
);
2161 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
2162 segmentIndex
= immediate
;
2163 segmentOffset
= read_uleb128(diag
, p
, end
);
2166 case BIND_OPCODE_ADD_ADDR_ULEB
:
2167 segmentOffset
+= read_uleb128(diag
, p
, end
);
2169 case BIND_OPCODE_DO_BIND
:
2170 handler("BIND_OPCODE_DO_BIND", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2171 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2172 segmentOffset
+= ptrSize
;
2174 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
2175 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2176 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2177 segmentOffset
+= read_uleb128(diag
, p
, end
) + ptrSize
;
2179 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
2180 handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2181 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2182 segmentOffset
+= immediate
*ptrSize
+ ptrSize
;
2184 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
2185 count
= read_uleb128(diag
, p
, end
);
2186 skip
= read_uleb128(diag
, p
, end
);
2187 for (uint32_t i
=0; i
< count
; ++i
) {
2188 handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
,
2189 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2190 segmentOffset
+= skip
+ ptrSize
;
2196 diag
.error("bad bind opcode 0x%02X", *p
);
2201 else if ( leInfo
.chainedFixups
!= nullptr ) {
2202 // binary uses chained fixups, so do nothing
2204 else if ( leInfo
.dynSymTab
!= nullptr ) {
2205 // old binary, process external relocations
2206 const uint64_t relocsStartAddress
= externalRelocBaseAddress(segmentsInfo
, leInfo
.layout
.linkeditSegIndex
);
2207 const relocation_info
* const relocsStart
= (relocation_info
*)getLinkEditContent(leInfo
.layout
, leInfo
.dynSymTab
->extreloff
);
2208 const relocation_info
* const relocsEnd
= &relocsStart
[leInfo
.dynSymTab
->nextrel
];
2209 bool is64Bit
= is64() ;
2210 const uint8_t relocSize
= (is64Bit
? 3 : 2);
2211 const void* symbolTable
= getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
);
2212 const struct nlist_64
* symbols64
= (nlist_64
*)symbolTable
;
2213 const struct nlist
* symbols32
= (struct nlist
*)symbolTable
;
2214 const char* stringPool
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
);
2215 uint32_t symCount
= leInfo
.symTab
->nsyms
;
2216 uint32_t poolSize
= leInfo
.symTab
->strsize
;
2217 for (const relocation_info
* reloc
=relocsStart
; (reloc
< relocsEnd
) && !stop
; ++reloc
) {
2218 bool isBranch
= false;
2219 #if BUILDING_APP_CACHE_UTIL
2220 if ( isKextBundle() ) {
2221 // kext's may have other kinds of relocations, eg, branch relocs. Skip them
2222 if ( isArch("x86_64") || isArch("x86_64h") ) {
2223 if ( reloc
->r_type
== X86_64_RELOC_BRANCH
) {
2224 if ( reloc
->r_length
!= 2 ) {
2225 diag
.error("external relocation has wrong r_length");
2228 if ( reloc
->r_pcrel
!= true ) {
2229 diag
.error("external relocation should be pcrel");
2239 if ( reloc
->r_length
!= relocSize
) {
2240 diag
.error("external relocation has wrong r_length");
2243 if ( reloc
->r_type
!= 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED
2244 diag
.error("external relocation has wrong r_type");
2248 uint32_t segIndex
= 0;
2249 uint64_t segOffset
= 0;
2250 if ( segIndexAndOffsetForAddress(relocsStartAddress
+reloc
->r_address
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) {
2251 uint32_t symbolIndex
= reloc
->r_symbolnum
;
2252 if ( symbolIndex
> symCount
) {
2253 diag
.error("external relocation has out of range r_symbolnum");
2257 uint32_t strOffset
= is64Bit
? symbols64
[symbolIndex
].n_un
.n_strx
: symbols32
[symbolIndex
].n_un
.n_strx
;
2258 uint16_t n_desc
= is64Bit
? symbols64
[symbolIndex
].n_desc
: symbols32
[symbolIndex
].n_desc
;
2259 uint8_t n_type
= is64Bit
? symbols64
[symbolIndex
].n_type
: symbols32
[symbolIndex
].n_type
;
2260 uint32_t libOrdinal
= libOrdinalFromDesc(n_desc
);
2261 if ( strOffset
>= poolSize
) {
2262 diag
.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex
);
2266 const char* symbolName
= stringPool
+ strOffset
;
2267 bool weakImport
= (n_desc
& N_WEAK_REF
);
2268 const uint8_t* content
= (uint8_t*)this + segmentsInfo
[segIndex
].vmAddr
- leInfo
.layout
.textUnslidVMAddr
+ segOffset
;
2269 uint64_t addend
= (reloc
->r_length
== 3) ? *((uint64_t*)content
) : *((uint32_t*)content
);
2270 // Handle defined weak def symbols which need to get a special ordinal
2271 if ( ((n_type
& N_TYPE
) == N_SECT
) && ((n_type
& N_EXT
) != 0) && ((n_desc
& N_WEAK_DEF
) != 0) )
2272 libOrdinal
= BIND_SPECIAL_DYLIB_WEAK_LOOKUP
;
2273 uint8_t type
= isBranch
? BIND_TYPE_TEXT_PCREL32
: BIND_TYPE_POINTER
;
2274 handler("external relocation", leInfo
, segmentsInfo
, true, true, dylibCount
, libOrdinal
,
2275 ptrSize
, segIndex
, segOffset
, type
, symbolName
, weakImport
, false, addend
, stop
);
2280 diag
.error("local relocation has out of range r_address");
2284 // then process indirect symbols
2285 forEachIndirectPointer(diag
, ^(uint64_t address
, bool bind
, int bindLibOrdinal
,
2286 const char* bindSymbolName
, bool bindWeakImport
, bool bindLazy
, bool selfModifyingStub
, bool& indStop
) {
2289 uint32_t segIndex
= 0;
2290 uint64_t segOffset
= 0;
2291 if ( segIndexAndOffsetForAddress(address
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) {
2292 handler("indirect symbol", leInfo
, segmentsInfo
, true, true, dylibCount
, bindLibOrdinal
,
2293 ptrSize
, segIndex
, segOffset
, BIND_TYPE_POINTER
, bindSymbolName
, bindWeakImport
, bindLazy
, 0, indStop
);
2296 diag
.error("indirect symbol has out of range address");
2304 bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics
& diag
, const char* path
) const
2306 LinkEditInfo leInfo
;
2307 getLinkEditPointers(diag
, leInfo
);
2308 if ( diag
.hasError() )
2311 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1);
2312 getAllSegmentsInfos(diag
, segmentsInfo
);
2313 if ( diag
.hasError() )
2316 // validate dyld_chained_fixups_header
2317 const dyld_chained_fixups_header
* chainsHeader
= (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
);
2318 if ( chainsHeader
->fixups_version
!= 0 ) {
2319 diag
.error("chained fixups, unknown header version");
2322 if ( chainsHeader
->starts_offset
>= leInfo
.chainedFixups
->datasize
) {
2323 diag
.error("chained fixups, starts_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
2326 if ( chainsHeader
->imports_offset
> leInfo
.chainedFixups
->datasize
) {
2327 diag
.error("chained fixups, imports_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
2331 uint32_t formatEntrySize
;
2332 switch ( chainsHeader
->imports_format
) {
2333 case DYLD_CHAINED_IMPORT
:
2334 formatEntrySize
= sizeof(dyld_chained_import
);
2336 case DYLD_CHAINED_IMPORT_ADDEND
:
2337 formatEntrySize
= sizeof(dyld_chained_import_addend
);
2339 case DYLD_CHAINED_IMPORT_ADDEND64
:
2340 formatEntrySize
= sizeof(dyld_chained_import_addend64
);
2343 diag
.error("chained fixups, unknown imports_format");
2346 if ( greaterThanAddOrOverflow(chainsHeader
->imports_offset
, (formatEntrySize
* chainsHeader
->imports_count
), chainsHeader
->symbols_offset
) ) {
2347 diag
.error("chained fixups, imports array overlaps symbols");
2350 if ( chainsHeader
->symbols_format
!= 0 ) {
2351 diag
.error("chained fixups, symbols_format unknown");
2355 // validate dyld_chained_starts_in_image
2356 const dyld_chained_starts_in_image
* startsInfo
= (dyld_chained_starts_in_image
*)((uint8_t*)chainsHeader
+ chainsHeader
->starts_offset
);
2357 if ( startsInfo
->seg_count
!= leInfo
.layout
.linkeditSegIndex
+1 ) {
2358 // We can have fewer segments than the count, so long as those we are missing have no relocs
2359 // This can happen because __CTF is inserted by ctf_insert after linking, and between __DATA and __LINKEDIT, but has no relocs
2360 // ctf_insert updates the load commands to put __CTF between __DATA and __LINKEDIT, but doesn't update the chained fixups data structures
2361 if ( startsInfo
->seg_count
> (leInfo
.layout
.linkeditSegIndex
+ 1) ) {
2362 diag
.error("chained fixups, seg_count exceeds number of segments");
2366 // We can have fewer segments than the count, so long as those we are missing have no relocs
2367 uint32_t numNoRelocSegments
= 0;
2368 uint32_t numExtraSegments
= (leInfo
.layout
.lastSegIndex
+ 1) - startsInfo
->seg_count
;
2369 for (unsigned i
= 0; i
!= numExtraSegments
; ++i
) {
2370 // Check each extra segment before linkedit
2371 const SegmentInfo
& segInfo
= segmentsInfo
[leInfo
.layout
.linkeditSegIndex
- (i
+ 1)];
2372 if ( segInfo
.vmSize
== 0 )
2373 ++numNoRelocSegments
;
2376 if ( numNoRelocSegments
!= numExtraSegments
) {
2377 diag
.error("chained fixups, seg_count does not match number of segments");
2381 const uint64_t baseAddress
= preferredLoadAddress();
2382 uint32_t maxValidPointerSeen
= 0;
2383 uint16_t pointer_format_for_all
= 0;
2384 bool pointer_format_found
= false;
2385 const uint8_t* endOfStarts
= (uint8_t*)chainsHeader
+ chainsHeader
->imports_offset
;
2386 for (uint32_t i
=0; i
< startsInfo
->seg_count
; ++i
) {
2387 uint32_t segInfoOffset
= startsInfo
->seg_info_offset
[i
];
2388 // 0 offset means this segment has no fixups
2389 if ( segInfoOffset
== 0 )
2391 const dyld_chained_starts_in_segment
* segInfo
= (dyld_chained_starts_in_segment
*)((uint8_t*)startsInfo
+ segInfoOffset
);
2392 if ( segInfo
->size
> (endOfStarts
- (uint8_t*)segInfo
) ) {
2393 diag
.error("chained fixups, dyld_chained_starts_in_segment for segment #%d overruns imports table", i
);
2397 // validate dyld_chained_starts_in_segment
2398 if ( (segInfo
->page_size
!= 0x1000) && (segInfo
->page_size
!= 0x4000) ) {
2399 diag
.error("chained fixups, page_size not 4KB or 16KB in segment #%d", i
);
2402 if ( segInfo
->pointer_format
> 12 ) {
2403 diag
.error("chained fixups, unknown pointer_format in segment #%d", i
);
2406 if ( !pointer_format_found
) {
2407 pointer_format_for_all
= segInfo
->pointer_format
;
2408 pointer_format_found
= true;
2410 if ( segInfo
->pointer_format
!= pointer_format_for_all
) {
2411 diag
.error("chained fixups, pointer_format not same for all segments %d and %d", segInfo
->pointer_format
, pointer_format_for_all
);
2414 if ( segInfo
->segment_offset
!= (segmentsInfo
[i
].vmAddr
- baseAddress
) ) {
2415 diag
.error("chained fixups, segment_offset does not match vmaddr from LC_SEGMENT in segment #%d", i
);
2418 if ( segInfo
->max_valid_pointer
!= 0 ) {
2419 if ( maxValidPointerSeen
== 0 ) {
2420 // record max_valid_pointer values seen
2421 maxValidPointerSeen
= segInfo
->max_valid_pointer
;
2423 else if ( maxValidPointerSeen
!= segInfo
->max_valid_pointer
) {
2424 diag
.error("chained fixups, different max_valid_pointer values seen in different segments");
2428 // validate starts table in segment
2429 if ( offsetof(dyld_chained_starts_in_segment
, page_start
[segInfo
->page_count
]) > segInfo
->size
) {
2430 diag
.error("chained fixups, page_start array overflows size");
2433 uint32_t maxOverflowIndex
= (uint32_t)(segInfo
->size
- offsetof(dyld_chained_starts_in_segment
, page_start
[segInfo
->page_count
]))/sizeof(uint16_t);
2434 for (int pageIndex
=0; pageIndex
< segInfo
->page_count
; ++pageIndex
) {
2435 uint16_t offsetInPage
= segInfo
->page_start
[pageIndex
];
2436 if ( offsetInPage
== DYLD_CHAINED_PTR_START_NONE
)
2438 if ( (offsetInPage
& DYLD_CHAINED_PTR_START_MULTI
) == 0 ) {
2439 // this is the offset into the page where the first fixup is
2440 if ( offsetInPage
> segInfo
->page_size
) {
2441 diag
.error("chained fixups, in segment #%d page_start[%d]=0x%04X exceeds page size", i
, pageIndex
, offsetInPage
);
2445 // this is actually an index into chain_starts[]
2446 uint32_t overflowIndex
= offsetInPage
& ~DYLD_CHAINED_PTR_START_MULTI
;
2447 // now verify all starts are within the page and in ascending order
2448 uint16_t lastOffsetInPage
= 0;
2450 if ( overflowIndex
> maxOverflowIndex
) {
2451 diag
.error("chain overflow index out of range %d (max=%d) in segment %s", overflowIndex
, maxOverflowIndex
, segmentName(i
));
2454 offsetInPage
= (segInfo
->page_start
[overflowIndex
] & ~DYLD_CHAINED_PTR_START_LAST
);
2455 if ( offsetInPage
> segInfo
->page_size
) {
2456 diag
.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X exceeds page size", i
, overflowIndex
, offsetInPage
);
2459 if ( (offsetInPage
<= lastOffsetInPage
) && (lastOffsetInPage
!= 0) ) {
2460 diag
.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X is before previous at 0x%04X\n", i
, overflowIndex
, offsetInPage
, lastOffsetInPage
);
2463 lastOffsetInPage
= offsetInPage
;
2465 } while ( (segInfo
->page_start
[overflowIndex
] & DYLD_CHAINED_PTR_START_LAST
) == 0 );
2470 // validate import table size can fit
2471 if ( chainsHeader
->imports_count
!= 0 ) {
2472 uint32_t maxBindOrdinal
= 0;
2473 switch (pointer_format_for_all
) {
2474 case DYLD_CHAINED_PTR_32
:
2475 maxBindOrdinal
= 0x0FFFFF; // 20-bits
2477 case DYLD_CHAINED_PTR_ARM64E
:
2478 case DYLD_CHAINED_PTR_ARM64E_USERLAND
:
2479 case DYLD_CHAINED_PTR_ARM64E_OFFSET
:
2480 maxBindOrdinal
= 0x00FFFF; // 16-bits
2482 case DYLD_CHAINED_PTR_64
:
2483 case DYLD_CHAINED_PTR_64_OFFSET
:
2484 case DYLD_CHAINED_PTR_ARM64E_USERLAND24
:
2485 maxBindOrdinal
= 0xFFFFFF; // 24 bits
2488 if ( chainsHeader
->imports_count
>= maxBindOrdinal
) {
2489 diag
.error("chained fixups, imports_count (%d) exceeds max of %d", chainsHeader
->imports_count
, maxBindOrdinal
);
2494 // validate max_valid_pointer is larger than last segment
2495 if ( (maxValidPointerSeen
!= 0) && !inDyldCache() ) {
2496 uint64_t lastSegmentLastVMAddr
= segmentsInfo
[leInfo
.layout
.linkeditSegIndex
-1].vmAddr
+ segmentsInfo
[leInfo
.layout
.linkeditSegIndex
-1].vmSize
;
2497 if ( maxValidPointerSeen
< lastSegmentLastVMAddr
) {
2498 diag
.error("chained fixups, max_valid_pointer too small for image");
2503 return diag
.noError();
2506 bool MachOAnalyzer::validChainedFixupsInfoOldArm64e(Diagnostics
& diag
, const char* path
) const
2508 __block
uint32_t maxTargetCount
= 0;
2509 __block
uint32_t currentTargetCount
= 0;
2510 parseOrgArm64eChainedFixups(diag
,
2511 ^(uint32_t totalTargets
, bool& stop
) {
2512 maxTargetCount
= totalTargets
;
2514 ^(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, uint8_t type
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
2515 if ( symbolName
== NULL
) {
2516 diag
.error("in '%s' missing BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path
);
2518 else if ( !libraryOrdinalSet
) {
2519 diag
.error("in '%s' missing BIND_OPCODE_SET_DYLIB_ORDINAL", path
);
2521 else if ( libOrdinal
> (int)dylibCount
) {
2522 diag
.error("in '%s' has library ordinal too large (%d) max (%d)", path
, libOrdinal
, dylibCount
);
2524 else if ( libOrdinal
< BIND_SPECIAL_DYLIB_WEAK_LOOKUP
) {
2525 diag
.error("in '%s' has unknown library special ordinal (%d)", path
, libOrdinal
);
2527 else if ( type
!= BIND_TYPE_POINTER
) {
2528 diag
.error("in '%s' unknown bind type %d", path
, type
);
2530 else if ( currentTargetCount
> maxTargetCount
) {
2531 diag
.error("in '%s' chained target counts exceeds BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB", path
);
2533 ++currentTargetCount
;
2534 if ( diag
.hasError() )
2537 ^(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], uint8_t segmentIndex
, bool segIndexSet
, uint64_t segmentOffset
, uint16_t format
, bool& stop
) {
2538 if ( !segIndexSet
) {
2539 diag
.error("in '%s' missing BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path
);
2541 else if ( segmentIndex
>= leInfo
.layout
.linkeditSegIndex
) {
2542 diag
.error("in '%s' segment index %d too large", path
, segmentIndex
);
2544 else if ( segmentOffset
> (segments
[segmentIndex
].vmSize
-8) ) {
2545 diag
.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path
, segmentOffset
, segments
[segmentIndex
].vmSize
);
2547 else if ( !segments
[segmentIndex
].writable() ) {
2548 diag
.error("in '%s' pointer bind is in non-writable segment", path
);
2550 else if ( segments
[segmentIndex
].executable() ) {
2551 diag
.error("in '%s' pointer bind is in executable segment", path
);
2553 if ( diag
.hasError() )
2558 return diag
.noError();
2563 void MachOAnalyzer::parseOrgArm64eChainedFixups(Diagnostics
& diag
, void (^targetCount
)(uint32_t totalTargets
, bool& stop
),
2564 void (^addTarget
)(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, uint8_t type
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
),
2565 void (^addChainStart
)(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], uint8_t segmentIndex
, bool segIndexSet
, uint64_t segmentOffset
, uint16_t format
, bool& stop
)) const
2569 LinkEditInfo leInfo
;
2570 getLinkEditPointers(diag
, leInfo
);
2571 if ( diag
.hasError() )
2574 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1);
2575 getAllSegmentsInfos(diag
, segmentsInfo
);
2576 if ( diag
.hasError() )
2579 const uint32_t dylibCount
= dependentDylibCount();
2581 if ( leInfo
.dyldInfo
!= nullptr ) {
2582 // process bind opcodes
2583 const uint8_t* p
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->bind_off
);
2584 const uint8_t* end
= p
+ leInfo
.dyldInfo
->bind_size
;
2586 uint64_t segmentOffset
= 0;
2587 uint8_t segmentIndex
= 0;
2588 const char* symbolName
= NULL
;
2589 int libraryOrdinal
= 0;
2590 bool segIndexSet
= false;
2591 bool libraryOrdinalSet
= false;
2592 uint64_t targetTableCount
;
2593 uint64_t addend
= 0;
2594 bool weakImport
= false;
2595 while ( !stop
&& diag
.noError() && (p
< end
) ) {
2596 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
2597 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
2600 case BIND_OPCODE_DONE
:
2603 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
2604 libraryOrdinal
= immediate
;
2605 libraryOrdinalSet
= true;
2607 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
2608 libraryOrdinal
= (int)read_uleb128(diag
, p
, end
);
2609 libraryOrdinalSet
= true;
2611 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
2612 // the special ordinals are negative numbers
2613 if ( immediate
== 0 )
2616 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
2617 libraryOrdinal
= signExtended
;
2619 libraryOrdinalSet
= true;
2621 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
2622 weakImport
= ( (immediate
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 );
2623 symbolName
= (char*)p
;
2628 case BIND_OPCODE_SET_TYPE_IMM
:
2631 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
2632 segmentIndex
= immediate
;
2633 segmentOffset
= read_uleb128(diag
, p
, end
);
2636 case BIND_OPCODE_SET_ADDEND_SLEB
:
2637 addend
= read_sleb128(diag
, p
, end
);
2639 case BIND_OPCODE_DO_BIND
:
2641 addTarget(leInfo
, segmentsInfo
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, type
, symbolName
, addend
, weakImport
, stop
);
2643 case BIND_OPCODE_THREADED
:
2644 switch (immediate
) {
2645 case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
:
2646 targetTableCount
= read_uleb128(diag
, p
, end
);
2647 if ( targetTableCount
> 65535 ) {
2648 diag
.error("BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB size too large");
2653 targetCount((uint32_t)targetTableCount
, stop
);
2656 case BIND_SUBOPCODE_THREADED_APPLY
:
2657 if ( addChainStart
)
2658 addChainStart(leInfo
, segmentsInfo
, segmentIndex
, segIndexSet
, segmentOffset
, DYLD_CHAINED_PTR_ARM64E
, stop
);
2661 diag
.error("bad BIND_OPCODE_THREADED sub-opcode 0x%02X", immediate
);
2665 diag
.error("bad bind opcode 0x%02X", immediate
);
2668 if ( diag
.hasError() )
2673 void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics
& diag
, void (^callback
)(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
)) const
2675 LinkEditInfo leInfo
;
2676 getLinkEditPointers(diag
, leInfo
);
2677 if ( diag
.hasError() )
2680 BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1);
2681 getAllSegmentsInfos(diag
, segmentsInfo
);
2682 if ( diag
.hasError() )
2686 if ( leInfo
.dyldInfo
!= nullptr ) {
2687 parseOrgArm64eChainedFixups(diag
, nullptr, ^(const LinkEditInfo
& leInfo2
, const SegmentInfo segments
[], bool libraryOrdinalSet
, uint32_t dylibCount
,
2688 int libOrdinal
, uint8_t type
, const char* symbolName
, uint64_t fixAddend
, bool weakImport
, bool& stopChain
) {
2689 callback(libOrdinal
, symbolName
, fixAddend
, weakImport
, stopChain
);
2692 else if ( leInfo
.chainedFixups
!= nullptr ) {
2693 const dyld_chained_fixups_header
* header
= (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
);
2694 if ( (header
->imports_offset
> leInfo
.chainedFixups
->datasize
) || (header
->symbols_offset
> leInfo
.chainedFixups
->datasize
) ) {
2695 diag
.error("malformed import table");
2698 const dyld_chained_import
* imports
;
2699 const dyld_chained_import_addend
* importsA32
;
2700 const dyld_chained_import_addend64
* importsA64
;
2701 const char* symbolsPool
= (char*)header
+ header
->symbols_offset
;
2702 uint32_t maxSymbolOffset
= leInfo
.chainedFixups
->datasize
- header
->symbols_offset
;
2704 switch (header
->imports_format
) {
2705 case DYLD_CHAINED_IMPORT
:
2706 imports
= (dyld_chained_import
*)((uint8_t*)header
+ header
->imports_offset
);
2707 for (uint32_t i
=0; i
< header
->imports_count
&& !stop
; ++i
) {
2708 const char* symbolName
= &symbolsPool
[imports
[i
].name_offset
];
2709 if ( imports
[i
].name_offset
> maxSymbolOffset
) {
2710 diag
.error("malformed import table, string overflow");
2713 uint8_t libVal
= imports
[i
].lib_ordinal
;
2714 if ( libVal
> 0xF0 )
2715 libOrdinal
= (int8_t)libVal
;
2717 libOrdinal
= libVal
;
2718 callback(libOrdinal
, symbolName
, 0, imports
[i
].weak_import
, stop
);
2721 case DYLD_CHAINED_IMPORT_ADDEND
:
2722 importsA32
= (dyld_chained_import_addend
*)((uint8_t*)header
+ header
->imports_offset
);
2723 for (uint32_t i
=0; i
< header
->imports_count
&& !stop
; ++i
) {
2724 const char* symbolName
= &symbolsPool
[importsA32
[i
].name_offset
];
2725 if ( importsA32
[i
].name_offset
> maxSymbolOffset
) {
2726 diag
.error("malformed import table, string overflow");
2729 uint8_t libVal
= importsA32
[i
].lib_ordinal
;
2730 if ( libVal
> 0xF0 )
2731 libOrdinal
= (int8_t)libVal
;
2733 libOrdinal
= libVal
;
2734 callback(libOrdinal
, symbolName
, importsA32
[i
].addend
, importsA32
[i
].weak_import
, stop
);
2737 case DYLD_CHAINED_IMPORT_ADDEND64
:
2738 importsA64
= (dyld_chained_import_addend64
*)((uint8_t*)header
+ header
->imports_offset
);
2739 for (uint32_t i
=0; i
< header
->imports_count
&& !stop
; ++i
) {
2740 const char* symbolName
= &symbolsPool
[importsA64
[i
].name_offset
];
2741 if ( importsA64
[i
].name_offset
> maxSymbolOffset
) {
2742 diag
.error("malformed import table, string overflow");
2745 uint16_t libVal
= importsA64
[i
].lib_ordinal
;
2746 if ( libVal
> 0xFFF0 )
2747 libOrdinal
= (int16_t)libVal
;
2749 libOrdinal
= libVal
;
2750 callback(libOrdinal
, symbolName
, importsA64
[i
].addend
, importsA64
[i
].weak_import
, stop
);
2754 diag
.error("unknown imports format");
2760 uint32_t MachOAnalyzer::segmentCount() const
2762 __block
uint32_t count
= 0;
2763 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
2769 bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset
, uint32_t& size
) const
2775 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
2776 if ( cmd
->cmd
== LC_CODE_SIGNATURE
) {
2777 const linkedit_data_command
* sigCmd
= (linkedit_data_command
*)cmd
;
2778 fileOffset
= sigCmd
->dataoff
;
2779 size
= sigCmd
->datasize
;
2783 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
2785 // early exist if no LC_CODE_SIGNATURE
2786 if ( fileOffset
== 0 )
2789 // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
2790 if ( (this->cputype
== CPU_TYPE_X86_64
) || (this->cputype
== CPU_TYPE_I386
) ) {
2791 __block
bool foundPlatform
= false;
2792 __block
bool badSignature
= false;
2793 forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) {
2794 foundPlatform
= true;
2795 if ( (platform
== Platform::macOS
) && (sdk
< 0x000A0900) )
2796 badSignature
= true;
2798 return foundPlatform
&& !badSignature
;
2804 bool MachOAnalyzer::hasProgramVars(Diagnostics
& diag
, uint32_t& progVarsOffset
) const
2806 if ( this->filetype
!= MH_EXECUTE
)
2809 // macOS 10.8+ program uses LC_MAIN and ProgramVars are in libdyld.dylib
2810 // macOS 10.6 -> 10.7 ProgramVars are in __program_vars section in main executable
2811 // macOS 10.5 ProgramVars are in __dyld section in main executable and 7 pointers in size
2812 // macOS 10.4 and earlier ProgramVars need to be looked up by name in nlist of main executable
2816 if ( getEntry(offset
, usesCRT
) && usesCRT
) {
2817 // is pre-10.8 program
2818 uint64_t sectionSize
;
2819 if ( const void* progVarsSection
= findSectionContent("__DATA", "__program_vars", sectionSize
) ) {
2820 progVarsOffset
= (uint32_t)((uint8_t*)progVarsSection
- (uint8_t*)this);
2823 else if ( const void* dyldSection
= findSectionContent("__DATA", "__dyld", sectionSize
) ) {
2824 if ( sectionSize
>= 7*pointerSize() ) {
2825 progVarsOffset
= (uint32_t)((uint8_t*)dyldSection
- (uint8_t*)this) + 2*pointerSize();
2829 diag
.error("pre-macOS 10.5 binaries not supported");
2835 // Convert from a (possibly) live pointer to a vmAddr
2836 uint64_t MachOAnalyzer::VMAddrConverter::convertToVMAddr(uint64_t value
) const {
2837 if ( contentRebased
) {
2840 // The value may have been signed. Strip the signature if that is the case
2841 #if __has_feature(ptrauth_calls)
2842 value
= (uint64_t)__builtin_ptrauth_strip((void*)value
, ptrauth_key_asia
);
2847 if ( chainedPointerFormat
!= 0 ) {
2848 auto* chainedValue
= (MachOAnalyzer::ChainedFixupPointerOnDisk
*)&value
;
2849 uint64_t targetRuntimeOffset
;
2850 if ( chainedValue
->isRebase(chainedPointerFormat
, preferredLoadAddress
, targetRuntimeOffset
) ) {
2851 value
= preferredLoadAddress
+ targetRuntimeOffset
;
2856 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
2857 typedef MachOAnalyzer::VMAddrConverter VMAddrConverter
;
2858 if ( sharedCacheChainedPointerFormat
!= VMAddrConverter::SharedCacheFormat::none
) {
2859 switch ( sharedCacheChainedPointerFormat
) {
2860 case VMAddrConverter::SharedCacheFormat::none
:
2862 case VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi
: {
2863 const uint64_t deltaMask
= 0x00FFFF0000000000;
2864 const uint64_t valueMask
= ~deltaMask
;
2865 const uint64_t valueAdd
= preferredLoadAddress
;
2866 value
= (value
& valueMask
);
2872 case VMAddrConverter::SharedCacheFormat::v3
: {
2873 // Just use the chained pointer format for arm64e
2874 auto* chainedValue
= (MachOAnalyzer::ChainedFixupPointerOnDisk
*)&value
;
2875 uint64_t targetRuntimeOffset
;
2876 if ( chainedValue
->isRebase(DYLD_CHAINED_PTR_ARM64E
, preferredLoadAddress
,
2877 targetRuntimeOffset
) ) {
2878 value
= preferredLoadAddress
+ targetRuntimeOffset
;
2890 MachOAnalyzer::VMAddrConverter
MachOAnalyzer::makeVMAddrConverter(bool contentRebased
) const {
2891 MachOAnalyzer::VMAddrConverter vmAddrConverter
;
2892 vmAddrConverter
.preferredLoadAddress
= preferredLoadAddress();
2893 vmAddrConverter
.slide
= getSlide();
2894 vmAddrConverter
.chainedPointerFormat
= hasChainedFixups() ? chainedPointerFormat() : 0;
2895 vmAddrConverter
.contentRebased
= contentRebased
;
2896 return vmAddrConverter
;
2899 bool MachOAnalyzer::hasInitializer(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, const void* dyldCache
) const
2901 __block
bool result
= false;
2902 forEachInitializer(diag
, vmAddrConverter
, ^(uint32_t offset
) {
2908 void MachOAnalyzer::forEachInitializerPointerSection(Diagnostics
& diag
, void (^callback
)(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
)) const
2910 const unsigned ptrSize
= pointerSize();
2911 const uint64_t baseAddress
= preferredLoadAddress();
2912 const uint64_t slide
= (uint64_t)this - baseAddress
;
2913 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& sectStop
) {
2914 if ( (info
.sectFlags
& SECTION_TYPE
) == S_MOD_INIT_FUNC_POINTERS
) {
2915 if ( (info
.sectSize
% ptrSize
) != 0 ) {
2916 diag
.error("initializer section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
);
2920 if ( malformedSectionRange
) {
2921 diag
.error("initializer section %s/%s extends beyond its segment", info
.segInfo
.segName
, info
.sectName
);
2925 const uint8_t* content
= (uint8_t*)(info
.sectAddr
+ slide
);
2926 if ( ((long)content
% ptrSize
) != 0 ) {
2927 diag
.error("initializer section %s/%s is not pointer aligned", info
.segInfo
.segName
, info
.sectName
);
2931 callback((uint32_t)(info
.sectAddr
- baseAddress
), (uint32_t)info
.sectSize
, content
, sectStop
);
2936 struct VIS_HIDDEN SegmentRanges
2938 struct SegmentRange
{
2939 uint64_t vmAddrStart
;
2944 bool contains(uint64_t vmAddr
) const {
2945 for (const SegmentRange
& range
: segments
) {
2946 if ( (range
.vmAddrStart
<= vmAddr
) && (vmAddr
< range
.vmAddrEnd
) )
2953 SegmentRange localAlloc
[1];
2956 dyld3::OverflowSafeArray
<SegmentRange
> segments
{ localAlloc
, sizeof(localAlloc
) / sizeof(localAlloc
[0]) };
2959 void MachOAnalyzer::forEachInitializer(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, void (^callback
)(uint32_t offset
), const void* dyldCache
) const
2961 __block SegmentRanges executableSegments
;
2962 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
2963 if ( (info
.protections
& VM_PROT_EXECUTE
) != 0 ) {
2964 executableSegments
.segments
.push_back({ info
.vmAddr
, info
.vmAddr
+ info
.vmSize
, (uint32_t)info
.fileSize
});
2968 if (executableSegments
.segments
.empty()) {
2969 diag
.error("no exeutable segments");
2973 uint64_t loadAddress
= preferredLoadAddress();
2974 intptr_t slide
= getSlide();
2976 // if dylib linked with -init linker option, that initializer is first
2977 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
2978 if ( cmd
->cmd
== LC_ROUTINES
) {
2979 const routines_command
* routines
= (routines_command
*)cmd
;
2980 uint64_t dashInit
= routines
->init_address
;
2981 if ( executableSegments
.contains(dashInit
) )
2982 callback((uint32_t)(dashInit
- loadAddress
));
2984 diag
.error("-init does not point within __TEXT segment");
2986 else if ( cmd
->cmd
== LC_ROUTINES_64
) {
2987 const routines_command_64
* routines
= (routines_command_64
*)cmd
;
2988 uint64_t dashInit
= routines
->init_address
;
2989 if ( executableSegments
.contains(dashInit
) )
2990 callback((uint32_t)(dashInit
- loadAddress
));
2992 diag
.error("-init does not point within __TEXT segment");
2996 // next any function pointers in mod-init section
2997 const unsigned ptrSize
= pointerSize();
2998 forEachInitializerPointerSection(diag
, ^(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
) {
2999 if ( ptrSize
== 8 ) {
3000 const uint64_t* initsStart
= (uint64_t*)content
;
3001 const uint64_t* initsEnd
= (uint64_t*)((uint8_t*)content
+ sectionSize
);
3002 for (const uint64_t* p
=initsStart
; p
< initsEnd
; ++p
) {
3003 uint64_t anInit
= vmAddrConverter
.convertToVMAddr(*p
);
3004 if ( !executableSegments
.contains(anInit
) ) {
3005 diag
.error("initializer 0x%0llX does not point within executable segment", anInit
);
3009 callback((uint32_t)(anInit
- loadAddress
));
3013 const uint32_t* initsStart
= (uint32_t*)content
;
3014 const uint32_t* initsEnd
= (uint32_t*)((uint8_t*)content
+ sectionSize
);
3015 for (const uint32_t* p
=initsStart
; p
< initsEnd
; ++p
) {
3016 uint32_t anInit
= (uint32_t)vmAddrConverter
.convertToVMAddr(*p
);
3017 if ( !executableSegments
.contains(anInit
) ) {
3018 diag
.error("initializer 0x%0X does not point within executable segment", anInit
);
3022 callback(anInit
- (uint32_t)loadAddress
);
3027 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
3028 if ( (info
.sectFlags
& SECTION_TYPE
) != S_INIT_FUNC_OFFSETS
)
3030 const uint8_t* content
= (uint8_t*)(info
.sectAddr
+ slide
);
3031 if ( info
.segInfo
.writable() ) {
3032 diag
.error("initializer offsets section %s/%s must be in read-only segment", info
.segInfo
.segName
, info
.sectName
);
3036 if ( (info
.sectSize
% 4) != 0 ) {
3037 diag
.error("initializer offsets section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
);
3041 if ( malformedSectionRange
) {
3042 diag
.error("initializer offsets section %s/%s extends beyond the end of the segment", info
.segInfo
.segName
, info
.sectName
);
3046 if ( (info
.sectAddr
% 4) != 0 ) {
3047 diag
.error("initializer offsets section %s/%s is not 4-byte aligned", info
.segInfo
.segName
, info
.sectName
);
3051 const uint32_t* initsStart
= (uint32_t*)content
;
3052 const uint32_t* initsEnd
= (uint32_t*)((uint8_t*)content
+ info
.sectSize
);
3053 for (const uint32_t* p
=initsStart
; p
< initsEnd
; ++p
) {
3054 uint32_t anInitOffset
= *p
;
3055 if ( !executableSegments
.contains(loadAddress
+ anInitOffset
) ) {
3056 diag
.error("initializer 0x%08X does not an offset to an executable segment", anInitOffset
);
3060 callback(anInitOffset
);
3065 bool MachOAnalyzer::hasTerminators(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
) const
3067 __block
bool result
= false;
3068 forEachTerminator(diag
, vmAddrConverter
, ^(uint32_t offset
) {
3074 void MachOAnalyzer::forEachTerminator(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, void (^callback
)(uint32_t offset
)) const
3076 __block SegmentRanges executableSegments
;
3077 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
3078 if ( (info
.protections
& VM_PROT_EXECUTE
) != 0 ) {
3079 executableSegments
.segments
.push_back({ info
.vmAddr
, info
.vmAddr
+ info
.vmSize
, (uint32_t)info
.fileSize
});
3083 if (executableSegments
.segments
.empty()) {
3084 diag
.error("no exeutable segments");
3088 uint64_t loadAddress
= preferredLoadAddress();
3089 intptr_t slide
= getSlide();
3091 // next any function pointers in mod-term section
3092 const unsigned ptrSize
= pointerSize();
3093 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
3094 if ( (info
.sectFlags
& SECTION_TYPE
) == S_MOD_TERM_FUNC_POINTERS
) {
3095 const uint8_t* content
;
3096 content
= (uint8_t*)(info
.sectAddr
+ slide
);
3097 if ( (info
.sectSize
% ptrSize
) != 0 ) {
3098 diag
.error("terminator section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
);
3102 if ( malformedSectionRange
) {
3103 diag
.error("terminator section %s/%s extends beyond its segment", info
.segInfo
.segName
, info
.sectName
);
3107 if ( ((long)content
% ptrSize
) != 0 ) {
3108 diag
.error("terminator section %s/%s is not pointer aligned", info
.segInfo
.segName
, info
.sectName
);
3112 if ( ptrSize
== 8 ) {
3113 const uint64_t* initsStart
= (uint64_t*)content
;
3114 const uint64_t* initsEnd
= (uint64_t*)((uint8_t*)content
+ info
.sectSize
);
3115 for (const uint64_t* p
=initsStart
; p
< initsEnd
; ++p
) {
3116 uint64_t anInit
= vmAddrConverter
.convertToVMAddr(*p
);
3117 if ( !executableSegments
.contains(anInit
) ) {
3118 diag
.error("terminator 0x%0llX does not point within executable segment", anInit
);
3122 callback((uint32_t)(anInit
- loadAddress
));
3126 const uint32_t* initsStart
= (uint32_t*)content
;
3127 const uint32_t* initsEnd
= (uint32_t*)((uint8_t*)content
+ info
.sectSize
);
3128 for (const uint32_t* p
=initsStart
; p
< initsEnd
; ++p
) {
3129 uint32_t anInit
= (uint32_t)vmAddrConverter
.convertToVMAddr(*p
);
3130 if ( !executableSegments
.contains(anInit
) ) {
3131 diag
.error("terminator 0x%0X does not point within executable segment", anInit
);
3135 callback(anInit
- (uint32_t)loadAddress
);
3144 void MachOAnalyzer::forEachRPath(void (^callback
)(const char* rPath
, bool& stop
)) const
3147 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
3148 if ( cmd
->cmd
== LC_RPATH
) {
3149 const char* rpath
= (char*)cmd
+ ((struct rpath_command
*)cmd
)->path
.offset
;
3150 callback(rpath
, stop
);
3153 diag
.assertNoError(); // any malformations in the file should have been caught by earlier validate() call
3157 bool MachOAnalyzer::hasObjC() const
3159 __block
bool result
= false;
3160 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
3161 if ( (strcmp(info
.sectName
, "__objc_imageinfo") == 0) && (strncmp(info
.segInfo
.segName
, "__DATA", 6) == 0) ) {
3165 if ( (this->cputype
== CPU_TYPE_I386
) && (strcmp(info
.sectName
, "__image_info") == 0) && (strcmp(info
.segInfo
.segName
, "__OBJC") == 0) ) {
3173 bool MachOAnalyzer::usesObjCGarbageCollection() const
3175 __block
bool result
= false;
3176 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
3177 if ( (strcmp(info
.sectName
, "__objc_imageinfo") == 0) && (strncmp(info
.segInfo
.segName
, "__DATA", 6) == 0) ) {
3178 const uint64_t slide
= (uint64_t)this - preferredLoadAddress();
3179 const uint32_t* flags
= (uint32_t*)(info
.sectAddr
+ slide
);
3189 bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics
& diag
) const
3191 __block
bool result
= false;
3192 if ( (this->cputype
== CPU_TYPE_I386
) && this->builtForPlatform(Platform::macOS
) ) {
3193 // old objc runtime has no special section for +load methods, scan for string
3194 int64_t slide
= getSlide();
3195 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
3196 if ( ( (info
.sectFlags
& SECTION_TYPE
) == S_CSTRING_LITERALS
) ) {
3197 if ( malformedSectionRange
) {
3198 diag
.error("cstring section %s/%s extends beyond the end of the segment", info
.segInfo
.segName
, info
.sectName
);
3202 const uint8_t* content
= (uint8_t*)(info
.sectAddr
+ slide
);
3203 const char* s
= (char*)content
;
3204 const char* end
= s
+ info
.sectSize
;
3206 if ( strcmp(s
, "load") == 0 ) {
3219 // in new objc runtime compiler puts classes/categories with +load method in specical section
3220 forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
3221 if ( strncmp(info
.segInfo
.segName
, "__DATA", 6) != 0 )
3223 if ( (strcmp(info
.sectName
, "__objc_nlclslist") == 0) || (strcmp(info
.sectName
, "__objc_nlcatlist") == 0)) {
3232 bool MachOAnalyzer::isSwiftLibrary() const
3234 struct objc_image_info
{
3239 int64_t slide
= getSlide();
3240 __block
bool result
= false;
3241 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
3242 if ( (strncmp(sectInfo
.sectName
, "__objc_imageinfo", 16) == 0) && (strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) == 0) ) {
3243 objc_image_info
* info
= (objc_image_info
*)((uint8_t*)sectInfo
.sectAddr
+ slide
);
3244 uint32_t swiftVersion
= ((info
->flags
>> 8) & 0xFF);
3253 const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size
) const
3256 LinkEditInfo leInfo
;
3257 getLinkEditPointers(diag
, leInfo
);
3258 if ( diag
.hasError() || (leInfo
.dyldInfo
== nullptr) )
3261 size
= leInfo
.dyldInfo
->rebase_size
;
3262 return getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->rebase_off
);
3265 const void* MachOAnalyzer::getBindOpcodes(uint32_t& size
) const
3268 LinkEditInfo leInfo
;
3269 getLinkEditPointers(diag
, leInfo
);
3270 if ( diag
.hasError() || (leInfo
.dyldInfo
== nullptr) )
3273 size
= leInfo
.dyldInfo
->bind_size
;
3274 return getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->bind_off
);
3277 const void* MachOAnalyzer::getLazyBindOpcodes(uint32_t& size
) const
3280 LinkEditInfo leInfo
;
3281 getLinkEditPointers(diag
, leInfo
);
3282 if ( diag
.hasError() || (leInfo
.dyldInfo
== nullptr) )
3285 size
= leInfo
.dyldInfo
->lazy_bind_size
;
3286 return getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->lazy_bind_off
);
3289 const void* MachOAnalyzer::getSplitSeg(uint32_t& size
) const
3292 LinkEditInfo leInfo
;
3293 getLinkEditPointers(diag
, leInfo
);
3294 if ( diag
.hasError() || (leInfo
.splitSegInfo
== nullptr) )
3297 size
= leInfo
.splitSegInfo
->datasize
;
3298 return getLinkEditContent(leInfo
.layout
, leInfo
.splitSegInfo
->dataoff
);
3301 bool MachOAnalyzer::hasSplitSeg() const {
3302 uint32_t splitSegSize
= 0;
3303 const void* splitSegStart
= getSplitSeg(splitSegSize
);
3304 return splitSegStart
!= nullptr;
3307 bool MachOAnalyzer::isSplitSegV1() const {
3308 uint32_t splitSegSize
= 0;
3309 const void* splitSegStart
= getSplitSeg(splitSegSize
);
3313 return (*(const uint8_t*)splitSegStart
) != DYLD_CACHE_ADJ_V2_FORMAT
;
3316 bool MachOAnalyzer::isSplitSegV2() const {
3317 uint32_t splitSegSize
= 0;
3318 const void* splitSegStart
= getSplitSeg(splitSegSize
);
3322 return (*(const uint8_t*)splitSegStart
) == DYLD_CACHE_ADJ_V2_FORMAT
;
3326 uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex
, uint64_t targetSegOffset
) const
3328 __block
uint64_t textVmAddr
= 0;
3329 __block
uint64_t result
= 0;
3330 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
3331 if ( strcmp(info
.segName
, "__TEXT") == 0 )
3332 textVmAddr
= info
.vmAddr
;
3333 if ( info
.segIndex
== targetSegIndex
) {
3334 result
= (info
.vmAddr
- textVmAddr
) + targetSegOffset
;
3340 bool MachOAnalyzer::hasLazyPointers(uint32_t& runtimeOffset
, uint32_t& size
) const
3343 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
3344 if ( (info
.sectFlags
& SECTION_TYPE
) == S_LAZY_SYMBOL_POINTERS
) {
3345 runtimeOffset
= (uint32_t)(info
.sectAddr
- preferredLoadAddress());
3346 size
= (uint32_t)info
.sectSize
;
3353 uint64_t MachOAnalyzer::preferredLoadAddress() const
3355 __block
uint64_t textVmAddr
= 0;
3356 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
3357 if ( strcmp(info
.segName
, "__TEXT") == 0 ) {
3358 textVmAddr
= info
.vmAddr
;
3366 bool MachOAnalyzer::getEntry(uint64_t& offset
, bool& usesCRT
) const
3370 forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) {
3371 if ( cmd
->cmd
== LC_MAIN
) {
3372 entry_point_command
* mainCmd
= (entry_point_command
*)cmd
;
3374 offset
= mainCmd
->entryoff
;
3377 else if ( cmd
->cmd
== LC_UNIXTHREAD
) {
3380 uint64_t startAddress
= entryAddrFromThreadCmd((thread_command
*)cmd
);
3381 offset
= startAddress
- preferredLoadAddress();
3384 return (offset
!= 0);
3388 void MachOAnalyzer::forEachInterposingSection(Diagnostics
& diag
, void (^handler
)(uint64_t vmOffset
, uint64_t vmSize
, bool& stop
)) const
3390 const unsigned ptrSize
= pointerSize();
3391 const unsigned entrySize
= 2 * ptrSize
;
3392 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
3393 if ( ((info
.sectFlags
& SECTION_TYPE
) == S_INTERPOSING
) || ((strcmp(info
.sectName
, "__interpose") == 0) && (strcmp(info
.segInfo
.segName
, "__DATA") == 0)) ) {
3394 if ( info
.sectSize
% entrySize
!= 0 ) {
3395 diag
.error("interposing section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
);
3399 if ( malformedSectionRange
) {
3400 diag
.error("interposing section %s/%s extends beyond the end of the segment", info
.segInfo
.segName
, info
.sectName
);
3404 if ( (info
.sectAddr
% ptrSize
) != 0 ) {
3405 diag
.error("interposing section %s/%s is not pointer aligned", info
.segInfo
.segName
, info
.sectName
);
3409 handler(info
.sectAddr
- preferredLoadAddress(), info
.sectSize
, stop
);
3414 void MachOAnalyzer::forEachDOFSection(Diagnostics
& diag
, void (^callback
)(uint32_t offset
)) const
3416 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
3417 if ( ( (info
.sectFlags
& SECTION_TYPE
) == S_DTRACE_DOF
) && !malformedSectionRange
) {
3418 callback((uint32_t)(info
.sectAddr
- info
.segInfo
.vmAddr
));
3423 void MachOAnalyzer::forEachCDHash(void (^handler
)(const uint8_t cdHash
[20])) const
3426 LinkEditInfo leInfo
;
3427 getLinkEditPointers(diag
, leInfo
);
3428 if ( diag
.hasError() || (leInfo
.codeSig
== nullptr) )
3431 forEachCDHashOfCodeSignature(getLinkEditContent(leInfo
.layout
, leInfo
.codeSig
->dataoff
),
3432 leInfo
.codeSig
->datasize
,
3436 bool MachOAnalyzer::isRestricted() const
3438 __block
bool result
= false;
3439 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) {
3440 if ( (strcmp(info
.segInfo
.segName
, "__RESTRICT") == 0) && (strcmp(info
.sectName
, "__restrict") == 0) ) {
3448 bool MachOAnalyzer::usesLibraryValidation() const
3451 LinkEditInfo leInfo
;
3452 getLinkEditPointers(diag
, leInfo
);
3453 if ( diag
.hasError() || (leInfo
.codeSig
== nullptr) )
3456 // check for CS_REQUIRE_LV in CS_CodeDirectory.flags
3457 __block
bool requiresLV
= false;
3458 forEachCodeDirectoryBlob(getLinkEditContent(leInfo
.layout
, leInfo
.codeSig
->dataoff
),
3459 leInfo
.codeSig
->datasize
,
3460 ^(const void *cdBuffer
) {
3461 const CS_CodeDirectory
* cd
= (const CS_CodeDirectory
*)cdBuffer
;
3462 requiresLV
|= (htonl(cd
->flags
) & CS_REQUIRE_LV
);
3468 bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path
, void (^failureReason
)(const char*)) const
3470 if (!MachOFile::canHavePrecomputedDlopenClosure(path
, failureReason
))
3473 // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same
3474 // at runtime as when the shared cache processed it. We must have a code signature to record this information
3475 uint32_t codeSigFileOffset
;
3476 uint32_t codeSigSize
;
3477 if ( !hasCodeSignature(codeSigFileOffset
, codeSigSize
) ) {
3478 failureReason("no code signature");
3482 __block
bool retval
= true;
3484 // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed
3486 auto checkBind
= ^(int libOrdinal
, bool& stop
) {
3487 switch (libOrdinal
) {
3488 case BIND_SPECIAL_DYLIB_WEAK_LOOKUP
:
3489 failureReason("has weak externals");
3493 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP
:
3494 failureReason("has dynamic_lookup binds");
3498 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
:
3499 failureReason("has reference to main executable (bundle loader)");
3506 if (hasChainedFixups()) {
3507 forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char *symbolName
, uint64_t addend
, bool weakImport
, bool &stop
) {
3508 checkBind(libOrdinal
, stop
);
3511 forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
3512 checkBind(libOrdinal
, stop
);
3514 ^(const char* symbolName
) {
3522 bool MachOAnalyzer::hasUnalignedPointerFixups() const
3524 // only look at 64-bit architectures
3525 if ( pointerSize() == 4 )
3528 __block Diagnostics diag
;
3529 __block
bool result
= false;
3530 if ( hasChainedFixups() ) {
3531 withChainStarts(diag
, chainStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) {
3532 forEachFixupInAllChains(diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) {
3533 if ( ((long)(fixupLoc
) & 7) != 0 ) {
3541 forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
3542 if ( (runtimeOffset
& 7) != 0 ) {
3547 ^(const char* symbolName
) {
3549 forEachRebase(diag
, true, ^(uint64_t runtimeOffset
, bool& stop
) {
3550 if ( (runtimeOffset
& 7) != 0 ) {
3560 void MachOAnalyzer::recurseTrie(Diagnostics
& diag
, const uint8_t* const start
, const uint8_t* p
, const uint8_t* const end
,
3561 OverflowSafeArray
<char>& cummulativeString
, int curStrOffset
, bool& stop
, ExportsCallback callback
) const
3564 diag
.error("malformed trie, node past end");
3567 const uint64_t terminalSize
= read_uleb128(diag
, p
, end
);
3568 const uint8_t* children
= p
+ terminalSize
;
3569 if ( terminalSize
!= 0 ) {
3570 uint64_t imageOffset
= 0;
3571 uint64_t flags
= read_uleb128(diag
, p
, end
);
3573 const char* importName
= nullptr;
3574 if ( flags
& EXPORT_SYMBOL_FLAGS_REEXPORT
) {
3575 other
= read_uleb128(diag
, p
, end
); // dylib ordinal
3576 importName
= (char*)p
;
3579 imageOffset
= read_uleb128(diag
, p
, end
);
3580 if ( flags
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
)
3581 other
= read_uleb128(diag
, p
, end
);
3585 if ( diag
.hasError() )
3587 callback(cummulativeString
.begin(), imageOffset
, flags
, other
, importName
, stop
);
3591 if ( children
> end
) {
3592 diag
.error("malformed trie, terminalSize extends beyond trie data");
3595 const uint8_t childrenCount
= *children
++;
3596 const uint8_t* s
= children
;
3597 for (uint8_t i
=0; i
< childrenCount
; ++i
) {
3599 while (*s
!= '\0') {
3600 cummulativeString
.resize(curStrOffset
+edgeStrLen
+ 1);
3601 cummulativeString
[curStrOffset
+edgeStrLen
] = *s
++;
3604 diag
.error("malformed trie node, child node extends past end of trie\n");
3608 cummulativeString
.resize(curStrOffset
+edgeStrLen
+ 1);
3609 cummulativeString
[curStrOffset
+edgeStrLen
] = *s
++;
3610 uint64_t childNodeOffset
= read_uleb128(diag
, s
, end
);
3611 if (childNodeOffset
== 0) {
3612 diag
.error("malformed trie, childNodeOffset==0");
3615 recurseTrie(diag
, start
, start
+childNodeOffset
, end
, cummulativeString
, curStrOffset
+edgeStrLen
, stop
, callback
);
3616 if ( diag
.hasError() || stop
)
3621 void MachOAnalyzer::forEachExportedSymbol(Diagnostics
& diag
, ExportsCallback callback
) const
3623 LinkEditInfo leInfo
;
3624 getLinkEditPointers(diag
, leInfo
);
3625 if ( diag
.hasError() )
3628 if ( const uint8_t* trieStart
= getExportsTrie(leInfo
, trieSize
) ) {
3629 const uint8_t* trieEnd
= trieStart
+ trieSize
;
3630 // We still emit empty export trie load commands just as a placeholder to show we have
3631 // no exports. In that case, don't start recursing as we'll immediately think we ran
3632 // of the end of the buffer
3633 if ( trieSize
== 0 )
3636 STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString
, 4096);
3637 recurseTrie(diag
, trieStart
, trieStart
, trieEnd
, cummulativeString
, 0, stop
, callback
);
3641 bool MachOAnalyzer::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const
3643 if (!MachOFile::canBePlacedInDyldCache(path
, failureReason
))
3646 // arm64e requires split seg v2 as the split seg code can't handle chained fixups for split seg v1
3647 if ( isArch("arm64e") ) {
3648 uint32_t splitSegSize
= 0;
3649 const uint8_t* infoStart
= (const uint8_t*)getSplitSeg(splitSegSize
);
3650 if ( *infoStart
!= DYLD_CACHE_ADJ_V2_FORMAT
) {
3651 failureReason("chained fixups requires split seg v2");
3656 // <rdar://problem/57769033> dyld_cache_patchable_location only supports addend in range 0..31
3657 const bool is64bit
= is64();
3658 __block Diagnostics diag
;
3659 __block
bool addendTooLarge
= false;
3660 if ( this->hasChainedFixups() ) {
3661 // with chained fixups, addends can be in the import table or embedded in a bind pointer
3662 forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
3664 addend
&= 0x00FFFFFFFFFFFFFF; // ignore TBI
3665 if ( addend
> 31 ) {
3666 addendTooLarge
= true;
3670 // check each pointer for embedded addend
3671 withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) {
3672 forEachFixupInAllChains(diag
, starts
, false, ^(ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& stop
) {
3673 switch (segInfo
->pointer_format
) {
3674 case DYLD_CHAINED_PTR_ARM64E
:
3675 case DYLD_CHAINED_PTR_ARM64E_USERLAND
:
3676 case DYLD_CHAINED_PTR_ARM64E_USERLAND24
:
3677 if ( fixupLoc
->arm64e
.bind
.bind
&& !fixupLoc
->arm64e
.authBind
.auth
) {
3678 if ( fixupLoc
->arm64e
.bind
.addend
> 31 ) {
3679 addendTooLarge
= true;
3684 case DYLD_CHAINED_PTR_64
:
3685 case DYLD_CHAINED_PTR_64_OFFSET
:
3686 if ( fixupLoc
->generic64
.rebase
.bind
) {
3687 if ( fixupLoc
->generic64
.bind
.addend
> 31 ) {
3688 addendTooLarge
= true;
3693 case DYLD_CHAINED_PTR_32
:
3694 if ( fixupLoc
->generic32
.bind
.bind
) {
3695 if ( fixupLoc
->generic32
.bind
.addend
> 31 ) {
3696 addendTooLarge
= true;
3706 // scan bind opcodes for large addend
3707 forEachBind(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo
* segments
, bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
,
3708 uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
3710 addend
&= 0x00FFFFFFFFFFFFFF; // ignore TBI
3711 if ( addend
> 31 ) {
3712 addendTooLarge
= true;
3716 ^(const char* symbolName
) {
3719 if ( addendTooLarge
) {
3720 failureReason("bind addend too large");
3724 if ( !(isArch("x86_64") || isArch("x86_64h")) )
3727 if ( hasChainedFixups() )
3730 // evict swift dylibs with split seg v1 info
3731 if ( this->isSwiftLibrary() && this->isSplitSegV1() )
3734 __block
bool rebasesOk
= true;
3735 uint64_t startVMAddr
= preferredLoadAddress();
3736 uint64_t endVMAddr
= startVMAddr
+ mappedSize();
3737 forEachRebase(diag
, false, ^(uint64_t runtimeOffset
, bool &stop
) {
3738 // We allow TBI for x86_64 dylibs, but then require that the remainder of the offset
3739 // is a 32-bit offset from the mach-header.
3740 uint64_t value
= *(uint64_t*)((uint8_t*)this + runtimeOffset
);
3741 value
&= 0x00FFFFFFFFFFFFFFULL
;
3742 if ( (value
< startVMAddr
) || (value
>= endVMAddr
) ) {
3743 failureReason("rebase value out of range of dylib");
3749 // Also error if the rebase location is anything other than 4/8 byte aligned
3750 if ( (runtimeOffset
& 0x3) != 0 ) {
3751 failureReason("rebase value is not 4-byte aligned");
3760 #if BUILDING_APP_CACHE_UTIL
3761 bool MachOAnalyzer::canBePlacedInKernelCollection(const char* path
, void (^failureReason
)(const char*)) const
3763 if (!MachOFile::canBePlacedInKernelCollection(path
, failureReason
))
3766 // App caches reguire that everything be built with split seg v2
3767 // This is because v1 can't move anything other than __TEXT and __DATA
3768 // but kernels have __TEXT_EXEC and other segments
3769 if ( isKextBundle() ) {
3770 // x86_64 kext's might not have split seg
3771 if ( !isArch("x86_64") && !isArch("x86_64h") ) {
3772 if ( !isSplitSegV2() ) {
3773 failureReason("Missing split seg v2");
3777 } else if ( isStaticExecutable() ) {
3778 // The kernel must always have split seg V2
3779 if ( !isSplitSegV2() ) {
3780 failureReason("Missing split seg v2");
3784 // The kernel should have __TEXT and __TEXT_EXEC
3785 __block
bool foundText
= false;
3786 __block
bool foundTextExec
= false;
3787 __block
bool foundHIB
= false;
3788 __block
uint64_t hibernateVMAddr
= 0;
3789 __block
uint64_t hibernateVMSize
= 0;
3790 forEachSegment(^(const SegmentInfo
&segmentInfo
, bool &stop
) {
3791 if ( strcmp(segmentInfo
.segName
, "__TEXT") == 0 ) {
3794 if ( strcmp(segmentInfo
.segName
, "__TEXT_EXEC") == 0 ) {
3795 foundTextExec
= true;
3797 if ( strcmp(segmentInfo
.segName
, "__HIB") == 0 ) {
3799 hibernateVMAddr
= segmentInfo
.vmAddr
;
3800 hibernateVMSize
= segmentInfo
.vmSize
;
3804 failureReason("Expected __TEXT segment");
3807 if ( foundTextExec
&& foundHIB
) {
3808 failureReason("Expected __TEXT_EXEC or __HIB segment, but found both");
3811 if ( !foundTextExec
&& !foundHIB
) {
3812 failureReason("Expected __TEXT_EXEC or __HIB segment, but found neither");
3816 // The hibernate segment should be mapped before the base address
3818 uint64_t baseAddress
= preferredLoadAddress();
3819 if ( greaterThanAddOrOverflow(hibernateVMAddr
, hibernateVMSize
, baseAddress
) ) {
3820 failureReason("__HIB segment should be mapped before base address");
3826 // Don't allow kext's to have load addresses
3827 if ( isKextBundle() && (preferredLoadAddress() != 0) ) {
3828 failureReason("Has load address");
3832 if (hasChainedFixups()) {
3833 if ( usesClassicRelocationsInKernelCollection() ) {
3834 failureReason("Cannot use fixup chains with binary expecting classic relocations");
3838 __block
bool fixupsOk
= true;
3839 __block Diagnostics diag
;
3840 withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) {
3841 forEachFixupInAllChains(diag
, starts
, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
,
3842 const dyld_chained_starts_in_segment
* segInfo
, bool& stop
) {
3843 // We only support inputs from a few pointer format types, so that we don't need to handle them all later
3844 switch (segInfo
->pointer_format
) {
3845 case DYLD_CHAINED_PTR_ARM64E
:
3846 case DYLD_CHAINED_PTR_64
:
3847 case DYLD_CHAINED_PTR_32
:
3848 case DYLD_CHAINED_PTR_32_CACHE
:
3849 case DYLD_CHAINED_PTR_32_FIRMWARE
:
3850 failureReason("unsupported chained fixups pointer format");
3854 case DYLD_CHAINED_PTR_64_OFFSET
:
3855 // arm64 kernel and kexts use this format
3857 case DYLD_CHAINED_PTR_ARM64E_KERNEL
:
3858 // arm64e kexts use this format
3860 case DYLD_CHAINED_PTR_64_KERNEL_CACHE
:
3861 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
:
3862 failureReason("unsupported chained fixups pointer format");
3867 failureReason("unknown chained fixups pointer format");
3873 uint64_t vmOffset
= (uint8_t*)fixupLoc
- (uint8_t*)this;
3874 // Error if the fixup location is anything other than 4/8 byte aligned
3875 if ( (vmOffset
& 0x3) != 0 ) {
3876 failureReason("fixup value is not 4-byte aligned");
3882 // We also must only need 30-bits for the chain format of the resulting cache
3883 if ( vmOffset
>= (1 << 30) ) {
3884 failureReason("fixup value does not fit in 30-bits");
3894 // x86_64 xnu will have unaligned text/data fixups and fixups inside __HIB __text.
3895 // We allow these as xnu is emitted with classic relocations
3896 bool canHaveUnalignedFixups
= usesClassicRelocationsInKernelCollection();
3897 canHaveUnalignedFixups
|= ( isArch("x86_64") || isArch("x86_64h") );
3898 __block
bool rebasesOk
= true;
3900 forEachRebase(diag
, false, ^(uint64_t runtimeOffset
, bool &stop
) {
3901 // Error if the rebase location is anything other than 4/8 byte aligned
3902 if ( !canHaveUnalignedFixups
&& ((runtimeOffset
& 0x3) != 0) ) {
3903 failureReason("rebase value is not 4-byte aligned");
3909 #if BUILDING_APP_CACHE_UTIL
3910 // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
3911 // negative. Adjust the fixups so that we don't think they are out of
3912 // range of the number of bits we have
3913 if ( isStaticExecutable() ) {
3914 __block
uint64_t baseAddress
= ~0ULL;
3915 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) {
3916 baseAddress
= std::min(baseAddress
, info
.vmAddr
);
3918 uint64_t textSegVMAddr
= preferredLoadAddress();
3919 runtimeOffset
= (textSegVMAddr
+ runtimeOffset
) - baseAddress
;
3923 // We also must only need 30-bits for the chain format of the resulting cache
3924 if ( runtimeOffset
>= (1 << 30) ) {
3925 failureReason("rebase value does not fit in 30-bits");
3934 __block
bool bindsOk
= true;
3935 forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, uint8_t type
, const char *symbolName
,
3936 bool weakImport
, bool lazyBind
, uint64_t addend
, bool &stop
) {
3938 // Don't validate branch fixups as we'll turn then in to direct jumps instead
3939 if ( type
== BIND_TYPE_TEXT_PCREL32
)
3942 // Error if the bind location is anything other than 4/8 byte aligned
3943 if ( !canHaveUnalignedFixups
&& ((runtimeOffset
& 0x3) != 0) ) {
3944 failureReason("bind value is not 4-byte aligned");
3950 // We also must only need 30-bits for the chain format of the resulting cache
3951 if ( runtimeOffset
>= (1 << 30) ) {
3952 failureReason("bind value does not fit in 30-bits");
3957 }, ^(const char *symbolName
) {
3968 bool MachOAnalyzer::usesClassicRelocationsInKernelCollection() const {
3969 // The xnu x86_64 static executable needs to do the i386->x86_64 transition
3970 // so will be emitted with classic relocations
3971 if ( isArch("x86_64") || isArch("x86_64h") ) {
3972 return isStaticExecutable() || isFileSet();
3977 uint64_t MachOAnalyzer::chainStartsOffset() const
3979 const dyld_chained_fixups_header
* header
= chainedFixupsHeader();
3980 // old arm64e binary has no dyld_chained_fixups_header
3981 if ( header
== nullptr )
3983 return header
->starts_offset
+ ((uint8_t*)header
- (uint8_t*)this);
3986 const dyld_chained_fixups_header
* MachOAnalyzer::chainedFixupsHeader() const
3989 LinkEditInfo leInfo
;
3990 getLinkEditPointers(diag
, leInfo
);
3991 if ( diag
.hasError() || (leInfo
.chainedFixups
== nullptr) )
3994 return (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
);
3997 uint16_t MachOAnalyzer::chainedPointerFormat(const dyld_chained_fixups_header
* header
)
3999 const dyld_chained_starts_in_image
* startsInfo
= (dyld_chained_starts_in_image
*)((uint8_t*)header
+ header
->starts_offset
);
4000 for (uint32_t i
=0; i
< startsInfo
->seg_count
; ++i
) {
4001 uint32_t segInfoOffset
= startsInfo
->seg_info_offset
[i
];
4002 // 0 offset means this segment has no fixups
4003 if ( segInfoOffset
== 0 )
4005 const dyld_chained_starts_in_segment
* segInfo
= (dyld_chained_starts_in_segment
*)((uint8_t*)startsInfo
+ segInfoOffset
);
4006 if ( segInfo
->page_count
!= 0 )
4007 return segInfo
->pointer_format
;
4009 return 0; // no chains (perhaps no __DATA segment)
4012 uint16_t MachOAnalyzer::chainedPointerFormat() const
4014 const dyld_chained_fixups_header
* header
= chainedFixupsHeader();
4015 if ( header
!= nullptr ) {
4016 // get pointer format from chain info struct in LINKEDIT
4017 return chainedPointerFormat(header
);
4019 assert(this->cputype
== CPU_TYPE_ARM64
&& (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E
) && "chainedPointerFormat() called on non-chained binary");
4020 return DYLD_CHAINED_PTR_ARM64E
;
4023 #if (BUILDING_DYLD || BUILDING_LIBDYLD) && !__arm64e__
4024 #define SUPPORT_OLD_ARM64E_FORMAT 0
4026 #define SUPPORT_OLD_ARM64E_FORMAT 1
4029 // find dyld_chained_starts_in_image* in image
4030 // if old arm64e binary, synthesize dyld_chained_starts_in_image*
4031 void MachOAnalyzer::withChainStarts(Diagnostics
& diag
, uint64_t startsStructOffsetHint
, void (^callback
)(const dyld_chained_starts_in_image
*)) const
4033 if ( startsStructOffsetHint
!= 0 ) {
4034 // we have a pre-computed offset into LINKEDIT for dyld_chained_starts_in_image
4035 callback((dyld_chained_starts_in_image
*)((uint8_t*)this + startsStructOffsetHint
));
4039 LinkEditInfo leInfo
;
4040 getLinkEditPointers(diag
, leInfo
);
4041 if ( diag
.hasError() )
4044 if ( leInfo
.chainedFixups
!= nullptr ) {
4045 // find dyld_chained_starts_in_image from dyld_chained_fixups_header
4046 const dyld_chained_fixups_header
* header
= (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
);
4047 callback((dyld_chained_starts_in_image
*)((uint8_t*)header
+ header
->starts_offset
));
4049 #if SUPPORT_OLD_ARM64E_FORMAT
4050 // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up
4051 else if ( (leInfo
.dyldInfo
!= nullptr) && (this->cputype
== CPU_TYPE_ARM64
) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E
) ) {
4052 // old arm64e binary, create a dyld_chained_starts_in_image for caller
4053 uint64_t baseAddress
= preferredLoadAddress();
4054 BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer
, leInfo
.dyldInfo
->bind_size
+ 512);
4055 dyld_chained_starts_in_image
* header
= (dyld_chained_starts_in_image
*)buffer
;
4056 header
->seg_count
= leInfo
.layout
.linkeditSegIndex
;
4057 for (uint32_t i
=0; i
< header
->seg_count
; ++i
)
4058 header
->seg_info_offset
[i
] = 0;
4059 __block
uint8_t curSegIndex
= 0;
4060 __block dyld_chained_starts_in_segment
* curSeg
= (dyld_chained_starts_in_segment
*)(&(header
->seg_info_offset
[header
->seg_count
]));
4061 parseOrgArm64eChainedFixups(diag
, nullptr, nullptr, ^(const LinkEditInfo
& leInfo2
, const SegmentInfo segments
[], uint8_t segmentIndex
,
4062 bool segIndexSet
, uint64_t segmentOffset
, uint16_t format
, bool& stop
) {
4063 uint32_t pageIndex
= (uint32_t)(segmentOffset
/0x1000);
4064 if ( segmentIndex
!= curSegIndex
) {
4065 if ( curSegIndex
== 0 ) {
4066 header
->seg_info_offset
[segmentIndex
] = (uint32_t)((uint8_t*)curSeg
- buffer
);
4069 header
->seg_info_offset
[segmentIndex
] = (uint32_t)((uint8_t*)(&curSeg
->page_start
[curSeg
->page_count
]) - buffer
);
4070 curSeg
= (dyld_chained_starts_in_segment
*)((uint8_t*)header
+header
->seg_info_offset
[segmentIndex
]);
4072 curSeg
->page_count
= 0;
4073 curSegIndex
= segmentIndex
;
4075 while ( curSeg
->page_count
!= pageIndex
) {
4076 curSeg
->page_start
[curSeg
->page_count
] = 0xFFFF;
4077 curSeg
->page_count
++;
4079 curSeg
->size
= (uint32_t)((uint8_t*)(&curSeg
->page_start
[pageIndex
]) - (uint8_t*)curSeg
);
4080 curSeg
->page_size
= 0x1000; // old arm64e encoding used 4KB pages
4081 curSeg
->pointer_format
= DYLD_CHAINED_PTR_ARM64E
;
4082 curSeg
->segment_offset
= segments
[segmentIndex
].vmAddr
- baseAddress
;
4083 curSeg
->max_valid_pointer
= 0;
4084 curSeg
->page_count
= pageIndex
+1;
4085 curSeg
->page_start
[pageIndex
] = segmentOffset
& 0xFFF;
4086 //fprintf(stderr, "segment_offset=0x%llX, vmAddr=0x%llX\n", curSeg->segment_offset, segments[segmentIndex].vmAddr );
4087 //printf("segIndex=%d, segOffset=0x%08llX, page_start[%d]=0x%04X, page_start[%d]=0x%04X\n",
4088 // segmentIndex, segmentOffset, pageIndex, curSeg->page_start[pageIndex], pageIndex-1, pageIndex ? curSeg->page_start[pageIndex-1] : 0);
4094 diag
.error("image does not use chained fixups");
4098 struct OldThreadsStartSection
4100 uint32_t padding
: 31,
4102 uint32_t chain_starts
[1];
4105 bool MachOAnalyzer::hasFirmwareChainStarts(uint16_t* pointerFormat
, uint32_t* startsCount
, const uint32_t** starts
) const
4107 if ( !this->isPreload() && !this->isStaticExecutable() )
4110 uint64_t sectionSize
;
4111 if (const dyld_chained_starts_offsets
* sect
= (dyld_chained_starts_offsets
*)this->findSectionContent("__TEXT", "__chain_starts", sectionSize
) ) {
4112 *pointerFormat
= sect
->pointer_format
;
4113 *startsCount
= sect
->starts_count
;
4114 *starts
= §
->chain_starts
[0];
4117 if (const OldThreadsStartSection
* sect
= (OldThreadsStartSection
*)this->findSectionContent("__TEXT", "__thread_starts", sectionSize
) ) {
4118 *pointerFormat
= sect
->stride8
? DYLD_CHAINED_PTR_ARM64E
: DYLD_CHAINED_PTR_ARM64E_FIRMWARE
;
4119 *startsCount
= (uint32_t)(sectionSize
/4) - 1;
4120 *starts
= sect
->chain_starts
;
4127 MachOAnalyzer::ObjCInfo
MachOAnalyzer::getObjCInfo() const
4129 __block ObjCInfo result
;
4130 result
.selRefCount
= 0;
4131 result
.classDefCount
= 0;
4132 result
.protocolDefCount
= 0;
4134 const uint32_t ptrSize
= pointerSize();
4135 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
4136 if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) == 0 ) {
4137 if ( strcmp(sectInfo
.sectName
, "__objc_selrefs") == 0 )
4138 result
.selRefCount
+= (sectInfo
.sectSize
/ptrSize
);
4139 else if ( strcmp(sectInfo
.sectName
, "__objc_classlist") == 0 )
4140 result
.classDefCount
+= (sectInfo
.sectSize
/ptrSize
);
4141 else if ( strcmp(sectInfo
.sectName
, "__objc_protolist") == 0 )
4142 result
.protocolDefCount
+= (sectInfo
.sectSize
/ptrSize
);
4144 else if ( (this->cputype
== CPU_TYPE_I386
) && (strcmp(sectInfo
.segInfo
.segName
, "__OBJC") == 0) ) {
4145 if ( strcmp(sectInfo
.sectName
, "__message_refs") == 0 )
4146 result
.selRefCount
+= (sectInfo
.sectSize
/4);
4147 else if ( strcmp(sectInfo
.sectName
, "__class") == 0 )
4148 result
.classDefCount
+= (sectInfo
.sectSize
/48);
4149 else if ( strcmp(sectInfo
.sectName
, "__protocol") == 0 )
4150 result
.protocolDefCount
+= (sectInfo
.sectSize
/20);
4157 uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadOnlyDataField field
, uint32_t pointerSize
) const {
4158 if (pointerSize
== 8) {
4159 typedef uint64_t PtrTy
;
4162 uint32_t instanceStart
;
4163 // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
4164 // on 64-bit archs, but no padding on 32-bit archs.
4165 // This union is a way to model that.
4167 uint32_t instanceSize
;
4170 PtrTy ivarLayoutVMAddr
;
4172 PtrTy baseMethodsVMAddr
;
4173 PtrTy baseProtocolsVMAddr
;
4175 PtrTy weakIvarLayoutVMAddr
;
4176 PtrTy basePropertiesVMAddr
;
4178 const class_ro_t
* classData
= (const class_ro_t
*)(dataVMAddr
+ vmAddrConverter
.slide
);
4180 case ObjCClassInfo::ReadOnlyDataField::name
:
4181 return vmAddrConverter
.convertToVMAddr(classData
->nameVMAddr
);
4182 case ObjCClassInfo::ReadOnlyDataField::baseProtocols
:
4183 return vmAddrConverter
.convertToVMAddr(classData
->baseProtocolsVMAddr
);
4184 case ObjCClassInfo::ReadOnlyDataField::baseMethods
:
4185 return vmAddrConverter
.convertToVMAddr(classData
->baseMethodsVMAddr
);
4186 case ObjCClassInfo::ReadOnlyDataField::baseProperties
:
4187 return vmAddrConverter
.convertToVMAddr(classData
->basePropertiesVMAddr
);
4188 case ObjCClassInfo::ReadOnlyDataField::flags
:
4189 return classData
->flags
;
4192 typedef uint32_t PtrTy
;
4195 uint32_t instanceStart
;
4196 // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
4197 // on 64-bit archs, but no padding on 32-bit archs.
4198 // This union is a way to model that.
4200 uint32_t instanceSize
;
4203 PtrTy ivarLayoutVMAddr
;
4205 PtrTy baseMethodsVMAddr
;
4206 PtrTy baseProtocolsVMAddr
;
4208 PtrTy weakIvarLayoutVMAddr
;
4209 PtrTy basePropertiesVMAddr
;
4211 const class_ro_t
* classData
= (const class_ro_t
*)(dataVMAddr
+ vmAddrConverter
.slide
);
4213 case ObjCClassInfo::ReadOnlyDataField::name
:
4214 return vmAddrConverter
.convertToVMAddr(classData
->nameVMAddr
);
4215 case ObjCClassInfo::ReadOnlyDataField::baseProtocols
:
4216 return vmAddrConverter
.convertToVMAddr(classData
->baseProtocolsVMAddr
);
4217 case ObjCClassInfo::ReadOnlyDataField::baseMethods
:
4218 return vmAddrConverter
.convertToVMAddr(classData
->baseMethodsVMAddr
);
4219 case ObjCClassInfo::ReadOnlyDataField::baseProperties
:
4220 return vmAddrConverter
.convertToVMAddr(classData
->basePropertiesVMAddr
);
4221 case ObjCClassInfo::ReadOnlyDataField::flags
:
4222 return classData
->flags
;
4227 const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr
, MachOAnalyzer::PrintableStringResult
& result
,
4228 SectionCache
* sectionCache
,
4229 bool (^sectionHandler
)(const SectionInfo
& sectionInfo
)) const {
4230 if ( sectionCache
!= nullptr ) {
4231 // Make sure the string is pointing in to one of the supported sections
4232 __block
const dyld3::MachOAnalyzer::SectionInfo
* nameSectionInfo
= nullptr;
4233 for (const dyld3::MachOAnalyzer::SectionInfo
& sectionInfo
: sectionCache
->sectionInfos
) {
4234 if ( stringVMAddr
< sectionInfo
.sectAddr
) {
4237 if ( stringVMAddr
>= ( sectionInfo
.sectAddr
+ sectionInfo
.sectSize
) ) {
4240 nameSectionInfo
= §ionInfo
;
4244 if ( nameSectionInfo
!= nullptr ) {
4245 // The section handler may also reject this section
4246 if ( sectionHandler
!= nullptr ) {
4247 if (!sectionHandler(*nameSectionInfo
)) {
4248 result
= PrintableStringResult::UnknownSection
;
4253 result
= PrintableStringResult::CanPrint
;
4254 return (const char*)(stringVMAddr
+ getSlide());
4258 // If the name isn't in the cache then find the section its in
4260 uint32_t fairplayTextOffsetStart
;
4261 uint32_t fairplayTextOffsetEnd
;
4262 uint32_t fairplaySize
;
4263 if ( isFairPlayEncrypted(fairplayTextOffsetStart
, fairplaySize
) ) {
4264 fairplayTextOffsetEnd
= fairplayTextOffsetStart
+ fairplaySize
;
4266 fairplayTextOffsetEnd
= 0;
4269 result
= PrintableStringResult::UnknownSection
;
4270 forEachSection(^(const MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
4271 if ( stringVMAddr
< sectInfo
.sectAddr
) {
4274 if ( stringVMAddr
>= ( sectInfo
.sectAddr
+ sectInfo
.sectSize
) ) {
4278 // We can't scan this section if its protected
4279 if ( sectInfo
.segInfo
.isProtected
) {
4280 result
= PrintableStringResult::ProtectedSection
;
4285 // We can't scan this section if it overlaps with the fairplay range
4286 if ( fairplayTextOffsetEnd
< sectInfo
.sectFileOffset
) {
4287 // Fairplay range ends before section
4288 } else if ( fairplayTextOffsetStart
> (sectInfo
.sectFileOffset
+ sectInfo
.sectSize
) ) {
4289 // Fairplay range starts after section
4292 result
= PrintableStringResult::FairPlayEncrypted
;
4297 // The section handler may also reject this section
4298 if ( sectionHandler
!= nullptr ) {
4299 if (!sectionHandler(sectInfo
)) {
4300 result
= PrintableStringResult::UnknownSection
;
4305 // Cache this section for later.
4306 if ( sectionCache
!= nullptr ) {
4307 sectionCache
->sectionInfos
.push_back(sectInfo
);
4309 result
= PrintableStringResult::CanPrint
;
4313 #if BUILDING_SHARED_CACHE_UTIL || BUILDING_DYLDINFO
4314 // The shared cache coalesces strings in to their own section.
4315 // Assume its a valid pointer
4316 if (result
== PrintableStringResult::UnknownSection
) {
4317 result
= PrintableStringResult::CanPrint
;
4318 return (const char*)(stringVMAddr
+ getSlide());
4322 if (result
== PrintableStringResult::CanPrint
)
4323 return (const char*)(stringVMAddr
+ getSlide());
4327 bool MachOAnalyzer::SectionCache::findSectionForVMAddr(uint64_t vmAddr
, bool (^sectionHandler
)(const SectionInfo
& sectionInfo
)) {
4329 // Make sure the string is pointing in to one of the supported sections
4330 __block
const dyld3::MachOAnalyzer::SectionInfo
* foundSectionInfo
= nullptr;
4331 for (const dyld3::MachOAnalyzer::SectionInfo
& sectionInfo
: sectionInfos
) {
4332 if ( vmAddr
< sectionInfo
.sectAddr
) {
4335 if ( vmAddr
>= ( sectionInfo
.sectAddr
+ sectionInfo
.sectSize
) ) {
4338 foundSectionInfo
= §ionInfo
;
4342 if ( foundSectionInfo
!= nullptr ) {
4343 // The section handler may also reject this section
4344 if ( sectionHandler
!= nullptr ) {
4345 if (!sectionHandler(*foundSectionInfo
)) {
4350 // Found a section, so return true
4354 // If the name isn't in the cache then find the section its in
4356 uint32_t fairplayTextOffsetStart
;
4357 uint32_t fairplayTextOffsetEnd
;
4358 uint32_t fairplaySize
;
4359 if ( ma
->isFairPlayEncrypted(fairplayTextOffsetStart
, fairplaySize
) ) {
4360 fairplayTextOffsetEnd
= fairplayTextOffsetStart
+ fairplaySize
;
4362 fairplayTextOffsetEnd
= 0;
4365 __block
bool foundValidSection
= false;
4366 ma
->forEachSection(^(const MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
4367 if ( vmAddr
< sectInfo
.sectAddr
) {
4370 if ( vmAddr
>= ( sectInfo
.sectAddr
+ sectInfo
.sectSize
) ) {
4374 // We can't scan this section if it overlaps with the fairplay range
4375 if ( fairplayTextOffsetEnd
< sectInfo
.sectFileOffset
) {
4376 // Fairplay range ends before section
4377 } else if ( fairplayTextOffsetStart
> (sectInfo
.sectFileOffset
+ sectInfo
.sectSize
) ) {
4378 // Fairplay range starts after section
4385 // The section handler may also reject this section
4386 if ( sectionHandler
!= nullptr ) {
4387 if (!sectionHandler(sectInfo
)) {
4392 // Cache this section for later.
4393 sectionInfos
.push_back(sectInfo
);
4394 foundValidSection
= true;
4398 return foundValidSection
;
4401 void MachOAnalyzer::forEachObjCClass(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
,
4402 void (^handler
)(Diagnostics
& diag
, uint64_t classVMAddr
,
4403 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
4404 const ObjCClassInfo
& objcClass
, bool isMetaClass
)) const {
4405 const uint64_t ptrSize
= pointerSize();
4406 intptr_t slide
= getSlide();
4408 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
4409 if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 )
4411 if ( strcmp(sectInfo
.sectName
, "__objc_classlist") != 0 )
4413 const uint8_t* classList
= (uint8_t*)(sectInfo
.sectAddr
+ slide
);
4414 uint64_t classListSize
= sectInfo
.sectSize
;
4416 if ( (classListSize
% ptrSize
) != 0 ) {
4417 diag
.error("Invalid objc class section size");
4421 if ( ptrSize
== 8 ) {
4422 typedef uint64_t PtrTy
;
4424 for (uint64_t i
= 0; i
!= classListSize
; i
+= sizeof(PtrTy
)) {
4425 uint64_t classVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(classList
+ i
));
4426 parseObjCClass(diag
, vmAddrConverter
, classVMAddr
, ^(Diagnostics
& classDiag
, uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, const ObjCClassInfo
& objcClass
) {
4427 handler(classDiag
, classVMAddr
, classSuperclassVMAddr
, classDataVMAddr
, objcClass
, false);
4428 if (classDiag
.hasError())
4431 // Then parse and call for the metaclass
4432 uint64_t isaVMAddr
= objcClass
.isaVMAddr
;
4433 parseObjCClass(classDiag
, vmAddrConverter
, isaVMAddr
, ^(Diagnostics
& metaclassDiag
, uint64_t metaclassSuperclassVMAddr
, uint64_t metaclassDataVMAddr
, const ObjCClassInfo
& objcMetaclass
) {
4434 handler(metaclassDiag
, isaVMAddr
, metaclassSuperclassVMAddr
, metaclassDataVMAddr
, objcMetaclass
, true);
4437 if (diag
.hasError())
4441 typedef uint32_t PtrTy
;
4443 for (uint64_t i
= 0; i
!= classListSize
; i
+= sizeof(PtrTy
)) {
4444 uint64_t classVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(classList
+ i
));
4445 parseObjCClass(diag
, vmAddrConverter
, classVMAddr
, ^(Diagnostics
& classDiag
, uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, const ObjCClassInfo
& objcClass
) {
4446 handler(classDiag
, classVMAddr
, classSuperclassVMAddr
, classDataVMAddr
, objcClass
, false);
4447 if (classDiag
.hasError())
4450 // Then parse and call for the metaclass
4451 uint64_t isaVMAddr
= objcClass
.isaVMAddr
;
4452 parseObjCClass(classDiag
, vmAddrConverter
, isaVMAddr
, ^(Diagnostics
& metaclassDiag
, uint64_t metaclassSuperclassVMAddr
, uint64_t metaclassDataVMAddr
, const ObjCClassInfo
& objcMetaclass
) {
4453 handler(metaclassDiag
, isaVMAddr
, metaclassSuperclassVMAddr
, metaclassDataVMAddr
, objcMetaclass
, true);
4456 if (diag
.hasError())
4463 void MachOAnalyzer::parseObjCClass(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
,
4464 uint64_t classVMAddr
,
4465 void (^handler
)(Diagnostics
& diag
,
4466 uint64_t classSuperclassVMAddr
,
4467 uint64_t classDataVMAddr
,
4468 const ObjCClassInfo
& objcClass
)) const {
4469 const uint64_t ptrSize
= pointerSize();
4470 intptr_t slide
= getSlide();
4472 uint64_t classSuperclassVMAddr
= 0;
4473 uint64_t classDataVMAddr
= 0;
4474 ObjCClassInfo objcClass
;
4476 if ( ptrSize
== 8 ) {
4477 struct objc_class_t
{
4479 uint64_t superclassVMAddr
;
4480 uint64_t methodCacheBuckets
;
4481 uint64_t methodCacheProperties
;
4482 uint64_t dataVMAddrAndFastFlags
;
4484 // This matches "struct TargetClassMetadata" from Metadata.h in Swift
4485 struct swift_class_metadata_t
: objc_class_t
{
4486 uint32_t swiftClassFlags
;
4489 FAST_DATA_MASK
= 0x00007ffffffffff8ULL
4491 classSuperclassVMAddr
= classVMAddr
+ offsetof(objc_class_t
, superclassVMAddr
);
4492 classDataVMAddr
= classVMAddr
+ offsetof(objc_class_t
, dataVMAddrAndFastFlags
);
4494 // First call the handler on the class
4495 const objc_class_t
* classPtr
= (const objc_class_t
*)(classVMAddr
+ slide
);
4496 const swift_class_metadata_t
* swiftClassPtr
= (const swift_class_metadata_t
*)classPtr
;
4497 objcClass
.isaVMAddr
= vmAddrConverter
.convertToVMAddr(classPtr
->isaVMAddr
);
4498 objcClass
.superclassVMAddr
= vmAddrConverter
.convertToVMAddr(classPtr
->superclassVMAddr
);
4499 objcClass
.methodCacheVMAddr
= classPtr
->methodCacheProperties
== 0 ? 0 : vmAddrConverter
.convertToVMAddr(classPtr
->methodCacheProperties
);
4500 objcClass
.dataVMAddr
= vmAddrConverter
.convertToVMAddr(classPtr
->dataVMAddrAndFastFlags
) & FAST_DATA_MASK
;
4501 objcClass
.vmAddrConverter
= vmAddrConverter
;
4502 objcClass
.isSwiftLegacy
= classPtr
->dataVMAddrAndFastFlags
& ObjCClassInfo::FAST_IS_SWIFT_LEGACY
;
4503 objcClass
.isSwiftStable
= classPtr
->dataVMAddrAndFastFlags
& ObjCClassInfo::FAST_IS_SWIFT_STABLE
;
4504 // The Swift class flags are only present if the class is swift
4505 objcClass
.swiftClassFlags
= (objcClass
.isSwiftLegacy
|| objcClass
.isSwiftStable
) ? swiftClassPtr
->swiftClassFlags
: 0;
4507 struct objc_class_t
{
4509 uint32_t superclassVMAddr
;
4510 uint32_t methodCacheBuckets
;
4511 uint32_t methodCacheProperties
;
4512 uint32_t dataVMAddrAndFastFlags
;
4514 // This matches "struct TargetClassMetadata" from Metadata.h in Swift
4515 struct swift_class_metadata_t
: objc_class_t
{
4516 uint32_t swiftClassFlags
;
4519 FAST_DATA_MASK
= 0xfffffffcUL
4521 classSuperclassVMAddr
= classVMAddr
+ offsetof(objc_class_t
, superclassVMAddr
);
4522 classDataVMAddr
= classVMAddr
+ offsetof(objc_class_t
, dataVMAddrAndFastFlags
);
4524 // First call the handler on the class
4525 const objc_class_t
* classPtr
= (const objc_class_t
*)(classVMAddr
+ slide
);
4526 const swift_class_metadata_t
* swiftClassPtr
= (const swift_class_metadata_t
*)classPtr
;
4527 objcClass
.isaVMAddr
= vmAddrConverter
.convertToVMAddr(classPtr
->isaVMAddr
);
4528 objcClass
.superclassVMAddr
= vmAddrConverter
.convertToVMAddr(classPtr
->superclassVMAddr
);
4529 objcClass
.methodCacheVMAddr
= classPtr
->methodCacheProperties
== 0 ? 0 : vmAddrConverter
.convertToVMAddr(classPtr
->methodCacheProperties
);
4530 objcClass
.dataVMAddr
= vmAddrConverter
.convertToVMAddr(classPtr
->dataVMAddrAndFastFlags
) & FAST_DATA_MASK
;
4531 objcClass
.vmAddrConverter
= vmAddrConverter
;
4532 objcClass
.isSwiftLegacy
= classPtr
->dataVMAddrAndFastFlags
& ObjCClassInfo::FAST_IS_SWIFT_LEGACY
;
4533 objcClass
.isSwiftStable
= classPtr
->dataVMAddrAndFastFlags
& ObjCClassInfo::FAST_IS_SWIFT_STABLE
;
4534 // The Swift class flags are only present if the class is swift
4535 objcClass
.swiftClassFlags
= (objcClass
.isSwiftLegacy
|| objcClass
.isSwiftStable
) ? swiftClassPtr
->swiftClassFlags
: 0;
4538 handler(diag
, classSuperclassVMAddr
, classDataVMAddr
, objcClass
);
4541 void MachOAnalyzer::forEachObjCCategory(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
,
4542 void (^handler
)(Diagnostics
& diag
, uint64_t categoryVMAddr
,
4543 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
)) const {
4544 const uint64_t ptrSize
= pointerSize();
4545 intptr_t slide
= getSlide();
4547 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
4548 if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 )
4550 if ( strcmp(sectInfo
.sectName
, "__objc_catlist") != 0 )
4552 const uint8_t* categoryList
= (uint8_t*)(sectInfo
.sectAddr
+ slide
);
4553 uint64_t categoryListSize
= sectInfo
.sectSize
;
4555 if ( (categoryListSize
% ptrSize
) != 0 ) {
4556 diag
.error("Invalid objc category section size");
4560 if ( ptrSize
== 8 ) {
4561 typedef uint64_t PtrTy
;
4562 struct objc_category_t
{
4565 PtrTy instanceMethodsVMAddr
;
4566 PtrTy classMethodsVMAddr
;
4567 PtrTy protocolsVMAddr
;
4568 PtrTy instancePropertiesVMAddr
;
4570 for (uint64_t i
= 0; i
!= categoryListSize
; i
+= sizeof(PtrTy
)) {
4571 uint64_t categoryVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(categoryList
+ i
));
4573 const objc_category_t
* categoryPtr
= (const objc_category_t
*)(categoryVMAddr
+ slide
);
4574 ObjCCategory objCCategory
;
4575 objCCategory
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->nameVMAddr
);
4576 objCCategory
.clsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->clsVMAddr
);
4577 objCCategory
.instanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instanceMethodsVMAddr
);
4578 objCCategory
.classMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->classMethodsVMAddr
);
4579 objCCategory
.protocolsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->protocolsVMAddr
);
4580 objCCategory
.instancePropertiesVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instancePropertiesVMAddr
);
4581 handler(diag
, categoryVMAddr
, objCCategory
);
4582 if (diag
.hasError())
4586 typedef uint32_t PtrTy
;
4587 struct objc_category_t
{
4590 PtrTy instanceMethodsVMAddr
;
4591 PtrTy classMethodsVMAddr
;
4592 PtrTy protocolsVMAddr
;
4593 PtrTy instancePropertiesVMAddr
;
4595 for (uint64_t i
= 0; i
!= categoryListSize
; i
+= sizeof(PtrTy
)) {
4596 uint64_t categoryVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(categoryList
+ i
));
4598 const objc_category_t
* categoryPtr
= (const objc_category_t
*)(categoryVMAddr
+ slide
);
4599 ObjCCategory objCCategory
;
4600 objCCategory
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->nameVMAddr
);
4601 objCCategory
.clsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->clsVMAddr
);
4602 objCCategory
.instanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instanceMethodsVMAddr
);
4603 objCCategory
.classMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->classMethodsVMAddr
);
4604 objCCategory
.protocolsVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->protocolsVMAddr
);
4605 objCCategory
.instancePropertiesVMAddr
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instancePropertiesVMAddr
);
4606 handler(diag
, categoryVMAddr
, objCCategory
);
4607 if (diag
.hasError())
4614 void MachOAnalyzer::forEachObjCProtocol(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
,
4615 void (^handler
)(Diagnostics
& diag
, uint64_t categoryVMAddr
,
4616 const dyld3::MachOAnalyzer::ObjCProtocol
& objCProtocol
)) const {
4617 const uint64_t ptrSize
= pointerSize();
4618 intptr_t slide
= getSlide();
4620 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
4621 if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 )
4623 if ( strcmp(sectInfo
.sectName
, "__objc_protolist") != 0 )
4625 const uint8_t* protocolList
= (uint8_t*)(sectInfo
.sectAddr
+ slide
);
4626 uint64_t protocolListSize
= sectInfo
.sectSize
;
4628 if ( (protocolListSize
% ptrSize
) != 0 ) {
4629 diag
.error("Invalid objc protocol section size");
4633 if ( ptrSize
== 8 ) {
4634 typedef uint64_t PtrTy
;
4638 PtrTy protocolsVMAddr
;
4639 PtrTy instanceMethodsVMAddr
;
4640 PtrTy classMethodsVMAddr
;
4641 PtrTy optionalInstanceMethodsVMAddr
;
4642 PtrTy optionalClassMethodsVMAddr
;
4643 PtrTy instancePropertiesVMAddr
;
4646 // Fields below this point are not always present on disk.
4647 PtrTy extendedMethodTypesVMAddr
;
4648 PtrTy demangledNameVMAddr
;
4649 PtrTy classPropertiesVMAddr
;
4651 for (uint64_t i
= 0; i
!= protocolListSize
; i
+= sizeof(PtrTy
)) {
4652 uint64_t protocolVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(protocolList
+ i
));
4654 const protocol_t
* protocolPtr
= (const protocol_t
*)(protocolVMAddr
+ slide
);
4655 ObjCProtocol objCProtocol
;
4656 objCProtocol
.isaVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
);
4657 objCProtocol
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
);
4658 objCProtocol
.protocolsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
);
4659 objCProtocol
.instanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
);
4660 objCProtocol
.classMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
);
4661 objCProtocol
.optionalInstanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
);
4662 objCProtocol
.optionalClassMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
);
4664 handler(diag
, protocolVMAddr
, objCProtocol
);
4665 if (diag
.hasError())
4669 typedef uint32_t PtrTy
;
4673 PtrTy protocolsVMAddr
;
4674 PtrTy instanceMethodsVMAddr
;
4675 PtrTy classMethodsVMAddr
;
4676 PtrTy optionalInstanceMethodsVMAddr
;
4677 PtrTy optionalClassMethodsVMAddr
;
4678 PtrTy instancePropertiesVMAddr
;
4681 // Fields below this point are not always present on disk.
4682 PtrTy extendedMethodTypesVMAddr
;
4683 PtrTy demangledNameVMAddr
;
4684 PtrTy classPropertiesVMAddr
;
4686 for (uint64_t i
= 0; i
!= protocolListSize
; i
+= sizeof(PtrTy
)) {
4687 uint64_t protocolVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(protocolList
+ i
));
4689 const protocol_t
* protocolPtr
= (const protocol_t
*)(protocolVMAddr
+ slide
);
4690 ObjCProtocol objCProtocol
;
4691 objCProtocol
.isaVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
);
4692 objCProtocol
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
);
4693 objCProtocol
.protocolsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
);
4694 objCProtocol
.instanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
);
4695 objCProtocol
.classMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
);
4696 objCProtocol
.optionalInstanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
);
4697 objCProtocol
.optionalClassMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
);
4699 handler(diag
, protocolVMAddr
, objCProtocol
);
4700 if (diag
.hasError())
4707 void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr
, const VMAddrConverter
& vmAddrConverter
,
4708 void (^handler
)(uint64_t methodVMAddr
, const ObjCMethod
& method
),
4709 bool* isRelativeMethodList
) const {
4710 if ( methodListVMAddr
== 0 )
4713 const uint64_t ptrSize
= pointerSize();
4714 intptr_t slide
= getSlide();
4716 if ( ptrSize
== 8 ) {
4717 typedef uint64_t PtrTy
;
4718 struct method_list_t
{
4721 PtrTy methodArrayBase
; // Note this is the start the array method_t[0]
4723 uint32_t getEntsize() const {
4724 return entsize
& ObjCMethodList::methodListSizeMask
;
4727 bool usesDirectOffsetsToSelectors() const {
4728 return (entsize
& 0x40000000) != 0;
4731 bool usesRelativeOffsets() const {
4732 return (entsize
& 0x80000000) != 0;
4737 PtrTy nameVMAddr
; // SEL
4738 PtrTy typesVMAddr
; // const char *
4739 PtrTy impVMAddr
; // IMP
4742 struct relative_method_t
{
4743 int32_t nameOffset
; // SEL*
4744 int32_t typesOffset
; // const char *
4745 int32_t impOffset
; // IMP
4748 const method_list_t
* methodList
= (const method_list_t
*)(methodListVMAddr
+ slide
);
4749 if ( methodList
== nullptr )
4751 bool relativeMethodListsAreOffsetsToSelectors
= methodList
->usesDirectOffsetsToSelectors();
4752 uint64_t methodListArrayBaseVMAddr
= methodListVMAddr
+ offsetof(method_list_t
, methodArrayBase
);
4753 for (unsigned i
= 0; i
!= methodList
->count
; ++i
) {
4754 uint64_t methodEntryOffset
= i
* methodList
->getEntsize();
4755 uint64_t methodVMAddr
= methodListArrayBaseVMAddr
+ methodEntryOffset
;
4757 if ( methodList
->usesRelativeOffsets() ) {
4758 const relative_method_t
* methodPtr
= (const relative_method_t
*)(methodVMAddr
+ slide
);
4759 if ( relativeMethodListsAreOffsetsToSelectors
) {
4760 method
.nameVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
;
4762 PtrTy
* nameLocation
= (PtrTy
*)((uint8_t*)&methodPtr
->nameOffset
+ methodPtr
->nameOffset
);
4763 method
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(*nameLocation
);
4765 method
.typesVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, typesOffset
) + methodPtr
->typesOffset
;
4766 method
.impVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, impOffset
) + methodPtr
->impOffset
;
4767 method
.nameLocationVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
;
4769 const method_t
* methodPtr
= (const method_t
*)(methodVMAddr
+ slide
);
4770 method
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(methodPtr
->nameVMAddr
);
4771 method
.typesVMAddr
= vmAddrConverter
.convertToVMAddr(methodPtr
->typesVMAddr
);
4772 method
.impVMAddr
= vmAddrConverter
.convertToVMAddr(methodPtr
->impVMAddr
);
4773 method
.nameLocationVMAddr
= methodVMAddr
+ offsetof(method_t
, nameVMAddr
);
4775 handler(methodVMAddr
, method
);
4778 if ( isRelativeMethodList
!= nullptr )
4779 *isRelativeMethodList
= methodList
->usesRelativeOffsets();
4781 typedef uint32_t PtrTy
;
4782 struct method_list_t
{
4785 PtrTy methodArrayBase
; // Note this is the start the array method_t[0]
4787 uint32_t getEntsize() const {
4788 return entsize
& ObjCMethodList::methodListSizeMask
;
4791 bool usesDirectOffsetsToSelectors() const {
4792 return (entsize
& 0x40000000) != 0;
4795 bool usesRelativeOffsets() const {
4796 return (entsize
& 0x80000000) != 0;
4801 PtrTy nameVMAddr
; // SEL
4802 PtrTy typesVMAddr
; // const char *
4803 PtrTy impVMAddr
; // IMP
4806 struct relative_method_t
{
4807 int32_t nameOffset
; // SEL*
4808 int32_t typesOffset
; // const char *
4809 int32_t impOffset
; // IMP
4812 const method_list_t
* methodList
= (const method_list_t
*)(methodListVMAddr
+ slide
);
4813 if ( methodList
== nullptr )
4815 bool relativeMethodListsAreOffsetsToSelectors
= methodList
->usesDirectOffsetsToSelectors();
4816 uint64_t methodListArrayBaseVMAddr
= methodListVMAddr
+ offsetof(method_list_t
, methodArrayBase
);
4817 for (unsigned i
= 0; i
!= methodList
->count
; ++i
) {
4818 uint64_t methodEntryOffset
= i
* methodList
->getEntsize();
4819 uint64_t methodVMAddr
= methodListArrayBaseVMAddr
+ methodEntryOffset
;
4821 if ( methodList
->usesRelativeOffsets() ) {
4822 const relative_method_t
* methodPtr
= (const relative_method_t
*)(methodVMAddr
+ slide
);
4823 if ( relativeMethodListsAreOffsetsToSelectors
) {
4824 method
.nameVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
;
4826 PtrTy
* nameLocation
= (PtrTy
*)((uint8_t*)&methodPtr
->nameOffset
+ methodPtr
->nameOffset
);
4827 method
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(*nameLocation
);
4829 method
.typesVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, typesOffset
) + methodPtr
->typesOffset
;
4830 method
.impVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, impOffset
) + methodPtr
->impOffset
;
4831 method
.nameLocationVMAddr
= methodVMAddr
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
;
4833 const method_t
* methodPtr
= (const method_t
*)(methodVMAddr
+ slide
);
4834 method
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(methodPtr
->nameVMAddr
);
4835 method
.typesVMAddr
= vmAddrConverter
.convertToVMAddr(methodPtr
->typesVMAddr
);
4836 method
.impVMAddr
= vmAddrConverter
.convertToVMAddr(methodPtr
->impVMAddr
);
4837 method
.nameLocationVMAddr
= methodVMAddr
+ offsetof(method_t
, nameVMAddr
);
4839 handler(methodVMAddr
, method
);
4842 if ( isRelativeMethodList
!= nullptr )
4843 *isRelativeMethodList
= methodList
->usesRelativeOffsets();
4847 void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr
, const VMAddrConverter
& vmAddrConverter
,
4848 void (^handler
)(uint64_t propertyVMAddr
, const ObjCProperty
& property
)) const {
4849 if ( propertyListVMAddr
== 0 )
4852 const uint64_t ptrSize
= pointerSize();
4853 intptr_t slide
= getSlide();
4855 if ( ptrSize
== 8 ) {
4856 typedef uint64_t PtrTy
;
4857 struct property_list_t
{
4860 PtrTy propertyArrayBase
; // Note this is the start the array property_t[0]
4862 uint32_t getEntsize() const {
4863 return (entsize
) & ~(uint32_t)3;
4868 PtrTy nameVMAddr
; // SEL
4869 PtrTy attributesVMAddr
; // const char *
4872 const property_list_t
* propertyList
= (const property_list_t
*)(propertyListVMAddr
+ slide
);
4873 uint64_t propertyListArrayBaseVMAddr
= propertyListVMAddr
+ offsetof(property_list_t
, propertyArrayBase
);
4874 for (unsigned i
= 0; i
!= propertyList
->count
; ++i
) {
4875 uint64_t propertyEntryOffset
= i
* propertyList
->getEntsize();
4876 uint64_t propertyVMAddr
= propertyListArrayBaseVMAddr
+ propertyEntryOffset
;
4877 const property_t
* propertyPtr
= (const property_t
*)(propertyVMAddr
+ slide
);
4878 ObjCProperty property
;
4879 property
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(propertyPtr
->nameVMAddr
);
4880 property
.attributesVMAddr
= vmAddrConverter
.convertToVMAddr(propertyPtr
->attributesVMAddr
);
4881 handler(propertyVMAddr
, property
);
4884 typedef uint32_t PtrTy
;
4885 struct property_list_t
{
4888 PtrTy propertyArrayBase
; // Note this is the start the array property_t[0]
4890 uint32_t getEntsize() const {
4891 return (entsize
) & ~(uint32_t)3;
4896 PtrTy nameVMAddr
; // SEL
4897 PtrTy attributesVMAddr
; // const char *
4900 const property_list_t
* propertyList
= (const property_list_t
*)(propertyListVMAddr
+ slide
);
4901 uint64_t propertyListArrayBaseVMAddr
= propertyListVMAddr
+ offsetof(property_list_t
, propertyArrayBase
);
4902 for (unsigned i
= 0; i
!= propertyList
->count
; ++i
) {
4903 uint64_t propertyEntryOffset
= i
* propertyList
->getEntsize();
4904 uint64_t propertyVMAddr
= propertyListArrayBaseVMAddr
+ propertyEntryOffset
;
4905 const property_t
* propertyPtr
= (const property_t
*)(propertyVMAddr
+ slide
);
4906 ObjCProperty property
;
4907 property
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(propertyPtr
->nameVMAddr
);
4908 property
.attributesVMAddr
= vmAddrConverter
.convertToVMAddr(propertyPtr
->attributesVMAddr
);
4909 handler(propertyVMAddr
, property
);
4914 void MachOAnalyzer::forEachObjCProtocol(uint64_t protocolListVMAddr
, const VMAddrConverter
& vmAddrConverter
,
4915 void (^handler
)(uint64_t protocolRefVMAddr
, const ObjCProtocol
&)) const
4917 if ( protocolListVMAddr
== 0 )
4920 auto ptrSize
= pointerSize();
4921 intptr_t slide
= getSlide();
4923 if ( ptrSize
== 8 ) {
4924 typedef uint64_t PtrTy
;
4925 struct protocol_ref_t
{
4928 struct protocol_list_t
{
4930 protocol_ref_t array
[];
4935 PtrTy protocolsVMAddr
;
4936 PtrTy instanceMethodsVMAddr
;
4937 PtrTy classMethodsVMAddr
;
4938 PtrTy optionalInstanceMethodsVMAddr
;
4939 PtrTy optionalClassMethodsVMAddr
;
4940 PtrTy instancePropertiesVMAddr
;
4943 // Fields below this point are not always present on disk.
4944 PtrTy extendedMethodTypesVMAddr
;
4945 PtrTy demangledNameVMAddr
;
4946 PtrTy classPropertiesVMAddr
;
4949 const protocol_list_t
* protoList
= (const protocol_list_t
*)(protocolListVMAddr
+ slide
);
4950 for (PtrTy i
= 0; i
!= protoList
->count
; ++i
) {
4951 uint64_t protocolVMAddr
= vmAddrConverter
.convertToVMAddr(protoList
->array
[i
].refVMAddr
);
4953 const protocol_t
* protocolPtr
= (const protocol_t
*)(protocolVMAddr
+ slide
);
4954 ObjCProtocol objCProtocol
;
4955 objCProtocol
.isaVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
);
4956 objCProtocol
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
);
4957 objCProtocol
.protocolsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
);
4958 objCProtocol
.instanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
);
4959 objCProtocol
.classMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
);
4960 objCProtocol
.optionalInstanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
);
4961 objCProtocol
.optionalClassMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
);
4963 handler(protocolVMAddr
, objCProtocol
);
4966 typedef uint32_t PtrTy
;
4967 struct protocol_ref_t
{
4970 struct protocol_list_t
{
4972 protocol_ref_t array
[];
4977 PtrTy protocolsVMAddr
;
4978 PtrTy instanceMethodsVMAddr
;
4979 PtrTy classMethodsVMAddr
;
4980 PtrTy optionalInstanceMethodsVMAddr
;
4981 PtrTy optionalClassMethodsVMAddr
;
4982 PtrTy instancePropertiesVMAddr
;
4985 // Fields below this point are not always present on disk.
4986 PtrTy extendedMethodTypesVMAddr
;
4987 PtrTy demangledNameVMAddr
;
4988 PtrTy classPropertiesVMAddr
;
4991 const protocol_list_t
* protoList
= (const protocol_list_t
*)(protocolListVMAddr
+ slide
);
4992 for (PtrTy i
= 0; i
!= protoList
->count
; ++i
) {
4993 uint64_t protocolVMAddr
= vmAddrConverter
.convertToVMAddr(protoList
->array
[i
].refVMAddr
);
4995 const protocol_t
* protocolPtr
= (const protocol_t
*)(protocolVMAddr
+ slide
);
4996 ObjCProtocol objCProtocol
;
4997 objCProtocol
.isaVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
);
4998 objCProtocol
.nameVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
);
4999 objCProtocol
.protocolsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
);
5000 objCProtocol
.instanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
);
5001 objCProtocol
.classMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
);
5002 objCProtocol
.optionalInstanceMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
);
5003 objCProtocol
.optionalClassMethodsVMAddr
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
);
5005 handler(protocolVMAddr
, objCProtocol
);
5011 void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
,
5012 void (^handler
)(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
)) const {
5013 const uint64_t ptrSize
= pointerSize();
5014 intptr_t slide
= getSlide();
5016 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
5017 if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 )
5019 if ( strcmp(sectInfo
.sectName
, "__objc_selrefs") != 0 )
5021 uint64_t selRefSectionVMAddr
= sectInfo
.sectAddr
;
5022 const uint8_t* selRefs
= (uint8_t*)(selRefSectionVMAddr
+ slide
);
5023 uint64_t selRefsSize
= sectInfo
.sectSize
;
5025 if ( (selRefsSize
% ptrSize
) != 0 ) {
5026 diag
.error("Invalid sel ref section size");
5030 if ( ptrSize
== 8 ) {
5031 typedef uint64_t PtrTy
;
5032 for (uint64_t i
= 0; i
!= selRefsSize
; i
+= sizeof(PtrTy
)) {
5033 uint64_t selRefVMAddr
= selRefSectionVMAddr
+ i
;
5034 uint64_t selRefTargetVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(selRefs
+ i
));
5035 handler(selRefVMAddr
, selRefTargetVMAddr
);
5036 if (diag
.hasError()) {
5042 typedef uint32_t PtrTy
;
5043 for (uint64_t i
= 0; i
!= selRefsSize
; i
+= sizeof(PtrTy
)) {
5044 uint64_t selRefVMAddr
= selRefSectionVMAddr
+ i
;
5045 uint64_t selRefTargetVMAddr
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(selRefs
+ i
));
5046 handler(selRefVMAddr
, selRefTargetVMAddr
);
5047 if (diag
.hasError()) {
5056 void MachOAnalyzer::forEachObjCMethodName(void (^handler
)(const char* methodName
)) const {
5057 intptr_t slide
= getSlide();
5058 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
5059 if ( strcmp(sectInfo
.segInfo
.segName
, "__TEXT") != 0 )
5061 if ( strcmp(sectInfo
.sectName
, "__objc_methname") != 0 )
5063 if ( sectInfo
.segInfo
.isProtected
|| ( (sectInfo
.sectFlags
& SECTION_TYPE
) != S_CSTRING_LITERALS
) ) {
5067 if ( malformedSectionRange
) {
5072 const char* content
= (const char*)(sectInfo
.sectAddr
+ slide
);
5073 uint64_t sectionSize
= sectInfo
.sectSize
;
5075 const char* s
= (const char*)content
;
5076 const char* end
= s
+ sectionSize
;
5085 bool MachOAnalyzer::hasObjCMessageReferences() const {
5087 __block
bool foundSection
= false;
5088 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
5089 if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 )
5091 if ( strcmp(sectInfo
.sectName
, "__objc_msgrefs") != 0 )
5093 foundSection
= true;
5096 return foundSection
;
5099 const MachOAnalyzer::ObjCImageInfo
* MachOAnalyzer::objcImageInfo() const {
5100 int64_t slide
= getSlide();
5102 __block
bool foundInvalidObjCImageInfo
= false;
5103 __block
const ObjCImageInfo
* imageInfo
= nullptr;
5104 forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& sectionInfo
, bool malformedSectionRange
, bool& stop
) {
5105 if ( strncmp(sectionInfo
.segInfo
.segName
, "__DATA", 6) != 0 )
5107 if (strcmp(sectionInfo
.sectName
, "__objc_imageinfo") != 0)
5109 if ( malformedSectionRange
) {
5113 if ( sectionInfo
.sectSize
!= 8 ) {
5117 imageInfo
= (const ObjCImageInfo
*)(sectionInfo
.sectAddr
+ slide
);
5118 if ( (imageInfo
->flags
& ObjCImageInfo::dyldPreoptimized
) != 0 ) {
5119 foundInvalidObjCImageInfo
= true;
5125 if ( foundInvalidObjCImageInfo
)
5130 uint32_t MachOAnalyzer::loadCommandsFreeSpace() const
5132 __block
uint32_t firstSectionFileOffset
= 0;
5133 __block
uint32_t firstSegmentFileOffset
= 0;
5134 forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
5135 firstSectionFileOffset
= sectInfo
.sectFileOffset
;
5136 firstSegmentFileOffset
= (uint32_t)sectInfo
.segInfo
.fileOffset
;
5140 uint32_t headerSize
= (this->magic
== MH_MAGIC_64
) ? sizeof(mach_header_64
) : sizeof(mach_header
);
5141 uint32_t existSpaceUsed
= this->sizeofcmds
+ headerSize
;
5142 return firstSectionFileOffset
- firstSegmentFileOffset
- existSpaceUsed
;
5145 void MachOAnalyzer::forEachWeakDef(Diagnostics
& diag
,
5146 void (^handler
)(const char* symbolName
, uint64_t imageOffset
, bool isFromExportTrie
)) const {
5147 uint64_t baseAddress
= preferredLoadAddress();
5148 forEachGlobalSymbol(diag
, ^(const char *symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool &stop
) {
5149 if ( (n_desc
& N_WEAK_DEF
) != 0 ) {
5150 handler(symbolName
, n_value
- baseAddress
, false);
5153 forEachExportedSymbol(diag
, ^(const char *symbolName
, uint64_t imageOffset
, uint64_t flags
, uint64_t other
, const char *importName
, bool &stop
) {
5154 if ( (flags
& EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION
) == 0 )
5156 // Skip resolvers and re-exports
5157 if ( (flags
& EXPORT_SYMBOL_FLAGS_REEXPORT
) != 0 )
5159 if ( (flags
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
) != 0 )
5161 handler(symbolName
, imageOffset
, true);