1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
36 #include <unordered_set>
38 #include "configure.h"
40 #include "MachOFileAbstraction.hpp"
41 #include "Architectures.hpp"
44 __attribute__((noreturn
))
45 void throwf(const char* format
, ...)
49 va_start(list
, format
);
50 vasprintf(&p
, format
, list
);
57 static uint64_t read_uleb128(const uint8_t*& p
, const uint8_t* end
)
63 throwf("malformed uleb128");
65 uint64_t slice
= *p
& 0x7f;
67 if (bit
>= 64 || slice
<< bit
>> bit
!= slice
)
68 throwf("uleb128 too big");
70 result
|= (slice
<< bit
);
79 static int64_t read_sleb128(const uint8_t*& p
, const uint8_t* end
)
86 throwf("malformed sleb128");
88 result
|= ((byte
& 0x7f) << bit
);
90 } while (byte
& 0x80);
91 // sign extend negative numbers
92 if ( (byte
& 0x40) != 0 )
93 result
|= (-1LL) << bit
;
102 static bool validFile(const uint8_t* fileContent
);
103 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
104 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
105 virtual ~MachOChecker() {}
109 typedef typename
A::P P
;
110 typedef typename
A::P::E E
;
111 typedef typename
A::P::uint_t pint_t
;
113 // utility classes for using std::unordered_map with c-strings
115 size_t operator()(const char* __s
) const {
118 __h
= 5 * __h
+ *__s
;
124 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
127 typedef std::unordered_set
<const char*, CStringHash
, CStringEquals
> StringSet
;
129 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
130 void checkMachHeader();
131 void checkLoadCommands();
132 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
133 uint8_t loadCommandSizeMask();
134 void checkSymbolTable();
135 void checkInitTerms();
136 void checkIndirectSymbolTable();
137 void checkRelocations();
138 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
139 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
141 bool addressInWritableSegment(pint_t address
);
142 bool hasTextRelocInRange(pint_t start
, pint_t end
);
143 pint_t
segStartAddress(uint8_t segIndex
);
144 bool addressIsRebaseSite(pint_t addr
);
145 bool addressIsBindingSite(pint_t addr
);
146 pint_t
getInitialStackPointer(const macho_thread_command
<P
>*);
147 pint_t
getEntryPoint(const macho_thread_command
<P
>*);
152 const macho_header
<P
>* fHeader
;
154 const char* fStrings
;
155 const char* fStringsEnd
;
156 const macho_nlist
<P
>* fSymbols
;
157 uint32_t fSymbolCount
;
158 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
159 const uint32_t* fIndirectTable
;
160 uint32_t fIndirectTableCount
;
161 const macho_relocation_info
<P
>* fLocalRelocations
;
162 uint32_t fLocalRelocationsCount
;
163 const macho_relocation_info
<P
>* fExternalRelocations
;
164 uint32_t fExternalRelocationsCount
;
165 bool fWriteableSegmentWithAddrOver4G
;
167 const macho_segment_command
<P
>* fFirstSegment
;
168 const macho_segment_command
<P
>* fFirstWritableSegment
;
169 const macho_segment_command
<P
>* fTEXTSegment
;
170 const macho_dyld_info_command
<P
>* fDyldInfo
;
171 uint32_t fSectionCount
;
172 std::vector
<const macho_segment_command
<P
>*>fSegments
;
178 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
180 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
181 if ( header
->magic() != MH_MAGIC
)
183 if ( header
->cputype() != CPU_TYPE_POWERPC
)
185 switch (header
->filetype()) {
196 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
198 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
199 if ( header
->magic() != MH_MAGIC_64
)
201 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
203 switch (header
->filetype()) {
214 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
216 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
217 if ( header
->magic() != MH_MAGIC
)
219 if ( header
->cputype() != CPU_TYPE_I386
)
221 switch (header
->filetype()) {
232 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
234 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
235 if ( header
->magic() != MH_MAGIC_64
)
237 if ( header
->cputype() != CPU_TYPE_X86_64
)
239 switch (header
->filetype()) {
250 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
252 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
253 if ( header
->magic() != MH_MAGIC
)
255 if ( header
->cputype() != CPU_TYPE_ARM
)
257 switch (header
->filetype()) {
267 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
268 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
269 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
270 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
271 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
275 ppc::P::uint_t MachOChecker
<ppc
>::getInitialStackPointer(const macho_thread_command
<ppc::P
>* threadInfo
)
277 return threadInfo
->thread_register(3);
281 ppc64::P::uint_t MachOChecker
<ppc64
>::getInitialStackPointer(const macho_thread_command
<ppc64::P
>* threadInfo
)
283 return threadInfo
->thread_register(3);
287 x86::P::uint_t MachOChecker
<x86
>::getInitialStackPointer(const macho_thread_command
<x86::P
>* threadInfo
)
289 return threadInfo
->thread_register(7);
293 x86_64::P::uint_t MachOChecker
<x86_64
>::getInitialStackPointer(const macho_thread_command
<x86_64::P
>* threadInfo
)
295 return threadInfo
->thread_register(7);
299 arm::P::uint_t MachOChecker
<arm
>::getInitialStackPointer(const macho_thread_command
<arm::P
>* threadInfo
)
301 return threadInfo
->thread_register(13);
309 ppc::P::uint_t MachOChecker
<ppc
>::getEntryPoint(const macho_thread_command
<ppc::P
>* threadInfo
)
311 return threadInfo
->thread_register(0);
315 ppc64::P::uint_t MachOChecker
<ppc64
>::getEntryPoint(const macho_thread_command
<ppc64::P
>* threadInfo
)
317 return threadInfo
->thread_register(0);
321 x86::P::uint_t MachOChecker
<x86
>::getEntryPoint(const macho_thread_command
<x86::P
>* threadInfo
)
323 return threadInfo
->thread_register(10);
327 x86_64::P::uint_t MachOChecker
<x86_64
>::getEntryPoint(const macho_thread_command
<x86_64::P
>* threadInfo
)
329 return threadInfo
->thread_register(16);
333 arm::P::uint_t MachOChecker
<arm
>::getEntryPoint(const macho_thread_command
<arm::P
>* threadInfo
)
335 return threadInfo
->thread_register(15);
339 template <typename A
>
340 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
341 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
342 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
343 fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
),
344 fTEXTSegment(NULL
), fDyldInfo(NULL
), fSectionCount(0)
347 if ( ! validFile(fileContent
) )
348 throw "not a mach-o file that can be checked";
350 fPath
= strdup(path
);
351 fHeader
= (const macho_header
<P
>*)fileContent
;
353 // sanity check header
356 // check load commands
359 checkIndirectSymbolTable();
369 template <typename A
>
370 void MachOChecker
<A
>::checkMachHeader()
372 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
373 throw "sizeofcmds in mach_header is larger than file";
375 uint32_t flags
= fHeader
->flags();
376 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFE000000;
377 if ( flags
& invalidBits
)
378 throw "invalid bits in mach_header flags";
379 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
380 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
382 switch ( fHeader
->filetype() ) {
384 fSlidableImage
= ( flags
& MH_PIE
);
388 fSlidableImage
= true;
391 throw "not a mach-o file type supported by this tool";
395 template <typename A
>
396 void MachOChecker
<A
>::checkLoadCommands()
398 // check that all load commands fit within the load command space file
399 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
400 const macho_thread_command
<P
>* threadInfo
= NULL
;
401 const macho_entry_point_command
<P
>* entryPoint
= NULL
;
402 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
403 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
404 const uint32_t cmd_count
= fHeader
->ncmds();
405 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
406 const macho_load_command
<P
>* cmd
= cmds
;
407 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
408 uint32_t size
= cmd
->cmdsize();
409 if ( (size
& this->loadCommandSizeMask()) != 0 )
410 throwf("load command #%d has a unaligned size", i
);
411 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
412 if ( endOfCmd
> endOfLoadCommands
)
413 throwf("load command #%d extends beyond the end of the load commands", i
);
414 if ( endOfCmd
> endOfFile
)
415 throwf("load command #%d extends beyond the end of the file", i
);
416 switch ( cmd
->cmd() ) {
417 case macho_segment_command
<P
>::CMD
:
422 case LC_LOAD_DYLINKER
:
424 case macho_routines_command
<P
>::CMD
:
425 case LC_SUB_FRAMEWORK
:
427 case LC_TWOLEVEL_HINTS
:
428 case LC_PREBIND_CKSUM
:
429 case LC_LOAD_WEAK_DYLIB
:
430 case LC_LAZY_LOAD_DYLIB
:
432 case LC_REEXPORT_DYLIB
:
433 case LC_SEGMENT_SPLIT_INFO
:
434 case LC_CODE_SIGNATURE
:
435 case LC_LOAD_UPWARD_DYLIB
:
436 case LC_VERSION_MIN_MACOSX
:
437 case LC_VERSION_MIN_IPHONEOS
:
439 case LC_FUNCTION_STARTS
:
440 case LC_DYLD_ENVIRONMENT
:
441 case LC_DATA_IN_CODE
:
442 case LC_DYLIB_CODE_SIGN_DRS
:
443 case LC_SOURCE_VERSION
:
446 case LC_DYLD_INFO_ONLY
:
447 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
449 case LC_ENCRYPTION_INFO
:
450 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
452 case LC_SUB_UMBRELLA
:
454 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
455 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";
458 if ( fHeader
->filetype() != MH_EXECUTE
)
459 throw "LC_MAIN can only be used in MH_EXECUTE file types";
460 entryPoint
= (macho_entry_point_command
<P
>*)cmd
;
463 if ( fHeader
->filetype() != MH_EXECUTE
)
464 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
465 threadInfo
= (macho_thread_command
<P
>*)cmd
;
468 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
470 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
475 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
476 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
477 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
478 const macho_segment_command
<P
>* stackSegment
= NULL
;
479 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
480 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
481 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
482 fSegments
.push_back(segCmd
);
483 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
484 throw "invalid segment load command size";
486 // see if this overlaps another segment address range
487 uint64_t startAddr
= segCmd
->vmaddr();
488 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
489 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
490 if ( it
->first
< startAddr
) {
491 if ( it
->second
> startAddr
)
492 throw "overlapping segment vm addresses";
494 else if ( it
->first
> startAddr
) {
495 if ( it
->first
< endAddr
)
496 throw "overlapping segment vm addresses";
499 throw "overlapping segment vm addresses";
501 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
503 // see if this overlaps another segment file offset range
504 uint64_t startOffset
= segCmd
->fileoff();
505 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
506 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
507 if ( it
->first
< startOffset
) {
508 if ( it
->second
> startOffset
)
509 throw "overlapping segment file data";
511 else if ( it
->first
> startOffset
) {
512 if ( it
->first
< endOffset
)
513 throw "overlapping segment file data";
516 throw "overlapping segment file data";
518 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
519 // check is within file bounds
520 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
521 throw "segment file data is past end of file";
523 // verify it fits in file
524 if ( startOffset
> fLength
)
525 throw "segment fileoff does not fit in file";
526 if ( endOffset
> fLength
)
527 throw "segment fileoff+filesize does not fit in file";
529 // record special segments
530 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
531 linkEditSegment
= segCmd
;
532 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
533 stackSegment
= segCmd
;
535 // cache interesting segments
536 if ( fFirstSegment
== NULL
)
537 fFirstSegment
= segCmd
;
538 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
539 fTEXTSegment
= segCmd
;
540 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
541 if ( fFirstWritableSegment
== NULL
)
542 fFirstWritableSegment
= segCmd
;
543 if ( segCmd
->vmaddr() > 0x100000000ULL
)
544 fWriteableSegmentWithAddrOver4G
= true;
547 // check section ranges
548 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
549 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
550 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
551 // check all non-zero sized sections are within segment
552 if ( sect
->addr() < startAddr
)
553 throwf("section %s vm address not within segment", sect
->sectname());
554 if ( (sect
->addr()+sect
->size()) > endAddr
)
555 throwf("section %s vm address not within segment", sect
->sectname());
556 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
557 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
558 && (segCmd
->filesize() != 0)
559 && (sect
->size() != 0) ) {
560 if ( sect
->offset() < startOffset
)
561 throwf("section %s file offset not within segment", sect
->sectname());
562 if ( (sect
->offset()+sect
->size()) > endOffset
)
563 throwf("section %s file offset not within segment", sect
->sectname());
565 checkSection(segCmd
, sect
);
569 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
572 // verify there was a LINKEDIT segment
573 if ( linkEditSegment
== NULL
)
574 throw "no __LINKEDIT segment";
576 // verify there was an executable __TEXT segment and load commands are in it
577 if ( fTEXTSegment
== NULL
)
578 throw "no __TEXT segment";
579 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
580 throw "__TEXT segment does not have r-x init permissions";
581 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
582 // throw "__TEXT segment does not have rwx max permissions";
583 if ( fTEXTSegment
->fileoff() != 0 )
584 throw "__TEXT segment does not start at mach_header";
585 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
586 throw "__TEXT segment smaller than load commands";
588 // verify if custom stack used, that stack is in __UNIXSTACK segment
589 if ( threadInfo
!= NULL
) {
590 pint_t initialSP
= getInitialStackPointer(threadInfo
);
591 if ( initialSP
!= 0 ) {
592 if ( stackSegment
== NULL
)
593 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
594 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
595 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
599 // verify __UNIXSTACK is zero fill
600 if ( stackSegment
!= NULL
) {
601 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
602 throw "__UNIXSTACK is not a zero-fill segment";
603 if ( stackSegment
->vmsize() < 4096 )
604 throw "__UNIXSTACK segment is too small";
607 // verify entry point is in __TEXT segment
608 if ( threadInfo
!= NULL
) {
609 pint_t initialPC
= getEntryPoint(threadInfo
);
610 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
611 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
613 else if ( entryPoint
!= NULL
) {
614 pint_t initialOffset
= entryPoint
->entryoff();
615 if ( (initialOffset
< fTEXTSegment
->fileoff()) || (initialOffset
>= (fTEXTSegment
->fileoff()+fTEXTSegment
->filesize())) )
616 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialOffset
);
619 // checks for executables
620 bool isStaticExecutable
= false;
621 if ( fHeader
->filetype() == MH_EXECUTE
) {
622 isStaticExecutable
= true;
624 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
625 switch ( cmd
->cmd() ) {
626 case LC_LOAD_DYLINKER
:
627 // the existence of a dyld load command makes a executable dynamic
628 isStaticExecutable
= false;
631 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
633 if ( isStaticExecutable
) {
634 if ( (fHeader
->flags() != MH_NOUNDEFS
) && (fHeader
->flags() != (MH_NOUNDEFS
|MH_PIE
)) )
635 throw "invalid bits in mach_header flags for static executable";
639 // verify encryption info
640 if ( encryption_info
!= NULL
) {
641 if ( fHeader
->filetype() != MH_EXECUTE
)
642 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
643 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
644 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
645 if ( (encryption_info
->cryptoff() % 4096) != 0 )
646 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
647 if ( (encryption_info
->cryptsize() % 4096) != 0 )
648 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
649 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
650 it
!= segmentFileOffsetRanges
.end(); ++it
) {
651 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
652 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
653 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
658 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
660 bool foundDynamicSymTab
= false;
661 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
662 switch ( cmd
->cmd() ) {
665 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
666 fSymbolCount
= symtab
->nsyms();
667 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
668 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
669 throw "symbol table not in __LINKEDIT";
670 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
671 throw "symbol table end not in __LINKEDIT";
672 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
673 throw "symbol table start not pointer aligned";
674 fStrings
= (char*)fHeader
+ symtab
->stroff();
675 fStringsEnd
= fStrings
+ symtab
->strsize();
676 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
677 throw "string pool not in __LINKEDIT";
678 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
679 throw "string pool extends beyond __LINKEDIT";
680 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
681 throw "string pool start not pointer aligned";
682 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
683 throw "string pool size not a multiple of pointer size";
688 if ( isStaticExecutable
&&! fSlidableImage
)
689 throw "LC_DYSYMTAB should not be used in static executable";
690 foundDynamicSymTab
= true;
691 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
692 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
693 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
694 if ( fIndirectTableCount
!= 0 ) {
695 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
696 throw "indirect symbol table not in __LINKEDIT";
697 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
698 throw "indirect symbol table not in __LINKEDIT";
699 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
700 throw "indirect symbol table not pointer aligned";
702 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
703 if ( fLocalRelocationsCount
!= 0 ) {
704 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
705 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
706 throw "local relocations not in __LINKEDIT";
707 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
708 throw "local relocations not in __LINKEDIT";
709 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
710 throw "local relocations table not pointer aligned";
712 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
713 if ( fExternalRelocationsCount
!= 0 ) {
714 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
715 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
716 throw "external relocations not in __LINKEDIT";
717 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
718 throw "external relocations not in __LINKEDIT";
719 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
720 throw "external relocations table not pointer aligned";
724 case LC_SEGMENT_SPLIT_INFO
:
726 if ( isStaticExecutable
)
727 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
728 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
729 if ( info
->dataoff() < linkEditSegment
->fileoff() )
730 throw "split seg info not in __LINKEDIT";
731 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
732 throw "split seg info not in __LINKEDIT";
733 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
734 throw "split seg info table not pointer aligned";
735 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
736 throw "split seg info size not a multiple of pointer size";
739 case LC_FUNCTION_STARTS
:
741 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
742 if ( info
->dataoff() < linkEditSegment
->fileoff() )
743 throw "function starts data not in __LINKEDIT";
744 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
745 throw "function starts data not in __LINKEDIT";
746 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
747 throw "function starts data table not pointer aligned";
748 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
749 throw "function starts data size not a multiple of pointer size";
752 case LC_DATA_IN_CODE
:
754 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
755 if ( info
->dataoff() < linkEditSegment
->fileoff() )
756 throw "data-in-code data not in __LINKEDIT";
757 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
758 throw "data-in-code data not in __LINKEDIT";
759 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
760 throw "data-in-code data table not pointer aligned";
761 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
762 throw "data-in-code data size not a multiple of pointer size";
765 case LC_DYLIB_CODE_SIGN_DRS
:
767 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
768 if ( info
->dataoff() < linkEditSegment
->fileoff() )
769 throw "dependent dylib DR data not in __LINKEDIT";
770 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
771 throw "dependent dylib DR data not in __LINKEDIT";
772 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
773 throw "dependent dylib DR data table not pointer aligned";
774 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
775 throw "dependent dylib DR data size not a multiple of pointer size";
779 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
781 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
782 throw "missing dynamic symbol table";
783 if ( fStrings
== NULL
)
784 throw "missing symbol table";
788 template <typename A
>
789 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
791 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
792 if ( sectionType
== S_ZEROFILL
) {
793 if ( sect
->offset() != 0 )
794 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
797 // check section's segment name matches segment
798 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
799 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
801 // more section tests here
807 template <typename A
>
808 void MachOChecker
<A
>::checkIndirectSymbolTable()
810 // static executables don't have indirect symbol table
811 if ( fDynamicSymbolTable
== NULL
)
813 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
814 const uint32_t cmd_count
= fHeader
->ncmds();
815 const macho_load_command
<P
>* cmd
= cmds
;
816 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
817 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
818 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
819 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
820 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
821 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
822 // make sure all magic sections that use indirect symbol table fit within it
824 uint32_t elementSize
= 0;
825 switch ( sect
->flags() & SECTION_TYPE
) {
827 elementSize
= sect
->reserved2();
828 start
= sect
->reserved1();
830 case S_LAZY_SYMBOL_POINTERS
:
831 case S_NON_LAZY_SYMBOL_POINTERS
:
832 elementSize
= sizeof(pint_t
);
833 start
= sect
->reserved1();
836 if ( elementSize
!= 0 ) {
837 uint32_t count
= sect
->size() / elementSize
;
838 if ( (count
*elementSize
) != sect
->size() )
839 throwf("%s section size is not an even multiple of element size", sect
->sectname());
840 if ( (start
+count
) > fIndirectTableCount
)
841 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
845 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
852 template <typename A
>
853 void MachOChecker
<A
>::checkSymbolTable()
855 // verify no duplicate external symbol names
856 if ( fDynamicSymbolTable
!= NULL
) {
857 StringSet externalNames
;
858 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
859 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
860 int i
= fDynamicSymbolTable
->iextdefsym();
861 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
862 const char* symName
= &fStrings
[p
->n_strx()];
863 if ( symName
> fStringsEnd
)
864 throw "string index out of range";
865 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
866 if ( externalNames
.find(symName
) != externalNames
.end() )
867 throwf("duplicate external symbol: %s", symName
);
868 if ( (p
->n_type() & N_EXT
) == 0 )
869 throwf("non-external symbol in external symbol range: %s", symName
);
870 // don't add N_INDR to externalNames because there is likely an undefine with same name
871 if ( (p
->n_type() & N_INDR
) == 0 )
872 externalNames
.insert(symName
);
874 // verify no undefines with same name as an external symbol
875 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
876 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
877 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
878 const char* symName
= &fStrings
[p
->n_strx()];
879 if ( symName
> fStringsEnd
)
880 throw "string index out of range";
881 if ( externalNames
.find(symName
) != externalNames
.end() )
882 throwf("undefine with same name as external symbol: %s", symName
);
884 // verify all N_SECT values are valid
885 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
886 uint8_t type
= p
->n_type();
887 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
888 if ( p
->n_sect() > fSectionCount
) {
889 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
897 template <typename A
>
898 void MachOChecker
<A
>::checkInitTerms()
900 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
901 const uint32_t cmd_count
= fHeader
->ncmds();
902 const macho_load_command
<P
>* cmd
= cmds
;
903 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
904 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
905 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
906 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
907 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
908 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
909 // make sure all magic sections that use indirect symbol table fit within it
913 const char* kind
= "initializer";
914 switch ( sect
->flags() & SECTION_TYPE
) {
915 case S_MOD_TERM_FUNC_POINTERS
:
918 case S_MOD_INIT_FUNC_POINTERS
:
919 count
= sect
->size() / sizeof(pint_t
);
920 if ( (count
*sizeof(pint_t
)) != sect
->size() )
921 throwf("%s section size is not an even multiple of element size", sect
->sectname());
922 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
923 throwf("%s section size is not pointer size aligned", sect
->sectname());
924 // check each pointer in array points within TEXT
925 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
926 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
927 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
928 pint_t pointer
= P::getP(*p
);
929 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
930 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
932 // check each pointer in array will be rebased and not bound
933 if ( fSlidableImage
) {
934 pint_t sectionBeginAddr
= sect
->addr();
935 pint_t sectionEndddr
= sect
->addr() + sect
->size();
936 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
937 if ( addressIsBindingSite(addr
) )
938 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
939 if ( ! addressIsRebaseSite(addr
) )
940 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
947 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
954 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
956 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
957 return fFirstWritableSegment
->vmaddr();
959 return fFirstSegment
->vmaddr();
963 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
965 if ( fWriteableSegmentWithAddrOver4G
)
966 return fFirstWritableSegment
->vmaddr();
968 return fFirstSegment
->vmaddr();
972 x86::P::uint_t MachOChecker
<x86
>::relocBase()
974 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
975 return fFirstWritableSegment
->vmaddr();
977 return fFirstSegment
->vmaddr();
981 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
983 // check for split-seg
984 return fFirstWritableSegment
->vmaddr();
988 arm::P::uint_t MachOChecker
<arm
>::relocBase()
990 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
991 return fFirstWritableSegment
->vmaddr();
993 return fFirstSegment
->vmaddr();
997 template <typename A
>
998 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
1000 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1001 const uint32_t cmd_count
= fHeader
->ncmds();
1002 const macho_load_command
<P
>* cmd
= cmds
;
1003 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1004 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1005 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1006 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
1007 // if segment is writable, we are fine
1008 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1010 // could be a text reloc, make sure section bit is set
1011 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1012 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1013 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1014 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
1015 // found section for this address, if has relocs we are fine
1016 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
1021 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1028 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1030 if ( reloc
->r_length() != 2 )
1031 throw "bad external relocation length";
1032 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1033 throw "unknown external relocation type";
1034 if ( reloc
->r_pcrel() != 0 )
1035 throw "bad external relocation pc_rel";
1036 if ( reloc
->r_extern() == 0 )
1037 throw "local relocation found with external relocations";
1038 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1039 throw "external relocation address not in writable segment";
1040 // FIX: check r_symbol
1044 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1046 if ( reloc
->r_length() != 3 )
1047 throw "bad external relocation length";
1048 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1049 throw "unknown external relocation type";
1050 if ( reloc
->r_pcrel() != 0 )
1051 throw "bad external relocation pc_rel";
1052 if ( reloc
->r_extern() == 0 )
1053 throw "local relocation found with external relocations";
1054 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1055 throw "external relocation address not in writable segment";
1056 // FIX: check r_symbol
1060 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1062 if ( reloc
->r_length() != 2 )
1063 throw "bad external relocation length";
1064 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1065 throw "unknown external relocation type";
1066 if ( reloc
->r_pcrel() != 0 )
1067 throw "bad external relocation pc_rel";
1068 if ( reloc
->r_extern() == 0 )
1069 throw "local relocation found with external relocations";
1070 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1071 throw "external relocation address not in writable segment";
1072 // FIX: check r_symbol
1077 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1079 if ( reloc
->r_length() != 3 )
1080 throw "bad external relocation length";
1081 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1082 throw "unknown external relocation type";
1083 if ( reloc
->r_pcrel() != 0 )
1084 throw "bad external relocation pc_rel";
1085 if ( reloc
->r_extern() == 0 )
1086 throw "local relocation found with external relocations";
1087 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1088 throw "exernal relocation address not in writable segment";
1089 // FIX: check r_symbol
1092 #if SUPPORT_ARCH_arm_any
1094 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1096 if ( reloc
->r_length() != 2 )
1097 throw "bad external relocation length";
1098 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1099 throw "unknown external relocation type";
1100 if ( reloc
->r_pcrel() != 0 )
1101 throw "bad external relocation pc_rel";
1102 if ( reloc
->r_extern() == 0 )
1103 throw "local relocation found with external relocations";
1104 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1105 throw "external relocation address not in writable segment";
1106 // FIX: check r_symbol
1112 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1114 if ( reloc
->r_address() & R_SCATTERED
) {
1116 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1122 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1123 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
1129 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1131 if ( reloc
->r_length() != 3 )
1132 throw "bad local relocation length";
1133 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1134 throw "unknown local relocation type";
1135 if ( reloc
->r_pcrel() != 0 )
1136 throw "bad local relocation pc_rel";
1137 if ( reloc
->r_extern() != 0 )
1138 throw "external relocation found with local relocations";
1139 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1140 throw "local relocation address not in writable segment";
1144 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1150 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1152 if ( reloc
->r_length() != 3 )
1153 throw "bad local relocation length";
1154 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1155 throw "unknown local relocation type";
1156 if ( reloc
->r_pcrel() != 0 )
1157 throw "bad local relocation pc_rel";
1158 if ( reloc
->r_extern() != 0 )
1159 throw "external relocation found with local relocations";
1160 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1161 throw "local relocation address not in writable segment";
1164 #if SUPPORT_ARCH_arm_any
1166 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1168 if ( reloc
->r_address() & R_SCATTERED
) {
1170 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1171 if ( sreloc
->r_length() != 2 )
1172 throw "bad local scattered relocation length";
1173 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1174 throw "bad local scattered relocation type";
1177 if ( reloc
->r_length() != 2 )
1178 throw "bad local relocation length";
1179 if ( reloc
->r_extern() != 0 )
1180 throw "external relocation found with local relocations";
1181 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1182 throw "local relocation address not in writable segment";
1187 template <typename A
>
1188 void MachOChecker
<A
>::checkRelocations()
1190 // external relocations should be sorted to minimize dyld symbol lookups
1191 // therefore every reloc with the same r_symbolnum value should be contiguous
1192 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1193 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1194 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1195 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1196 this->checkExternalReloation(reloc
);
1197 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1198 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1199 throw "external relocations not sorted";
1200 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1201 lastSymbolIndex
= reloc
->r_symbolnum();
1205 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1206 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1207 this->checkLocalReloation(reloc
);
1210 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1211 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1212 const uint32_t cmd_count
= fHeader
->ncmds();
1213 const macho_load_command
<P
>* cmd
= cmds
;
1214 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1215 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1216 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1217 // if segment is writable, we are fine
1218 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1220 // look at sections that have text reloc bit set
1221 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1222 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1223 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1224 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1225 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1226 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1231 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1235 template <typename A
>
1236 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1238 if ( segIndex
> fSegments
.size() )
1239 throw "segment index out of range";
1240 return fSegments
[segIndex
]->vmaddr();
1243 template <typename A
>
1244 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1246 // look at local relocs
1247 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1248 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1249 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1250 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1254 if ( fDyldInfo
!= NULL
) {
1255 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1256 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1259 uint64_t segOffset
= 0;
1263 pint_t segStartAddr
= 0;
1266 while ( !done
&& (p
< end
) ) {
1267 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1268 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1271 case REBASE_OPCODE_DONE
:
1274 case REBASE_OPCODE_SET_TYPE_IMM
:
1277 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1278 segIndex
= immediate
;
1279 segStartAddr
= segStartAddress(segIndex
);
1280 segOffset
= read_uleb128(p
, end
);
1282 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1283 segOffset
+= read_uleb128(p
, end
);
1285 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1286 segOffset
+= immediate
*sizeof(pint_t
);
1288 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1289 for (int i
=0; i
< immediate
; ++i
) {
1290 addr
= segStartAddr
+segOffset
;
1291 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1293 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1294 segOffset
+= sizeof(pint_t
);
1297 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1298 count
= read_uleb128(p
, end
);
1299 for (uint32_t i
=0; i
< count
; ++i
) {
1300 addr
= segStartAddr
+segOffset
;
1301 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1303 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1304 segOffset
+= sizeof(pint_t
);
1307 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1308 addr
= segStartAddr
+segOffset
;
1309 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1311 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1312 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1314 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1315 count
= read_uleb128(p
, end
);
1316 skip
= read_uleb128(p
, end
);
1317 for (uint32_t i
=0; i
< count
; ++i
) {
1318 addr
= segStartAddr
+segOffset
;
1319 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1321 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1322 segOffset
+= skip
+ sizeof(pint_t
);
1326 throwf("bad rebase opcode %d", *p
);
1333 template <typename A
>
1334 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
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 ( relocAddress
== targetAddr
)
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 ( addr
== targetAddr
)
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 ( addr
== targetAddr
)
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 ( addr
== targetAddr
)
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 ( addr
== targetAddr
)
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
);
1424 template <typename A
>
1425 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1427 // look at external relocs
1428 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1429 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1430 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1431 if ( relocAddress
== targetAddr
)
1435 if ( fDyldInfo
!= NULL
) {
1436 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1437 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1440 uint64_t segOffset
= 0;
1444 const char* symbolName
= NULL
;
1445 int libraryOrdinal
= 0;
1448 pint_t segStartAddr
= 0;
1451 while ( !done
&& (p
< end
) ) {
1452 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1453 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1456 case BIND_OPCODE_DONE
:
1459 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1460 libraryOrdinal
= immediate
;
1462 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1463 libraryOrdinal
= read_uleb128(p
, end
);
1465 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1466 // the special ordinals are negative numbers
1467 if ( immediate
== 0 )
1470 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1471 libraryOrdinal
= signExtended
;
1474 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1475 symbolName
= (char*)p
;
1480 case BIND_OPCODE_SET_TYPE_IMM
:
1483 case BIND_OPCODE_SET_ADDEND_SLEB
:
1484 addend
= read_sleb128(p
, end
);
1486 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1487 segIndex
= immediate
;
1488 segStartAddr
= segStartAddress(segIndex
);
1489 segOffset
= read_uleb128(p
, end
);
1491 case BIND_OPCODE_ADD_ADDR_ULEB
:
1492 segOffset
+= read_uleb128(p
, end
);
1494 case BIND_OPCODE_DO_BIND
:
1495 if ( (segStartAddr
+segOffset
) == targetAddr
)
1497 segOffset
+= sizeof(pint_t
);
1499 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
1500 if ( (segStartAddr
+segOffset
) == targetAddr
)
1502 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1504 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
1505 if ( (segStartAddr
+segOffset
) == targetAddr
)
1507 segOffset
+= immediate
*sizeof(pint_t
) + sizeof(pint_t
);
1509 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
1510 count
= read_uleb128(p
, end
);
1511 skip
= read_uleb128(p
, end
);
1512 for (uint32_t i
=0; i
< count
; ++i
) {
1513 if ( (segStartAddr
+segOffset
) == targetAddr
)
1515 segOffset
+= skip
+ sizeof(pint_t
);
1519 throwf("bad bind opcode %d", *p
);
1527 static void check(const char* path
)
1529 struct stat stat_buf
;
1532 int fd
= ::open(path
, O_RDONLY
, 0);
1534 throw "cannot open file";
1535 if ( ::fstat(fd
, &stat_buf
) != 0 )
1536 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1537 uint32_t length
= stat_buf
.st_size
;
1538 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1539 if ( p
== ((uint8_t*)(-1)) )
1540 throw "cannot map file";
1542 const mach_header
* mh
= (mach_header
*)p
;
1543 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1544 const struct fat_header
* fh
= (struct fat_header
*)p
;
1545 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1546 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1547 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1548 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1549 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1552 case CPU_TYPE_POWERPC
:
1553 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
1554 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
1556 throw "in universal file, ppc slice does not contain ppc mach-o";
1559 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1560 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
1562 throw "in universal file, i386 slice does not contain i386 mach-o";
1564 case CPU_TYPE_POWERPC64
:
1565 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
1566 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
1568 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
1570 case CPU_TYPE_X86_64
:
1571 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1572 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
1574 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1576 #if SUPPORT_ARCH_arm_any
1578 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1579 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
1581 throw "in universal file, arm slice does not contain arm mach-o";
1585 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1589 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1590 MachOChecker
<x86
>::make(p
, length
, path
);
1592 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
1593 MachOChecker
<ppc
>::make(p
, length
, path
);
1595 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
1596 MachOChecker
<ppc64
>::make(p
, length
, path
);
1598 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1599 MachOChecker
<x86_64
>::make(p
, length
, path
);
1601 #if SUPPORT_ARCH_arm_any
1602 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1603 MachOChecker
<arm
>::make(p
, length
, path
);
1607 throw "not a known file type";
1610 catch (const char* msg
) {
1611 throwf("%s in %s", msg
, path
);
1616 int main(int argc
, const char* argv
[])
1618 bool progress
= false;
1620 for(int i
=1; i
< argc
; ++i
) {
1621 const char* arg
= argv
[i
];
1622 if ( arg
[0] == '-' ) {
1623 if ( strcmp(arg
, "-progress") == 0 ) {
1627 throwf("unknown option: %s\n", arg
);
1631 bool success
= true;
1635 catch (const char* msg
) {
1636 fprintf(stderr
, "machocheck failed: %s %s\n", arg
, msg
);
1640 if ( success
&& progress
)
1641 printf("ok: %s\n", arg
);