]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/macho++.cpp
012e0af04a3844082a812c88237ad85dca4c019d
[apple/libsecurity_codesigning.git] / 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/machine.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
44 //
45 // The local architecture (on demand; cached)
46 //
47 struct LocalArch {
48 const NXArchInfo *arch;
49 LocalArch() { arch = NXGetLocalArchInfo(); }
50 };
51 static ModuleNexus<LocalArch> localArch;
52
53 Architecture Architecture::local()
54 {
55 const NXArchInfo &local = *localArch().arch;
56 return Architecture(local.cputype, local.cpusubtype);
57 }
58
59
60 #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
61
62 //
63 // Translate between names and numbers
64 //
65 static const char *uob(char *s) { if (*s != 'a') for (char *p = s; *p; p++) *p ^= 0x77; return s; }
66
67 const char *Architecture::name() const
68 {
69 if (const NXArchInfo *info = NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
70 return info->name;
71 else if (cpuType() == CPU_TYPE_ARM) { // work-around for non-ARM Leopard systems
72 static char arm5[] = "\026\005\032\001B";
73 static char arm6[] = "\026\005\032\001A";
74 static char arm7[] = "\026\005\032\001@";
75 static char arm[] = "\026\005\032";
76 if (cpuSubtype() == CPU_SUBTYPE_ARM_V5TEJ)
77 return uob(arm5);
78 else if (cpuSubtype() == CPU_SUBTYPE_ARM_V6)
79 return uob(arm6);
80 else if (cpuSubtype() == CPU_SUBTYPE_ARM_V7)
81 return uob(arm7);
82 else
83 return uob(arm);
84 } else
85 return NULL;
86 }
87
88
89 //
90 // Create a MachO object from an open file and a starting offset.
91 // We load (only) the header and load commands into memory at that time.
92 //
93 MachO::MachO(FileDesc fd, size_t offset, size_t length)
94 : FileDesc(fd), mOffset(offset), mLength(length ? length : (fd.fileSize() - offset))
95 {
96 size_t size = fd.read(&mHeader, sizeof(mHeader), mOffset);
97 if (size != sizeof(mHeader))
98 UnixError::throwMe(ENOEXEC);
99 switch (mHeader.magic) {
100 case MH_MAGIC:
101 mFlip = false;
102 m64 = false;
103 break;
104 case MH_CIGAM:
105 mFlip = true;
106 m64 = false;
107 break;
108 case MH_MAGIC_64:
109 mFlip = false;
110 m64 = true;
111 break;
112 case MH_CIGAM_64:
113 mFlip = true;
114 m64 = true;
115 break;
116 default:
117 UnixError::throwMe(ENOEXEC);
118 }
119
120 size_t cmdSize = flip(mHeader.sizeofcmds);
121 size_t cmdStart = m64 ? sizeof(mach_header_64) : sizeof(mach_header);
122 mCommands = (load_command *)malloc(cmdSize);
123 if (!mCommands)
124 UnixError::throwMe();
125 if (fd.read(mCommands, cmdSize, cmdStart + mOffset) != cmdSize)
126 UnixError::throwMe(ENOEXEC);
127 mEndCommands = LowLevelMemoryUtilities::increment<load_command>(mCommands, cmdSize);
128 secdebug("macho", "%p created fd=%d offset=0x%zx size=0x%zx %s%s %d command(s)",
129 this, this->fd(), mOffset, mLength, mFlip ? " flipped" : "", m64 ? " 64-bit" : "",
130 flip(mHeader.ncmds));
131 }
132
133
134 //
135 // Destroy a MachO.
136 // Note that we don't close the file descriptor.
137 //
138 MachO::~MachO()
139 {
140 secdebug("macho", "%p destroyed", this);
141 ::free(mCommands);
142 }
143
144
145 //
146 // Return various header fields
147 //
148 Architecture MachO::architecture() const
149 {
150 return Architecture(flip(mHeader.cputype), flip(mHeader.cpusubtype));
151 }
152
153 uint32_t MachO::type() const
154 {
155 return flip(mHeader.filetype);
156 }
157
158 uint32_t MachO::flags() const
159 {
160 return flip(mHeader.flags);
161 }
162
163
164 //
165 // Iterate through load commands
166 //
167 const load_command *MachO::nextCommand(const load_command *command) const
168 {
169 using LowLevelMemoryUtilities::increment;
170 command = increment<const load_command>(command, flip(command->cmdsize));
171 return (command < mEndCommands) ? command : NULL;
172 }
173
174
175 //
176 // Locate a segment command, by name
177 //
178 const segment_command *MachO::findSegment(const char *segname) const
179 {
180 for (const load_command *command = loadCommands(); command; command = nextCommand(command)) {
181 if (flip(command->cmd) == LC_SEGMENT) {
182 const segment_command *seg = reinterpret_cast<const segment_command *>(command);
183 if (!strcmp(seg->segname, segname))
184 return seg;
185 }
186 }
187 return NULL;
188 }
189
190 const section *MachO::findSection(const char *segname, const char *sectname) const
191 {
192 using LowLevelMemoryUtilities::increment;
193 if (const segment_command *seg = findSegment(segname)) {
194 if (is64()) {
195 const segment_command_64 *seg64 = reinterpret_cast<const segment_command_64 *>(seg);
196 const section_64 *sect = increment<const section_64>(seg64 + 1, 0);
197 for (unsigned n = flip(seg64->nsects); n > 0; n--, sect++) {
198 if (!strcmp(sect->sectname, sectname))
199 return reinterpret_cast<const section *>(sect);
200 }
201 } else {
202 const section *sect = increment<const section>(seg + 1, 0);
203 for (unsigned n = flip(seg->nsects); n > 0; n--, sect++) {
204 if (!strcmp(sect->sectname, sectname))
205 return sect;
206 }
207 }
208 }
209 return NULL;
210 }
211
212
213 //
214 // Figure out where the Code Signing information starts in the Mach-O binary image.
215 // The code signature is at the end of the file, and identified
216 // by a specially-named section. So its starting offset is also the end
217 // of the signable part.
218 // Note that the offset returned is relative to the start of the Mach-O image.
219 // Returns zero if not found (usually indicating that the binary was not signed).
220 //
221 const linkedit_data_command *MachO::findCodeSignature() const
222 {
223 for (const load_command *cmd = loadCommands(); cmd; cmd = nextCommand(cmd))
224 if (flip(cmd->cmd) == LC_CODE_SIGNATURE)
225 return reinterpret_cast<const linkedit_data_command *>(cmd);
226 return NULL; // not found
227 }
228
229 size_t MachO::signingOffset() const
230 {
231 if (const linkedit_data_command *lec = findCodeSignature())
232 return flip(lec->dataoff);
233 else
234 return 0;
235 }
236
237 size_t MachO::signingLength() const
238 {
239 if (const linkedit_data_command *lec = findCodeSignature())
240 return flip(lec->datasize);
241 else
242 return 0;
243 }
244
245
246 //
247 // Return the signing-limit length for this Mach-O binary image.
248 // This is the signingOffset if present, or the full length if not.
249 //
250 size_t MachO::signingExtent() const
251 {
252 if (size_t offset = signingOffset())
253 return offset;
254 else
255 return length();
256 }
257
258
259 //
260 // I/O operations
261 //
262 void MachO::seek(size_t offset)
263 {
264 FileDesc::seek(mOffset + offset);
265 }
266
267 CFDataRef MachO::dataAt(size_t offset, size_t size)
268 {
269 CFMallocData buffer(size);
270 if (this->read(buffer, size, mOffset + offset) != size)
271 UnixError::throwMe();
272 return buffer;
273 }
274
275
276 //
277 // Fat (aka universal) file wrappers
278 //
279 Universal::Universal(FileDesc fd)
280 : FileDesc(fd)
281 {
282 union {
283 fat_header header; // if this is a fat file
284 mach_header mheader; // if this is a thin file
285 };
286 const size_t size = max(sizeof(header), sizeof(mheader));
287 if (fd.read(&header, size, 0) != size)
288 UnixError::throwMe(ENOEXEC);
289 switch (header.magic) {
290 case FAT_MAGIC:
291 case FAT_CIGAM:
292 {
293 mArchCount = ntohl(header.nfat_arch);
294 size_t archSize = sizeof(fat_arch) * mArchCount;
295 mArchList = (fat_arch *)malloc(archSize);
296 if (!mArchList)
297 UnixError::throwMe();
298 if (fd.read(mArchList, archSize, sizeof(header)) != archSize) {
299 ::free(mArchList);
300 UnixError::throwMe(ENOEXEC);
301 }
302 for (fat_arch *arch = mArchList; arch < mArchList + mArchCount; arch++) {
303 n2hi(arch->cputype);
304 n2hi(arch->cpusubtype);
305 n2hi(arch->offset);
306 n2hi(arch->size);
307 n2hi(arch->align);
308 }
309 secdebug("macho", "%p is a fat file with %d architectures",
310 this, mArchCount);
311 break;
312 }
313 case MH_MAGIC:
314 case MH_MAGIC_64:
315 mArchList = NULL;
316 mArchCount = 0;
317 mThinArch = Architecture(mheader.cputype, mheader.cpusubtype);
318 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
319 break;
320 case MH_CIGAM:
321 case MH_CIGAM_64:
322 mArchList = NULL;
323 mArchCount = 0;
324 mThinArch = Architecture(flip(mheader.cputype), flip(mheader.cpusubtype));
325 secdebug("macho", "%p is a thin file (%s)", this, mThinArch.name());
326 break;
327 default:
328 UnixError::throwMe(ENOEXEC);
329 }
330 }
331
332 Universal::~Universal()
333 {
334 ::free(mArchList);
335 }
336
337
338 //
339 // Get the "local" architecture from the fat file
340 // Throws ENOEXEC if not found.
341 //
342 MachO *Universal::architecture() const
343 {
344 if (isUniversal())
345 return findImage(bestNativeArch());
346 else
347 return new MachO(*this);
348 }
349
350 size_t Universal::archOffset() const
351 {
352 if (isUniversal())
353 return findArch(bestNativeArch())->offset;
354 else
355 return 0;
356 }
357
358
359 //
360 // Get the specified architecture from the fat file
361 // Throws ENOEXEC if not found.
362 //
363 MachO *Universal::architecture(const Architecture &arch) const
364 {
365 if (isUniversal())
366 return findImage(arch);
367 else if (mThinArch.matches(arch))
368 return new MachO(*this);
369 else
370 UnixError::throwMe(ENOEXEC);
371 }
372
373 size_t Universal::archOffset(const Architecture &arch) const
374 {
375 if (isUniversal())
376 return findArch(arch)->offset;
377 else if (mThinArch.matches(arch))
378 return 0;
379 else
380 UnixError::throwMe(ENOEXEC);
381 }
382
383
384 //
385 // Locate an architecture from the fat file's list.
386 // Throws ENOEXEC if not found.
387 //
388 const fat_arch *Universal::findArch(const Architecture &target) const
389 {
390 assert(isUniversal());
391 const fat_arch *end = mArchList + mArchCount;
392 // exact match
393 for (const fat_arch *arch = mArchList; arch < end; ++arch)
394 if (arch->cputype == target.cpuType()
395 && arch->cpusubtype == target.cpuSubtype())
396 return arch;
397 // match for generic model of main architecture
398 for (const fat_arch *arch = mArchList; arch < end; ++arch)
399 if (arch->cputype == target.cpuType() && arch->cpusubtype == 0)
400 return arch;
401 // match for any subarchitecture of the main architeture (questionable)
402 for (const fat_arch *arch = mArchList; arch < end; ++arch)
403 if (arch->cputype == target.cpuType())
404 return arch;
405 // no match
406 UnixError::throwMe(ENOEXEC); // not found
407 }
408
409 MachO *Universal::findImage(const Architecture &target) const
410 {
411 const fat_arch *arch = findArch(target);
412 return new MachO(*this, arch->offset, arch->size);
413 }
414
415
416 //
417 // Find the best-matching architecture for this fat file.
418 // We pick the native architecture if it's available.
419 // If it contains exactly one architecture, we take that.
420 // Otherwise, we throw.
421 //
422 Architecture Universal::bestNativeArch() const
423 {
424 if (isUniversal()) {
425 // ask the NXArch API for our native architecture
426 const Architecture native = Architecture::local();
427 if (fat_arch *match = NXFindBestFatArch(native.cpuType(), native.cpuSubtype(), mArchList, mArchCount))
428 return *match;
429 // if the system can't figure it out, pick (arbitrarily) the first one
430 return mArchList[0];
431 } else
432 return mThinArch;
433 }
434
435
436 //
437 // List all architectures from the fat file's list.
438 //
439 void Universal::architectures(Architectures &archs)
440 {
441 if (isUniversal()) {
442 for (unsigned n = 0; n < mArchCount; n++)
443 archs.insert(mArchList[n]);
444 } else {
445 auto_ptr<MachO> macho(architecture());
446 archs.insert(macho->architecture());
447 }
448 }
449
450
451 //
452 // Quickly guess the Mach-O type of a file.
453 // Returns type zero if the file isn't Mach-O or Universal.
454 // Does not reposition the file.
455 //
456 uint32_t Universal::typeOf(FileDesc fd)
457 {
458 mach_header header;
459 if (fd.read(&header, sizeof(header), 0) != sizeof(header))
460 return false;
461 for (;;) {
462 switch (header.magic) {
463 case MH_MAGIC:
464 case MH_MAGIC_64:
465 return header.filetype;
466 break;
467 case MH_CIGAM:
468 case MH_CIGAM_64:
469 return flip(header.filetype);
470 break;
471 case FAT_MAGIC:
472 case FAT_CIGAM:
473 {
474 const fat_arch *arch1 =
475 LowLevelMemoryUtilities::increment<fat_arch>(&header, sizeof(fat_header));
476 if (fd.read(&header, sizeof(header), ntohl(arch1->offset)) != sizeof(header))
477 return 0;
478 continue;
479 }
480 default:
481 return 0;
482 }
483 }
484 }
485
486
487 } // Security