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>
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 <ext/hash_set>
43 #include "MachOFileAbstraction.hpp"
44 #include "Architectures.hpp"
47 __attribute__((noreturn
))
48 void throwf(const char* format
, ...)
52 va_start(list
, format
);
53 vasprintf(&p
, format
, list
);
65 static bool validFile(const uint8_t* fileContent
);
66 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
67 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
68 virtual ~MachOChecker() {}
72 typedef typename
A::P P
;
73 typedef typename
A::P::E E
;
74 typedef typename
A::P::uint_t pint_t
;
79 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
82 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
84 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
85 void checkMachHeader();
86 void checkLoadCommands();
87 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
88 uint8_t loadCommandSizeMask();
89 void checkSymbolTable();
90 void checkIndirectSymbolTable();
91 void checkRelocations();
92 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
93 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
95 bool addressInWritableSegment(pint_t address
);
98 const macho_header
<P
>* fHeader
;
100 const char* fStrings
;
101 const char* fStringsEnd
;
102 const macho_nlist
<P
>* fSymbols
;
103 uint32_t fSymbolCount
;
104 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
105 const uint32_t* fIndirectTable
;
106 uint32_t fIndirectTableCount
;
107 const macho_relocation_info
<P
>* fLocalRelocations
;
108 uint32_t fLocalRelocationsCount
;
109 const macho_relocation_info
<P
>* fExternalRelocations
;
110 uint32_t fExternalRelocationsCount
;
111 bool fWriteableSegmentWithAddrOver4G
;
112 const macho_segment_command
<P
>* fFirstSegment
;
113 const macho_segment_command
<P
>* fFirstWritableSegment
;
119 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
121 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
122 if ( header
->magic() != MH_MAGIC
)
124 if ( header
->cputype() != CPU_TYPE_POWERPC
)
126 switch (header
->filetype()) {
137 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
139 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
140 if ( header
->magic() != MH_MAGIC_64
)
142 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
144 switch (header
->filetype()) {
155 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
157 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
158 if ( header
->magic() != MH_MAGIC
)
160 if ( header
->cputype() != CPU_TYPE_I386
)
162 switch (header
->filetype()) {
173 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
175 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
176 if ( header
->magic() != MH_MAGIC_64
)
178 if ( header
->cputype() != CPU_TYPE_X86_64
)
180 switch (header
->filetype()) {
191 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
192 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
193 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
194 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
197 template <typename A
>
198 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
199 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
200 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
201 fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
)
204 if ( ! validFile(fileContent
) )
205 throw "not a mach-o file that can be checked";
207 fPath
= strdup(path
);
208 fHeader
= (const macho_header
<P
>*)fileContent
;
210 // sanity check header
213 // check load commands
216 checkIndirectSymbolTable();
224 template <typename A
>
225 void MachOChecker
<A
>::checkMachHeader()
227 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
228 throw "sizeofcmds in mach_header is larger than file";
230 uint32_t flags
= fHeader
->flags();
231 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFFE00000;
232 if ( flags
& invalidBits
)
233 throw "invalid bits in mach_header flags";
234 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
235 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
238 template <typename A
>
239 void MachOChecker
<A
>::checkLoadCommands()
241 // check that all load commands fit within the load command space file
242 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
243 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
244 const uint32_t cmd_count
= fHeader
->ncmds();
245 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
246 const macho_load_command
<P
>* cmd
= cmds
;
247 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
248 uint32_t size
= cmd
->cmdsize();
249 if ( (size
& this->loadCommandSizeMask()) != 0 )
250 throwf("load command #%d has a unaligned size", i
);
251 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
252 if ( endOfCmd
> endOfLoadCommands
)
253 throwf("load command #%d extends beyond the end of the load commands", i
);
254 if ( endOfCmd
> endOfFile
)
255 throwf("load command #%d extends beyond the end of the file", i
);
256 switch ( cmd
->cmd() ) {
257 case macho_segment_command
<P
>::CMD
:
263 case LC_LOAD_DYLINKER
:
265 case macho_routines_command
<P
>::CMD
:
266 case LC_SUB_FRAMEWORK
:
268 case LC_TWOLEVEL_HINTS
:
269 case LC_PREBIND_CKSUM
:
270 case LC_LOAD_WEAK_DYLIB
:
272 case LC_REEXPORT_DYLIB
:
273 case LC_SEGMENT_SPLIT_INFO
:
275 case LC_SUB_UMBRELLA
:
277 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
278 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";
281 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
283 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
288 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
289 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
290 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
291 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
292 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
293 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
294 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
295 throw "invalid segment load command size";
297 // see if this overlaps another segment address range
298 uint64_t startAddr
= segCmd
->vmaddr();
299 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
300 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
301 if ( it
->first
< startAddr
) {
302 if ( it
->second
> startAddr
)
303 throw "overlapping segment vm addresses";
305 else if ( it
->first
> startAddr
) {
306 if ( it
->first
< endAddr
)
307 throw "overlapping segment vm addresses";
310 throw "overlapping segment vm addresses";
312 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
314 // see if this overlaps another segment file offset range
315 uint64_t startOffset
= segCmd
->fileoff();
316 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
317 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
318 if ( it
->first
< startOffset
) {
319 if ( it
->second
> startOffset
)
320 throw "overlapping segment file data";
322 else if ( it
->first
> startOffset
) {
323 if ( it
->first
< endOffset
)
324 throw "overlapping segment file data";
327 throw "overlapping segment file data";
329 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
330 // check is within file bounds
331 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
332 throw "segment file data is past end of file";
334 // verify it fits in file
335 if ( startOffset
> fLength
)
336 throw "segment fileoff does not fit in file";
337 if ( endOffset
> fLength
)
338 throw "segment fileoff+filesize does not fit in file";
340 // keep LINKEDIT segment
341 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
342 linkEditSegment
= segCmd
;
344 // cache interesting segments
345 if ( fFirstSegment
== NULL
)
346 fFirstSegment
= segCmd
;
347 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
348 if ( fFirstWritableSegment
== NULL
)
349 fFirstWritableSegment
= segCmd
;
350 if ( segCmd
->vmaddr() > 0x100000000ULL
)
351 fWriteableSegmentWithAddrOver4G
= true;
354 // check section ranges
355 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
356 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
357 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
358 // check all sections are within segment
359 if ( sect
->addr() < startAddr
)
360 throwf("section %s vm address not within segment", sect
->sectname());
361 if ( (sect
->addr()+sect
->size()) > endAddr
)
362 throwf("section %s vm address not within segment", sect
->sectname());
363 if ( ((sect
->flags() &SECTION_TYPE
) != S_ZEROFILL
) && (segCmd
->filesize() != 0) ) {
364 if ( sect
->offset() < startOffset
)
365 throwf("section %s file offset not within segment", sect
->sectname());
366 if ( (sect
->offset()+sect
->size()) > endOffset
)
367 throwf("section %s file offset not within segment", sect
->sectname());
369 checkSection(segCmd
, sect
);
372 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
375 // verify there was a LINKEDIT segment
376 if ( linkEditSegment
== NULL
)
377 throw "no __LINKEDIT segment";
379 // checks for executables
380 bool isStaticExecutable
= false;
381 if ( fHeader
->filetype() == MH_EXECUTE
) {
382 isStaticExecutable
= true;
384 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
385 switch ( cmd
->cmd() ) {
386 case LC_LOAD_DYLINKER
:
387 // the existence of a dyld load command makes a executable dynamic
388 isStaticExecutable
= false;
391 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
393 if ( isStaticExecutable
) {
394 if ( fHeader
->flags() != MH_NOUNDEFS
)
395 throw "invalid bits in mach_header flags for static executable";
399 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
401 bool foundDynamicSymTab
= false;
402 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
403 switch ( cmd
->cmd() ) {
406 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
407 fSymbolCount
= symtab
->nsyms();
408 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
409 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
410 throw "symbol table not in __LINKEDIT";
411 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
412 throw "symbol table end not in __LINKEDIT";
413 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
414 throw "symbol table start not pointer aligned";
415 fStrings
= (char*)fHeader
+ symtab
->stroff();
416 fStringsEnd
= fStrings
+ symtab
->strsize();
417 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
418 throw "string pool not in __LINKEDIT";
419 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
420 throw "string pool extends beyond __LINKEDIT";
421 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
422 throw "string pool start not pointer aligned";
423 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
424 throw "string pool size not a multiple of pointer size";
429 if ( isStaticExecutable
)
430 throw "LC_DYSYMTAB should not be used in static executable";
431 foundDynamicSymTab
= true;
432 fDynamicSymbolTable
= (struct macho_dysymtab_command
<P
>*)cmd
;
433 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
434 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
435 if ( fIndirectTableCount
!= 0 ) {
436 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
437 throw "indirect symbol table not in __LINKEDIT";
438 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
439 throw "indirect symbol table not in __LINKEDIT";
440 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
441 throw "indirect symbol table not pointer aligned";
443 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
444 if ( fLocalRelocationsCount
!= 0 ) {
445 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
446 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
447 throw "local relocations not in __LINKEDIT";
448 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
449 throw "local relocations not in __LINKEDIT";
450 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
451 throw "local relocations table not pointer aligned";
453 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
454 if ( fExternalRelocationsCount
!= 0 ) {
455 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
456 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
457 throw "external relocations not in __LINKEDIT";
458 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
459 throw "external relocations not in __LINKEDIT";
460 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
461 throw "external relocations table not pointer aligned";
465 case LC_SEGMENT_SPLIT_INFO
:
467 if ( isStaticExecutable
)
468 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
469 const macho_linkedit_data_command
<P
>* info
= (struct macho_linkedit_data_command
<P
>*)cmd
;
470 if ( info
->dataoff() < linkEditSegment
->fileoff() )
471 throw "split seg info not in __LINKEDIT";
472 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
473 throw "split seg info not in __LINKEDIT";
474 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
475 throw "split seg info table not pointer aligned";
476 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
477 throw "split seg info size not a multiple of pointer size";
481 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
483 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
484 throw "missing dynamic symbol table";
485 if ( fStrings
== NULL
)
486 throw "missing symbol table";
490 template <typename A
>
491 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
493 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
494 if ( sectionType
== S_ZEROFILL
) {
495 if ( sect
->offset() != 0 )
496 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
499 // more section tests here
502 template <typename A
>
503 void MachOChecker
<A
>::checkIndirectSymbolTable()
505 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
506 const uint32_t cmd_count
= fHeader
->ncmds();
507 const macho_load_command
<P
>* cmd
= cmds
;
508 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
509 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
510 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
511 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
512 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
513 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
514 // make sure all magic sections that use indirect symbol table fit within it
516 uint32_t elementSize
= 0;
517 switch ( sect
->flags() & SECTION_TYPE
) {
519 elementSize
= sect
->reserved2();
520 start
= sect
->reserved1();
522 case S_LAZY_SYMBOL_POINTERS
:
523 case S_NON_LAZY_SYMBOL_POINTERS
:
524 elementSize
= sizeof(pint_t
);
525 start
= sect
->reserved1();
528 if ( elementSize
!= 0 ) {
529 uint32_t count
= sect
->size() / elementSize
;
530 if ( (count
*elementSize
) != sect
->size() )
531 throwf("%s section size is not an even multiple of element size", sect
->sectname());
532 if ( (start
+count
) > fIndirectTableCount
)
533 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
537 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
542 template <typename A
>
543 void MachOChecker
<A
>::checkSymbolTable()
545 // verify no duplicate external symbol names
546 if ( fDynamicSymbolTable
!= NULL
) {
547 StringSet externalNames
;
548 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
549 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
550 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
) {
551 const char* symName
= &fStrings
[p
->n_strx()];
552 if ( externalNames
.find(symName
) != externalNames
.end() )
553 throwf("duplicate external symbol: %s", symName
);
554 externalNames
.insert(symName
);
561 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
563 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
564 return fFirstWritableSegment
->vmaddr();
566 return fFirstSegment
->vmaddr();
570 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
572 if ( fWriteableSegmentWithAddrOver4G
)
573 return fFirstWritableSegment
->vmaddr();
575 return fFirstSegment
->vmaddr();
579 x86::P::uint_t MachOChecker
<x86
>::relocBase()
581 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
582 return fFirstWritableSegment
->vmaddr();
584 return fFirstSegment
->vmaddr();
588 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
590 // check for split-seg
591 return fFirstWritableSegment
->vmaddr();
595 template <typename A
>
596 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
598 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
599 const uint32_t cmd_count
= fHeader
->ncmds();
600 const macho_load_command
<P
>* cmd
= cmds
;
601 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
602 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
603 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
604 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
605 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) )
609 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
616 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
618 if ( reloc
->r_length() != 2 )
619 throw "bad external relocation length";
620 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
621 throw "unknown external relocation type";
622 if ( reloc
->r_pcrel() != 0 )
623 throw "bad external relocation pc_rel";
624 if ( reloc
->r_extern() == 0 )
625 throw "local relocation found with external relocations";
626 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
627 throw "external relocation address not in writable segment";
628 // FIX: check r_symbol
632 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
634 if ( reloc
->r_length() != 3 )
635 throw "bad external relocation length";
636 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
637 throw "unknown external relocation type";
638 if ( reloc
->r_pcrel() != 0 )
639 throw "bad external relocation pc_rel";
640 if ( reloc
->r_extern() == 0 )
641 throw "local relocation found with external relocations";
642 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
643 throw "external relocation address not in writable segment";
644 // FIX: check r_symbol
648 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
650 if ( reloc
->r_length() != 2 )
651 throw "bad external relocation length";
652 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
653 throw "unknown external relocation type";
654 if ( reloc
->r_pcrel() != 0 )
655 throw "bad external relocation pc_rel";
656 if ( reloc
->r_extern() == 0 )
657 throw "local relocation found with external relocations";
658 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
659 throw "external relocation address not in writable segment";
660 // FIX: check r_symbol
665 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
667 if ( reloc
->r_length() != 3 )
668 throw "bad external relocation length";
669 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
670 throw "unknown external relocation type";
671 if ( reloc
->r_pcrel() != 0 )
672 throw "bad external relocation pc_rel";
673 if ( reloc
->r_extern() == 0 )
674 throw "local relocation found with external relocations";
675 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
676 throw "exernal relocation address not in writable segment";
677 // FIX: check r_symbol
681 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
683 if ( reloc
->r_address() & R_SCATTERED
) {
685 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
691 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
692 throw "local relocation address not in writable segment";
698 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
700 if ( reloc
->r_length() != 3 )
701 throw "bad local relocation length";
702 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
703 throw "unknown local relocation type";
704 if ( reloc
->r_pcrel() != 0 )
705 throw "bad local relocation pc_rel";
706 if ( reloc
->r_extern() != 0 )
707 throw "external relocation found with local relocations";
708 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
709 throw "local relocation address not in writable segment";
713 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
719 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
721 if ( reloc
->r_length() != 3 )
722 throw "bad local relocation length";
723 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
724 throw "unknown local relocation type";
725 if ( reloc
->r_pcrel() != 0 )
726 throw "bad local relocation pc_rel";
727 if ( reloc
->r_extern() != 0 )
728 throw "external relocation found with local relocations";
729 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
730 throw "local relocation address not in writable segment";
735 template <typename A
>
736 void MachOChecker
<A
>::checkRelocations()
738 // external relocations should be sorted to minimize dyld symbol lookups
739 // therefore every reloc with the same r_symbolnum value should be contiguous
740 std::set
<uint32_t> previouslySeenSymbolIndexes
;
741 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
742 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
743 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
744 this->checkExternalReloation(reloc
);
745 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
746 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
747 throw "external relocations not sorted";
748 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
749 lastSymbolIndex
= reloc
->r_symbolnum();
753 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
754 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
755 this->checkLocalReloation(reloc
);
760 static void check(const char* path
)
762 struct stat stat_buf
;
765 int fd
= ::open(path
, O_RDONLY
, 0);
767 throw "cannot open file";
768 ::fstat(fd
, &stat_buf
);
769 uint32_t length
= stat_buf
.st_size
;
770 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
771 if ( p
== ((uint8_t*)(-1)) )
772 throw "cannot map file";
774 const mach_header
* mh
= (mach_header
*)p
;
775 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
776 const struct fat_header
* fh
= (struct fat_header
*)p
;
777 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
778 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
779 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
780 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
781 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
784 case CPU_TYPE_POWERPC
:
785 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
786 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
788 throw "in universal file, ppc slice does not contain ppc mach-o";
791 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
792 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
794 throw "in universal file, i386 slice does not contain i386 mach-o";
796 case CPU_TYPE_POWERPC64
:
797 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
798 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
800 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
802 case CPU_TYPE_X86_64
:
803 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
804 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
806 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
809 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
813 else if ( MachOChecker
<x86
>::validFile(p
) ) {
814 MachOChecker
<x86
>::make(p
, length
, path
);
816 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
817 MachOChecker
<ppc
>::make(p
, length
, path
);
819 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
820 MachOChecker
<ppc64
>::make(p
, length
, path
);
822 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
823 MachOChecker
<x86_64
>::make(p
, length
, path
);
826 throw "not a known file type";
829 catch (const char* msg
) {
830 throwf("%s in %s", msg
, path
);
835 int main(int argc
, const char* argv
[])
838 for(int i
=1; i
< argc
; ++i
) {
839 const char* arg
= argv
[i
];
840 if ( arg
[0] == '-' ) {
841 if ( strcmp(arg
, "-no_content") == 0 ) {
845 throwf("unknown option: %s\n", arg
);
853 catch (const char* msg
) {
854 fprintf(stderr
, "machocheck failed: %s\n", msg
);