1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2007 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
36 #include <ext/hash_set>
38 #include "MachOFileAbstraction.hpp"
39 #include "Architectures.hpp"
42 __attribute__((noreturn
))
43 void throwf(const char* format
, ...)
47 va_start(list
, format
);
48 vasprintf(&p
, format
, list
);
60 static bool validFile(const uint8_t* fileContent
);
61 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
62 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
63 virtual ~MachOChecker() {}
67 typedef typename
A::P P
;
68 typedef typename
A::P::E E
;
69 typedef typename
A::P::uint_t pint_t
;
74 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
77 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
79 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
80 void checkMachHeader();
81 void checkLoadCommands();
82 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
83 uint8_t loadCommandSizeMask();
84 void checkSymbolTable();
85 void checkIndirectSymbolTable();
86 void checkRelocations();
87 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
88 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
90 bool addressInWritableSegment(pint_t address
);
93 const macho_header
<P
>* fHeader
;
96 const char* fStringsEnd
;
97 const macho_nlist
<P
>* fSymbols
;
98 uint32_t fSymbolCount
;
99 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
100 const uint32_t* fIndirectTable
;
101 uint32_t fIndirectTableCount
;
102 const macho_relocation_info
<P
>* fLocalRelocations
;
103 uint32_t fLocalRelocationsCount
;
104 const macho_relocation_info
<P
>* fExternalRelocations
;
105 uint32_t fExternalRelocationsCount
;
106 bool fWriteableSegmentWithAddrOver4G
;
107 const macho_segment_command
<P
>* fFirstSegment
;
108 const macho_segment_command
<P
>* fFirstWritableSegment
;
109 uint32_t fSectionCount
;
115 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
117 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
118 if ( header
->magic() != MH_MAGIC
)
120 if ( header
->cputype() != CPU_TYPE_POWERPC
)
122 switch (header
->filetype()) {
133 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
135 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
136 if ( header
->magic() != MH_MAGIC_64
)
138 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
140 switch (header
->filetype()) {
151 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
153 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
154 if ( header
->magic() != MH_MAGIC
)
156 if ( header
->cputype() != CPU_TYPE_I386
)
158 switch (header
->filetype()) {
169 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
171 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
172 if ( header
->magic() != MH_MAGIC_64
)
174 if ( header
->cputype() != CPU_TYPE_X86_64
)
176 switch (header
->filetype()) {
187 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
189 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
190 if ( header
->magic() != MH_MAGIC
)
192 if ( header
->cputype() != CPU_TYPE_ARM
)
194 switch (header
->filetype()) {
204 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
205 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
206 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
207 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
208 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
210 template <typename A
>
211 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
212 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
213 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
214 fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
), fSectionCount(0)
217 if ( ! validFile(fileContent
) )
218 throw "not a mach-o file that can be checked";
220 fPath
= strdup(path
);
221 fHeader
= (const macho_header
<P
>*)fileContent
;
223 // sanity check header
226 // check load commands
229 checkIndirectSymbolTable();
237 template <typename A
>
238 void MachOChecker
<A
>::checkMachHeader()
240 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
241 throw "sizeofcmds in mach_header is larger than file";
243 uint32_t flags
= fHeader
->flags();
244 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFFC00000;
245 if ( flags
& invalidBits
)
246 throw "invalid bits in mach_header flags";
247 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
248 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
251 template <typename A
>
252 void MachOChecker
<A
>::checkLoadCommands()
254 // check that all load commands fit within the load command space file
255 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
256 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
257 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
258 const uint32_t cmd_count
= fHeader
->ncmds();
259 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
260 const macho_load_command
<P
>* cmd
= cmds
;
261 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
262 uint32_t size
= cmd
->cmdsize();
263 if ( (size
& this->loadCommandSizeMask()) != 0 )
264 throwf("load command #%d has a unaligned size", i
);
265 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
266 if ( endOfCmd
> endOfLoadCommands
)
267 throwf("load command #%d extends beyond the end of the load commands", i
);
268 if ( endOfCmd
> endOfFile
)
269 throwf("load command #%d extends beyond the end of the file", i
);
270 switch ( cmd
->cmd() ) {
271 case macho_segment_command
<P
>::CMD
:
277 case LC_LOAD_DYLINKER
:
279 case macho_routines_command
<P
>::CMD
:
280 case LC_SUB_FRAMEWORK
:
282 case LC_TWOLEVEL_HINTS
:
283 case LC_PREBIND_CKSUM
:
284 case LC_LOAD_WEAK_DYLIB
:
285 case LC_LAZY_LOAD_DYLIB
:
287 case LC_REEXPORT_DYLIB
:
288 case LC_SEGMENT_SPLIT_INFO
:
289 case LC_CODE_SIGNATURE
:
291 case LC_DYLD_INFO_ONLY
:
293 case LC_ENCRYPTION_INFO
:
294 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
296 case LC_SUB_UMBRELLA
:
298 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
299 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";
302 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
304 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
309 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
310 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
311 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
312 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
313 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
314 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
315 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
316 throw "invalid segment load command size";
318 // see if this overlaps another segment address range
319 uint64_t startAddr
= segCmd
->vmaddr();
320 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
321 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
322 if ( it
->first
< startAddr
) {
323 if ( it
->second
> startAddr
)
324 throw "overlapping segment vm addresses";
326 else if ( it
->first
> startAddr
) {
327 if ( it
->first
< endAddr
)
328 throw "overlapping segment vm addresses";
331 throw "overlapping segment vm addresses";
333 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
335 // see if this overlaps another segment file offset range
336 uint64_t startOffset
= segCmd
->fileoff();
337 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
338 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
339 if ( it
->first
< startOffset
) {
340 if ( it
->second
> startOffset
)
341 throw "overlapping segment file data";
343 else if ( it
->first
> startOffset
) {
344 if ( it
->first
< endOffset
)
345 throw "overlapping segment file data";
348 throw "overlapping segment file data";
350 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
351 // check is within file bounds
352 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
353 throw "segment file data is past end of file";
355 // verify it fits in file
356 if ( startOffset
> fLength
)
357 throw "segment fileoff does not fit in file";
358 if ( endOffset
> fLength
)
359 throw "segment fileoff+filesize does not fit in file";
361 // keep LINKEDIT segment
362 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
363 linkEditSegment
= segCmd
;
365 // cache interesting segments
366 if ( fFirstSegment
== NULL
)
367 fFirstSegment
= segCmd
;
368 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
369 if ( fFirstWritableSegment
== NULL
)
370 fFirstWritableSegment
= segCmd
;
371 if ( segCmd
->vmaddr() > 0x100000000ULL
)
372 fWriteableSegmentWithAddrOver4G
= true;
375 // check section ranges
376 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
377 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
378 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
379 // check all sections are within segment
380 if ( sect
->addr() < startAddr
)
381 throwf("section %s vm address not within segment", sect
->sectname());
382 if ( (sect
->addr()+sect
->size()) > endAddr
)
383 throwf("section %s vm address not within segment", sect
->sectname());
384 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
) && (segCmd
->filesize() != 0) ) {
385 if ( sect
->offset() < startOffset
)
386 throwf("section %s file offset not within segment", sect
->sectname());
387 if ( (sect
->offset()+sect
->size()) > endOffset
)
388 throwf("section %s file offset not within segment", sect
->sectname());
390 checkSection(segCmd
, sect
);
394 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
397 // verify there was a LINKEDIT segment
398 if ( linkEditSegment
== NULL
)
399 throw "no __LINKEDIT segment";
401 // checks for executables
402 bool isStaticExecutable
= false;
403 if ( fHeader
->filetype() == MH_EXECUTE
) {
404 isStaticExecutable
= true;
406 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
407 switch ( cmd
->cmd() ) {
408 case LC_LOAD_DYLINKER
:
409 // the existence of a dyld load command makes a executable dynamic
410 isStaticExecutable
= false;
413 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
415 if ( isStaticExecutable
) {
416 if ( fHeader
->flags() != MH_NOUNDEFS
)
417 throw "invalid bits in mach_header flags for static executable";
421 // verify encryption info
422 if ( encryption_info
!= NULL
) {
423 if ( fHeader
->filetype() != MH_EXECUTE
)
424 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
425 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
426 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
427 if ( (encryption_info
->cryptoff() % 4096) != 0 )
428 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
429 if ( (encryption_info
->cryptsize() % 4096) != 0 )
430 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
431 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
432 it
!= segmentFileOffsetRanges
.end(); ++it
) {
433 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
434 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
435 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
440 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
442 bool foundDynamicSymTab
= false;
443 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
444 switch ( cmd
->cmd() ) {
447 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
448 fSymbolCount
= symtab
->nsyms();
449 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
450 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
451 throw "symbol table not in __LINKEDIT";
452 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
453 throw "symbol table end not in __LINKEDIT";
454 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
455 throw "symbol table start not pointer aligned";
456 fStrings
= (char*)fHeader
+ symtab
->stroff();
457 fStringsEnd
= fStrings
+ symtab
->strsize();
458 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
459 throw "string pool not in __LINKEDIT";
460 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
461 throw "string pool extends beyond __LINKEDIT";
462 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
463 throw "string pool start not pointer aligned";
464 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
465 throw "string pool size not a multiple of pointer size";
470 if ( isStaticExecutable
)
471 throw "LC_DYSYMTAB should not be used in static executable";
472 foundDynamicSymTab
= true;
473 fDynamicSymbolTable
= (struct macho_dysymtab_command
<P
>*)cmd
;
474 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
475 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
476 if ( fIndirectTableCount
!= 0 ) {
477 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
478 throw "indirect symbol table not in __LINKEDIT";
479 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
480 throw "indirect symbol table not in __LINKEDIT";
481 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
482 throw "indirect symbol table not pointer aligned";
484 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
485 if ( fLocalRelocationsCount
!= 0 ) {
486 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
487 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
488 throw "local relocations not in __LINKEDIT";
489 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
490 throw "local relocations not in __LINKEDIT";
491 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
492 throw "local relocations table not pointer aligned";
494 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
495 if ( fExternalRelocationsCount
!= 0 ) {
496 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
497 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
498 throw "external relocations not in __LINKEDIT";
499 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
500 throw "external relocations not in __LINKEDIT";
501 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
502 throw "external relocations table not pointer aligned";
506 case LC_SEGMENT_SPLIT_INFO
:
508 if ( isStaticExecutable
)
509 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
510 const macho_linkedit_data_command
<P
>* info
= (struct macho_linkedit_data_command
<P
>*)cmd
;
511 if ( info
->dataoff() < linkEditSegment
->fileoff() )
512 throw "split seg info not in __LINKEDIT";
513 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
514 throw "split seg info not in __LINKEDIT";
515 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
516 throw "split seg info table not pointer aligned";
517 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
518 throw "split seg info size not a multiple of pointer size";
522 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
524 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
525 throw "missing dynamic symbol table";
526 if ( fStrings
== NULL
)
527 throw "missing symbol table";
531 template <typename A
>
532 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
534 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
535 if ( sectionType
== S_ZEROFILL
) {
536 if ( sect
->offset() != 0 )
537 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
540 // more section tests here
543 template <typename A
>
544 void MachOChecker
<A
>::checkIndirectSymbolTable()
546 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
547 const uint32_t cmd_count
= fHeader
->ncmds();
548 const macho_load_command
<P
>* cmd
= cmds
;
549 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
550 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
551 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
552 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
553 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
554 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
555 // make sure all magic sections that use indirect symbol table fit within it
557 uint32_t elementSize
= 0;
558 switch ( sect
->flags() & SECTION_TYPE
) {
560 elementSize
= sect
->reserved2();
561 start
= sect
->reserved1();
563 case S_LAZY_SYMBOL_POINTERS
:
564 case S_NON_LAZY_SYMBOL_POINTERS
:
565 elementSize
= sizeof(pint_t
);
566 start
= sect
->reserved1();
569 if ( elementSize
!= 0 ) {
570 uint32_t count
= sect
->size() / elementSize
;
571 if ( (count
*elementSize
) != sect
->size() )
572 throwf("%s section size is not an even multiple of element size", sect
->sectname());
573 if ( (start
+count
) > fIndirectTableCount
)
574 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
578 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
583 template <typename A
>
584 void MachOChecker
<A
>::checkSymbolTable()
586 // verify no duplicate external symbol names
587 if ( fDynamicSymbolTable
!= NULL
) {
588 StringSet externalNames
;
589 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
590 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
591 int i
= fDynamicSymbolTable
->iextdefsym();
592 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
593 const char* symName
= &fStrings
[p
->n_strx()];
594 if ( symName
> fStringsEnd
)
595 throw "string index out of range";
596 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
597 if ( externalNames
.find(symName
) != externalNames
.end() )
598 throwf("duplicate external symbol: %s", symName
);
599 externalNames
.insert(symName
);
601 // verify no undefines with same name as an external symbol
602 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
603 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
604 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
605 const char* symName
= &fStrings
[p
->n_strx()];
606 if ( symName
> fStringsEnd
)
607 throw "string index out of range";
608 if ( externalNames
.find(symName
) != externalNames
.end() )
609 throwf("undefine with same name as external symbol: %s", symName
);
611 // verify all N_SECT values are valid
612 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
613 uint8_t type
= p
->n_type();
614 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
615 if ( p
->n_sect() > fSectionCount
) {
616 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
625 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
627 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
628 return fFirstWritableSegment
->vmaddr();
630 return fFirstSegment
->vmaddr();
634 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
636 if ( fWriteableSegmentWithAddrOver4G
)
637 return fFirstWritableSegment
->vmaddr();
639 return fFirstSegment
->vmaddr();
643 x86::P::uint_t MachOChecker
<x86
>::relocBase()
645 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
646 return fFirstWritableSegment
->vmaddr();
648 return fFirstSegment
->vmaddr();
652 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
654 // check for split-seg
655 return fFirstWritableSegment
->vmaddr();
659 arm::P::uint_t MachOChecker
<arm
>::relocBase()
661 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
662 return fFirstWritableSegment
->vmaddr();
664 return fFirstSegment
->vmaddr();
668 template <typename A
>
669 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
671 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
672 const uint32_t cmd_count
= fHeader
->ncmds();
673 const macho_load_command
<P
>* cmd
= cmds
;
674 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
675 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
676 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
677 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
678 // if segment is writable, we are fine
679 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
681 // could be a text reloc, make sure section bit is set
682 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
683 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
684 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
685 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
686 // found section for this address, if has relocs we are fine
687 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
692 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
699 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
701 if ( reloc
->r_length() != 2 )
702 throw "bad external relocation length";
703 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
704 throw "unknown external relocation type";
705 if ( reloc
->r_pcrel() != 0 )
706 throw "bad external relocation pc_rel";
707 if ( reloc
->r_extern() == 0 )
708 throw "local relocation found with external relocations";
709 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
710 throw "external relocation address not in writable segment";
711 // FIX: check r_symbol
715 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
717 if ( reloc
->r_length() != 3 )
718 throw "bad external relocation length";
719 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
720 throw "unknown external relocation type";
721 if ( reloc
->r_pcrel() != 0 )
722 throw "bad external relocation pc_rel";
723 if ( reloc
->r_extern() == 0 )
724 throw "local relocation found with external relocations";
725 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
726 throw "external relocation address not in writable segment";
727 // FIX: check r_symbol
731 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
733 if ( reloc
->r_length() != 2 )
734 throw "bad external relocation length";
735 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
736 throw "unknown external relocation type";
737 if ( reloc
->r_pcrel() != 0 )
738 throw "bad external relocation pc_rel";
739 if ( reloc
->r_extern() == 0 )
740 throw "local relocation found with external relocations";
741 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
742 throw "external relocation address not in writable segment";
743 // FIX: check r_symbol
748 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
750 if ( reloc
->r_length() != 3 )
751 throw "bad external relocation length";
752 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
753 throw "unknown external relocation type";
754 if ( reloc
->r_pcrel() != 0 )
755 throw "bad external relocation pc_rel";
756 if ( reloc
->r_extern() == 0 )
757 throw "local relocation found with external relocations";
758 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
759 throw "exernal relocation address not in writable segment";
760 // FIX: check r_symbol
764 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
766 if ( reloc
->r_length() != 2 )
767 throw "bad external relocation length";
768 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
769 throw "unknown external relocation type";
770 if ( reloc
->r_pcrel() != 0 )
771 throw "bad external relocation pc_rel";
772 if ( reloc
->r_extern() == 0 )
773 throw "local relocation found with external relocations";
774 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
775 throw "external relocation address not in writable segment";
776 // FIX: check r_symbol
781 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
783 if ( reloc
->r_address() & R_SCATTERED
) {
785 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
790 // ignore pair relocs
791 if ( reloc
->r_type() == PPC_RELOC_PAIR
)
794 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
795 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
801 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
803 if ( reloc
->r_length() != 3 )
804 throw "bad local relocation length";
805 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
806 throw "unknown local relocation type";
807 if ( reloc
->r_pcrel() != 0 )
808 throw "bad local relocation pc_rel";
809 if ( reloc
->r_extern() != 0 )
810 throw "external relocation found with local relocations";
811 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
812 throw "local relocation address not in writable segment";
816 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
822 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
824 if ( reloc
->r_length() != 3 )
825 throw "bad local relocation length";
826 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
827 throw "unknown local relocation type";
828 if ( reloc
->r_pcrel() != 0 )
829 throw "bad local relocation pc_rel";
830 if ( reloc
->r_extern() != 0 )
831 throw "external relocation found with local relocations";
832 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
833 throw "local relocation address not in writable segment";
837 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
839 if ( reloc
->r_address() & R_SCATTERED
) {
841 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
842 if ( sreloc
->r_length() != 2 )
843 throw "bad local scattered relocation length";
844 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
845 throw "bad local scattered relocation type";
848 if ( reloc
->r_length() != 2 )
849 throw "bad local relocation length";
850 if ( reloc
->r_extern() != 0 )
851 throw "external relocation found with local relocations";
852 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
853 throw "local relocation address not in writable segment";
857 template <typename A
>
858 void MachOChecker
<A
>::checkRelocations()
860 // external relocations should be sorted to minimize dyld symbol lookups
861 // therefore every reloc with the same r_symbolnum value should be contiguous
862 std::set
<uint32_t> previouslySeenSymbolIndexes
;
863 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
864 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
865 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
866 this->checkExternalReloation(reloc
);
867 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
868 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
869 throw "external relocations not sorted";
870 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
871 lastSymbolIndex
= reloc
->r_symbolnum();
875 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
876 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
877 this->checkLocalReloation(reloc
);
882 static void check(const char* path
)
884 struct stat stat_buf
;
887 int fd
= ::open(path
, O_RDONLY
, 0);
889 throw "cannot open file";
890 if ( ::fstat(fd
, &stat_buf
) != 0 )
891 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
892 uint32_t length
= stat_buf
.st_size
;
893 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
894 if ( p
== ((uint8_t*)(-1)) )
895 throw "cannot map file";
897 const mach_header
* mh
= (mach_header
*)p
;
898 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
899 const struct fat_header
* fh
= (struct fat_header
*)p
;
900 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
901 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
902 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
903 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
904 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
907 case CPU_TYPE_POWERPC
:
908 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
909 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
911 throw "in universal file, ppc slice does not contain ppc mach-o";
914 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
915 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
917 throw "in universal file, i386 slice does not contain i386 mach-o";
919 case CPU_TYPE_POWERPC64
:
920 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
921 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
923 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
925 case CPU_TYPE_X86_64
:
926 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
927 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
929 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
932 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
933 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
935 throw "in universal file, arm slice does not contain arm mach-o";
938 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
942 else if ( MachOChecker
<x86
>::validFile(p
) ) {
943 MachOChecker
<x86
>::make(p
, length
, path
);
945 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
946 MachOChecker
<ppc
>::make(p
, length
, path
);
948 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
949 MachOChecker
<ppc64
>::make(p
, length
, path
);
951 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
952 MachOChecker
<x86_64
>::make(p
, length
, path
);
954 else if ( MachOChecker
<arm
>::validFile(p
) ) {
955 MachOChecker
<arm
>::make(p
, length
, path
);
958 throw "not a known file type";
961 catch (const char* msg
) {
962 throwf("%s in %s", msg
, path
);
967 int main(int argc
, const char* argv
[])
970 for(int i
=1; i
< argc
; ++i
) {
971 const char* arg
= argv
[i
];
972 if ( arg
[0] == '-' ) {
973 if ( strcmp(arg
, "-no_content") == 0 ) {
977 throwf("unknown option: %s\n", arg
);
985 catch (const char* msg
) {
986 fprintf(stderr
, "machocheck failed: %s\n", msg
);