/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 2.0 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * load64.c - Functions for decoding a Mach-o x86_64 Kernel. * * Adapted from load.c * Copyright (c) 1998-2003 Apple Computer, Inc. * * Copyright 2008 David F. Elliott */ #include #include #include #include extern bool isCpuTypeValid(cpu_type_t type); static unsigned long gBinaryAddress; static long DecodeSegment64(long cmdBase, unsigned int*load_addr, unsigned int *load_size); static long DecodeUnixThread64(long cmdBase, unsigned int *entry); long DecodeMachO64(void *binary, entry_t *rentry, char **raddr, int *rsize) { struct mach_header_64 *mH; unsigned long ncmds, cmdBase, cmd, cmdsize; // long headerBase, headerAddr, headerSize; unsigned int vmaddr = ~0; unsigned int vmend = 0; unsigned long cnt; long ret = -1; unsigned int entry = 0; gBinaryAddress = (unsigned long)binary; // headerBase = gBinaryAddress; cmdBase = (unsigned long)gBinaryAddress + sizeof(struct mach_header_64); mH = (struct mach_header_64 *)(gBinaryAddress); if (mH->magic != MH_MAGIC_64) { error("Mach-O file has bad magic number\n"); return -1; } // Record the selected CPU type so that further calls to thin fat files // use this type from now on. Typically the first binary decoded will // be the kernel so this makes sure that calls to ThinFatFile for // the drivers will rturn the right arch. if(!isCpuTypeValid(mH->cputype)) { printf("bad type\n"); getc(); return -1; } #if DEBUG printf("magic: %x\n", (unsigned)mH->magic); printf("cputype: %x\n", (unsigned)mH->cputype); printf("cpusubtype: %x\n", (unsigned)mH->cpusubtype); printf("filetype: %x\n", (unsigned)mH->filetype); printf("ncmds: %x\n", (unsigned)mH->ncmds); printf("sizeofcmds: %x\n", (unsigned)mH->sizeofcmds); printf("flags: %x\n", (unsigned)mH->flags); getc(); #endif ncmds = mH->ncmds; for (cnt = 0; cnt < ncmds; cnt++) { cmd = ((long *)cmdBase)[0]; cmdsize = ((long *)cmdBase)[1]; unsigned int load_addr; unsigned int load_size; switch (cmd) { case LC_SEGMENT_64: ret = DecodeSegment64(cmdBase, &load_addr, &load_size); if (ret == 0 && load_size != 0 && load_addr >= KERNEL_ADDR) { vmaddr = min(vmaddr, load_addr); vmend = max(vmend, load_addr + load_size); } break; case LC_UNIXTHREAD: ret = DecodeUnixThread64(cmdBase, &entry); break; default: #if NOTDEF printf("Ignoring cmd type %d.\n", (unsigned)cmd); #endif break; } if (ret != 0) return -1; cmdBase += cmdsize; } *rentry = (entry_t)( (unsigned long) entry & 0x3fffffff ); *rsize = vmend - vmaddr; *raddr = (char *)vmaddr; return ret; } static long DecodeSegment64(long cmdBase, unsigned int *load_addr, unsigned int *load_size) { struct segment_command_64 *segCmd; unsigned long vmaddr, fileaddr; long vmsize, filesize; segCmd = (struct segment_command_64 *)cmdBase; vmaddr = (segCmd->vmaddr & 0x3fffffff); vmsize = segCmd->vmsize; fileaddr = (gBinaryAddress + segCmd->fileoff); filesize = segCmd->filesize; if (filesize == 0 || vmsize == 0) { *load_addr = ~0; *load_size = 0; return 0; } #if DEBUG printf("segname: %s, vmaddr: %x, vmsize: %x, fileoff: %x, filesize: %x, nsects: %d, flags: %x.\n", segCmd->segname, (unsigned)vmaddr, (unsigned)vmsize, (unsigned)fileaddr, (unsigned)filesize, (unsigned) segCmd->nsects, (unsigned)segCmd->flags); getc(); #endif if (! ((vmaddr >= KERNEL_ADDR && (vmaddr + vmsize) <= (KERNEL_ADDR + KERNEL_LEN)) || (vmaddr >= HIB_ADDR && (vmaddr + vmsize) <= (HIB_ADDR + HIB_LEN)))) { stop("Kernel overflows available space"); } if (vmsize && (strcmp(segCmd->segname, "__PRELINK") == 0)) { gHaveKernelCache = 1; } // Newer xnu use multiple __PRELINK segments // This is kPrelinkInfoSegment else if (vmsize && (strcmp(segCmd->segname, "__PRELINK_INFO") == 0)) { gHaveKernelCache = 1; } // Copy from file load area. bcopy((char *)fileaddr, (char *)vmaddr, filesize); // Zero space at the end of the segment. bzero((char *)(vmaddr + filesize), vmsize - filesize); *load_addr = vmaddr; *load_size = vmsize; return 0; } static long DecodeUnixThread64(long cmdBase, unsigned int *entry) { x86_thread_state64_t *i386ThreadState; i386ThreadState = (x86_thread_state64_t *) (cmdBase + sizeof(struct thread_command) + 8); #if defined(__DARWIN_UNIX03) && __DARWIN_UNIX03 *entry = i386ThreadState->__rip; #else *entry = i386ThreadState->rip; #endif return 0; }