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