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()) {
261 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
262 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
263 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
264 template <> uint8_t MachOChecker
<arm64
>::loadCommandSizeMask() { return 0x07; }
269 x86::P::uint_t MachOChecker
<x86
>::getInitialStackPointer(const macho_thread_command
<x86::P
>* threadInfo
)
271 return threadInfo
->thread_register(7);
275 x86_64::P::uint_t MachOChecker
<x86_64
>::getInitialStackPointer(const macho_thread_command
<x86_64::P
>* threadInfo
)
277 return threadInfo
->thread_register(7);
281 arm::P::uint_t MachOChecker
<arm
>::getInitialStackPointer(const macho_thread_command
<arm::P
>* threadInfo
)
283 return threadInfo
->thread_register(13);
287 arm64::P::uint_t MachOChecker
<arm64
>::getInitialStackPointer(const macho_thread_command
<arm64::P
>* threadInfo
)
289 throw "LC_UNIXTHREAD not supported for arm64";
294 x86::P::uint_t MachOChecker
<x86
>::getEntryPoint(const macho_thread_command
<x86::P
>* threadInfo
)
296 return threadInfo
->thread_register(10);
300 x86_64::P::uint_t MachOChecker
<x86_64
>::getEntryPoint(const macho_thread_command
<x86_64::P
>* threadInfo
)
302 return threadInfo
->thread_register(16);
306 arm::P::uint_t MachOChecker
<arm
>::getEntryPoint(const macho_thread_command
<arm::P
>* threadInfo
)
308 return threadInfo
->thread_register(15);
312 arm64::P::uint_t MachOChecker
<arm64
>::getEntryPoint(const macho_thread_command
<arm64::P
>* threadInfo
)
314 throw "LC_UNIXTHREAD not supported for arm64";
318 template <typename A
>
319 const char* MachOChecker
<A
>::archName()
321 switch ( fHeader
->cputype() ) {
324 case CPU_TYPE_X86_64
:
325 if ( fHeader
->cpusubtype() == CPU_SUBTYPE_X86_64_H
)
330 switch ( fHeader
->cpusubtype() ) {
331 case CPU_SUBTYPE_ARM_V7
:
333 case CPU_SUBTYPE_ARM_V7S
:
335 case CPU_SUBTYPE_ARM_V7K
:
347 template <typename A
>
348 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
, const char* verifierDstRoot
)
349 : fHeader(NULL
), fLength(fileLength
), fInstallName(NULL
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
350 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
351 fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fHasLC_RPATH(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
),
352 fTEXTSegment(NULL
), fDyldInfo(NULL
), fSectionCount(0)
355 if ( ! validFile(fileContent
) )
356 throw "not a mach-o file that can be checked";
358 fPath
= strdup(path
);
359 fDstRoot
= verifierDstRoot
? strdup(verifierDstRoot
) : NULL
;
360 fHeader
= (const macho_header
<P
>*)fileContent
;
362 // sanity check header
365 // check load commands
368 checkIndirectSymbolTable();
376 if ( verifierDstRoot
!= NULL
)
381 template <typename A
>
382 void MachOChecker
<A
>::checkMachHeader()
384 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
385 throw "sizeofcmds in mach_header is larger than file";
387 uint32_t flags
= fHeader
->flags();
388 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFC000000;
389 if ( flags
& invalidBits
)
390 throw "invalid bits in mach_header flags";
391 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
392 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
394 switch ( fHeader
->filetype() ) {
396 fSlidableImage
= ( flags
& MH_PIE
);
400 fSlidableImage
= true;
403 throw "not a mach-o file type supported by this tool";
407 template <typename A
>
408 void MachOChecker
<A
>::checkLoadCommands()
410 // check that all load commands fit within the load command space file
411 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
412 const macho_thread_command
<P
>* threadInfo
= NULL
;
413 const macho_entry_point_command
<P
>* entryPoint
= NULL
;
414 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
415 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
416 const uint32_t cmd_count
= fHeader
->ncmds();
417 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
418 const macho_load_command
<P
>* cmd
= cmds
;
419 const macho_dylib_command
<P
>* dylibID
;
420 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
421 uint32_t size
= cmd
->cmdsize();
422 if ( (size
& this->loadCommandSizeMask()) != 0 )
423 throwf("load command #%d has a unaligned size", i
);
424 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
425 if ( endOfCmd
> endOfLoadCommands
)
426 throwf("load command #%d extends beyond the end of the load commands", i
);
427 if ( endOfCmd
> endOfFile
)
428 throwf("load command #%d extends beyond the end of the file", i
);
429 switch ( cmd
->cmd() ) {
430 case macho_segment_command
<P
>::CMD
:
435 case LC_LOAD_DYLINKER
:
436 case macho_routines_command
<P
>::CMD
:
437 case LC_SUB_FRAMEWORK
:
439 case LC_TWOLEVEL_HINTS
:
440 case LC_PREBIND_CKSUM
:
441 case LC_LOAD_WEAK_DYLIB
:
442 case LC_LAZY_LOAD_DYLIB
:
444 case LC_REEXPORT_DYLIB
:
445 case LC_SEGMENT_SPLIT_INFO
:
446 case LC_CODE_SIGNATURE
:
447 case LC_LOAD_UPWARD_DYLIB
:
448 case LC_VERSION_MIN_MACOSX
:
449 case LC_VERSION_MIN_IPHONEOS
:
450 case LC_FUNCTION_STARTS
:
451 case LC_DYLD_ENVIRONMENT
:
452 case LC_DATA_IN_CODE
:
453 case LC_DYLIB_CODE_SIGN_DRS
:
454 case LC_SOURCE_VERSION
:
460 dylibID
= (macho_dylib_command
<P
>*)cmd
;
461 if ( dylibID
->name_offset() > size
)
462 throwf("malformed mach-o: LC_ID_DYLIB load command has offset (%u) outside its size (%u)", dylibID
->name_offset(), size
);
463 if ( (dylibID
->name_offset() + strlen(dylibID
->name()) + 1) > size
)
464 throwf("malformed mach-o: LC_ID_DYLIB load command string extends beyond end of load command");
465 fInstallName
= dylibID
->name();
468 case LC_DYLD_INFO_ONLY
:
469 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
471 case LC_ENCRYPTION_INFO
:
472 case LC_ENCRYPTION_INFO_64
:
473 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
475 case LC_SUB_UMBRELLA
:
477 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
478 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";
481 if ( fHeader
->filetype() != MH_EXECUTE
)
482 throw "LC_MAIN can only be used in MH_EXECUTE file types";
483 entryPoint
= (macho_entry_point_command
<P
>*)cmd
;
486 if ( fHeader
->filetype() != MH_EXECUTE
)
487 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
488 threadInfo
= (macho_thread_command
<P
>*)cmd
;
491 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
493 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
498 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
499 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
500 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
501 const macho_segment_command
<P
>* stackSegment
= NULL
;
502 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
503 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
504 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
505 fSegments
.push_back(segCmd
);
506 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
507 throw "invalid segment load command size";
509 // see if this overlaps another segment address range
510 uint64_t startAddr
= segCmd
->vmaddr();
511 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
512 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
513 if ( it
->first
< startAddr
) {
514 if ( it
->second
> startAddr
)
515 throw "overlapping segment vm addresses";
517 else if ( it
->first
> startAddr
) {
518 if ( it
->first
< endAddr
)
519 throw "overlapping segment vm addresses";
522 throw "overlapping segment vm addresses";
524 segmentAddressRanges
.push_back(std::make_pair(startAddr
, endAddr
));
526 // see if this overlaps another segment file offset range
527 uint64_t startOffset
= segCmd
->fileoff();
528 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
529 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
530 if ( it
->first
< startOffset
) {
531 if ( it
->second
> startOffset
)
532 throw "overlapping segment file data";
534 else if ( it
->first
> startOffset
) {
535 if ( it
->first
< endOffset
)
536 throw "overlapping segment file data";
539 throw "overlapping segment file data";
541 segmentFileOffsetRanges
.push_back(std::make_pair(startOffset
, endOffset
));
542 // check is within file bounds
543 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
544 throw "segment file data is past end of file";
546 // verify it fits in file
547 if ( startOffset
> fLength
)
548 throw "segment fileoff does not fit in file";
549 if ( endOffset
> fLength
)
550 throw "segment fileoff+filesize does not fit in file";
552 // record special segments
553 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
554 linkEditSegment
= segCmd
;
555 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
556 stackSegment
= segCmd
;
558 // cache interesting segments
559 if ( fFirstSegment
== NULL
)
560 fFirstSegment
= segCmd
;
561 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
562 fTEXTSegment
= segCmd
;
563 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
564 if ( fFirstWritableSegment
== NULL
)
565 fFirstWritableSegment
= segCmd
;
566 if ( segCmd
->vmaddr() > 0x100000000ULL
)
567 fWriteableSegmentWithAddrOver4G
= true;
570 // check section ranges
571 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
572 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
573 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
574 // check all non-zero sized sections are within segment
575 if ( sect
->addr() < startAddr
)
576 throwf("section %s vm address not within segment", sect
->sectname());
577 if ( (sect
->addr()+sect
->size()) > endAddr
)
578 throwf("section %s vm address not within segment", sect
->sectname());
579 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
580 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
581 && (segCmd
->filesize() != 0)
582 && (sect
->size() != 0) ) {
583 if ( sect
->offset() < startOffset
)
584 throwf("section %s file offset not within segment", sect
->sectname());
585 if ( (sect
->offset()+sect
->size()) > endOffset
)
586 throwf("section %s file offset not within segment", sect
->sectname());
588 checkSection(segCmd
, sect
);
592 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
595 // verify there was a LINKEDIT segment
596 if ( linkEditSegment
== NULL
)
597 throw "no __LINKEDIT segment";
599 // verify there was an executable __TEXT segment and load commands are in it
600 if ( fTEXTSegment
== NULL
)
601 throw "no __TEXT segment";
602 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
603 throw "__TEXT segment does not have r-x init permissions";
604 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
605 // throw "__TEXT segment does not have rwx max permissions";
606 if ( fTEXTSegment
->fileoff() != 0 )
607 throw "__TEXT segment does not start at mach_header";
608 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
609 throw "__TEXT segment smaller than load commands";
611 // verify if custom stack used, that stack is in __UNIXSTACK segment
612 if ( threadInfo
!= NULL
) {
613 pint_t initialSP
= getInitialStackPointer(threadInfo
);
614 if ( initialSP
!= 0 ) {
615 if ( stackSegment
== NULL
)
616 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
617 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
618 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
622 // verify __UNIXSTACK is zero fill
623 if ( stackSegment
!= NULL
) {
624 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
625 throw "__UNIXSTACK is not a zero-fill segment";
626 if ( stackSegment
->vmsize() < 4096 )
627 throw "__UNIXSTACK segment is too small";
630 // verify entry point is in __TEXT segment
631 if ( threadInfo
!= NULL
) {
632 pint_t initialPC
= getEntryPoint(threadInfo
);
633 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
634 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
636 else if ( entryPoint
!= NULL
) {
637 pint_t initialOffset
= entryPoint
->entryoff();
638 if ( (initialOffset
< fTEXTSegment
->fileoff()) || (initialOffset
>= (fTEXTSegment
->fileoff()+fTEXTSegment
->filesize())) )
639 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset
);
642 // checks for executables
643 bool isStaticExecutable
= false;
644 if ( fHeader
->filetype() == MH_EXECUTE
) {
645 isStaticExecutable
= true;
647 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
648 switch ( cmd
->cmd() ) {
649 case LC_LOAD_DYLINKER
:
650 // the existence of a dyld load command makes a executable dynamic
651 isStaticExecutable
= false;
654 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
656 if ( isStaticExecutable
) {
657 if ( (fHeader
->flags() != MH_NOUNDEFS
) && (fHeader
->flags() != (MH_NOUNDEFS
|MH_PIE
)) )
658 throw "invalid bits in mach_header flags for static executable";
662 // verify encryption info
663 if ( encryption_info
!= NULL
) {
664 if ( fHeader
->filetype() != MH_EXECUTE
)
665 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
666 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
667 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
668 if ( (encryption_info
->cryptoff() % 4096) != 0 )
669 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
670 if ( (encryption_info
->cryptsize() % 4096) != 0 )
671 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
672 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
673 it
!= segmentFileOffsetRanges
.end(); ++it
) {
674 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
675 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
676 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
681 // verify dylib has LC_ID_DYLIB
682 if ( fHeader
->filetype() == MH_DYLIB
) {
683 if ( fInstallName
== NULL
)
684 throw "MH_DYLIB missing LC_ID_DYLIB";
687 if ( fInstallName
!= NULL
)
688 throw "LC_ID_DYLIB found but file type is not MH_DYLIB";
691 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
693 bool foundDynamicSymTab
= false;
694 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
695 switch ( cmd
->cmd() ) {
698 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
699 fSymbolCount
= symtab
->nsyms();
700 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
701 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
702 throw "symbol table not in __LINKEDIT";
703 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > symtab
->stroff() )
704 throw "symbol table overlaps string pool";
705 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
706 throw "symbol table start not pointer aligned";
707 fStrings
= (char*)fHeader
+ symtab
->stroff();
708 fStringsEnd
= fStrings
+ symtab
->strsize();
709 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
710 throw "string pool not in __LINKEDIT";
711 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
712 throw "string pool extends beyond __LINKEDIT";
713 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
714 throw "string pool start not pointer aligned";
715 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
716 throw "string pool size not a multiple of pointer size";
721 if ( isStaticExecutable
&&! fSlidableImage
)
722 throw "LC_DYSYMTAB should not be used in static executable";
723 foundDynamicSymTab
= true;
724 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
725 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
726 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
727 if ( fIndirectTableCount
!= 0 ) {
728 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
729 throw "indirect symbol table not in __LINKEDIT";
730 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
731 throw "indirect symbol table not in __LINKEDIT";
732 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
733 throw "indirect symbol table not pointer aligned";
735 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
736 if ( fLocalRelocationsCount
!= 0 ) {
737 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
738 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
739 throw "local relocations not in __LINKEDIT";
740 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
741 throw "local relocations not in __LINKEDIT";
742 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
743 throw "local relocations table not pointer aligned";
745 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
746 if ( fExternalRelocationsCount
!= 0 ) {
747 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
748 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
749 throw "external relocations not in __LINKEDIT";
750 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
751 throw "external relocations not in __LINKEDIT";
752 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
753 throw "external relocations table not pointer aligned";
757 case LC_SEGMENT_SPLIT_INFO
:
759 if ( isStaticExecutable
)
760 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
761 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
762 if ( info
->dataoff() < linkEditSegment
->fileoff() )
763 throw "split seg info not in __LINKEDIT";
764 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
765 throw "split seg info not in __LINKEDIT";
766 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
767 throw "split seg info table not pointer aligned";
768 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
769 throw "split seg info size not a multiple of pointer size";
772 case LC_FUNCTION_STARTS
:
774 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
775 if ( info
->dataoff() < linkEditSegment
->fileoff() )
776 throw "function starts data not in __LINKEDIT";
777 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
778 throw "function starts data not in __LINKEDIT";
779 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
780 throw "function starts data table not pointer aligned";
781 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
782 throw "function starts data size not a multiple of pointer size";
785 case LC_DATA_IN_CODE
:
787 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
788 if ( info
->dataoff() < linkEditSegment
->fileoff() )
789 throw "data-in-code data not in __LINKEDIT";
790 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
791 throw "data-in-code data not in __LINKEDIT";
792 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
793 throw "data-in-code data table not pointer aligned";
794 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
795 throw "data-in-code data size not a multiple of pointer size";
798 case LC_DYLIB_CODE_SIGN_DRS
:
800 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
801 if ( info
->dataoff() < linkEditSegment
->fileoff() )
802 throw "dependent dylib DR data not in __LINKEDIT";
803 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
804 throw "dependent dylib DR data not in __LINKEDIT";
805 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
806 throw "dependent dylib DR data table not pointer aligned";
807 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
808 throw "dependent dylib DR data size not a multiple of pointer size";
812 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
814 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
815 throw "missing dynamic symbol table";
816 if ( fStrings
== NULL
)
817 throw "missing symbol table";
821 template <typename A
>
822 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
824 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
825 if ( sectionType
== S_ZEROFILL
) {
826 if ( sect
->offset() != 0 )
827 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
830 // check section's segment name matches segment
831 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
832 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
834 // more section tests here
838 template <typename A
>
839 void MachOChecker
<A
>::verify()
841 bool sharedCacheCandidate
= false;
842 if ( fInstallName
!= NULL
) {
843 if ( (strncmp(fInstallName
, "/usr/lib/", 9) == 0) || (strncmp(fInstallName
, "/System/Library/", 16) == 0) ) {
844 sharedCacheCandidate
= true;
849 verifyNoFlatLookups();
853 template <typename A
>
854 void MachOChecker
<A
>::verifyInstallName()
856 // Don't allow @rpath to be used as -install_name for OS dylibs
857 if ( strncmp(fInstallName
, "@rpath/", 7) == 0 ) {
858 printf("os_dylib_rpath_install_name\tfatal\t-install_name uses @rpath in arch %s\n", archName());
861 // Verify -install_name match actual path of dylib
862 const char* installPathWithinDstRoot
= &fPath
[strlen(fDstRoot
)];
863 if ( strcmp(installPathWithinDstRoot
, fInstallName
) != 0 ) {
864 // see if install name is a symlink to actual file
865 bool symlinkToDylib
= false;
866 char absDstPath
[PATH_MAX
];
867 if ( realpath(fDstRoot
, absDstPath
) != NULL
) {
868 char fullInstallNamePath
[PATH_MAX
];
869 strlcpy(fullInstallNamePath
, absDstPath
, PATH_MAX
);
870 strlcat(fullInstallNamePath
, fInstallName
, PATH_MAX
);
871 char absInstallNamePath
[PATH_MAX
];
872 if ( realpath(fullInstallNamePath
, absInstallNamePath
) != NULL
) {
873 char absFPath
[PATH_MAX
];
874 if ( realpath(fPath
, absFPath
) != NULL
) {
875 if ( strcmp(absInstallNamePath
, absFPath
) == 0 )
876 symlinkToDylib
= true;
880 if ( !symlinkToDylib
)
881 printf("os_dylib_bad_install_name\twarn\t-install_name does not match install location in arch %s\n", archName());
887 template <typename A
>
888 void MachOChecker
<A
>::verifyNoRpaths()
890 // Don't allow OS dylibs to add rpaths
891 if ( fHasLC_RPATH
) {
892 printf("os_dylib_rpath\twarn\tcontains LC_RPATH load command in arch %s\n", archName());
897 template <typename A
>
898 void MachOChecker
<A
>::verifyNoFlatLookups()
900 if ( (fHeader
->flags() & MH_TWOLEVEL
) == 0 ) {
901 printf("os_dylib_flat_namespace\twarn\tbuilt with -flat_namespace in arch %s\n", archName());
905 if ( fDynamicSymbolTable
!= NULL
) {
906 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
907 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
908 for(const macho_nlist
<P
>* sym
= undefinesStart
; sym
< undefinesEnd
; ++sym
) {
909 //printf("0x%04X %s\n", sym->n_desc(), &fStrings[sym->n_strx()]);
910 if ( GET_LIBRARY_ORDINAL(sym
->n_desc()) == DYNAMIC_LOOKUP_ORDINAL
) {
911 const char* symName
= &fStrings
[sym
->n_strx()];
912 printf("os_dylib_undefined_dynamic_lookup\twarn\tbuilt with -undefined dynamic_lookup for symbol %s in arch %s\n", symName
, archName());
918 template <typename A
>
919 void MachOChecker
<A
>::checkIndirectSymbolTable()
921 // static executables don't have indirect symbol table
922 if ( fDynamicSymbolTable
== NULL
)
924 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
925 const uint32_t cmd_count
= fHeader
->ncmds();
926 const macho_load_command
<P
>* cmd
= cmds
;
927 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
928 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
929 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
930 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
931 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
932 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
933 // make sure all magic sections that use indirect symbol table fit within it
935 uint32_t elementSize
= 0;
936 switch ( sect
->flags() & SECTION_TYPE
) {
938 elementSize
= sect
->reserved2();
939 start
= sect
->reserved1();
941 case S_LAZY_SYMBOL_POINTERS
:
942 case S_NON_LAZY_SYMBOL_POINTERS
:
943 elementSize
= sizeof(pint_t
);
944 start
= sect
->reserved1();
947 if ( elementSize
!= 0 ) {
948 uint32_t count
= sect
->size() / elementSize
;
949 if ( (count
*elementSize
) != sect
->size() )
950 throwf("%s section size is not an even multiple of element size", sect
->sectname());
951 if ( (start
+count
) > fIndirectTableCount
)
952 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
956 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
960 if ( fDynamicSymbolTable
->ilocalsym() != 0 )
961 throwf("start of local symbols (%d) not at start of symbol table", fDynamicSymbolTable
->ilocalsym());
963 if ( fDynamicSymbolTable
->ilocalsym() > fSymbolCount
)
964 throwf("start of local symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->ilocalsym(), fSymbolCount
);
965 if ( fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() > fSymbolCount
) {
966 throwf("local symbols out of range (%d+%d > %d) in indirect symbol table",
967 fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym(), fSymbolCount
);
970 if ( fDynamicSymbolTable
->iextdefsym() > fSymbolCount
)
971 throwf("start of extern symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iextdefsym(), fSymbolCount
);
972 if ( fDynamicSymbolTable
->iextdefsym() != fDynamicSymbolTable
->ilocalsym() + fDynamicSymbolTable
->nlocalsym() ) {
973 throwf("start of extern symbols (%d) not contiguous to local symbols (%d+%d) in indirect symbol table",
974 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->ilocalsym(), fDynamicSymbolTable
->nlocalsym() );
976 if ( fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() > fSymbolCount
) {
977 throwf("extern symbols out of range (%d+%d > %d) in indirect symbol table",
978 fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym(), fSymbolCount
);
981 if ( fDynamicSymbolTable
->iundefsym() > fSymbolCount
)
982 throwf("start of undefined symbols out of range (%d > %d) in indirect symbol table", fDynamicSymbolTable
->iundefsym(), fSymbolCount
);
983 if ( fDynamicSymbolTable
->iundefsym() != fDynamicSymbolTable
->iextdefsym() + fDynamicSymbolTable
->nextdefsym() ) {
984 throwf("start of undefined symbols (%d) not contiguous to extern symbols (%d+%d) in indirect symbol table",
985 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->iextdefsym(), fDynamicSymbolTable
->nextdefsym());
987 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() > fSymbolCount
) {
988 throwf("undefined symbols out of range (%d+%d > %d) in indirect symbol table",
989 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
992 if ( fDynamicSymbolTable
->iundefsym() + fDynamicSymbolTable
->nundefsym() != fSymbolCount
) {
993 throwf("end undefined symbols (%d+%d) not at end of all symbols (%d) in indirect symbol table",
994 fDynamicSymbolTable
->iundefsym(), fDynamicSymbolTable
->nundefsym(), fSymbolCount
);
1001 template <typename A
>
1002 void MachOChecker
<A
>::checkSymbolTable()
1004 // verify no duplicate external symbol names
1005 if ( fDynamicSymbolTable
!= NULL
) {
1006 StringSet externalNames
;
1007 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
1008 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
1009 int i
= fDynamicSymbolTable
->iextdefsym();
1010 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
1011 const char* symName
= &fStrings
[p
->n_strx()];
1012 if ( symName
> fStringsEnd
)
1013 throw "string index out of range";
1014 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
1015 if ( externalNames
.find(symName
) != externalNames
.end() )
1016 throwf("duplicate external symbol: %s", symName
);
1017 if ( (p
->n_type() & N_EXT
) == 0 )
1018 throwf("non-external symbol in external symbol range: %s", symName
);
1019 // don't add N_INDR to externalNames because there is likely an undefine with same name
1020 if ( (p
->n_type() & N_INDR
) == 0 )
1021 externalNames
.insert(symName
);
1023 // verify no undefines with same name as an external symbol
1024 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
1025 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
1026 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
1027 const char* symName
= &fStrings
[p
->n_strx()];
1028 if ( symName
> fStringsEnd
)
1029 throw "string index out of range";
1030 if ( externalNames
.find(symName
) != externalNames
.end() )
1031 throwf("undefine with same name as external symbol: %s", symName
);
1033 // verify all N_SECT values are valid
1034 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
1035 uint8_t type
= p
->n_type();
1036 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
1037 if ( p
->n_sect() > fSectionCount
) {
1038 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
1046 template <typename A
>
1047 void MachOChecker
<A
>::checkInitTerms()
1049 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1050 const uint32_t cmd_count
= fHeader
->ncmds();
1051 const macho_load_command
<P
>* cmd
= cmds
;
1052 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1053 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1054 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1055 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1056 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1057 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1058 // make sure all magic sections that use indirect symbol table fit within it
1062 const char* kind
= "initializer";
1063 switch ( sect
->flags() & SECTION_TYPE
) {
1064 case S_MOD_TERM_FUNC_POINTERS
:
1065 kind
= "terminator";
1067 case S_MOD_INIT_FUNC_POINTERS
:
1068 count
= sect
->size() / sizeof(pint_t
);
1069 if ( (count
*sizeof(pint_t
)) != sect
->size() )
1070 throwf("%s section size is not an even multiple of element size", sect
->sectname());
1071 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
1072 throwf("%s section size is not pointer size aligned", sect
->sectname());
1073 // check each pointer in array points within TEXT
1074 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
1075 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
1076 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
1077 pint_t pointer
= P::getP(*p
);
1078 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
1079 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
1081 // check each pointer in array will be rebased and not bound
1082 if ( fSlidableImage
) {
1083 pint_t sectionBeginAddr
= sect
->addr();
1084 pint_t sectionEndddr
= sect
->addr() + sect
->size();
1085 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
1086 if ( addressIsBindingSite(addr
) )
1087 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
1088 if ( ! addressIsRebaseSite(addr
) )
1089 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
1096 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1104 x86::P::uint_t MachOChecker
<x86
>::relocBase()
1106 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1107 return fFirstWritableSegment
->vmaddr();
1109 return fFirstSegment
->vmaddr();
1113 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
1115 // check for split-seg
1116 return fFirstWritableSegment
->vmaddr();
1120 arm::P::uint_t MachOChecker
<arm
>::relocBase()
1122 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
1123 return fFirstWritableSegment
->vmaddr();
1125 return fFirstSegment
->vmaddr();
1129 arm64::P::uint_t MachOChecker
<arm64
>::relocBase()
1131 return fFirstWritableSegment
->vmaddr();
1134 template <typename A
>
1135 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
1137 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1138 const uint32_t cmd_count
= fHeader
->ncmds();
1139 const macho_load_command
<P
>* cmd
= cmds
;
1140 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1141 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1142 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1143 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
1144 // if segment is writable, we are fine
1145 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1147 // could be a text reloc, make sure section bit is set
1148 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1149 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1150 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1151 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
1152 // found section for this address, if has relocs we are fine
1153 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
1158 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1166 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1168 if ( reloc
->r_length() != 2 )
1169 throw "bad external relocation length";
1170 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1171 throw "unknown external relocation type";
1172 if ( reloc
->r_pcrel() != 0 )
1173 throw "bad external relocation pc_rel";
1174 if ( reloc
->r_extern() == 0 )
1175 throw "local relocation found with external relocations";
1176 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1177 throw "external relocation address not in writable segment";
1178 // FIX: check r_symbol
1183 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1185 if ( reloc
->r_length() != 3 )
1186 throw "bad external relocation length";
1187 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1188 throw "unknown external relocation type";
1189 if ( reloc
->r_pcrel() != 0 )
1190 throw "bad external relocation pc_rel";
1191 if ( reloc
->r_extern() == 0 )
1192 throw "local relocation found with external relocations";
1193 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1194 throw "exernal relocation address not in writable segment";
1195 // FIX: check r_symbol
1198 #if SUPPORT_ARCH_arm_any
1200 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1202 if ( reloc
->r_length() != 2 )
1203 throw "bad external relocation length";
1204 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1205 throw "unknown external relocation type";
1206 if ( reloc
->r_pcrel() != 0 )
1207 throw "bad external relocation pc_rel";
1208 if ( reloc
->r_extern() == 0 )
1209 throw "local relocation found with external relocations";
1210 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1211 throw "external relocation address not in writable segment";
1212 // FIX: check r_symbol
1216 #if SUPPORT_ARCH_arm64
1218 void MachOChecker
<arm64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1220 throw "external relocations not used for arm64";
1226 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1232 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1234 if ( reloc
->r_length() != 3 )
1235 throw "bad local relocation length";
1236 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1237 throw "unknown local relocation type";
1238 if ( reloc
->r_pcrel() != 0 )
1239 throw "bad local relocation pc_rel";
1240 if ( reloc
->r_extern() != 0 )
1241 throw "external relocation found with local relocations";
1242 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1243 throw "local relocation address not in writable segment";
1246 #if SUPPORT_ARCH_arm_any
1248 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1250 if ( reloc
->r_address() & R_SCATTERED
) {
1252 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1253 if ( sreloc
->r_length() != 2 )
1254 throw "bad local scattered relocation length";
1255 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1256 throw "bad local scattered relocation type";
1259 if ( reloc
->r_length() != 2 )
1260 throw "bad local relocation length";
1261 if ( reloc
->r_extern() != 0 )
1262 throw "external relocation found with local relocations";
1263 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1264 throw "local relocation address not in writable segment";
1269 #if SUPPORT_ARCH_arm64
1271 void MachOChecker
<arm64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1273 throw "local relocations not used for arm64";
1277 template <typename A
>
1278 void MachOChecker
<A
>::checkRelocations()
1280 // external relocations should be sorted to minimize dyld symbol lookups
1281 // therefore every reloc with the same r_symbolnum value should be contiguous
1282 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1283 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1284 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1285 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1286 this->checkExternalReloation(reloc
);
1287 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1288 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1289 throw "external relocations not sorted";
1290 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1291 lastSymbolIndex
= reloc
->r_symbolnum();
1295 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1296 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1297 this->checkLocalReloation(reloc
);
1300 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1301 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1302 const uint32_t cmd_count
= fHeader
->ncmds();
1303 const macho_load_command
<P
>* cmd
= cmds
;
1304 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1305 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1306 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1307 // if segment is writable, we are fine
1308 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1310 // look at sections that have text reloc bit set
1311 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1312 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1313 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1314 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1315 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1316 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1321 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1325 template <typename A
>
1326 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1328 if ( segIndex
> fSegments
.size() )
1329 throw "segment index out of range";
1330 return fSegments
[segIndex
]->vmaddr();
1333 template <typename A
>
1334 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1336 // look at local relocs
1337 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1338 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1339 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1340 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1344 if ( fDyldInfo
!= NULL
) {
1345 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1346 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1349 uint64_t segOffset
= 0;
1353 pint_t segStartAddr
= 0;
1356 while ( !done
&& (p
< end
) ) {
1357 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1358 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1361 case REBASE_OPCODE_DONE
:
1364 case REBASE_OPCODE_SET_TYPE_IMM
:
1367 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1368 segIndex
= immediate
;
1369 segStartAddr
= segStartAddress(segIndex
);
1370 segOffset
= read_uleb128(p
, end
);
1372 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1373 segOffset
+= read_uleb128(p
, end
);
1375 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1376 segOffset
+= immediate
*sizeof(pint_t
);
1378 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1379 for (int i
=0; i
< immediate
; ++i
) {
1380 addr
= segStartAddr
+segOffset
;
1381 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1383 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1384 segOffset
+= sizeof(pint_t
);
1387 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1388 count
= read_uleb128(p
, end
);
1389 for (uint32_t i
=0; i
< count
; ++i
) {
1390 addr
= segStartAddr
+segOffset
;
1391 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1393 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1394 segOffset
+= sizeof(pint_t
);
1397 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1398 addr
= segStartAddr
+segOffset
;
1399 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1401 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1402 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1404 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1405 count
= read_uleb128(p
, end
);
1406 skip
= read_uleb128(p
, end
);
1407 for (uint32_t i
=0; i
< count
; ++i
) {
1408 addr
= segStartAddr
+segOffset
;
1409 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1411 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1412 segOffset
+= skip
+ sizeof(pint_t
);
1416 throwf("bad rebase opcode %d", *p
);
1423 template <typename A
>
1424 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
1426 // look at local relocs
1427 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1428 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1429 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1430 if ( relocAddress
== targetAddr
)
1434 if ( fDyldInfo
!= NULL
) {
1435 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1436 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1439 uint64_t segOffset
= 0;
1443 pint_t segStartAddr
= 0;
1446 while ( !done
&& (p
< end
) ) {
1447 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1448 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1451 case REBASE_OPCODE_DONE
:
1454 case REBASE_OPCODE_SET_TYPE_IMM
:
1457 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1458 segIndex
= immediate
;
1459 segStartAddr
= segStartAddress(segIndex
);
1460 segOffset
= read_uleb128(p
, end
);
1462 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1463 segOffset
+= read_uleb128(p
, end
);
1465 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1466 segOffset
+= immediate
*sizeof(pint_t
);
1468 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1469 for (int i
=0; i
< immediate
; ++i
) {
1470 addr
= segStartAddr
+segOffset
;
1471 if ( addr
== targetAddr
)
1473 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1474 segOffset
+= sizeof(pint_t
);
1477 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1478 count
= read_uleb128(p
, end
);
1479 for (uint32_t i
=0; i
< count
; ++i
) {
1480 addr
= segStartAddr
+segOffset
;
1481 if ( addr
== targetAddr
)
1483 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1484 segOffset
+= sizeof(pint_t
);
1487 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1488 addr
= segStartAddr
+segOffset
;
1489 if ( addr
== targetAddr
)
1491 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1492 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1494 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1495 count
= read_uleb128(p
, end
);
1496 skip
= read_uleb128(p
, end
);
1497 for (uint32_t i
=0; i
< count
; ++i
) {
1498 addr
= segStartAddr
+segOffset
;
1499 if ( addr
== targetAddr
)
1501 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1502 segOffset
+= skip
+ sizeof(pint_t
);
1506 throwf("bad rebase opcode %d", *p
);
1514 template <typename A
>
1515 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1517 // look at external relocs
1518 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1519 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1520 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1521 if ( relocAddress
== targetAddr
)
1525 if ( fDyldInfo
!= NULL
) {
1526 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1527 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1530 uint64_t segOffset
= 0;
1534 const char* symbolName
= NULL
;
1535 int libraryOrdinal
= 0;
1538 pint_t segStartAddr
= 0;
1541 while ( !done
&& (p
< end
) ) {
1542 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1543 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1546 case BIND_OPCODE_DONE
:
1549 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1550 libraryOrdinal
= immediate
;
1552 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1553 libraryOrdinal
= read_uleb128(p
, end
);
1555 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1556 // the special ordinals are negative numbers
1557 if ( immediate
== 0 )
1560 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1561 libraryOrdinal
= signExtended
;
1564 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1565 symbolName
= (char*)p
;
1570 case BIND_OPCODE_SET_TYPE_IMM
:
1573 case BIND_OPCODE_SET_ADDEND_SLEB
:
1574 addend
= read_sleb128(p
, end
);
1576 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1577 segIndex
= immediate
;
1578 segStartAddr
= segStartAddress(segIndex
);
1579 segOffset
= read_uleb128(p
, end
);
1581 case BIND_OPCODE_ADD_ADDR_ULEB
:
1582 segOffset
+= read_uleb128(p
, end
);
1584 case BIND_OPCODE_DO_BIND
:
1585 if ( (segStartAddr
+segOffset
) == targetAddr
)
1587 segOffset
+= sizeof(pint_t
);
1589 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
1590 if ( (segStartAddr
+segOffset
) == targetAddr
)
1592 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1594 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
1595 if ( (segStartAddr
+segOffset
) == targetAddr
)
1597 segOffset
+= immediate
*sizeof(pint_t
) + sizeof(pint_t
);
1599 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
1600 count
= read_uleb128(p
, end
);
1601 skip
= read_uleb128(p
, end
);
1602 for (uint32_t i
=0; i
< count
; ++i
) {
1603 if ( (segStartAddr
+segOffset
) == targetAddr
)
1605 segOffset
+= skip
+ sizeof(pint_t
);
1609 throwf("bad bind opcode %d", *p
);
1617 static void check(const char* path
, const char* verifierDstRoot
)
1619 struct stat stat_buf
;
1622 int fd
= ::open(path
, O_RDONLY
, 0);
1624 throw "cannot open file";
1625 if ( ::fstat(fd
, &stat_buf
) != 0 )
1626 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1627 uint32_t length
= stat_buf
.st_size
;
1628 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1629 if ( p
== ((uint8_t*)(-1)) )
1630 throw "cannot map file";
1632 const mach_header
* mh
= (mach_header
*)p
;
1633 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1634 const struct fat_header
* fh
= (struct fat_header
*)p
;
1635 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1636 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1637 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1638 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1639 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1643 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1644 MachOChecker
<x86
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1646 throw "in universal file, i386 slice does not contain i386 mach-o";
1648 case CPU_TYPE_X86_64
:
1649 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1650 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1652 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1654 #if SUPPORT_ARCH_arm_any
1656 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1657 MachOChecker
<arm
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1659 throw "in universal file, arm slice does not contain arm mach-o";
1662 #if SUPPORT_ARCH_arm64
1663 case CPU_TYPE_ARM64
:
1664 if ( MachOChecker
<arm64
>::validFile(p
+ offset
) )
1665 MachOChecker
<arm64
>::make(p
+ offset
, size
, path
, verifierDstRoot
);
1667 throw "in universal file, arm64 slice does not contain arm mach-o";
1671 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1675 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1676 MachOChecker
<x86
>::make(p
, length
, path
, verifierDstRoot
);
1678 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1679 MachOChecker
<x86_64
>::make(p
, length
, path
, verifierDstRoot
);
1681 #if SUPPORT_ARCH_arm_any
1682 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1683 MachOChecker
<arm
>::make(p
, length
, path
, verifierDstRoot
);
1686 #if SUPPORT_ARCH_arm64
1687 else if ( MachOChecker
<arm64
>::validFile(p
) ) {
1688 MachOChecker
<arm64
>::make(p
, length
, path
, verifierDstRoot
);
1692 throw "not a known file type";
1695 catch (const char* msg
) {
1696 throwf("%s in %s", msg
, path
);
1701 int main(int argc
, const char* argv
[])
1703 bool progress
= false;
1704 const char* verifierDstRoot
= NULL
;
1706 for(int i
=1; i
< argc
; ++i
) {
1707 const char* arg
= argv
[i
];
1708 if ( arg
[0] == '-' ) {
1709 if ( strcmp(arg
, "-progress") == 0 ) {
1712 else if ( strcmp(arg
, "-verifier_dstroot") == 0 ) {
1713 verifierDstRoot
= argv
[++i
];
1715 else if ( strcmp(arg
, "-verifier_error_list") == 0 ) {
1716 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");
1717 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");
1718 printf("os_dylib_rpath\tOS dylibs should not contain LC_RPATH load commands (from -rpath linker option)\n");
1719 printf("os_dylib_flat_namespace\tOS dylibs should not be built with -flat_namespace\n");
1720 printf("os_dylib_undefined_dynamic_lookup\tOS dylibs should not be built with -undefined dynamic_lookup\n");
1721 printf("os_dylib_malformed\the mach-o is malformed\n");
1725 throwf("unknown option: %s\n", arg
);
1729 bool success
= true;
1731 check(arg
, verifierDstRoot
);
1733 catch (const char* msg
) {
1734 if ( verifierDstRoot
) {
1735 printf("os_dylib_malformed\twarn\t%s\n", msg
);
1738 fprintf(stderr
, "machocheck failed: %s\n", msg
);
1743 if ( success
&& progress
)
1744 printf("ok: %s\n", arg
);