]>
git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/macho++.cpp
9c5572b8480a5c506e0f7f060ab0d61d76e74c22
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>
34 // Architecture values
36 Architecture::Architecture(const fat_arch
&arch
)
37 : pair
<cpu_type_t
, cpu_subtype_t
>(ntohl(arch
.cputype
), ntohl(arch
.cpusubtype
))
43 // The local architecture (on demand; cached)
46 const NXArchInfo
*arch
;
47 LocalArch() { arch
= NXGetLocalArchInfo(); }
49 static ModuleNexus
<LocalArch
> localArch
;
51 Architecture
Architecture::local()
53 const NXArchInfo
&local
= *localArch().arch
;
54 return Architecture(local
.cputype
, local
.cpusubtype
);
59 // Translate between names and numbers
61 const char *Architecture::name() const
63 if (const NXArchInfo
*info
= NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
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.
74 MachO::MachO(FileDesc fd
, size_t offset
, size_t length
)
75 : FileDesc(fd
), mOffset(offset
), mLength(length
? length
: (fd
.fileSize() - offset
))
77 size_t size
= fd
.read(&mHeader
, sizeof(mHeader
), mOffset
);
78 if (size
!= sizeof(mHeader
))
79 UnixError::throwMe(ENOEXEC
);
80 switch (mHeader
.magic
) {
98 UnixError::throwMe(ENOEXEC
);
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
);
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
));
117 // Note that we don't close the file descriptor.
121 secdebug("macho", "%p destroyed", this);
127 // Return various header fields
129 Architecture
MachO::architecture() const
131 return Architecture(flip(mHeader
.cputype
), flip(mHeader
.cpusubtype
));
134 uint32_t MachO::type() const
136 return flip(mHeader
.filetype
);
139 uint32_t MachO::flags() const
141 return flip(mHeader
.flags
);
146 // Iterate through load commands
148 const load_command
*MachO::nextCommand(const load_command
*command
) const
150 using LowLevelMemoryUtilities::increment
;
151 command
= increment
<const load_command
>(command
, flip(command
->cmdsize
));
152 return (command
< mEndCommands
) ? command
: NULL
;
157 // Locate a segment command, by name
159 const segment_command
*MachO::findSegment(const char *segname
) const
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
))
171 const section
*MachO::findSection(const char *segname
, const char *sectname
) const
173 using LowLevelMemoryUtilities::increment
;
174 if (const segment_command
*seg
= findSegment(segname
)) {
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
);
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
))
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).
202 const linkedit_data_command
*MachO::findCodeSignature() const
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
210 size_t MachO::signingOffset() const
212 if (const linkedit_data_command
*lec
= findCodeSignature())
213 return flip(lec
->dataoff
);
218 size_t MachO::signingLength() const
220 if (const linkedit_data_command
*lec
= findCodeSignature())
221 return flip(lec
->datasize
);
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.
231 size_t MachO::signingExtent() const
233 if (size_t offset
= signingOffset())
243 void MachO::seek(size_t offset
)
245 FileDesc::seek(mOffset
+ offset
);
248 CFDataRef
MachO::dataAt(size_t offset
, size_t size
)
250 CFMallocData
buffer(size
);
251 if (this->read(buffer
, size
, mOffset
+ offset
) != size
)
252 UnixError::throwMe();
258 // Fat (aka universal) file wrappers
260 Universal::Universal(FileDesc fd
)
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
) {
271 mArchCount
= ntohl(header
.nfat_arch
);
272 size_t archSize
= sizeof(fat_arch
) * mArchCount
;
273 mArchList
= (fat_arch
*)malloc(archSize
);
275 UnixError::throwMe();
276 if (fd
.read(mArchList
, archSize
, sizeof(header
)) != archSize
) {
278 UnixError::throwMe(ENOEXEC
);
280 secdebug("macho", "%p is a fat file with %d architectures",
288 mThinArch
= Architecture(reinterpret_cast<mach_header
&>(header
).cputype
);
289 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
295 mThinArch
= Architecture(ntohl(reinterpret_cast<mach_header
&>(header
).cputype
));
296 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
299 UnixError::throwMe(ENOEXEC
);
303 Universal::~Universal()
310 // Get the "local" architecture from the fat file
311 // Throws ENOEXEC if not found.
313 MachO
*Universal::architecture() const
316 return findImage(bestNativeArch());
318 return new MachO(*this);
321 size_t Universal::archOffset() const
324 return ntohl(findArch(bestNativeArch())->offset
);
331 // Get the specified architecture from the fat file
332 // Throws ENOEXEC if not found.
334 MachO
*Universal::architecture(const Architecture
&arch
) const
337 return findImage(arch
);
338 else if (arch
== mThinArch
)
339 return new MachO(*this);
341 UnixError::throwMe(ENOEXEC
);
344 size_t Universal::archOffset(const Architecture
&arch
) const
347 return ntohl(findArch(arch
)->offset
);
348 else if (arch
== mThinArch
)
351 UnixError::throwMe(ENOEXEC
);
356 // Locate an architecture from the fat file's list.
357 // Throws ENOEXEC if not found.
359 const fat_arch
*Universal::findArch(const Architecture
&target
) const
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
366 UnixError::throwMe(ENOEXEC
); // not found
369 MachO
*Universal::findImage(const Architecture
&target
) const
371 const fat_arch
*arch
= findArch(target
);
372 return new MachO(*this, ntohl(arch
->offset
), ntohl(arch
->size
));
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.
382 Architecture
Universal::bestNativeArch() const
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
);
391 return Architecture(ntohl(mArchList
->cputype
));
392 UnixError::throwMe(ENOEXEC
);
399 // List all architectures from the fat file's list.
401 void Universal::architectures(Architectures
&archs
)
404 for (unsigned n
= 0; n
< mArchCount
; n
++)
405 archs
.insert(mArchList
[n
]);
407 auto_ptr
<MachO
> macho(architecture());
408 archs
.insert(macho
->architecture());
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.
418 uint32_t Universal::typeOf(FileDesc fd
)
421 if (fd
.read(&header
, sizeof(header
), 0) != sizeof(header
))
424 switch (header
.magic
) {
427 return header
.filetype
;
431 return flip(header
.filetype
);
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
))