]>
git.saurik.com Git - apple/security.git/blob - Security/libsecurity_utilities/lib/macho++.cpp
cfd448abb2eae8a341191285479279e7b48d14b9
2 * Copyright (c) 2006-2014 Apple 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/alloc.h>
29 #include <security_utilities/memutils.h>
30 #include <security_utilities/endian.h>
31 #include <mach-o/dyld.h>
38 /* Maximum number of archs a fat binary can have */
39 static const int MAX_ARCH_COUNT
= 100;
40 /* Maximum power of 2 that a mach-o can be aligned by */
41 static const int MAX_ALIGN
= 30;
44 // Architecture values
46 Architecture::Architecture(const fat_arch
&arch
)
47 : pair
<cpu_type_t
, cpu_subtype_t
>(arch
.cputype
, arch
.cpusubtype
)
51 Architecture::Architecture(const char *name
)
53 if (const NXArchInfo
*nxa
= NXGetArchInfoFromName(name
)) {
54 this->first
= nxa
->cputype
;
55 this->second
= nxa
->cpusubtype
;
57 this->first
= this->second
= none
;
63 // The local architecture.
65 // We take this from ourselves - the architecture of our main program Mach-O binary.
66 // There's the NXGetLocalArchInfo API, but it insists on saying "i386" on modern
67 // x86_64-centric systems, and lies to ppc (Rosetta) programs claiming they're native ppc.
68 // So let's not use that.
70 Architecture
Architecture::local()
72 return MainMachOImage().architecture();
77 // Translate between names and numbers
79 const char *Architecture::name() const
81 if (const NXArchInfo
*info
= NXGetArchInfoFromCpuType(cpuType(), cpuSubtype()))
87 std::string
Architecture::displayName() const
89 if (const char *s
= this->name())
92 snprintf(buf
, sizeof(buf
), "(%d:%d)", cpuType(), cpuSubtype());
98 // Compare architectures.
99 // This is asymmetrical; the second argument provides for some templating.
101 bool Architecture::matches(const Architecture
&templ
) const
103 if (first
!= templ
.first
)
104 return false; // main architecture mismatch
105 if (templ
.second
== CPU_SUBTYPE_MULTIPLE
)
106 return true; // subtype wildcard
107 // match subtypes, ignoring feature bits
108 return ((second
^ templ
.second
) & ~CPU_SUBTYPE_MASK
) == 0;
113 // MachOBase contains knowledge of the Mach-O object file format,
114 // but abstracts from any particular sourcing. It must be subclassed,
115 // and the subclass must provide the file header and commands area
116 // during its construction. Memory is owned by the subclass.
118 MachOBase::~MachOBase()
121 // provide the Mach-O file header, somehow
122 void MachOBase::initHeader(const mach_header
*header
)
125 switch (mHeader
->magic
) {
143 secdebug("macho", "%p: unrecognized header magic (%x)", this, mHeader
->magic
);
144 UnixError::throwMe(ENOEXEC
);
148 // provide the Mach-O commands section, somehow
149 void MachOBase::initCommands(const load_command
*commands
)
151 mCommands
= commands
;
152 mEndCommands
= LowLevelMemoryUtilities::increment
<load_command
>(commands
, flip(mHeader
->sizeofcmds
));
153 if (mCommands
+ 1 > mEndCommands
) // ensure initial load command core available
154 UnixError::throwMe(ENOEXEC
);
158 size_t MachOBase::headerSize() const
160 return m64
? sizeof(mach_header_64
) : sizeof(mach_header
);
163 size_t MachOBase::commandSize() const
165 return flip(mHeader
->sizeofcmds
);
170 // Create a MachO object from an open file and a starting offset.
171 // We load (only) the header and load commands into memory at that time.
172 // Note that the offset must be relative to the start of the containing file
173 // (not relative to some intermediate container).
175 MachO::MachO(FileDesc fd
, size_t offset
, size_t length
)
176 : FileDesc(fd
), mOffset(offset
), mLength(length
), mSuspicious(false)
179 mLength
= fd
.fileSize();
180 size_t size
= fd
.read(&mHeaderBuffer
, sizeof(mHeaderBuffer
), mOffset
);
181 if (size
!= sizeof(mHeaderBuffer
))
182 UnixError::throwMe(ENOEXEC
);
183 this->initHeader(&mHeaderBuffer
);
184 size_t cmdSize
= this->commandSize();
185 mCommandBuffer
= (load_command
*)malloc(cmdSize
);
187 UnixError::throwMe();
188 if (fd
.read(mCommandBuffer
, cmdSize
, this->headerSize() + mOffset
) != cmdSize
)
189 UnixError::throwMe(ENOEXEC
);
190 this->initCommands(mCommandBuffer
);
191 /* If we do not know the length, we cannot do a verification of the mach-o structure */
193 this->validateStructure();
196 void MachO::validateStructure()
198 bool isValid
= false;
200 /* There should be either an LC_SEGMENT, an LC_SEGMENT_64, or an LC_SYMTAB
201 load_command and that + size must be equal to the end of the arch */
202 for (const struct load_command
*cmd
= loadCommands(); cmd
!= NULL
; cmd
= nextCommand(cmd
)) {
203 uint32_t cmd_type
= flip(cmd
->cmd
);
204 struct segment_command
*seg
= NULL
;
205 struct segment_command_64
*seg64
= NULL
;
206 struct symtab_command
*symtab
= NULL
;
208 if (cmd_type
== LC_SEGMENT
) {
209 seg
= (struct segment_command
*)cmd
;
210 if (strcmp(seg
->segname
, SEG_LINKEDIT
) == 0) {
211 isValid
= flip(seg
->fileoff
) + flip(seg
->filesize
) == this->length();
214 } else if (cmd_type
== LC_SEGMENT_64
) {
215 seg64
= (struct segment_command_64
*)cmd
;
216 if (strcmp(seg64
->segname
, SEG_LINKEDIT
) == 0) {
217 isValid
= flip(seg64
->fileoff
) + flip(seg64
->filesize
) == this->length();
220 /* PPC binaries have a SYMTAB section */
221 } else if (cmd_type
== LC_SYMTAB
) {
222 symtab
= (struct symtab_command
*)cmd
;
223 isValid
= flip(symtab
->stroff
) + flip(symtab
->strsize
) == this->length();
234 ::free(mCommandBuffer
);
239 // Create a MachO object that is (entirely) mapped into memory.
240 // The caller must ensire that the underlying mapping persists
241 // at least as long as our object.
243 MachOImage::MachOImage(const void *address
)
245 this->initHeader((const mach_header
*)address
);
246 this->initCommands(LowLevelMemoryUtilities::increment
<const load_command
>(address
, this->headerSize()));
251 // Locate the Mach-O image of the main program
253 MainMachOImage::MainMachOImage()
254 : MachOImage(mainImageAddress())
258 const void *MainMachOImage::mainImageAddress()
260 return _dyld_get_image_header(0);
265 // Return various header fields
267 Architecture
MachOBase::architecture() const
269 return Architecture(flip(mHeader
->cputype
), flip(mHeader
->cpusubtype
));
272 uint32_t MachOBase::type() const
274 return flip(mHeader
->filetype
);
277 uint32_t MachOBase::flags() const
279 return flip(mHeader
->flags
);
284 // Iterate through load commands
286 const load_command
*MachOBase::nextCommand(const load_command
*command
) const
288 using LowLevelMemoryUtilities::increment
;
289 command
= increment
<const load_command
>(command
, flip(command
->cmdsize
));
290 if (command
>= mEndCommands
) // end of load commands
292 if (increment(command
, sizeof(load_command
)) > mEndCommands
293 || increment(command
, flip(command
->cmdsize
)) > mEndCommands
)
294 UnixError::throwMe(ENOEXEC
);
300 // Find a specific load command, by command number.
301 // If there are multiples, returns the first one found.
303 const load_command
*MachOBase::findCommand(uint32_t cmd
) const
305 for (const load_command
*command
= loadCommands(); command
; command
= nextCommand(command
))
306 if (flip(command
->cmd
) == cmd
)
313 // Locate a segment command, by name
315 const segment_command
*MachOBase::findSegment(const char *segname
) const
317 for (const load_command
*command
= loadCommands(); command
; command
= nextCommand(command
)) {
318 switch (flip(command
->cmd
)) {
322 const segment_command
*seg
= reinterpret_cast<const segment_command
*>(command
);
323 if (!strcmp(seg
->segname
, segname
))
334 const section
*MachOBase::findSection(const char *segname
, const char *sectname
) const
336 using LowLevelMemoryUtilities::increment
;
337 if (const segment_command
*seg
= findSegment(segname
)) {
339 const segment_command_64
*seg64
= reinterpret_cast<const segment_command_64
*>(seg
);
340 // As a sanity check, if the reported number of sections is not consistent
341 // with the reported size, return NULL
342 if (sizeof(*seg64
) + (seg64
->nsects
* sizeof(section_64
)) > seg64
->cmdsize
)
344 const section_64
*sect
= increment
<const section_64
>(seg64
+ 1, 0);
345 for (unsigned n
= flip(seg64
->nsects
); n
> 0; n
--, sect
++) {
346 if (!strcmp(sect
->sectname
, sectname
))
347 return reinterpret_cast<const section
*>(sect
);
350 const section
*sect
= increment
<const section
>(seg
+ 1, 0);
351 for (unsigned n
= flip(seg
->nsects
); n
> 0; n
--, sect
++) {
352 if (!strcmp(sect
->sectname
, sectname
))
362 // Translate a union lc_str into the string it denotes.
363 // Returns NULL (no exceptions) if the entry is corrupt.
365 const char *MachOBase::string(const load_command
*cmd
, const lc_str
&str
) const
367 size_t offset
= flip(str
.offset
);
368 const char *sp
= LowLevelMemoryUtilities::increment
<const char>(cmd
, offset
);
369 if (offset
+ strlen(sp
) + 1 > flip(cmd
->cmdsize
)) // corrupt string reference
376 // Figure out where the Code Signing information starts in the Mach-O binary image.
377 // The code signature is at the end of the file, and identified
378 // by a specially-named section. So its starting offset is also the end
379 // of the signable part.
380 // Note that the offset returned is relative to the start of the Mach-O image.
381 // Returns zero if not found (usually indicating that the binary was not signed).
383 const linkedit_data_command
*MachOBase::findCodeSignature() const
385 if (const load_command
*cmd
= findCommand(LC_CODE_SIGNATURE
))
386 return reinterpret_cast<const linkedit_data_command
*>(cmd
);
387 return NULL
; // not found
390 size_t MachOBase::signingOffset() const
392 if (const linkedit_data_command
*lec
= findCodeSignature())
393 return flip(lec
->dataoff
);
398 size_t MachOBase::signingLength() const
400 if (const linkedit_data_command
*lec
= findCodeSignature())
401 return flip(lec
->datasize
);
406 const linkedit_data_command
*MachOBase::findLibraryDependencies() const
408 if (const load_command
*cmd
= findCommand(LC_DYLIB_CODE_SIGN_DRS
))
409 return reinterpret_cast<const linkedit_data_command
*>(cmd
);
410 return NULL
; // not found
415 // Return the signing-limit length for this Mach-O binary image.
416 // This is the signingOffset if present, or the full length if not.
418 size_t MachO::signingExtent() const
420 if (size_t offset
= signingOffset())
430 void MachO::seek(size_t offset
)
432 FileDesc::seek(mOffset
+ offset
);
435 CFDataRef
MachO::dataAt(size_t offset
, size_t size
)
437 CFMallocData
buffer(size
);
438 if (this->read(buffer
, size
, mOffset
+ offset
) != size
)
439 UnixError::throwMe();
444 // Fat (aka universal) file wrappers.
445 // The offset is relative to the start of the containing file.
447 Universal::Universal(FileDesc fd
, size_t offset
/* = 0 */, size_t length
/* = 0 */)
448 : FileDesc(fd
), mBase(offset
), mLength(length
), mSuspicious(false)
451 fat_header header
; // if this is a fat file
452 mach_header mheader
; // if this is a thin file
454 const size_t size
= max(sizeof(header
), sizeof(mheader
));
455 if (fd
.read(&header
, size
, offset
) != size
)
456 UnixError::throwMe(ENOEXEC
);
457 switch (header
.magic
) {
463 // Under certain circumstances (15001604), mArchCount under-counts the architectures
464 // by one, and special testing is required to validate the extra-curricular entry.
465 // We always read an extra entry; in the situations where this might hit end-of-file,
466 // we are content to fail.
468 mArchCount
= ntohl(header
.nfat_arch
);
470 if (mArchCount
> MAX_ARCH_COUNT
)
471 UnixError::throwMe(ENOEXEC
);
473 size_t archSize
= sizeof(fat_arch
) * (mArchCount
+ 1);
474 mArchList
= (fat_arch
*)malloc(archSize
);
476 UnixError::throwMe();
477 if (fd
.read(mArchList
, archSize
, mBase
+ sizeof(header
)) != archSize
) {
479 UnixError::throwMe(ENOEXEC
);
481 for (fat_arch
*arch
= mArchList
; arch
<= mArchList
+ mArchCount
; arch
++) {
483 n2hi(arch
->cpusubtype
);
488 const fat_arch
*last_arch
= mArchList
+ mArchCount
;
489 if (last_arch
->cputype
== (CPU_ARCH_ABI64
| CPU_TYPE_ARM
)) {
492 secdebug("macho", "%p is a fat file with %d architectures",
495 /* A Mach-O universal file has padding of no more than "page size"
496 * between the header and slices. This padding must be zeroed out or the file
498 std::list
<struct fat_arch
*> sortedList
;
499 for (unsigned i
= 0; i
< mArchCount
; i
++)
500 sortedList
.push_back(mArchList
+ i
);
502 sortedList
.sort(^ bool (const struct fat_arch
*arch1
, const struct fat_arch
*arch2
) { return arch1
->offset
< arch2
->offset
; });
504 const size_t universalHeaderEnd
= mBase
+ sizeof(header
) + (sizeof(fat_arch
) * mArchCount
);
505 size_t prevHeaderEnd
= universalHeaderEnd
;
506 size_t prevArchSize
= 0, prevArchStart
= 0;
508 for (auto iterator
= sortedList
.begin(); iterator
!= sortedList
.end(); ++iterator
) {
509 auto ret
= mSizes
.insert(std::pair
<size_t, size_t>((*iterator
)->offset
, (*iterator
)->size
));
510 if (ret
.second
== false) {
512 MacOSError::throwMe(errSecInternalError
); // Something is wrong if the same size was encountered twice
515 size_t gapSize
= (*iterator
)->offset
- prevHeaderEnd
;
517 /* The size of the padding after the universal cannot be calculated to a fixed size */
518 if (prevHeaderEnd
!= universalHeaderEnd
) {
519 if (((*iterator
)->align
> MAX_ALIGN
) || gapSize
>= (1 << (*iterator
)->align
)) {
525 // validate gap bytes in tasty page-sized chunks
526 CssmAutoPtr
<uint8_t> gapBytes(Allocator::standard().malloc
<uint8_t>(PAGE_SIZE
));
528 while (off
< gapSize
) {
529 size_t want
= min(gapSize
- off
, (size_t)PAGE_SIZE
);
530 size_t got
= fd
.read(gapBytes
, want
, prevHeaderEnd
+ off
);
532 for (size_t x
= 0; x
< got
; x
++) {
533 if (gapBytes
[x
] != 0) {
546 prevHeaderEnd
= (*iterator
)->offset
+ (*iterator
)->size
;
547 prevArchSize
= (*iterator
)->size
;
548 prevArchStart
= (*iterator
)->offset
;
551 /* If there is anything extra at the end of the file, reject this */
552 if (!mSuspicious
&& (prevArchStart
+ prevArchSize
!= fd
.fileSize()))
561 mThinArch
= Architecture(mheader
.cputype
, mheader
.cpusubtype
);
562 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
568 mThinArch
= Architecture(flip(mheader
.cputype
), flip(mheader
.cpusubtype
));
569 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
572 UnixError::throwMe(ENOEXEC
);
576 Universal::~Universal()
581 const size_t Universal::lengthOfSlice(size_t offset
) const
583 auto ret
= mSizes
.find(offset
);
584 if (ret
== mSizes
.end())
585 MacOSError::throwMe(errSecInternalError
);
590 // Get the "local" architecture from the fat file
591 // Throws ENOEXEC if not found.
593 MachO
*Universal::architecture() const
596 return findImage(bestNativeArch());
598 return new MachO(*this, mBase
, mLength
);
601 size_t Universal::archOffset() const
604 return mBase
+ findArch(bestNativeArch())->offset
;
611 // Get the specified architecture from the fat file
612 // Throws ENOEXEC if not found.
614 MachO
*Universal::architecture(const Architecture
&arch
) const
617 return findImage(arch
);
618 else if (mThinArch
.matches(arch
))
619 return new MachO(*this, mBase
);
621 UnixError::throwMe(ENOEXEC
);
624 size_t Universal::archOffset(const Architecture
&arch
) const
627 return mBase
+ findArch(arch
)->offset
;
628 else if (mThinArch
.matches(arch
))
631 UnixError::throwMe(ENOEXEC
);
634 size_t Universal::archLength(const Architecture
&arch
) const
637 return mBase
+ findArch(arch
)->size
;
638 else if (mThinArch
.matches(arch
))
639 return this->fileSize();
641 UnixError::throwMe(ENOEXEC
);
645 // Get the architecture at a specified offset from the fat file.
646 // Throws an exception of the offset does not point at a Mach-O image.
648 MachO
*Universal::architecture(size_t offset
) const
651 return new MachO(*this, offset
);
652 else if (offset
== mBase
)
653 return new MachO(*this);
655 UnixError::throwMe(ENOEXEC
);
660 // Locate an architecture from the fat file's list.
661 // Throws ENOEXEC if not found.
663 const fat_arch
*Universal::findArch(const Architecture
&target
) const
665 assert(isUniversal());
666 const fat_arch
*end
= mArchList
+ mArchCount
;
668 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
669 if (arch
->cputype
== target
.cpuType()
670 && arch
->cpusubtype
== target
.cpuSubtype())
672 // match for generic model of main architecture
673 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
674 if (arch
->cputype
== target
.cpuType() && arch
->cpusubtype
== 0)
676 // match for any subarchitecture of the main architecture (questionable)
677 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
678 if (arch
->cputype
== target
.cpuType())
681 UnixError::throwMe(ENOEXEC
); // not found
684 MachO
*Universal::findImage(const Architecture
&target
) const
686 const fat_arch
*arch
= findArch(target
);
687 return new MachO(*this, mBase
+ arch
->offset
, arch
->size
);
692 // Find the best-matching architecture for this fat file.
693 // We pick the native architecture if it's available.
694 // If it contains exactly one architecture, we take that.
695 // Otherwise, we throw.
697 Architecture
Universal::bestNativeArch() const
700 // ask the NXArch API for our native architecture
701 const Architecture native
= Architecture::local();
702 if (fat_arch
*match
= NXFindBestFatArch(native
.cpuType(), native
.cpuSubtype(), mArchList
, mArchCount
))
704 // if the system can't figure it out, pick (arbitrarily) the first one
711 // List all architectures from the fat file's list.
713 void Universal::architectures(Architectures
&archs
) const
716 for (unsigned n
= 0; n
< mArchCount
; n
++)
717 archs
.insert(mArchList
[n
]);
719 auto_ptr
<MachO
> macho(architecture());
720 archs
.insert(macho
->architecture());
725 // Quickly guess the Mach-O type of a file.
726 // Returns type zero if the file isn't Mach-O or Universal.
727 // Always looks at the start of the file, and does not change the file pointer.
729 uint32_t Universal::typeOf(FileDesc fd
)
733 if (fd
.read(&header
, sizeof(header
), 0) != sizeof(header
))
735 while (max_tries
> 0) {
736 switch (header
.magic
) {
739 return header
.filetype
;
743 return flip(header
.filetype
);
748 const fat_arch
*arch1
=
749 LowLevelMemoryUtilities::increment
<fat_arch
>(&header
, sizeof(fat_header
));
750 if (fd
.read(&header
, sizeof(header
), ntohl(arch1
->offset
)) != sizeof(header
))
765 bool Universal::isSuspicious() const
769 Universal::Architectures archList
;
770 architectures(archList
);
771 for (Universal::Architectures::const_iterator it
= archList
.begin(); it
!= archList
.end(); ++it
) {
772 auto_ptr
<MachO
> macho(architecture(*it
));
773 if (macho
->isSuspicious())