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>
35 #include <ext/hash_set>
37 #include "MachOFileAbstraction.hpp"
38 #include "Architectures.hpp"
41 __attribute__((noreturn
))
42 void throwf(const char* format
, ...)
46 va_start(list
, format
);
47 vasprintf(&p
, format
, list
);
59 static bool validFile(const uint8_t* fileContent
);
60 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
61 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
62 virtual ~MachOChecker() {}
66 typedef typename
A::P P
;
67 typedef typename
A::P::E E
;
68 typedef typename
A::P::uint_t pint_t
;
73 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
76 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
78 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
79 void checkMachHeader();
80 void checkLoadCommands();
81 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
82 uint8_t loadCommandSizeMask();
83 void checkSymbolTable();
84 void checkIndirectSymbolTable();
85 void checkRelocations();
86 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
87 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
89 bool addressInWritableSegment(pint_t address
);
92 const macho_header
<P
>* fHeader
;
95 const char* fStringsEnd
;
96 const macho_nlist
<P
>* fSymbols
;
97 uint32_t fSymbolCount
;
98 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
99 const uint32_t* fIndirectTable
;
100 uint32_t fIndirectTableCount
;
101 const macho_relocation_info
<P
>* fLocalRelocations
;
102 uint32_t fLocalRelocationsCount
;
103 const macho_relocation_info
<P
>* fExternalRelocations
;
104 uint32_t fExternalRelocationsCount
;
105 bool fWriteableSegmentWithAddrOver4G
;
106 const macho_segment_command
<P
>* fFirstSegment
;
107 const macho_segment_command
<P
>* fFirstWritableSegment
;
113 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
115 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
116 if ( header
->magic() != MH_MAGIC
)
118 if ( header
->cputype() != CPU_TYPE_POWERPC
)
120 switch (header
->filetype()) {
131 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
133 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
134 if ( header
->magic() != MH_MAGIC_64
)
136 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
138 switch (header
->filetype()) {
149 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
151 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
152 if ( header
->magic() != MH_MAGIC
)
154 if ( header
->cputype() != CPU_TYPE_I386
)
156 switch (header
->filetype()) {
167 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
169 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
170 if ( header
->magic() != MH_MAGIC_64
)
172 if ( header
->cputype() != CPU_TYPE_X86_64
)
174 switch (header
->filetype()) {
185 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
187 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
188 if ( header
->magic() != MH_MAGIC
)
190 if ( header
->cputype() != CPU_TYPE_ARM
)
192 switch (header
->filetype()) {
202 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
203 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
204 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
205 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
206 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
208 template <typename A
>
209 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
210 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
211 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
212 fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
)
215 if ( ! validFile(fileContent
) )
216 throw "not a mach-o file that can be checked";
218 fPath
= strdup(path
);
219 fHeader
= (const macho_header
<P
>*)fileContent
;
221 // sanity check header
224 // check load commands
227 checkIndirectSymbolTable();
235 template <typename A
>
236 void MachOChecker
<A
>::checkMachHeader()
238 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
239 throw "sizeofcmds in mach_header is larger than file";
241 uint32_t flags
= fHeader
->flags();
242 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFFC00000;
243 if ( flags
& invalidBits
)
244 throw "invalid bits in mach_header flags";
245 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
246 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
249 template <typename A
>
250 void MachOChecker
<A
>::checkLoadCommands()
252 // check that all load commands fit within the load command space file
253 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
254 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
255 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
256 const uint32_t cmd_count
= fHeader
->ncmds();
257 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
258 const macho_load_command
<P
>* cmd
= cmds
;
259 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
260 uint32_t size
= cmd
->cmdsize();
261 if ( (size
& this->loadCommandSizeMask()) != 0 )
262 throwf("load command #%d has a unaligned size", i
);
263 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
264 if ( endOfCmd
> endOfLoadCommands
)
265 throwf("load command #%d extends beyond the end of the load commands", i
);
266 if ( endOfCmd
> endOfFile
)
267 throwf("load command #%d extends beyond the end of the file", i
);
268 switch ( cmd
->cmd() ) {
269 case macho_segment_command
<P
>::CMD
:
275 case LC_LOAD_DYLINKER
:
277 case macho_routines_command
<P
>::CMD
:
278 case LC_SUB_FRAMEWORK
:
280 case LC_TWOLEVEL_HINTS
:
281 case LC_PREBIND_CKSUM
:
282 case LC_LOAD_WEAK_DYLIB
:
283 case LC_LAZY_LOAD_DYLIB
:
285 case LC_REEXPORT_DYLIB
:
286 case LC_SEGMENT_SPLIT_INFO
:
287 case LC_CODE_SIGNATURE
:
289 case LC_ENCRYPTION_INFO
:
290 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
292 case LC_SUB_UMBRELLA
:
294 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
295 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";
298 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
300 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
305 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
306 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
307 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
308 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
309 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
310 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
311 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
312 throw "invalid segment load command size";
314 // see if this overlaps another segment address range
315 uint64_t startAddr
= segCmd
->vmaddr();
316 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
317 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
318 if ( it
->first
< startAddr
) {
319 if ( it
->second
> startAddr
)
320 throw "overlapping segment vm addresses";
322 else if ( it
->first
> startAddr
) {
323 if ( it
->first
< endAddr
)
324 throw "overlapping segment vm addresses";
327 throw "overlapping segment vm addresses";
329 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
331 // see if this overlaps another segment file offset range
332 uint64_t startOffset
= segCmd
->fileoff();
333 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
334 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
335 if ( it
->first
< startOffset
) {
336 if ( it
->second
> startOffset
)
337 throw "overlapping segment file data";
339 else if ( it
->first
> startOffset
) {
340 if ( it
->first
< endOffset
)
341 throw "overlapping segment file data";
344 throw "overlapping segment file data";
346 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
347 // check is within file bounds
348 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
349 throw "segment file data is past end of file";
351 // verify it fits in file
352 if ( startOffset
> fLength
)
353 throw "segment fileoff does not fit in file";
354 if ( endOffset
> fLength
)
355 throw "segment fileoff+filesize does not fit in file";
357 // keep LINKEDIT segment
358 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
359 linkEditSegment
= segCmd
;
361 // cache interesting segments
362 if ( fFirstSegment
== NULL
)
363 fFirstSegment
= segCmd
;
364 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
365 if ( fFirstWritableSegment
== NULL
)
366 fFirstWritableSegment
= segCmd
;
367 if ( segCmd
->vmaddr() > 0x100000000ULL
)
368 fWriteableSegmentWithAddrOver4G
= true;
371 // check section ranges
372 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
373 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
374 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
375 // check all sections are within segment
376 if ( sect
->addr() < startAddr
)
377 throwf("section %s vm address not within segment", sect
->sectname());
378 if ( (sect
->addr()+sect
->size()) > endAddr
)
379 throwf("section %s vm address not within segment", sect
->sectname());
380 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
) && (segCmd
->filesize() != 0) ) {
381 if ( sect
->offset() < startOffset
)
382 throwf("section %s file offset not within segment", sect
->sectname());
383 if ( (sect
->offset()+sect
->size()) > endOffset
)
384 throwf("section %s file offset not within segment", sect
->sectname());
386 checkSection(segCmd
, sect
);
389 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
392 // verify there was a LINKEDIT segment
393 if ( linkEditSegment
== NULL
)
394 throw "no __LINKEDIT segment";
396 // checks for executables
397 bool isStaticExecutable
= false;
398 if ( fHeader
->filetype() == MH_EXECUTE
) {
399 isStaticExecutable
= true;
401 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
402 switch ( cmd
->cmd() ) {
403 case LC_LOAD_DYLINKER
:
404 // the existence of a dyld load command makes a executable dynamic
405 isStaticExecutable
= false;
408 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
410 if ( isStaticExecutable
) {
411 if ( fHeader
->flags() != MH_NOUNDEFS
)
412 throw "invalid bits in mach_header flags for static executable";
416 // verify encryption info
417 if ( encryption_info
!= NULL
) {
418 if ( fHeader
->filetype() != MH_EXECUTE
)
419 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
420 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
421 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
422 if ( (encryption_info
->cryptoff() % 4096) != 0 )
423 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
424 if ( (encryption_info
->cryptsize() % 4096) != 0 )
425 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
426 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
427 it
!= segmentFileOffsetRanges
.end(); ++it
) {
428 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
429 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
430 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
435 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
437 bool foundDynamicSymTab
= false;
438 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
439 switch ( cmd
->cmd() ) {
442 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
443 fSymbolCount
= symtab
->nsyms();
444 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
445 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
446 throw "symbol table not in __LINKEDIT";
447 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
448 throw "symbol table end not in __LINKEDIT";
449 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
450 throw "symbol table start not pointer aligned";
451 fStrings
= (char*)fHeader
+ symtab
->stroff();
452 fStringsEnd
= fStrings
+ symtab
->strsize();
453 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
454 throw "string pool not in __LINKEDIT";
455 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
456 throw "string pool extends beyond __LINKEDIT";
457 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
458 throw "string pool start not pointer aligned";
459 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
460 throw "string pool size not a multiple of pointer size";
465 if ( isStaticExecutable
)
466 throw "LC_DYSYMTAB should not be used in static executable";
467 foundDynamicSymTab
= true;
468 fDynamicSymbolTable
= (struct macho_dysymtab_command
<P
>*)cmd
;
469 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
470 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
471 if ( fIndirectTableCount
!= 0 ) {
472 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
473 throw "indirect symbol table not in __LINKEDIT";
474 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
475 throw "indirect symbol table not in __LINKEDIT";
476 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
477 throw "indirect symbol table not pointer aligned";
479 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
480 if ( fLocalRelocationsCount
!= 0 ) {
481 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
482 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
483 throw "local relocations not in __LINKEDIT";
484 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
485 throw "local relocations not in __LINKEDIT";
486 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
487 throw "local relocations table not pointer aligned";
489 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
490 if ( fExternalRelocationsCount
!= 0 ) {
491 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
492 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
493 throw "external relocations not in __LINKEDIT";
494 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
495 throw "external relocations not in __LINKEDIT";
496 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
497 throw "external relocations table not pointer aligned";
501 case LC_SEGMENT_SPLIT_INFO
:
503 if ( isStaticExecutable
)
504 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
505 const macho_linkedit_data_command
<P
>* info
= (struct macho_linkedit_data_command
<P
>*)cmd
;
506 if ( info
->dataoff() < linkEditSegment
->fileoff() )
507 throw "split seg info not in __LINKEDIT";
508 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
509 throw "split seg info not in __LINKEDIT";
510 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
511 throw "split seg info table not pointer aligned";
512 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
513 throw "split seg info size not a multiple of pointer size";
517 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
519 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
520 throw "missing dynamic symbol table";
521 if ( fStrings
== NULL
)
522 throw "missing symbol table";
526 template <typename A
>
527 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
529 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
530 if ( sectionType
== S_ZEROFILL
) {
531 if ( sect
->offset() != 0 )
532 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
535 // more section tests here
538 template <typename A
>
539 void MachOChecker
<A
>::checkIndirectSymbolTable()
541 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
542 const uint32_t cmd_count
= fHeader
->ncmds();
543 const macho_load_command
<P
>* cmd
= cmds
;
544 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
545 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
546 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
547 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
548 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
549 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
550 // make sure all magic sections that use indirect symbol table fit within it
552 uint32_t elementSize
= 0;
553 switch ( sect
->flags() & SECTION_TYPE
) {
555 elementSize
= sect
->reserved2();
556 start
= sect
->reserved1();
558 case S_LAZY_SYMBOL_POINTERS
:
559 case S_NON_LAZY_SYMBOL_POINTERS
:
560 elementSize
= sizeof(pint_t
);
561 start
= sect
->reserved1();
564 if ( elementSize
!= 0 ) {
565 uint32_t count
= sect
->size() / elementSize
;
566 if ( (count
*elementSize
) != sect
->size() )
567 throwf("%s section size is not an even multiple of element size", sect
->sectname());
568 if ( (start
+count
) > fIndirectTableCount
)
569 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
573 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
578 template <typename A
>
579 void MachOChecker
<A
>::checkSymbolTable()
581 // verify no duplicate external symbol names
582 if ( fDynamicSymbolTable
!= NULL
) {
583 StringSet externalNames
;
584 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
585 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
586 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
) {
587 const char* symName
= &fStrings
[p
->n_strx()];
588 if ( externalNames
.find(symName
) != externalNames
.end() )
589 throwf("duplicate external symbol: %s", symName
);
590 externalNames
.insert(symName
);
597 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
599 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
600 return fFirstWritableSegment
->vmaddr();
602 return fFirstSegment
->vmaddr();
606 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
608 if ( fWriteableSegmentWithAddrOver4G
)
609 return fFirstWritableSegment
->vmaddr();
611 return fFirstSegment
->vmaddr();
615 x86::P::uint_t MachOChecker
<x86
>::relocBase()
617 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
618 return fFirstWritableSegment
->vmaddr();
620 return fFirstSegment
->vmaddr();
624 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
626 // check for split-seg
627 return fFirstWritableSegment
->vmaddr();
631 arm::P::uint_t MachOChecker
<arm
>::relocBase()
633 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
634 return fFirstWritableSegment
->vmaddr();
636 return fFirstSegment
->vmaddr();
640 template <typename A
>
641 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
643 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
644 const uint32_t cmd_count
= fHeader
->ncmds();
645 const macho_load_command
<P
>* cmd
= cmds
;
646 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
647 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
648 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
649 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
650 // if segment is writable, we are fine
651 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
653 // could be a text reloc, make sure section bit is set
654 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
655 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
656 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
657 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
658 // found section for this address, if has relocs we are fine
659 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
664 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
671 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
673 if ( reloc
->r_length() != 2 )
674 throw "bad external relocation length";
675 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
676 throw "unknown external relocation type";
677 if ( reloc
->r_pcrel() != 0 )
678 throw "bad external relocation pc_rel";
679 if ( reloc
->r_extern() == 0 )
680 throw "local relocation found with external relocations";
681 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
682 throw "external relocation address not in writable segment";
683 // FIX: check r_symbol
687 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
689 if ( reloc
->r_length() != 3 )
690 throw "bad external relocation length";
691 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
692 throw "unknown external relocation type";
693 if ( reloc
->r_pcrel() != 0 )
694 throw "bad external relocation pc_rel";
695 if ( reloc
->r_extern() == 0 )
696 throw "local relocation found with external relocations";
697 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
698 throw "external relocation address not in writable segment";
699 // FIX: check r_symbol
703 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
705 if ( reloc
->r_length() != 2 )
706 throw "bad external relocation length";
707 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
708 throw "unknown external relocation type";
709 if ( reloc
->r_pcrel() != 0 )
710 throw "bad external relocation pc_rel";
711 if ( reloc
->r_extern() == 0 )
712 throw "local relocation found with external relocations";
713 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
714 throw "external relocation address not in writable segment";
715 // FIX: check r_symbol
720 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
722 if ( reloc
->r_length() != 3 )
723 throw "bad external relocation length";
724 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
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 "exernal relocation address not in writable segment";
732 // FIX: check r_symbol
736 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
738 if ( reloc
->r_length() != 2 )
739 throw "bad external relocation length";
740 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
741 throw "unknown external relocation type";
742 if ( reloc
->r_pcrel() != 0 )
743 throw "bad external relocation pc_rel";
744 if ( reloc
->r_extern() == 0 )
745 throw "local relocation found with external relocations";
746 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
747 throw "external relocation address not in writable segment";
748 // FIX: check r_symbol
753 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
755 if ( reloc
->r_address() & R_SCATTERED
) {
757 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
762 // ignore pair relocs
763 if ( reloc
->r_type() == PPC_RELOC_PAIR
)
766 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
767 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
773 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
775 if ( reloc
->r_length() != 3 )
776 throw "bad local relocation length";
777 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
778 throw "unknown local relocation type";
779 if ( reloc
->r_pcrel() != 0 )
780 throw "bad local relocation pc_rel";
781 if ( reloc
->r_extern() != 0 )
782 throw "external relocation found with local relocations";
783 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
784 throw "local relocation address not in writable segment";
788 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
794 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
796 if ( reloc
->r_length() != 3 )
797 throw "bad local relocation length";
798 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
799 throw "unknown local relocation type";
800 if ( reloc
->r_pcrel() != 0 )
801 throw "bad local relocation pc_rel";
802 if ( reloc
->r_extern() != 0 )
803 throw "external relocation found with local relocations";
804 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
805 throw "local relocation address not in writable segment";
809 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
811 if ( reloc
->r_address() & R_SCATTERED
) {
813 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
814 if ( sreloc
->r_length() != 2 )
815 throw "bad local scattered relocation length";
816 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
817 throw "bad local scattered relocation type";
820 if ( reloc
->r_length() != 2 )
821 throw "bad local relocation length";
822 if ( reloc
->r_extern() != 0 )
823 throw "external relocation found with local relocations";
824 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
825 throw "local relocation address not in writable segment";
829 template <typename A
>
830 void MachOChecker
<A
>::checkRelocations()
832 // external relocations should be sorted to minimize dyld symbol lookups
833 // therefore every reloc with the same r_symbolnum value should be contiguous
834 std::set
<uint32_t> previouslySeenSymbolIndexes
;
835 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
836 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
837 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
838 this->checkExternalReloation(reloc
);
839 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
840 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
841 throw "external relocations not sorted";
842 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
843 lastSymbolIndex
= reloc
->r_symbolnum();
847 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
848 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
849 this->checkLocalReloation(reloc
);
854 static void check(const char* path
)
856 struct stat stat_buf
;
859 int fd
= ::open(path
, O_RDONLY
, 0);
861 throw "cannot open file";
862 ::fstat(fd
, &stat_buf
);
863 uint32_t length
= stat_buf
.st_size
;
864 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
865 if ( p
== ((uint8_t*)(-1)) )
866 throw "cannot map file";
868 const mach_header
* mh
= (mach_header
*)p
;
869 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
870 const struct fat_header
* fh
= (struct fat_header
*)p
;
871 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
872 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
873 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
874 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
875 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
878 case CPU_TYPE_POWERPC
:
879 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
880 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
882 throw "in universal file, ppc slice does not contain ppc mach-o";
885 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
886 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
888 throw "in universal file, i386 slice does not contain i386 mach-o";
890 case CPU_TYPE_POWERPC64
:
891 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
892 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
894 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
896 case CPU_TYPE_X86_64
:
897 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
898 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
900 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
903 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
904 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
906 throw "in universal file, arm slice does not contain arm mach-o";
909 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
913 else if ( MachOChecker
<x86
>::validFile(p
) ) {
914 MachOChecker
<x86
>::make(p
, length
, path
);
916 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
917 MachOChecker
<ppc
>::make(p
, length
, path
);
919 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
920 MachOChecker
<ppc64
>::make(p
, length
, path
);
922 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
923 MachOChecker
<x86_64
>::make(p
, length
, path
);
925 else if ( MachOChecker
<arm
>::validFile(p
) ) {
926 MachOChecker
<arm
>::make(p
, length
, path
);
929 throw "not a known file type";
932 catch (const char* msg
) {
933 throwf("%s in %s", msg
, path
);
938 int main(int argc
, const char* argv
[])
941 for(int i
=1; i
< argc
; ++i
) {
942 const char* arg
= argv
[i
];
943 if ( arg
[0] == '-' ) {
944 if ( strcmp(arg
, "-no_content") == 0 ) {
948 throwf("unknown option: %s\n", arg
);
956 catch (const char* msg
) {
957 fprintf(stderr
, "machocheck failed: %s\n", msg
);