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_VERSION_MIN_TVOS
:
459 case LC_VERSION_MIN_WATCHOS
:
460 case LC_FUNCTION_STARTS
:
461 case LC_DYLD_ENVIRONMENT
:
462 case LC_DATA_IN_CODE
:
463 case LC_DYLIB_CODE_SIGN_DRS
:
464 case LC_SOURCE_VERSION
:
470 dylibID
= (macho_dylib_command
<P
>*)cmd
;
471 if ( dylibID
->name_offset() > size
)
472 throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID
->name_offset(), size
);
473 if ( (dylibID
->name_offset() + strlen(dylibID
->name()) + 1) > size
)
474 throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command");
475 fInstallName
= dylibID
->name();
478 case LC_DYLD_INFO_ONLY
:
479 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
481 case LC_ENCRYPTION_INFO
:
482 case LC_ENCRYPTION_INFO_64
:
483 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
485 case LC_SUB_UMBRELLA
:
487 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
488 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";
491 if ( fHeader
->filetype() != MH_EXECUTE
)
492 throw "LC_MAIN can only be used in MH_EXECUTE file types";
493 entryPoint
= (macho_entry_point_command
<P
>*)cmd
;
496 if ( fHeader
->filetype() != MH_EXECUTE
)
497 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
498 threadInfo
= (macho_thread_command
<P
>*)cmd
;
501 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
503 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
508 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
509 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
510 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
511 const macho_segment_command
<P
>* stackSegment
= NULL
;
512 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
513 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
514 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
515 fSegments
.push_back(segCmd
);
516 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
517 throw "invalid segment load command size";
519 // see if this overlaps another segment address range
520 uint64_t startAddr
= segCmd
->vmaddr();
521 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
522 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
523 if ( it
->first
< startAddr
) {
524 if ( it
->second
> startAddr
)
525 throw "overlapping segment vm addresses";
527 else if ( it
->first
> startAddr
) {
528 if ( it
->first
< endAddr
)
529 throw "overlapping segment vm addresses";
532 throw "overlapping segment vm addresses";
534 segmentAddressRanges
.push_back(std::make_pair(startAddr
, endAddr
));
536 // see if this overlaps another segment file offset range
537 uint64_t startOffset
= segCmd
->fileoff();
538 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
539 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
540 if ( it
->first
< startOffset
) {
541 if ( it
->second
> startOffset
)
542 throw "overlapping segment file data";
544 else if ( it
->first
> startOffset
) {
545 if ( it
->first
< endOffset
)
546 throw "overlapping segment file data";
549 throw "overlapping segment file data";
551 segmentFileOffsetRanges
.push_back(std::make_pair(startOffset
, endOffset
));
552 // check is within file bounds
553 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
554 throw "segment file data is past end of file";
556 // verify it fits in file
557 if ( startOffset
> fLength
)
558 throw "segment fileoff does not fit in file";
559 if ( endOffset
> fLength
)
560 throw "segment fileoff+filesize does not fit in file";
562 // record special segments
563 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
564 linkEditSegment
= segCmd
;
565 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
566 stackSegment
= segCmd
;
568 // cache interesting segments
569 if ( fFirstSegment
== NULL
)
570 fFirstSegment
= segCmd
;
571 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
572 fTEXTSegment
= segCmd
;
573 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
574 if ( fFirstWritableSegment
== NULL
)
575 fFirstWritableSegment
= segCmd
;
576 if ( segCmd
->vmaddr() > 0x100000000ULL
)
577 fWriteableSegmentWithAddrOver4G
= true;
580 // check section ranges
581 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
582 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
583 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
584 // check all non-zero sized sections are within segment
585 if ( sect
->addr() < startAddr
)
586 throwf("section %s vm address not within segment", sect
->sectname());
587 if ( (sect
->addr()+sect
->size()) > endAddr
)
588 throwf("section %s vm address not within segment", sect
->sectname());
589 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
590 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
591 && (segCmd
->filesize() != 0)
592 && (sect
->size() != 0) ) {
593 if ( sect
->offset() < startOffset
)
594 throwf("section %s file offset not within segment", sect
->sectname());
595 if ( (sect
->offset()+sect
->size()) > endOffset
)
596 throwf("section %s file offset not within segment", sect
->sectname());
598 checkSection(segCmd
, sect
);
602 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
605 // verify there was a LINKEDIT segment
606 if ( linkEditSegment
== NULL
)
607 throw "no __LINKEDIT segment";
609 // verify there was an executable __TEXT segment and load commands are in it
610 if ( fTEXTSegment
== NULL
)
611 throw "no __TEXT segment";
612 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
613 throw "__TEXT segment does not have r-x init permissions";
614 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
615 // throw "__TEXT segment does not have rwx max permissions";
616 if ( fTEXTSegment
->fileoff() != 0 )
617 throw "__TEXT segment does not start at mach_header";
618 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
619 throw "__TEXT segment smaller than load commands";
621 // verify if custom stack used, that stack is in __UNIXSTACK segment
622 if ( threadInfo
!= NULL
) {
623 pint_t initialSP
= getInitialStackPointer(threadInfo
);
624 if ( initialSP
!= 0 ) {
625 if ( stackSegment
== NULL
)
626 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
627 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
628 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
632 // verify __UNIXSTACK is zero fill
633 if ( stackSegment
!= NULL
) {
634 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
635 throw "__UNIXSTACK is not a zero-fill segment";
636 if ( stackSegment
->vmsize() < 4096 )
637 throw "__UNIXSTACK segment is too small";
640 // verify entry point is in __TEXT segment
641 if ( threadInfo
!= NULL
) {
642 pint_t initialPC
= getEntryPoint(threadInfo
);
643 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
644 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
646 else if ( entryPoint
!= NULL
) {
647 pint_t initialOffset
= entryPoint
->entryoff();
648 if ( (initialOffset
< fTEXTSegment
->fileoff()) || (initialOffset
>= (fTEXTSegment
->fileoff()+fTEXTSegment
->filesize())) )
649 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset
);
652 // checks for executables
653 bool isStaticExecutable
= false;
654 if ( fHeader
->filetype() == MH_EXECUTE
) {
655 isStaticExecutable
= true;
657 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
658 switch ( cmd
->cmd() ) {
659 case LC_LOAD_DYLINKER
:
660 // the existence of a dyld load command makes a executable dynamic
661 isStaticExecutable
= false;
664 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
666 if ( isStaticExecutable
) {
667 if ( (fHeader
->flags() != MH_NOUNDEFS
) && (fHeader
->flags() != (MH_NOUNDEFS
|MH_PIE
)) )
668 throw "invalid bits in mach_header flags for static executable";
672 // verify encryption info
673 if ( encryption_info
!= NULL
) {
674 switch ( fHeader
->filetype() ) {
675 case MH_EXECUTE
: case MH_DYLIB
: case MH_BUNDLE
:
678 throw "LC_ENCRYPTION_INFO load command is not allowed in this file type";
680 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
681 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
682 if ( (encryption_info
->cryptoff() % 4096) != 0 )
683 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
684 if ( (encryption_info
->cryptsize() % 4096) != 0 )
685 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
686 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
687 it
!= segmentFileOffsetRanges
.end(); ++it
) {
688 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
689 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
690 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
695 // verify dylib has LC_ID_DYLIB
696 if ( fHeader
->filetype() == MH_DYLIB
) {
697 if ( fInstallName
== NULL
)
698 throw "MH_DYLIB missing LC_ID_DYLIB";
701 if ( fInstallName
!= NULL
)
702 throw "LC_ID_DYLIB found but file type is not MH_DYLIB";
705 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
707 bool foundDynamicSymTab
= false;
708 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
709 switch ( cmd
->cmd() ) {
712 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
713 fSymbolCount
= symtab
->nsyms();
714 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
715 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
716 throw "symbol table not in __LINKEDIT";
717 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > symtab
->stroff() )
718 throw "symbol table overlaps string pool";
719 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
720 throw "symbol table start not pointer aligned";
721 fStrings
= (char*)fHeader
+ symtab
->stroff();
722 fStringsEnd
= fStrings
+ symtab
->strsize();
723 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
724 throw "string pool not in __LINKEDIT";
725 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
726 throw "string pool extends beyond __LINKEDIT";
727 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
728 throw "string pool start not pointer aligned";
733 if ( isStaticExecutable
&&! fSlidableImage
)
734 throw "LC_DYSYMTAB should not be used in static executable";
735 foundDynamicSymTab
= true;
736 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
737 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
738 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
739 if ( fIndirectTableCount
!= 0 ) {
740 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
741 throw "indirect symbol table not in __LINKEDIT";
742 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
743 throw "indirect symbol table not in __LINKEDIT";
744 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
745 throw "indirect symbol table not pointer aligned";
747 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
748 if ( fLocalRelocationsCount
!= 0 ) {
749 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
750 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
751 throw "local relocations not in __LINKEDIT";
752 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
753 throw "local relocations not in __LINKEDIT";
754 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
755 throw "local relocations table not pointer aligned";
757 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
758 if ( fExternalRelocationsCount
!= 0 ) {
759 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
760 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
761 throw "external relocations not in __LINKEDIT";
762 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
763 throw "external relocations not in __LINKEDIT";
764 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
765 throw "external relocations table not pointer aligned";
769 case LC_SEGMENT_SPLIT_INFO
:
771 if ( isStaticExecutable
)
772 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
773 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
774 if ( info
->dataoff() < linkEditSegment
->fileoff() )
775 throw "split seg info not in __LINKEDIT";
776 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
777 throw "split seg info not in __LINKEDIT";
778 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
779 throw "split seg info table not pointer aligned";
780 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
781 throw "split seg info size not a multiple of pointer size";
784 case LC_FUNCTION_STARTS
:
786 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
787 if ( info
->dataoff() < linkEditSegment
->fileoff() )
788 throw "function starts data not in __LINKEDIT";
789 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
790 throw "function starts data not in __LINKEDIT";
791 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
792 throw "function starts data table not pointer aligned";
793 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
794 throw "function starts data size not a multiple of pointer size";
797 case LC_DATA_IN_CODE
:
799 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
800 if ( info
->dataoff() < linkEditSegment
->fileoff() )
801 throw "data-in-code data not in __LINKEDIT";
802 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
803 throw "data-in-code data not in __LINKEDIT";
804 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
805 throw "data-in-code data table not pointer aligned";
806 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
807 throw "data-in-code data size not a multiple of pointer size";
810 case LC_DYLIB_CODE_SIGN_DRS
:
812 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
813 if ( info
->dataoff() < linkEditSegment
->fileoff() )
814 throw "dependent dylib DR data not in __LINKEDIT";
815 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
816 throw "dependent dylib DR data not in __LINKEDIT";
817 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
818 throw "dependent dylib DR data table not pointer aligned";
819 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
820 throw "dependent dylib DR data size not a multiple of pointer size";
824 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
826 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
827 throw "missing dynamic symbol table";
828 if ( fStrings
== NULL
)
829 throw "missing symbol table";
833 template <typename A
>
834 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
836 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
837 if ( sectionType
== S_ZEROFILL
) {
838 if ( sect
->offset() != 0 )
839 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
842 // check section's segment name matches segment
843 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
844 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
846 // more section tests here
850 template <typename A
>
851 void MachOChecker
<A
>::verify()
853 bool sharedCacheCandidate
= false;
854 if ( fInstallName
!= NULL
) {
855 if ( (strncmp(fInstallName
, "/usr/lib/", 9) == 0) || (strncmp(fInstallName
, "/System/Library/", 16) == 0) ) {
856 sharedCacheCandidate
= true;
861 verifyNoFlatLookups();
865 template <typename A
>
866 void MachOChecker
<A
>::verifyInstallName()
868 // Don't allow @rpath to be used as -install_name for OS dylibs
869 if ( strncmp(fInstallName
, "@rpath/", 7) == 0 ) {
870 printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName());
873 // Verify -install_name match actual path of dylib
874 const char* installPathWithinDstRoot
= &fPath
[strlen(fDstRoot
)];
875 if ( strcmp(installPathWithinDstRoot
, fInstallName
) != 0 ) {
876 // see if install name is a symlink to actual file
877 bool symlinkToDylib
= false;
878 char absDstPath
[PATH_MAX
];
879 if ( realpath(fDstRoot
, absDstPath
) != NULL
) {
880 char fullInstallNamePath
[PATH_MAX
];
881 strlcpy(fullInstallNamePath
, absDstPath
, PATH_MAX
);
882 strlcat(fullInstallNamePath
, fInstallName
, PATH_MAX
);
883 char absInstallNamePath
[PATH_MAX
];
884 if ( realpath(fullInstallNamePath
, absInstallNamePath
) != NULL
) {
885 char absFPath
[PATH_MAX
];
886 if ( realpath(fPath
, absFPath
) != NULL
) {
887 if ( strcmp(absInstallNamePath
, absFPath
) == 0 )
888 symlinkToDylib
= true;
892 if ( !symlinkToDylib
)
893 printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName());
899 template <typename A
>
900 void MachOChecker
<A
>::verifyNoRpaths()
902 // Don't allow OS dylibs to add rpaths
903 if ( fHasLC_RPATH
) {
904 printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName());
909 template <typename A
>
910 void MachOChecker
<A
>::verifyNoFlatLookups()
912 if ( (fHeader
->flags() & MH_TWOLEVEL
) == 0 ) {
913 printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName());
917 if ( fDynamicSymbolTable
!= NULL
) {
918 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
919 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
920 for(const macho_nlist
<P
>* sym
= undefinesStart
; sym
< undefinesEnd
; ++sym
) {
921 //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]);
922 if ( GET_LIBRARY_ORDINAL(sym
->n_desc()) == DYNAMIC_LOOKUP_ORDINAL
) {
923 const char* symName
= &fStrings
[sym
->n_strx()];
924 printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName
, archName());
930 template <typename A
>
931 void MachOChecker
<A
>::checkIndirectSymbolTable()
933 // static executables don't have indirect symbol table
934 if ( fDynamicSymbolTable
== NULL
)
936 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
937 const uint32_t cmd_count
= fHeader
->ncmds();
938 const macho_load_command
<P
>* cmd
= cmds
;
939 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
940 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
941 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
942 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
943 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
944 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
945 // make sure all magic sections that use indirect symbol table fit within it
947 uint32_t elementSize
= 0;
948 switch ( sect
->flags() & SECTION_TYPE
) {
950 elementSize
= sect
->reserved2();
951 start
= sect
->reserved1();
953 case S_LAZY_SYMBOL_POINTERS
:
954 case S_NON_LAZY_SYMBOL_POINTERS
:
955 elementSize
= sizeof(pint_t
);
956 start
= sect
->reserved1();
959 if ( elementSize
!= 0 ) {
960 uint32_t count
= sect
->size() / elementSize
;
961 if ( (count
*elementSize
) != sect
->size() )
962 throwf("%s section size is not an even multiple of element size", sect
->sectname());
963 if ( (start
+count
) > fIndirectTableCount
)
964 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
968 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
972 if ( fDynamicSymbolTable
->ilocalsym() != 0 )
973 throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable
->ilocalsym());
975 if ( fDynamicSymbolTable
->ilocalsym() > fSymbolCount
)
976 throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->ilocalsym(), fSymbolCount
);
977 if ( fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() > fSymbolCount
) {
978 throwf("local symbols out of range (%d+%d > %d) in indirect symbol table",
979 fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym(), fSymbolCount
);
982 if ( fDynamicSymbolTable
->iextdefsym() > fSymbolCount
)
983 throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iextdefsym(), fSymbolCount
);
984 if ( fDynamicSymbolTable
->iextdefsym() != fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() ) {
985 throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table",
986 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym() );
988 if ( fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() > fSymbolCount
) {
989 throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table",
990 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym(), fSymbolCount
);
993 if ( fDynamicSymbolTable
->iundefsym() > fSymbolCount
)
994 throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iundefsym(), fSymbolCount
);
995 if ( fDynamicSymbolTable
->iundefsym() != fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() ) {
996 throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table",
997 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym());
999 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() > fSymbolCount
) {
1000 throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table",
1001 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1004 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() != fSymbolCount
) {
1005 throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table",
1006 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1013 template <typename A
>
1014 void MachOChecker
<A
>::checkSymbolTable()
1016 // verify no duplicate external symbol names
1017 if ( fDynamicSymbolTable
!= NULL
) {
1018 StringSet externalNames
;
1019 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
1020 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
1021 int i
= fDynamicSymbolTable
->iextdefsym();
1022 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
1023 const char* symName
= &fStrings
[p
->n_strx()];
1024 if ( symName
> fStringsEnd
)
1025 throw "string index out of range";
1026 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
1027 if ( externalNames
.find(symName
) != externalNames
.end() )
1028 throwf("duplicate external symbol: %s", symName
);
1029 if ( (p
->n_type() & N_EXT
) == 0 )
1030 throwf("non-external symbol in external symbol range: %s", symName
);
1031 // don't add N_INDR to externalNames because there is likely an undefine with same name
1032 if ( (p
->n_type() & N_INDR
) == 0 )
1033 externalNames
.insert(symName
);
1035 // verify no undefines with same name as an external symbol
1036 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
1037 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
1038 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
1039 const char* symName
= &fStrings
[p
->n_strx()];
1040 if ( symName
> fStringsEnd
)
1041 throw "string index out of range";
1042 if ( externalNames
.find(symName
) != externalNames
.end() )
1043 throwf("undefine with same name as external symbol: %s", symName
);
1045 // verify all N_SECT values are valid
1046 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
1047 uint8_t type
= p
->n_type();
1048 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
1049 if ( p
->n_sect() > fSectionCount
) {
1050 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
1058 template <typename A
>
1059 void MachOChecker
<A
>::checkInitTerms()
1061 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1062 const uint32_t cmd_count
= fHeader
->ncmds();
1063 const macho_load_command
<P
>* cmd
= cmds
;
1064 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1065 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1066 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1067 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1068 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1069 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1070 // make sure all magic sections that use indirect symbol table fit within it
1074 const char* kind
= "initializer";
1075 switch ( sect
->flags() & SECTION_TYPE
) {
1076 case S_MOD_TERM_FUNC_POINTERS
:
1077 kind
= "terminator";
1079 case S_MOD_INIT_FUNC_POINTERS
:
1080 count
= sect
->size() / sizeof(pint_t
);
1081 if ( (count
*sizeof(pint_t
)) != sect
->size() )
1082 throwf("%s section size is not an even multiple of element size", sect
->sectname());
1083 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
1084 throwf("%s section size is not pointer size aligned", sect
->sectname());
1085 // check each pointer in array points within TEXT
1086 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
1087 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
1088 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
1089 pint_t pointer
= P::getP(*p
);
1090 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
1091 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
1093 // check each pointer in array will be rebased and not bound
1094 if ( fSlidableImage
) {
1095 pint_t sectionBeginAddr
= sect
->addr();
1096 pint_t sectionEndddr
= sect
->addr() + sect
->size();
1097 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
1098 if ( addressIsBindingSite(addr
) )
1099 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
1100 if ( ! addressIsRebaseSite(addr
) )
1101 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
1108 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1116 x86::P::uint_t MachOChecker
<x86
>::relocBase()
1118 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1119 return fFirstWritableSegment
->vmaddr();
1121 return fFirstSegment
->vmaddr();
1125 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
1127 // check for split-seg
1128 return fFirstWritableSegment
->vmaddr();
1132 arm::P::uint_t MachOChecker
<arm
>::relocBase()
1134 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1135 return fFirstWritableSegment
->vmaddr();
1137 return fFirstSegment
->vmaddr();
1141 arm64::P::uint_t MachOChecker
<arm64
>::relocBase()
1143 return fFirstWritableSegment
->vmaddr();
1147 template <typename A
>
1148 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
1150 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1151 const uint32_t cmd_count
= fHeader
->ncmds();
1152 const macho_load_command
<P
>* cmd
= cmds
;
1153 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1154 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1155 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1156 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
1157 // if segment is writable, we are fine
1158 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1160 // could be a text reloc, make sure section bit is set
1161 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1162 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1163 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1164 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
1165 // found section for this address, if has relocs we are fine
1166 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
1171 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1179 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1181 if ( reloc
->r_length() != 2 )
1182 throw "bad external relocation length";
1183 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1184 throw "unknown external relocation type";
1185 if ( reloc
->r_pcrel() != 0 )
1186 throw "bad external relocation pc_rel";
1187 if ( reloc
->r_extern() == 0 )
1188 throw "local relocation found with external relocations";
1189 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1190 throw "external relocation address not in writable segment";
1191 // FIX: check r_symbol
1196 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1198 if ( reloc
->r_length() != 3 )
1199 throw "bad external relocation length";
1200 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1201 throw "unknown external relocation type";
1202 if ( reloc
->r_pcrel() != 0 )
1203 throw "bad external relocation pc_rel";
1204 if ( reloc
->r_extern() == 0 )
1205 throw "local relocation found with external relocations";
1206 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1207 throw "exernal relocation address not in writable segment";
1208 // FIX: check r_symbol
1211 #if SUPPORT_ARCH_arm_any
1213 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1215 if ( reloc
->r_length() != 2 )
1216 throw "bad external relocation length";
1217 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1218 throw "unknown external relocation type";
1219 if ( reloc
->r_pcrel() != 0 )
1220 throw "bad external relocation pc_rel";
1221 if ( reloc
->r_extern() == 0 )
1222 throw "local relocation found with external relocations";
1223 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1224 throw "external relocation address not in writable segment";
1225 // FIX: check r_symbol
1229 #if SUPPORT_ARCH_arm64
1231 void MachOChecker
<arm64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1233 throw "external relocations not used for arm64";
1239 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1245 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1247 if ( reloc
->r_length() != 3 )
1248 throw "bad local relocation length";
1249 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1250 throw "unknown local relocation type";
1251 if ( reloc
->r_pcrel() != 0 )
1252 throw "bad local relocation pc_rel";
1253 if ( reloc
->r_extern() != 0 )
1254 throw "external relocation found with local relocations";
1255 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1256 throw "local relocation address not in writable segment";
1259 #if SUPPORT_ARCH_arm_any
1261 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1263 if ( reloc
->r_address() & R_SCATTERED
) {
1265 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1266 if ( sreloc
->r_length() != 2 )
1267 throw "bad local scattered relocation length";
1268 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1269 throw "bad local scattered relocation type";
1272 if ( reloc
->r_length() != 2 )
1273 throw "bad local relocation length";
1274 if ( reloc
->r_extern() != 0 )
1275 throw "external relocation found with local relocations";
1276 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1277 throw "local relocation address not in writable segment";
1282 #if SUPPORT_ARCH_arm64
1284 void MachOChecker
<arm64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1286 throw "local relocations not used for arm64";
1291 template <typename A
>
1292 void MachOChecker
<A
>::checkRelocations()
1294 // external relocations should be sorted to minimize dyld symbol lookups
1295 // therefore every reloc with the same r_symbolnum value should be contiguous
1296 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1297 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1298 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1299 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1300 this->checkExternalReloation(reloc
);
1301 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1302 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1303 throw "external relocations not sorted";
1304 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1305 lastSymbolIndex
= reloc
->r_symbolnum();
1309 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1310 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1311 this->checkLocalReloation(reloc
);
1314 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1315 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1316 const uint32_t cmd_count
= fHeader
->ncmds();
1317 const macho_load_command
<P
>* cmd
= cmds
;
1318 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1319 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1320 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1321 // if segment is writable, we are fine
1322 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1324 // look at sections that have text reloc bit set
1325 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1326 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1327 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1328 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1329 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1330 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1335 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1339 template <typename A
>
1340 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1342 if ( segIndex
> fSegments
.size() )
1343 throw "segment index out of range";
1344 return fSegments
[segIndex
]->vmaddr();
1347 template <typename A
>
1348 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1350 // look at local relocs
1351 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1352 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1353 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1354 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1358 if ( fDyldInfo
!= NULL
) {
1359 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1360 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1363 uint64_t segOffset
= 0;
1367 pint_t segStartAddr
= 0;
1370 while ( !done
&& (p
< end
) ) {
1371 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1372 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1375 case REBASE_OPCODE_DONE
:
1378 case REBASE_OPCODE_SET_TYPE_IMM
:
1381 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1382 segIndex
= immediate
;
1383 segStartAddr
= segStartAddress(segIndex
);
1384 segOffset
= read_uleb128(p
, end
);
1386 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1387 segOffset
+= read_uleb128(p
, end
);
1389 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1390 segOffset
+= immediate
*sizeof(pint_t
);
1392 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1393 for (int i
=0; i
< immediate
; ++i
) {
1394 addr
= segStartAddr
+segOffset
;
1395 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1397 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1398 segOffset
+= sizeof(pint_t
);
1401 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1402 count
= read_uleb128(p
, end
);
1403 for (uint32_t i
=0; i
< count
; ++i
) {
1404 addr
= segStartAddr
+segOffset
;
1405 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1407 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1408 segOffset
+= sizeof(pint_t
);
1411 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1412 addr
= segStartAddr
+segOffset
;
1413 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1415 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1416 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1418 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1419 count
= read_uleb128(p
, end
);
1420 skip
= read_uleb128(p
, end
);
1421 for (uint32_t i
=0; i
< count
; ++i
) {
1422 addr
= segStartAddr
+segOffset
;
1423 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1425 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1426 segOffset
+= skip
+ sizeof(pint_t
);
1430 throwf("bad rebase opcode %d", *p
);
1437 template <typename A
>
1438 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
1440 // look at local relocs
1441 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1442 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1443 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1444 if ( relocAddress
== targetAddr
)
1448 if ( fDyldInfo
!= NULL
) {
1449 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1450 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1453 uint64_t segOffset
= 0;
1457 pint_t segStartAddr
= 0;
1460 while ( !done
&& (p
< end
) ) {
1461 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1462 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1465 case REBASE_OPCODE_DONE
:
1468 case REBASE_OPCODE_SET_TYPE_IMM
:
1471 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1472 segIndex
= immediate
;
1473 segStartAddr
= segStartAddress(segIndex
);
1474 segOffset
= read_uleb128(p
, end
);
1476 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1477 segOffset
+= read_uleb128(p
, end
);
1479 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1480 segOffset
+= immediate
*sizeof(pint_t
);
1482 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1483 for (int i
=0; i
< immediate
; ++i
) {
1484 addr
= segStartAddr
+segOffset
;
1485 if ( addr
== targetAddr
)
1487 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1488 segOffset
+= sizeof(pint_t
);
1491 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1492 count
= read_uleb128(p
, end
);
1493 for (uint32_t i
=0; i
< count
; ++i
) {
1494 addr
= segStartAddr
+segOffset
;
1495 if ( addr
== targetAddr
)
1497 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1498 segOffset
+= sizeof(pint_t
);
1501 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1502 addr
= segStartAddr
+segOffset
;
1503 if ( addr
== targetAddr
)
1505 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1506 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1508 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1509 count
= read_uleb128(p
, end
);
1510 skip
= read_uleb128(p
, end
);
1511 for (uint32_t i
=0; i
< count
; ++i
) {
1512 addr
= segStartAddr
+segOffset
;
1513 if ( addr
== targetAddr
)
1515 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1516 segOffset
+= skip
+ sizeof(pint_t
);
1520 throwf("bad rebase opcode %d", *p
);
1528 template <typename A
>
1529 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1531 // look at external relocs
1532 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1533 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1534 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1535 if ( relocAddress
== targetAddr
)
1539 if ( fDyldInfo
!= NULL
) {
1540 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1541 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1544 uint64_t segOffset
= 0;
1548 const char* symbolName
= NULL
;
1549 int libraryOrdinal
= 0;
1552 pint_t segStartAddr
= 0;
1555 while ( !done
&& (p
< end
) ) {
1556 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1557 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1560 case BIND_OPCODE_DONE
:
1563 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1564 libraryOrdinal
= immediate
;
1566 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1567 libraryOrdinal
= read_uleb128(p
, end
);
1569 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1570 // the special ordinals are negative numbers
1571 if ( immediate
== 0 )
1574 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1575 libraryOrdinal
= signExtended
;
1578 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1579 symbolName
= (char*)p
;
1584 case BIND_OPCODE_SET_TYPE_IMM
:
1587 case BIND_OPCODE_SET_ADDEND_SLEB
:
1588 addend
= read_sleb128(p
, end
);
1590 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1591 segIndex
= immediate
;
1592 segStartAddr
= segStartAddress(segIndex
);
1593 segOffset
= read_uleb128(p
, end
);
1595 case BIND_OPCODE_ADD_ADDR_ULEB
:
1596 segOffset
+= read_uleb128(p
, end
);
1598 case BIND_OPCODE_DO_BIND
:
1599 if ( (segStartAddr
+segOffset
) == targetAddr
)
1601 segOffset
+= sizeof(pint_t
);
1603 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
1604 if ( (segStartAddr
+segOffset
) == targetAddr
)
1606 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1608 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
1609 if ( (segStartAddr
+segOffset
) == targetAddr
)
1611 segOffset
+= immediate
*sizeof(pint_t
) + sizeof(pint_t
);
1613 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
1614 count
= read_uleb128(p
, end
);
1615 skip
= read_uleb128(p
, end
);
1616 for (uint32_t i
=0; i
< count
; ++i
) {
1617 if ( (segStartAddr
+segOffset
) == targetAddr
)
1619 segOffset
+= skip
+ sizeof(pint_t
);
1623 throwf("bad bind opcode %d", *p
);
1631 static void check(const char* path
, const char* verifierDstRoot
)
1633 struct stat stat_buf
;
1636 int fd
= ::open(path
, O_RDONLY
, 0);
1638 throw "cannot open file";
1639 if ( ::fstat(fd
, &stat_buf
) != 0 )
1640 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1641 uint32_t length
= stat_buf
.st_size
;
1642 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1643 if ( p
== ((uint8_t*)(-1)) )
1644 throw "cannot map file";
1646 const mach_header
* mh
= (mach_header
*)p
;
1647 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1648 const struct fat_header
* fh
= (struct fat_header
*)p
;
1649 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1650 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1651 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1652 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1653 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1657 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1658 MachOChecker
<x86
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1660 throw "in universal file, i386 slice does not contain i386 mach-o";
1662 case CPU_TYPE_X86_64
:
1663 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1664 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1666 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1668 #if SUPPORT_ARCH_arm_any
1670 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1671 MachOChecker
<arm
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1673 throw "in universal file, arm slice does not contain arm mach-o";
1676 #if SUPPORT_ARCH_arm64
1677 case CPU_TYPE_ARM64
:
1678 if ( MachOChecker
<arm64
>::validFile(p
+ offset
) )
1679 MachOChecker
<arm64
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1681 throw "in universal file, arm64 slice does not contain arm mach-o";
1685 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1689 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1690 MachOChecker
<x86
>::make(p
, length
, path
, verifierDstRoot
);
1692 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1693 MachOChecker
<x86_64
>::make(p
, length
, path
, verifierDstRoot
);
1695 #if SUPPORT_ARCH_arm_any
1696 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1697 MachOChecker
<arm
>::make(p
, length
, path
, verifierDstRoot
);
1700 #if SUPPORT_ARCH_arm64
1701 else if ( MachOChecker
<arm64
>::validFile(p
) ) {
1702 MachOChecker
<arm64
>::make(p
, length
, path
, verifierDstRoot
);
1706 throw "not a known file type";
1709 catch (const char* msg
) {
1710 throwf("%s in %s", msg
, path
);
1715 int main(int argc
, const char* argv
[])
1717 bool progress
= false;
1718 const char* verifierDstRoot
= NULL
;
1720 for(int i
=1; i
< argc
; ++i
) {
1721 const char* arg
= argv
[i
];
1722 if ( arg
[0] == '-' ) {
1723 if ( strcmp(arg
, "-progress") == 0 ) {
1726 else if ( strcmp(arg
, "-verifier_dstroot") == 0 ) {
1727 verifierDstRoot
= argv
[++i
];
1729 else if ( strcmp(arg
, "-verifier_error_list") == 0 ) {
1730 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");
1731 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");
1732 printf("os_dylib_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)\n");
1733 printf("os_dylib_flat_namespace\tOS dylibs should not be built with -flat_namespace\n");
1734 printf("os_dylib_undefined_dynamic_lookup\tOS dylibs should not be built with -undefined dynamic_lookup\n");
1735 printf("os_dylib_malformed\the mach-o is malformed\n");
1739 throwf("unknown option: %s\n", arg
);
1743 bool success
= true;
1745 check(arg
, verifierDstRoot
);
1747 catch (const char* msg
) {
1748 if ( verifierDstRoot
) {
1749 printf("os_dylib_malformed\twarn\t%s\n", msg
);
1752 fprintf(stderr
, "machocheck failed: %s\n", msg
);
1757 if ( success
&& progress
)
1758 printf("ok: %s\n", arg
);