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 <ext/hash_set>
38 #include "MachOFileAbstraction.hpp"
39 #include "Architectures.hpp"
42 __attribute__((noreturn
))
43 void throwf(const char* format
, ...)
47 va_start(list
, format
);
48 vasprintf(&p
, format
, list
);
55 static uint64_t read_uleb128(const uint8_t*& p
, const uint8_t* end
)
61 throwf("malformed uleb128");
63 uint64_t slice
= *p
& 0x7f;
65 if (bit
>= 64 || slice
<< bit
>> bit
!= slice
)
66 throwf("uleb128 too big");
68 result
|= (slice
<< bit
);
77 static int64_t read_sleb128(const uint8_t*& p
, const uint8_t* end
)
84 throwf("malformed sleb128");
86 result
|= ((byte
& 0x7f) << bit
);
88 } while (byte
& 0x80);
89 // sign extend negative numbers
90 if ( (byte
& 0x40) != 0 )
91 result
|= (-1LL) << bit
;
100 static bool validFile(const uint8_t* fileContent
);
101 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
102 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
103 virtual ~MachOChecker() {}
107 typedef typename
A::P P
;
108 typedef typename
A::P::E E
;
109 typedef typename
A::P::uint_t pint_t
;
114 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
117 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
119 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
120 void checkMachHeader();
121 void checkLoadCommands();
122 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
123 uint8_t loadCommandSizeMask();
124 void checkSymbolTable();
125 void checkInitTerms();
126 void checkIndirectSymbolTable();
127 void checkRelocations();
128 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
129 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
131 bool addressInWritableSegment(pint_t address
);
132 bool hasTextRelocInRange(pint_t start
, pint_t end
);
133 pint_t
segStartAddress(uint8_t segIndex
);
134 bool addressIsRebaseSite(pint_t addr
);
135 bool addressIsBindingSite(pint_t addr
);
136 pint_t
getInitialStackPointer(const macho_thread_command
<P
>*);
137 pint_t
getEntryPoint(const macho_thread_command
<P
>*);
142 const macho_header
<P
>* fHeader
;
144 const char* fStrings
;
145 const char* fStringsEnd
;
146 const macho_nlist
<P
>* fSymbols
;
147 uint32_t fSymbolCount
;
148 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
149 const uint32_t* fIndirectTable
;
150 uint32_t fIndirectTableCount
;
151 const macho_relocation_info
<P
>* fLocalRelocations
;
152 uint32_t fLocalRelocationsCount
;
153 const macho_relocation_info
<P
>* fExternalRelocations
;
154 uint32_t fExternalRelocationsCount
;
155 bool fWriteableSegmentWithAddrOver4G
;
157 const macho_segment_command
<P
>* fFirstSegment
;
158 const macho_segment_command
<P
>* fFirstWritableSegment
;
159 const macho_segment_command
<P
>* fTEXTSegment
;
160 const macho_dyld_info_command
<P
>* fDyldInfo
;
161 uint32_t fSectionCount
;
162 std::vector
<const macho_segment_command
<P
>*>fSegments
;
168 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
170 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
171 if ( header
->magic() != MH_MAGIC
)
173 if ( header
->cputype() != CPU_TYPE_POWERPC
)
175 switch (header
->filetype()) {
186 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
188 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
189 if ( header
->magic() != MH_MAGIC_64
)
191 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
193 switch (header
->filetype()) {
204 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
206 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
207 if ( header
->magic() != MH_MAGIC
)
209 if ( header
->cputype() != CPU_TYPE_I386
)
211 switch (header
->filetype()) {
222 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
224 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
225 if ( header
->magic() != MH_MAGIC_64
)
227 if ( header
->cputype() != CPU_TYPE_X86_64
)
229 switch (header
->filetype()) {
240 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
242 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
243 if ( header
->magic() != MH_MAGIC
)
245 if ( header
->cputype() != CPU_TYPE_ARM
)
247 switch (header
->filetype()) {
257 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
258 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
259 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
260 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
261 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
265 ppc::P::uint_t MachOChecker
<ppc
>::getInitialStackPointer(const macho_thread_command
<ppc::P
>* threadInfo
)
267 return threadInfo
->thread_register(3);
271 ppc64::P::uint_t MachOChecker
<ppc64
>::getInitialStackPointer(const macho_thread_command
<ppc64::P
>* threadInfo
)
273 return threadInfo
->thread_register(3);
277 x86::P::uint_t MachOChecker
<x86
>::getInitialStackPointer(const macho_thread_command
<x86::P
>* threadInfo
)
279 return threadInfo
->thread_register(7);
283 x86_64::P::uint_t MachOChecker
<x86_64
>::getInitialStackPointer(const macho_thread_command
<x86_64::P
>* threadInfo
)
285 return threadInfo
->thread_register(7);
289 arm::P::uint_t MachOChecker
<arm
>::getInitialStackPointer(const macho_thread_command
<arm::P
>* threadInfo
)
291 return threadInfo
->thread_register(13);
299 ppc::P::uint_t MachOChecker
<ppc
>::getEntryPoint(const macho_thread_command
<ppc::P
>* threadInfo
)
301 return threadInfo
->thread_register(0);
305 ppc64::P::uint_t MachOChecker
<ppc64
>::getEntryPoint(const macho_thread_command
<ppc64::P
>* threadInfo
)
307 return threadInfo
->thread_register(0);
311 x86::P::uint_t MachOChecker
<x86
>::getEntryPoint(const macho_thread_command
<x86::P
>* threadInfo
)
313 return threadInfo
->thread_register(10);
317 x86_64::P::uint_t MachOChecker
<x86_64
>::getEntryPoint(const macho_thread_command
<x86_64::P
>* threadInfo
)
319 return threadInfo
->thread_register(16);
323 arm::P::uint_t MachOChecker
<arm
>::getEntryPoint(const macho_thread_command
<arm::P
>* threadInfo
)
325 return threadInfo
->thread_register(15);
329 template <typename A
>
330 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
331 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
332 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
333 fWriteableSegmentWithAddrOver4G(false), fSlidableImage(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
),
334 fTEXTSegment(NULL
), fDyldInfo(NULL
), fSectionCount(0)
337 if ( ! validFile(fileContent
) )
338 throw "not a mach-o file that can be checked";
340 fPath
= strdup(path
);
341 fHeader
= (const macho_header
<P
>*)fileContent
;
343 // sanity check header
346 // check load commands
349 checkIndirectSymbolTable();
359 template <typename A
>
360 void MachOChecker
<A
>::checkMachHeader()
362 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
363 throw "sizeofcmds in mach_header is larger than file";
365 uint32_t flags
= fHeader
->flags();
366 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFE000000;
367 if ( flags
& invalidBits
)
368 throw "invalid bits in mach_header flags";
369 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
370 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
372 switch ( fHeader
->filetype() ) {
374 fSlidableImage
= ( flags
& MH_PIE
);
378 fSlidableImage
= true;
381 throw "not a mach-o file type supported by this tool";
385 template <typename A
>
386 void MachOChecker
<A
>::checkLoadCommands()
388 // check that all load commands fit within the load command space file
389 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
390 const macho_thread_command
<P
>* threadInfo
= NULL
;
391 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
392 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
393 const uint32_t cmd_count
= fHeader
->ncmds();
394 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
395 const macho_load_command
<P
>* cmd
= cmds
;
396 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
397 uint32_t size
= cmd
->cmdsize();
398 if ( (size
& this->loadCommandSizeMask()) != 0 )
399 throwf("load command #%d has a unaligned size", i
);
400 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
401 if ( endOfCmd
> endOfLoadCommands
)
402 throwf("load command #%d extends beyond the end of the load commands", i
);
403 if ( endOfCmd
> endOfFile
)
404 throwf("load command #%d extends beyond the end of the file", i
);
405 switch ( cmd
->cmd() ) {
406 case macho_segment_command
<P
>::CMD
:
411 case LC_LOAD_DYLINKER
:
413 case macho_routines_command
<P
>::CMD
:
414 case LC_SUB_FRAMEWORK
:
416 case LC_TWOLEVEL_HINTS
:
417 case LC_PREBIND_CKSUM
:
418 case LC_LOAD_WEAK_DYLIB
:
419 case LC_LAZY_LOAD_DYLIB
:
421 case LC_REEXPORT_DYLIB
:
422 case LC_SEGMENT_SPLIT_INFO
:
423 case LC_CODE_SIGNATURE
:
424 case LC_LOAD_UPWARD_DYLIB
:
425 case LC_VERSION_MIN_MACOSX
:
426 case LC_VERSION_MIN_IPHONEOS
:
427 case LC_FUNCTION_STARTS
:
431 case LC_DYLD_INFO_ONLY
:
432 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
434 case LC_ENCRYPTION_INFO
:
435 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
437 case LC_SUB_UMBRELLA
:
439 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
440 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";
443 if ( fHeader
->filetype() != MH_EXECUTE
)
444 throw "LC_UNIXTHREAD can only be used in MH_EXECUTE file types";
445 threadInfo
= (macho_thread_command
<P
>*)cmd
;
448 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
450 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
455 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
456 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
457 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
458 const macho_segment_command
<P
>* stackSegment
= NULL
;
459 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
460 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
461 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
462 fSegments
.push_back(segCmd
);
463 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
464 throw "invalid segment load command size";
466 // see if this overlaps another segment address range
467 uint64_t startAddr
= segCmd
->vmaddr();
468 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
469 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
470 if ( it
->first
< startAddr
) {
471 if ( it
->second
> startAddr
)
472 throw "overlapping segment vm addresses";
474 else if ( it
->first
> startAddr
) {
475 if ( it
->first
< endAddr
)
476 throw "overlapping segment vm addresses";
479 throw "overlapping segment vm addresses";
481 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
483 // see if this overlaps another segment file offset range
484 uint64_t startOffset
= segCmd
->fileoff();
485 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
486 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
487 if ( it
->first
< startOffset
) {
488 if ( it
->second
> startOffset
)
489 throw "overlapping segment file data";
491 else if ( it
->first
> startOffset
) {
492 if ( it
->first
< endOffset
)
493 throw "overlapping segment file data";
496 throw "overlapping segment file data";
498 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
499 // check is within file bounds
500 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
501 throw "segment file data is past end of file";
503 // verify it fits in file
504 if ( startOffset
> fLength
)
505 throw "segment fileoff does not fit in file";
506 if ( endOffset
> fLength
)
507 throw "segment fileoff+filesize does not fit in file";
509 // record special segments
510 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
511 linkEditSegment
= segCmd
;
512 else if ( strcmp(segCmd
->segname(), "__UNIXSTACK") == 0 )
513 stackSegment
= segCmd
;
515 // cache interesting segments
516 if ( fFirstSegment
== NULL
)
517 fFirstSegment
= segCmd
;
518 if ( (fTEXTSegment
== NULL
) && (strcmp(segCmd
->segname(), "__TEXT") == 0) )
519 fTEXTSegment
= segCmd
;
520 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
521 if ( fFirstWritableSegment
== NULL
)
522 fFirstWritableSegment
= segCmd
;
523 if ( segCmd
->vmaddr() > 0x100000000ULL
)
524 fWriteableSegmentWithAddrOver4G
= true;
527 // check section ranges
528 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
529 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
530 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
531 // check all non-zero sized sections are within segment
532 if ( sect
->addr() < startAddr
)
533 throwf("section %s vm address not within segment", sect
->sectname());
534 if ( (sect
->addr()+sect
->size()) > endAddr
)
535 throwf("section %s vm address not within segment", sect
->sectname());
536 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
537 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
538 && (segCmd
->filesize() != 0)
539 && (sect
->size() != 0) ) {
540 if ( sect
->offset() < startOffset
)
541 throwf("section %s file offset not within segment", sect
->sectname());
542 if ( (sect
->offset()+sect
->size()) > endOffset
)
543 throwf("section %s file offset not within segment", sect
->sectname());
545 checkSection(segCmd
, sect
);
549 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
552 // verify there was a LINKEDIT segment
553 if ( linkEditSegment
== NULL
)
554 throw "no __LINKEDIT segment";
556 // verify there was an executable __TEXT segment and load commands are in it
557 if ( fTEXTSegment
== NULL
)
558 throw "no __TEXT segment";
559 if ( fTEXTSegment
->initprot() != (VM_PROT_READ
|VM_PROT_EXECUTE
) )
560 throw "__TEXT segment does not have r-x init permissions";
561 //if ( fTEXTSegment->maxprot() != (VM_PROT_READ|VM_PROT_EXECUTE|VM_PROT_WRITE) )
562 // throw "__TEXT segment does not have rwx max permissions";
563 if ( fTEXTSegment
->fileoff() != 0 )
564 throw "__TEXT segment does not start at mach_header";
565 if ( fTEXTSegment
->filesize() < (sizeof(macho_header
<P
>)+fHeader
->sizeofcmds()) )
566 throw "__TEXT segment smaller than load commands";
568 // verify if custom stack used, that stack is in __UNIXSTACK segment
569 if ( threadInfo
!= NULL
) {
570 pint_t initialSP
= getInitialStackPointer(threadInfo
);
571 if ( initialSP
!= 0 ) {
572 if ( stackSegment
== NULL
)
573 throw "LC_UNIXTHREAD specifics custom initial stack pointer, but no __UNIXSTACK segment";
574 if ( (initialSP
< stackSegment
->vmaddr()) || (initialSP
> (stackSegment
->vmaddr()+stackSegment
->vmsize())) )
575 throw "LC_UNIXTHREAD specifics custom initial stack pointer which does not point into __UNIXSTACK segment";
579 // verify __UNIXSTACK is zero fill
580 if ( stackSegment
!= NULL
) {
581 if ( (stackSegment
->filesize() != 0) || (stackSegment
->fileoff() != 0) )
582 throw "__UNIXSTACK is not a zero-fill segment";
583 if ( stackSegment
->vmsize() < 4096 )
584 throw "__UNIXSTACK segment is too small";
587 // verify entry point is in __TEXT segment
588 if ( threadInfo
!= NULL
) {
589 pint_t initialPC
= getEntryPoint(threadInfo
);
590 if ( (initialPC
< fTEXTSegment
->vmaddr()) || (initialPC
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
591 throwf("entry point 0x%0llX is outside __TEXT segment", (long long)initialPC
);
595 // checks for executables
596 bool isStaticExecutable
= false;
597 if ( fHeader
->filetype() == MH_EXECUTE
) {
598 isStaticExecutable
= true;
600 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
601 switch ( cmd
->cmd() ) {
602 case LC_LOAD_DYLINKER
:
603 // the existence of a dyld load command makes a executable dynamic
604 isStaticExecutable
= false;
607 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
609 if ( isStaticExecutable
) {
610 if ( fHeader
->flags() != MH_NOUNDEFS
)
611 throw "invalid bits in mach_header flags for static executable";
615 // verify encryption info
616 if ( encryption_info
!= NULL
) {
617 if ( fHeader
->filetype() != MH_EXECUTE
)
618 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
619 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
620 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
621 if ( (encryption_info
->cryptoff() % 4096) != 0 )
622 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
623 if ( (encryption_info
->cryptsize() % 4096) != 0 )
624 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
625 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
626 it
!= segmentFileOffsetRanges
.end(); ++it
) {
627 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
628 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
629 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
634 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
636 bool foundDynamicSymTab
= false;
637 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
638 switch ( cmd
->cmd() ) {
641 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
642 fSymbolCount
= symtab
->nsyms();
643 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
644 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
645 throw "symbol table not in __LINKEDIT";
646 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
647 throw "symbol table end not in __LINKEDIT";
648 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
649 throw "symbol table start not pointer aligned";
650 fStrings
= (char*)fHeader
+ symtab
->stroff();
651 fStringsEnd
= fStrings
+ symtab
->strsize();
652 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
653 throw "string pool not in __LINKEDIT";
654 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
655 throw "string pool extends beyond __LINKEDIT";
656 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
657 throw "string pool start not pointer aligned";
658 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
659 throw "string pool size not a multiple of pointer size";
664 if ( isStaticExecutable
)
665 throw "LC_DYSYMTAB should not be used in static executable";
666 foundDynamicSymTab
= true;
667 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
668 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
669 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
670 if ( fIndirectTableCount
!= 0 ) {
671 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
672 throw "indirect symbol table not in __LINKEDIT";
673 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
674 throw "indirect symbol table not in __LINKEDIT";
675 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
676 throw "indirect symbol table not pointer aligned";
678 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
679 if ( fLocalRelocationsCount
!= 0 ) {
680 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
681 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
682 throw "local relocations not in __LINKEDIT";
683 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
684 throw "local relocations not in __LINKEDIT";
685 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
686 throw "local relocations table not pointer aligned";
688 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
689 if ( fExternalRelocationsCount
!= 0 ) {
690 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
691 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
692 throw "external relocations not in __LINKEDIT";
693 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
694 throw "external relocations not in __LINKEDIT";
695 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
696 throw "external relocations table not pointer aligned";
700 case LC_SEGMENT_SPLIT_INFO
:
702 if ( isStaticExecutable
)
703 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
704 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
705 if ( info
->dataoff() < linkEditSegment
->fileoff() )
706 throw "split seg info not in __LINKEDIT";
707 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
708 throw "split seg info not in __LINKEDIT";
709 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
710 throw "split seg info table not pointer aligned";
711 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
712 throw "split seg info size not a multiple of pointer size";
715 case LC_FUNCTION_STARTS
:
717 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
718 if ( info
->dataoff() < linkEditSegment
->fileoff() )
719 throw "function starts data not in __LINKEDIT";
720 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
721 throw "function starts data not in __LINKEDIT";
722 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
723 throw "function starts data table not pointer aligned";
724 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
725 throw "function starts data size not a multiple of pointer size";
729 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
731 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
732 throw "missing dynamic symbol table";
733 if ( fStrings
== NULL
)
734 throw "missing symbol table";
738 template <typename A
>
739 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
741 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
742 if ( sectionType
== S_ZEROFILL
) {
743 if ( sect
->offset() != 0 )
744 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
747 // check section's segment name matches segment
748 // if ( strncmp(sect->segname(), segCmd->segname(), 16) != 0 )
749 // throwf("section %s in segment %s has wrong segment name", sect->sectname(), segCmd->segname());
751 // more section tests here
757 template <typename A
>
758 void MachOChecker
<A
>::checkIndirectSymbolTable()
760 // static executables don't have indirect symbol table
761 if ( fDynamicSymbolTable
== NULL
)
763 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
764 const uint32_t cmd_count
= fHeader
->ncmds();
765 const macho_load_command
<P
>* cmd
= cmds
;
766 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
767 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
768 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
769 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
770 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
771 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
772 // make sure all magic sections that use indirect symbol table fit within it
774 uint32_t elementSize
= 0;
775 switch ( sect
->flags() & SECTION_TYPE
) {
777 elementSize
= sect
->reserved2();
778 start
= sect
->reserved1();
780 case S_LAZY_SYMBOL_POINTERS
:
781 case S_NON_LAZY_SYMBOL_POINTERS
:
782 elementSize
= sizeof(pint_t
);
783 start
= sect
->reserved1();
786 if ( elementSize
!= 0 ) {
787 uint32_t count
= sect
->size() / elementSize
;
788 if ( (count
*elementSize
) != sect
->size() )
789 throwf("%s section size is not an even multiple of element size", sect
->sectname());
790 if ( (start
+count
) > fIndirectTableCount
)
791 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
795 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
802 template <typename A
>
803 void MachOChecker
<A
>::checkSymbolTable()
805 // verify no duplicate external symbol names
806 if ( fDynamicSymbolTable
!= NULL
) {
807 StringSet externalNames
;
808 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
809 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
810 int i
= fDynamicSymbolTable
->iextdefsym();
811 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
812 const char* symName
= &fStrings
[p
->n_strx()];
813 if ( symName
> fStringsEnd
)
814 throw "string index out of range";
815 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
816 if ( externalNames
.find(symName
) != externalNames
.end() )
817 throwf("duplicate external symbol: %s", symName
);
818 if ( (p
->n_type() & N_EXT
) == 0 )
819 throwf("non-external symbol in external symbol range: %s", symName
);
820 // don't add N_INDR to externalNames because there is likely an undefine with same name
821 if ( (p
->n_type() & N_INDR
) == 0 )
822 externalNames
.insert(symName
);
824 // verify no undefines with same name as an external symbol
825 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
826 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
827 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
828 const char* symName
= &fStrings
[p
->n_strx()];
829 if ( symName
> fStringsEnd
)
830 throw "string index out of range";
831 if ( externalNames
.find(symName
) != externalNames
.end() )
832 throwf("undefine with same name as external symbol: %s", symName
);
834 // verify all N_SECT values are valid
835 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
836 uint8_t type
= p
->n_type();
837 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
838 if ( p
->n_sect() > fSectionCount
) {
839 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
847 template <typename A
>
848 void MachOChecker
<A
>::checkInitTerms()
850 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
851 const uint32_t cmd_count
= fHeader
->ncmds();
852 const macho_load_command
<P
>* cmd
= cmds
;
853 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
854 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
855 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
856 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
857 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
858 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
859 // make sure all magic sections that use indirect symbol table fit within it
863 const char* kind
= "initializer";
864 switch ( sect
->flags() & SECTION_TYPE
) {
865 case S_MOD_TERM_FUNC_POINTERS
:
868 case S_MOD_INIT_FUNC_POINTERS
:
869 count
= sect
->size() / sizeof(pint_t
);
870 if ( (count
*sizeof(pint_t
)) != sect
->size() )
871 throwf("%s section size is not an even multiple of element size", sect
->sectname());
872 if ( (sect
->addr() % sizeof(pint_t
)) != 0 )
873 throwf("%s section size is not pointer size aligned", sect
->sectname());
874 // check each pointer in array points within TEXT
875 arrayStart
= (pint_t
*)((char*)fHeader
+ sect
->offset());
876 arrayEnd
= (pint_t
*)((char*)fHeader
+ sect
->offset() + sect
->size());
877 for (pint_t
* p
=arrayStart
; p
< arrayEnd
; ++p
) {
878 pint_t pointer
= P::getP(*p
);
879 if ( (pointer
< fTEXTSegment
->vmaddr()) || (pointer
>= (fTEXTSegment
->vmaddr()+fTEXTSegment
->vmsize())) )
880 throwf("%s 0x%08llX points outside __TEXT segment", kind
, (long long)pointer
);
882 // check each pointer in array will be rebased and not bound
883 if ( fSlidableImage
) {
884 pint_t sectionBeginAddr
= sect
->addr();
885 pint_t sectionEndddr
= sect
->addr() + sect
->size();
886 for(pint_t addr
= sectionBeginAddr
; addr
< sectionEndddr
; addr
+= sizeof(pint_t
)) {
887 if ( addressIsBindingSite(addr
) )
888 throwf("%s at 0x%0llX has binding to external symbol", kind
, (long long)addr
);
889 if ( ! addressIsRebaseSite(addr
) )
890 throwf("%s at 0x%0llX is not rebased", kind
, (long long)addr
);
897 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
904 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
906 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
907 return fFirstWritableSegment
->vmaddr();
909 return fFirstSegment
->vmaddr();
913 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
915 if ( fWriteableSegmentWithAddrOver4G
)
916 return fFirstWritableSegment
->vmaddr();
918 return fFirstSegment
->vmaddr();
922 x86::P::uint_t MachOChecker
<x86
>::relocBase()
924 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
925 return fFirstWritableSegment
->vmaddr();
927 return fFirstSegment
->vmaddr();
931 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
933 // check for split-seg
934 return fFirstWritableSegment
->vmaddr();
938 arm::P::uint_t MachOChecker
<arm
>::relocBase()
940 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
941 return fFirstWritableSegment
->vmaddr();
943 return fFirstSegment
->vmaddr();
947 template <typename A
>
948 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
950 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
951 const uint32_t cmd_count
= fHeader
->ncmds();
952 const macho_load_command
<P
>* cmd
= cmds
;
953 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
954 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
955 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
956 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
957 // if segment is writable, we are fine
958 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
960 // could be a text reloc, make sure section bit is set
961 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
962 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
963 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
964 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
965 // found section for this address, if has relocs we are fine
966 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
971 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
978 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
980 if ( reloc
->r_length() != 2 )
981 throw "bad external relocation length";
982 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
983 throw "unknown external relocation type";
984 if ( reloc
->r_pcrel() != 0 )
985 throw "bad external relocation pc_rel";
986 if ( reloc
->r_extern() == 0 )
987 throw "local relocation found with external relocations";
988 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
989 throw "external relocation address not in writable segment";
990 // FIX: check r_symbol
994 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
996 if ( reloc
->r_length() != 3 )
997 throw "bad external relocation length";
998 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
999 throw "unknown external relocation type";
1000 if ( reloc
->r_pcrel() != 0 )
1001 throw "bad external relocation pc_rel";
1002 if ( reloc
->r_extern() == 0 )
1003 throw "local relocation found with external relocations";
1004 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1005 throw "external relocation address not in writable segment";
1006 // FIX: check r_symbol
1010 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1012 if ( reloc
->r_length() != 2 )
1013 throw "bad external relocation length";
1014 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1015 throw "unknown external relocation type";
1016 if ( reloc
->r_pcrel() != 0 )
1017 throw "bad external relocation pc_rel";
1018 if ( reloc
->r_extern() == 0 )
1019 throw "local relocation found with external relocations";
1020 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1021 throw "external relocation address not in writable segment";
1022 // FIX: check r_symbol
1027 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1029 if ( reloc
->r_length() != 3 )
1030 throw "bad external relocation length";
1031 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1032 throw "unknown external relocation type";
1033 if ( reloc
->r_pcrel() != 0 )
1034 throw "bad external relocation pc_rel";
1035 if ( reloc
->r_extern() == 0 )
1036 throw "local relocation found with external relocations";
1037 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1038 throw "exernal relocation address not in writable segment";
1039 // FIX: check r_symbol
1043 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
1045 if ( reloc
->r_length() != 2 )
1046 throw "bad external relocation length";
1047 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
1048 throw "unknown external relocation type";
1049 if ( reloc
->r_pcrel() != 0 )
1050 throw "bad external relocation pc_rel";
1051 if ( reloc
->r_extern() == 0 )
1052 throw "local relocation found with external relocations";
1053 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1054 throw "external relocation address not in writable segment";
1055 // FIX: check r_symbol
1060 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1062 if ( reloc
->r_address() & R_SCATTERED
) {
1064 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1070 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1071 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
1077 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1079 if ( reloc
->r_length() != 3 )
1080 throw "bad local relocation length";
1081 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
1082 throw "unknown local relocation type";
1083 if ( reloc
->r_pcrel() != 0 )
1084 throw "bad local relocation pc_rel";
1085 if ( reloc
->r_extern() != 0 )
1086 throw "external relocation found with local relocations";
1087 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1088 throw "local relocation address not in writable segment";
1092 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1098 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1100 if ( reloc
->r_length() != 3 )
1101 throw "bad local relocation length";
1102 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
1103 throw "unknown local relocation type";
1104 if ( reloc
->r_pcrel() != 0 )
1105 throw "bad local relocation pc_rel";
1106 if ( reloc
->r_extern() != 0 )
1107 throw "external relocation found with local relocations";
1108 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1109 throw "local relocation address not in writable segment";
1113 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
1115 if ( reloc
->r_address() & R_SCATTERED
) {
1117 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
1118 if ( sreloc
->r_length() != 2 )
1119 throw "bad local scattered relocation length";
1120 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
1121 throw "bad local scattered relocation type";
1124 if ( reloc
->r_length() != 2 )
1125 throw "bad local relocation length";
1126 if ( reloc
->r_extern() != 0 )
1127 throw "external relocation found with local relocations";
1128 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
1129 throw "local relocation address not in writable segment";
1133 template <typename A
>
1134 void MachOChecker
<A
>::checkRelocations()
1136 // external relocations should be sorted to minimize dyld symbol lookups
1137 // therefore every reloc with the same r_symbolnum value should be contiguous
1138 std::set
<uint32_t> previouslySeenSymbolIndexes
;
1139 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
1140 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1141 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1142 this->checkExternalReloation(reloc
);
1143 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
1144 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
1145 throw "external relocations not sorted";
1146 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
1147 lastSymbolIndex
= reloc
->r_symbolnum();
1151 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1152 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1153 this->checkLocalReloation(reloc
);
1156 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
1157 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
1158 const uint32_t cmd_count
= fHeader
->ncmds();
1159 const macho_load_command
<P
>* cmd
= cmds
;
1160 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
1161 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
1162 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
1163 // if segment is writable, we are fine
1164 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
1166 // look at sections that have text reloc bit set
1167 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
1168 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
1169 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
1170 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
1171 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
1172 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
1177 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
1181 template <typename A
>
1182 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
1184 if ( segIndex
> fSegments
.size() )
1185 throw "segment index out of range";
1186 return fSegments
[segIndex
]->vmaddr();
1189 template <typename A
>
1190 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
1192 // look at local relocs
1193 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1194 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1195 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1196 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
1200 if ( fDyldInfo
!= NULL
) {
1201 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1202 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1205 uint64_t segOffset
= 0;
1209 pint_t segStartAddr
= 0;
1212 while ( !done
&& (p
< end
) ) {
1213 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1214 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1217 case REBASE_OPCODE_DONE
:
1220 case REBASE_OPCODE_SET_TYPE_IMM
:
1223 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1224 segIndex
= immediate
;
1225 segStartAddr
= segStartAddress(segIndex
);
1226 segOffset
= read_uleb128(p
, end
);
1228 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1229 segOffset
+= read_uleb128(p
, end
);
1231 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1232 segOffset
+= immediate
*sizeof(pint_t
);
1234 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1235 for (int i
=0; i
< immediate
; ++i
) {
1236 addr
= segStartAddr
+segOffset
;
1237 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1239 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1240 segOffset
+= sizeof(pint_t
);
1243 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1244 count
= read_uleb128(p
, end
);
1245 for (uint32_t i
=0; i
< count
; ++i
) {
1246 addr
= segStartAddr
+segOffset
;
1247 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1249 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1250 segOffset
+= sizeof(pint_t
);
1253 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1254 addr
= segStartAddr
+segOffset
;
1255 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1257 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1258 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1260 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1261 count
= read_uleb128(p
, end
);
1262 skip
= read_uleb128(p
, end
);
1263 for (uint32_t i
=0; i
< count
; ++i
) {
1264 addr
= segStartAddr
+segOffset
;
1265 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1267 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1268 segOffset
+= skip
+ sizeof(pint_t
);
1272 throwf("bad rebase opcode %d", *p
);
1278 template <typename A
>
1279 bool MachOChecker
<A
>::addressIsRebaseSite(pint_t targetAddr
)
1281 // look at local relocs
1282 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
1283 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
1284 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1285 if ( relocAddress
== targetAddr
)
1289 if ( fDyldInfo
!= NULL
) {
1290 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
1291 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
1294 uint64_t segOffset
= 0;
1298 pint_t segStartAddr
= 0;
1301 while ( !done
&& (p
< end
) ) {
1302 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
1303 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
1306 case REBASE_OPCODE_DONE
:
1309 case REBASE_OPCODE_SET_TYPE_IMM
:
1312 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1313 segIndex
= immediate
;
1314 segStartAddr
= segStartAddress(segIndex
);
1315 segOffset
= read_uleb128(p
, end
);
1317 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1318 segOffset
+= read_uleb128(p
, end
);
1320 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1321 segOffset
+= immediate
*sizeof(pint_t
);
1323 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1324 for (int i
=0; i
< immediate
; ++i
) {
1325 addr
= segStartAddr
+segOffset
;
1326 if ( addr
== targetAddr
)
1328 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1329 segOffset
+= sizeof(pint_t
);
1332 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1333 count
= read_uleb128(p
, end
);
1334 for (uint32_t i
=0; i
< count
; ++i
) {
1335 addr
= segStartAddr
+segOffset
;
1336 if ( addr
== targetAddr
)
1338 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1339 segOffset
+= sizeof(pint_t
);
1342 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1343 addr
= segStartAddr
+segOffset
;
1344 if ( addr
== targetAddr
)
1346 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1347 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1349 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1350 count
= read_uleb128(p
, end
);
1351 skip
= read_uleb128(p
, end
);
1352 for (uint32_t i
=0; i
< count
; ++i
) {
1353 addr
= segStartAddr
+segOffset
;
1354 if ( addr
== targetAddr
)
1356 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1357 segOffset
+= skip
+ sizeof(pint_t
);
1361 throwf("bad rebase opcode %d", *p
);
1369 template <typename A
>
1370 bool MachOChecker
<A
>::addressIsBindingSite(pint_t targetAddr
)
1372 // look at external relocs
1373 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
1374 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
1375 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
1376 if ( relocAddress
== targetAddr
)
1380 if ( fDyldInfo
!= NULL
) {
1381 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->bind_off();
1382 const uint8_t* end
= &p
[fDyldInfo
->bind_size()];
1385 uint64_t segOffset
= 0;
1389 const char* symbolName
= NULL
;
1390 int libraryOrdinal
= 0;
1393 pint_t segStartAddr
= 0;
1396 while ( !done
&& (p
< end
) ) {
1397 uint8_t immediate
= *p
& BIND_IMMEDIATE_MASK
;
1398 uint8_t opcode
= *p
& BIND_OPCODE_MASK
;
1401 case BIND_OPCODE_DONE
:
1404 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
:
1405 libraryOrdinal
= immediate
;
1407 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
:
1408 libraryOrdinal
= read_uleb128(p
, end
);
1410 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
:
1411 // the special ordinals are negative numbers
1412 if ( immediate
== 0 )
1415 int8_t signExtended
= BIND_OPCODE_MASK
| immediate
;
1416 libraryOrdinal
= signExtended
;
1419 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
:
1420 symbolName
= (char*)p
;
1425 case BIND_OPCODE_SET_TYPE_IMM
:
1428 case BIND_OPCODE_SET_ADDEND_SLEB
:
1429 addend
= read_sleb128(p
, end
);
1431 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1432 segIndex
= immediate
;
1433 segStartAddr
= segStartAddress(segIndex
);
1434 segOffset
= read_uleb128(p
, end
);
1436 case BIND_OPCODE_ADD_ADDR_ULEB
:
1437 segOffset
+= read_uleb128(p
, end
);
1439 case BIND_OPCODE_DO_BIND
:
1440 if ( (segStartAddr
+segOffset
) == targetAddr
)
1442 segOffset
+= sizeof(pint_t
);
1444 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
:
1445 if ( (segStartAddr
+segOffset
) == targetAddr
)
1447 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1449 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
:
1450 if ( (segStartAddr
+segOffset
) == targetAddr
)
1452 segOffset
+= immediate
*sizeof(pint_t
) + sizeof(pint_t
);
1454 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
:
1455 count
= read_uleb128(p
, end
);
1456 skip
= read_uleb128(p
, end
);
1457 for (uint32_t i
=0; i
< count
; ++i
) {
1458 if ( (segStartAddr
+segOffset
) == targetAddr
)
1460 segOffset
+= skip
+ sizeof(pint_t
);
1464 throwf("bad bind opcode %d", *p
);
1472 static void check(const char* path
)
1474 struct stat stat_buf
;
1477 int fd
= ::open(path
, O_RDONLY
, 0);
1479 throw "cannot open file";
1480 if ( ::fstat(fd
, &stat_buf
) != 0 )
1481 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1482 uint32_t length
= stat_buf
.st_size
;
1483 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1484 if ( p
== ((uint8_t*)(-1)) )
1485 throw "cannot map file";
1487 const mach_header
* mh
= (mach_header
*)p
;
1488 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1489 const struct fat_header
* fh
= (struct fat_header
*)p
;
1490 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1491 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1492 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1493 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1494 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1497 case CPU_TYPE_POWERPC
:
1498 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
1499 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
1501 throw "in universal file, ppc slice does not contain ppc mach-o";
1504 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1505 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
1507 throw "in universal file, i386 slice does not contain i386 mach-o";
1509 case CPU_TYPE_POWERPC64
:
1510 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
1511 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
1513 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
1515 case CPU_TYPE_X86_64
:
1516 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1517 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
1519 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1522 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1523 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
1525 throw "in universal file, arm slice does not contain arm mach-o";
1528 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1532 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1533 MachOChecker
<x86
>::make(p
, length
, path
);
1535 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
1536 MachOChecker
<ppc
>::make(p
, length
, path
);
1538 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
1539 MachOChecker
<ppc64
>::make(p
, length
, path
);
1541 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1542 MachOChecker
<x86_64
>::make(p
, length
, path
);
1544 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1545 MachOChecker
<arm
>::make(p
, length
, path
);
1548 throw "not a known file type";
1551 catch (const char* msg
) {
1552 throwf("%s in %s", msg
, path
);
1557 int main(int argc
, const char* argv
[])
1559 bool progress
= false;
1561 for(int i
=1; i
< argc
; ++i
) {
1562 const char* arg
= argv
[i
];
1563 if ( arg
[0] == '-' ) {
1564 if ( strcmp(arg
, "-progress") == 0 ) {
1568 throwf("unknown option: %s\n", arg
);
1572 bool success
= true;
1576 catch (const char* msg
) {
1577 fprintf(stderr
, "machocheck failed: %s %s\n", arg
, msg
);
1581 if ( success
&& progress
)
1582 printf("ok: %s\n", arg
);