1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006 Apple Computer, 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>
32 #include <mach-o/loader.h>
33 #include <mach-o/fat.h>
34 #include <mach-o/stab.h>
35 #include <mach-o/reloc.h>
36 #include <mach-o/ppc/reloc.h>
37 #include <mach-o/x86_64/reloc.h>
41 #include "MachOFileAbstraction.hpp"
42 #include "Architectures.hpp"
45 __attribute__((noreturn
))
46 void throwf(const char* format
, ...)
50 va_start(list
, format
);
51 vasprintf(&p
, format
, list
);
63 static bool validFile(const uint8_t* fileContent
);
64 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
65 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
66 virtual ~MachOChecker() {}
70 typedef typename
A::P P
;
71 typedef typename
A::P::E E
;
72 typedef typename
A::P::uint_t pint_t
;
74 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
75 void checkMachHeader();
76 void checkLoadCommands();
77 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
78 uint8_t loadCommandSizeMask();
79 void checkIndirectSymbolTable();
80 void checkRelocations();
81 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
82 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
84 bool addressInWritableSegment(pint_t address
);
87 const macho_header
<P
>* fHeader
;
90 const char* fStringsEnd
;
91 const macho_nlist
<P
>* fSymbols
;
92 uint32_t fSymbolCount
;
93 const uint32_t* fIndirectTable
;
94 uint32_t fIndirectTableCount
;
95 const macho_relocation_info
<P
>* fLocalRelocations
;
96 uint32_t fLocalRelocationsCount
;
97 const macho_relocation_info
<P
>* fExternalRelocations
;
98 uint32_t fExternalRelocationsCount
;
100 bool fWriteableSegmentWithAddrOver4G
;
101 const macho_segment_command
<P
>* fFirstSegment
;
102 const macho_segment_command
<P
>* fFirstWritableSegment
;
108 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
110 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
111 if ( header
->magic() != MH_MAGIC
)
113 if ( header
->cputype() != CPU_TYPE_POWERPC
)
115 switch (header
->filetype()) {
126 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
128 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
129 if ( header
->magic() != MH_MAGIC_64
)
131 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
133 switch (header
->filetype()) {
144 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
146 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
147 if ( header
->magic() != MH_MAGIC
)
149 if ( header
->cputype() != CPU_TYPE_I386
)
151 switch (header
->filetype()) {
162 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
164 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
165 if ( header
->magic() != MH_MAGIC_64
)
167 if ( header
->cputype() != CPU_TYPE_X86_64
)
169 switch (header
->filetype()) {
180 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
181 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
182 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
183 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
186 template <typename A
>
187 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
188 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fIndirectTableCount(0),
189 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
190 fRelocBase(0), fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
)
193 if ( ! validFile(fileContent
) )
194 throw "not a mach-o file that can be checked";
196 fPath
= strdup(path
);
197 fHeader
= (const macho_header
<P
>*)fileContent
;
199 // sanity check header
202 // check load commands
205 checkIndirectSymbolTable();
211 template <typename A
>
212 void MachOChecker
<A
>::checkMachHeader()
214 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
215 throw "sizeofcmds in mach_header is larger than file";
217 uint32_t flags
= fHeader
->flags();
218 uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFFFC0000;
219 if ( flags
& invalidBits
)
220 throw "invalid bits in mach_header flags";
224 template <typename A
>
225 void MachOChecker
<A
>::checkLoadCommands()
227 // check that all load commands fit within the load command space file
228 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
229 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
230 const uint32_t cmd_count
= fHeader
->ncmds();
231 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
232 const macho_load_command
<P
>* cmd
= cmds
;
233 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
234 uint32_t size
= cmd
->cmdsize();
235 if ( (size
& this->loadCommandSizeMask()) != 0 )
236 throwf("load command #%d has a unaligned size", i
);
237 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
238 if ( endOfCmd
> endOfLoadCommands
)
239 throwf("load command #%d extends beyond the end of the load commands", i
);
240 if ( endOfCmd
> endOfFile
)
241 throwf("load command #%d extends beyond the end of the file", i
);
242 switch ( cmd
->cmd() ) {
243 case macho_segment_command
<P
>::CMD
:
249 case LC_LOAD_DYLINKER
:
251 case macho_routines_command
<P
>::CMD
:
252 case LC_SUB_FRAMEWORK
:
253 case LC_SUB_UMBRELLA
:
255 case LC_TWOLEVEL_HINTS
:
256 case LC_PREBIND_CKSUM
:
257 case LC_LOAD_WEAK_DYLIB
:
261 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
263 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
268 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
269 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
270 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
271 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
272 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
273 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
274 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
275 throw "invalid segment load command size";
277 // see if this overlaps another segment address range
278 uint64_t startAddr
= segCmd
->vmaddr();
279 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
280 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
281 if ( it
->first
< startAddr
) {
282 if ( it
->second
> startAddr
)
283 throw "overlapping segment vm addresses";
285 else if ( it
->first
> startAddr
) {
286 if ( it
->first
< endAddr
)
287 throw "overlapping segment vm addresses";
290 throw "overlapping segment vm addresses";
292 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
294 // see if this overlaps another segment file offset range
295 uint64_t startOffset
= segCmd
->fileoff();
296 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
297 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
298 if ( it
->first
< startOffset
) {
299 if ( it
->second
> startOffset
)
300 throw "overlapping segment file data";
302 else if ( it
->first
> startOffset
) {
303 if ( it
->first
< endOffset
)
304 throw "overlapping segment file data";
307 throw "overlapping segment file data";
309 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
310 // check is within file bounds
311 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
312 throw "segment file data is past end of file";
314 // verify it fits in file
315 if ( startOffset
> fLength
)
316 throw "segment fileoff does not fit in file";
317 if ( endOffset
> fLength
)
318 throw "segment fileoff+filesize does not fit in file";
320 // keep LINKEDIT segment
321 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
322 linkEditSegment
= segCmd
;
324 // cache interesting segments
325 if ( fFirstSegment
== NULL
)
326 fFirstSegment
= segCmd
;
327 if ( (fFirstWritableSegment
== NULL
) && ((segCmd
->initprot() & VM_PROT_WRITE
) != 0) )
328 fFirstWritableSegment
= segCmd
;
330 // check section ranges
331 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
332 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
333 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
334 // check all sections are within segment
335 if ( sect
->addr() < startAddr
)
336 throwf("section %s vm address not within segment", sect
->sectname());
337 if ( (sect
->addr()+sect
->size()) > endAddr
)
338 throwf("section %s vm address not within segment", sect
->sectname());
339 if ( ((sect
->flags() &SECTION_TYPE
) != S_ZEROFILL
) && (segCmd
->filesize() != 0) ) {
340 if ( sect
->offset() < startOffset
)
341 throwf("section %s file offset not within segment", sect
->sectname());
342 if ( (sect
->offset()+sect
->size()) > endOffset
)
343 throwf("section %s file offset not within segment", sect
->sectname());
345 checkSection(segCmd
, sect
);
348 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
351 // verify there was a LINKEDIT segment
352 if ( linkEditSegment
== NULL
)
353 throw "no __LINKEDIT segment";
355 // checks for executables
356 bool isStaticExecutable
= false;
357 if ( fHeader
->filetype() == MH_EXECUTE
) {
358 isStaticExecutable
= true;
360 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
361 switch ( cmd
->cmd() ) {
362 case LC_LOAD_DYLINKER
:
363 // the existence of a dyld load command makes a executable dynamic
364 isStaticExecutable
= false;
367 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
369 if ( isStaticExecutable
) {
370 if ( fHeader
->flags() != MH_NOUNDEFS
)
371 throw "invalid bits in mach_header flags for static executable";
375 // check LC_SYMTAB and LC_DYSYMTAB
377 bool foundDynamicSymTab
= false;
378 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
379 switch ( cmd
->cmd() ) {
382 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
383 fSymbolCount
= symtab
->nsyms();
384 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
385 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
386 throw "symbol table not in __LINKEDIT";
387 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
388 throw "symbol table end not in __LINKEDIT";
389 fStrings
= (char*)fHeader
+ symtab
->stroff();
390 fStringsEnd
= fStrings
+ symtab
->strsize();
391 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
392 throw "string pool not in __LINKEDIT";
393 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
394 throw "string pool extends beyond __LINKEDIT";
399 if ( isStaticExecutable
)
400 throw "LC_DYSYMTAB should not be used in static executable";
401 foundDynamicSymTab
= true;
402 const macho_dysymtab_command
<P
>* dsymtab
= (struct macho_dysymtab_command
<P
>*)cmd
;
403 fIndirectTable
= (uint32_t*)((char*)fHeader
+ dsymtab
->indirectsymoff());
404 fIndirectTableCount
= dsymtab
->nindirectsyms();
405 if ( fIndirectTableCount
!= 0 ) {
406 if ( dsymtab
->indirectsymoff() < linkEditSegment
->fileoff() )
407 throw "indirect symbol table not in __LINKEDIT";
408 if ( (dsymtab
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
409 throw "indirect symbol table not in __LINKEDIT";
411 fLocalRelocationsCount
= dsymtab
->nlocrel();
412 if ( fLocalRelocationsCount
!= 0 ) {
413 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ dsymtab
->locreloff());
414 if ( dsymtab
->locreloff() < linkEditSegment
->fileoff() )
415 throw "local relocations not in __LINKEDIT";
416 if ( (dsymtab
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
417 throw "local relocations not in __LINKEDIT";
419 fExternalRelocationsCount
= dsymtab
->nextrel();
420 if ( fExternalRelocationsCount
!= 0 ) {
421 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ dsymtab
->extreloff());
422 if ( dsymtab
->extreloff() < linkEditSegment
->fileoff() )
423 throw "local relocations not in __LINKEDIT";
424 if ( (dsymtab
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
425 throw "local relocations not in __LINKEDIT";
430 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
432 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
433 throw "missing dynamic symbol table";
434 if ( fStrings
== NULL
)
435 throw "missing symbol table";
437 fRelocBase
= this->relocBase();
441 template <typename A
>
442 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
444 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
445 if ( sectionType
== S_ZEROFILL
) {
446 if ( sect
->offset() != 0 )
447 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
450 // more section tests here
453 template <typename A
>
454 void MachOChecker
<A
>::checkIndirectSymbolTable()
456 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
457 const uint32_t cmd_count
= fHeader
->ncmds();
458 const macho_load_command
<P
>* cmd
= cmds
;
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 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
463 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
464 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
465 // make sure all magic sections that use indirect symbol table fit within it
467 uint32_t elementSize
= 0;
468 switch ( sect
->flags() & SECTION_TYPE
) {
470 elementSize
= sect
->reserved2();
471 start
= sect
->reserved1();
473 case S_LAZY_SYMBOL_POINTERS
:
474 case S_NON_LAZY_SYMBOL_POINTERS
:
475 elementSize
= sizeof(pint_t
);
476 start
= sect
->reserved1();
479 if ( elementSize
!= 0 ) {
480 uint32_t count
= sect
->size() / elementSize
;
481 if ( (count
*elementSize
) != sect
->size() )
482 throwf("%s section size is not an even multiple of element size", sect
->sectname());
483 if ( (start
+count
) > fIndirectTableCount
)
484 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
488 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
495 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
497 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
498 return fFirstWritableSegment
->vmaddr();
500 return fFirstSegment
->vmaddr();
504 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
506 if ( fWriteableSegmentWithAddrOver4G
)
507 return fFirstWritableSegment
->vmaddr();
509 return fFirstSegment
->vmaddr();
513 x86::P::uint_t MachOChecker
<x86
>::relocBase()
515 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
516 return fFirstWritableSegment
->vmaddr();
518 return fFirstSegment
->vmaddr();
522 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
524 // check for split-seg
525 return fFirstWritableSegment
->vmaddr();
529 template <typename A
>
530 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
532 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
533 const uint32_t cmd_count
= fHeader
->ncmds();
534 const macho_load_command
<P
>* cmd
= cmds
;
535 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
536 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
537 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
538 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
539 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) )
543 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
550 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
556 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
558 if ( reloc
->r_length() != 3 )
559 throw "bad external relocation length";
560 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
561 throw "unknown external relocation type";
562 if ( reloc
->r_pcrel() != 0 )
563 throw "bad external relocation pc_rel";
564 if ( reloc
->r_extern() == 0 )
565 throw "local relocation found with external relocations";
566 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
567 throw "local relocation address not in writable segment";
568 // FIX: check r_symbol
572 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
579 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
581 if ( reloc
->r_length() != 3 )
582 throw "bad external relocation length";
583 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
584 throw "unknown external relocation type";
585 if ( reloc
->r_pcrel() != 0 )
586 throw "bad external relocation pc_rel";
587 if ( reloc
->r_extern() == 0 )
588 throw "local relocation found with external relocations";
589 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
590 throw "exernal relocation address not in writable segment";
591 // FIX: check r_symbol
595 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
597 if ( reloc
->r_address() & R_SCATTERED
) {
599 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
605 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
606 throw "local relocation address not in writable segment";
612 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
614 if ( reloc
->r_length() != 3 )
615 throw "bad local relocation length";
616 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
617 throw "unknown local relocation type";
618 if ( reloc
->r_pcrel() != 0 )
619 throw "bad local relocation pc_rel";
620 if ( reloc
->r_extern() != 0 )
621 throw "external relocation found with local relocations";
622 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
623 throw "local relocation address not in writable segment";
627 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
633 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
635 if ( reloc
->r_length() != 3 )
636 throw "bad local relocation length";
637 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
638 throw "unknown local relocation type";
639 if ( reloc
->r_pcrel() != 0 )
640 throw "bad local relocation pc_rel";
641 if ( reloc
->r_extern() != 0 )
642 throw "external relocation found with local relocations";
643 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
644 throw "local relocation address not in writable segment";
649 template <typename A
>
650 void MachOChecker
<A
>::checkRelocations()
652 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
653 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
654 this->checkExternalReloation(reloc
);
657 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
658 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
659 this->checkLocalReloation(reloc
);
664 static void check(const char* path
)
666 struct stat stat_buf
;
669 int fd
= ::open(path
, O_RDONLY
, 0);
671 throw "cannot open file";
672 ::fstat(fd
, &stat_buf
);
673 uint32_t length
= stat_buf
.st_size
;
674 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
675 if ( p
== ((uint8_t*)(-1)) )
676 throw "cannot map file";
678 const mach_header
* mh
= (mach_header
*)p
;
679 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
680 const struct fat_header
* fh
= (struct fat_header
*)p
;
681 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
682 for (unsigned long i
=0; i
< fh
->nfat_arch
; ++i
) {
683 if ( archs
[i
].cputype
== CPU_TYPE_POWERPC
) {
684 if ( MachOChecker
<ppc
>::validFile(p
+ archs
[i
].offset
) )
685 MachOChecker
<ppc
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
687 throw "in universal file, ppc slice does not contain ppc mach-o";
689 else if ( archs
[i
].cputype
== CPU_TYPE_I386
) {
690 if ( MachOChecker
<x86
>::validFile(p
+ archs
[i
].offset
) )
691 MachOChecker
<x86
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
693 throw "in universal file, i386 slice does not contain i386 mach-o";
695 else if ( archs
[i
].cputype
== CPU_TYPE_POWERPC64
) {
696 if ( MachOChecker
<ppc64
>::validFile(p
+ archs
[i
].offset
) )
697 MachOChecker
<ppc64
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
699 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
701 else if ( archs
[i
].cputype
== CPU_TYPE_X86_64
) {
702 if ( MachOChecker
<x86_64
>::validFile(p
+ archs
[i
].offset
) )
703 MachOChecker
<x86_64
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
705 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
708 throw "in universal file, unknown architecture slice";
712 else if ( MachOChecker
<x86
>::validFile(p
) ) {
713 MachOChecker
<x86
>::make(p
, length
, path
);
715 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
716 MachOChecker
<ppc
>::make(p
, length
, path
);
718 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
719 MachOChecker
<ppc64
>::make(p
, length
, path
);
721 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
722 MachOChecker
<x86_64
>::make(p
, length
, path
);
725 throw "not a known file type";
728 catch (const char* msg
) {
729 throwf("%s in %s", msg
, path
);
734 int main(int argc
, const char* argv
[])
737 for(int i
=1; i
< argc
; ++i
) {
738 const char* arg
= argv
[i
];
739 if ( arg
[0] == '-' ) {
740 if ( strcmp(arg
, "-no_content") == 0 ) {
744 throwf("unknown option: %s\n", arg
);
752 catch (const char* msg
) {
753 fprintf(stderr
, "machocheck failed: %s\n", msg
);