]>
git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/macho++.cpp
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
));
147 size_t MachOBase::headerSize() const
149 return m64
? sizeof(mach_header_64
) : sizeof(mach_header
);
152 size_t MachOBase::commandSize() const
154 return flip(mHeader
->sizeofcmds
);
159 // Create a MachO object from an open file and a starting offset.
160 // We load (only) the header and load commands into memory at that time.
161 // Note that the offset must be relative to the start of the containing file
162 // (not relative to some intermediate container).
164 MachO::MachO(FileDesc fd
, size_t offset
, size_t length
)
165 : FileDesc(fd
), mOffset(offset
), mLength(length
? length
: (fd
.fileSize() - offset
))
167 size_t size
= fd
.read(&mHeaderBuffer
, sizeof(mHeaderBuffer
), mOffset
);
168 if (size
!= sizeof(mHeaderBuffer
))
169 UnixError::throwMe(ENOEXEC
);
170 this->initHeader(&mHeaderBuffer
);
171 size_t cmdSize
= this->commandSize();
172 mCommandBuffer
= (load_command
*)malloc(cmdSize
);
174 UnixError::throwMe();
175 if (fd
.read(mCommandBuffer
, cmdSize
, this->headerSize() + mOffset
) != cmdSize
)
176 UnixError::throwMe(ENOEXEC
);
177 this->initCommands(mCommandBuffer
);
182 ::free(mCommandBuffer
);
187 // Create a MachO object that is (entirely) mapped into memory.
188 // The caller must ensire that the underlying mapping persists
189 // at least as long as our object.
191 MachOImage::MachOImage(const void *address
)
193 this->initHeader((const mach_header
*)address
);
194 this->initCommands(LowLevelMemoryUtilities::increment
<const load_command
>(address
, this->headerSize()));
199 // Locate the Mach-O image of the main program
201 MainMachOImage::MainMachOImage()
202 : MachOImage(mainImageAddress())
206 const void *MainMachOImage::mainImageAddress()
208 return _dyld_get_image_header(0);
213 // Return various header fields
215 Architecture
MachOBase::architecture() const
217 return Architecture(flip(mHeader
->cputype
), flip(mHeader
->cpusubtype
));
220 uint32_t MachOBase::type() const
222 return flip(mHeader
->filetype
);
225 uint32_t MachOBase::flags() const
227 return flip(mHeader
->flags
);
232 // Iterate through load commands
234 const load_command
*MachOBase::nextCommand(const load_command
*command
) const
236 using LowLevelMemoryUtilities::increment
;
237 command
= increment
<const load_command
>(command
, flip(command
->cmdsize
));
238 return (command
< mEndCommands
) ? command
: NULL
;
243 // Find a specific load command, by command number.
244 // If there are multiples, returns the first one found.
246 const load_command
*MachOBase::findCommand(uint32_t cmd
) const
248 for (const load_command
*command
= loadCommands(); command
; command
= nextCommand(command
))
249 if (flip(command
->cmd
) == cmd
)
256 // Locate a segment command, by name
258 const segment_command
*MachOBase::findSegment(const char *segname
) const
260 for (const load_command
*command
= loadCommands(); command
; command
= nextCommand(command
)) {
261 switch (flip(command
->cmd
)) {
265 const segment_command
*seg
= reinterpret_cast<const segment_command
*>(command
);
266 if (!strcmp(seg
->segname
, segname
))
277 const section
*MachOBase::findSection(const char *segname
, const char *sectname
) const
279 using LowLevelMemoryUtilities::increment
;
280 if (const segment_command
*seg
= findSegment(segname
)) {
282 const segment_command_64
*seg64
= reinterpret_cast<const segment_command_64
*>(seg
);
283 const section_64
*sect
= increment
<const section_64
>(seg64
+ 1, 0);
284 for (unsigned n
= flip(seg64
->nsects
); n
> 0; n
--, sect
++) {
285 if (!strcmp(sect
->sectname
, sectname
))
286 return reinterpret_cast<const section
*>(sect
);
289 const section
*sect
= increment
<const section
>(seg
+ 1, 0);
290 for (unsigned n
= flip(seg
->nsects
); n
> 0; n
--, sect
++) {
291 if (!strcmp(sect
->sectname
, sectname
))
301 // Translate a union lc_str into the string it denotes.
302 // Returns NULL (no exceptions) if the entry is corrupt.
304 const char *MachOBase::string(const load_command
*cmd
, const lc_str
&str
) const
306 size_t offset
= flip(str
.offset
);
307 const char *sp
= LowLevelMemoryUtilities::increment
<const char>(cmd
, offset
);
308 if (offset
+ strlen(sp
) + 1 > flip(cmd
->cmdsize
)) // corrupt string reference
315 // Figure out where the Code Signing information starts in the Mach-O binary image.
316 // The code signature is at the end of the file, and identified
317 // by a specially-named section. So its starting offset is also the end
318 // of the signable part.
319 // Note that the offset returned is relative to the start of the Mach-O image.
320 // Returns zero if not found (usually indicating that the binary was not signed).
322 const linkedit_data_command
*MachOBase::findCodeSignature() const
324 if (const load_command
*cmd
= findCommand(LC_CODE_SIGNATURE
))
325 return reinterpret_cast<const linkedit_data_command
*>(cmd
);
326 return NULL
; // not found
329 size_t MachOBase::signingOffset() const
331 if (const linkedit_data_command
*lec
= findCodeSignature())
332 return flip(lec
->dataoff
);
337 size_t MachOBase::signingLength() const
339 if (const linkedit_data_command
*lec
= findCodeSignature())
340 return flip(lec
->datasize
);
345 const linkedit_data_command
*MachOBase::findLibraryDependencies() const
347 if (const load_command
*cmd
= findCommand(LC_DYLIB_CODE_SIGN_DRS
))
348 return reinterpret_cast<const linkedit_data_command
*>(cmd
);
349 return NULL
; // not found
354 // Return the signing-limit length for this Mach-O binary image.
355 // This is the signingOffset if present, or the full length if not.
357 size_t MachO::signingExtent() const
359 if (size_t offset
= signingOffset())
369 void MachO::seek(size_t offset
)
371 FileDesc::seek(mOffset
+ offset
);
374 CFDataRef
MachO::dataAt(size_t offset
, size_t size
)
376 CFMallocData
buffer(size
);
377 if (this->read(buffer
, size
, mOffset
+ offset
) != size
)
378 UnixError::throwMe();
384 // Fat (aka universal) file wrappers.
385 // The offset is relative to the start of the containing file.
387 Universal::Universal(FileDesc fd
, off_t offset
/* = 0 */)
388 : FileDesc(fd
), mBase(offset
)
391 fat_header header
; // if this is a fat file
392 mach_header mheader
; // if this is a thin file
394 const size_t size
= max(sizeof(header
), sizeof(mheader
));
395 if (fd
.read(&header
, size
, offset
) != size
)
396 UnixError::throwMe(ENOEXEC
);
397 switch (header
.magic
) {
401 mArchCount
= ntohl(header
.nfat_arch
);
402 size_t archSize
= sizeof(fat_arch
) * mArchCount
;
403 mArchList
= (fat_arch
*)malloc(archSize
);
405 UnixError::throwMe();
406 if (fd
.read(mArchList
, archSize
, mBase
+ sizeof(header
)) != archSize
) {
408 UnixError::throwMe(ENOEXEC
);
410 for (fat_arch
*arch
= mArchList
; arch
< mArchList
+ mArchCount
; arch
++) {
412 n2hi(arch
->cpusubtype
);
417 secdebug("macho", "%p is a fat file with %d architectures",
425 mThinArch
= Architecture(mheader
.cputype
, mheader
.cpusubtype
);
426 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
432 mThinArch
= Architecture(flip(mheader
.cputype
), flip(mheader
.cpusubtype
));
433 secdebug("macho", "%p is a thin file (%s)", this, mThinArch
.name());
436 UnixError::throwMe(ENOEXEC
);
440 Universal::~Universal()
447 // Get the "local" architecture from the fat file
448 // Throws ENOEXEC if not found.
450 MachO
*Universal::architecture() const
453 return findImage(bestNativeArch());
455 return new MachO(*this, mBase
);
458 size_t Universal::archOffset() const
461 return mBase
+ findArch(bestNativeArch())->offset
;
468 // Get the specified architecture from the fat file
469 // Throws ENOEXEC if not found.
471 MachO
*Universal::architecture(const Architecture
&arch
) const
474 return findImage(arch
);
475 else if (mThinArch
.matches(arch
))
476 return new MachO(*this, mBase
);
478 UnixError::throwMe(ENOEXEC
);
481 size_t Universal::archOffset(const Architecture
&arch
) const
484 return mBase
+ findArch(arch
)->offset
;
485 else if (mThinArch
.matches(arch
))
488 UnixError::throwMe(ENOEXEC
);
493 // Get the architecture at a specified offset from the fat file.
494 // Throws an exception of the offset does not point at a Mach-O image.
496 MachO
*Universal::architecture(off_t offset
) const
499 return new MachO(*this, offset
);
500 else if (offset
== mBase
)
501 return new MachO(*this);
503 UnixError::throwMe(ENOEXEC
);
508 // Locate an architecture from the fat file's list.
509 // Throws ENOEXEC if not found.
511 const fat_arch
*Universal::findArch(const Architecture
&target
) const
513 assert(isUniversal());
514 const fat_arch
*end
= mArchList
+ mArchCount
;
516 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
517 if (arch
->cputype
== target
.cpuType()
518 && arch
->cpusubtype
== target
.cpuSubtype())
520 // match for generic model of main architecture
521 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
522 if (arch
->cputype
== target
.cpuType() && arch
->cpusubtype
== 0)
524 // match for any subarchitecture of the main architecture (questionable)
525 for (const fat_arch
*arch
= mArchList
; arch
< end
; ++arch
)
526 if (arch
->cputype
== target
.cpuType())
529 UnixError::throwMe(ENOEXEC
); // not found
532 MachO
*Universal::findImage(const Architecture
&target
) const
534 const fat_arch
*arch
= findArch(target
);
535 return new MachO(*this, mBase
+ arch
->offset
, arch
->size
);
540 // Find the best-matching architecture for this fat file.
541 // We pick the native architecture if it's available.
542 // If it contains exactly one architecture, we take that.
543 // Otherwise, we throw.
545 Architecture
Universal::bestNativeArch() const
548 // ask the NXArch API for our native architecture
549 const Architecture native
= Architecture::local();
550 if (fat_arch
*match
= NXFindBestFatArch(native
.cpuType(), native
.cpuSubtype(), mArchList
, mArchCount
))
552 // if the system can't figure it out, pick (arbitrarily) the first one
560 // List all architectures from the fat file's list.
562 void Universal::architectures(Architectures
&archs
)
565 for (unsigned n
= 0; n
< mArchCount
; n
++)
566 archs
.insert(mArchList
[n
]);
568 auto_ptr
<MachO
> macho(architecture());
569 archs
.insert(macho
->architecture());
575 // Quickly guess the Mach-O type of a file.
576 // Returns type zero if the file isn't Mach-O or Universal.
577 // Always looks at the start of the file, and does not change the file pointer.
579 uint32_t Universal::typeOf(FileDesc fd
)
583 if (fd
.read(&header
, sizeof(header
), 0) != sizeof(header
))
585 while (max_tries
> 0) {
586 switch (header
.magic
) {
589 return header
.filetype
;
593 return flip(header
.filetype
);
598 const fat_arch
*arch1
=
599 LowLevelMemoryUtilities::increment
<fat_arch
>(&header
, sizeof(fat_header
));
600 if (fd
.read(&header
, sizeof(header
), ntohl(arch1
->offset
)) != sizeof(header
))