]> git.saurik.com Git - apple/ld64.git/blob - src/machochecker.cpp
ld64-62.1.tar.gz
[apple/ld64.git] / src / machochecker.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <unistd.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>
38
39 #include <vector>
40
41 #include "MachOFileAbstraction.hpp"
42 #include "Architectures.hpp"
43
44
45 __attribute__((noreturn))
46 void throwf(const char* format, ...)
47 {
48 va_list list;
49 char* p;
50 va_start(list, format);
51 vasprintf(&p, format, list);
52 va_end(list);
53
54 const char* t = p;
55 throw t;
56 }
57
58
59 template <typename A>
60 class MachOChecker
61 {
62 public:
63 static bool validFile(const uint8_t* fileContent);
64 static MachOChecker<A>* make(const uint8_t* fileContent, uint32_t fileLength, const char* path)
65 { return new MachOChecker<A>(fileContent, fileLength, path); }
66 virtual ~MachOChecker() {}
67
68
69 private:
70 typedef typename A::P P;
71 typedef typename A::P::E E;
72 typedef typename A::P::uint_t pint_t;
73
74 MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path);
75 void checkMachHeader();
76 void checkLoadCommands();
77 void checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect);
78 uint8_t loadCommandSizeMask();
79 void checkIndirectSymbolTable();
80 void checkRelocations();
81 void checkExternalReloation(const macho_relocation_info<P>* reloc);
82 void checkLocalReloation(const macho_relocation_info<P>* reloc);
83 pint_t relocBase();
84 bool addressInWritableSegment(pint_t address);
85
86 const char* fPath;
87 const macho_header<P>* fHeader;
88 uint32_t fLength;
89 const char* fStrings;
90 const char* fStringsEnd;
91 const macho_nlist<P>* fSymbols;
92 uint32_t fSymbolCount;
93 const uint32_t* fIndirectTable;
94 uint32_t fIndirectTableCount;
95 const macho_relocation_info<P>* fLocalRelocations;
96 uint32_t fLocalRelocationsCount;
97 const macho_relocation_info<P>* fExternalRelocations;
98 uint32_t fExternalRelocationsCount;
99 pint_t fRelocBase;
100 bool fWriteableSegmentWithAddrOver4G;
101 const macho_segment_command<P>* fFirstSegment;
102 const macho_segment_command<P>* fFirstWritableSegment;
103 };
104
105
106
107 template <>
108 bool MachOChecker<ppc>::validFile(const uint8_t* fileContent)
109 {
110 const macho_header<P>* header = (const macho_header<P>*)fileContent;
111 if ( header->magic() != MH_MAGIC )
112 return false;
113 if ( header->cputype() != CPU_TYPE_POWERPC )
114 return false;
115 switch (header->filetype()) {
116 case MH_EXECUTE:
117 case MH_DYLIB:
118 case MH_BUNDLE:
119 case MH_DYLINKER:
120 return true;
121 }
122 return false;
123 }
124
125 template <>
126 bool MachOChecker<ppc64>::validFile(const uint8_t* fileContent)
127 {
128 const macho_header<P>* header = (const macho_header<P>*)fileContent;
129 if ( header->magic() != MH_MAGIC_64 )
130 return false;
131 if ( header->cputype() != CPU_TYPE_POWERPC64 )
132 return false;
133 switch (header->filetype()) {
134 case MH_EXECUTE:
135 case MH_DYLIB:
136 case MH_BUNDLE:
137 case MH_DYLINKER:
138 return true;
139 }
140 return false;
141 }
142
143 template <>
144 bool MachOChecker<x86>::validFile(const uint8_t* fileContent)
145 {
146 const macho_header<P>* header = (const macho_header<P>*)fileContent;
147 if ( header->magic() != MH_MAGIC )
148 return false;
149 if ( header->cputype() != CPU_TYPE_I386 )
150 return false;
151 switch (header->filetype()) {
152 case MH_EXECUTE:
153 case MH_DYLIB:
154 case MH_BUNDLE:
155 case MH_DYLINKER:
156 return true;
157 }
158 return false;
159 }
160
161 template <>
162 bool MachOChecker<x86_64>::validFile(const uint8_t* fileContent)
163 {
164 const macho_header<P>* header = (const macho_header<P>*)fileContent;
165 if ( header->magic() != MH_MAGIC_64 )
166 return false;
167 if ( header->cputype() != CPU_TYPE_X86_64 )
168 return false;
169 switch (header->filetype()) {
170 case MH_EXECUTE:
171 case MH_DYLIB:
172 case MH_BUNDLE:
173 case MH_DYLINKER:
174 return true;
175 }
176 return false;
177 }
178
179
180 template <> uint8_t MachOChecker<ppc>::loadCommandSizeMask() { return 0x03; }
181 template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; }
182 template <> uint8_t MachOChecker<x86>::loadCommandSizeMask() { return 0x03; }
183 template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
184
185
186 template <typename A>
187 MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path)
188 : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fIndirectTableCount(0),
189 fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0),
190 fRelocBase(0), fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL)
191 {
192 // sanity check
193 if ( ! validFile(fileContent) )
194 throw "not a mach-o file that can be checked";
195
196 fPath = strdup(path);
197 fHeader = (const macho_header<P>*)fileContent;
198
199 // sanity check header
200 checkMachHeader();
201
202 // check load commands
203 checkLoadCommands();
204
205 checkIndirectSymbolTable();
206
207 checkRelocations();
208 }
209
210
211 template <typename A>
212 void MachOChecker<A>::checkMachHeader()
213 {
214 if ( (fHeader->sizeofcmds() + sizeof(macho_header<P>)) > fLength )
215 throw "sizeofcmds in mach_header is larger than file";
216
217 uint32_t flags = fHeader->flags();
218 uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFFFC0000;
219 if ( flags & invalidBits )
220 throw "invalid bits in mach_header flags";
221
222 }
223
224 template <typename A>
225 void MachOChecker<A>::checkLoadCommands()
226 {
227 // check that all load commands fit within the load command space file
228 const uint8_t* const endOfFile = (uint8_t*)fHeader + fLength;
229 const uint8_t* const endOfLoadCommands = (uint8_t*)fHeader + sizeof(macho_header<P>) + fHeader->sizeofcmds();
230 const uint32_t cmd_count = fHeader->ncmds();
231 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
232 const macho_load_command<P>* cmd = cmds;
233 for (uint32_t i = 0; i < cmd_count; ++i) {
234 uint32_t size = cmd->cmdsize();
235 if ( (size & this->loadCommandSizeMask()) != 0 )
236 throwf("load command #%d has a unaligned size", i);
237 const uint8_t* endOfCmd = ((uint8_t*)cmd)+cmd->cmdsize();
238 if ( endOfCmd > endOfLoadCommands )
239 throwf("load command #%d extends beyond the end of the load commands", i);
240 if ( endOfCmd > endOfFile )
241 throwf("load command #%d extends beyond the end of the file", i);
242 switch ( cmd->cmd() ) {
243 case macho_segment_command<P>::CMD:
244 case LC_SYMTAB:
245 case LC_UNIXTHREAD:
246 case LC_DYSYMTAB:
247 case LC_LOAD_DYLIB:
248 case LC_ID_DYLIB:
249 case LC_LOAD_DYLINKER:
250 case LC_ID_DYLINKER:
251 case macho_routines_command<P>::CMD:
252 case LC_SUB_FRAMEWORK:
253 case LC_SUB_UMBRELLA:
254 case LC_SUB_CLIENT:
255 case LC_TWOLEVEL_HINTS:
256 case LC_PREBIND_CKSUM:
257 case LC_LOAD_WEAK_DYLIB:
258 case LC_UUID:
259 break;
260 default:
261 throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd());
262 }
263 cmd = (const macho_load_command<P>*)endOfCmd;
264 }
265
266 // check segments
267 cmd = cmds;
268 std::vector<std::pair<pint_t, pint_t> > segmentAddressRanges;
269 std::vector<std::pair<pint_t, pint_t> > segmentFileOffsetRanges;
270 const macho_segment_command<P>* linkEditSegment = NULL;
271 for (uint32_t i = 0; i < cmd_count; ++i) {
272 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
273 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
274 if ( segCmd->cmdsize() != (sizeof(macho_segment_command<P>) + segCmd->nsects() * sizeof(macho_section_content<P>)) )
275 throw "invalid segment load command size";
276
277 // see if this overlaps another segment address range
278 uint64_t startAddr = segCmd->vmaddr();
279 uint64_t endAddr = startAddr + segCmd->vmsize();
280 for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentAddressRanges.begin(); it != segmentAddressRanges.end(); ++it) {
281 if ( it->first < startAddr ) {
282 if ( it->second > startAddr )
283 throw "overlapping segment vm addresses";
284 }
285 else if ( it->first > startAddr ) {
286 if ( it->first < endAddr )
287 throw "overlapping segment vm addresses";
288 }
289 else {
290 throw "overlapping segment vm addresses";
291 }
292 segmentAddressRanges.push_back(std::make_pair<pint_t, pint_t>(startAddr, endAddr));
293 }
294 // see if this overlaps another segment file offset range
295 uint64_t startOffset = segCmd->fileoff();
296 uint64_t endOffset = startOffset + segCmd->filesize();
297 for (typename std::vector<std::pair<pint_t, pint_t> >::iterator it = segmentFileOffsetRanges.begin(); it != segmentFileOffsetRanges.end(); ++it) {
298 if ( it->first < startOffset ) {
299 if ( it->second > startOffset )
300 throw "overlapping segment file data";
301 }
302 else if ( it->first > startOffset ) {
303 if ( it->first < endOffset )
304 throw "overlapping segment file data";
305 }
306 else {
307 throw "overlapping segment file data";
308 }
309 segmentFileOffsetRanges.push_back(std::make_pair<pint_t, pint_t>(startOffset, endOffset));
310 // check is within file bounds
311 if ( (startOffset > fLength) || (endOffset > fLength) )
312 throw "segment file data is past end of file";
313 }
314 // verify it fits in file
315 if ( startOffset > fLength )
316 throw "segment fileoff does not fit in file";
317 if ( endOffset > fLength )
318 throw "segment fileoff+filesize does not fit in file";
319
320 // keep LINKEDIT segment
321 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
322 linkEditSegment = segCmd;
323
324 // cache interesting segments
325 if ( fFirstSegment == NULL )
326 fFirstSegment = segCmd;
327 if ( (fFirstWritableSegment == NULL) && ((segCmd->initprot() & VM_PROT_WRITE) != 0) )
328 fFirstWritableSegment = segCmd;
329
330 // check section ranges
331 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
332 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
333 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
334 // check all sections are within segment
335 if ( sect->addr() < startAddr )
336 throwf("section %s vm address not within segment", sect->sectname());
337 if ( (sect->addr()+sect->size()) > endAddr )
338 throwf("section %s vm address not within segment", sect->sectname());
339 if ( ((sect->flags() &SECTION_TYPE) != S_ZEROFILL) && (segCmd->filesize() != 0) ) {
340 if ( sect->offset() < startOffset )
341 throwf("section %s file offset not within segment", sect->sectname());
342 if ( (sect->offset()+sect->size()) > endOffset )
343 throwf("section %s file offset not within segment", sect->sectname());
344 }
345 checkSection(segCmd, sect);
346 }
347 }
348 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
349 }
350
351 // verify there was a LINKEDIT segment
352 if ( linkEditSegment == NULL )
353 throw "no __LINKEDIT segment";
354
355 // checks for executables
356 bool isStaticExecutable = false;
357 if ( fHeader->filetype() == MH_EXECUTE ) {
358 isStaticExecutable = true;
359 cmd = cmds;
360 for (uint32_t i = 0; i < cmd_count; ++i) {
361 switch ( cmd->cmd() ) {
362 case LC_LOAD_DYLINKER:
363 // the existence of a dyld load command makes a executable dynamic
364 isStaticExecutable = false;
365 break;
366 }
367 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
368 }
369 if ( isStaticExecutable ) {
370 if ( fHeader->flags() != MH_NOUNDEFS )
371 throw "invalid bits in mach_header flags for static executable";
372 }
373 }
374
375 // check LC_SYMTAB and LC_DYSYMTAB
376 cmd = cmds;
377 bool foundDynamicSymTab = false;
378 for (uint32_t i = 0; i < cmd_count; ++i) {
379 switch ( cmd->cmd() ) {
380 case LC_SYMTAB:
381 {
382 const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
383 fSymbolCount = symtab->nsyms();
384 fSymbols = (const macho_nlist<P>*)((char*)fHeader + symtab->symoff());
385 if ( symtab->symoff() < linkEditSegment->fileoff() )
386 throw "symbol table not in __LINKEDIT";
387 if ( (symtab->symoff() + fSymbolCount*sizeof(macho_nlist<P>*)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
388 throw "symbol table end not in __LINKEDIT";
389 fStrings = (char*)fHeader + symtab->stroff();
390 fStringsEnd = fStrings + symtab->strsize();
391 if ( symtab->stroff() < linkEditSegment->fileoff() )
392 throw "string pool not in __LINKEDIT";
393 if ( (symtab->stroff()+symtab->strsize()) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
394 throw "string pool extends beyond __LINKEDIT";
395 }
396 break;
397 case LC_DYSYMTAB:
398 {
399 if ( isStaticExecutable )
400 throw "LC_DYSYMTAB should not be used in static executable";
401 foundDynamicSymTab = true;
402 const macho_dysymtab_command<P>* dsymtab = (struct macho_dysymtab_command<P>*)cmd;
403 fIndirectTable = (uint32_t*)((char*)fHeader + dsymtab->indirectsymoff());
404 fIndirectTableCount = dsymtab->nindirectsyms();
405 if ( fIndirectTableCount != 0 ) {
406 if ( dsymtab->indirectsymoff() < linkEditSegment->fileoff() )
407 throw "indirect symbol table not in __LINKEDIT";
408 if ( (dsymtab->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
409 throw "indirect symbol table not in __LINKEDIT";
410 }
411 fLocalRelocationsCount = dsymtab->nlocrel();
412 if ( fLocalRelocationsCount != 0 ) {
413 fLocalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + dsymtab->locreloff());
414 if ( dsymtab->locreloff() < linkEditSegment->fileoff() )
415 throw "local relocations not in __LINKEDIT";
416 if ( (dsymtab->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
417 throw "local relocations not in __LINKEDIT";
418 }
419 fExternalRelocationsCount = dsymtab->nextrel();
420 if ( fExternalRelocationsCount != 0 ) {
421 fExternalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + dsymtab->extreloff());
422 if ( dsymtab->extreloff() < linkEditSegment->fileoff() )
423 throw "local relocations not in __LINKEDIT";
424 if ( (dsymtab->extreloff()+fExternalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
425 throw "local relocations not in __LINKEDIT";
426 }
427 }
428 break;
429 }
430 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
431 }
432 if ( !isStaticExecutable && !foundDynamicSymTab )
433 throw "missing dynamic symbol table";
434 if ( fStrings == NULL )
435 throw "missing symbol table";
436
437 fRelocBase = this->relocBase();
438
439 }
440
441 template <typename A>
442 void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect)
443 {
444 uint8_t sectionType = (sect->flags() & SECTION_TYPE);
445 if ( sectionType == S_ZEROFILL ) {
446 if ( sect->offset() != 0 )
447 throwf("section offset should be zero for zero-fill section %s", sect->sectname());
448 }
449
450 // more section tests here
451 }
452
453 template <typename A>
454 void MachOChecker<A>::checkIndirectSymbolTable()
455 {
456 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
457 const uint32_t cmd_count = fHeader->ncmds();
458 const macho_load_command<P>* cmd = cmds;
459 for (uint32_t i = 0; i < cmd_count; ++i) {
460 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
461 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
462 const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
463 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
464 for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
465 // make sure all magic sections that use indirect symbol table fit within it
466 uint32_t start = 0;
467 uint32_t elementSize = 0;
468 switch ( sect->flags() & SECTION_TYPE ) {
469 case S_SYMBOL_STUBS:
470 elementSize = sect->reserved2();
471 start = sect->reserved1();
472 break;
473 case S_LAZY_SYMBOL_POINTERS:
474 case S_NON_LAZY_SYMBOL_POINTERS:
475 elementSize = sizeof(pint_t);
476 start = sect->reserved1();
477 break;
478 }
479 if ( elementSize != 0 ) {
480 uint32_t count = sect->size() / elementSize;
481 if ( (count*elementSize) != sect->size() )
482 throwf("%s section size is not an even multiple of element size", sect->sectname());
483 if ( (start+count) > fIndirectTableCount )
484 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect->sectname(), start+count, fIndirectTableCount );
485 }
486 }
487 }
488 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
489 }
490 }
491
492
493
494 template <>
495 ppc::P::uint_t MachOChecker<ppc>::relocBase()
496 {
497 if ( fHeader->flags() & MH_SPLIT_SEGS )
498 return fFirstWritableSegment->vmaddr();
499 else
500 return fFirstSegment->vmaddr();
501 }
502
503 template <>
504 ppc64::P::uint_t MachOChecker<ppc64>::relocBase()
505 {
506 if ( fWriteableSegmentWithAddrOver4G )
507 return fFirstWritableSegment->vmaddr();
508 else
509 return fFirstSegment->vmaddr();
510 }
511
512 template <>
513 x86::P::uint_t MachOChecker<x86>::relocBase()
514 {
515 if ( fHeader->flags() & MH_SPLIT_SEGS )
516 return fFirstWritableSegment->vmaddr();
517 else
518 return fFirstSegment->vmaddr();
519 }
520
521 template <>
522 x86_64::P::uint_t MachOChecker<x86_64>::relocBase()
523 {
524 // check for split-seg
525 return fFirstWritableSegment->vmaddr();
526 }
527
528
529 template <typename A>
530 bool MachOChecker<A>::addressInWritableSegment(pint_t address)
531 {
532 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
533 const uint32_t cmd_count = fHeader->ncmds();
534 const macho_load_command<P>* cmd = cmds;
535 for (uint32_t i = 0; i < cmd_count; ++i) {
536 if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
537 const macho_segment_command<P>* segCmd = (const macho_segment_command<P>*)cmd;
538 if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
539 if ( (address >= segCmd->vmaddr()) && (address < segCmd->vmaddr()+segCmd->vmsize()) )
540 return true;
541 }
542 }
543 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
544 }
545 return false;
546 }
547
548
549 template <>
550 void MachOChecker<ppc>::checkExternalReloation(const macho_relocation_info<P>* reloc)
551 {
552 // FIX
553 }
554
555 template <>
556 void MachOChecker<ppc64>::checkExternalReloation(const macho_relocation_info<P>* reloc)
557 {
558 if ( reloc->r_length() != 3 )
559 throw "bad external relocation length";
560 if ( reloc->r_type() != GENERIC_RELOC_VANILLA )
561 throw "unknown external relocation type";
562 if ( reloc->r_pcrel() != 0 )
563 throw "bad external relocation pc_rel";
564 if ( reloc->r_extern() == 0 )
565 throw "local relocation found with external relocations";
566 if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
567 throw "local relocation address not in writable segment";
568 // FIX: check r_symbol
569 }
570
571 template <>
572 void MachOChecker<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc)
573 {
574 // FIX
575 }
576
577
578 template <>
579 void MachOChecker<x86_64>::checkExternalReloation(const macho_relocation_info<P>* reloc)
580 {
581 if ( reloc->r_length() != 3 )
582 throw "bad external relocation length";
583 if ( reloc->r_type() != X86_64_RELOC_UNSIGNED )
584 throw "unknown external relocation type";
585 if ( reloc->r_pcrel() != 0 )
586 throw "bad external relocation pc_rel";
587 if ( reloc->r_extern() == 0 )
588 throw "local relocation found with external relocations";
589 if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
590 throw "exernal relocation address not in writable segment";
591 // FIX: check r_symbol
592 }
593
594 template <>
595 void MachOChecker<ppc>::checkLocalReloation(const macho_relocation_info<P>* reloc)
596 {
597 if ( reloc->r_address() & R_SCATTERED ) {
598 // scattered
599 const macho_scattered_relocation_info<P>* sreloc = (const macho_scattered_relocation_info<P>*)reloc;
600 // FIX
601
602 }
603 else {
604 // FIX
605 if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
606 throw "local relocation address not in writable segment";
607 }
608 }
609
610
611 template <>
612 void MachOChecker<ppc64>::checkLocalReloation(const macho_relocation_info<P>* reloc)
613 {
614 if ( reloc->r_length() != 3 )
615 throw "bad local relocation length";
616 if ( reloc->r_type() != GENERIC_RELOC_VANILLA )
617 throw "unknown local relocation type";
618 if ( reloc->r_pcrel() != 0 )
619 throw "bad local relocation pc_rel";
620 if ( reloc->r_extern() != 0 )
621 throw "external relocation found with local relocations";
622 if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
623 throw "local relocation address not in writable segment";
624 }
625
626 template <>
627 void MachOChecker<x86>::checkLocalReloation(const macho_relocation_info<P>* reloc)
628 {
629 // FIX
630 }
631
632 template <>
633 void MachOChecker<x86_64>::checkLocalReloation(const macho_relocation_info<P>* reloc)
634 {
635 if ( reloc->r_length() != 3 )
636 throw "bad local relocation length";
637 if ( reloc->r_type() != X86_64_RELOC_UNSIGNED )
638 throw "unknown local relocation type";
639 if ( reloc->r_pcrel() != 0 )
640 throw "bad local relocation pc_rel";
641 if ( reloc->r_extern() != 0 )
642 throw "external relocation found with local relocations";
643 if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
644 throw "local relocation address not in writable segment";
645 }
646
647
648
649 template <typename A>
650 void MachOChecker<A>::checkRelocations()
651 {
652 const macho_relocation_info<P>* const externRelocsEnd = &fExternalRelocations[fExternalRelocationsCount];
653 for (const macho_relocation_info<P>* reloc = fExternalRelocations; reloc < externRelocsEnd; ++reloc) {
654 this->checkExternalReloation(reloc);
655 }
656
657 const macho_relocation_info<P>* const localRelocsEnd = &fLocalRelocations[fLocalRelocationsCount];
658 for (const macho_relocation_info<P>* reloc = fLocalRelocations; reloc < localRelocsEnd; ++reloc) {
659 this->checkLocalReloation(reloc);
660 }
661 }
662
663
664 static void check(const char* path)
665 {
666 struct stat stat_buf;
667
668 try {
669 int fd = ::open(path, O_RDONLY, 0);
670 if ( fd == -1 )
671 throw "cannot open file";
672 ::fstat(fd, &stat_buf);
673 uint32_t length = stat_buf.st_size;
674 uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
675 if ( p == ((uint8_t*)(-1)) )
676 throw "cannot map file";
677 ::close(fd);
678 const mach_header* mh = (mach_header*)p;
679 if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
680 const struct fat_header* fh = (struct fat_header*)p;
681 const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
682 for (unsigned long i=0; i < fh->nfat_arch; ++i) {
683 if ( archs[i].cputype == CPU_TYPE_POWERPC ) {
684 if ( MachOChecker<ppc>::validFile(p + archs[i].offset) )
685 MachOChecker<ppc>::make(p + archs[i].offset, archs[i].size, path);
686 else
687 throw "in universal file, ppc slice does not contain ppc mach-o";
688 }
689 else if ( archs[i].cputype == CPU_TYPE_I386 ) {
690 if ( MachOChecker<x86>::validFile(p + archs[i].offset) )
691 MachOChecker<x86>::make(p + archs[i].offset, archs[i].size, path);
692 else
693 throw "in universal file, i386 slice does not contain i386 mach-o";
694 }
695 else if ( archs[i].cputype == CPU_TYPE_POWERPC64 ) {
696 if ( MachOChecker<ppc64>::validFile(p + archs[i].offset) )
697 MachOChecker<ppc64>::make(p + archs[i].offset, archs[i].size, path);
698 else
699 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
700 }
701 else if ( archs[i].cputype == CPU_TYPE_X86_64 ) {
702 if ( MachOChecker<x86_64>::validFile(p + archs[i].offset) )
703 MachOChecker<x86_64>::make(p + archs[i].offset, archs[i].size, path);
704 else
705 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
706 }
707 else {
708 throw "in universal file, unknown architecture slice";
709 }
710 }
711 }
712 else if ( MachOChecker<x86>::validFile(p) ) {
713 MachOChecker<x86>::make(p, length, path);
714 }
715 else if ( MachOChecker<ppc>::validFile(p) ) {
716 MachOChecker<ppc>::make(p, length, path);
717 }
718 else if ( MachOChecker<ppc64>::validFile(p) ) {
719 MachOChecker<ppc64>::make(p, length, path);
720 }
721 else if ( MachOChecker<x86_64>::validFile(p) ) {
722 MachOChecker<x86_64>::make(p, length, path);
723 }
724 else {
725 throw "not a known file type";
726 }
727 }
728 catch (const char* msg) {
729 throwf("%s in %s", msg, path);
730 }
731 }
732
733
734 int main(int argc, const char* argv[])
735 {
736 try {
737 for(int i=1; i < argc; ++i) {
738 const char* arg = argv[i];
739 if ( arg[0] == '-' ) {
740 if ( strcmp(arg, "-no_content") == 0 ) {
741
742 }
743 else {
744 throwf("unknown option: %s\n", arg);
745 }
746 }
747 else {
748 check(arg);
749 }
750 }
751 }
752 catch (const char* msg) {
753 fprintf(stderr, "machocheck failed: %s\n", msg);
754 return 1;
755 }
756
757 return 0;
758 }
759
760
761