]> git.saurik.com Git - apple/ld64.git/blame - src/other/machochecker.cpp
ld64-123.2.tar.gz
[apple/ld64.git] / src / other / machochecker.cpp
CommitLineData
d696c285
A
1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
a645023d 3 * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
d696c285
A
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>
55e3d2f6 32#include <errno.h>
d696c285
A
33
34#include <vector>
a61fdf0a
A
35#include <set>
36#include <ext/hash_set>
d696c285
A
37
38#include "MachOFileAbstraction.hpp"
39#include "Architectures.hpp"
40
41
42 __attribute__((noreturn))
43void throwf(const char* format, ...)
44{
45 va_list list;
46 char* p;
47 va_start(list, format);
48 vasprintf(&p, format, list);
49 va_end(list);
50
51 const char* t = p;
52 throw t;
53}
54
a645023d
A
55static uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end)
56{
57 uint64_t result = 0;
58 int bit = 0;
59 do {
60 if (p == end)
61 throwf("malformed uleb128");
62
63 uint64_t slice = *p & 0x7f;
64
65 if (bit >= 64 || slice << bit >> bit != slice)
66 throwf("uleb128 too big");
67 else {
68 result |= (slice << bit);
69 bit += 7;
70 }
71 }
72 while (*p++ & 0x80);
73 return result;
74}
d696c285
A
75
76template <typename A>
77class MachOChecker
78{
79public:
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() {}
84
85
86private:
87 typedef typename A::P P;
88 typedef typename A::P::E E;
89 typedef typename A::P::uint_t pint_t;
90
a61fdf0a
A
91 class CStringEquals
92 {
93 public:
94 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
95 };
96
97 typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringSet;
98
d696c285
A
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();
a61fdf0a 104 void checkSymbolTable();
d696c285 105 void checkIndirectSymbolTable();
69a49097
A
106 void checkRelocations();
107 void checkExternalReloation(const macho_relocation_info<P>* reloc);
108 void checkLocalReloation(const macho_relocation_info<P>* reloc);
109 pint_t relocBase();
110 bool addressInWritableSegment(pint_t address);
a645023d
A
111 bool hasTextRelocInRange(pint_t start, pint_t end);
112 pint_t segStartAddress(uint8_t segIndex);
d696c285
A
113
114 const char* fPath;
115 const macho_header<P>* fHeader;
116 uint32_t fLength;
117 const char* fStrings;
118 const char* fStringsEnd;
119 const macho_nlist<P>* fSymbols;
120 uint32_t fSymbolCount;
a61fdf0a 121 const macho_dysymtab_command<P>* fDynamicSymbolTable;
d696c285
A
122 const uint32_t* fIndirectTable;
123 uint32_t fIndirectTableCount;
69a49097
A
124 const macho_relocation_info<P>* fLocalRelocations;
125 uint32_t fLocalRelocationsCount;
126 const macho_relocation_info<P>* fExternalRelocations;
127 uint32_t fExternalRelocationsCount;
69a49097
A
128 bool fWriteableSegmentWithAddrOver4G;
129 const macho_segment_command<P>* fFirstSegment;
130 const macho_segment_command<P>* fFirstWritableSegment;
a645023d 131 const macho_dyld_info_command<P>* fDyldInfo;
c211e7c9 132 uint32_t fSectionCount;
a645023d 133 std::vector<const macho_segment_command<P>*>fSegments;
d696c285
A
134};
135
136
137
138template <>
139bool MachOChecker<ppc>::validFile(const uint8_t* fileContent)
140{
141 const macho_header<P>* header = (const macho_header<P>*)fileContent;
142 if ( header->magic() != MH_MAGIC )
143 return false;
144 if ( header->cputype() != CPU_TYPE_POWERPC )
145 return false;
146 switch (header->filetype()) {
147 case MH_EXECUTE:
148 case MH_DYLIB:
149 case MH_BUNDLE:
150 case MH_DYLINKER:
151 return true;
152 }
153 return false;
154}
155
156template <>
157bool MachOChecker<ppc64>::validFile(const uint8_t* fileContent)
158{
159 const macho_header<P>* header = (const macho_header<P>*)fileContent;
160 if ( header->magic() != MH_MAGIC_64 )
161 return false;
162 if ( header->cputype() != CPU_TYPE_POWERPC64 )
163 return false;
164 switch (header->filetype()) {
165 case MH_EXECUTE:
166 case MH_DYLIB:
167 case MH_BUNDLE:
168 case MH_DYLINKER:
169 return true;
170 }
171 return false;
172}
173
174template <>
175bool MachOChecker<x86>::validFile(const uint8_t* fileContent)
176{
177 const macho_header<P>* header = (const macho_header<P>*)fileContent;
178 if ( header->magic() != MH_MAGIC )
179 return false;
180 if ( header->cputype() != CPU_TYPE_I386 )
181 return false;
182 switch (header->filetype()) {
183 case MH_EXECUTE:
184 case MH_DYLIB:
185 case MH_BUNDLE:
186 case MH_DYLINKER:
187 return true;
188 }
189 return false;
190}
191
69a49097
A
192template <>
193bool MachOChecker<x86_64>::validFile(const uint8_t* fileContent)
194{
195 const macho_header<P>* header = (const macho_header<P>*)fileContent;
196 if ( header->magic() != MH_MAGIC_64 )
197 return false;
198 if ( header->cputype() != CPU_TYPE_X86_64 )
199 return false;
200 switch (header->filetype()) {
201 case MH_EXECUTE:
202 case MH_DYLIB:
203 case MH_BUNDLE:
204 case MH_DYLINKER:
205 return true;
206 }
207 return false;
208}
d696c285 209
2f2f92e4
A
210template <>
211bool MachOChecker<arm>::validFile(const uint8_t* fileContent)
212{
213 const macho_header<P>* header = (const macho_header<P>*)fileContent;
214 if ( header->magic() != MH_MAGIC )
215 return false;
216 if ( header->cputype() != CPU_TYPE_ARM )
217 return false;
218 switch (header->filetype()) {
219 case MH_EXECUTE:
220 case MH_DYLIB:
221 case MH_BUNDLE:
222 case MH_DYLINKER:
223 return true;
224 }
225 return false;
226}
d696c285
A
227
228template <> uint8_t MachOChecker<ppc>::loadCommandSizeMask() { return 0x03; }
229template <> uint8_t MachOChecker<ppc64>::loadCommandSizeMask() { return 0x07; }
230template <> uint8_t MachOChecker<x86>::loadCommandSizeMask() { return 0x03; }
69a49097 231template <> uint8_t MachOChecker<x86_64>::loadCommandSizeMask() { return 0x07; }
2f2f92e4 232template <> uint8_t MachOChecker<arm>::loadCommandSizeMask() { return 0x03; }
d696c285
A
233
234template <typename A>
235MachOChecker<A>::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path)
a61fdf0a 236 : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0),
69a49097 237 fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0),
a645023d 238 fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fDyldInfo(NULL), fSectionCount(0)
d696c285
A
239{
240 // sanity check
241 if ( ! validFile(fileContent) )
242 throw "not a mach-o file that can be checked";
243
244 fPath = strdup(path);
245 fHeader = (const macho_header<P>*)fileContent;
246
247 // sanity check header
248 checkMachHeader();
249
250 // check load commands
251 checkLoadCommands();
252
253 checkIndirectSymbolTable();
254
69a49097 255 checkRelocations();
a61fdf0a
A
256
257 checkSymbolTable();
d696c285
A
258}
259
260
261template <typename A>
262void MachOChecker<A>::checkMachHeader()
263{
264 if ( (fHeader->sizeofcmds() + sizeof(macho_header<P>)) > fLength )
265 throw "sizeofcmds in mach_header is larger than file";
266
267 uint32_t flags = fHeader->flags();
a645023d 268 const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFE000000;
d696c285
A
269 if ( flags & invalidBits )
270 throw "invalid bits in mach_header flags";
a61fdf0a
A
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";
d696c285
A
273}
274
275template <typename A>
276void MachOChecker<A>::checkLoadCommands()
277{
278 // check that all load commands fit within the load command space file
2f2f92e4 279 const macho_encryption_info_command<P>* encryption_info = NULL;
d696c285
A
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:
296 case LC_SYMTAB:
297 case LC_UNIXTHREAD:
298 case LC_DYSYMTAB:
299 case LC_LOAD_DYLIB:
300 case LC_ID_DYLIB:
301 case LC_LOAD_DYLINKER:
302 case LC_ID_DYLINKER:
303 case macho_routines_command<P>::CMD:
304 case LC_SUB_FRAMEWORK:
d696c285
A
305 case LC_SUB_CLIENT:
306 case LC_TWOLEVEL_HINTS:
307 case LC_PREBIND_CKSUM:
308 case LC_LOAD_WEAK_DYLIB:
2f2f92e4 309 case LC_LAZY_LOAD_DYLIB:
d696c285 310 case LC_UUID:
a61fdf0a
A
311 case LC_REEXPORT_DYLIB:
312 case LC_SEGMENT_SPLIT_INFO:
2f2f92e4 313 case LC_CODE_SIGNATURE:
a645023d
A
314 case LC_LOAD_UPWARD_DYLIB:
315 case LC_VERSION_MIN_MACOSX:
316 case LC_VERSION_MIN_IPHONEOS:
317 case LC_FUNCTION_STARTS:
318 break;
55e3d2f6
A
319 case LC_DYLD_INFO:
320 case LC_DYLD_INFO_ONLY:
a645023d 321 fDyldInfo = (macho_dyld_info_command<P>*)cmd;
2f2f92e4
A
322 break;
323 case LC_ENCRYPTION_INFO:
324 encryption_info = (macho_encryption_info_command<P>*)cmd;
a61fdf0a
A
325 break;
326 case LC_SUB_UMBRELLA:
327 case LC_SUB_LIBRARY:
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";
d696c285
A
330 break;
331 default:
332 throwf("load command #%d is an unknown kind 0x%X", i, cmd->cmd());
333 }
334 cmd = (const macho_load_command<P>*)endOfCmd;
335 }
336
337 // check segments
338 cmd = cmds;
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;
a645023d 345 fSegments.push_back(segCmd);
d696c285
A
346 if ( segCmd->cmdsize() != (sizeof(macho_segment_command<P>) + segCmd->nsects() * sizeof(macho_section_content<P>)) )
347 throw "invalid segment load command size";
348
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";
356 }
357 else if ( it->first > startAddr ) {
358 if ( it->first < endAddr )
359 throw "overlapping segment vm addresses";
360 }
361 else {
362 throw "overlapping segment vm addresses";
363 }
364 segmentAddressRanges.push_back(std::make_pair<pint_t, pint_t>(startAddr, endAddr));
365 }
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";
373 }
374 else if ( it->first > startOffset ) {
375 if ( it->first < endOffset )
376 throw "overlapping segment file data";
377 }
378 else {
379 throw "overlapping segment file data";
380 }
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";
385 }
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";
391
392 // keep LINKEDIT segment
393 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
394 linkEditSegment = segCmd;
69a49097
A
395
396 // cache interesting segments
397 if ( fFirstSegment == NULL )
398 fFirstSegment = segCmd;
a61fdf0a
A
399 if ( (segCmd->initprot() & VM_PROT_WRITE) != 0 ) {
400 if ( fFirstWritableSegment == NULL )
401 fFirstWritableSegment = segCmd;
402 if ( segCmd->vmaddr() > 0x100000000ULL )
403 fWriteableSegmentWithAddrOver4G = true;
404 }
405
d696c285
A
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 = &sectionsStart[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());
a645023d
A
415 if ( ((sect->flags() & SECTION_TYPE) != S_ZEROFILL)
416 && ((sect->flags() & SECTION_TYPE) != S_THREAD_LOCAL_ZEROFILL)
417 && (segCmd->filesize() != 0) ) {
d696c285
A
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());
422 }
423 checkSection(segCmd, sect);
c211e7c9 424 ++fSectionCount;
d696c285
A
425 }
426 }
427 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
428 }
429
430 // verify there was a LINKEDIT segment
431 if ( linkEditSegment == NULL )
432 throw "no __LINKEDIT segment";
433
434 // checks for executables
435 bool isStaticExecutable = false;
436 if ( fHeader->filetype() == MH_EXECUTE ) {
437 isStaticExecutable = true;
438 cmd = cmds;
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;
444 break;
445 }
446 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
447 }
448 if ( isStaticExecutable ) {
449 if ( fHeader->flags() != MH_NOUNDEFS )
450 throw "invalid bits in mach_header flags for static executable";
451 }
452 }
453
2f2f92e4
A
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";
469 }
470 }
471 }
472
a61fdf0a 473 // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO
d696c285
A
474 cmd = cmds;
475 bool foundDynamicSymTab = false;
476 for (uint32_t i = 0; i < cmd_count; ++i) {
477 switch ( cmd->cmd() ) {
478 case LC_SYMTAB:
479 {
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";
a61fdf0a
A
487 if ( (symtab->symoff() % sizeof(pint_t)) != 0 )
488 throw "symbol table start not pointer aligned";
d696c285
A
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";
a61fdf0a
A
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";
d696c285
A
499 }
500 break;
501 case LC_DYSYMTAB:
502 {
503 if ( isStaticExecutable )
504 throw "LC_DYSYMTAB should not be used in static executable";
505 foundDynamicSymTab = true;
a645023d 506 fDynamicSymbolTable = (macho_dysymtab_command<P>*)cmd;
a61fdf0a
A
507 fIndirectTable = (uint32_t*)((char*)fHeader + fDynamicSymbolTable->indirectsymoff());
508 fIndirectTableCount = fDynamicSymbolTable->nindirectsyms();
74cfe461 509 if ( fIndirectTableCount != 0 ) {
a61fdf0a 510 if ( fDynamicSymbolTable->indirectsymoff() < linkEditSegment->fileoff() )
74cfe461 511 throw "indirect symbol table not in __LINKEDIT";
a61fdf0a 512 if ( (fDynamicSymbolTable->indirectsymoff()+fIndirectTableCount*8) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
74cfe461 513 throw "indirect symbol table not in __LINKEDIT";
a61fdf0a
A
514 if ( (fDynamicSymbolTable->indirectsymoff() % sizeof(pint_t)) != 0 )
515 throw "indirect symbol table not pointer aligned";
74cfe461 516 }
a61fdf0a 517 fLocalRelocationsCount = fDynamicSymbolTable->nlocrel();
69a49097 518 if ( fLocalRelocationsCount != 0 ) {
a61fdf0a
A
519 fLocalRelocations = (const macho_relocation_info<P>*)((char*)fHeader + fDynamicSymbolTable->locreloff());
520 if ( fDynamicSymbolTable->locreloff() < linkEditSegment->fileoff() )
69a49097 521 throw "local relocations not in __LINKEDIT";
a61fdf0a 522 if ( (fDynamicSymbolTable->locreloff()+fLocalRelocationsCount*sizeof(macho_relocation_info<P>)) > (linkEditSegment->fileoff()+linkEditSegment->filesize()) )
69a49097 523 throw "local relocations not in __LINKEDIT";
a61fdf0a
A
524 if ( (fDynamicSymbolTable->locreloff() % sizeof(pint_t)) != 0 )
525 throw "local relocations table not pointer aligned";
69a49097 526 }
a61fdf0a 527 fExternalRelocationsCount = fDynamicSymbolTable->nextrel();
69a49097 528 if ( fExternalRelocationsCount != 0 ) {
a61fdf0a
A
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";
69a49097 536 }
d696c285
A
537 }
538 break;
a61fdf0a
A
539 case LC_SEGMENT_SPLIT_INFO:
540 {
541 if ( isStaticExecutable )
542 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable";
a645023d 543 const macho_linkedit_data_command<P>* info = (macho_linkedit_data_command<P>*)cmd;
a61fdf0a
A
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";
552 }
553 break;
a645023d
A
554 case LC_FUNCTION_STARTS:
555 {
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";
565 }
566 break;
d696c285
A
567 }
568 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
569 }
570 if ( !isStaticExecutable && !foundDynamicSymTab )
571 throw "missing dynamic symbol table";
572 if ( fStrings == NULL )
573 throw "missing symbol table";
a61fdf0a 574
d696c285
A
575}
576
577template <typename A>
578void MachOChecker<A>::checkSection(const macho_segment_command<P>* segCmd, const macho_section<P>* sect)
579{
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());
584 }
585
586 // more section tests here
587}
588
589template <typename A>
590void MachOChecker<A>::checkIndirectSymbolTable()
591{
a645023d
A
592 // static executables don't have indirect symbol table
593 if ( fDynamicSymbolTable == NULL )
594 return;
d696c285
A
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 = &sectionsStart[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
605 uint32_t start = 0;
606 uint32_t elementSize = 0;
607 switch ( sect->flags() & SECTION_TYPE ) {
608 case S_SYMBOL_STUBS:
609 elementSize = sect->reserved2();
610 start = sect->reserved1();
611 break;
612 case S_LAZY_SYMBOL_POINTERS:
613 case S_NON_LAZY_SYMBOL_POINTERS:
614 elementSize = sizeof(pint_t);
615 start = sect->reserved1();
616 break;
617 }
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 );
624 }
625 }
626 }
627 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
628 }
629}
630
631
a61fdf0a
A
632template <typename A>
633void MachOChecker<A>::checkSymbolTable()
634{
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()];
55e3d2f6
A
640 int i = fDynamicSymbolTable->iextdefsym();
641 for(const macho_nlist<P>* p = exportedStart; p < exportedEnd; ++p, ++i) {
a61fdf0a 642 const char* symName = &fStrings[p->n_strx()];
55e3d2f6
A
643 if ( symName > fStringsEnd )
644 throw "string index out of range";
645 //fprintf(stderr, "sym[%d] = %s\n", i, symName);
a61fdf0a
A
646 if ( externalNames.find(symName) != externalNames.end() )
647 throwf("duplicate external symbol: %s", symName);
a645023d
A
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);
a61fdf0a 653 }
55e3d2f6
A
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);
663 }
c211e7c9
A
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());
670 }
671 }
672 }
a61fdf0a
A
673 }
674}
675
69a49097
A
676
677template <>
678ppc::P::uint_t MachOChecker<ppc>::relocBase()
679{
680 if ( fHeader->flags() & MH_SPLIT_SEGS )
681 return fFirstWritableSegment->vmaddr();
682 else
683 return fFirstSegment->vmaddr();
684}
685
686template <>
687ppc64::P::uint_t MachOChecker<ppc64>::relocBase()
688{
689 if ( fWriteableSegmentWithAddrOver4G )
690 return fFirstWritableSegment->vmaddr();
691 else
692 return fFirstSegment->vmaddr();
693}
694
695template <>
696x86::P::uint_t MachOChecker<x86>::relocBase()
697{
698 if ( fHeader->flags() & MH_SPLIT_SEGS )
699 return fFirstWritableSegment->vmaddr();
700 else
701 return fFirstSegment->vmaddr();
702}
703
704template <>
705x86_64::P::uint_t MachOChecker<x86_64>::relocBase()
706{
707 // check for split-seg
708 return fFirstWritableSegment->vmaddr();
709}
710
2f2f92e4
A
711template <>
712arm::P::uint_t MachOChecker<arm>::relocBase()
713{
714 if ( fHeader->flags() & MH_SPLIT_SEGS )
715 return fFirstWritableSegment->vmaddr();
716 else
717 return fFirstSegment->vmaddr();
718}
719
69a49097
A
720
721template <typename A>
722bool MachOChecker<A>::addressInWritableSegment(pint_t address)
723{
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;
2f2f92e4
A
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 )
69a49097 733 return true;
2f2f92e4
A
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 = &sectionsStart[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 );
741 }
742 }
69a49097
A
743 }
744 }
745 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
746 }
747 return false;
748}
749
750
751template <>
752void MachOChecker<ppc>::checkExternalReloation(const macho_relocation_info<P>* reloc)
753{
a61fdf0a
A
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
69a49097
A
765}
766
767template <>
768void MachOChecker<ppc64>::checkExternalReloation(const macho_relocation_info<P>* reloc)
769{
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()) )
a61fdf0a 779 throw "external relocation address not in writable segment";
69a49097
A
780 // FIX: check r_symbol
781}
782
783template <>
784void MachOChecker<x86>::checkExternalReloation(const macho_relocation_info<P>* reloc)
785{
a61fdf0a
A
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
69a49097
A
797}
798
799
800template <>
801void MachOChecker<x86_64>::checkExternalReloation(const macho_relocation_info<P>* reloc)
802{
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
814}
815
2f2f92e4
A
816template <>
817void MachOChecker<arm>::checkExternalReloation(const macho_relocation_info<P>* reloc)
818{
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
830}
831
832
69a49097
A
833template <>
834void MachOChecker<ppc>::checkLocalReloation(const macho_relocation_info<P>* reloc)
835{
836 if ( reloc->r_address() & R_SCATTERED ) {
837 // scattered
838 const macho_scattered_relocation_info<P>* sreloc = (const macho_scattered_relocation_info<P>*)reloc;
839 // FIX
840
841 }
842 else {
2f2f92e4
A
843 // ignore pair relocs
844 if ( reloc->r_type() == PPC_RELOC_PAIR )
845 return;
69a49097
A
846 // FIX
847 if ( ! this->addressInWritableSegment(reloc->r_address() + this->relocBase()) )
2f2f92e4 848 throwf("local relocation address 0x%08X not in writable segment", reloc->r_address());
69a49097
A
849 }
850}
851
852
853template <>
854void MachOChecker<ppc64>::checkLocalReloation(const macho_relocation_info<P>* reloc)
855{
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";
866}
867
868template <>
869void MachOChecker<x86>::checkLocalReloation(const macho_relocation_info<P>* reloc)
870{
871 // FIX
872}
873
874template <>
875void MachOChecker<x86_64>::checkLocalReloation(const macho_relocation_info<P>* reloc)
876{
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";
887}
888
2f2f92e4
A
889template <>
890void MachOChecker<arm>::checkLocalReloation(const macho_relocation_info<P>* reloc)
891{
892 if ( reloc->r_address() & R_SCATTERED ) {
893 // 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";
899 }
900 else {
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";
907 }
908}
69a49097
A
909
910template <typename A>
911void MachOChecker<A>::checkRelocations()
912{
a61fdf0a
A
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;
69a49097
A
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);
a61fdf0a
A
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();
925 }
69a49097
A
926 }
927
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);
931 }
a645023d
A
932
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 )
942 continue;
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 = &sectionsStart[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());
950 }
951 }
952 }
953 }
954 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
955 }
69a49097
A
956}
957
a645023d
A
958template <typename A>
959typename A::P::uint_t MachOChecker<A>::segStartAddress(uint8_t segIndex)
960{
961 if ( segIndex > fSegments.size() )
962 throw "segment index out of range";
963 return fSegments[segIndex]->vmaddr();
964}
965
966template <typename A>
967bool MachOChecker<A>::hasTextRelocInRange(pint_t rangeStart, pint_t rangeEnd)
968{
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) )
974 return true;
975 }
976 // look rebase info
977 if ( fDyldInfo != NULL ) {
978 const uint8_t* p = (uint8_t*)fHeader + fDyldInfo->rebase_off();
979 const uint8_t* end = &p[fDyldInfo->rebase_size()];
980
981 uint8_t type = 0;
982 uint64_t segOffset = 0;
983 uint32_t count;
984 uint32_t skip;
985 int segIndex;
986 pint_t segStartAddr = 0;
987 pint_t addr;
988 bool done = false;
989 while ( !done && (p < end) ) {
990 uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
991 uint8_t opcode = *p & REBASE_OPCODE_MASK;
992 ++p;
993 switch (opcode) {
994 case REBASE_OPCODE_DONE:
995 done = true;
996 break;
997 case REBASE_OPCODE_SET_TYPE_IMM:
998 type = immediate;
999 break;
1000 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
1001 segIndex = immediate;
1002 segStartAddr = segStartAddress(segIndex);
1003 segOffset = read_uleb128(p, end);
1004 break;
1005 case REBASE_OPCODE_ADD_ADDR_ULEB:
1006 segOffset += read_uleb128(p, end);
1007 break;
1008 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
1009 segOffset += immediate*sizeof(pint_t);
1010 break;
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) )
1015 return true;
1016 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1017 segOffset += sizeof(pint_t);
1018 }
1019 break;
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) )
1025 return true;
1026 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1027 segOffset += sizeof(pint_t);
1028 }
1029 break;
1030 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
1031 addr = segStartAddr+segOffset;
1032 if ( (rangeStart <= addr) && (addr < rangeEnd) )
1033 return true;
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);
1036 break;
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) )
1043 return true;
1044 //printf("%-7s %-16s 0x%08llX %s\n", segName, sectionName(segIndex, segStartAddr+segOffset), segStartAddr+segOffset, typeName);
1045 segOffset += skip + sizeof(pint_t);
1046 }
1047 break;
1048 default:
1049 throwf("bad rebase opcode %d", *p);
1050 }
1051 }
1052 }
1053}
69a49097 1054
d696c285
A
1055static void check(const char* path)
1056{
1057 struct stat stat_buf;
1058
1059 try {
1060 int fd = ::open(path, O_RDONLY, 0);
1061 if ( fd == -1 )
1062 throw "cannot open file";
55e3d2f6
A
1063 if ( ::fstat(fd, &stat_buf) != 0 )
1064 throwf("fstat(%s) failed, errno=%d\n", path, errno);
d696c285 1065 uint32_t length = stat_buf.st_size;
69a49097
A
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";
d696c285
A
1069 ::close(fd);
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));
a61fdf0a
A
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);
1078
1079 switch(cputype) {
1080 case CPU_TYPE_POWERPC:
1081 if ( MachOChecker<ppc>::validFile(p + offset) )
1082 MachOChecker<ppc>::make(p + offset, size, path);
d696c285
A
1083 else
1084 throw "in universal file, ppc slice does not contain ppc mach-o";
a61fdf0a
A
1085 break;
1086 case CPU_TYPE_I386:
1087 if ( MachOChecker<x86>::validFile(p + offset) )
1088 MachOChecker<x86>::make(p + offset, size, path);
d696c285
A
1089 else
1090 throw "in universal file, i386 slice does not contain i386 mach-o";
a61fdf0a
A
1091 break;
1092 case CPU_TYPE_POWERPC64:
1093 if ( MachOChecker<ppc64>::validFile(p + offset) )
1094 MachOChecker<ppc64>::make(p + offset, size, path);
d696c285
A
1095 else
1096 throw "in universal file, ppc64 slice does not contain ppc64 mach-o";
a61fdf0a
A
1097 break;
1098 case CPU_TYPE_X86_64:
1099 if ( MachOChecker<x86_64>::validFile(p + offset) )
1100 MachOChecker<x86_64>::make(p + offset, size, path);
69a49097
A
1101 else
1102 throw "in universal file, x86_64 slice does not contain x86_64 mach-o";
a61fdf0a 1103 break;
2f2f92e4
A
1104 case CPU_TYPE_ARM:
1105 if ( MachOChecker<arm>::validFile(p + offset) )
1106 MachOChecker<arm>::make(p + offset, size, path);
1107 else
1108 throw "in universal file, arm slice does not contain arm mach-o";
1109 break;
a61fdf0a
A
1110 default:
1111 throwf("in universal file, unknown architecture slice 0x%x\n", cputype);
d696c285
A
1112 }
1113 }
1114 }
1115 else if ( MachOChecker<x86>::validFile(p) ) {
1116 MachOChecker<x86>::make(p, length, path);
1117 }
1118 else if ( MachOChecker<ppc>::validFile(p) ) {
1119 MachOChecker<ppc>::make(p, length, path);
1120 }
1121 else if ( MachOChecker<ppc64>::validFile(p) ) {
1122 MachOChecker<ppc64>::make(p, length, path);
1123 }
69a49097
A
1124 else if ( MachOChecker<x86_64>::validFile(p) ) {
1125 MachOChecker<x86_64>::make(p, length, path);
1126 }
2f2f92e4
A
1127 else if ( MachOChecker<arm>::validFile(p) ) {
1128 MachOChecker<arm>::make(p, length, path);
1129 }
d696c285
A
1130 else {
1131 throw "not a known file type";
1132 }
1133 }
1134 catch (const char* msg) {
1135 throwf("%s in %s", msg, path);
1136 }
1137}
1138
1139
1140int main(int argc, const char* argv[])
1141{
1142 try {
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 ) {
1147
1148 }
1149 else {
1150 throwf("unknown option: %s\n", arg);
1151 }
1152 }
1153 else {
1154 check(arg);
1155 }
1156 }
1157 }
1158 catch (const char* msg) {
1159 fprintf(stderr, "machocheck failed: %s\n", msg);
1160 return 1;
1161 }
1162
1163 return 0;
1164}
1165
1166
1167