]> git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/macho++.cpp
e6f39de314088bf6d4c90ec8bd9822c7a44340d2
[apple/security.git] / libsecurity_utilities / lib / macho++.cpp
1 /*
2 * Copyright (c) 2006-2013 Apple Computer, 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/memutils.h>
29 #include <security_utilities/endian.h>
30 #include <mach-o/dyld.h>
31
32 namespace Security {
33
34
35 //
36 // Architecture values
37 //
38 Architecture::Architecture(const fat_arch &arch)
39 : pair<cpu_type_t, cpu_subtype_t>(arch.cputype, arch.cpusubtype)
40 {
41 }
42
43 Architecture::Architecture(const char *name)
44 {
45 if (const NXArchInfo *nxa = NXGetArchInfoFromName(name)) {
46 this->first = nxa->cputype;
47 this->second = nxa->cpusubtype;
48 } else {
49 this->first = this->second = none;
50 }
51 }
52
53
54 //
55 // The local architecture.
56 //
57 // We take this from ourselves - the architecture of our main program Mach-O binary.
58 // There's the NXGetLocalArchInfo API, but it insists on saying "i386" on modern
59 // x86_64-centric systems, and lies to ppc (Rosetta) programs claiming they're native ppc.
60 // So let's not use that.
61 //
62 Architecture Architecture::local()
63 {
64 return MainMachOImage().architecture();
65 }
66
67
68 //
69 // Translate between names and numbers
70 //
71 const char *Architecture::name() const
72 {
73 if (const NXArchInfo *info = NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
74 return info->name;
75 else
76 return NULL;
77 }
78
79 std::string Architecture::displayName() const
80 {
81 if (const char *s = this->name())
82 return s;
83 char buf[20];
84 snprintf(buf, sizeof(buf), "(%d:%d)", cpuType(), cpuSubtype());
85 return buf;
86 }
87
88
89 //
90 // Compare architectures.
91 // This is asymmetrical; the second argument provides for some templating.
92 //
93 bool Architecture::matches(const Architecture &templ) const
94 {
95 if (first != templ.first)
96 return false; // main architecture mismatch
97 if (templ.second == CPU_SUBTYPE_MULTIPLE)
98 return true; // subtype wildcard
99 // match subtypes, ignoring feature bits
100 return ((second ^ templ.second) & ~CPU_SUBTYPE_MASK) == 0;
101 }
102
103
104 //
105 // MachOBase contains knowledge of the Mach-O object file format,
106 // but abstracts from any particular sourcing. It must be subclassed,
107 // and the subclass must provide the file header and commands area
108 // during its construction. Memory is owned by the subclass.
109 //
110 MachOBase::~MachOBase()
111 { /* virtual */ }
112
113 // provide the Mach-O file header, somehow
114 void MachOBase::initHeader(const mach_header *header)
115 {
116 mHeader = header;
117 switch (mHeader->magic) {
118 case MH_MAGIC:
119 mFlip = false;
120 m64 = false;
121 break;
122 case MH_CIGAM:
123 mFlip = true;
124 m64 = false;
125 break;
126 case MH_MAGIC_64:
127 mFlip = false;
128 m64 = true;
129 break;
130 case MH_CIGAM_64:
131 mFlip = true;
132 m64 = true;
133 break;
134 default:
135 UnixError::throwMe(ENOEXEC);
136 }
137 }
138
139 // provide the Mach-O commands section, somehow
140 void MachOBase::initCommands(const load_command *commands)
141 {
142 mCommands = commands;
143 mEndCommands = LowLevelMemoryUtilities::increment<load_command>(commands, flip(mHeader->sizeofcmds));
144 if (mCommands + 1 > mEndCommands) // ensure initial load command core available
145 UnixError::throwMe(ENOEXEC);
146 }
147
148
149 size_t MachOBase::headerSize() const
150 {
151 return m64 ? sizeof(mach_header_64) : sizeof(mach_header);
152 }
153
154 size_t MachOBase::commandSize() const
155 {
156 return flip(mHeader->sizeofcmds);
157 }
158
159
160 //
161 // Create a MachO object from an open file and a starting offset.
162 // We load (only) the header and load commands into memory at that time.
163 // Note that the offset must be relative to the start of the containing file
164 // (not relative to some intermediate container).
165 //
166 MachO::MachO(FileDesc fd, size_t offset, size_t length)
167 : FileDesc(fd), mOffset(offset), mLength(length ? length : (fd.fileSize() - offset))
168 {
169 size_t size = fd.read(&mHeaderBuffer, sizeof(mHeaderBuffer), mOffset);
170 if (size != sizeof(mHeaderBuffer))
171 UnixError::throwMe(ENOEXEC);
172 this->initHeader(&mHeaderBuffer);
173 size_t cmdSize = this->commandSize();
174 mCommandBuffer = (load_command *)malloc(cmdSize);
175 if (!mCommandBuffer)
176 UnixError::throwMe();
177 if (fd.read(mCommandBuffer, cmdSize, this->headerSize() + mOffset) != cmdSize)
178 UnixError::throwMe(ENOEXEC);
179 this->initCommands(mCommandBuffer);
180 }
181
182 MachO::~MachO()
183 {
184 ::free(mCommandBuffer);
185 }
186
187
188 //
189 // Create a MachO object that is (entirely) mapped into memory.
190 // The caller must ensire that the underlying mapping persists
191 // at least as long as our object.
192 //
193 MachOImage::MachOImage(const void *address)
194 {
195 this->initHeader((const mach_header *)address);
196 this->initCommands(LowLevelMemoryUtilities::increment<const load_command>(address, this->headerSize()));
197 }
198
199
200 //
201 // Locate the Mach-O image of the main program
202 //
203 MainMachOImage::MainMachOImage()
204 : MachOImage(mainImageAddress())
205 {
206 }
207
208 const void *MainMachOImage::mainImageAddress()
209 {
210 return _dyld_get_image_header(0);
211 }
212
213
214 //
215 // Return various header fields
216 //
217 Architecture MachOBase::architecture() const
218 {
219 return Architecture(flip(mHeader->cputype), flip(mHeader->cpusubtype));
220 }
221
222 uint32_t MachOBase::type() const
223 {
224 return flip(mHeader->filetype);
225 }
226
227 uint32_t MachOBase::flags() const
228 {
229 return flip(mHeader->flags);
230 }
231
232
233 //
234 // Iterate through load commands
235 //
236 const load_command *MachOBase::nextCommand(const load_command *command) const
237 {
238 using LowLevelMemoryUtilities::increment;
239 command = increment<const load_command>(command, flip(command->cmdsize));
240 if (command >= mEndCommands) // end of load commands
241 return NULL;
242 if (increment(command, sizeof(load_command)) > mEndCommands
243 || increment(command, flip(command->cmdsize)) > mEndCommands)
244 UnixError::throwMe(ENOEXEC);
245 return command;
246 }
247
248
249 //
250 // Find a specific load command, by command number.
251 // If there are multiples, returns the first one found.
252 //
253 const load_command *MachOBase::findCommand(uint32_t cmd) const
254 {
255 for (const load_command *command = loadCommands(); command; command = nextCommand(command))
256 if (flip(command->cmd) == cmd)
257 return command;
258 return NULL;
259 }
260
261
262 //
263 // Locate a segment command, by name
264 //
265 const segment_command *MachOBase::findSegment(const char *segname) const
266 {
267 for (const load_command *command = loadCommands(); command; command = nextCommand(command)) {
268 switch (flip(command->cmd)) {
269 case LC_SEGMENT:
270 case LC_SEGMENT_64:
271 {
272 const segment_command *seg = reinterpret_cast<const segment_command *>(command);
273 if (!strcmp(seg->segname, segname))
274 return seg;
275 break;
276 }
277 default:
278 break;
279 }
280 }
281 return NULL;
282 }
283
284 const section *MachOBase::findSection(const char *segname, const char *sectname) const
285 {
286 using LowLevelMemoryUtilities::increment;
287 if (const segment_command *seg = findSegment(segname)) {
288 if (is64()) {
289 const segment_command_64 *seg64 = reinterpret_cast<const segment_command_64 *>(seg);
290 const section_64 *sect = increment<const section_64>(seg64 + 1, 0);
291 for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) {
292 if (!strcmp(sect->sectname, sectname))
293 return reinterpret_cast<const section *>(sect);
294 }
295 } else {
296 const section *sect = increment<const section>(seg + 1, 0);
297 for (unsigned n = flip(seg->nsects); n > 0; n--, sect++) {
298 if (!strcmp(sect->sectname, sectname))
299 return sect;
300 }
301 }
302 }
303 return NULL;
304 }
305
306
307 //
308 // Translate a union lc_str into the string it denotes.
309 // Returns NULL (no exceptions) if the entry is corrupt.
310 //
311 const char *MachOBase::string(const load_command *cmd, const lc_str &str) const
312 {
313 size_t offset = flip(str.offset);
314 const char *sp = LowLevelMemoryUtilities::increment<const char>(cmd, offset);
315 if (offset + strlen(sp) + 1 > flip(cmd->cmdsize)) // corrupt string reference
316 return NULL;
317 return sp;
318 }
319
320
321 //
322 // Figure out where the Code Signing information starts in the Mach-O binary image.
323 // The code signature is at the end of the file, and identified
324 // by a specially-named section. So its starting offset is also the end
325 // of the signable part.
326 // Note that the offset returned is relative to the start of the Mach-O image.
327 // Returns zero if not found (usually indicating that the binary was not signed).
328 //
329 const linkedit_data_command *MachOBase::findCodeSignature() const
330 {
331 if (const load_command *cmd = findCommand(LC_CODE_SIGNATURE))
332 return reinterpret_cast<const linkedit_data_command *>(cmd);
333 return NULL; // not found
334 }
335
336 size_t MachOBase::signingOffset() const
337 {
338 if (const linkedit_data_command *lec = findCodeSignature())
339 return flip(lec->dataoff);
340 else
341 return 0;
342 }
343
344 size_t MachOBase::signingLength() const
345 {
346 if (const linkedit_data_command *lec = findCodeSignature())
347 return flip(lec->datasize);
348 else
349 return 0;
350 }
351
352 const linkedit_data_command *MachOBase::findLibraryDependencies() const
353 {
354 if (const load_command *cmd = findCommand(LC_DYLIB_CODE_SIGN_DRS))
355 return reinterpret_cast<const linkedit_data_command *>(cmd);
356 return NULL; // not found
357 }
358
359
360 //
361 // Return the signing-limit length for this Mach-O binary image.
362 // This is the signingOffset if present, or the full length if not.
363 //
364 size_t MachO::signingExtent() const
365 {
366 if (size_t offset = signingOffset())
367 return offset;
368 else
369 return length();
370 }
371
372
373 //
374 // I/O operations
375 //
376 void MachO::seek(size_t offset)
377 {
378 FileDesc::seek(mOffset + offset);
379 }
380
381 CFDataRef MachO::dataAt(size_t offset, size_t size)
382 {
383 CFMallocData buffer(size);
384 if (this->read(buffer, size, mOffset + offset) != size)
385 UnixError::throwMe();
386 return buffer;
387 }
388
389
390 //
391 // Fat (aka universal) file wrappers.
392 // The offset is relative to the start of the containing file.
393 //
394 Universal::Universal(FileDesc fd, size_t offset /* = 0 */)
395 : FileDesc(fd), mBase(offset)
396 {
397 union {
398 fat_header header; // if this is a fat file
399 mach_header mheader; // if this is a thin file
400 };
401 const size_t size = max(sizeof(header), sizeof(mheader));
402 if (fd.read(&header, size, offset) != size)
403 UnixError::throwMe(ENOEXEC);
404 switch (header.magic) {
405 case FAT_MAGIC:
406 case FAT_CIGAM:
407 {
408 //
409 // Hack alert.
410 // Under certain circumstances (15001604), mArchCount under-counts the architectures
411 // by one, and special testing is required to validate the extra-curricular entry.
412 // We always read an extra entry; in the situations where this might hit end-of-file,
413 // we are content to fail.
414 //
415 mArchCount = ntohl(header.nfat_arch);
416 size_t archSize = sizeof(fat_arch) * (mArchCount + 1);
417 mArchList = (fat_arch *)malloc(archSize);
418 if (!mArchList)
419 UnixError::throwMe();
420 if (fd.read(mArchList, archSize, mBase + sizeof(header)) != archSize) {
421 ::free(mArchList);
422 UnixError::throwMe(ENOEXEC);
423 }
424 for (fat_arch *arch = mArchList; arch <= mArchList + mArchCount; arch++) {
425 n2hi(arch->cputype);
426 n2hi(arch->cpusubtype);
427 n2hi(arch->offset);
428 n2hi(arch->size);
429 n2hi(arch->align);
430 }
431 const fat_arch *last_arch = mArchList + mArchCount;
432 if (last_arch->cputype == (CPU_ARCH_ABI64 | CPU_TYPE_ARM)) {
433 mArchCount++;
434 }
435 secdebug("macho", "%p is a fat file with %d architectures",
436 this, mArchCount);
437 break;
438 }
439 case MH_MAGIC:
440 case MH_MAGIC_64:
441 mArchList = NULL;
442 mArchCount = 0;
443 mThinArch = Architecture(mheader.cputype, mheader.cpusubtype);
444 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
445 break;
446 case MH_CIGAM:
447 case MH_CIGAM_64:
448 mArchList = NULL;
449 mArchCount = 0;
450 mThinArch = Architecture(flip(mheader.cputype), flip(mheader.cpusubtype));
451 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
452 break;
453 default:
454 UnixError::throwMe(ENOEXEC);
455 }
456 }
457
458 Universal::~Universal()
459 {
460 ::free(mArchList);
461 }
462
463
464 //
465 // Get the "local" architecture from the fat file
466 // Throws ENOEXEC if not found.
467 //
468 MachO *Universal::architecture() const
469 {
470 if (isUniversal())
471 return findImage(bestNativeArch());
472 else
473 return new MachO(*this, mBase);
474 }
475
476 size_t Universal::archOffset() const
477 {
478 if (isUniversal())
479 return mBase + findArch(bestNativeArch())->offset;
480 else
481 return mBase;
482 }
483
484
485 //
486 // Get the specified architecture from the fat file
487 // Throws ENOEXEC if not found.
488 //
489 MachO *Universal::architecture(const Architecture &arch) const
490 {
491 if (isUniversal())
492 return findImage(arch);
493 else if (mThinArch.matches(arch))
494 return new MachO(*this, mBase);
495 else
496 UnixError::throwMe(ENOEXEC);
497 }
498
499 size_t Universal::archOffset(const Architecture &arch) const
500 {
501 if (isUniversal())
502 return mBase + findArch(arch)->offset;
503 else if (mThinArch.matches(arch))
504 return 0;
505 else
506 UnixError::throwMe(ENOEXEC);
507 }
508
509
510 //
511 // Get the architecture at a specified offset from the fat file.
512 // Throws an exception of the offset does not point at a Mach-O image.
513 //
514 MachO *Universal::architecture(size_t offset) const
515 {
516 if (isUniversal())
517 return new MachO(*this, offset);
518 else if (offset == mBase)
519 return new MachO(*this);
520 else
521 UnixError::throwMe(ENOEXEC);
522 }
523
524
525 //
526 // Locate an architecture from the fat file's list.
527 // Throws ENOEXEC if not found.
528 //
529 const fat_arch *Universal::findArch(const Architecture &target) const
530 {
531 assert(isUniversal());
532 const fat_arch *end = mArchList + mArchCount;
533 // exact match
534 for (const fat_arch *arch = mArchList; arch < end; ++arch)
535 if (arch->cputype == target.cpuType()
536 && arch->cpusubtype == target.cpuSubtype())
537 return arch;
538 // match for generic model of main architecture
539 for (const fat_arch *arch = mArchList; arch < end; ++arch)
540 if (arch->cputype == target.cpuType() && arch->cpusubtype == 0)
541 return arch;
542 // match for any subarchitecture of the main architecture (questionable)
543 for (const fat_arch *arch = mArchList; arch < end; ++arch)
544 if (arch->cputype == target.cpuType())
545 return arch;
546 // no match
547 UnixError::throwMe(ENOEXEC); // not found
548 }
549
550 MachO *Universal::findImage(const Architecture &target) const
551 {
552 const fat_arch *arch = findArch(target);
553 return new MachO(*this, mBase + arch->offset, arch->size);
554 }
555
556
557 //
558 // Find the best-matching architecture for this fat file.
559 // We pick the native architecture if it's available.
560 // If it contains exactly one architecture, we take that.
561 // Otherwise, we throw.
562 //
563 Architecture Universal::bestNativeArch() const
564 {
565 if (isUniversal()) {
566 // ask the NXArch API for our native architecture
567 const Architecture native = Architecture::local();
568 if (fat_arch *match = NXFindBestFatArch(native.cpuType(), native.cpuSubtype(), mArchList, mArchCount))
569 return *match;
570 // if the system can't figure it out, pick (arbitrarily) the first one
571 return mArchList[0];
572 } else
573 return mThinArch;
574 }
575
576
577 //
578 // List all architectures from the fat file's list.
579 //
580 void Universal::architectures(Architectures &archs)
581 {
582 if (isUniversal()) {
583 for (unsigned n = 0; n < mArchCount; n++)
584 archs.insert(mArchList[n]);
585 } else {
586 auto_ptr<MachO> macho(architecture());
587 archs.insert(macho->architecture());
588 }
589 }
590
591
592 //
593 // Quickly guess the Mach-O type of a file.
594 // Returns type zero if the file isn't Mach-O or Universal.
595 // Always looks at the start of the file, and does not change the file pointer.
596 //
597 uint32_t Universal::typeOf(FileDesc fd)
598 {
599 mach_header header;
600 int max_tries = 3;
601 if (fd.read(&header, sizeof(header), 0) != sizeof(header))
602 return 0;
603 while (max_tries > 0) {
604 switch (header.magic) {
605 case MH_MAGIC:
606 case MH_MAGIC_64:
607 return header.filetype;
608 break;
609 case MH_CIGAM:
610 case MH_CIGAM_64:
611 return flip(header.filetype);
612 break;
613 case FAT_MAGIC:
614 case FAT_CIGAM:
615 {
616 const fat_arch *arch1 =
617 LowLevelMemoryUtilities::increment<fat_arch>(&header, sizeof(fat_header));
618 if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header))
619 return 0;
620 max_tries--;
621 continue;
622 }
623 default:
624 return 0;
625 }
626 }
627 return 0;
628 }
629
630
631 } // Security