]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/macho++.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / macho++.cpp
1 /*
2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // macho++ - Mach-O object file helpers
26 //
27 #include "macho++.h"
28 #include <security_utilities/alloc.h>
29 #include <security_utilities/memutils.h>
30 #include <security_utilities/endian.h>
31 #include <mach-o/dyld.h>
32 #include <list>
33 #include <algorithm>
34 #include <iterator>
35
36 namespace Security {
37
38 /* Maximum number of archs a fat binary can have */
39 static const int MAX_ARCH_COUNT = 100;
40 /* Maximum power of 2 that a mach-o can be aligned by */
41 static const int MAX_ALIGN = 30;
42
43 //
44 // Architecture values
45 //
46 Architecture::Architecture(const fat_arch &arch)
47 : pair<cpu_type_t, cpu_subtype_t>(arch.cputype, arch.cpusubtype)
48 {
49 }
50
51 Architecture::Architecture(const char *name)
52 {
53 if (const NXArchInfo *nxa = NXGetArchInfoFromName(name)) {
54 this->first = nxa->cputype;
55 this->second = nxa->cpusubtype;
56 } else {
57 this->first = this->second = none;
58 }
59 }
60
61
62 //
63 // The local architecture.
64 //
65 // We take this from ourselves - the architecture of our main program Mach-O binary.
66 // There's the NXGetLocalArchInfo API, but it insists on saying "i386" on modern
67 // x86_64-centric systems, and lies to ppc (Rosetta) programs claiming they're native ppc.
68 // So let's not use that.
69 //
70 Architecture Architecture::local()
71 {
72 return MainMachOImage().architecture();
73 }
74
75
76 //
77 // Translate between names and numbers
78 //
79 const char *Architecture::name() const
80 {
81 if (const NXArchInfo *info = NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
82 return info->name;
83 else
84 return NULL;
85 }
86
87 std::string Architecture::displayName() const
88 {
89 if (const char *s = this->name())
90 return s;
91 char buf[20];
92 snprintf(buf, sizeof(buf), "(%d:%d)", cpuType(), cpuSubtype());
93 return buf;
94 }
95
96
97 //
98 // Compare architectures.
99 // This is asymmetrical; the second argument provides for some templating.
100 //
101 bool Architecture::matches(const Architecture &templ) const
102 {
103 if (first != templ.first)
104 return false; // main architecture mismatch
105 if (templ.second == CPU_SUBTYPE_MULTIPLE)
106 return true; // subtype wildcard
107 // match subtypes, ignoring feature bits
108 return ((second ^ templ.second) & ~CPU_SUBTYPE_MASK) == 0;
109 }
110
111
112 //
113 // MachOBase contains knowledge of the Mach-O object file format,
114 // but abstracts from any particular sourcing. It must be subclassed,
115 // and the subclass must provide the file header and commands area
116 // during its construction. Memory is owned by the subclass.
117 //
118 MachOBase::~MachOBase()
119 { /* virtual */ }
120
121 // provide the Mach-O file header, somehow
122 void MachOBase::initHeader(const mach_header *header)
123 {
124 mHeader = header;
125 switch (mHeader->magic) {
126 case MH_MAGIC:
127 mFlip = false;
128 m64 = false;
129 break;
130 case MH_CIGAM:
131 mFlip = true;
132 m64 = false;
133 break;
134 case MH_MAGIC_64:
135 mFlip = false;
136 m64 = true;
137 break;
138 case MH_CIGAM_64:
139 mFlip = true;
140 m64 = true;
141 break;
142 default:
143 secdebug("macho", "%p: unrecognized header magic (%x)", this, mHeader->magic);
144 UnixError::throwMe(ENOEXEC);
145 }
146 }
147
148 // provide the Mach-O commands section, somehow
149 void MachOBase::initCommands(const load_command *commands)
150 {
151 mCommands = commands;
152 mEndCommands = LowLevelMemoryUtilities::increment<load_command>(commands, flip(mHeader->sizeofcmds));
153 if (mCommands + 1 > mEndCommands) // ensure initial load command core available
154 UnixError::throwMe(ENOEXEC);
155 }
156
157
158 size_t MachOBase::headerSize() const
159 {
160 return m64 ? sizeof(mach_header_64) : sizeof(mach_header);
161 }
162
163 size_t MachOBase::commandSize() const
164 {
165 return flip(mHeader->sizeofcmds);
166 }
167
168
169 //
170 // Create a MachO object from an open file and a starting offset.
171 // We load (only) the header and load commands into memory at that time.
172 // Note that the offset must be relative to the start of the containing file
173 // (not relative to some intermediate container).
174 //
175 MachO::MachO(FileDesc fd, size_t offset, size_t length)
176 : FileDesc(fd), mOffset(offset), mLength(length), mSuspicious(false)
177 {
178 if (mOffset == 0)
179 mLength = fd.fileSize();
180 size_t size = fd.read(&mHeaderBuffer, sizeof(mHeaderBuffer), mOffset);
181 if (size != sizeof(mHeaderBuffer))
182 UnixError::throwMe(ENOEXEC);
183 this->initHeader(&mHeaderBuffer);
184 size_t cmdSize = this->commandSize();
185 mCommandBuffer = (load_command *)malloc(cmdSize);
186 if (!mCommandBuffer)
187 UnixError::throwMe();
188 if (fd.read(mCommandBuffer, cmdSize, this->headerSize() + mOffset) != cmdSize)
189 UnixError::throwMe(ENOEXEC);
190 this->initCommands(mCommandBuffer);
191 /* If we do not know the length, we cannot do a verification of the mach-o structure */
192 if (mLength != 0)
193 this->validateStructure();
194 }
195
196 void MachO::validateStructure()
197 {
198 bool isValid = false;
199
200 /* There should be either an LC_SEGMENT, an LC_SEGMENT_64, or an LC_SYMTAB
201 load_command and that + size must be equal to the end of the arch */
202 for (const struct load_command *cmd = loadCommands(); cmd != NULL; cmd = nextCommand(cmd)) {
203 uint32_t cmd_type = flip(cmd->cmd);
204 struct segment_command *seg = NULL;
205 struct segment_command_64 *seg64 = NULL;
206 struct symtab_command *symtab = NULL;
207
208 if (cmd_type == LC_SEGMENT) {
209 seg = (struct segment_command *)cmd;
210 if (strcmp(seg->segname, SEG_LINKEDIT) == 0) {
211 isValid = flip(seg->fileoff) + flip(seg->filesize) == this->length();
212 break;
213 }
214 } else if (cmd_type == LC_SEGMENT_64) {
215 seg64 = (struct segment_command_64 *)cmd;
216 if (strcmp(seg64->segname, SEG_LINKEDIT) == 0) {
217 isValid = flip(seg64->fileoff) + flip(seg64->filesize) == this->length();
218 break;
219 }
220 /* PPC binaries have a SYMTAB section */
221 } else if (cmd_type == LC_SYMTAB) {
222 symtab = (struct symtab_command *)cmd;
223 isValid = flip(symtab->stroff) + flip(symtab->strsize) == this->length();
224 break;
225 }
226 }
227
228 if (!isValid)
229 mSuspicious = true;
230 }
231
232 MachO::~MachO()
233 {
234 ::free(mCommandBuffer);
235 }
236
237
238 //
239 // Create a MachO object that is (entirely) mapped into memory.
240 // The caller must ensire that the underlying mapping persists
241 // at least as long as our object.
242 //
243 MachOImage::MachOImage(const void *address)
244 {
245 this->initHeader((const mach_header *)address);
246 this->initCommands(LowLevelMemoryUtilities::increment<const load_command>(address, this->headerSize()));
247 }
248
249
250 //
251 // Locate the Mach-O image of the main program
252 //
253 MainMachOImage::MainMachOImage()
254 : MachOImage(mainImageAddress())
255 {
256 }
257
258 const void *MainMachOImage::mainImageAddress()
259 {
260 return _dyld_get_image_header(0);
261 }
262
263
264 //
265 // Return various header fields
266 //
267 Architecture MachOBase::architecture() const
268 {
269 return Architecture(flip(mHeader->cputype), flip(mHeader->cpusubtype));
270 }
271
272 uint32_t MachOBase::type() const
273 {
274 return flip(mHeader->filetype);
275 }
276
277 uint32_t MachOBase::flags() const
278 {
279 return flip(mHeader->flags);
280 }
281
282
283 //
284 // Iterate through load commands
285 //
286 const load_command *MachOBase::nextCommand(const load_command *command) const
287 {
288 using LowLevelMemoryUtilities::increment;
289 /* Do not try and increment by 0, or it will loop forever */
290 if (flip(command->cmdsize) == 0)
291 UnixError::throwMe(ENOEXEC);
292 command = increment<const load_command>(command, flip(command->cmdsize));
293 if (command >= mEndCommands) // end of load commands
294 return NULL;
295 if (increment(command, sizeof(load_command)) > mEndCommands
296 || increment(command, flip(command->cmdsize)) > mEndCommands)
297 UnixError::throwMe(ENOEXEC);
298 return command;
299 }
300
301
302 //
303 // Find a specific load command, by command number.
304 // If there are multiples, returns the first one found.
305 //
306 const load_command *MachOBase::findCommand(uint32_t cmd) const
307 {
308 for (const load_command *command = loadCommands(); command; command = nextCommand(command))
309 if (flip(command->cmd) == cmd)
310 return command;
311 return NULL;
312 }
313
314
315 //
316 // Locate a segment command, by name
317 //
318 const segment_command *MachOBase::findSegment(const char *segname) const
319 {
320 for (const load_command *command = loadCommands(); command; command = nextCommand(command)) {
321 switch (flip(command->cmd)) {
322 case LC_SEGMENT:
323 case LC_SEGMENT_64:
324 {
325 const segment_command *seg = reinterpret_cast<const segment_command *>(command);
326 if (!strcmp(seg->segname, segname))
327 return seg;
328 break;
329 }
330 default:
331 break;
332 }
333 }
334 return NULL;
335 }
336
337 const section *MachOBase::findSection(const char *segname, const char *sectname) const
338 {
339 using LowLevelMemoryUtilities::increment;
340 if (const segment_command *seg = findSegment(segname)) {
341 if (is64()) {
342 const segment_command_64 *seg64 = reinterpret_cast<const segment_command_64 *>(seg);
343 // As a sanity check, if the reported number of sections is not consistent
344 // with the reported size, return NULL
345 if (sizeof(*seg64) + (seg64->nsects * sizeof(section_64)) > seg64->cmdsize)
346 return NULL;
347 const section_64 *sect = increment<const section_64>(seg64 + 1, 0);
348 for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) {
349 if (!strcmp(sect->sectname, sectname))
350 return reinterpret_cast<const section *>(sect);
351 }
352 } else {
353 const section *sect = increment<const section>(seg + 1, 0);
354 for (unsigned n = flip(seg->nsects); n > 0; n--, sect++) {
355 if (!strcmp(sect->sectname, sectname))
356 return sect;
357 }
358 }
359 }
360 return NULL;
361 }
362
363
364 //
365 // Translate a union lc_str into the string it denotes.
366 // Returns NULL (no exceptions) if the entry is corrupt.
367 //
368 const char *MachOBase::string(const load_command *cmd, const lc_str &str) const
369 {
370 size_t offset = flip(str.offset);
371 const char *sp = LowLevelMemoryUtilities::increment<const char>(cmd, offset);
372 if (offset + strlen(sp) + 1 > flip(cmd->cmdsize)) // corrupt string reference
373 return NULL;
374 return sp;
375 }
376
377
378 //
379 // Figure out where the Code Signing information starts in the Mach-O binary image.
380 // The code signature is at the end of the file, and identified
381 // by a specially-named section. So its starting offset is also the end
382 // of the signable part.
383 // Note that the offset returned is relative to the start of the Mach-O image.
384 // Returns zero if not found (usually indicating that the binary was not signed).
385 //
386 const linkedit_data_command *MachOBase::findCodeSignature() const
387 {
388 if (const load_command *cmd = findCommand(LC_CODE_SIGNATURE))
389 return reinterpret_cast<const linkedit_data_command *>(cmd);
390 return NULL; // not found
391 }
392
393 size_t MachOBase::signingOffset() const
394 {
395 if (const linkedit_data_command *lec = findCodeSignature())
396 return flip(lec->dataoff);
397 else
398 return 0;
399 }
400
401 size_t MachOBase::signingLength() const
402 {
403 if (const linkedit_data_command *lec = findCodeSignature())
404 return flip(lec->datasize);
405 else
406 return 0;
407 }
408
409 const linkedit_data_command *MachOBase::findLibraryDependencies() const
410 {
411 if (const load_command *cmd = findCommand(LC_DYLIB_CODE_SIGN_DRS))
412 return reinterpret_cast<const linkedit_data_command *>(cmd);
413 return NULL; // not found
414 }
415
416 const version_min_command *MachOBase::findMinVersion() const
417 {
418 for (const load_command *command = loadCommands(); command; command = nextCommand(command))
419 switch (flip(command->cmd)) {
420 case LC_VERSION_MIN_MACOSX:
421 case LC_VERSION_MIN_IPHONEOS:
422 case LC_VERSION_MIN_WATCHOS:
423 case LC_VERSION_MIN_TVOS:
424 return reinterpret_cast<const version_min_command *>(command);
425 }
426 return NULL;
427 }
428
429
430 //
431 // Return the signing-limit length for this Mach-O binary image.
432 // This is the signingOffset if present, or the full length if not.
433 //
434 size_t MachO::signingExtent() const
435 {
436 if (size_t offset = signingOffset())
437 return offset;
438 else
439 return length();
440 }
441
442
443 //
444 // I/O operations
445 //
446 void MachO::seek(size_t offset)
447 {
448 FileDesc::seek(mOffset + offset);
449 }
450
451 CFDataRef MachO::dataAt(size_t offset, size_t size)
452 {
453 CFMallocData buffer(size);
454 if (this->read(buffer, size, mOffset + offset) != size)
455 UnixError::throwMe();
456 return buffer;
457 }
458
459 //
460 // Fat (aka universal) file wrappers.
461 // The offset is relative to the start of the containing file.
462 //
463 Universal::Universal(FileDesc fd, size_t offset /* = 0 */, size_t length /* = 0 */)
464 : FileDesc(fd), mBase(offset), mLength(length), mMachType(0), mSuspicious(false)
465 {
466 union {
467 fat_header header; // if this is a fat file
468 mach_header mheader; // if this is a thin file
469 };
470 const size_t size = max(sizeof(header), sizeof(mheader));
471 if (fd.read(&header, size, offset) != size)
472 UnixError::throwMe(ENOEXEC);
473 switch (header.magic) {
474 case FAT_MAGIC:
475 case FAT_CIGAM:
476 {
477 //
478 // Hack alert.
479 // Under certain circumstances (15001604), mArchCount under-counts the architectures
480 // by one, and special testing is required to validate the extra-curricular entry.
481 // We always read an extra entry; in the situations where this might hit end-of-file,
482 // we are content to fail.
483 //
484 mArchCount = ntohl(header.nfat_arch);
485
486 if (mArchCount > MAX_ARCH_COUNT)
487 UnixError::throwMe(ENOEXEC);
488
489 size_t archSize = sizeof(fat_arch) * (mArchCount + 1);
490 mArchList = (fat_arch *)malloc(archSize);
491 if (!mArchList)
492 UnixError::throwMe();
493 if (fd.read(mArchList, archSize, mBase + sizeof(header)) != archSize) {
494 ::free(mArchList);
495 UnixError::throwMe(ENOEXEC);
496 }
497 for (fat_arch *arch = mArchList; arch <= mArchList + mArchCount; arch++) {
498 n2hi(arch->cputype);
499 n2hi(arch->cpusubtype);
500 n2hi(arch->offset);
501 n2hi(arch->size);
502 n2hi(arch->align);
503 }
504 const fat_arch *last_arch = mArchList + mArchCount;
505 if (last_arch->cputype == (CPU_ARCH_ABI64 | CPU_TYPE_ARM)) {
506 mArchCount++;
507 }
508 secdebug("macho", "%p is a fat file with %d architectures",
509 this, mArchCount);
510
511 /* A Mach-O universal file has padding of no more than "page size"
512 * between the header and slices. This padding must be zeroed out or the file
513 is not valid */
514 std::list<struct fat_arch *> sortedList;
515 for (unsigned i = 0; i < mArchCount; i++)
516 sortedList.push_back(mArchList + i);
517
518 sortedList.sort(^ bool (const struct fat_arch *arch1, const struct fat_arch *arch2) { return arch1->offset < arch2->offset; });
519
520 const size_t universalHeaderEnd = mBase + sizeof(header) + (sizeof(fat_arch) * mArchCount);
521 size_t prevHeaderEnd = universalHeaderEnd;
522 size_t prevArchSize = 0, prevArchStart = 0;
523
524 for (auto iterator = sortedList.begin(); iterator != sortedList.end(); ++iterator) {
525 auto ret = mSizes.insert(std::pair<size_t, size_t>((*iterator)->offset, (*iterator)->size));
526 if (ret.second == false) {
527 ::free(mArchList);
528 MacOSError::throwMe(errSecInternalError); // Something is wrong if the same size was encountered twice
529 }
530
531 size_t gapSize = (*iterator)->offset - prevHeaderEnd;
532
533 /* The size of the padding after the universal cannot be calculated to a fixed size */
534 if (prevHeaderEnd != universalHeaderEnd) {
535 if (((*iterator)->align > MAX_ALIGN) || gapSize >= (1 << (*iterator)->align)) {
536 mSuspicious = true;
537 break;
538 }
539 }
540
541 // validate gap bytes in tasty page-sized chunks
542 CssmAutoPtr<uint8_t> gapBytes(Allocator::standard().malloc<uint8_t>(PAGE_SIZE));
543 size_t off = 0;
544 while (off < gapSize) {
545 size_t want = min(gapSize - off, (size_t)PAGE_SIZE);
546 size_t got = fd.read(gapBytes, want, prevHeaderEnd + off);
547 if (got == 0) {
548 mSuspicious = true;
549 break;
550 }
551 off += got;
552 for (size_t x = 0; x < got; x++) {
553 if (gapBytes[x] != 0) {
554 mSuspicious = true;
555 break;
556 }
557 }
558 if (mSuspicious)
559 break;
560 }
561 if (off != gapSize)
562 mSuspicious = true;
563 if (mSuspicious)
564 break;
565
566 prevHeaderEnd = (*iterator)->offset + (*iterator)->size;
567 prevArchSize = (*iterator)->size;
568 prevArchStart = (*iterator)->offset;
569 }
570
571 /* If there is anything extra at the end of the file, reject this */
572 if (!mSuspicious && (prevArchStart + prevArchSize != fd.fileSize()))
573 mSuspicious = true;
574
575 break;
576 }
577 case MH_MAGIC:
578 case MH_MAGIC_64:
579 mArchList = NULL;
580 mArchCount = 0;
581 mThinArch = Architecture(mheader.cputype, mheader.cpusubtype);
582 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
583 break;
584 case MH_CIGAM:
585 case MH_CIGAM_64:
586 mArchList = NULL;
587 mArchCount = 0;
588 mThinArch = Architecture(flip(mheader.cputype), flip(mheader.cpusubtype));
589 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
590 break;
591 default:
592 UnixError::throwMe(ENOEXEC);
593 }
594 }
595
596 Universal::~Universal()
597 {
598 ::free(mArchList);
599 }
600
601 const size_t Universal::lengthOfSlice(size_t offset) const
602 {
603 auto ret = mSizes.find(offset);
604 if (ret == mSizes.end())
605 MacOSError::throwMe(errSecInternalError);
606 return ret->second;
607 }
608
609 //
610 // Get the "local" architecture from the fat file
611 // Throws ENOEXEC if not found.
612 //
613 MachO *Universal::architecture() const
614 {
615 if (isUniversal())
616 return findImage(bestNativeArch());
617 else
618 return new MachO(*this, mBase, mLength);
619 }
620
621 size_t Universal::archOffset() const
622 {
623 if (isUniversal())
624 return mBase + findArch(bestNativeArch())->offset;
625 else
626 return mBase;
627 }
628
629
630 //
631 // Get the specified architecture from the fat file
632 // Throws ENOEXEC if not found.
633 //
634 MachO *Universal::architecture(const Architecture &arch) const
635 {
636 if (isUniversal())
637 return findImage(arch);
638 else if (mThinArch.matches(arch))
639 return new MachO(*this, mBase);
640 else
641 UnixError::throwMe(ENOEXEC);
642 }
643
644 size_t Universal::archOffset(const Architecture &arch) const
645 {
646 if (isUniversal())
647 return mBase + findArch(arch)->offset;
648 else if (mThinArch.matches(arch))
649 return 0;
650 else
651 UnixError::throwMe(ENOEXEC);
652 }
653
654 size_t Universal::archLength(const Architecture &arch) const
655 {
656 if (isUniversal())
657 return mBase + findArch(arch)->size;
658 else if (mThinArch.matches(arch))
659 return this->fileSize();
660 else
661 UnixError::throwMe(ENOEXEC);
662 }
663
664 //
665 // Get the architecture at a specified offset from the fat file.
666 // Throws an exception of the offset does not point at a Mach-O image.
667 //
668 MachO *Universal::architecture(size_t offset) const
669 {
670 if (isUniversal())
671 return make(new MachO(*this, offset));
672 else if (offset == mBase)
673 return new MachO(*this);
674 else
675 UnixError::throwMe(ENOEXEC);
676 }
677
678
679 //
680 // Locate an architecture from the fat file's list.
681 // Throws ENOEXEC if not found.
682 //
683 const fat_arch *Universal::findArch(const Architecture &target) const
684 {
685 assert(isUniversal());
686 const fat_arch *end = mArchList + mArchCount;
687 // exact match
688 for (const fat_arch *arch = mArchList; arch < end; ++arch)
689 if (arch->cputype == target.cpuType()
690 && arch->cpusubtype == target.cpuSubtype())
691 return arch;
692 // match for generic model of main architecture
693 for (const fat_arch *arch = mArchList; arch < end; ++arch)
694 if (arch->cputype == target.cpuType() && arch->cpusubtype == 0)
695 return arch;
696 // match for any subarchitecture of the main architecture (questionable)
697 for (const fat_arch *arch = mArchList; arch < end; ++arch)
698 if (arch->cputype == target.cpuType())
699 return arch;
700 // no match
701 UnixError::throwMe(ENOEXEC); // not found
702 }
703
704 MachO *Universal::findImage(const Architecture &target) const
705 {
706 const fat_arch *arch = findArch(target);
707 return make(new MachO(*this, mBase + arch->offset, arch->size));
708 }
709
710 MachO* Universal::make(MachO* macho) const
711 {
712 auto_ptr<MachO> mo(macho); // safe resource
713 uint32_t type = mo->type();
714 if (type == 0) // not a recognized Mach-O type
715 UnixError::throwMe(ENOEXEC);
716 if (mMachType && mMachType != type) // inconsistent members
717 UnixError::throwMe(ENOEXEC);
718 mMachType = type; // record
719 return mo.release();
720 }
721
722
723 //
724 // Find the best-matching architecture for this fat file.
725 // We pick the native architecture if it's available.
726 // If it contains exactly one architecture, we take that.
727 // Otherwise, we throw.
728 //
729 Architecture Universal::bestNativeArch() const
730 {
731 if (isUniversal()) {
732 // ask the NXArch API for our native architecture
733 const Architecture native = Architecture::local();
734 if (fat_arch *match = NXFindBestFatArch(native.cpuType(), native.cpuSubtype(), mArchList, mArchCount))
735 return *match;
736 // if the system can't figure it out, pick (arbitrarily) the first one
737 return mArchList[0];
738 } else
739 return mThinArch;
740 }
741
742 //
743 // List all architectures from the fat file's list.
744 //
745 void Universal::architectures(Architectures &archs) const
746 {
747 if (isUniversal()) {
748 for (unsigned n = 0; n < mArchCount; n++)
749 archs.insert(mArchList[n]);
750 } else {
751 auto_ptr<MachO> macho(architecture());
752 archs.insert(macho->architecture());
753 }
754 }
755
756 //
757 // Quickly guess the Mach-O type of a file.
758 // Returns type zero if the file isn't Mach-O or Universal.
759 // Always looks at the start of the file, and does not change the file pointer.
760 //
761 uint32_t Universal::typeOf(FileDesc fd)
762 {
763 mach_header header;
764 int max_tries = 3;
765 if (fd.read(&header, sizeof(header), 0) != sizeof(header))
766 return 0;
767 while (max_tries > 0) {
768 switch (header.magic) {
769 case MH_MAGIC:
770 case MH_MAGIC_64:
771 return header.filetype;
772 break;
773 case MH_CIGAM:
774 case MH_CIGAM_64:
775 return flip(header.filetype);
776 break;
777 case FAT_MAGIC:
778 case FAT_CIGAM:
779 {
780 const fat_arch *arch1 =
781 LowLevelMemoryUtilities::increment<fat_arch>(&header, sizeof(fat_header));
782 if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header))
783 return 0;
784 max_tries--;
785 continue;
786 }
787 default:
788 return 0;
789 }
790 }
791 return 0;
792 }
793
794 //
795 // Strict validation
796 //
797 bool Universal::isSuspicious() const
798 {
799 if (mSuspicious)
800 return true;
801 Universal::Architectures archList;
802 architectures(archList);
803 for (Universal::Architectures::const_iterator it = archList.begin(); it != archList.end(); ++it) {
804 auto_ptr<MachO> macho(architecture(*it));
805 if (macho->isSuspicious())
806 return true;
807 }
808 return false;
809 }
810
811
812 } // Security