]> git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/macho++.cpp
8c56b0e3b007ab2752927ceb268e7962587c26d5
[apple/security.git] / libsecurity_utilities / lib / macho++.cpp
1 /*
2 * Copyright (c) 2006 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 }
145
146
147 size_t MachOBase::headerSize() const
148 {
149 return m64 ? sizeof(mach_header_64) : sizeof(mach_header);
150 }
151
152 size_t MachOBase::commandSize() const
153 {
154 return flip(mHeader->sizeofcmds);
155 }
156
157
158 //
159 // Create a MachO object from an open file and a starting offset.
160 // We load (only) the header and load commands into memory at that time.
161 // Note that the offset must be relative to the start of the containing file
162 // (not relative to some intermediate container).
163 //
164 MachO::MachO(FileDesc fd, size_t offset, size_t length)
165 : FileDesc(fd), mOffset(offset), mLength(length ? length : (fd.fileSize() - offset))
166 {
167 size_t size = fd.read(&mHeaderBuffer, sizeof(mHeaderBuffer), mOffset);
168 if (size != sizeof(mHeaderBuffer))
169 UnixError::throwMe(ENOEXEC);
170 this->initHeader(&mHeaderBuffer);
171 size_t cmdSize = this->commandSize();
172 mCommandBuffer = (load_command *)malloc(cmdSize);
173 if (!mCommandBuffer)
174 UnixError::throwMe();
175 if (fd.read(mCommandBuffer, cmdSize, this->headerSize() + mOffset) != cmdSize)
176 UnixError::throwMe(ENOEXEC);
177 this->initCommands(mCommandBuffer);
178 }
179
180 MachO::~MachO()
181 {
182 ::free(mCommandBuffer);
183 }
184
185
186 //
187 // Create a MachO object that is (entirely) mapped into memory.
188 // The caller must ensire that the underlying mapping persists
189 // at least as long as our object.
190 //
191 MachOImage::MachOImage(const void *address)
192 {
193 this->initHeader((const mach_header *)address);
194 this->initCommands(LowLevelMemoryUtilities::increment<const load_command>(address, this->headerSize()));
195 }
196
197
198 //
199 // Locate the Mach-O image of the main program
200 //
201 MainMachOImage::MainMachOImage()
202 : MachOImage(mainImageAddress())
203 {
204 }
205
206 const void *MainMachOImage::mainImageAddress()
207 {
208 return _dyld_get_image_header(0);
209 }
210
211
212 //
213 // Return various header fields
214 //
215 Architecture MachOBase::architecture() const
216 {
217 return Architecture(flip(mHeader->cputype), flip(mHeader->cpusubtype));
218 }
219
220 uint32_t MachOBase::type() const
221 {
222 return flip(mHeader->filetype);
223 }
224
225 uint32_t MachOBase::flags() const
226 {
227 return flip(mHeader->flags);
228 }
229
230
231 //
232 // Iterate through load commands
233 //
234 const load_command *MachOBase::nextCommand(const load_command *command) const
235 {
236 using LowLevelMemoryUtilities::increment;
237 command = increment<const load_command>(command, flip(command->cmdsize));
238 return (command < mEndCommands) ? command : NULL;
239 }
240
241
242 //
243 // Find a specific load command, by command number.
244 // If there are multiples, returns the first one found.
245 //
246 const load_command *MachOBase::findCommand(uint32_t cmd) const
247 {
248 for (const load_command *command = loadCommands(); command; command = nextCommand(command))
249 if (flip(command->cmd) == cmd)
250 return command;
251 return NULL;
252 }
253
254
255 //
256 // Locate a segment command, by name
257 //
258 const segment_command *MachOBase::findSegment(const char *segname) const
259 {
260 for (const load_command *command = loadCommands(); command; command = nextCommand(command)) {
261 switch (flip(command->cmd)) {
262 case LC_SEGMENT:
263 case LC_SEGMENT_64:
264 {
265 const segment_command *seg = reinterpret_cast<const segment_command *>(command);
266 if (!strcmp(seg->segname, segname))
267 return seg;
268 break;
269 }
270 default:
271 break;
272 }
273 }
274 return NULL;
275 }
276
277 const section *MachOBase::findSection(const char *segname, const char *sectname) const
278 {
279 using LowLevelMemoryUtilities::increment;
280 if (const segment_command *seg = findSegment(segname)) {
281 if (is64()) {
282 const segment_command_64 *seg64 = reinterpret_cast<const segment_command_64 *>(seg);
283 const section_64 *sect = increment<const section_64>(seg64 + 1, 0);
284 for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) {
285 if (!strcmp(sect->sectname, sectname))
286 return reinterpret_cast<const section *>(sect);
287 }
288 } else {
289 const section *sect = increment<const section>(seg + 1, 0);
290 for (unsigned n = flip(seg->nsects); n > 0; n--, sect++) {
291 if (!strcmp(sect->sectname, sectname))
292 return sect;
293 }
294 }
295 }
296 return NULL;
297 }
298
299
300 //
301 // Translate a union lc_str into the string it denotes.
302 // Returns NULL (no exceptions) if the entry is corrupt.
303 //
304 const char *MachOBase::string(const load_command *cmd, const lc_str &str) const
305 {
306 size_t offset = flip(str.offset);
307 const char *sp = LowLevelMemoryUtilities::increment<const char>(cmd, offset);
308 if (offset + strlen(sp) + 1 > flip(cmd->cmdsize)) // corrupt string reference
309 return NULL;
310 return sp;
311 }
312
313
314 //
315 // Figure out where the Code Signing information starts in the Mach-O binary image.
316 // The code signature is at the end of the file, and identified
317 // by a specially-named section. So its starting offset is also the end
318 // of the signable part.
319 // Note that the offset returned is relative to the start of the Mach-O image.
320 // Returns zero if not found (usually indicating that the binary was not signed).
321 //
322 const linkedit_data_command *MachOBase::findCodeSignature() const
323 {
324 if (const load_command *cmd = findCommand(LC_CODE_SIGNATURE))
325 return reinterpret_cast<const linkedit_data_command *>(cmd);
326 return NULL; // not found
327 }
328
329 size_t MachOBase::signingOffset() const
330 {
331 if (const linkedit_data_command *lec = findCodeSignature())
332 return flip(lec->dataoff);
333 else
334 return 0;
335 }
336
337 size_t MachOBase::signingLength() const
338 {
339 if (const linkedit_data_command *lec = findCodeSignature())
340 return flip(lec->datasize);
341 else
342 return 0;
343 }
344
345 const linkedit_data_command *MachOBase::findLibraryDependencies() const
346 {
347 if (const load_command *cmd = findCommand(LC_DYLIB_CODE_SIGN_DRS))
348 return reinterpret_cast<const linkedit_data_command *>(cmd);
349 return NULL; // not found
350 }
351
352
353 //
354 // Return the signing-limit length for this Mach-O binary image.
355 // This is the signingOffset if present, or the full length if not.
356 //
357 size_t MachO::signingExtent() const
358 {
359 if (size_t offset = signingOffset())
360 return offset;
361 else
362 return length();
363 }
364
365
366 //
367 // I/O operations
368 //
369 void MachO::seek(size_t offset)
370 {
371 FileDesc::seek(mOffset + offset);
372 }
373
374 CFDataRef MachO::dataAt(size_t offset, size_t size)
375 {
376 CFMallocData buffer(size);
377 if (this->read(buffer, size, mOffset + offset) != size)
378 UnixError::throwMe();
379 return buffer;
380 }
381
382
383 //
384 // Fat (aka universal) file wrappers.
385 // The offset is relative to the start of the containing file.
386 //
387 Universal::Universal(FileDesc fd, off_t offset /* = 0 */)
388 : FileDesc(fd), mBase(offset)
389 {
390 union {
391 fat_header header; // if this is a fat file
392 mach_header mheader; // if this is a thin file
393 };
394 const size_t size = max(sizeof(header), sizeof(mheader));
395 if (fd.read(&header, size, offset) != size)
396 UnixError::throwMe(ENOEXEC);
397 switch (header.magic) {
398 case FAT_MAGIC:
399 case FAT_CIGAM:
400 {
401 mArchCount = ntohl(header.nfat_arch);
402 size_t archSize = sizeof(fat_arch) * mArchCount;
403 mArchList = (fat_arch *)malloc(archSize);
404 if (!mArchList)
405 UnixError::throwMe();
406 if (fd.read(mArchList, archSize, mBase + sizeof(header)) != archSize) {
407 ::free(mArchList);
408 UnixError::throwMe(ENOEXEC);
409 }
410 for (fat_arch *arch = mArchList; arch < mArchList + mArchCount; arch++) {
411 n2hi(arch->cputype);
412 n2hi(arch->cpusubtype);
413 n2hi(arch->offset);
414 n2hi(arch->size);
415 n2hi(arch->align);
416 }
417 secdebug("macho", "%p is a fat file with %d architectures",
418 this, mArchCount);
419 break;
420 }
421 case MH_MAGIC:
422 case MH_MAGIC_64:
423 mArchList = NULL;
424 mArchCount = 0;
425 mThinArch = Architecture(mheader.cputype, mheader.cpusubtype);
426 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
427 break;
428 case MH_CIGAM:
429 case MH_CIGAM_64:
430 mArchList = NULL;
431 mArchCount = 0;
432 mThinArch = Architecture(flip(mheader.cputype), flip(mheader.cpusubtype));
433 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
434 break;
435 default:
436 UnixError::throwMe(ENOEXEC);
437 }
438 }
439
440 Universal::~Universal()
441 {
442 ::free(mArchList);
443 }
444
445
446 //
447 // Get the "local" architecture from the fat file
448 // Throws ENOEXEC if not found.
449 //
450 MachO *Universal::architecture() const
451 {
452 if (isUniversal())
453 return findImage(bestNativeArch());
454 else
455 return new MachO(*this, mBase);
456 }
457
458 size_t Universal::archOffset() const
459 {
460 if (isUniversal())
461 return mBase + findArch(bestNativeArch())->offset;
462 else
463 return mBase;
464 }
465
466
467 //
468 // Get the specified architecture from the fat file
469 // Throws ENOEXEC if not found.
470 //
471 MachO *Universal::architecture(const Architecture &arch) const
472 {
473 if (isUniversal())
474 return findImage(arch);
475 else if (mThinArch.matches(arch))
476 return new MachO(*this, mBase);
477 else
478 UnixError::throwMe(ENOEXEC);
479 }
480
481 size_t Universal::archOffset(const Architecture &arch) const
482 {
483 if (isUniversal())
484 return mBase + findArch(arch)->offset;
485 else if (mThinArch.matches(arch))
486 return 0;
487 else
488 UnixError::throwMe(ENOEXEC);
489 }
490
491
492 //
493 // Get the architecture at a specified offset from the fat file.
494 // Throws an exception of the offset does not point at a Mach-O image.
495 //
496 MachO *Universal::architecture(off_t offset) const
497 {
498 if (isUniversal())
499 return new MachO(*this, offset);
500 else if (offset == mBase)
501 return new MachO(*this);
502 else
503 UnixError::throwMe(ENOEXEC);
504 }
505
506
507 //
508 // Locate an architecture from the fat file's list.
509 // Throws ENOEXEC if not found.
510 //
511 const fat_arch *Universal::findArch(const Architecture &target) const
512 {
513 assert(isUniversal());
514 const fat_arch *end = mArchList + mArchCount;
515 // exact match
516 for (const fat_arch *arch = mArchList; arch < end; ++arch)
517 if (arch->cputype == target.cpuType()
518 && arch->cpusubtype == target.cpuSubtype())
519 return arch;
520 // match for generic model of main architecture
521 for (const fat_arch *arch = mArchList; arch < end; ++arch)
522 if (arch->cputype == target.cpuType() && arch->cpusubtype == 0)
523 return arch;
524 // match for any subarchitecture of the main architecture (questionable)
525 for (const fat_arch *arch = mArchList; arch < end; ++arch)
526 if (arch->cputype == target.cpuType())
527 return arch;
528 // no match
529 UnixError::throwMe(ENOEXEC); // not found
530 }
531
532 MachO *Universal::findImage(const Architecture &target) const
533 {
534 const fat_arch *arch = findArch(target);
535 return new MachO(*this, mBase + arch->offset, arch->size);
536 }
537
538
539 //
540 // Find the best-matching architecture for this fat file.
541 // We pick the native architecture if it's available.
542 // If it contains exactly one architecture, we take that.
543 // Otherwise, we throw.
544 //
545 Architecture Universal::bestNativeArch() const
546 {
547 if (isUniversal()) {
548 // ask the NXArch API for our native architecture
549 const Architecture native = Architecture::local();
550 if (fat_arch *match = NXFindBestFatArch(native.cpuType(), native.cpuSubtype(), mArchList, mArchCount))
551 return *match;
552 // if the system can't figure it out, pick (arbitrarily) the first one
553 return mArchList[0];
554 } else
555 return mThinArch;
556 }
557
558
559 //
560 // List all architectures from the fat file's list.
561 //
562 void Universal::architectures(Architectures &archs)
563 {
564 if (isUniversal()) {
565 for (unsigned n = 0; n < mArchCount; n++)
566 archs.insert(mArchList[n]);
567 } else {
568 auto_ptr<MachO> macho(architecture());
569 archs.insert(macho->architecture());
570 }
571 }
572
573
574 //
575 // Quickly guess the Mach-O type of a file.
576 // Returns type zero if the file isn't Mach-O or Universal.
577 // Always looks at the start of the file, and does not change the file pointer.
578 //
579 uint32_t Universal::typeOf(FileDesc fd)
580 {
581 mach_header header;
582 int max_tries = 3;
583 if (fd.read(&header, sizeof(header), 0) != sizeof(header))
584 return 0;
585 while (max_tries > 0) {
586 switch (header.magic) {
587 case MH_MAGIC:
588 case MH_MAGIC_64:
589 return header.filetype;
590 break;
591 case MH_CIGAM:
592 case MH_CIGAM_64:
593 return flip(header.filetype);
594 break;
595 case FAT_MAGIC:
596 case FAT_CIGAM:
597 {
598 const fat_arch *arch1 =
599 LowLevelMemoryUtilities::increment<fat_arch>(&header, sizeof(fat_header));
600 if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header))
601 return 0;
602 max_tries--;
603 continue;
604 }
605 default:
606 return 0;
607 }
608 }
609 }
610
611
612 } // Security