1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2007 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
);
60 static bool validFile(const uint8_t* fileContent
);
61 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
62 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
63 virtual ~MachOChecker() {}
67 typedef typename
A::P P
;
68 typedef typename
A::P::E E
;
69 typedef typename
A::P::uint_t pint_t
;
74 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
77 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
79 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
80 void checkMachHeader();
81 void checkLoadCommands();
82 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
83 uint8_t loadCommandSizeMask();
84 void checkSymbolTable();
85 void checkIndirectSymbolTable();
86 void checkRelocations();
87 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
88 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
90 bool addressInWritableSegment(pint_t address
);
93 const macho_header
<P
>* fHeader
;
96 const char* fStringsEnd
;
97 const macho_nlist
<P
>* fSymbols
;
98 uint32_t fSymbolCount
;
99 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
100 const uint32_t* fIndirectTable
;
101 uint32_t fIndirectTableCount
;
102 const macho_relocation_info
<P
>* fLocalRelocations
;
103 uint32_t fLocalRelocationsCount
;
104 const macho_relocation_info
<P
>* fExternalRelocations
;
105 uint32_t fExternalRelocationsCount
;
106 bool fWriteableSegmentWithAddrOver4G
;
107 const macho_segment_command
<P
>* fFirstSegment
;
108 const macho_segment_command
<P
>* fFirstWritableSegment
;
114 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
116 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
117 if ( header
->magic() != MH_MAGIC
)
119 if ( header
->cputype() != CPU_TYPE_POWERPC
)
121 switch (header
->filetype()) {
132 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
134 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
135 if ( header
->magic() != MH_MAGIC_64
)
137 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
139 switch (header
->filetype()) {
150 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
152 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
153 if ( header
->magic() != MH_MAGIC
)
155 if ( header
->cputype() != CPU_TYPE_I386
)
157 switch (header
->filetype()) {
168 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
170 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
171 if ( header
->magic() != MH_MAGIC_64
)
173 if ( header
->cputype() != CPU_TYPE_X86_64
)
175 switch (header
->filetype()) {
186 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
188 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
189 if ( header
->magic() != MH_MAGIC
)
191 if ( header
->cputype() != CPU_TYPE_ARM
)
193 switch (header
->filetype()) {
203 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
204 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
205 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
206 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
207 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
209 template <typename A
>
210 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
211 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
212 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
213 fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
)
216 if ( ! validFile(fileContent
) )
217 throw "not a mach-o file that can be checked";
219 fPath
= strdup(path
);
220 fHeader
= (const macho_header
<P
>*)fileContent
;
222 // sanity check header
225 // check load commands
228 checkIndirectSymbolTable();
236 template <typename A
>
237 void MachOChecker
<A
>::checkMachHeader()
239 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
240 throw "sizeofcmds in mach_header is larger than file";
242 uint32_t flags
= fHeader
->flags();
243 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFFC00000;
244 if ( flags
& invalidBits
)
245 throw "invalid bits in mach_header flags";
246 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
247 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
250 template <typename A
>
251 void MachOChecker
<A
>::checkLoadCommands()
253 // check that all load commands fit within the load command space file
254 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
255 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
256 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
257 const uint32_t cmd_count
= fHeader
->ncmds();
258 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
259 const macho_load_command
<P
>* cmd
= cmds
;
260 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
261 uint32_t size
= cmd
->cmdsize();
262 if ( (size
& this->loadCommandSizeMask()) != 0 )
263 throwf("load command #%d has a unaligned size", i
);
264 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
265 if ( endOfCmd
> endOfLoadCommands
)
266 throwf("load command #%d extends beyond the end of the load commands", i
);
267 if ( endOfCmd
> endOfFile
)
268 throwf("load command #%d extends beyond the end of the file", i
);
269 switch ( cmd
->cmd() ) {
270 case macho_segment_command
<P
>::CMD
:
276 case LC_LOAD_DYLINKER
:
278 case macho_routines_command
<P
>::CMD
:
279 case LC_SUB_FRAMEWORK
:
281 case LC_TWOLEVEL_HINTS
:
282 case LC_PREBIND_CKSUM
:
283 case LC_LOAD_WEAK_DYLIB
:
284 case LC_LAZY_LOAD_DYLIB
:
286 case LC_REEXPORT_DYLIB
:
287 case LC_SEGMENT_SPLIT_INFO
:
288 case LC_CODE_SIGNATURE
:
290 case LC_DYLD_INFO_ONLY
:
292 case LC_ENCRYPTION_INFO
:
293 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
295 case LC_SUB_UMBRELLA
:
297 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
298 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";
301 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
303 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
308 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
309 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
310 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
311 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
312 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
313 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
314 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
315 throw "invalid segment load command size";
317 // see if this overlaps another segment address range
318 uint64_t startAddr
= segCmd
->vmaddr();
319 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
320 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
321 if ( it
->first
< startAddr
) {
322 if ( it
->second
> startAddr
)
323 throw "overlapping segment vm addresses";
325 else if ( it
->first
> startAddr
) {
326 if ( it
->first
< endAddr
)
327 throw "overlapping segment vm addresses";
330 throw "overlapping segment vm addresses";
332 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
334 // see if this overlaps another segment file offset range
335 uint64_t startOffset
= segCmd
->fileoff();
336 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
337 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
338 if ( it
->first
< startOffset
) {
339 if ( it
->second
> startOffset
)
340 throw "overlapping segment file data";
342 else if ( it
->first
> startOffset
) {
343 if ( it
->first
< endOffset
)
344 throw "overlapping segment file data";
347 throw "overlapping segment file data";
349 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
350 // check is within file bounds
351 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
352 throw "segment file data is past end of file";
354 // verify it fits in file
355 if ( startOffset
> fLength
)
356 throw "segment fileoff does not fit in file";
357 if ( endOffset
> fLength
)
358 throw "segment fileoff+filesize does not fit in file";
360 // keep LINKEDIT segment
361 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
362 linkEditSegment
= segCmd
;
364 // cache interesting segments
365 if ( fFirstSegment
== NULL
)
366 fFirstSegment
= segCmd
;
367 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
368 if ( fFirstWritableSegment
== NULL
)
369 fFirstWritableSegment
= segCmd
;
370 if ( segCmd
->vmaddr() > 0x100000000ULL
)
371 fWriteableSegmentWithAddrOver4G
= true;
374 // check section ranges
375 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
376 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
377 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
378 // check all sections are within segment
379 if ( sect
->addr() < startAddr
)
380 throwf("section %s vm address not within segment", sect
->sectname());
381 if ( (sect
->addr()+sect
->size()) > endAddr
)
382 throwf("section %s vm address not within segment", sect
->sectname());
383 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
) && (segCmd
->filesize() != 0) ) {
384 if ( sect
->offset() < startOffset
)
385 throwf("section %s file offset not within segment", sect
->sectname());
386 if ( (sect
->offset()+sect
->size()) > endOffset
)
387 throwf("section %s file offset not within segment", sect
->sectname());
389 checkSection(segCmd
, sect
);
392 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
395 // verify there was a LINKEDIT segment
396 if ( linkEditSegment
== NULL
)
397 throw "no __LINKEDIT segment";
399 // checks for executables
400 bool isStaticExecutable
= false;
401 if ( fHeader
->filetype() == MH_EXECUTE
) {
402 isStaticExecutable
= true;
404 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
405 switch ( cmd
->cmd() ) {
406 case LC_LOAD_DYLINKER
:
407 // the existence of a dyld load command makes a executable dynamic
408 isStaticExecutable
= false;
411 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
413 if ( isStaticExecutable
) {
414 if ( fHeader
->flags() != MH_NOUNDEFS
)
415 throw "invalid bits in mach_header flags for static executable";
419 // verify encryption info
420 if ( encryption_info
!= NULL
) {
421 if ( fHeader
->filetype() != MH_EXECUTE
)
422 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
423 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
424 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
425 if ( (encryption_info
->cryptoff() % 4096) != 0 )
426 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
427 if ( (encryption_info
->cryptsize() % 4096) != 0 )
428 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
429 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
430 it
!= segmentFileOffsetRanges
.end(); ++it
) {
431 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
432 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
433 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
438 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
440 bool foundDynamicSymTab
= false;
441 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
442 switch ( cmd
->cmd() ) {
445 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
446 fSymbolCount
= symtab
->nsyms();
447 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
448 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
449 throw "symbol table not in __LINKEDIT";
450 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
451 throw "symbol table end not in __LINKEDIT";
452 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
453 throw "symbol table start not pointer aligned";
454 fStrings
= (char*)fHeader
+ symtab
->stroff();
455 fStringsEnd
= fStrings
+ symtab
->strsize();
456 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
457 throw "string pool not in __LINKEDIT";
458 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
459 throw "string pool extends beyond __LINKEDIT";
460 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
461 throw "string pool start not pointer aligned";
462 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
463 throw "string pool size not a multiple of pointer size";
468 if ( isStaticExecutable
)
469 throw "LC_DYSYMTAB should not be used in static executable";
470 foundDynamicSymTab
= true;
471 fDynamicSymbolTable
= (struct macho_dysymtab_command
<P
>*)cmd
;
472 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
473 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
474 if ( fIndirectTableCount
!= 0 ) {
475 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
476 throw "indirect symbol table not in __LINKEDIT";
477 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
478 throw "indirect symbol table not in __LINKEDIT";
479 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
480 throw "indirect symbol table not pointer aligned";
482 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
483 if ( fLocalRelocationsCount
!= 0 ) {
484 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
485 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
486 throw "local relocations not in __LINKEDIT";
487 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
488 throw "local relocations not in __LINKEDIT";
489 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
490 throw "local relocations table not pointer aligned";
492 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
493 if ( fExternalRelocationsCount
!= 0 ) {
494 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
495 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
496 throw "external relocations not in __LINKEDIT";
497 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
498 throw "external relocations not in __LINKEDIT";
499 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
500 throw "external relocations table not pointer aligned";
504 case LC_SEGMENT_SPLIT_INFO
:
506 if ( isStaticExecutable
)
507 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
508 const macho_linkedit_data_command
<P
>* info
= (struct macho_linkedit_data_command
<P
>*)cmd
;
509 if ( info
->dataoff() < linkEditSegment
->fileoff() )
510 throw "split seg info not in __LINKEDIT";
511 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
512 throw "split seg info not in __LINKEDIT";
513 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
514 throw "split seg info table not pointer aligned";
515 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
516 throw "split seg info size not a multiple of pointer size";
520 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
522 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
523 throw "missing dynamic symbol table";
524 if ( fStrings
== NULL
)
525 throw "missing symbol table";
529 template <typename A
>
530 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
532 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
533 if ( sectionType
== S_ZEROFILL
) {
534 if ( sect
->offset() != 0 )
535 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
538 // more section tests here
541 template <typename A
>
542 void MachOChecker
<A
>::checkIndirectSymbolTable()
544 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
545 const uint32_t cmd_count
= fHeader
->ncmds();
546 const macho_load_command
<P
>* cmd
= cmds
;
547 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
548 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
549 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
550 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
551 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
552 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
553 // make sure all magic sections that use indirect symbol table fit within it
555 uint32_t elementSize
= 0;
556 switch ( sect
->flags() & SECTION_TYPE
) {
558 elementSize
= sect
->reserved2();
559 start
= sect
->reserved1();
561 case S_LAZY_SYMBOL_POINTERS
:
562 case S_NON_LAZY_SYMBOL_POINTERS
:
563 elementSize
= sizeof(pint_t
);
564 start
= sect
->reserved1();
567 if ( elementSize
!= 0 ) {
568 uint32_t count
= sect
->size() / elementSize
;
569 if ( (count
*elementSize
) != sect
->size() )
570 throwf("%s section size is not an even multiple of element size", sect
->sectname());
571 if ( (start
+count
) > fIndirectTableCount
)
572 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
576 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
581 template <typename A
>
582 void MachOChecker
<A
>::checkSymbolTable()
584 // verify no duplicate external symbol names
585 if ( fDynamicSymbolTable
!= NULL
) {
586 StringSet externalNames
;
587 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
588 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
589 int i
= fDynamicSymbolTable
->iextdefsym();
590 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
591 const char* symName
= &fStrings
[p
->n_strx()];
592 if ( symName
> fStringsEnd
)
593 throw "string index out of range";
594 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
595 if ( externalNames
.find(symName
) != externalNames
.end() )
596 throwf("duplicate external symbol: %s", symName
);
597 externalNames
.insert(symName
);
599 // verify no undefines with same name as an external symbol
600 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
601 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
602 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
603 const char* symName
= &fStrings
[p
->n_strx()];
604 if ( symName
> fStringsEnd
)
605 throw "string index out of range";
606 if ( externalNames
.find(symName
) != externalNames
.end() )
607 throwf("undefine with same name as external symbol: %s", symName
);
614 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
616 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
617 return fFirstWritableSegment
->vmaddr();
619 return fFirstSegment
->vmaddr();
623 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
625 if ( fWriteableSegmentWithAddrOver4G
)
626 return fFirstWritableSegment
->vmaddr();
628 return fFirstSegment
->vmaddr();
632 x86::P::uint_t MachOChecker
<x86
>::relocBase()
634 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
635 return fFirstWritableSegment
->vmaddr();
637 return fFirstSegment
->vmaddr();
641 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
643 // check for split-seg
644 return fFirstWritableSegment
->vmaddr();
648 arm::P::uint_t MachOChecker
<arm
>::relocBase()
650 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
651 return fFirstWritableSegment
->vmaddr();
653 return fFirstSegment
->vmaddr();
657 template <typename A
>
658 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
660 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
661 const uint32_t cmd_count
= fHeader
->ncmds();
662 const macho_load_command
<P
>* cmd
= cmds
;
663 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
664 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
665 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
666 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
667 // if segment is writable, we are fine
668 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
670 // could be a text reloc, make sure section bit is set
671 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
672 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
673 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
674 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
675 // found section for this address, if has relocs we are fine
676 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
681 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
688 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
690 if ( reloc
->r_length() != 2 )
691 throw "bad external relocation length";
692 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
693 throw "unknown external relocation type";
694 if ( reloc
->r_pcrel() != 0 )
695 throw "bad external relocation pc_rel";
696 if ( reloc
->r_extern() == 0 )
697 throw "local relocation found with external relocations";
698 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
699 throw "external relocation address not in writable segment";
700 // FIX: check r_symbol
704 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
706 if ( reloc
->r_length() != 3 )
707 throw "bad external relocation length";
708 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
709 throw "unknown external relocation type";
710 if ( reloc
->r_pcrel() != 0 )
711 throw "bad external relocation pc_rel";
712 if ( reloc
->r_extern() == 0 )
713 throw "local relocation found with external relocations";
714 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
715 throw "external relocation address not in writable segment";
716 // FIX: check r_symbol
720 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
722 if ( reloc
->r_length() != 2 )
723 throw "bad external relocation length";
724 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
725 throw "unknown external relocation type";
726 if ( reloc
->r_pcrel() != 0 )
727 throw "bad external relocation pc_rel";
728 if ( reloc
->r_extern() == 0 )
729 throw "local relocation found with external relocations";
730 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
731 throw "external relocation address not in writable segment";
732 // FIX: check r_symbol
737 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
739 if ( reloc
->r_length() != 3 )
740 throw "bad external relocation length";
741 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
742 throw "unknown external relocation type";
743 if ( reloc
->r_pcrel() != 0 )
744 throw "bad external relocation pc_rel";
745 if ( reloc
->r_extern() == 0 )
746 throw "local relocation found with external relocations";
747 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
748 throw "exernal relocation address not in writable segment";
749 // FIX: check r_symbol
753 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
755 if ( reloc
->r_length() != 2 )
756 throw "bad external relocation length";
757 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
758 throw "unknown external relocation type";
759 if ( reloc
->r_pcrel() != 0 )
760 throw "bad external relocation pc_rel";
761 if ( reloc
->r_extern() == 0 )
762 throw "local relocation found with external relocations";
763 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
764 throw "external relocation address not in writable segment";
765 // FIX: check r_symbol
770 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
772 if ( reloc
->r_address() & R_SCATTERED
) {
774 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
779 // ignore pair relocs
780 if ( reloc
->r_type() == PPC_RELOC_PAIR
)
783 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
784 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
790 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
792 if ( reloc
->r_length() != 3 )
793 throw "bad local relocation length";
794 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
795 throw "unknown local relocation type";
796 if ( reloc
->r_pcrel() != 0 )
797 throw "bad local relocation pc_rel";
798 if ( reloc
->r_extern() != 0 )
799 throw "external relocation found with local relocations";
800 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
801 throw "local relocation address not in writable segment";
805 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
811 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
813 if ( reloc
->r_length() != 3 )
814 throw "bad local relocation length";
815 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
816 throw "unknown local relocation type";
817 if ( reloc
->r_pcrel() != 0 )
818 throw "bad local relocation pc_rel";
819 if ( reloc
->r_extern() != 0 )
820 throw "external relocation found with local relocations";
821 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
822 throw "local relocation address not in writable segment";
826 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
828 if ( reloc
->r_address() & R_SCATTERED
) {
830 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
831 if ( sreloc
->r_length() != 2 )
832 throw "bad local scattered relocation length";
833 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
834 throw "bad local scattered relocation type";
837 if ( reloc
->r_length() != 2 )
838 throw "bad local relocation length";
839 if ( reloc
->r_extern() != 0 )
840 throw "external relocation found with local relocations";
841 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
842 throw "local relocation address not in writable segment";
846 template <typename A
>
847 void MachOChecker
<A
>::checkRelocations()
849 // external relocations should be sorted to minimize dyld symbol lookups
850 // therefore every reloc with the same r_symbolnum value should be contiguous
851 std::set
<uint32_t> previouslySeenSymbolIndexes
;
852 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
853 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
854 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
855 this->checkExternalReloation(reloc
);
856 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
857 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
858 throw "external relocations not sorted";
859 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
860 lastSymbolIndex
= reloc
->r_symbolnum();
864 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
865 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
866 this->checkLocalReloation(reloc
);
871 static void check(const char* path
)
873 struct stat stat_buf
;
876 int fd
= ::open(path
, O_RDONLY
, 0);
878 throw "cannot open file";
879 if ( ::fstat(fd
, &stat_buf
) != 0 )
880 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
881 uint32_t length
= stat_buf
.st_size
;
882 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
883 if ( p
== ((uint8_t*)(-1)) )
884 throw "cannot map file";
886 const mach_header
* mh
= (mach_header
*)p
;
887 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
888 const struct fat_header
* fh
= (struct fat_header
*)p
;
889 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
890 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
891 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
892 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
893 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
896 case CPU_TYPE_POWERPC
:
897 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
898 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
900 throw "in universal file, ppc slice does not contain ppc mach-o";
903 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
904 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
906 throw "in universal file, i386 slice does not contain i386 mach-o";
908 case CPU_TYPE_POWERPC64
:
909 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
910 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
912 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
914 case CPU_TYPE_X86_64
:
915 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
916 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
918 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
921 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
922 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
924 throw "in universal file, arm slice does not contain arm mach-o";
927 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
931 else if ( MachOChecker
<x86
>::validFile(p
) ) {
932 MachOChecker
<x86
>::make(p
, length
, path
);
934 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
935 MachOChecker
<ppc
>::make(p
, length
, path
);
937 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
938 MachOChecker
<ppc64
>::make(p
, length
, path
);
940 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
941 MachOChecker
<x86_64
>::make(p
, length
, path
);
943 else if ( MachOChecker
<arm
>::validFile(p
) ) {
944 MachOChecker
<arm
>::make(p
, length
, path
);
947 throw "not a known file type";
950 catch (const char* msg
) {
951 throwf("%s in %s", msg
, path
);
956 int main(int argc
, const char* argv
[])
959 for(int i
=1; i
< argc
; ++i
) {
960 const char* arg
= argv
[i
];
961 if ( arg
[0] == '-' ) {
962 if ( strcmp(arg
, "-no_content") == 0 ) {
966 throwf("unknown option: %s\n", arg
);
974 catch (const char* msg
) {
975 fprintf(stderr
, "machocheck failed: %s\n", msg
);