]>
git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/macho++.cpp
71e226b61676b974b58172c587e26d2e18a4443d
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-o/dyld.h>
36 // Architecture values
38 Architecture::Architecture(const fat_arch
&arch
)
39 : pair
<cpu_type_t
, cpu_subtype_t
>(arch
.cputype
, arch
.cpusubtype
)
43 Architecture::Architecture(const char *name
)
45 if (const NXArchInfo
*nxa
= NXGetArchInfoFromName(name
)) {
46 this->first
= nxa
->cputype
;
47 this->second
= nxa
->cpusubtype
;
49 this->first
= this->second
= none
;
55 // The local architecture.
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.
62 Architecture
Architecture::local()
64 return MainMachOImage().architecture();
69 // Translate between names and numbers
71 const char *Architecture::name() const
73 if (const NXArchInfo
*info
= NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
79 std::string
Architecture::displayName() const
81 if (const char *s
= this->name())
84 snprintf(buf
, sizeof(buf
), "(%d:%d)", cpuType(), cpuSubtype());
90 // Compare architectures.
91 // This is asymmetrical; the second argument provides for some templating.
93 bool Architecture::matches(const Architecture
&templ
) const
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;
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.
110 MachOBase::~MachOBase()
113 // provide the Mach-O file header, somehow
114 void MachOBase::initHeader(const mach_header
*header
)
117 switch (mHeader
->magic
) {
135 UnixError::throwMe(ENOEXEC
);
139 // provide the Mach-O commands section, somehow
140 void MachOBase::initCommands(const load_command
*commands
)
142 mCommands
= commands
;
143 mEndCommands
= LowLevelMemoryUtilities::increment
<load_command
>(commands
, flip(mHeader
->sizeofcmds
));
144 if (mCommands
+ 1 > mEndCommands
) // ensure initial load command core available
145 UnixError::throwMe(ENOEXEC
);
149 size_t MachOBase::headerSize() const
151 return m64
? sizeof(mach_header_64
) : sizeof(mach_header
);
154 size_t MachOBase::commandSize() const
156 return flip(mHeader
->sizeofcmds
);
161 // Create a MachO object from an open file and a starting offset.
162 // We load (only) the header and load commands into memory at that time.
163 // Note that the offset must be relative to the start of the containing file
164 // (not relative to some intermediate container).
166 MachO::MachO(FileDesc fd
, size_t offset
, size_t length
)
167 : FileDesc(fd
), mOffset(offset
), mLength(length
? length
: (fd
.fileSize() - offset
))
169 size_t size
= fd
.read(&mHeaderBuffer
, sizeof(mHeaderBuffer
), mOffset
);
170 if (size
!= sizeof(mHeaderBuffer
))
171 UnixError::throwMe(ENOEXEC
);
172 this->initHeader(&mHeaderBuffer
);
173 size_t cmdSize
= this->commandSize();
174 mCommandBuffer
= (load_command
*)malloc(cmdSize
);
176 UnixError::throwMe();
177 if (fd
.read(mCommandBuffer
, cmdSize
, this->headerSize() + mOffset
) != cmdSize
)
178 UnixError::throwMe(ENOEXEC
);
179 this->initCommands(mCommandBuffer
);
184 ::free(mCommandBuffer
);
189 // Create a MachO object that is (entirely) mapped into memory.
190 // The caller must ensire that the underlying mapping persists
191 // at least as long as our object.
193 MachOImage::MachOImage(const void *address
)
195 this->initHeader((const mach_header
*)address
);
196 this->initCommands(LowLevelMemoryUtilities::increment
<const load_command
>(address
, this->headerSize()));
201 // Locate the Mach-O image of the main program
203 MainMachOImage::MainMachOImage()
204 : MachOImage(mainImageAddress())
208 const void *MainMachOImage::mainImageAddress()
210 return _dyld_get_image_header(0);
215 // Return various header fields
217 Architecture
MachOBase::architecture() const
219 return Architecture(flip(mHeader
->cputype
), flip(mHeader
->cpusubtype
));
222 uint32_t MachOBase::type() const
224 return flip(mHeader
->filetype
);
227 uint32_t MachOBase::flags() const
229 return flip(mHeader
->flags
);
234 // Iterate through load commands
236 const load_command
*MachOBase::nextCommand(const load_command
*command
) const
238 using LowLevelMemoryUtilities::increment
;
239 command
= increment
<const load_command
>(command
, flip(command
->cmdsize
));
240 if (command
>= mEndCommands
) // end of load commands
242 if (increment(command
, sizeof(load_command
)) > mEndCommands
243 || increment(command
, flip(command
->cmdsize
)) > mEndCommands
)
244 UnixError::throwMe(ENOEXEC
);
250 // Find a specific load command, by command number.
251 // If there are multiples, returns the first one found.
253 const load_command
*MachOBase::findCommand(uint32_t cmd
) const
255 for (const load_command
*command
= loadCommands(); command
; command
= nextCommand(command
))
256 if (flip(command
->cmd
) == cmd
)
263 // Locate a segment command, by name
265 const segment_command
*MachOBase::findSegment(const char *segname
) const
267 for (const load_command
*command
= loadCommands(); command
; command
= nextCommand(command
)) {
268 switch (flip(command
->cmd
)) {
272 const segment_command
*seg
= reinterpret_cast<const segment_command
*>(command
);
273 if (!strcmp(seg
->segname
, segname
))
284 const section
*MachOBase::findSection(const char *segname
, const char *sectname
) const
286 using LowLevelMemoryUtilities::increment
;
287 if (const segment_command
*seg
= findSegment(segname
)) {
289 const segment_command_64
*seg64
= reinterpret_cast<const segment_command_64
*>(seg
);
290 const section_64
*sect
= increment
<const section_64
>(seg64
+ 1, 0);
291 for (unsigned n
= flip(seg64
->nsects
); n
> 0; n
--, sect
++) {
292 if (!strcmp(sect
->sectname
, sectname
))
293 return reinterpret_cast<const section
*>(sect
);
296 const section
*sect
= increment
<const section
>(seg
+ 1, 0);
297 for (unsigned n
= flip(seg
->nsects
); n
> 0; n
--, sect
++) {
298 if (!strcmp(sect
->sectname
, sectname
))
308 // Translate a union lc_str into the string it denotes.
309 // Returns NULL (no exceptions) if the entry is corrupt.
311 const char *MachOBase::string(const load_command
*cmd
, const lc_str
&str
) const
313 size_t offset
= flip(str
.offset
);
314 const char *sp
= LowLevelMemoryUtilities::increment
<const char>(cmd
, offset
);
315 if (offset
+ strlen(sp
) + 1 > flip(cmd
->cmdsize
)) // corrupt string reference
322 // Figure out where the Code Signing information starts in the Mach-O binary image.
323 // The code signature is at the end of the file, and identified
324 // by a specially-named section. So its starting offset is also the end
325 // of the signable part.
326 // Note that the offset returned is relative to the start of the Mach-O image.
327 // Returns zero if not found (usually indicating that the binary was not signed).
329 const linkedit_data_command
*MachOBase::findCodeSignature() const
331 if (const load_command
*cmd
= findCommand(LC_CODE_SIGNATURE
))
332 return reinterpret_cast<const linkedit_data_command
*>(cmd
);
333 return NULL
; // not found
336 size_t MachOBase::signingOffset() const
338 if (const linkedit_data_command
*lec
= findCodeSignature())
339 return flip(lec
->dataoff
);
344 size_t MachOBase::signingLength() const
346 if (const linkedit_data_command
*lec
= findCodeSignature())
347 return flip(lec
->datasize
);
352 const linkedit_data_command
*MachOBase::findLibraryDependencies() const
354 if (const load_command
*cmd
= findCommand(LC_DYLIB_CODE_SIGN_DRS
))
355 return reinterpret_cast<const linkedit_data_command
*>(cmd
);
356 return NULL
; // not found
361 // Return the signing-limit length for this Mach-O binary image.
362 // This is the signingOffset if present, or the full length if not.
364 size_t MachO::signingExtent() const
366 if (size_t offset
= signingOffset())
376 void MachO::seek(size_t offset
)
378 FileDesc::seek(mOffset
+ offset
);
381 CFDataRef
MachO::dataAt(size_t offset
, size_t size
)
383 CFMallocData
buffer(size
);
384 if (this->read(buffer
, size
, mOffset
+ offset
) != size
)
385 UnixError::throwMe();
391 // Fat (aka universal) file wrappers.
392 // The offset is relative to the start of the containing file.
394 Universal::Universal(FileDesc fd
, size_t offset
/* = 0 */)
395 : FileDesc(fd
), mBase(offset
)
398 fat_header header
; // if this is a fat file
399 mach_header mheader
; // if this is a thin file
401 const size_t size
= max(sizeof(header
), sizeof(mheader
));
402 if (fd
.read(&header
, size
, offset
) != size
)
403 UnixError::throwMe(ENOEXEC
);
404 switch (header
.magic
) {
408 mArchCount
= ntohl(header
.nfat_arch
);
409 size_t archSize
= sizeof(fat_arch
) * mArchCount
;
410 mArchList
= (fat_arch
*)malloc(archSize
);
412 UnixError::throwMe();
413 if (fd
.read(mArchList
, archSize
, mBase
+ sizeof(header
)) != archSize
) {
415 UnixError::throwMe(ENOEXEC
);
417 for (fat_arch
*arch
= mArchList
; arch
< mArchList
+ mArchCount
; arch
++) {
419 n2hi(arch
->cpusubtype
);
424 secdebug("macho", "%p is a fat file with %d architectures",
432 mThinArch
= Architecture(mheader
.cputype
, mheader
.cpusubtype
);
433 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
439 mThinArch
= Architecture(flip(mheader
.cputype
), flip(mheader
.cpusubtype
));
440 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
443 UnixError::throwMe(ENOEXEC
);
447 Universal::~Universal()
454 // Get the "local" architecture from the fat file
455 // Throws ENOEXEC if not found.
457 MachO
*Universal::architecture() const
460 return findImage(bestNativeArch());
462 return new MachO(*this, mBase
);
465 size_t Universal::archOffset() const
468 return mBase
+ findArch(bestNativeArch())->offset
;
475 // Get the specified architecture from the fat file
476 // Throws ENOEXEC if not found.
478 MachO
*Universal::architecture(const Architecture
&arch
) const
481 return findImage(arch
);
482 else if (mThinArch
.matches(arch
))
483 return new MachO(*this, mBase
);
485 UnixError::throwMe(ENOEXEC
);
488 size_t Universal::archOffset(const Architecture
&arch
) const
491 return mBase
+ findArch(arch
)->offset
;
492 else if (mThinArch
.matches(arch
))
495 UnixError::throwMe(ENOEXEC
);
500 // Get the architecture at a specified offset from the fat file.
501 // Throws an exception of the offset does not point at a Mach-O image.
503 MachO
*Universal::architecture(size_t offset
) const
506 return new MachO(*this, offset
);
507 else if (offset
== mBase
)
508 return new MachO(*this);
510 UnixError::throwMe(ENOEXEC
);
515 // Locate an architecture from the fat file's list.
516 // Throws ENOEXEC if not found.
518 const fat_arch
*Universal::findArch(const Architecture
&target
) const
520 assert(isUniversal());
521 const fat_arch
*end
= mArchList
+ mArchCount
;
523 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
524 if (arch
->cputype
== target
.cpuType()
525 && arch
->cpusubtype
== target
.cpuSubtype())
527 // match for generic model of main architecture
528 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
529 if (arch
->cputype
== target
.cpuType() && arch
->cpusubtype
== 0)
531 // match for any subarchitecture of the main architecture (questionable)
532 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
533 if (arch
->cputype
== target
.cpuType())
536 UnixError::throwMe(ENOEXEC
); // not found
539 MachO
*Universal::findImage(const Architecture
&target
) const
541 const fat_arch
*arch
= findArch(target
);
542 return new MachO(*this, mBase
+ arch
->offset
, arch
->size
);
547 // Find the best-matching architecture for this fat file.
548 // We pick the native architecture if it's available.
549 // If it contains exactly one architecture, we take that.
550 // Otherwise, we throw.
552 Architecture
Universal::bestNativeArch() const
555 // ask the NXArch API for our native architecture
556 const Architecture native
= Architecture::local();
557 if (fat_arch
*match
= NXFindBestFatArch(native
.cpuType(), native
.cpuSubtype(), mArchList
, mArchCount
))
559 // if the system can't figure it out, pick (arbitrarily) the first one
567 // List all architectures from the fat file's list.
569 void Universal::architectures(Architectures
&archs
)
572 for (unsigned n
= 0; n
< mArchCount
; n
++)
573 archs
.insert(mArchList
[n
]);
575 auto_ptr
<MachO
> macho(architecture());
576 archs
.insert(macho
->architecture());
582 // Quickly guess the Mach-O type of a file.
583 // Returns type zero if the file isn't Mach-O or Universal.
584 // Always looks at the start of the file, and does not change the file pointer.
586 uint32_t Universal::typeOf(FileDesc fd
)
590 if (fd
.read(&header
, sizeof(header
), 0) != sizeof(header
))
592 while (max_tries
> 0) {
593 switch (header
.magic
) {
596 return header
.filetype
;
600 return flip(header
.filetype
);
605 const fat_arch
*arch1
=
606 LowLevelMemoryUtilities::increment
<fat_arch
>(&header
, sizeof(fat_header
));
607 if (fd
.read(&header
, sizeof(header
), ntohl(arch1
->offset
)) != sizeof(header
))