1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2010 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
);
55 static uint64_t read_uleb128(const uint8_t*& p
, const uint8_t* end
)
61 throwf("malformed uleb128");
63 uint64_t slice
= *p
& 0x7f;
65 if (bit
>= 64 || slice
<< bit
>> bit
!= slice
)
66 throwf("uleb128 too big");
68 result
|= (slice
<< bit
);
80 static bool validFile(const uint8_t* fileContent
);
81 static MachOChecker
<A
>* make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
82 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); }
83 virtual ~MachOChecker() {}
87 typedef typename
A::P P
;
88 typedef typename
A::P::E E
;
89 typedef typename
A::P::uint_t pint_t
;
94 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); }
97 typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
> StringSet
;
99 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
);
100 void checkMachHeader();
101 void checkLoadCommands();
102 void checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
);
103 uint8_t loadCommandSizeMask();
104 void checkSymbolTable();
105 void checkIndirectSymbolTable();
106 void checkRelocations();
107 void checkExternalReloation(const macho_relocation_info
<P
>* reloc
);
108 void checkLocalReloation(const macho_relocation_info
<P
>* reloc
);
110 bool addressInWritableSegment(pint_t address
);
111 bool hasTextRelocInRange(pint_t start
, pint_t end
);
112 pint_t
segStartAddress(uint8_t segIndex
);
115 const macho_header
<P
>* fHeader
;
117 const char* fStrings
;
118 const char* fStringsEnd
;
119 const macho_nlist
<P
>* fSymbols
;
120 uint32_t fSymbolCount
;
121 const macho_dysymtab_command
<P
>* fDynamicSymbolTable
;
122 const uint32_t* fIndirectTable
;
123 uint32_t fIndirectTableCount
;
124 const macho_relocation_info
<P
>* fLocalRelocations
;
125 uint32_t fLocalRelocationsCount
;
126 const macho_relocation_info
<P
>* fExternalRelocations
;
127 uint32_t fExternalRelocationsCount
;
128 bool fWriteableSegmentWithAddrOver4G
;
129 const macho_segment_command
<P
>* fFirstSegment
;
130 const macho_segment_command
<P
>* fFirstWritableSegment
;
131 const macho_dyld_info_command
<P
>* fDyldInfo
;
132 uint32_t fSectionCount
;
133 std::vector
<const macho_segment_command
<P
>*>fSegments
;
139 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
)
141 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
142 if ( header
->magic() != MH_MAGIC
)
144 if ( header
->cputype() != CPU_TYPE_POWERPC
)
146 switch (header
->filetype()) {
157 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
)
159 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
160 if ( header
->magic() != MH_MAGIC_64
)
162 if ( header
->cputype() != CPU_TYPE_POWERPC64
)
164 switch (header
->filetype()) {
175 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
)
177 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
178 if ( header
->magic() != MH_MAGIC
)
180 if ( header
->cputype() != CPU_TYPE_I386
)
182 switch (header
->filetype()) {
193 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
)
195 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
196 if ( header
->magic() != MH_MAGIC_64
)
198 if ( header
->cputype() != CPU_TYPE_X86_64
)
200 switch (header
->filetype()) {
211 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
)
213 const macho_header
<P
>* header
= (const macho_header
<P
>*)fileContent
;
214 if ( header
->magic() != MH_MAGIC
)
216 if ( header
->cputype() != CPU_TYPE_ARM
)
218 switch (header
->filetype()) {
228 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask() { return 0x03; }
229 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask() { return 0x07; }
230 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask() { return 0x03; }
231 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; }
232 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask() { return 0x03; }
234 template <typename A
>
235 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)
236 : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0),
237 fLocalRelocations(NULL
), fLocalRelocationsCount(0), fExternalRelocations(NULL
), fExternalRelocationsCount(0),
238 fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
), fDyldInfo(NULL
), fSectionCount(0)
241 if ( ! validFile(fileContent
) )
242 throw "not a mach-o file that can be checked";
244 fPath
= strdup(path
);
245 fHeader
= (const macho_header
<P
>*)fileContent
;
247 // sanity check header
250 // check load commands
253 checkIndirectSymbolTable();
261 template <typename A
>
262 void MachOChecker
<A
>::checkMachHeader()
264 if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength
)
265 throw "sizeofcmds in mach_header is larger than file";
267 uint32_t flags
= fHeader
->flags();
268 const uint32_t invalidBits
= MH_INCRLINK
| MH_LAZY_INIT
| 0xFE000000;
269 if ( flags
& invalidBits
)
270 throw "invalid bits in mach_header flags";
271 if ( (flags
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )
272 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs";
275 template <typename A
>
276 void MachOChecker
<A
>::checkLoadCommands()
278 // check that all load commands fit within the load command space file
279 const macho_encryption_info_command
<P
>* encryption_info
= NULL
;
280 const uint8_t* const endOfFile
= (uint8_t*)fHeader
+ fLength
;
281 const uint8_t* const endOfLoadCommands
= (uint8_t*)fHeader
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds();
282 const uint32_t cmd_count
= fHeader
->ncmds();
283 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
284 const macho_load_command
<P
>* cmd
= cmds
;
285 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
286 uint32_t size
= cmd
->cmdsize();
287 if ( (size
& this->loadCommandSizeMask()) != 0 )
288 throwf("load command #%d has a unaligned size", i
);
289 const uint8_t* endOfCmd
= ((uint8_t*)cmd
)+cmd
->cmdsize();
290 if ( endOfCmd
> endOfLoadCommands
)
291 throwf("load command #%d extends beyond the end of the load commands", i
);
292 if ( endOfCmd
> endOfFile
)
293 throwf("load command #%d extends beyond the end of the file", i
);
294 switch ( cmd
->cmd() ) {
295 case macho_segment_command
<P
>::CMD
:
301 case LC_LOAD_DYLINKER
:
303 case macho_routines_command
<P
>::CMD
:
304 case LC_SUB_FRAMEWORK
:
306 case LC_TWOLEVEL_HINTS
:
307 case LC_PREBIND_CKSUM
:
308 case LC_LOAD_WEAK_DYLIB
:
309 case LC_LAZY_LOAD_DYLIB
:
311 case LC_REEXPORT_DYLIB
:
312 case LC_SEGMENT_SPLIT_INFO
:
313 case LC_CODE_SIGNATURE
:
314 case LC_LOAD_UPWARD_DYLIB
:
315 case LC_VERSION_MIN_MACOSX
:
316 case LC_VERSION_MIN_IPHONEOS
:
317 case LC_FUNCTION_STARTS
:
320 case LC_DYLD_INFO_ONLY
:
321 fDyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
323 case LC_ENCRYPTION_INFO
:
324 encryption_info
= (macho_encryption_info_command
<P
>*)cmd
;
326 case LC_SUB_UMBRELLA
:
328 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS
)
329 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";
332 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd());
334 cmd
= (const macho_load_command
<P
>*)endOfCmd
;
339 std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
;
340 std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
;
341 const macho_segment_command
<P
>* linkEditSegment
= NULL
;
342 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
343 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
344 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
345 fSegments
.push_back(segCmd
);
346 if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) )
347 throw "invalid segment load command size";
349 // see if this overlaps another segment address range
350 uint64_t startAddr
= segCmd
->vmaddr();
351 uint64_t endAddr
= startAddr
+ segCmd
->vmsize();
352 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentAddressRanges
.begin(); it
!= segmentAddressRanges
.end(); ++it
) {
353 if ( it
->first
< startAddr
) {
354 if ( it
->second
> startAddr
)
355 throw "overlapping segment vm addresses";
357 else if ( it
->first
> startAddr
) {
358 if ( it
->first
< endAddr
)
359 throw "overlapping segment vm addresses";
362 throw "overlapping segment vm addresses";
364 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
));
366 // see if this overlaps another segment file offset range
367 uint64_t startOffset
= segCmd
->fileoff();
368 uint64_t endOffset
= startOffset
+ segCmd
->filesize();
369 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin(); it
!= segmentFileOffsetRanges
.end(); ++it
) {
370 if ( it
->first
< startOffset
) {
371 if ( it
->second
> startOffset
)
372 throw "overlapping segment file data";
374 else if ( it
->first
> startOffset
) {
375 if ( it
->first
< endOffset
)
376 throw "overlapping segment file data";
379 throw "overlapping segment file data";
381 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
));
382 // check is within file bounds
383 if ( (startOffset
> fLength
) || (endOffset
> fLength
) )
384 throw "segment file data is past end of file";
386 // verify it fits in file
387 if ( startOffset
> fLength
)
388 throw "segment fileoff does not fit in file";
389 if ( endOffset
> fLength
)
390 throw "segment fileoff+filesize does not fit in file";
392 // keep LINKEDIT segment
393 if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 )
394 linkEditSegment
= segCmd
;
396 // cache interesting segments
397 if ( fFirstSegment
== NULL
)
398 fFirstSegment
= segCmd
;
399 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) {
400 if ( fFirstWritableSegment
== NULL
)
401 fFirstWritableSegment
= segCmd
;
402 if ( segCmd
->vmaddr() > 0x100000000ULL
)
403 fWriteableSegmentWithAddrOver4G
= true;
406 // check section ranges
407 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
408 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
409 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
410 // check all sections are within segment
411 if ( sect
->addr() < startAddr
)
412 throwf("section %s vm address not within segment", sect
->sectname());
413 if ( (sect
->addr()+sect
->size()) > endAddr
)
414 throwf("section %s vm address not within segment", sect
->sectname());
415 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
)
416 && ((sect
->flags() & SECTION_TYPE
) != S_THREAD_LOCAL_ZEROFILL
)
417 && (segCmd
->filesize() != 0) ) {
418 if ( sect
->offset() < startOffset
)
419 throwf("section %s file offset not within segment", sect
->sectname());
420 if ( (sect
->offset()+sect
->size()) > endOffset
)
421 throwf("section %s file offset not within segment", sect
->sectname());
423 checkSection(segCmd
, sect
);
427 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
430 // verify there was a LINKEDIT segment
431 if ( linkEditSegment
== NULL
)
432 throw "no __LINKEDIT segment";
434 // checks for executables
435 bool isStaticExecutable
= false;
436 if ( fHeader
->filetype() == MH_EXECUTE
) {
437 isStaticExecutable
= true;
439 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
440 switch ( cmd
->cmd() ) {
441 case LC_LOAD_DYLINKER
:
442 // the existence of a dyld load command makes a executable dynamic
443 isStaticExecutable
= false;
446 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
448 if ( isStaticExecutable
) {
449 if ( fHeader
->flags() != MH_NOUNDEFS
)
450 throw "invalid bits in mach_header flags for static executable";
454 // verify encryption info
455 if ( encryption_info
!= NULL
) {
456 if ( fHeader
->filetype() != MH_EXECUTE
)
457 throw "LC_ENCRYPTION_INFO load command is only legal in main executables";
458 if ( encryption_info
->cryptoff() < (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) )
459 throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands";
460 if ( (encryption_info
->cryptoff() % 4096) != 0 )
461 throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned";
462 if ( (encryption_info
->cryptsize() % 4096) != 0 )
463 throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized";
464 for (typename
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it
= segmentFileOffsetRanges
.begin();
465 it
!= segmentFileOffsetRanges
.end(); ++it
) {
466 if ( (it
->first
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) {
467 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second
)
468 throw "LC_ENCRYPTION_INFO load command is not contained within one segment";
473 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
475 bool foundDynamicSymTab
= false;
476 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
477 switch ( cmd
->cmd() ) {
480 const macho_symtab_command
<P
>* symtab
= (macho_symtab_command
<P
>*)cmd
;
481 fSymbolCount
= symtab
->nsyms();
482 fSymbols
= (const macho_nlist
<P
>*)((char*)fHeader
+ symtab
->symoff());
483 if ( symtab
->symoff() < linkEditSegment
->fileoff() )
484 throw "symbol table not in __LINKEDIT";
485 if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
486 throw "symbol table end not in __LINKEDIT";
487 if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 )
488 throw "symbol table start not pointer aligned";
489 fStrings
= (char*)fHeader
+ symtab
->stroff();
490 fStringsEnd
= fStrings
+ symtab
->strsize();
491 if ( symtab
->stroff() < linkEditSegment
->fileoff() )
492 throw "string pool not in __LINKEDIT";
493 if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
494 throw "string pool extends beyond __LINKEDIT";
495 if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed
496 throw "string pool start not pointer aligned";
497 if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )
498 throw "string pool size not a multiple of pointer size";
503 if ( isStaticExecutable
)
504 throw "LC_DYSYMTAB should not be used in static executable";
505 foundDynamicSymTab
= true;
506 fDynamicSymbolTable
= (macho_dysymtab_command
<P
>*)cmd
;
507 fIndirectTable
= (uint32_t*)((char*)fHeader
+ fDynamicSymbolTable
->indirectsymoff());
508 fIndirectTableCount
= fDynamicSymbolTable
->nindirectsyms();
509 if ( fIndirectTableCount
!= 0 ) {
510 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() )
511 throw "indirect symbol table not in __LINKEDIT";
512 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
513 throw "indirect symbol table not in __LINKEDIT";
514 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 )
515 throw "indirect symbol table not pointer aligned";
517 fLocalRelocationsCount
= fDynamicSymbolTable
->nlocrel();
518 if ( fLocalRelocationsCount
!= 0 ) {
519 fLocalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->locreloff());
520 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() )
521 throw "local relocations not in __LINKEDIT";
522 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
523 throw "local relocations not in __LINKEDIT";
524 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 )
525 throw "local relocations table not pointer aligned";
527 fExternalRelocationsCount
= fDynamicSymbolTable
->nextrel();
528 if ( fExternalRelocationsCount
!= 0 ) {
529 fExternalRelocations
= (const macho_relocation_info
<P
>*)((char*)fHeader
+ fDynamicSymbolTable
->extreloff());
530 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() )
531 throw "external relocations not in __LINKEDIT";
532 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
533 throw "external relocations not in __LINKEDIT";
534 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 )
535 throw "external relocations table not pointer aligned";
539 case LC_SEGMENT_SPLIT_INFO
:
541 if ( isStaticExecutable
)
542 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
543 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
544 if ( info
->dataoff() < linkEditSegment
->fileoff() )
545 throw "split seg info not in __LINKEDIT";
546 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
547 throw "split seg info not in __LINKEDIT";
548 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
549 throw "split seg info table not pointer aligned";
550 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
551 throw "split seg info size not a multiple of pointer size";
554 case LC_FUNCTION_STARTS
:
556 const macho_linkedit_data_command
<P
>* info
= (macho_linkedit_data_command
<P
>*)cmd
;
557 if ( info
->dataoff() < linkEditSegment
->fileoff() )
558 throw "function starts data not in __LINKEDIT";
559 if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) )
560 throw "function starts data not in __LINKEDIT";
561 if ( (info
->dataoff() % sizeof(pint_t
)) != 0 )
562 throw "function starts data table not pointer aligned";
563 if ( (info
->datasize() % sizeof(pint_t
)) != 0 )
564 throw "function starts data size not a multiple of pointer size";
568 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
570 if ( !isStaticExecutable
&& !foundDynamicSymTab
)
571 throw "missing dynamic symbol table";
572 if ( fStrings
== NULL
)
573 throw "missing symbol table";
577 template <typename A
>
578 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
)
580 uint8_t sectionType
= (sect
->flags() & SECTION_TYPE
);
581 if ( sectionType
== S_ZEROFILL
) {
582 if ( sect
->offset() != 0 )
583 throwf("section offset should be zero for zero-fill section %s", sect
->sectname());
586 // more section tests here
589 template <typename A
>
590 void MachOChecker
<A
>::checkIndirectSymbolTable()
592 // static executables don't have indirect symbol table
593 if ( fDynamicSymbolTable
== NULL
)
595 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
596 const uint32_t cmd_count
= fHeader
->ncmds();
597 const macho_load_command
<P
>* cmd
= cmds
;
598 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
599 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
600 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
601 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
602 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
603 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
604 // make sure all magic sections that use indirect symbol table fit within it
606 uint32_t elementSize
= 0;
607 switch ( sect
->flags() & SECTION_TYPE
) {
609 elementSize
= sect
->reserved2();
610 start
= sect
->reserved1();
612 case S_LAZY_SYMBOL_POINTERS
:
613 case S_NON_LAZY_SYMBOL_POINTERS
:
614 elementSize
= sizeof(pint_t
);
615 start
= sect
->reserved1();
618 if ( elementSize
!= 0 ) {
619 uint32_t count
= sect
->size() / elementSize
;
620 if ( (count
*elementSize
) != sect
->size() )
621 throwf("%s section size is not an even multiple of element size", sect
->sectname());
622 if ( (start
+count
) > fIndirectTableCount
)
623 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount
);
627 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
632 template <typename A
>
633 void MachOChecker
<A
>::checkSymbolTable()
635 // verify no duplicate external symbol names
636 if ( fDynamicSymbolTable
!= NULL
) {
637 StringSet externalNames
;
638 const macho_nlist
<P
>* const exportedStart
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()];
639 const macho_nlist
<P
>* const exportedEnd
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()];
640 int i
= fDynamicSymbolTable
->iextdefsym();
641 for(const macho_nlist
<P
>* p
= exportedStart
; p
< exportedEnd
; ++p
, ++i
) {
642 const char* symName
= &fStrings
[p
->n_strx()];
643 if ( symName
> fStringsEnd
)
644 throw "string index out of range";
645 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
646 if ( externalNames
.find(symName
) != externalNames
.end() )
647 throwf("duplicate external symbol: %s", symName
);
648 if ( (p
->n_type() & N_EXT
) == 0 )
649 throwf("non-external symbol in external symbol range: %s", symName
);
650 // don't add N_INDR to externalNames because there is likely an undefine with same name
651 if ( (p
->n_type() & N_INDR
) == 0 )
652 externalNames
.insert(symName
);
654 // verify no undefines with same name as an external symbol
655 const macho_nlist
<P
>* const undefinesStart
= &fSymbols
[fDynamicSymbolTable
->iundefsym()];
656 const macho_nlist
<P
>* const undefinesEnd
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()];
657 for(const macho_nlist
<P
>* p
= undefinesStart
; p
< undefinesEnd
; ++p
) {
658 const char* symName
= &fStrings
[p
->n_strx()];
659 if ( symName
> fStringsEnd
)
660 throw "string index out of range";
661 if ( externalNames
.find(symName
) != externalNames
.end() )
662 throwf("undefine with same name as external symbol: %s", symName
);
664 // verify all N_SECT values are valid
665 for(const macho_nlist
<P
>* p
= fSymbols
; p
< &fSymbols
[fSymbolCount
]; ++p
) {
666 uint8_t type
= p
->n_type();
667 if ( ((type
& N_STAB
) == 0) && ((type
& N_TYPE
) == N_SECT
) ) {
668 if ( p
->n_sect() > fSectionCount
) {
669 throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect());
678 ppc::P::uint_t MachOChecker
<ppc
>::relocBase()
680 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
681 return fFirstWritableSegment
->vmaddr();
683 return fFirstSegment
->vmaddr();
687 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase()
689 if ( fWriteableSegmentWithAddrOver4G
)
690 return fFirstWritableSegment
->vmaddr();
692 return fFirstSegment
->vmaddr();
696 x86::P::uint_t MachOChecker
<x86
>::relocBase()
698 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
699 return fFirstWritableSegment
->vmaddr();
701 return fFirstSegment
->vmaddr();
705 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase()
707 // check for split-seg
708 return fFirstWritableSegment
->vmaddr();
712 arm::P::uint_t MachOChecker
<arm
>::relocBase()
714 if ( fHeader
->flags() & MH_SPLIT_SEGS
)
715 return fFirstWritableSegment
->vmaddr();
717 return fFirstSegment
->vmaddr();
721 template <typename A
>
722 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
)
724 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
725 const uint32_t cmd_count
= fHeader
->ncmds();
726 const macho_load_command
<P
>* cmd
= cmds
;
727 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
728 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
729 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
730 if ( (address
>= segCmd
->vmaddr()) && (address
< segCmd
->vmaddr()+segCmd
->vmsize()) ) {
731 // if segment is writable, we are fine
732 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
734 // could be a text reloc, make sure section bit is set
735 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
736 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
737 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
738 if ( (sect
->addr() <= address
) && (address
< (sect
->addr()+sect
->size())) ) {
739 // found section for this address, if has relocs we are fine
740 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 );
745 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
752 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
754 if ( reloc
->r_length() != 2 )
755 throw "bad external relocation length";
756 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
757 throw "unknown external relocation type";
758 if ( reloc
->r_pcrel() != 0 )
759 throw "bad external relocation pc_rel";
760 if ( reloc
->r_extern() == 0 )
761 throw "local relocation found with external relocations";
762 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
763 throw "external relocation address not in writable segment";
764 // FIX: check r_symbol
768 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
770 if ( reloc
->r_length() != 3 )
771 throw "bad external relocation length";
772 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
773 throw "unknown external relocation type";
774 if ( reloc
->r_pcrel() != 0 )
775 throw "bad external relocation pc_rel";
776 if ( reloc
->r_extern() == 0 )
777 throw "local relocation found with external relocations";
778 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
779 throw "external relocation address not in writable segment";
780 // FIX: check r_symbol
784 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
786 if ( reloc
->r_length() != 2 )
787 throw "bad external relocation length";
788 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
789 throw "unknown external relocation type";
790 if ( reloc
->r_pcrel() != 0 )
791 throw "bad external relocation pc_rel";
792 if ( reloc
->r_extern() == 0 )
793 throw "local relocation found with external relocations";
794 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
795 throw "external relocation address not in writable segment";
796 // FIX: check r_symbol
801 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
803 if ( reloc
->r_length() != 3 )
804 throw "bad external relocation length";
805 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
806 throw "unknown external relocation type";
807 if ( reloc
->r_pcrel() != 0 )
808 throw "bad external relocation pc_rel";
809 if ( reloc
->r_extern() == 0 )
810 throw "local relocation found with external relocations";
811 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
812 throw "exernal relocation address not in writable segment";
813 // FIX: check r_symbol
817 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
)
819 if ( reloc
->r_length() != 2 )
820 throw "bad external relocation length";
821 if ( reloc
->r_type() != ARM_RELOC_VANILLA
)
822 throw "unknown external relocation type";
823 if ( reloc
->r_pcrel() != 0 )
824 throw "bad external relocation pc_rel";
825 if ( reloc
->r_extern() == 0 )
826 throw "local relocation found with external relocations";
827 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
828 throw "external relocation address not in writable segment";
829 // FIX: check r_symbol
834 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
836 if ( reloc
->r_address() & R_SCATTERED
) {
838 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
843 // ignore pair relocs
844 if ( reloc
->r_type() == PPC_RELOC_PAIR
)
847 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
848 throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address());
854 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
856 if ( reloc
->r_length() != 3 )
857 throw "bad local relocation length";
858 if ( reloc
->r_type() != GENERIC_RELOC_VANILLA
)
859 throw "unknown local relocation type";
860 if ( reloc
->r_pcrel() != 0 )
861 throw "bad local relocation pc_rel";
862 if ( reloc
->r_extern() != 0 )
863 throw "external relocation found with local relocations";
864 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
865 throw "local relocation address not in writable segment";
869 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
875 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
877 if ( reloc
->r_length() != 3 )
878 throw "bad local relocation length";
879 if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED
)
880 throw "unknown local relocation type";
881 if ( reloc
->r_pcrel() != 0 )
882 throw "bad local relocation pc_rel";
883 if ( reloc
->r_extern() != 0 )
884 throw "external relocation found with local relocations";
885 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
886 throw "local relocation address not in writable segment";
890 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
)
892 if ( reloc
->r_address() & R_SCATTERED
) {
894 const macho_scattered_relocation_info
<P
>* sreloc
= (const macho_scattered_relocation_info
<P
>*)reloc
;
895 if ( sreloc
->r_length() != 2 )
896 throw "bad local scattered relocation length";
897 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR
)
898 throw "bad local scattered relocation type";
901 if ( reloc
->r_length() != 2 )
902 throw "bad local relocation length";
903 if ( reloc
->r_extern() != 0 )
904 throw "external relocation found with local relocations";
905 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) )
906 throw "local relocation address not in writable segment";
910 template <typename A
>
911 void MachOChecker
<A
>::checkRelocations()
913 // external relocations should be sorted to minimize dyld symbol lookups
914 // therefore every reloc with the same r_symbolnum value should be contiguous
915 std::set
<uint32_t> previouslySeenSymbolIndexes
;
916 uint32_t lastSymbolIndex
= 0xFFFFFFFF;
917 const macho_relocation_info
<P
>* const externRelocsEnd
= &fExternalRelocations
[fExternalRelocationsCount
];
918 for (const macho_relocation_info
<P
>* reloc
= fExternalRelocations
; reloc
< externRelocsEnd
; ++reloc
) {
919 this->checkExternalReloation(reloc
);
920 if ( reloc
->r_symbolnum() != lastSymbolIndex
) {
921 if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 )
922 throw "external relocations not sorted";
923 previouslySeenSymbolIndexes
.insert(lastSymbolIndex
);
924 lastSymbolIndex
= reloc
->r_symbolnum();
928 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
929 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
930 this->checkLocalReloation(reloc
);
933 // verify any section with S_ATTR_LOC_RELOC bits set actually has text relocs
934 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uint8_t*)fHeader
+ sizeof(macho_header
<P
>));
935 const uint32_t cmd_count
= fHeader
->ncmds();
936 const macho_load_command
<P
>* cmd
= cmds
;
937 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
938 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD
) {
939 const macho_segment_command
<P
>* segCmd
= (const macho_segment_command
<P
>*)cmd
;
940 // if segment is writable, we are fine
941 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )
943 // look at sections that have text reloc bit set
944 const macho_section
<P
>* const sectionsStart
= (macho_section
<P
>*)((char*)segCmd
+ sizeof(macho_segment_command
<P
>));
945 const macho_section
<P
>* const sectionsEnd
= §ionsStart
[segCmd
->nsects()];
946 for(const macho_section
<P
>* sect
= sectionsStart
; sect
< sectionsEnd
; ++sect
) {
947 if ( (sect
->flags() & S_ATTR_LOC_RELOC
) != 0 ) {
948 if ( ! hasTextRelocInRange(sect
->addr(), sect
->addr()+sect
->size()) ) {
949 throwf("section %s has attribute set that it has relocs, but it has none", sect
->sectname());
954 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
958 template <typename A
>
959 typename
A::P::uint_t MachOChecker
<A
>::segStartAddress(uint8_t segIndex
)
961 if ( segIndex
> fSegments
.size() )
962 throw "segment index out of range";
963 return fSegments
[segIndex
]->vmaddr();
966 template <typename A
>
967 bool MachOChecker
<A
>::hasTextRelocInRange(pint_t rangeStart
, pint_t rangeEnd
)
969 // look at local relocs
970 const macho_relocation_info
<P
>* const localRelocsEnd
= &fLocalRelocations
[fLocalRelocationsCount
];
971 for (const macho_relocation_info
<P
>* reloc
= fLocalRelocations
; reloc
< localRelocsEnd
; ++reloc
) {
972 pint_t relocAddress
= reloc
->r_address() + this->relocBase();
973 if ( (rangeStart
<= relocAddress
) && (relocAddress
< rangeEnd
) )
977 if ( fDyldInfo
!= NULL
) {
978 const uint8_t* p
= (uint8_t*)fHeader
+ fDyldInfo
->rebase_off();
979 const uint8_t* end
= &p
[fDyldInfo
->rebase_size()];
982 uint64_t segOffset
= 0;
986 pint_t segStartAddr
= 0;
989 while ( !done
&& (p
< end
) ) {
990 uint8_t immediate
= *p
& REBASE_IMMEDIATE_MASK
;
991 uint8_t opcode
= *p
& REBASE_OPCODE_MASK
;
994 case REBASE_OPCODE_DONE
:
997 case REBASE_OPCODE_SET_TYPE_IMM
:
1000 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
:
1001 segIndex
= immediate
;
1002 segStartAddr
= segStartAddress(segIndex
);
1003 segOffset
= read_uleb128(p
, end
);
1005 case REBASE_OPCODE_ADD_ADDR_ULEB
:
1006 segOffset
+= read_uleb128(p
, end
);
1008 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
:
1009 segOffset
+= immediate
*sizeof(pint_t
);
1011 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
:
1012 for (int i
=0; i
< immediate
; ++i
) {
1013 addr
= segStartAddr
+segOffset
;
1014 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1016 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1017 segOffset
+= sizeof(pint_t
);
1020 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
:
1021 count
= read_uleb128(p
, end
);
1022 for (uint32_t i
=0; i
< count
; ++i
) {
1023 addr
= segStartAddr
+segOffset
;
1024 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1026 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1027 segOffset
+= sizeof(pint_t
);
1030 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
:
1031 addr
= segStartAddr
+segOffset
;
1032 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1034 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1035 segOffset
+= read_uleb128(p
, end
) + sizeof(pint_t
);
1037 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
:
1038 count
= read_uleb128(p
, end
);
1039 skip
= read_uleb128(p
, end
);
1040 for (uint32_t i
=0; i
< count
; ++i
) {
1041 addr
= segStartAddr
+segOffset
;
1042 if ( (rangeStart
<= addr
) && (addr
< rangeEnd
) )
1044 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1045 segOffset
+= skip
+ sizeof(pint_t
);
1049 throwf("bad rebase opcode %d", *p
);
1055 static void check(const char* path
)
1057 struct stat stat_buf
;
1060 int fd
= ::open(path
, O_RDONLY
, 0);
1062 throw "cannot open file";
1063 if ( ::fstat(fd
, &stat_buf
) != 0 )
1064 throwf("fstat(%s) failed, errno=%d\n", path
, errno
);
1065 uint32_t length
= stat_buf
.st_size
;
1066 uint8_t* p
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE
| MAP_PRIVATE
, fd
, 0);
1067 if ( p
== ((uint8_t*)(-1)) )
1068 throw "cannot map file";
1070 const mach_header
* mh
= (mach_header
*)p
;
1071 if ( mh
->magic
== OSSwapBigToHostInt32(FAT_MAGIC
) ) {
1072 const struct fat_header
* fh
= (struct fat_header
*)p
;
1073 const struct fat_arch
* archs
= (struct fat_arch
*)(p
+ sizeof(struct fat_header
));
1074 for (unsigned long i
=0; i
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) {
1075 size_t offset
= OSSwapBigToHostInt32(archs
[i
].offset
);
1076 size_t size
= OSSwapBigToHostInt32(archs
[i
].size
);
1077 unsigned int cputype
= OSSwapBigToHostInt32(archs
[i
].cputype
);
1080 case CPU_TYPE_POWERPC
:
1081 if ( MachOChecker
<ppc
>::validFile(p
+ offset
) )
1082 MachOChecker
<ppc
>::make(p
+ offset
, size
, path
);
1084 throw "in universal file, ppc slice does not contain ppc mach-o";
1087 if ( MachOChecker
<x86
>::validFile(p
+ offset
) )
1088 MachOChecker
<x86
>::make(p
+ offset
, size
, path
);
1090 throw "in universal file, i386 slice does not contain i386 mach-o";
1092 case CPU_TYPE_POWERPC64
:
1093 if ( MachOChecker
<ppc64
>::validFile(p
+ offset
) )
1094 MachOChecker
<ppc64
>::make(p
+ offset
, size
, path
);
1096 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
1098 case CPU_TYPE_X86_64
:
1099 if ( MachOChecker
<x86_64
>::validFile(p
+ offset
) )
1100 MachOChecker
<x86_64
>::make(p
+ offset
, size
, path
);
1102 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
1105 if ( MachOChecker
<arm
>::validFile(p
+ offset
) )
1106 MachOChecker
<arm
>::make(p
+ offset
, size
, path
);
1108 throw "in universal file, arm slice does not contain arm mach-o";
1111 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
);
1115 else if ( MachOChecker
<x86
>::validFile(p
) ) {
1116 MachOChecker
<x86
>::make(p
, length
, path
);
1118 else if ( MachOChecker
<ppc
>::validFile(p
) ) {
1119 MachOChecker
<ppc
>::make(p
, length
, path
);
1121 else if ( MachOChecker
<ppc64
>::validFile(p
) ) {
1122 MachOChecker
<ppc64
>::make(p
, length
, path
);
1124 else if ( MachOChecker
<x86_64
>::validFile(p
) ) {
1125 MachOChecker
<x86_64
>::make(p
, length
, path
);
1127 else if ( MachOChecker
<arm
>::validFile(p
) ) {
1128 MachOChecker
<arm
>::make(p
, length
, path
);
1131 throw "not a known file type";
1134 catch (const char* msg
) {
1135 throwf("%s in %s", msg
, path
);
1140 int main(int argc
, const char* argv
[])
1143 for(int i
=1; i
< argc
; ++i
) {
1144 const char* arg
= argv
[i
];
1145 if ( arg
[0] == '-' ) {
1146 if ( strcmp(arg
, "-no_content") == 0 ) {
1150 throwf("unknown option: %s\n", arg
);
1158 catch (const char* msg
) {
1159 fprintf(stderr
, "machocheck failed: %s\n", msg
);