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
) {
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 ( dsymtab
->indirectsymoff() < linkEditSegment
->fileoff() )
406 throw "indirect symbol table not in __LINKEDIT";
407 if ( (dsymtab
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
408 throw "indirect symbol table not in __LINKEDIT";
409 fLocalRelocationsCount
= dsymtab
->nlocrel();
410 if ( fLocalRelocationsCount
!= 0 ) {
411 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ dsymtab
->locreloff());
412 if ( dsymtab
->locreloff() < linkEditSegment
->fileoff() )
413 throw "local relocations not in __LINKEDIT";
414 if ( (dsymtab
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
415 throw "local relocations not in __LINKEDIT";
417 fExternalRelocationsCount
= dsymtab
->nextrel();
418 if ( fExternalRelocationsCount
!= 0 ) {
419 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ dsymtab
->extreloff());
420 if ( dsymtab
->extreloff() < linkEditSegment
->fileoff() )
421 throw "local relocations not in __LINKEDIT";
422 if ( (dsymtab
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
423 throw "local relocations not in __LINKEDIT";
428 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
430 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
431 throw "missing dynamic symbol table";
432 if ( fStrings
== NULL
)
433 throw "missing symbol table";
435 fRelocBase
= this->relocBase();
439 template <typename A
>
440 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
442 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
443 if ( sectionType
== S_ZEROFILL
) {
444 if ( sect
->offset() != 0 )
445 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
448 // more section tests here
451 template <typename A
>
452 void MachOChecker
<A
>::checkIndirectSymbolTable()
454 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
455 const uint32_t cmd_count
= fHeader
->ncmds();
456 const macho_load_command
<P
>* cmd
= cmds
;
457 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
458 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
459 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
460 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
461 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
462 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
463 // make sure all magic sections that use indirect symbol table fit within it
465 uint32_t elementSize
= 0;
466 switch ( sect
->flags() & SECTION_TYPE
) {
468 elementSize
= sect
->reserved2();
469 start
= sect
->reserved1();
471 case S_LAZY_SYMBOL_POINTERS
:
472 case S_NON_LAZY_SYMBOL_POINTERS
:
473 elementSize
= sizeof(pint_t
);
474 start
= sect
->reserved1();
477 if ( elementSize
!= 0 ) {
478 uint32_t count
= sect
->size() / elementSize
;
479 if ( (count
*elementSize
) != sect
->size() )
480 throwf("%s section size is not an even multiple of element size", sect
->sectname());
481 if ( (start
+count
) > fIndirectTableCount
)
482 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
486 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
493 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
495 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
496 return fFirstWritableSegment
->vmaddr();
498 return fFirstSegment
->vmaddr();
502 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
504 if ( fWriteableSegmentWithAddrOver4G
)
505 return fFirstWritableSegment
->vmaddr();
507 return fFirstSegment
->vmaddr();
511 x86::P::uint_t MachOChecker
<x86
>::relocBase()
513 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
514 return fFirstWritableSegment
->vmaddr();
516 return fFirstSegment
->vmaddr();
520 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
522 // check for split-seg
523 return fFirstWritableSegment
->vmaddr();
527 template <typename A
>
528 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
530 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
531 const uint32_t cmd_count
= fHeader
->ncmds();
532 const macho_load_command
<P
>* cmd
= cmds
;
533 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
534 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
535 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
536 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
537 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) )
541 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
548 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
554 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
556 if ( reloc
->r_length() != 3 )
557 throw "bad external relocation length";
558 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
559 throw "unknown external relocation type";
560 if ( reloc
->r_pcrel() != 0 )
561 throw "bad external relocation pc_rel";
562 if ( reloc
->r_extern() == 0 )
563 throw "local relocation found with external relocations";
564 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
565 throw "local relocation address not in writable segment";
566 // FIX: check r_symbol
570 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
577 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
579 if ( reloc
->r_length() != 3 )
580 throw "bad external relocation length";
581 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
582 throw "unknown external relocation type";
583 if ( reloc
->r_pcrel() != 0 )
584 throw "bad external relocation pc_rel";
585 if ( reloc
->r_extern() == 0 )
586 throw "local relocation found with external relocations";
587 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
588 throw "exernal relocation address not in writable segment";
589 // FIX: check r_symbol
593 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
595 if ( reloc
->r_address() & R_SCATTERED
) {
597 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
603 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
604 throw "local relocation address not in writable segment";
610 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
612 if ( reloc
->r_length() != 3 )
613 throw "bad local relocation length";
614 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
615 throw "unknown local relocation type";
616 if ( reloc
->r_pcrel() != 0 )
617 throw "bad local relocation pc_rel";
618 if ( reloc
->r_extern() != 0 )
619 throw "external relocation found with local relocations";
620 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
621 throw "local relocation address not in writable segment";
625 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
631 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
633 if ( reloc
->r_length() != 3 )
634 throw "bad local relocation length";
635 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
636 throw "unknown local relocation type";
637 if ( reloc
->r_pcrel() != 0 )
638 throw "bad local relocation pc_rel";
639 if ( reloc
->r_extern() != 0 )
640 throw "external relocation found with local relocations";
641 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
642 throw "local relocation address not in writable segment";
647 template <typename A
>
648 void MachOChecker
<A
>::checkRelocations()
650 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
651 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
652 this->checkExternalReloation(reloc
);
655 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
656 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
657 this->checkLocalReloation(reloc
);
662 static void check(const char* path
)
664 struct stat stat_buf
;
667 int fd
= ::open(path
, O_RDONLY
, 0);
669 throw "cannot open file";
670 ::fstat(fd
, &stat_buf
);
671 uint32_t length
= stat_buf
.st_size
;
672 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
673 if ( p
== ((uint8_t*)(-1)) )
674 throw "cannot map file";
676 const mach_header
* mh
= (mach_header
*)p
;
677 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
678 const struct fat_header
* fh
= (struct fat_header
*)p
;
679 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
680 for (unsigned long i
=0; i
< fh
->nfat_arch
; ++i
) {
681 if ( archs
[i
].cputype
== CPU_TYPE_POWERPC
) {
682 if ( MachOChecker
<ppc
>::validFile(p
+ archs
[i
].offset
) )
683 MachOChecker
<ppc
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
685 throw "in universal file, ppc slice does not contain ppc mach-o";
687 else if ( archs
[i
].cputype
== CPU_TYPE_I386
) {
688 if ( MachOChecker
<x86
>::validFile(p
+ archs
[i
].offset
) )
689 MachOChecker
<x86
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
691 throw "in universal file, i386 slice does not contain i386 mach-o";
693 else if ( archs
[i
].cputype
== CPU_TYPE_POWERPC64
) {
694 if ( MachOChecker
<ppc64
>::validFile(p
+ archs
[i
].offset
) )
695 MachOChecker
<ppc64
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
697 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
699 else if ( archs
[i
].cputype
== CPU_TYPE_X86_64
) {
700 if ( MachOChecker
<x86_64
>::validFile(p
+ archs
[i
].offset
) )
701 MachOChecker
<x86_64
>::make(p
+ archs
[i
].offset
, archs
[i
].size
, path
);
703 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
706 throw "in universal file, unknown architecture slice";
710 else if ( MachOChecker
<x86
>::validFile(p
) ) {
711 MachOChecker
<x86
>::make(p
, length
, path
);
713 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
714 MachOChecker
<ppc
>::make(p
, length
, path
);
716 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
717 MachOChecker
<ppc64
>::make(p
, length
, path
);
719 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
720 MachOChecker
<x86_64
>::make(p
, length
, path
);
723 throw "not a known file type";
726 catch (const char* msg
) {
727 throwf("%s in %s", msg
, path
);
732 int main(int argc
, const char* argv
[])
735 for(int i
=1; i
< argc
; ++i
) {
736 const char* arg
= argv
[i
];
737 if ( arg
[0] == '-' ) {
738 if ( strcmp(arg
, "-no_content") == 0 ) {
742 throwf("unknown option: %s\n", arg
);
750 catch (const char* msg
) {
751 fprintf(stderr
, "machocheck failed: %s\n", msg
);