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
| 0xF8000000;
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
:
466 case LC_BUILD_VERSION
:
472 dylibID
= (macho_dylib_command
<P
>*)cmd
;
473 if ( dylibID
->name_offset() > size
)
474 throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID
->name_offset(), size
);
475 if ( (dylibID
->name_offset() + strlen(dylibID
->name()) + 1) > size
)
476 throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command");
477 fInstallName
= dylibID
->name();
480 case LC_DYLD_INFO_ONLY
:
481 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
483 case LC_ENCRYPTION_INFO
:
484 case LC_ENCRYPTION_INFO_64
:
485 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
487 case LC_SUB_UMBRELLA
:
489 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
490 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA";
493 if ( fHeader
->filetype() != MH_EXECUTE
)
494 throw "LC_MAIN can only be used in MH_EXECUTE file types";
495 entryPoint
= (macho_entry_point_command
<P
>*)cmd
;
498 if ( fHeader
->filetype() != MH_EXECUTE
)
499 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
500 threadInfo
= (macho_thread_command
<P
>*)cmd
;
503 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
505 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
510 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
511 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
512 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
513 const macho_segment_command
<P
>* stackSegment
= NULL
;
514 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
515 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
516 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
517 fSegments
.push_back(segCmd
);
518 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
519 throw "invalid segment load command size";
521 // see if this overlaps another segment address range
522 uint64_t startAddr
= segCmd
->vmaddr();
523 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
524 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
525 if ( it
->first
< startAddr
) {
526 if ( it
->second
> startAddr
)
527 throw "overlapping segment vm addresses";
529 else if ( it
->first
> startAddr
) {
530 if ( it
->first
< endAddr
)
531 throw "overlapping segment vm addresses";
534 throw "overlapping segment vm addresses";
536 segmentAddressRanges
.push_back(std::make_pair(startAddr
, endAddr
));
538 // see if this overlaps another segment file offset range
539 uint64_t startOffset
= segCmd
->fileoff();
540 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
541 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
542 if ( it
->first
< startOffset
) {
543 if ( it
->second
> startOffset
)
544 throw "overlapping segment file data";
546 else if ( it
->first
> startOffset
) {
547 if ( it
->first
< endOffset
)
548 throw "overlapping segment file data";
551 throw "overlapping segment file data";
553 segmentFileOffsetRanges
.push_back(std::make_pair(startOffset
, endOffset
));
554 // check is within file bounds
555 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
556 throw "segment file data is past end of file";
558 // verify it fits in file
559 if ( startOffset
> fLength
)
560 throw "segment fileoff does not fit in file";
561 if ( endOffset
> fLength
)
562 throw "segment fileoff+filesize does not fit in file";
564 // record special segments
565 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
566 linkEditSegment
= segCmd
;
567 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
568 stackSegment
= segCmd
;
570 // cache interesting segments
571 if ( fFirstSegment
== NULL
)
572 fFirstSegment
= segCmd
;
573 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
574 fTEXTSegment
= segCmd
;
575 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
576 if ( fFirstWritableSegment
== NULL
)
577 fFirstWritableSegment
= segCmd
;
578 if ( segCmd
->vmaddr() > 0x100000000ULL
)
579 fWriteableSegmentWithAddrOver4G
= true;
582 // check section ranges
583 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
584 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
585 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
586 // check all non-zero sized sections are within segment
587 if ( sect
->addr() < startAddr
)
588 throwf("section %s vm address not within segment", sect
->sectname());
589 if ( (sect
->addr()+sect
->size()) > endAddr
)
590 throwf("section %s vm address not within segment", sect
->sectname());
591 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
592 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
593 && (segCmd
->filesize() != 0)
594 && (sect
->size() != 0) ) {
595 if ( sect
->offset() < startOffset
)
596 throwf("section %s file offset not within segment", sect
->sectname());
597 if ( (sect
->offset()+sect
->size()) > endOffset
)
598 throwf("section %s file offset not within segment", sect
->sectname());
600 checkSection(segCmd
, sect
);
604 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
607 // verify there was a LINKEDIT segment
608 if ( linkEditSegment
== NULL
)
609 throw "no __LINKEDIT segment";
611 // verify there was an executable __TEXT segment and load commands are in it
612 if ( fTEXTSegment
== NULL
)
613 throw "no __TEXT segment";
614 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
615 throw "__TEXT segment does not have r-x init permissions";
616 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
617 // throw "__TEXT segment does not have rwx max permissions";
618 if ( fTEXTSegment
->fileoff() != 0 )
619 throw "__TEXT segment does not start at mach_header";
620 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
621 throw "__TEXT segment smaller than load commands";
623 // verify if custom stack used, that stack is in __UNIXSTACK segment
624 if ( threadInfo
!= NULL
) {
625 pint_t initialSP
= getInitialStackPointer(threadInfo
);
626 if ( initialSP
!= 0 ) {
627 if ( stackSegment
== NULL
)
628 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
629 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
630 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
634 // verify __UNIXSTACK is zero fill
635 if ( stackSegment
!= NULL
) {
636 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
637 throw "__UNIXSTACK is not a zero-fill segment";
638 if ( stackSegment
->vmsize() < 4096 )
639 throw "__UNIXSTACK segment is too small";
642 // verify entry point is in __TEXT segment
643 if ( threadInfo
!= NULL
) {
644 pint_t initialPC
= getEntryPoint(threadInfo
);
645 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
646 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
648 else if ( entryPoint
!= NULL
) {
649 pint_t initialOffset
= entryPoint
->entryoff();
650 if ( (initialOffset
< fTEXTSegment
->fileoff()) || (initialOffset
>= (fTEXTSegment
->fileoff()+fTEXTSegment
->filesize())) )
651 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset
);
654 // checks for executables
655 bool isStaticExecutable
= false;
656 if ( fHeader
->filetype() == MH_EXECUTE
) {
657 isStaticExecutable
= true;
659 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
660 switch ( cmd
->cmd() ) {
661 case LC_LOAD_DYLINKER
:
662 // the existence of a dyld load command makes a executable dynamic
663 isStaticExecutable
= false;
666 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
668 if ( isStaticExecutable
) {
669 if ( (fHeader
->flags() != MH_NOUNDEFS
) && (fHeader
->flags() != (MH_NOUNDEFS
|MH_PIE
)) )
670 throw "invalid bits in mach_header flags for static executable";
674 // verify encryption info
675 if ( encryption_info
!= NULL
) {
676 switch ( fHeader
->filetype() ) {
677 case MH_EXECUTE
: case MH_DYLIB
: case MH_BUNDLE
:
680 throw "LC_ENCRYPTION_INFO load command is not allowed in this file type";
682 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
683 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
684 if ( (encryption_info
->cryptoff() % 4096) != 0 )
685 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
686 if ( (encryption_info
->cryptsize() % 4096) != 0 )
687 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
688 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
689 it
!= segmentFileOffsetRanges
.end(); ++it
) {
690 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
691 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
692 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
697 // verify dylib has LC_ID_DYLIB
698 if ( fHeader
->filetype() == MH_DYLIB
) {
699 if ( fInstallName
== NULL
)
700 throw "MH_DYLIB missing LC_ID_DYLIB";
703 if ( fInstallName
!= NULL
)
704 throw "LC_ID_DYLIB found but file type is not MH_DYLIB";
707 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
709 bool foundDynamicSymTab
= false;
710 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
711 switch ( cmd
->cmd() ) {
714 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
715 fSymbolCount
= symtab
->nsyms();
716 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
717 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
718 throw "symbol table not in __LINKEDIT";
719 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > symtab
->stroff() )
720 throw "symbol table overlaps string pool";
721 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
722 throw "symbol table start not pointer aligned";
723 fStrings
= (char*)fHeader
+ symtab
->stroff();
724 fStringsEnd
= fStrings
+ symtab
->strsize();
725 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
726 throw "string pool not in __LINKEDIT";
727 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
728 throw "string pool extends beyond __LINKEDIT";
729 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
730 throw "string pool start not pointer aligned";
735 if ( isStaticExecutable
&&! fSlidableImage
)
736 throw "LC_DYSYMTAB should not be used in static executable";
737 foundDynamicSymTab
= true;
738 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
739 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
740 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
741 if ( fIndirectTableCount
!= 0 ) {
742 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
743 throw "indirect symbol table not in __LINKEDIT";
744 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*4) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
745 throw "indirect symbol table not in __LINKEDIT";
746 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
747 throw "indirect symbol table not pointer aligned";
749 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
750 if ( fLocalRelocationsCount
!= 0 ) {
751 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
752 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
753 throw "local relocations not in __LINKEDIT";
754 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
755 throw "local relocations not in __LINKEDIT";
756 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
757 throw "local relocations table not pointer aligned";
759 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
760 if ( fExternalRelocationsCount
!= 0 ) {
761 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
762 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
763 throw "external relocations not in __LINKEDIT";
764 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
765 throw "external relocations not in __LINKEDIT";
766 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
767 throw "external relocations table not pointer aligned";
771 case LC_SEGMENT_SPLIT_INFO
:
773 if ( isStaticExecutable
)
774 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
775 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
776 if ( info
->dataoff() < linkEditSegment
->fileoff() )
777 throw "split seg info not in __LINKEDIT";
778 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
779 throw "split seg info not in __LINKEDIT";
780 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
781 throw "split seg info table not pointer aligned";
782 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
783 throw "split seg info size not a multiple of pointer size";
786 case LC_FUNCTION_STARTS
:
788 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
789 if ( info
->dataoff() < linkEditSegment
->fileoff() )
790 throw "function starts data not in __LINKEDIT";
791 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
792 throw "function starts data not in __LINKEDIT";
793 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
794 throw "function starts data table not pointer aligned";
795 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
796 throw "function starts data size not a multiple of pointer size";
799 case LC_DATA_IN_CODE
:
801 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
802 if ( info
->dataoff() < linkEditSegment
->fileoff() )
803 throw "data-in-code data not in __LINKEDIT";
804 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
805 throw "data-in-code data not in __LINKEDIT";
806 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
807 throw "data-in-code data table not pointer aligned";
808 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
809 throw "data-in-code data size not a multiple of pointer size";
812 case LC_DYLIB_CODE_SIGN_DRS
:
814 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
815 if ( info
->dataoff() < linkEditSegment
->fileoff() )
816 throw "dependent dylib DR data not in __LINKEDIT";
817 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
818 throw "dependent dylib DR data not in __LINKEDIT";
819 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
820 throw "dependent dylib DR data table not pointer aligned";
821 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
822 throw "dependent dylib DR data size not a multiple of pointer size";
826 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
828 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
829 throw "missing dynamic symbol table";
830 if ( fStrings
== NULL
)
831 throw "missing symbol table";
835 template <typename A
>
836 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
838 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
839 if ( sectionType
== S_ZEROFILL
) {
840 if ( sect
->offset() != 0 )
841 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
844 // check section's segment name matches segment
845 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
846 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
848 // more section tests here
852 template <typename A
>
853 void MachOChecker
<A
>::verify()
855 bool sharedCacheCandidate
= false;
856 if ( fInstallName
!= NULL
) {
857 if ( (strncmp(fInstallName
, "/usr/lib/", 9) == 0) || (strncmp(fInstallName
, "/System/Library/", 16) == 0) ) {
858 sharedCacheCandidate
= true;
863 verifyNoFlatLookups();
867 template <typename A
>
868 void MachOChecker
<A
>::verifyInstallName()
870 // Don't allow @rpath to be used as -install_name for OS dylibs
871 if ( strncmp(fInstallName
, "@rpath/", 7) == 0 ) {
872 printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName());
875 // Verify -install_name match actual path of dylib
876 const char* installPathWithinDstRoot
= &fPath
[strlen(fDstRoot
)];
877 if ( strcmp(installPathWithinDstRoot
, fInstallName
) != 0 ) {
878 // see if install name is a symlink to actual file
879 bool symlinkToDylib
= false;
880 char absDstPath
[PATH_MAX
];
881 if ( realpath(fDstRoot
, absDstPath
) != NULL
) {
882 char fullInstallNamePath
[PATH_MAX
];
883 strlcpy(fullInstallNamePath
, absDstPath
, PATH_MAX
);
884 strlcat(fullInstallNamePath
, fInstallName
, PATH_MAX
);
885 char absInstallNamePath
[PATH_MAX
];
886 if ( realpath(fullInstallNamePath
, absInstallNamePath
) != NULL
) {
887 char absFPath
[PATH_MAX
];
888 if ( realpath(fPath
, absFPath
) != NULL
) {
889 if ( strcmp(absInstallNamePath
, absFPath
) == 0 )
890 symlinkToDylib
= true;
894 if ( !symlinkToDylib
)
895 printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName());
901 template <typename A
>
902 void MachOChecker
<A
>::verifyNoRpaths()
904 // Don't allow OS dylibs to add rpaths
905 if ( fHasLC_RPATH
) {
906 printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName());
911 template <typename A
>
912 void MachOChecker
<A
>::verifyNoFlatLookups()
914 if ( (fHeader
->flags() & MH_TWOLEVEL
) == 0 ) {
915 printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName());
919 if ( fDynamicSymbolTable
!= NULL
) {
920 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
921 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
922 for(const macho_nlist
<P
>* sym
= undefinesStart
; sym
< undefinesEnd
; ++sym
) {
923 //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]);
924 if ( GET_LIBRARY_ORDINAL(sym
->n_desc()) == DYNAMIC_LOOKUP_ORDINAL
) {
925 const char* symName
= &fStrings
[sym
->n_strx()];
926 printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName
, archName());
932 template <typename A
>
933 void MachOChecker
<A
>::checkIndirectSymbolTable()
935 // static executables don't have indirect symbol table
936 if ( fDynamicSymbolTable
== NULL
)
938 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
939 const uint32_t cmd_count
= fHeader
->ncmds();
940 const macho_load_command
<P
>* cmd
= cmds
;
941 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
942 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
943 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
944 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
945 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
946 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
947 // make sure all magic sections that use indirect symbol table fit within it
949 uint32_t elementSize
= 0;
950 switch ( sect
->flags() & SECTION_TYPE
) {
952 elementSize
= sect
->reserved2();
953 start
= sect
->reserved1();
955 case S_LAZY_SYMBOL_POINTERS
:
956 case S_NON_LAZY_SYMBOL_POINTERS
:
957 elementSize
= sizeof(pint_t
);
958 start
= sect
->reserved1();
961 if ( elementSize
!= 0 ) {
962 uint32_t count
= sect
->size() / elementSize
;
963 if ( (count
*elementSize
) != sect
->size() )
964 throwf("%s section size is not an even multiple of element size", sect
->sectname());
965 if ( (start
+count
) > fIndirectTableCount
)
966 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
970 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
974 if ( fDynamicSymbolTable
->ilocalsym() != 0 )
975 throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable
->ilocalsym());
977 if ( fDynamicSymbolTable
->ilocalsym() > fSymbolCount
)
978 throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->ilocalsym(), fSymbolCount
);
979 if ( fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() > fSymbolCount
) {
980 throwf("local symbols out of range (%d+%d > %d) in indirect symbol table",
981 fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym(), fSymbolCount
);
984 if ( fDynamicSymbolTable
->iextdefsym() > fSymbolCount
)
985 throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iextdefsym(), fSymbolCount
);
986 if ( fDynamicSymbolTable
->iextdefsym() != fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() ) {
987 throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table",
988 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym() );
990 if ( fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() > fSymbolCount
) {
991 throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table",
992 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym(), fSymbolCount
);
995 if ( fDynamicSymbolTable
->iundefsym() > fSymbolCount
)
996 throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iundefsym(), fSymbolCount
);
997 if ( fDynamicSymbolTable
->iundefsym() != fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() ) {
998 throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table",
999 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym());
1001 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() > fSymbolCount
) {
1002 throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table",
1003 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1006 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() != fSymbolCount
) {
1007 throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table",
1008 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1015 template <typename A
>
1016 void MachOChecker
<A
>::checkSymbolTable()
1018 // verify no duplicate external symbol names
1019 if ( fDynamicSymbolTable
!= NULL
) {
1020 StringSet externalNames
;
1021 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
1022 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
1023 int i
= fDynamicSymbolTable
->iextdefsym();
1024 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
1025 const char* symName
= &fStrings
[p
->n_strx()];
1026 if ( symName
> fStringsEnd
)
1027 throw "string index out of range";
1028 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
1029 if ( externalNames
.find(symName
) != externalNames
.end() )
1030 throwf("duplicate external symbol: %s", symName
);
1031 if ( (p
->n_type() & N_EXT
) == 0 )
1032 throwf("non-external symbol in external symbol range: %s", symName
);
1033 // don't add N_INDR to externalNames because there is likely an undefine with same name
1034 if ( (p
->n_type() & N_INDR
) == 0 )
1035 externalNames
.insert(symName
);
1037 // verify no undefines with same name as an external symbol
1038 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
1039 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
1040 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
1041 const char* symName
= &fStrings
[p
->n_strx()];
1042 if ( symName
> fStringsEnd
)
1043 throw "string index out of range";
1044 if ( externalNames
.find(symName
) != externalNames
.end() )
1045 throwf("undefine with same name as external symbol: %s", symName
);
1047 // verify all N_SECT values are valid
1048 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
1049 uint8_t type
= p
->n_type();
1050 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
1051 if ( p
->n_sect() > fSectionCount
) {
1052 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
1060 template <typename A
>
1061 void MachOChecker
<A
>::checkInitTerms()
1063 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1064 const uint32_t cmd_count
= fHeader
->ncmds();
1065 const macho_load_command
<P
>* cmd
= cmds
;
1066 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1067 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1068 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1069 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1070 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1071 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1072 // make sure all magic sections that use indirect symbol table fit within it
1076 const char* kind
= "initializer";
1077 switch ( sect
->flags() & SECTION_TYPE
) {
1078 case S_MOD_TERM_FUNC_POINTERS
:
1079 kind
= "terminator";
1081 case S_MOD_INIT_FUNC_POINTERS
:
1082 count
= sect
->size() / sizeof(pint_t
);
1083 if ( (count
*sizeof(pint_t
)) != sect
->size() )
1084 throwf("%s section size is not an even multiple of element size", sect
->sectname());
1085 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
1086 throwf("%s section size is not pointer size aligned", sect
->sectname());
1087 // check each pointer in array points within TEXT
1088 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
1089 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
1090 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
1091 pint_t pointer
= P::getP(*p
);
1092 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
1093 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
1095 // check each pointer in array will be rebased and not bound
1096 if ( fSlidableImage
) {
1097 pint_t sectionBeginAddr
= sect
->addr();
1098 pint_t sectionEndddr
= sect
->addr() + sect
->size();
1099 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
1100 if ( addressIsBindingSite(addr
) )
1101 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
1102 if ( ! addressIsRebaseSite(addr
) )
1103 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
1110 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1118 x86::P::uint_t MachOChecker
<x86
>::relocBase()
1120 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1121 return fFirstWritableSegment
->vmaddr();
1123 return fFirstSegment
->vmaddr();
1127 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
1129 // check for split-seg
1130 return fFirstWritableSegment
->vmaddr();
1134 arm::P::uint_t MachOChecker
<arm
>::relocBase()
1136 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1137 return fFirstWritableSegment
->vmaddr();
1139 return fFirstSegment
->vmaddr();
1143 arm64::P::uint_t MachOChecker
<arm64
>::relocBase()
1145 return fFirstWritableSegment
->vmaddr();
1149 template <typename A
>
1150 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
1152 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1153 const uint32_t cmd_count
= fHeader
->ncmds();
1154 const macho_load_command
<P
>* cmd
= cmds
;
1155 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1156 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1157 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1158 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
1159 // if segment is writable, we are fine
1160 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1162 // could be a text reloc, make sure section bit is set
1163 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1164 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1165 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1166 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
1167 // found section for this address, if has relocs we are fine
1168 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
1173 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1181 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1183 if ( reloc
->r_length() != 2 )
1184 throw "bad external relocation length";
1185 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1186 throw "unknown external relocation type";
1187 if ( reloc
->r_pcrel() != 0 )
1188 throw "bad external relocation pc_rel";
1189 if ( reloc
->r_extern() == 0 )
1190 throw "local relocation found with external relocations";
1191 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1192 throw "external relocation address not in writable segment";
1193 // FIX: check r_symbol
1198 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1200 if ( reloc
->r_length() != 3 )
1201 throw "bad external relocation length";
1202 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1203 throw "unknown external relocation type";
1204 if ( reloc
->r_pcrel() != 0 )
1205 throw "bad external relocation pc_rel";
1206 if ( reloc
->r_extern() == 0 )
1207 throw "local relocation found with external relocations";
1208 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1209 throw "exernal relocation address not in writable segment";
1210 // FIX: check r_symbol
1213 #if SUPPORT_ARCH_arm_any
1215 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1217 if ( reloc
->r_length() != 2 )
1218 throw "bad external relocation length";
1219 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1220 throw "unknown external relocation type";
1221 if ( reloc
->r_pcrel() != 0 )
1222 throw "bad external relocation pc_rel";
1223 if ( reloc
->r_extern() == 0 )
1224 throw "local relocation found with external relocations";
1225 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1226 throw "external relocation address not in writable segment";
1227 // FIX: check r_symbol
1231 #if SUPPORT_ARCH_arm64
1233 void MachOChecker
<arm64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1235 throw "external relocations not used for arm64";
1241 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1247 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1249 if ( reloc
->r_length() != 3 )
1250 throw "bad local relocation length";
1251 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1252 throw "unknown local relocation type";
1253 if ( reloc
->r_pcrel() != 0 )
1254 throw "bad local relocation pc_rel";
1255 if ( reloc
->r_extern() != 0 )
1256 throw "external relocation found with local relocations";
1257 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1258 throw "local relocation address not in writable segment";
1261 #if SUPPORT_ARCH_arm_any
1263 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1265 if ( reloc
->r_address() & R_SCATTERED
) {
1267 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1268 if ( sreloc
->r_length() != 2 )
1269 throw "bad local scattered relocation length";
1270 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1271 throw "bad local scattered relocation type";
1274 if ( reloc
->r_length() != 2 )
1275 throw "bad local relocation length";
1276 if ( reloc
->r_extern() != 0 )
1277 throw "external relocation found with local relocations";
1278 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1279 throw "local relocation address not in writable segment";
1284 #if SUPPORT_ARCH_arm64
1286 void MachOChecker
<arm64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1288 throw "local relocations not used for arm64";
1293 template <typename A
>
1294 void MachOChecker
<A
>::checkRelocations()
1296 // external relocations should be sorted to minimize dyld symbol lookups
1297 // therefore every reloc with the same r_symbolnum value should be contiguous
1298 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1299 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1300 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1301 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1302 this->checkExternalReloation(reloc
);
1303 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1304 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1305 throw "external relocations not sorted";
1306 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1307 lastSymbolIndex
= reloc
->r_symbolnum();
1311 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1312 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1313 this->checkLocalReloation(reloc
);
1316 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1317 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1318 const uint32_t cmd_count
= fHeader
->ncmds();
1319 const macho_load_command
<P
>* cmd
= cmds
;
1320 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1321 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1322 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1323 // if segment is writable, we are fine
1324 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1326 // look at sections that have text reloc bit set
1327 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1328 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1329 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1330 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1331 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1332 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1337 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1341 template <typename A
>
1342 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1344 if ( segIndex
> fSegments
.size() )
1345 throw "segment index out of range";
1346 return fSegments
[segIndex
]->vmaddr();
1349 template <typename A
>
1350 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1352 // look at local relocs
1353 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1354 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1355 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1356 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1360 if ( fDyldInfo
!= NULL
) {
1361 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1362 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1365 uint64_t segOffset
= 0;
1369 pint_t segStartAddr
= 0;
1372 while ( !done
&& (p
< end
) ) {
1373 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1374 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1377 case REBASE_OPCODE_DONE
:
1380 case REBASE_OPCODE_SET_TYPE_IMM
:
1383 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1384 segIndex
= immediate
;
1385 segStartAddr
= segStartAddress(segIndex
);
1386 segOffset
= read_uleb128(p
, end
);
1388 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1389 segOffset
+= read_uleb128(p
, end
);
1391 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1392 segOffset
+= immediate
*sizeof(pint_t
);
1394 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1395 for (int i
=0; i
< immediate
; ++i
) {
1396 addr
= segStartAddr
+segOffset
;
1397 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1399 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1400 segOffset
+= sizeof(pint_t
);
1403 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1404 count
= read_uleb128(p
, end
);
1405 for (uint32_t i
=0; i
< count
; ++i
) {
1406 addr
= segStartAddr
+segOffset
;
1407 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1409 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1410 segOffset
+= sizeof(pint_t
);
1413 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1414 addr
= segStartAddr
+segOffset
;
1415 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1417 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1418 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1420 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1421 count
= read_uleb128(p
, end
);
1422 skip
= read_uleb128(p
, end
);
1423 for (uint32_t i
=0; i
< count
; ++i
) {
1424 addr
= segStartAddr
+segOffset
;
1425 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1427 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1428 segOffset
+= skip
+ sizeof(pint_t
);
1432 throwf("bad rebase opcode %d", *p
);
1439 template <typename A
>
1440 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
1442 // look at local relocs
1443 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1444 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1445 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1446 if ( relocAddress
== targetAddr
)
1450 if ( fDyldInfo
!= NULL
) {
1451 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1452 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1455 uint64_t segOffset
= 0;
1459 pint_t segStartAddr
= 0;
1462 while ( !done
&& (p
< end
) ) {
1463 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1464 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1467 case REBASE_OPCODE_DONE
:
1470 case REBASE_OPCODE_SET_TYPE_IMM
:
1473 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1474 segIndex
= immediate
;
1475 segStartAddr
= segStartAddress(segIndex
);
1476 segOffset
= read_uleb128(p
, end
);
1478 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1479 segOffset
+= read_uleb128(p
, end
);
1481 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1482 segOffset
+= immediate
*sizeof(pint_t
);
1484 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1485 for (int i
=0; i
< immediate
; ++i
) {
1486 addr
= segStartAddr
+segOffset
;
1487 if ( addr
== targetAddr
)
1489 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1490 segOffset
+= sizeof(pint_t
);
1493 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1494 count
= read_uleb128(p
, end
);
1495 for (uint32_t i
=0; i
< count
; ++i
) {
1496 addr
= segStartAddr
+segOffset
;
1497 if ( addr
== targetAddr
)
1499 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1500 segOffset
+= sizeof(pint_t
);
1503 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1504 addr
= segStartAddr
+segOffset
;
1505 if ( addr
== targetAddr
)
1507 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1508 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1510 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1511 count
= read_uleb128(p
, end
);
1512 skip
= read_uleb128(p
, end
);
1513 for (uint32_t i
=0; i
< count
; ++i
) {
1514 addr
= segStartAddr
+segOffset
;
1515 if ( addr
== targetAddr
)
1517 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1518 segOffset
+= skip
+ sizeof(pint_t
);
1522 throwf("bad rebase opcode %d", *p
);
1530 template <typename A
>
1531 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1533 // look at external relocs
1534 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1535 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1536 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1537 if ( relocAddress
== targetAddr
)
1541 if ( fDyldInfo
!= NULL
) {
1542 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1543 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1546 uint64_t segOffset
= 0;
1549 const char* symbolName
= NULL
;
1550 int libraryOrdinal
= 0;
1553 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
);