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>
37 #include <unordered_set>
39 #include "configure.h"
41 #include "MachOFileAbstraction.hpp"
42 #include "Architectures.hpp"
45 __attribute__((noreturn
))
46 void throwf(const char* format
, ...)
50 va_start(list
, format
);
51 vasprintf(&p
, format
, list
);
58 static uint64_t read_uleb128(const uint8_t*& p
, const uint8_t* end
)
64 throwf("malformed uleb128");
66 uint64_t slice
= *p
& 0x7f;
68 if (bit
>= 64 || slice
<< bit
>> bit
!= slice
)
69 throwf("uleb128 too big");
71 result
|= (slice
<< bit
);
80 static int64_t read_sleb128(const uint8_t*& p
, const uint8_t* end
)
87 throwf("malformed sleb128");
89 result
|= ((byte
& 0x7f) << bit
);
91 } while (byte
& 0x80);
92 // sign extend negative numbers
93 if ( (byte
& 0x40) != 0 )
94 result
|= (-1LL) << bit
;
103 static bool validFile(const uint8_t* fileContent
);
104 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, const char* verifierDstRoot
)
105 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
, verifierDstRoot
); }
106 virtual ~MachOChecker() {}
110 typedef typename
A::P P
;
111 typedef typename
A::P::E E
;
112 typedef typename
A::P::uint_t pint_t
;
114 // utility classes for using std::unordered_map with c-strings
116 size_t operator()(const char* __s
) const {
119 __h
= 5 * __h
+ *__s
;
125 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
128 typedef std::unordered_set
<const char*, CStringHash
, CStringEquals
> StringSet
;
130 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, const char* verifierDstRoot
);
131 void checkMachHeader();
132 void checkLoadCommands();
133 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
134 uint8_t loadCommandSizeMask();
135 void checkSymbolTable();
136 void checkInitTerms();
137 void checkIndirectSymbolTable();
138 void checkRelocations();
139 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
140 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
142 void verifyInstallName();
143 void verifyNoRpaths();
144 void verifyNoFlatLookups();
147 bool addressInWritableSegment(pint_t address
);
148 bool hasTextRelocInRange(pint_t start
, pint_t end
);
149 pint_t
segStartAddress(uint8_t segIndex
);
150 bool addressIsRebaseSite(pint_t addr
);
151 bool addressIsBindingSite(pint_t addr
);
152 pint_t
getInitialStackPointer(const macho_thread_command
<P
>*);
153 pint_t
getEntryPoint(const macho_thread_command
<P
>*);
154 const char* archName();
158 const char* fDstRoot
;
159 const macho_header
<P
>* fHeader
;
161 const char* fInstallName
;
162 const char* fStrings
;
163 const char* fStringsEnd
;
164 const macho_nlist
<P
>* fSymbols
;
165 uint32_t fSymbolCount
;
166 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
167 const uint32_t* fIndirectTable
;
168 uint32_t fIndirectTableCount
;
169 const macho_relocation_info
<P
>* fLocalRelocations
;
170 uint32_t fLocalRelocationsCount
;
171 const macho_relocation_info
<P
>* fExternalRelocations
;
172 uint32_t fExternalRelocationsCount
;
173 bool fWriteableSegmentWithAddrOver4G
;
176 const macho_segment_command
<P
>* fFirstSegment
;
177 const macho_segment_command
<P
>* fFirstWritableSegment
;
178 const macho_segment_command
<P
>* fTEXTSegment
;
179 const macho_dyld_info_command
<P
>* fDyldInfo
;
180 uint32_t fSectionCount
;
181 std::vector
<const macho_segment_command
<P
>*>fSegments
;
186 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
188 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
189 if ( header
->magic() != MH_MAGIC
)
191 if ( header
->cputype() != CPU_TYPE_I386
)
193 switch (header
->filetype()) {
204 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
206 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
207 if ( header
->magic() != MH_MAGIC_64
)
209 if ( header
->cputype() != CPU_TYPE_X86_64
)
211 switch (header
->filetype()) {
221 #if SUPPORT_ARCH_arm_any
223 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
225 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
226 if ( header
->magic() != MH_MAGIC
)
228 if ( header
->cputype() != CPU_TYPE_ARM
)
230 switch (header
->filetype()) {
241 #if SUPPORT_ARCH_arm64
243 bool MachOChecker
<arm64
>::validFile(const uint8_t* fileContent
)
245 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
246 if ( header
->magic() != MH_MAGIC_64
)
248 if ( header
->cputype() != CPU_TYPE_ARM64
)
250 switch (header
->filetype()) {
262 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
263 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
264 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
265 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
266 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
267 template <> uint8_t MachOChecker
<arm64
>::loadCommandSizeMask() { return 0x07; }
271 x86::P::uint_t MachOChecker
<x86
>::getInitialStackPointer(const macho_thread_command
<x86::P
>* threadInfo
)
273 return threadInfo
->thread_register(7);
277 x86_64::P::uint_t MachOChecker
<x86_64
>::getInitialStackPointer(const macho_thread_command
<x86_64::P
>* threadInfo
)
279 return threadInfo
->thread_register(7);
283 arm::P::uint_t MachOChecker
<arm
>::getInitialStackPointer(const macho_thread_command
<arm::P
>* threadInfo
)
285 return threadInfo
->thread_register(13);
289 arm64::P::uint_t MachOChecker
<arm64
>::getInitialStackPointer(const macho_thread_command
<arm64::P
>* threadInfo
)
291 throw "LC_UNIXTHREAD not supported for arm64";
296 ppc::P::uint_t MachOChecker
<ppc
>::getEntryPoint(const macho_thread_command
<ppc::P
>* threadInfo
)
298 return threadInfo
->thread_register(0);
303 x86::P::uint_t MachOChecker
<x86
>::getEntryPoint(const macho_thread_command
<x86::P
>* threadInfo
)
305 return threadInfo
->thread_register(10);
309 x86_64::P::uint_t MachOChecker
<x86_64
>::getEntryPoint(const macho_thread_command
<x86_64::P
>* threadInfo
)
311 return threadInfo
->thread_register(16);
315 arm::P::uint_t MachOChecker
<arm
>::getEntryPoint(const macho_thread_command
<arm::P
>* threadInfo
)
317 return threadInfo
->thread_register(15);
321 arm64::P::uint_t MachOChecker
<arm64
>::getEntryPoint(const macho_thread_command
<arm64::P
>* threadInfo
)
323 throw "LC_UNIXTHREAD not supported for arm64";
327 template <typename A
>
328 const char* MachOChecker
<A
>::archName()
330 switch ( fHeader
->cputype() ) {
333 case CPU_TYPE_X86_64
:
334 if ( fHeader
->cpusubtype() == CPU_SUBTYPE_X86_64_H
)
339 switch ( fHeader
->cpusubtype() ) {
340 case CPU_SUBTYPE_ARM_V7
:
342 case CPU_SUBTYPE_ARM_V7S
:
344 case CPU_SUBTYPE_ARM_V7K
:
355 template <typename A
>
356 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, const char* verifierDstRoot
)
357 : fHeader(NULL
), fLength(fileLength
), fInstallName(NULL
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
358 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
359 fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fHasLC_RPATH(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
),
360 fTEXTSegment(NULL
), fDyldInfo(NULL
), fSectionCount(0)
363 if ( ! validFile(fileContent
) )
364 throw "not a mach-o file that can be checked";
366 fPath
= strdup(path
);
367 fDstRoot
= verifierDstRoot
? strdup(verifierDstRoot
) : NULL
;
368 fHeader
= (const macho_header
<P
>*)fileContent
;
370 // sanity check header
373 // check load commands
376 checkIndirectSymbolTable();
384 if ( verifierDstRoot
!= NULL
)
389 template <typename A
>
390 void MachOChecker
<A
>::checkMachHeader()
392 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
393 throw "sizeofcmds in mach_header is larger than file";
395 uint32_t flags
= fHeader
->flags();
396 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFC000000;
397 if ( flags
& invalidBits
)
398 throw "invalid bits in mach_header flags";
399 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
400 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
402 switch ( fHeader
->filetype() ) {
404 fSlidableImage
= ( flags
& MH_PIE
);
408 fSlidableImage
= true;
411 throw "not a mach-o file type supported by this tool";
415 template <typename A
>
416 void MachOChecker
<A
>::checkLoadCommands()
418 // check that all load commands fit within the load command space file
419 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
420 const macho_thread_command
<P
>* threadInfo
= NULL
;
421 const macho_entry_point_command
<P
>* entryPoint
= NULL
;
422 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
423 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
424 const uint32_t cmd_count
= fHeader
->ncmds();
425 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
426 const macho_load_command
<P
>* cmd
= cmds
;
427 const macho_dylib_command
<P
>* dylibID
;
428 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
429 uint32_t size
= cmd
->cmdsize();
430 if ( (size
& this->loadCommandSizeMask()) != 0 )
431 throwf("load command #%d has a unaligned size", i
);
432 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
433 if ( endOfCmd
> endOfLoadCommands
)
434 throwf("load command #%d extends beyond the end of the load commands", i
);
435 if ( endOfCmd
> endOfFile
)
436 throwf("load command #%d extends beyond the end of the file", i
);
437 switch ( cmd
->cmd() ) {
438 case macho_segment_command
<P
>::CMD
:
443 case LC_LOAD_DYLINKER
:
444 case macho_routines_command
<P
>::CMD
:
445 case LC_SUB_FRAMEWORK
:
447 case LC_TWOLEVEL_HINTS
:
448 case LC_PREBIND_CKSUM
:
449 case LC_LOAD_WEAK_DYLIB
:
450 case LC_LAZY_LOAD_DYLIB
:
452 case LC_REEXPORT_DYLIB
:
453 case LC_SEGMENT_SPLIT_INFO
:
454 case LC_CODE_SIGNATURE
:
455 case LC_LOAD_UPWARD_DYLIB
:
456 case LC_VERSION_MIN_MACOSX
:
457 case LC_VERSION_MIN_IPHONEOS
:
458 case LC_FUNCTION_STARTS
:
459 case LC_DYLD_ENVIRONMENT
:
460 case LC_DATA_IN_CODE
:
461 case LC_DYLIB_CODE_SIGN_DRS
:
462 case LC_SOURCE_VERSION
:
468 dylibID
= (macho_dylib_command
<P
>*)cmd
;
469 if ( dylibID
->name_offset() > size
)
470 throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID
->name_offset(), size
);
471 if ( (dylibID
->name_offset() + strlen(dylibID
->name()) + 1) > size
)
472 throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command");
473 fInstallName
= dylibID
->name();
476 case LC_DYLD_INFO_ONLY
:
477 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
479 case LC_ENCRYPTION_INFO
:
480 case LC_ENCRYPTION_INFO_64
:
481 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
483 case LC_SUB_UMBRELLA
:
485 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
486 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";
489 if ( fHeader
->filetype() != MH_EXECUTE
)
490 throw "LC_MAIN can only be used in MH_EXECUTE file types";
491 entryPoint
= (macho_entry_point_command
<P
>*)cmd
;
494 if ( fHeader
->filetype() != MH_EXECUTE
)
495 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
496 threadInfo
= (macho_thread_command
<P
>*)cmd
;
499 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
501 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
506 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
507 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
508 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
509 const macho_segment_command
<P
>* stackSegment
= NULL
;
510 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
511 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
512 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
513 fSegments
.push_back(segCmd
);
514 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
515 throw "invalid segment load command size";
517 // see if this overlaps another segment address range
518 uint64_t startAddr
= segCmd
->vmaddr();
519 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
520 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
521 if ( it
->first
< startAddr
) {
522 if ( it
->second
> startAddr
)
523 throw "overlapping segment vm addresses";
525 else if ( it
->first
> startAddr
) {
526 if ( it
->first
< endAddr
)
527 throw "overlapping segment vm addresses";
530 throw "overlapping segment vm addresses";
532 segmentAddressRanges
.push_back(std::make_pair(startAddr
, endAddr
));
534 // see if this overlaps another segment file offset range
535 uint64_t startOffset
= segCmd
->fileoff();
536 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
537 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
538 if ( it
->first
< startOffset
) {
539 if ( it
->second
> startOffset
)
540 throw "overlapping segment file data";
542 else if ( it
->first
> startOffset
) {
543 if ( it
->first
< endOffset
)
544 throw "overlapping segment file data";
547 throw "overlapping segment file data";
549 segmentFileOffsetRanges
.push_back(std::make_pair(startOffset
, endOffset
));
550 // check is within file bounds
551 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
552 throw "segment file data is past end of file";
554 // verify it fits in file
555 if ( startOffset
> fLength
)
556 throw "segment fileoff does not fit in file";
557 if ( endOffset
> fLength
)
558 throw "segment fileoff+filesize does not fit in file";
560 // record special segments
561 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
562 linkEditSegment
= segCmd
;
563 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
564 stackSegment
= segCmd
;
566 // cache interesting segments
567 if ( fFirstSegment
== NULL
)
568 fFirstSegment
= segCmd
;
569 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
570 fTEXTSegment
= segCmd
;
571 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
572 if ( fFirstWritableSegment
== NULL
)
573 fFirstWritableSegment
= segCmd
;
574 if ( segCmd
->vmaddr() > 0x100000000ULL
)
575 fWriteableSegmentWithAddrOver4G
= true;
578 // check section ranges
579 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
580 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
581 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
582 // check all non-zero sized sections are within segment
583 if ( sect
->addr() < startAddr
)
584 throwf("section %s vm address not within segment", sect
->sectname());
585 if ( (sect
->addr()+sect
->size()) > endAddr
)
586 throwf("section %s vm address not within segment", sect
->sectname());
587 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
588 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
589 && (segCmd
->filesize() != 0)
590 && (sect
->size() != 0) ) {
591 if ( sect
->offset() < startOffset
)
592 throwf("section %s file offset not within segment", sect
->sectname());
593 if ( (sect
->offset()+sect
->size()) > endOffset
)
594 throwf("section %s file offset not within segment", sect
->sectname());
596 checkSection(segCmd
, sect
);
600 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
603 // verify there was a LINKEDIT segment
604 if ( linkEditSegment
== NULL
)
605 throw "no __LINKEDIT segment";
607 // verify there was an executable __TEXT segment and load commands are in it
608 if ( fTEXTSegment
== NULL
)
609 throw "no __TEXT segment";
610 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
611 throw "__TEXT segment does not have r-x init permissions";
612 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
613 // throw "__TEXT segment does not have rwx max permissions";
614 if ( fTEXTSegment
->fileoff() != 0 )
615 throw "__TEXT segment does not start at mach_header";
616 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
617 throw "__TEXT segment smaller than load commands";
619 // verify if custom stack used, that stack is in __UNIXSTACK segment
620 if ( threadInfo
!= NULL
) {
621 pint_t initialSP
= getInitialStackPointer(threadInfo
);
622 if ( initialSP
!= 0 ) {
623 if ( stackSegment
== NULL
)
624 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
625 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
626 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
630 // verify __UNIXSTACK is zero fill
631 if ( stackSegment
!= NULL
) {
632 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
633 throw "__UNIXSTACK is not a zero-fill segment";
634 if ( stackSegment
->vmsize() < 4096 )
635 throw "__UNIXSTACK segment is too small";
638 // verify entry point is in __TEXT segment
639 if ( threadInfo
!= NULL
) {
640 pint_t initialPC
= getEntryPoint(threadInfo
);
641 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
642 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
644 else if ( entryPoint
!= NULL
) {
645 pint_t initialOffset
= entryPoint
->entryoff();
646 if ( (initialOffset
< fTEXTSegment
->fileoff()) || (initialOffset
>= (fTEXTSegment
->fileoff()+fTEXTSegment
->filesize())) )
647 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset
);
650 // checks for executables
651 bool isStaticExecutable
= false;
652 if ( fHeader
->filetype() == MH_EXECUTE
) {
653 isStaticExecutable
= true;
655 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
656 switch ( cmd
->cmd() ) {
657 case LC_LOAD_DYLINKER
:
658 // the existence of a dyld load command makes a executable dynamic
659 isStaticExecutable
= false;
662 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
664 if ( isStaticExecutable
) {
665 if ( (fHeader
->flags() != MH_NOUNDEFS
) && (fHeader
->flags() != (MH_NOUNDEFS
|MH_PIE
)) )
666 throw "invalid bits in mach_header flags for static executable";
670 // verify encryption info
671 if ( encryption_info
!= NULL
) {
672 switch ( fHeader
->filetype() ) {
673 case MH_EXECUTE
: case MH_DYLIB
: case MH_BUNDLE
:
676 throw "LC_ENCRYPTION_INFO load command is not allowed in this file type";
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 // verify dylib has LC_ID_DYLIB
694 if ( fHeader
->filetype() == MH_DYLIB
) {
695 if ( fInstallName
== NULL
)
696 throw "MH_DYLIB missing LC_ID_DYLIB";
699 if ( fInstallName
!= NULL
)
700 throw "LC_ID_DYLIB found but file type is not MH_DYLIB";
703 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
705 bool foundDynamicSymTab
= false;
706 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
707 switch ( cmd
->cmd() ) {
710 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
711 fSymbolCount
= symtab
->nsyms();
712 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
713 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
714 throw "symbol table not in __LINKEDIT";
715 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > symtab
->stroff() )
716 throw "symbol table overlaps string pool";
717 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
718 throw "symbol table start not pointer aligned";
719 fStrings
= (char*)fHeader
+ symtab
->stroff();
720 fStringsEnd
= fStrings
+ symtab
->strsize();
721 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
722 throw "string pool not in __LINKEDIT";
723 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
724 throw "string pool extends beyond __LINKEDIT";
725 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
726 throw "string pool start not pointer aligned";
731 if ( isStaticExecutable
&&! fSlidableImage
)
732 throw "LC_DYSYMTAB should not be used in static executable";
733 foundDynamicSymTab
= true;
734 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
735 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
736 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
737 if ( fIndirectTableCount
!= 0 ) {
738 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
739 throw "indirect symbol table not in __LINKEDIT";
740 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
741 throw "indirect symbol table not in __LINKEDIT";
742 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
743 throw "indirect symbol table not pointer aligned";
745 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
746 if ( fLocalRelocationsCount
!= 0 ) {
747 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
748 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
749 throw "local relocations not in __LINKEDIT";
750 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
751 throw "local relocations not in __LINKEDIT";
752 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
753 throw "local relocations table not pointer aligned";
755 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
756 if ( fExternalRelocationsCount
!= 0 ) {
757 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
758 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
759 throw "external relocations not in __LINKEDIT";
760 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
761 throw "external relocations not in __LINKEDIT";
762 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
763 throw "external relocations table not pointer aligned";
767 case LC_SEGMENT_SPLIT_INFO
:
769 if ( isStaticExecutable
)
770 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
771 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
772 if ( info
->dataoff() < linkEditSegment
->fileoff() )
773 throw "split seg info not in __LINKEDIT";
774 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
775 throw "split seg info not in __LINKEDIT";
776 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
777 throw "split seg info table not pointer aligned";
778 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
779 throw "split seg info size not a multiple of pointer size";
782 case LC_FUNCTION_STARTS
:
784 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
785 if ( info
->dataoff() < linkEditSegment
->fileoff() )
786 throw "function starts data not in __LINKEDIT";
787 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
788 throw "function starts data not in __LINKEDIT";
789 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
790 throw "function starts data table not pointer aligned";
791 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
792 throw "function starts data size not a multiple of pointer size";
795 case LC_DATA_IN_CODE
:
797 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
798 if ( info
->dataoff() < linkEditSegment
->fileoff() )
799 throw "data-in-code data not in __LINKEDIT";
800 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
801 throw "data-in-code data not in __LINKEDIT";
802 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
803 throw "data-in-code data table not pointer aligned";
804 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
805 throw "data-in-code data size not a multiple of pointer size";
808 case LC_DYLIB_CODE_SIGN_DRS
:
810 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
811 if ( info
->dataoff() < linkEditSegment
->fileoff() )
812 throw "dependent dylib DR data not in __LINKEDIT";
813 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
814 throw "dependent dylib DR data not in __LINKEDIT";
815 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
816 throw "dependent dylib DR data table not pointer aligned";
817 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
818 throw "dependent dylib DR data size not a multiple of pointer size";
822 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
824 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
825 throw "missing dynamic symbol table";
826 if ( fStrings
== NULL
)
827 throw "missing symbol table";
831 template <typename A
>
832 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
834 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
835 if ( sectionType
== S_ZEROFILL
) {
836 if ( sect
->offset() != 0 )
837 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
840 // check section's segment name matches segment
841 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
842 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
844 // more section tests here
848 template <typename A
>
849 void MachOChecker
<A
>::verify()
851 bool sharedCacheCandidate
= false;
852 if ( fInstallName
!= NULL
) {
853 if ( (strncmp(fInstallName
, "/usr/lib/", 9) == 0) || (strncmp(fInstallName
, "/System/Library/", 16) == 0) ) {
854 sharedCacheCandidate
= true;
859 verifyNoFlatLookups();
863 template <typename A
>
864 void MachOChecker
<A
>::verifyInstallName()
866 // Don't allow @rpath to be used as -install_name for OS dylibs
867 if ( strncmp(fInstallName
, "@rpath/", 7) == 0 ) {
868 printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName());
871 // Verify -install_name match actual path of dylib
872 const char* installPathWithinDstRoot
= &fPath
[strlen(fDstRoot
)];
873 if ( strcmp(installPathWithinDstRoot
, fInstallName
) != 0 ) {
874 // see if install name is a symlink to actual file
875 bool symlinkToDylib
= false;
876 char absDstPath
[PATH_MAX
];
877 if ( realpath(fDstRoot
, absDstPath
) != NULL
) {
878 char fullInstallNamePath
[PATH_MAX
];
879 strlcpy(fullInstallNamePath
, absDstPath
, PATH_MAX
);
880 strlcat(fullInstallNamePath
, fInstallName
, PATH_MAX
);
881 char absInstallNamePath
[PATH_MAX
];
882 if ( realpath(fullInstallNamePath
, absInstallNamePath
) != NULL
) {
883 char absFPath
[PATH_MAX
];
884 if ( realpath(fPath
, absFPath
) != NULL
) {
885 if ( strcmp(absInstallNamePath
, absFPath
) == 0 )
886 symlinkToDylib
= true;
890 if ( !symlinkToDylib
)
891 printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName());
897 template <typename A
>
898 void MachOChecker
<A
>::verifyNoRpaths()
900 // Don't allow OS dylibs to add rpaths
901 if ( fHasLC_RPATH
) {
902 printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName());
907 template <typename A
>
908 void MachOChecker
<A
>::verifyNoFlatLookups()
910 if ( (fHeader
->flags() & MH_TWOLEVEL
) == 0 ) {
911 printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName());
915 if ( fDynamicSymbolTable
!= NULL
) {
916 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
917 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
918 for(const macho_nlist
<P
>* sym
= undefinesStart
; sym
< undefinesEnd
; ++sym
) {
919 //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]);
920 if ( GET_LIBRARY_ORDINAL(sym
->n_desc()) == DYNAMIC_LOOKUP_ORDINAL
) {
921 const char* symName
= &fStrings
[sym
->n_strx()];
922 printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName
, archName());
928 template <typename A
>
929 void MachOChecker
<A
>::checkIndirectSymbolTable()
931 // static executables don't have indirect symbol table
932 if ( fDynamicSymbolTable
== NULL
)
934 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
935 const uint32_t cmd_count
= fHeader
->ncmds();
936 const macho_load_command
<P
>* cmd
= cmds
;
937 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
938 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
939 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
940 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
941 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
942 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
943 // make sure all magic sections that use indirect symbol table fit within it
945 uint32_t elementSize
= 0;
946 switch ( sect
->flags() & SECTION_TYPE
) {
948 elementSize
= sect
->reserved2();
949 start
= sect
->reserved1();
951 case S_LAZY_SYMBOL_POINTERS
:
952 case S_NON_LAZY_SYMBOL_POINTERS
:
953 elementSize
= sizeof(pint_t
);
954 start
= sect
->reserved1();
957 if ( elementSize
!= 0 ) {
958 uint32_t count
= sect
->size() / elementSize
;
959 if ( (count
*elementSize
) != sect
->size() )
960 throwf("%s section size is not an even multiple of element size", sect
->sectname());
961 if ( (start
+count
) > fIndirectTableCount
)
962 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
966 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
970 if ( fDynamicSymbolTable
->ilocalsym() != 0 )
971 throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable
->ilocalsym());
973 if ( fDynamicSymbolTable
->ilocalsym() > fSymbolCount
)
974 throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->ilocalsym(), fSymbolCount
);
975 if ( fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() > fSymbolCount
) {
976 throwf("local symbols out of range (%d+%d > %d) in indirect symbol table",
977 fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym(), fSymbolCount
);
980 if ( fDynamicSymbolTable
->iextdefsym() > fSymbolCount
)
981 throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iextdefsym(), fSymbolCount
);
982 if ( fDynamicSymbolTable
->iextdefsym() != fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() ) {
983 throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table",
984 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym() );
986 if ( fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() > fSymbolCount
) {
987 throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table",
988 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym(), fSymbolCount
);
991 if ( fDynamicSymbolTable
->iundefsym() > fSymbolCount
)
992 throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iundefsym(), fSymbolCount
);
993 if ( fDynamicSymbolTable
->iundefsym() != fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() ) {
994 throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table",
995 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym());
997 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() > fSymbolCount
) {
998 throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table",
999 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1002 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() != fSymbolCount
) {
1003 throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table",
1004 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1011 template <typename A
>
1012 void MachOChecker
<A
>::checkSymbolTable()
1014 // verify no duplicate external symbol names
1015 if ( fDynamicSymbolTable
!= NULL
) {
1016 StringSet externalNames
;
1017 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
1018 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
1019 int i
= fDynamicSymbolTable
->iextdefsym();
1020 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
1021 const char* symName
= &fStrings
[p
->n_strx()];
1022 if ( symName
> fStringsEnd
)
1023 throw "string index out of range";
1024 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
1025 if ( externalNames
.find(symName
) != externalNames
.end() )
1026 throwf("duplicate external symbol: %s", symName
);
1027 if ( (p
->n_type() & N_EXT
) == 0 )
1028 throwf("non-external symbol in external symbol range: %s", symName
);
1029 // don't add N_INDR to externalNames because there is likely an undefine with same name
1030 if ( (p
->n_type() & N_INDR
) == 0 )
1031 externalNames
.insert(symName
);
1033 // verify no undefines with same name as an external symbol
1034 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
1035 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
1036 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
1037 const char* symName
= &fStrings
[p
->n_strx()];
1038 if ( symName
> fStringsEnd
)
1039 throw "string index out of range";
1040 if ( externalNames
.find(symName
) != externalNames
.end() )
1041 throwf("undefine with same name as external symbol: %s", symName
);
1043 // verify all N_SECT values are valid
1044 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
1045 uint8_t type
= p
->n_type();
1046 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
1047 if ( p
->n_sect() > fSectionCount
) {
1048 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
1056 template <typename A
>
1057 void MachOChecker
<A
>::checkInitTerms()
1059 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1060 const uint32_t cmd_count
= fHeader
->ncmds();
1061 const macho_load_command
<P
>* cmd
= cmds
;
1062 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1063 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1064 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1065 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1066 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1067 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1068 // make sure all magic sections that use indirect symbol table fit within it
1072 const char* kind
= "initializer";
1073 switch ( sect
->flags() & SECTION_TYPE
) {
1074 case S_MOD_TERM_FUNC_POINTERS
:
1075 kind
= "terminator";
1077 case S_MOD_INIT_FUNC_POINTERS
:
1078 count
= sect
->size() / sizeof(pint_t
);
1079 if ( (count
*sizeof(pint_t
)) != sect
->size() )
1080 throwf("%s section size is not an even multiple of element size", sect
->sectname());
1081 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
1082 throwf("%s section size is not pointer size aligned", sect
->sectname());
1083 // check each pointer in array points within TEXT
1084 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
1085 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
1086 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
1087 pint_t pointer
= P::getP(*p
);
1088 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
1089 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
1091 // check each pointer in array will be rebased and not bound
1092 if ( fSlidableImage
) {
1093 pint_t sectionBeginAddr
= sect
->addr();
1094 pint_t sectionEndddr
= sect
->addr() + sect
->size();
1095 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
1096 if ( addressIsBindingSite(addr
) )
1097 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
1098 if ( ! addressIsRebaseSite(addr
) )
1099 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
1106 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1114 x86::P::uint_t MachOChecker
<x86
>::relocBase()
1116 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1117 return fFirstWritableSegment
->vmaddr();
1119 return fFirstSegment
->vmaddr();
1123 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
1125 // check for split-seg
1126 return fFirstWritableSegment
->vmaddr();
1130 arm::P::uint_t MachOChecker
<arm
>::relocBase()
1132 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1133 return fFirstWritableSegment
->vmaddr();
1135 return fFirstSegment
->vmaddr();
1139 arm64::P::uint_t MachOChecker
<arm64
>::relocBase()
1141 return fFirstWritableSegment
->vmaddr();
1145 template <typename A
>
1146 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
1148 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1149 const uint32_t cmd_count
= fHeader
->ncmds();
1150 const macho_load_command
<P
>* cmd
= cmds
;
1151 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1152 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1153 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1154 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
1155 // if segment is writable, we are fine
1156 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1158 // could be a text reloc, make sure section bit is set
1159 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1160 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1161 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1162 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
1163 // found section for this address, if has relocs we are fine
1164 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
1169 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1177 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1179 if ( reloc
->r_length() != 2 )
1180 throw "bad external relocation length";
1181 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1182 throw "unknown external relocation type";
1183 if ( reloc
->r_pcrel() != 0 )
1184 throw "bad external relocation pc_rel";
1185 if ( reloc
->r_extern() == 0 )
1186 throw "local relocation found with external relocations";
1187 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1188 throw "external relocation address not in writable segment";
1189 // FIX: check r_symbol
1194 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1196 if ( reloc
->r_length() != 3 )
1197 throw "bad external relocation length";
1198 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1199 throw "unknown external relocation type";
1200 if ( reloc
->r_pcrel() != 0 )
1201 throw "bad external relocation pc_rel";
1202 if ( reloc
->r_extern() == 0 )
1203 throw "local relocation found with external relocations";
1204 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1205 throw "exernal relocation address not in writable segment";
1206 // FIX: check r_symbol
1209 #if SUPPORT_ARCH_arm_any
1211 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1213 if ( reloc
->r_length() != 2 )
1214 throw "bad external relocation length";
1215 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1216 throw "unknown external relocation type";
1217 if ( reloc
->r_pcrel() != 0 )
1218 throw "bad external relocation pc_rel";
1219 if ( reloc
->r_extern() == 0 )
1220 throw "local relocation found with external relocations";
1221 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1222 throw "external relocation address not in writable segment";
1223 // FIX: check r_symbol
1227 #if SUPPORT_ARCH_arm64
1229 void MachOChecker
<arm64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1231 throw "external relocations not used for arm64";
1237 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1243 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1245 if ( reloc
->r_length() != 3 )
1246 throw "bad local relocation length";
1247 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1248 throw "unknown local relocation type";
1249 if ( reloc
->r_pcrel() != 0 )
1250 throw "bad local relocation pc_rel";
1251 if ( reloc
->r_extern() != 0 )
1252 throw "external relocation found with local relocations";
1253 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1254 throw "local relocation address not in writable segment";
1257 #if SUPPORT_ARCH_arm_any
1259 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1261 if ( reloc
->r_address() & R_SCATTERED
) {
1263 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1264 if ( sreloc
->r_length() != 2 )
1265 throw "bad local scattered relocation length";
1266 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1267 throw "bad local scattered relocation type";
1270 if ( reloc
->r_length() != 2 )
1271 throw "bad local relocation length";
1272 if ( reloc
->r_extern() != 0 )
1273 throw "external relocation found with local relocations";
1274 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1275 throw "local relocation address not in writable segment";
1280 #if SUPPORT_ARCH_arm64
1282 void MachOChecker
<arm64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1284 throw "local relocations not used for arm64";
1289 template <typename A
>
1290 void MachOChecker
<A
>::checkRelocations()
1292 // external relocations should be sorted to minimize dyld symbol lookups
1293 // therefore every reloc with the same r_symbolnum value should be contiguous
1294 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1295 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1296 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1297 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1298 this->checkExternalReloation(reloc
);
1299 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1300 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1301 throw "external relocations not sorted";
1302 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1303 lastSymbolIndex
= reloc
->r_symbolnum();
1307 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1308 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1309 this->checkLocalReloation(reloc
);
1312 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1313 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1314 const uint32_t cmd_count
= fHeader
->ncmds();
1315 const macho_load_command
<P
>* cmd
= cmds
;
1316 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1317 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1318 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1319 // if segment is writable, we are fine
1320 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1322 // look at sections that have text reloc bit set
1323 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1324 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1325 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1326 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1327 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1328 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1333 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1337 template <typename A
>
1338 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1340 if ( segIndex
> fSegments
.size() )
1341 throw "segment index out of range";
1342 return fSegments
[segIndex
]->vmaddr();
1345 template <typename A
>
1346 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1348 // look at local relocs
1349 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1350 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1351 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1352 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1356 if ( fDyldInfo
!= NULL
) {
1357 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1358 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1361 uint64_t segOffset
= 0;
1365 pint_t segStartAddr
= 0;
1368 while ( !done
&& (p
< end
) ) {
1369 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1370 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1373 case REBASE_OPCODE_DONE
:
1376 case REBASE_OPCODE_SET_TYPE_IMM
:
1379 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1380 segIndex
= immediate
;
1381 segStartAddr
= segStartAddress(segIndex
);
1382 segOffset
= read_uleb128(p
, end
);
1384 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1385 segOffset
+= read_uleb128(p
, end
);
1387 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1388 segOffset
+= immediate
*sizeof(pint_t
);
1390 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1391 for (int i
=0; i
< immediate
; ++i
) {
1392 addr
= segStartAddr
+segOffset
;
1393 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1395 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1396 segOffset
+= sizeof(pint_t
);
1399 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1400 count
= read_uleb128(p
, end
);
1401 for (uint32_t i
=0; i
< count
; ++i
) {
1402 addr
= segStartAddr
+segOffset
;
1403 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1405 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1406 segOffset
+= sizeof(pint_t
);
1409 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1410 addr
= segStartAddr
+segOffset
;
1411 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1413 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1414 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1416 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1417 count
= read_uleb128(p
, end
);
1418 skip
= read_uleb128(p
, end
);
1419 for (uint32_t i
=0; i
< count
; ++i
) {
1420 addr
= segStartAddr
+segOffset
;
1421 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1423 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1424 segOffset
+= skip
+ sizeof(pint_t
);
1428 throwf("bad rebase opcode %d", *p
);
1435 template <typename A
>
1436 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
1438 // look at local relocs
1439 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1440 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1441 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1442 if ( relocAddress
== targetAddr
)
1446 if ( fDyldInfo
!= NULL
) {
1447 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1448 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1451 uint64_t segOffset
= 0;
1455 pint_t segStartAddr
= 0;
1458 while ( !done
&& (p
< end
) ) {
1459 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1460 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1463 case REBASE_OPCODE_DONE
:
1466 case REBASE_OPCODE_SET_TYPE_IMM
:
1469 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1470 segIndex
= immediate
;
1471 segStartAddr
= segStartAddress(segIndex
);
1472 segOffset
= read_uleb128(p
, end
);
1474 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1475 segOffset
+= read_uleb128(p
, end
);
1477 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1478 segOffset
+= immediate
*sizeof(pint_t
);
1480 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1481 for (int i
=0; i
< immediate
; ++i
) {
1482 addr
= segStartAddr
+segOffset
;
1483 if ( addr
== targetAddr
)
1485 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1486 segOffset
+= sizeof(pint_t
);
1489 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1490 count
= read_uleb128(p
, end
);
1491 for (uint32_t i
=0; i
< count
; ++i
) {
1492 addr
= segStartAddr
+segOffset
;
1493 if ( addr
== targetAddr
)
1495 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1496 segOffset
+= sizeof(pint_t
);
1499 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1500 addr
= segStartAddr
+segOffset
;
1501 if ( addr
== targetAddr
)
1503 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1504 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1506 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1507 count
= read_uleb128(p
, end
);
1508 skip
= read_uleb128(p
, end
);
1509 for (uint32_t i
=0; i
< count
; ++i
) {
1510 addr
= segStartAddr
+segOffset
;
1511 if ( addr
== targetAddr
)
1513 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1514 segOffset
+= skip
+ sizeof(pint_t
);
1518 throwf("bad rebase opcode %d", *p
);
1526 template <typename A
>
1527 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1529 // look at external relocs
1530 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1531 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1532 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1533 if ( relocAddress
== targetAddr
)
1537 if ( fDyldInfo
!= NULL
) {
1538 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1539 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1542 uint64_t segOffset
= 0;
1546 const char* symbolName
= NULL
;
1547 int libraryOrdinal
= 0;
1550 pint_t segStartAddr
= 0;
1553 while ( !done
&& (p
< end
) ) {
1554 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1555 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1558 case BIND_OPCODE_DONE
:
1561 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1562 libraryOrdinal
= immediate
;
1564 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1565 libraryOrdinal
= read_uleb128(p
, end
);
1567 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1568 // the special ordinals are negative numbers
1569 if ( immediate
== 0 )
1572 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1573 libraryOrdinal
= signExtended
;
1576 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1577 symbolName
= (char*)p
;
1582 case BIND_OPCODE_SET_TYPE_IMM
:
1585 case BIND_OPCODE_SET_ADDEND_SLEB
:
1586 addend
= read_sleb128(p
, end
);
1588 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1589 segIndex
= immediate
;
1590 segStartAddr
= segStartAddress(segIndex
);
1591 segOffset
= read_uleb128(p
, end
);
1593 case BIND_OPCODE_ADD_ADDR_ULEB
:
1594 segOffset
+= read_uleb128(p
, end
);
1596 case BIND_OPCODE_DO_BIND
:
1597 if ( (segStartAddr
+segOffset
) == targetAddr
)
1599 segOffset
+= sizeof(pint_t
);
1601 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
1602 if ( (segStartAddr
+segOffset
) == targetAddr
)
1604 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1606 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
1607 if ( (segStartAddr
+segOffset
) == targetAddr
)
1609 segOffset
+= immediate
*sizeof(pint_t
) + sizeof(pint_t
);
1611 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
1612 count
= read_uleb128(p
, end
);
1613 skip
= read_uleb128(p
, end
);
1614 for (uint32_t i
=0; i
< count
; ++i
) {
1615 if ( (segStartAddr
+segOffset
) == targetAddr
)
1617 segOffset
+= skip
+ sizeof(pint_t
);
1621 throwf("bad bind opcode %d", *p
);
1629 static void check(const char* path
, const char* verifierDstRoot
)
1631 struct stat stat_buf
;
1634 int fd
= ::open(path
, O_RDONLY
, 0);
1636 throw "cannot open file";
1637 if ( ::fstat(fd
, &stat_buf
) != 0 )
1638 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1639 uint32_t length
= stat_buf
.st_size
;
1640 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1641 if ( p
== ((uint8_t*)(-1)) )
1642 throw "cannot map file";
1644 const mach_header
* mh
= (mach_header
*)p
;
1645 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1646 const struct fat_header
* fh
= (struct fat_header
*)p
;
1647 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1648 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1649 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1650 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1651 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1655 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1656 MachOChecker
<x86
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1658 throw "in universal file, i386 slice does not contain i386 mach-o";
1660 case CPU_TYPE_X86_64
:
1661 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1662 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1664 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1666 #if SUPPORT_ARCH_arm_any
1668 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1669 MachOChecker
<arm
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1671 throw "in universal file, arm slice does not contain arm mach-o";
1674 #if SUPPORT_ARCH_arm64
1675 case CPU_TYPE_ARM64
:
1676 if ( MachOChecker
<arm64
>::validFile(p
+ offset
) )
1677 MachOChecker
<arm64
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1679 throw "in universal file, arm64 slice does not contain arm mach-o";
1683 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1687 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1688 MachOChecker
<x86
>::make(p
, length
, path
, verifierDstRoot
);
1690 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1691 MachOChecker
<x86_64
>::make(p
, length
, path
, verifierDstRoot
);
1693 #if SUPPORT_ARCH_arm_any
1694 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1695 MachOChecker
<arm
>::make(p
, length
, path
, verifierDstRoot
);
1698 #if SUPPORT_ARCH_arm64
1699 else if ( MachOChecker
<arm64
>::validFile(p
) ) {
1700 MachOChecker
<arm64
>::make(p
, length
, path
, verifierDstRoot
);
1704 throw "not a known file type";
1707 catch (const char* msg
) {
1708 throwf("%s in %s", msg
, path
);
1713 int main(int argc
, const char* argv
[])
1715 bool progress
= false;
1716 const char* verifierDstRoot
= NULL
;
1718 for(int i
=1; i
< argc
; ++i
) {
1719 const char* arg
= argv
[i
];
1720 if ( arg
[0] == '-' ) {
1721 if ( strcmp(arg
, "-progress") == 0 ) {
1724 else if ( strcmp(arg
, "-verifier_dstroot") == 0 ) {
1725 verifierDstRoot
= argv
[++i
];
1727 else if ( strcmp(arg
, "-verifier_error_list") == 0 ) {
1728 printf("os_dylib_rpath_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name that is an absolute path - not an @rpath\n");
1729 printf("os_dylib_bad_install_name\tOS dylibs (those in /usr/lib/ or /System/Library/) must be built with -install_name matching their file system location\n");
1730 printf("os_dylib_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)\n");
1731 printf("os_dylib_flat_namespace\tOS dylibs should not be built with -flat_namespace\n");
1732 printf("os_dylib_undefined_dynamic_lookup\tOS dylibs should not be built with -undefined dynamic_lookup\n");
1733 printf("os_dylib_malformed\the mach-o is malformed\n");
1737 throwf("unknown option: %s\n", arg
);
1741 bool success
= true;
1743 check(arg
, verifierDstRoot
);
1745 catch (const char* msg
) {
1746 if ( verifierDstRoot
) {
1747 printf("os_dylib_malformed\twarn\t%s\n", msg
);
1750 fprintf(stderr
, "machocheck failed: %s\n", msg
);
1755 if ( success
&& progress
)
1756 printf("ok: %s\n", arg
);