1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
36 #include <unordered_set>
38 #include "configure.h"
40 #include "MachOFileAbstraction.hpp"
41 #include "Architectures.hpp"
44 __attribute__((noreturn
))
45 void throwf(const char* format
, ...)
49 va_start(list
, format
);
50 vasprintf(&p
, format
, list
);
57 static uint64_t read_uleb128(const uint8_t*& p
, const uint8_t* end
)
63 throwf("malformed uleb128");
65 uint64_t slice
= *p
& 0x7f;
67 if (bit
>= 64 || slice
<< bit
>> bit
!= slice
)
68 throwf("uleb128 too big");
70 result
|= (slice
<< bit
);
79 static int64_t read_sleb128(const uint8_t*& p
, const uint8_t* end
)
86 throwf("malformed sleb128");
88 result
|= ((byte
& 0x7f) << bit
);
90 } while (byte
& 0x80);
91 // sign extend negative numbers
92 if ( (byte
& 0x40) != 0 )
93 result
|= (-1LL) << bit
;
102 static bool validFile(const uint8_t* fileContent
);
103 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
104 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
105 virtual ~MachOChecker() {}
109 typedef typename
A::P P
;
110 typedef typename
A::P::E E
;
111 typedef typename
A::P::uint_t pint_t
;
113 // utility classes for using std::unordered_map with c-strings
115 size_t operator()(const char* __s
) const {
118 __h
= 5 * __h
+ *__s
;
124 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
127 typedef std::unordered_set
<const char*, CStringHash
, CStringEquals
> StringSet
;
129 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
130 void checkMachHeader();
131 void checkLoadCommands();
132 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
133 uint8_t loadCommandSizeMask();
134 void checkSymbolTable();
135 void checkInitTerms();
136 void checkIndirectSymbolTable();
137 void checkRelocations();
138 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
139 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
141 bool addressInWritableSegment(pint_t address
);
142 bool hasTextRelocInRange(pint_t start
, pint_t end
);
143 pint_t
segStartAddress(uint8_t segIndex
);
144 bool addressIsRebaseSite(pint_t addr
);
145 bool addressIsBindingSite(pint_t addr
);
146 pint_t
getInitialStackPointer(const macho_thread_command
<P
>*);
147 pint_t
getEntryPoint(const macho_thread_command
<P
>*);
152 const macho_header
<P
>* fHeader
;
154 const char* fStrings
;
155 const char* fStringsEnd
;
156 const macho_nlist
<P
>* fSymbols
;
157 uint32_t fSymbolCount
;
158 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
159 const uint32_t* fIndirectTable
;
160 uint32_t fIndirectTableCount
;
161 const macho_relocation_info
<P
>* fLocalRelocations
;
162 uint32_t fLocalRelocationsCount
;
163 const macho_relocation_info
<P
>* fExternalRelocations
;
164 uint32_t fExternalRelocationsCount
;
165 bool fWriteableSegmentWithAddrOver4G
;
167 const macho_segment_command
<P
>* fFirstSegment
;
168 const macho_segment_command
<P
>* fFirstWritableSegment
;
169 const macho_segment_command
<P
>* fTEXTSegment
;
170 const macho_dyld_info_command
<P
>* fDyldInfo
;
171 uint32_t fSectionCount
;
172 std::vector
<const macho_segment_command
<P
>*>fSegments
;
178 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
180 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
181 if ( header
->magic() != MH_MAGIC
)
183 if ( header
->cputype() != CPU_TYPE_POWERPC
)
185 switch (header
->filetype()) {
196 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
198 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
199 if ( header
->magic() != MH_MAGIC_64
)
201 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
203 switch (header
->filetype()) {
214 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
216 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
217 if ( header
->magic() != MH_MAGIC
)
219 if ( header
->cputype() != CPU_TYPE_I386
)
221 switch (header
->filetype()) {
232 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
234 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
235 if ( header
->magic() != MH_MAGIC_64
)
237 if ( header
->cputype() != CPU_TYPE_X86_64
)
239 switch (header
->filetype()) {
250 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
252 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
253 if ( header
->magic() != MH_MAGIC
)
255 if ( header
->cputype() != CPU_TYPE_ARM
)
257 switch (header
->filetype()) {
267 #if SUPPORT_ARCH_arm64
269 bool MachOChecker
<arm64
>::validFile(const uint8_t* fileContent
)
271 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
272 if ( header
->magic() != MH_MAGIC_64
)
274 if ( header
->cputype() != CPU_TYPE_ARM64
)
276 switch (header
->filetype()) {
287 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
288 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
289 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
290 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
291 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
292 #if SUPPORT_ARCH_arm64
293 template <> uint8_t MachOChecker
<arm64
>::loadCommandSizeMask() { return 0x07; }
298 ppc::P::uint_t MachOChecker
<ppc
>::getInitialStackPointer(const macho_thread_command
<ppc::P
>* threadInfo
)
300 return threadInfo
->thread_register(3);
304 ppc64::P::uint_t MachOChecker
<ppc64
>::getInitialStackPointer(const macho_thread_command
<ppc64::P
>* threadInfo
)
306 return threadInfo
->thread_register(3);
310 x86::P::uint_t MachOChecker
<x86
>::getInitialStackPointer(const macho_thread_command
<x86::P
>* threadInfo
)
312 return threadInfo
->thread_register(7);
316 x86_64::P::uint_t MachOChecker
<x86_64
>::getInitialStackPointer(const macho_thread_command
<x86_64::P
>* threadInfo
)
318 return threadInfo
->thread_register(7);
322 arm::P::uint_t MachOChecker
<arm
>::getInitialStackPointer(const macho_thread_command
<arm::P
>* threadInfo
)
324 return threadInfo
->thread_register(13);
327 #if SUPPORT_ARCH_arm64
329 arm64::P::uint_t MachOChecker
<arm64
>::getInitialStackPointer(const macho_thread_command
<arm64::P
>* threadInfo
)
331 throw "LC_UNIXTHREAD not supported for arm64";
336 ppc::P::uint_t MachOChecker
<ppc
>::getEntryPoint(const macho_thread_command
<ppc::P
>* threadInfo
)
338 return threadInfo
->thread_register(0);
342 ppc64::P::uint_t MachOChecker
<ppc64
>::getEntryPoint(const macho_thread_command
<ppc64::P
>* threadInfo
)
344 return threadInfo
->thread_register(0);
348 x86::P::uint_t MachOChecker
<x86
>::getEntryPoint(const macho_thread_command
<x86::P
>* threadInfo
)
350 return threadInfo
->thread_register(10);
354 x86_64::P::uint_t MachOChecker
<x86_64
>::getEntryPoint(const macho_thread_command
<x86_64::P
>* threadInfo
)
356 return threadInfo
->thread_register(16);
360 arm::P::uint_t MachOChecker
<arm
>::getEntryPoint(const macho_thread_command
<arm::P
>* threadInfo
)
362 return threadInfo
->thread_register(15);
365 #if SUPPORT_ARCH_arm64
367 arm64::P::uint_t MachOChecker
<arm64
>::getEntryPoint(const macho_thread_command
<arm64::P
>* threadInfo
)
369 throw "LC_UNIXTHREAD not supported for arm64";
373 template <typename A
>
374 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
375 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
376 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
377 fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
),
378 fTEXTSegment(NULL
), fDyldInfo(NULL
), fSectionCount(0)
381 if ( ! validFile(fileContent
) )
382 throw "not a mach-o file that can be checked";
384 fPath
= strdup(path
);
385 fHeader
= (const macho_header
<P
>*)fileContent
;
387 // sanity check header
390 // check load commands
393 checkIndirectSymbolTable();
403 template <typename A
>
404 void MachOChecker
<A
>::checkMachHeader()
406 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
407 throw "sizeofcmds in mach_header is larger than file";
409 uint32_t flags
= fHeader
->flags();
410 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFE000000;
411 if ( flags
& invalidBits
)
412 throw "invalid bits in mach_header flags";
413 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
414 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
416 switch ( fHeader
->filetype() ) {
418 fSlidableImage
= ( flags
& MH_PIE
);
422 fSlidableImage
= true;
425 throw "not a mach-o file type supported by this tool";
429 template <typename A
>
430 void MachOChecker
<A
>::checkLoadCommands()
432 // check that all load commands fit within the load command space file
433 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
434 const macho_thread_command
<P
>* threadInfo
= NULL
;
435 const macho_entry_point_command
<P
>* entryPoint
= NULL
;
436 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
437 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
438 const uint32_t cmd_count
= fHeader
->ncmds();
439 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
440 const macho_load_command
<P
>* cmd
= cmds
;
441 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
442 uint32_t size
= cmd
->cmdsize();
443 if ( (size
& this->loadCommandSizeMask()) != 0 )
444 throwf("load command #%d has a unaligned size", i
);
445 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
446 if ( endOfCmd
> endOfLoadCommands
)
447 throwf("load command #%d extends beyond the end of the load commands", i
);
448 if ( endOfCmd
> endOfFile
)
449 throwf("load command #%d extends beyond the end of the file", i
);
450 switch ( cmd
->cmd() ) {
451 case macho_segment_command
<P
>::CMD
:
456 case LC_LOAD_DYLINKER
:
458 case macho_routines_command
<P
>::CMD
:
459 case LC_SUB_FRAMEWORK
:
461 case LC_TWOLEVEL_HINTS
:
462 case LC_PREBIND_CKSUM
:
463 case LC_LOAD_WEAK_DYLIB
:
464 case LC_LAZY_LOAD_DYLIB
:
466 case LC_REEXPORT_DYLIB
:
467 case LC_SEGMENT_SPLIT_INFO
:
468 case LC_CODE_SIGNATURE
:
469 case LC_LOAD_UPWARD_DYLIB
:
470 case LC_VERSION_MIN_MACOSX
:
471 case LC_VERSION_MIN_IPHONEOS
:
473 case LC_FUNCTION_STARTS
:
474 case LC_DYLD_ENVIRONMENT
:
475 case LC_DATA_IN_CODE
:
476 case LC_DYLIB_CODE_SIGN_DRS
:
477 case LC_SOURCE_VERSION
:
480 case LC_DYLD_INFO_ONLY
:
481 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
483 case LC_ENCRYPTION_INFO
:
484 case LC_ENCRYPTION_INFO_64
:
485 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
487 case LC_SUB_UMBRELLA
:
489 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
490 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA";
493 if ( fHeader
->filetype() != MH_EXECUTE
)
494 throw "LC_MAIN can only be used in MH_EXECUTE file types";
495 entryPoint
= (macho_entry_point_command
<P
>*)cmd
;
498 if ( fHeader
->filetype() != MH_EXECUTE
)
499 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
500 threadInfo
= (macho_thread_command
<P
>*)cmd
;
503 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
505 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
510 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
511 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
512 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
513 const macho_segment_command
<P
>* stackSegment
= NULL
;
514 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
515 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
516 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
517 fSegments
.push_back(segCmd
);
518 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
519 throw "invalid segment load command size";
521 // see if this overlaps another segment address range
522 uint64_t startAddr
= segCmd
->vmaddr();
523 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
524 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
525 if ( it
->first
< startAddr
) {
526 if ( it
->second
> startAddr
)
527 throw "overlapping segment vm addresses";
529 else if ( it
->first
> startAddr
) {
530 if ( it
->first
< endAddr
)
531 throw "overlapping segment vm addresses";
534 throw "overlapping segment vm addresses";
536 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
538 // see if this overlaps another segment file offset range
539 uint64_t startOffset
= segCmd
->fileoff();
540 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
541 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
542 if ( it
->first
< startOffset
) {
543 if ( it
->second
> startOffset
)
544 throw "overlapping segment file data";
546 else if ( it
->first
> startOffset
) {
547 if ( it
->first
< endOffset
)
548 throw "overlapping segment file data";
551 throw "overlapping segment file data";
553 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
554 // check is within file bounds
555 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
556 throw "segment file data is past end of file";
558 // verify it fits in file
559 if ( startOffset
> fLength
)
560 throw "segment fileoff does not fit in file";
561 if ( endOffset
> fLength
)
562 throw "segment fileoff+filesize does not fit in file";
564 // record special segments
565 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
566 linkEditSegment
= segCmd
;
567 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
568 stackSegment
= segCmd
;
570 // cache interesting segments
571 if ( fFirstSegment
== NULL
)
572 fFirstSegment
= segCmd
;
573 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
574 fTEXTSegment
= segCmd
;
575 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
576 if ( fFirstWritableSegment
== NULL
)
577 fFirstWritableSegment
= segCmd
;
578 if ( segCmd
->vmaddr() > 0x100000000ULL
)
579 fWriteableSegmentWithAddrOver4G
= true;
582 // check section ranges
583 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
584 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
585 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
586 // check all non-zero sized sections are within segment
587 if ( sect
->addr() < startAddr
)
588 throwf("section %s vm address not within segment", sect
->sectname());
589 if ( (sect
->addr()+sect
->size()) > endAddr
)
590 throwf("section %s vm address not within segment", sect
->sectname());
591 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
592 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
593 && (segCmd
->filesize() != 0)
594 && (sect
->size() != 0) ) {
595 if ( sect
->offset() < startOffset
)
596 throwf("section %s file offset not within segment", sect
->sectname());
597 if ( (sect
->offset()+sect
->size()) > endOffset
)
598 throwf("section %s file offset not within segment", sect
->sectname());
600 checkSection(segCmd
, sect
);
604 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
607 // verify there was a LINKEDIT segment
608 if ( linkEditSegment
== NULL
)
609 throw "no __LINKEDIT segment";
611 // verify there was an executable __TEXT segment and load commands are in it
612 if ( fTEXTSegment
== NULL
)
613 throw "no __TEXT segment";
614 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
615 throw "__TEXT segment does not have r-x init permissions";
616 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
617 // throw "__TEXT segment does not have rwx max permissions";
618 if ( fTEXTSegment
->fileoff() != 0 )
619 throw "__TEXT segment does not start at mach_header";
620 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
621 throw "__TEXT segment smaller than load commands";
623 // verify if custom stack used, that stack is in __UNIXSTACK segment
624 if ( threadInfo
!= NULL
) {
625 pint_t initialSP
= getInitialStackPointer(threadInfo
);
626 if ( initialSP
!= 0 ) {
627 if ( stackSegment
== NULL
)
628 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
629 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
630 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
634 // verify __UNIXSTACK is zero fill
635 if ( stackSegment
!= NULL
) {
636 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
637 throw "__UNIXSTACK is not a zero-fill segment";
638 if ( stackSegment
->vmsize() < 4096 )
639 throw "__UNIXSTACK segment is too small";
642 // verify entry point is in __TEXT segment
643 if ( threadInfo
!= NULL
) {
644 pint_t initialPC
= getEntryPoint(threadInfo
);
645 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
646 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
648 else if ( entryPoint
!= NULL
) {
649 pint_t initialOffset
= entryPoint
->entryoff();
650 if ( (initialOffset
< fTEXTSegment
->fileoff()) || (initialOffset
>= (fTEXTSegment
->fileoff()+fTEXTSegment
->filesize())) )
651 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset
);
654 // checks for executables
655 bool isStaticExecutable
= false;
656 if ( fHeader
->filetype() == MH_EXECUTE
) {
657 isStaticExecutable
= true;
659 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
660 switch ( cmd
->cmd() ) {
661 case LC_LOAD_DYLINKER
:
662 // the existence of a dyld load command makes a executable dynamic
663 isStaticExecutable
= false;
666 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
668 if ( isStaticExecutable
) {
669 if ( (fHeader
->flags() != MH_NOUNDEFS
) && (fHeader
->flags() != (MH_NOUNDEFS
|MH_PIE
)) )
670 throw "invalid bits in mach_header flags for static executable";
674 // verify encryption info
675 if ( encryption_info
!= NULL
) {
676 if ( fHeader
->filetype() != MH_EXECUTE
)
677 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
678 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
679 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
680 if ( (encryption_info
->cryptoff() % 4096) != 0 )
681 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
682 if ( (encryption_info
->cryptsize() % 4096) != 0 )
683 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
684 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
685 it
!= segmentFileOffsetRanges
.end(); ++it
) {
686 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
687 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
688 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
693 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
695 bool foundDynamicSymTab
= false;
696 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
697 switch ( cmd
->cmd() ) {
700 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
701 fSymbolCount
= symtab
->nsyms();
702 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
703 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
704 throw "symbol table not in __LINKEDIT";
705 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
706 throw "symbol table end not in __LINKEDIT";
707 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
708 throw "symbol table start not pointer aligned";
709 fStrings
= (char*)fHeader
+ symtab
->stroff();
710 fStringsEnd
= fStrings
+ symtab
->strsize();
711 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
712 throw "string pool not in __LINKEDIT";
713 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
714 throw "string pool extends beyond __LINKEDIT";
715 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
716 throw "string pool start not pointer aligned";
717 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
718 throw "string pool size not a multiple of pointer size";
723 if ( isStaticExecutable
&&! fSlidableImage
)
724 throw "LC_DYSYMTAB should not be used in static executable";
725 foundDynamicSymTab
= true;
726 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
727 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
728 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
729 if ( fIndirectTableCount
!= 0 ) {
730 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
731 throw "indirect symbol table not in __LINKEDIT";
732 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
733 throw "indirect symbol table not in __LINKEDIT";
734 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
735 throw "indirect symbol table not pointer aligned";
737 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
738 if ( fLocalRelocationsCount
!= 0 ) {
739 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
740 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
741 throw "local relocations not in __LINKEDIT";
742 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
743 throw "local relocations not in __LINKEDIT";
744 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
745 throw "local relocations table not pointer aligned";
747 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
748 if ( fExternalRelocationsCount
!= 0 ) {
749 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
750 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
751 throw "external relocations not in __LINKEDIT";
752 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
753 throw "external relocations not in __LINKEDIT";
754 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
755 throw "external relocations table not pointer aligned";
759 case LC_SEGMENT_SPLIT_INFO
:
761 if ( isStaticExecutable
)
762 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
763 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
764 if ( info
->dataoff() < linkEditSegment
->fileoff() )
765 throw "split seg info not in __LINKEDIT";
766 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
767 throw "split seg info not in __LINKEDIT";
768 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
769 throw "split seg info table not pointer aligned";
770 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
771 throw "split seg info size not a multiple of pointer size";
774 case LC_FUNCTION_STARTS
:
776 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
777 if ( info
->dataoff() < linkEditSegment
->fileoff() )
778 throw "function starts data not in __LINKEDIT";
779 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
780 throw "function starts data not in __LINKEDIT";
781 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
782 throw "function starts data table not pointer aligned";
783 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
784 throw "function starts data size not a multiple of pointer size";
787 case LC_DATA_IN_CODE
:
789 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
790 if ( info
->dataoff() < linkEditSegment
->fileoff() )
791 throw "data-in-code data not in __LINKEDIT";
792 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
793 throw "data-in-code data not in __LINKEDIT";
794 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
795 throw "data-in-code data table not pointer aligned";
796 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
797 throw "data-in-code data size not a multiple of pointer size";
800 case LC_DYLIB_CODE_SIGN_DRS
:
802 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
803 if ( info
->dataoff() < linkEditSegment
->fileoff() )
804 throw "dependent dylib DR data not in __LINKEDIT";
805 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
806 throw "dependent dylib DR data not in __LINKEDIT";
807 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
808 throw "dependent dylib DR data table not pointer aligned";
809 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
810 throw "dependent dylib DR data size not a multiple of pointer size";
814 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
816 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
817 throw "missing dynamic symbol table";
818 if ( fStrings
== NULL
)
819 throw "missing symbol table";
823 template <typename A
>
824 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
826 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
827 if ( sectionType
== S_ZEROFILL
) {
828 if ( sect
->offset() != 0 )
829 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
832 // check section's segment name matches segment
833 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
834 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
836 // more section tests here
842 template <typename A
>
843 void MachOChecker
<A
>::checkIndirectSymbolTable()
845 // static executables don't have indirect symbol table
846 if ( fDynamicSymbolTable
== NULL
)
848 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
849 const uint32_t cmd_count
= fHeader
->ncmds();
850 const macho_load_command
<P
>* cmd
= cmds
;
851 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
852 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
853 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
854 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
855 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
856 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
857 // make sure all magic sections that use indirect symbol table fit within it
859 uint32_t elementSize
= 0;
860 switch ( sect
->flags() & SECTION_TYPE
) {
862 elementSize
= sect
->reserved2();
863 start
= sect
->reserved1();
865 case S_LAZY_SYMBOL_POINTERS
:
866 case S_NON_LAZY_SYMBOL_POINTERS
:
867 elementSize
= sizeof(pint_t
);
868 start
= sect
->reserved1();
871 if ( elementSize
!= 0 ) {
872 uint32_t count
= sect
->size() / elementSize
;
873 if ( (count
*elementSize
) != sect
->size() )
874 throwf("%s section size is not an even multiple of element size", sect
->sectname());
875 if ( (start
+count
) > fIndirectTableCount
)
876 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
880 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
887 template <typename A
>
888 void MachOChecker
<A
>::checkSymbolTable()
890 // verify no duplicate external symbol names
891 if ( fDynamicSymbolTable
!= NULL
) {
892 StringSet externalNames
;
893 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
894 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
895 int i
= fDynamicSymbolTable
->iextdefsym();
896 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
897 const char* symName
= &fStrings
[p
->n_strx()];
898 if ( symName
> fStringsEnd
)
899 throw "string index out of range";
900 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
901 if ( externalNames
.find(symName
) != externalNames
.end() )
902 throwf("duplicate external symbol: %s", symName
);
903 if ( (p
->n_type() & N_EXT
) == 0 )
904 throwf("non-external symbol in external symbol range: %s", symName
);
905 // don't add N_INDR to externalNames because there is likely an undefine with same name
906 if ( (p
->n_type() & N_INDR
) == 0 )
907 externalNames
.insert(symName
);
909 // verify no undefines with same name as an external symbol
910 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
911 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
912 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
913 const char* symName
= &fStrings
[p
->n_strx()];
914 if ( symName
> fStringsEnd
)
915 throw "string index out of range";
916 if ( externalNames
.find(symName
) != externalNames
.end() )
917 throwf("undefine with same name as external symbol: %s", symName
);
919 // verify all N_SECT values are valid
920 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
921 uint8_t type
= p
->n_type();
922 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
923 if ( p
->n_sect() > fSectionCount
) {
924 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
932 template <typename A
>
933 void MachOChecker
<A
>::checkInitTerms()
935 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
936 const uint32_t cmd_count
= fHeader
->ncmds();
937 const macho_load_command
<P
>* cmd
= cmds
;
938 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
939 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
940 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
941 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
942 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
943 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
944 // make sure all magic sections that use indirect symbol table fit within it
948 const char* kind
= "initializer";
949 switch ( sect
->flags() & SECTION_TYPE
) {
950 case S_MOD_TERM_FUNC_POINTERS
:
953 case S_MOD_INIT_FUNC_POINTERS
:
954 count
= sect
->size() / sizeof(pint_t
);
955 if ( (count
*sizeof(pint_t
)) != sect
->size() )
956 throwf("%s section size is not an even multiple of element size", sect
->sectname());
957 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
958 throwf("%s section size is not pointer size aligned", sect
->sectname());
959 // check each pointer in array points within TEXT
960 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
961 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
962 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
963 pint_t pointer
= P::getP(*p
);
964 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
965 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
967 // check each pointer in array will be rebased and not bound
968 if ( fSlidableImage
) {
969 pint_t sectionBeginAddr
= sect
->addr();
970 pint_t sectionEndddr
= sect
->addr() + sect
->size();
971 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
972 if ( addressIsBindingSite(addr
) )
973 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
974 if ( ! addressIsRebaseSite(addr
) )
975 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
982 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
989 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
991 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
992 return fFirstWritableSegment
->vmaddr();
994 return fFirstSegment
->vmaddr();
998 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
1000 if ( fWriteableSegmentWithAddrOver4G
)
1001 return fFirstWritableSegment
->vmaddr();
1003 return fFirstSegment
->vmaddr();
1007 x86::P::uint_t MachOChecker
<x86
>::relocBase()
1009 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1010 return fFirstWritableSegment
->vmaddr();
1012 return fFirstSegment
->vmaddr();
1016 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
1018 // check for split-seg
1019 return fFirstWritableSegment
->vmaddr();
1023 arm::P::uint_t MachOChecker
<arm
>::relocBase()
1025 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1026 return fFirstWritableSegment
->vmaddr();
1028 return fFirstSegment
->vmaddr();
1031 #if SUPPORT_ARCH_arm64
1033 arm64::P::uint_t MachOChecker
<arm64
>::relocBase()
1035 return fFirstWritableSegment
->vmaddr();
1041 template <typename A
>
1042 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
1044 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1045 const uint32_t cmd_count
= fHeader
->ncmds();
1046 const macho_load_command
<P
>* cmd
= cmds
;
1047 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1048 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1049 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1050 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
1051 // if segment is writable, we are fine
1052 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1054 // could be a text reloc, make sure section bit is set
1055 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1056 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1057 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1058 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
1059 // found section for this address, if has relocs we are fine
1060 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
1065 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1072 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1074 if ( reloc
->r_length() != 2 )
1075 throw "bad external relocation length";
1076 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1077 throw "unknown external relocation type";
1078 if ( reloc
->r_pcrel() != 0 )
1079 throw "bad external relocation pc_rel";
1080 if ( reloc
->r_extern() == 0 )
1081 throw "local relocation found with external relocations";
1082 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1083 throw "external relocation address not in writable segment";
1084 // FIX: check r_symbol
1088 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1090 if ( reloc
->r_length() != 3 )
1091 throw "bad external relocation length";
1092 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1093 throw "unknown external relocation type";
1094 if ( reloc
->r_pcrel() != 0 )
1095 throw "bad external relocation pc_rel";
1096 if ( reloc
->r_extern() == 0 )
1097 throw "local relocation found with external relocations";
1098 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1099 throw "external relocation address not in writable segment";
1100 // FIX: check r_symbol
1104 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1106 if ( reloc
->r_length() != 2 )
1107 throw "bad external relocation length";
1108 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1109 throw "unknown external relocation type";
1110 if ( reloc
->r_pcrel() != 0 )
1111 throw "bad external relocation pc_rel";
1112 if ( reloc
->r_extern() == 0 )
1113 throw "local relocation found with external relocations";
1114 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1115 throw "external relocation address not in writable segment";
1116 // FIX: check r_symbol
1121 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1123 if ( reloc
->r_length() != 3 )
1124 throw "bad external relocation length";
1125 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1126 throw "unknown external relocation type";
1127 if ( reloc
->r_pcrel() != 0 )
1128 throw "bad external relocation pc_rel";
1129 if ( reloc
->r_extern() == 0 )
1130 throw "local relocation found with external relocations";
1131 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1132 throw "exernal relocation address not in writable segment";
1133 // FIX: check r_symbol
1136 #if SUPPORT_ARCH_arm_any
1138 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1140 if ( reloc
->r_length() != 2 )
1141 throw "bad external relocation length";
1142 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1143 throw "unknown external relocation type";
1144 if ( reloc
->r_pcrel() != 0 )
1145 throw "bad external relocation pc_rel";
1146 if ( reloc
->r_extern() == 0 )
1147 throw "local relocation found with external relocations";
1148 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1149 throw "external relocation address not in writable segment";
1150 // FIX: check r_symbol
1154 #if SUPPORT_ARCH_arm64
1156 void MachOChecker
<arm64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1158 throw "external relocations not used for arm64";
1164 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1166 if ( reloc
->r_address() & R_SCATTERED
) {
1168 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1174 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1175 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
1181 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1183 if ( reloc
->r_length() != 3 )
1184 throw "bad local relocation length";
1185 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1186 throw "unknown local relocation type";
1187 if ( reloc
->r_pcrel() != 0 )
1188 throw "bad local relocation pc_rel";
1189 if ( reloc
->r_extern() != 0 )
1190 throw "external relocation found with local relocations";
1191 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1192 throw "local relocation address not in writable segment";
1196 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1202 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1204 if ( reloc
->r_length() != 3 )
1205 throw "bad local relocation length";
1206 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1207 throw "unknown local relocation type";
1208 if ( reloc
->r_pcrel() != 0 )
1209 throw "bad local relocation pc_rel";
1210 if ( reloc
->r_extern() != 0 )
1211 throw "external relocation found with local relocations";
1212 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1213 throw "local relocation address not in writable segment";
1216 #if SUPPORT_ARCH_arm_any
1218 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1220 if ( reloc
->r_address() & R_SCATTERED
) {
1222 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1223 if ( sreloc
->r_length() != 2 )
1224 throw "bad local scattered relocation length";
1225 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1226 throw "bad local scattered relocation type";
1229 if ( reloc
->r_length() != 2 )
1230 throw "bad local relocation length";
1231 if ( reloc
->r_extern() != 0 )
1232 throw "external relocation found with local relocations";
1233 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1234 throw "local relocation address not in writable segment";
1239 #if SUPPORT_ARCH_arm64
1241 void MachOChecker
<arm64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1243 throw "local relocations not used for arm64";
1248 template <typename A
>
1249 void MachOChecker
<A
>::checkRelocations()
1251 // external relocations should be sorted to minimize dyld symbol lookups
1252 // therefore every reloc with the same r_symbolnum value should be contiguous
1253 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1254 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1255 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1256 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1257 this->checkExternalReloation(reloc
);
1258 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1259 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1260 throw "external relocations not sorted";
1261 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1262 lastSymbolIndex
= reloc
->r_symbolnum();
1266 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1267 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1268 this->checkLocalReloation(reloc
);
1271 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1272 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1273 const uint32_t cmd_count
= fHeader
->ncmds();
1274 const macho_load_command
<P
>* cmd
= cmds
;
1275 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1276 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1277 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1278 // if segment is writable, we are fine
1279 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1281 // look at sections that have text reloc bit set
1282 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1283 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1284 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1285 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1286 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1287 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1292 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1296 template <typename A
>
1297 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1299 if ( segIndex
> fSegments
.size() )
1300 throw "segment index out of range";
1301 return fSegments
[segIndex
]->vmaddr();
1304 template <typename A
>
1305 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1307 // look at local relocs
1308 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1309 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1310 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1311 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1315 if ( fDyldInfo
!= NULL
) {
1316 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1317 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1320 uint64_t segOffset
= 0;
1324 pint_t segStartAddr
= 0;
1327 while ( !done
&& (p
< end
) ) {
1328 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1329 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1332 case REBASE_OPCODE_DONE
:
1335 case REBASE_OPCODE_SET_TYPE_IMM
:
1338 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1339 segIndex
= immediate
;
1340 segStartAddr
= segStartAddress(segIndex
);
1341 segOffset
= read_uleb128(p
, end
);
1343 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1344 segOffset
+= read_uleb128(p
, end
);
1346 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1347 segOffset
+= immediate
*sizeof(pint_t
);
1349 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1350 for (int i
=0; i
< immediate
; ++i
) {
1351 addr
= segStartAddr
+segOffset
;
1352 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1354 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1355 segOffset
+= sizeof(pint_t
);
1358 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1359 count
= read_uleb128(p
, end
);
1360 for (uint32_t i
=0; i
< count
; ++i
) {
1361 addr
= segStartAddr
+segOffset
;
1362 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1364 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1365 segOffset
+= sizeof(pint_t
);
1368 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1369 addr
= segStartAddr
+segOffset
;
1370 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1372 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1373 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1375 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1376 count
= read_uleb128(p
, end
);
1377 skip
= read_uleb128(p
, end
);
1378 for (uint32_t i
=0; i
< count
; ++i
) {
1379 addr
= segStartAddr
+segOffset
;
1380 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1382 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1383 segOffset
+= skip
+ sizeof(pint_t
);
1387 throwf("bad rebase opcode %d", *p
);
1394 template <typename A
>
1395 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
1397 // look at local relocs
1398 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1399 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1400 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1401 if ( relocAddress
== targetAddr
)
1405 if ( fDyldInfo
!= NULL
) {
1406 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1407 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1410 uint64_t segOffset
= 0;
1414 pint_t segStartAddr
= 0;
1417 while ( !done
&& (p
< end
) ) {
1418 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1419 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1422 case REBASE_OPCODE_DONE
:
1425 case REBASE_OPCODE_SET_TYPE_IMM
:
1428 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1429 segIndex
= immediate
;
1430 segStartAddr
= segStartAddress(segIndex
);
1431 segOffset
= read_uleb128(p
, end
);
1433 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1434 segOffset
+= read_uleb128(p
, end
);
1436 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1437 segOffset
+= immediate
*sizeof(pint_t
);
1439 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1440 for (int i
=0; i
< immediate
; ++i
) {
1441 addr
= segStartAddr
+segOffset
;
1442 if ( addr
== targetAddr
)
1444 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1445 segOffset
+= sizeof(pint_t
);
1448 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1449 count
= read_uleb128(p
, end
);
1450 for (uint32_t i
=0; i
< count
; ++i
) {
1451 addr
= segStartAddr
+segOffset
;
1452 if ( addr
== targetAddr
)
1454 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1455 segOffset
+= sizeof(pint_t
);
1458 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1459 addr
= segStartAddr
+segOffset
;
1460 if ( addr
== targetAddr
)
1462 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1463 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1465 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1466 count
= read_uleb128(p
, end
);
1467 skip
= read_uleb128(p
, end
);
1468 for (uint32_t i
=0; i
< count
; ++i
) {
1469 addr
= segStartAddr
+segOffset
;
1470 if ( addr
== targetAddr
)
1472 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1473 segOffset
+= skip
+ sizeof(pint_t
);
1477 throwf("bad rebase opcode %d", *p
);
1485 template <typename A
>
1486 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1488 // look at external relocs
1489 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1490 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1491 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1492 if ( relocAddress
== targetAddr
)
1496 if ( fDyldInfo
!= NULL
) {
1497 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1498 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1501 uint64_t segOffset
= 0;
1505 const char* symbolName
= NULL
;
1506 int libraryOrdinal
= 0;
1509 pint_t segStartAddr
= 0;
1512 while ( !done
&& (p
< end
) ) {
1513 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1514 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1517 case BIND_OPCODE_DONE
:
1520 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1521 libraryOrdinal
= immediate
;
1523 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1524 libraryOrdinal
= read_uleb128(p
, end
);
1526 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1527 // the special ordinals are negative numbers
1528 if ( immediate
== 0 )
1531 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1532 libraryOrdinal
= signExtended
;
1535 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1536 symbolName
= (char*)p
;
1541 case BIND_OPCODE_SET_TYPE_IMM
:
1544 case BIND_OPCODE_SET_ADDEND_SLEB
:
1545 addend
= read_sleb128(p
, end
);
1547 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1548 segIndex
= immediate
;
1549 segStartAddr
= segStartAddress(segIndex
);
1550 segOffset
= read_uleb128(p
, end
);
1552 case BIND_OPCODE_ADD_ADDR_ULEB
:
1553 segOffset
+= read_uleb128(p
, end
);
1555 case BIND_OPCODE_DO_BIND
:
1556 if ( (segStartAddr
+segOffset
) == targetAddr
)
1558 segOffset
+= sizeof(pint_t
);
1560 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
1561 if ( (segStartAddr
+segOffset
) == targetAddr
)
1563 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1565 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
1566 if ( (segStartAddr
+segOffset
) == targetAddr
)
1568 segOffset
+= immediate
*sizeof(pint_t
) + sizeof(pint_t
);
1570 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
1571 count
= read_uleb128(p
, end
);
1572 skip
= read_uleb128(p
, end
);
1573 for (uint32_t i
=0; i
< count
; ++i
) {
1574 if ( (segStartAddr
+segOffset
) == targetAddr
)
1576 segOffset
+= skip
+ sizeof(pint_t
);
1580 throwf("bad bind opcode %d", *p
);
1588 static void check(const char* path
)
1590 struct stat stat_buf
;
1593 int fd
= ::open(path
, O_RDONLY
, 0);
1595 throw "cannot open file";
1596 if ( ::fstat(fd
, &stat_buf
) != 0 )
1597 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1598 uint32_t length
= stat_buf
.st_size
;
1599 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1600 if ( p
== ((uint8_t*)(-1)) )
1601 throw "cannot map file";
1603 const mach_header
* mh
= (mach_header
*)p
;
1604 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1605 const struct fat_header
* fh
= (struct fat_header
*)p
;
1606 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1607 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1608 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1609 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1610 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1613 case CPU_TYPE_POWERPC
:
1614 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
1615 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
1617 throw "in universal file, ppc slice does not contain ppc mach-o";
1620 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1621 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
1623 throw "in universal file, i386 slice does not contain i386 mach-o";
1625 case CPU_TYPE_POWERPC64
:
1626 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
1627 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
1629 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
1631 case CPU_TYPE_X86_64
:
1632 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1633 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
1635 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1637 #if SUPPORT_ARCH_arm_any
1639 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1640 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
1642 throw "in universal file, arm slice does not contain arm mach-o";
1646 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1650 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1651 MachOChecker
<x86
>::make(p
, length
, path
);
1653 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
1654 MachOChecker
<ppc
>::make(p
, length
, path
);
1656 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
1657 MachOChecker
<ppc64
>::make(p
, length
, path
);
1659 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1660 MachOChecker
<x86_64
>::make(p
, length
, path
);
1662 #if SUPPORT_ARCH_arm_any
1663 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1664 MachOChecker
<arm
>::make(p
, length
, path
);
1667 #if SUPPORT_ARCH_arm64
1668 else if ( MachOChecker
<arm64
>::validFile(p
) ) {
1669 MachOChecker
<arm64
>::make(p
, length
, path
);
1673 throw "not a known file type";
1676 catch (const char* msg
) {
1677 throwf("%s in %s", msg
, path
);
1682 int main(int argc
, const char* argv
[])
1684 bool progress
= false;
1686 for(int i
=1; i
< argc
; ++i
) {
1687 const char* arg
= argv
[i
];
1688 if ( arg
[0] == '-' ) {
1689 if ( strcmp(arg
, "-progress") == 0 ) {
1693 throwf("unknown option: %s\n", arg
);
1697 bool success
= true;
1701 catch (const char* msg
) {
1702 fprintf(stderr
, "machocheck failed: %s %s\n", arg
, msg
);
1706 if ( success
&& progress
)
1707 printf("ok: %s\n", arg
);