340 lines
11 KiB
C++
340 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2009 Apple, Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Inc.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "sys.h"
|
|
#include "Buffer.h"
|
|
#include "LoadMachO.h"
|
|
|
|
#define PACKED __attribute__((packed))
|
|
|
|
// mach-o/fat.h
|
|
|
|
typedef uint32_t cpu_type_t;
|
|
typedef uint32_t cpu_subtype_t;
|
|
|
|
struct load_command {
|
|
uint32_t cmd;
|
|
uint32_t cmdsize;
|
|
};
|
|
|
|
// mach-o/loader.h
|
|
|
|
struct mach_header {
|
|
uint32_t magic; /* mach magic number identifier */
|
|
cpu_type_t cputype; /* cpu specifier */
|
|
cpu_subtype_t cpusubtype; /* machine specifier */
|
|
uint32_t filetype; /* type of file */
|
|
uint32_t ncmds; /* number of load commands */
|
|
uint32_t sizeofcmds; /* the size of all the load commands */
|
|
uint32_t flags; /* flags */
|
|
} PACKED;
|
|
|
|
struct mach_header_64 {
|
|
uint32_t magic; /* mach magic number identifier */
|
|
cpu_type_t cputype; /* cpu specifier */
|
|
cpu_subtype_t cpusubtype; /* machine specifier */
|
|
uint32_t filetype; /* type of file */
|
|
uint32_t ncmds; /* number of load commands */
|
|
uint32_t sizeofcmds; /* the size of all the load commands */
|
|
uint32_t flags; /* flags */
|
|
uint32_t reserved; /* reserved */
|
|
};
|
|
|
|
/* Constant for the magic field of the mach_header (32-bit architectures) */
|
|
#define MH_MAGIC 0xfeedface /* the mach magic number */
|
|
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
|
|
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
|
|
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
|
|
|
|
/* Constants for the cmd field of all load commands, the type */
|
|
#define LC_SEGMENT 0x1 /* segment of this file to be mapped */
|
|
#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */
|
|
#define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */
|
|
|
|
#define LC_THREAD 0x4 /* thread */
|
|
#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
|
|
#define LC_SEGMENT_64 0x19
|
|
|
|
typedef int32_t vm_prot_t;
|
|
|
|
struct segment_command { /* for 32-bit architectures */
|
|
uint32_t cmd; /* LC_SEGMENT */
|
|
uint32_t cmdsize; /* includes sizeof section structs */
|
|
char segname[16]; /* segment name */
|
|
uint32_t vmaddr; /* memory address of this segment */
|
|
uint32_t vmsize; /* memory size of this segment */
|
|
uint32_t fileoff; /* file offset of this segment */
|
|
uint32_t filesize; /* amount to map from the file */
|
|
vm_prot_t maxprot; /* maximum VM protection */
|
|
vm_prot_t initprot; /* initial VM protection */
|
|
uint32_t nsects; /* number of sections in segment */
|
|
uint32_t flags; /* flags */
|
|
} PACKED;
|
|
|
|
struct segment_command_64 { /* for 64-bit architectures */
|
|
uint32_t cmd; /* LC_SEGMENT_64 */
|
|
uint32_t cmdsize; /* includes sizeof section_64 structs */
|
|
char segname[16]; /* segment name */
|
|
uint64_t vmaddr; /* memory address of this segment */
|
|
uint64_t vmsize; /* memory size of this segment */
|
|
uint64_t fileoff; /* file offset of this segment */
|
|
uint64_t filesize; /* amount to map from the file */
|
|
vm_prot_t maxprot; /* maximum VM protection */
|
|
vm_prot_t initprot; /* initial VM protection */
|
|
uint32_t nsects; /* number of sections in segment */
|
|
uint32_t flags; /* flags */
|
|
};
|
|
|
|
struct thread_command {
|
|
uint32_t cmd; /* LC_THREAD or LC_UNIXTHREAD */
|
|
uint32_t cmdsize; /* total size of this command */
|
|
/* uint32_t flavor flavor of thread state */
|
|
/* uint32_t count count of longs in thread state */
|
|
/* struct XXX_thread_state state thread state for this flavor */
|
|
/* ... */
|
|
} PACKED;
|
|
|
|
// arm/_types.h
|
|
|
|
struct arm_thread_state
|
|
{
|
|
uint32_t r[13]; /* General purpose register r0-r12 */
|
|
uint32_t sp; /* Stack pointer r13 */
|
|
uint32_t lr; /* Link regisster r14 */
|
|
uint32_t pc; /* Program counter r15 */
|
|
uint32_t cpsr; /* Current program status register */
|
|
} PACKED;
|
|
|
|
struct arm_thread_state_64 {
|
|
uint64_t x[29]; /* General purpose registers x0-x28 */
|
|
uint64_t fp; /* Frame pointer x29 */
|
|
uint64_t lr; /* Link register x30 */
|
|
uint64_t sp; /* Stack pointer x31 */
|
|
uint64_t pc; /* Program counter */
|
|
uint32_t cpsr; /* Current program status register */
|
|
} __attribute__((aligned(8)));
|
|
|
|
LoadMachO::LoadMachO(const Buffer &macho,
|
|
Buffer *ram)
|
|
: _segment_list(),
|
|
_last_address(0),
|
|
_entry_point_phys_offset(0),
|
|
_macho(macho),
|
|
_virtual_base(0),
|
|
_have_virtual_base(false),
|
|
_ram(ram) {
|
|
}
|
|
|
|
void LoadMachO::OverrideVirtualBase(uint64_t virtual_base) {
|
|
_virtual_base = virtual_base;
|
|
_have_virtual_base = true;
|
|
}
|
|
|
|
bool LoadMachO::GetVirtualBase(uint64_t *virtual_base) const {
|
|
if (_have_virtual_base) {
|
|
*virtual_base = _virtual_base;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool LoadMachO::Load() {
|
|
struct mach_header *mH = (struct mach_header *) _macho.buf();
|
|
uint32_t ncmds = mH->ncmds;
|
|
char *cmdBase;
|
|
|
|
_is_64_bit = mH->magic == MH_MAGIC_64 ? true : false;
|
|
if (_is_64_bit) {
|
|
cmdBase = (char *) (((struct mach_header_64*)mH) + 1);
|
|
} else {
|
|
cmdBase = (char *) (mH + 1);
|
|
}
|
|
|
|
for (uint32_t cnt = 0; cnt < ncmds; cnt++) {
|
|
uint32_t cmd, cmdsize;
|
|
memcpy(&cmd, cmdBase, 4);
|
|
memcpy(&cmdsize, cmdBase + 4, 4);
|
|
bool ok;
|
|
|
|
//fprintf(stdout, "Current command has cmd %x, size %d\n", cmd, cmdsize);
|
|
|
|
switch (cmd) {
|
|
case LC_SEGMENT:
|
|
if (_is_64_bit) {
|
|
fprintf(stderr, "Found LC_SEGMENT in 64-bit Mach-O?");
|
|
ok = false;
|
|
break;
|
|
}
|
|
// Copy a segment into ram.
|
|
ok = DecodeSegment(cmdBase);
|
|
break;
|
|
case LC_SEGMENT_64:
|
|
if (!_is_64_bit) {
|
|
fprintf(stderr, "Found LC_SEGMENT64 in 32-bit Mach-O?");
|
|
ok = false;
|
|
break;
|
|
}
|
|
// Copy a segment into ram.
|
|
ok = DecodeSegment(cmdBase);
|
|
break;
|
|
case LC_UNIXTHREAD:
|
|
// Get entry point.
|
|
ok = DecodeUnixThread(cmdBase);
|
|
break;
|
|
|
|
case LC_SYMTAB:
|
|
// Don't do anything useful with symbol tables.
|
|
ok = true;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Ignoring cmd type %d.\n", (int) cmd);
|
|
ok = true;
|
|
}
|
|
|
|
if (!ok) return false;
|
|
|
|
cmdBase += cmdsize;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static inline void
|
|
widen_segment_command(const struct segment_command *scp32,
|
|
struct segment_command_64 *scp)
|
|
{
|
|
scp->cmd = scp32->cmd;
|
|
scp->cmdsize = scp32->cmdsize;
|
|
bcopy(scp32->segname, scp->segname, sizeof(scp->segname));
|
|
scp->vmaddr = scp32->vmaddr;
|
|
scp->vmsize = scp32->vmsize;
|
|
scp->fileoff = scp32->fileoff;
|
|
scp->filesize = scp32->filesize;
|
|
scp->maxprot = scp32->maxprot;
|
|
scp->initprot = scp32->initprot;
|
|
scp->nsects = scp32->nsects;
|
|
scp->flags = scp32->flags;
|
|
}
|
|
|
|
|
|
bool LoadMachO::DecodeSegment(const char *cmdBase) {
|
|
const char *src;
|
|
char *dst;
|
|
uint64_t phys_offset;
|
|
uint64_t vmsize, filesize;
|
|
struct segment_command_64 segment_command, *scp;
|
|
size_t segment_command_size;
|
|
struct load_command *lcp = (struct load_command*)cmdBase;
|
|
|
|
if (LC_SEGMENT_64 == lcp->cmd) {
|
|
segment_command_size = sizeof(struct segment_command_64);
|
|
} else {
|
|
segment_command_size = sizeof(struct segment_command);
|
|
}
|
|
|
|
if (lcp->cmdsize < segment_command_size)
|
|
return false;
|
|
|
|
if (LC_SEGMENT_64 == lcp->cmd) {
|
|
scp = (struct segment_command_64 *)lcp;
|
|
} else {
|
|
scp = &segment_command;
|
|
widen_segment_command((struct segment_command *)cmdBase, scp);
|
|
}
|
|
|
|
if (!_have_virtual_base) {
|
|
// Assume the virtual address base is the top bits of the first
|
|
// segment we encounter, aligned to 2MB
|
|
_have_virtual_base = true;
|
|
_virtual_base = scp->vmaddr;
|
|
_virtual_base -= _virtual_base % (1<<21);
|
|
fprintf(stderr, "inferring virtual base is 0x%016llx\n", _virtual_base);
|
|
}
|
|
|
|
phys_offset = scp->vmaddr - _virtual_base;
|
|
vmsize = scp->vmsize;
|
|
|
|
fprintf(stdout, "Handling %d-bit segment with vmaddr %llx, vmsize %llx, fileoff %lld, filesize %lld, maxprot %d, nsects %d\n",
|
|
lcp->cmd == LC_SEGMENT_64 ? 64 : 32, scp->vmaddr, scp->vmsize, scp->fileoff, scp->filesize, scp->maxprot, scp->nsects);
|
|
fprintf(stdout, "Placing at phys offset %llx\n", phys_offset);
|
|
|
|
if (phys_offset + vmsize > _ram->size()) {
|
|
fprintf(stderr, "segment overruns end of memory (0x%016llx vs 0x%08x)\n",
|
|
(phys_offset + vmsize),
|
|
(unsigned)_ram->size());
|
|
return false;
|
|
}
|
|
|
|
src = (const char *) _macho.buf() + scp->fileoff;
|
|
dst = (char *) _ram->buf() + phys_offset;
|
|
filesize = scp->filesize;
|
|
if (scp->fileoff + filesize > _macho.size()) {
|
|
fprintf(stderr,
|
|
"segment in Mach-O image past end of file (0x%016llx vs 0x%08x)\n",
|
|
(scp->fileoff + filesize),
|
|
(unsigned) _macho.size());
|
|
return false;
|
|
}
|
|
|
|
uint64_t cpysize = std::min(filesize, vmsize);
|
|
assert(cpysize == (size_t)cpysize);
|
|
memcpy(dst, src, (size_t)cpysize);
|
|
|
|
if (vmsize > filesize) {
|
|
assert((vmsize - filesize) == (size_t)(vmsize - filesize));
|
|
memset(dst + filesize, 0, (size_t)(vmsize - filesize));
|
|
}
|
|
|
|
// Adjust the last address used by the kernel
|
|
if (phys_offset + vmsize > _last_address) {
|
|
_last_address = phys_offset + vmsize;
|
|
}
|
|
|
|
// Record the segment in the list.
|
|
Segment segment;
|
|
segment.name = scp->segname;
|
|
segment.pmaddr = phys_offset;
|
|
segment.size = vmsize;
|
|
_segment_list.push_back(segment);
|
|
return true;
|
|
}
|
|
|
|
bool LoadMachO::DecodeUnixThread(const char *cmdBase) {
|
|
// The ARM Thread State starts after the thread command stuct plus,
|
|
// 2 longs for the flaver an num longs.
|
|
if (!_have_virtual_base) {
|
|
fprintf(stderr,
|
|
"got unix thread load command but don't know the virtual base\n");
|
|
return false;
|
|
}
|
|
if (_is_64_bit) {
|
|
const struct arm_thread_state_64 *armThreadState =
|
|
(const struct arm_thread_state_64 *)
|
|
(cmdBase + sizeof(struct thread_command) + 8);
|
|
_entry_point_phys_offset = armThreadState->pc - _virtual_base;
|
|
fprintf(stdout, "Setting 64-bit thread entry point (phys offset) to 0x%llx (pc %llx)\n", _entry_point_phys_offset, armThreadState->pc);
|
|
} else {
|
|
const struct arm_thread_state *armThreadState =
|
|
(const struct arm_thread_state *)
|
|
(cmdBase + sizeof(struct thread_command) + 8);
|
|
_entry_point_phys_offset = armThreadState->pc - _virtual_base;
|
|
fprintf(stdout, "Setting 32-bit thread entry point (phys offset) to 0x%x (pc %x)\n", (unsigned)_entry_point_phys_offset, armThreadState->pc);
|
|
}
|
|
|
|
return true;
|
|
}
|