]>
git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/macho++.cpp
012e0af04a3844082a812c88237ad85dca4c019d
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // macho++ - Mach-O object file helpers
28 #include <security_utilities/memutils.h>
29 #include <security_utilities/endian.h>
30 #include <mach/machine.h>
36 // Architecture values
38 Architecture::Architecture(const fat_arch
&arch
)
39 : pair
<cpu_type_t
, cpu_subtype_t
>(arch
.cputype
, arch
.cpusubtype
)
45 // The local architecture (on demand; cached)
48 const NXArchInfo
*arch
;
49 LocalArch() { arch
= NXGetLocalArchInfo(); }
51 static ModuleNexus
<LocalArch
> localArch
;
53 Architecture
Architecture::local()
55 const NXArchInfo
&local
= *localArch().arch
;
56 return Architecture(local
.cputype
, local
.cpusubtype
);
60 #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
63 // Translate between names and numbers
65 static const char *uob(char *s
) { if (*s
!= 'a') for (char *p
= s
; *p
; p
++) *p
^= 0x77; return s
; }
67 const char *Architecture::name() const
69 if (const NXArchInfo
*info
= NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
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
)
78 else if (cpuSubtype() == CPU_SUBTYPE_ARM_V6
)
80 else if (cpuSubtype() == CPU_SUBTYPE_ARM_V7
)
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.
93 MachO::MachO(FileDesc fd
, size_t offset
, size_t length
)
94 : FileDesc(fd
), mOffset(offset
), mLength(length
? length
: (fd
.fileSize() - offset
))
96 size_t size
= fd
.read(&mHeader
, sizeof(mHeader
), mOffset
);
97 if (size
!= sizeof(mHeader
))
98 UnixError::throwMe(ENOEXEC
);
99 switch (mHeader
.magic
) {
117 UnixError::throwMe(ENOEXEC
);
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
);
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
));
136 // Note that we don't close the file descriptor.
140 secdebug("macho", "%p destroyed", this);
146 // Return various header fields
148 Architecture
MachO::architecture() const
150 return Architecture(flip(mHeader
.cputype
), flip(mHeader
.cpusubtype
));
153 uint32_t MachO::type() const
155 return flip(mHeader
.filetype
);
158 uint32_t MachO::flags() const
160 return flip(mHeader
.flags
);
165 // Iterate through load commands
167 const load_command
*MachO::nextCommand(const load_command
*command
) const
169 using LowLevelMemoryUtilities::increment
;
170 command
= increment
<const load_command
>(command
, flip(command
->cmdsize
));
171 return (command
< mEndCommands
) ? command
: NULL
;
176 // Locate a segment command, by name
178 const segment_command
*MachO::findSegment(const char *segname
) const
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
))
190 const section
*MachO::findSection(const char *segname
, const char *sectname
) const
192 using LowLevelMemoryUtilities::increment
;
193 if (const segment_command
*seg
= findSegment(segname
)) {
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
);
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
))
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).
221 const linkedit_data_command
*MachO::findCodeSignature() const
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
229 size_t MachO::signingOffset() const
231 if (const linkedit_data_command
*lec
= findCodeSignature())
232 return flip(lec
->dataoff
);
237 size_t MachO::signingLength() const
239 if (const linkedit_data_command
*lec
= findCodeSignature())
240 return flip(lec
->datasize
);
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.
250 size_t MachO::signingExtent() const
252 if (size_t offset
= signingOffset())
262 void MachO::seek(size_t offset
)
264 FileDesc::seek(mOffset
+ offset
);
267 CFDataRef
MachO::dataAt(size_t offset
, size_t size
)
269 CFMallocData
buffer(size
);
270 if (this->read(buffer
, size
, mOffset
+ offset
) != size
)
271 UnixError::throwMe();
277 // Fat (aka universal) file wrappers
279 Universal::Universal(FileDesc fd
)
283 fat_header header
; // if this is a fat file
284 mach_header mheader
; // if this is a thin file
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
) {
293 mArchCount
= ntohl(header
.nfat_arch
);
294 size_t archSize
= sizeof(fat_arch
) * mArchCount
;
295 mArchList
= (fat_arch
*)malloc(archSize
);
297 UnixError::throwMe();
298 if (fd
.read(mArchList
, archSize
, sizeof(header
)) != archSize
) {
300 UnixError::throwMe(ENOEXEC
);
302 for (fat_arch
*arch
= mArchList
; arch
< mArchList
+ mArchCount
; arch
++) {
304 n2hi(arch
->cpusubtype
);
309 secdebug("macho", "%p is a fat file with %d architectures",
317 mThinArch
= Architecture(mheader
.cputype
, mheader
.cpusubtype
);
318 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
324 mThinArch
= Architecture(flip(mheader
.cputype
), flip(mheader
.cpusubtype
));
325 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
328 UnixError::throwMe(ENOEXEC
);
332 Universal::~Universal()
339 // Get the "local" architecture from the fat file
340 // Throws ENOEXEC if not found.
342 MachO
*Universal::architecture() const
345 return findImage(bestNativeArch());
347 return new MachO(*this);
350 size_t Universal::archOffset() const
353 return findArch(bestNativeArch())->offset
;
360 // Get the specified architecture from the fat file
361 // Throws ENOEXEC if not found.
363 MachO
*Universal::architecture(const Architecture
&arch
) const
366 return findImage(arch
);
367 else if (mThinArch
.matches(arch
))
368 return new MachO(*this);
370 UnixError::throwMe(ENOEXEC
);
373 size_t Universal::archOffset(const Architecture
&arch
) const
376 return findArch(arch
)->offset
;
377 else if (mThinArch
.matches(arch
))
380 UnixError::throwMe(ENOEXEC
);
385 // Locate an architecture from the fat file's list.
386 // Throws ENOEXEC if not found.
388 const fat_arch
*Universal::findArch(const Architecture
&target
) const
390 assert(isUniversal());
391 const fat_arch
*end
= mArchList
+ mArchCount
;
393 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
394 if (arch
->cputype
== target
.cpuType()
395 && arch
->cpusubtype
== target
.cpuSubtype())
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)
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())
406 UnixError::throwMe(ENOEXEC
); // not found
409 MachO
*Universal::findImage(const Architecture
&target
) const
411 const fat_arch
*arch
= findArch(target
);
412 return new MachO(*this, arch
->offset
, arch
->size
);
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.
422 Architecture
Universal::bestNativeArch() const
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
))
429 // if the system can't figure it out, pick (arbitrarily) the first one
437 // List all architectures from the fat file's list.
439 void Universal::architectures(Architectures
&archs
)
442 for (unsigned n
= 0; n
< mArchCount
; n
++)
443 archs
.insert(mArchList
[n
]);
445 auto_ptr
<MachO
> macho(architecture());
446 archs
.insert(macho
->architecture());
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.
456 uint32_t Universal::typeOf(FileDesc fd
)
459 if (fd
.read(&header
, sizeof(header
), 0) != sizeof(header
))
462 switch (header
.magic
) {
465 return header
.filetype
;
469 return flip(header
.filetype
);
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
))