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