/* * 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@ */ /* * load.c - Functions for decoding a Mach-o Kernel. * * Copyright (c) 1998-2003 Apple Computer, Inc. * */ #include #include #include #include static long DecodeSegment(long cmdBase, unsigned int*load_addr, unsigned int *load_size); static long DecodeUnixThread(long cmdBase, unsigned int *entry); static unsigned long gBinaryAddress; BOOL gHaveKernelCache; BOOL cpuHasIA32e(); static cpu_type_t gSelectedCpuType = CPU_TYPE_ANY; void selectCpuType(cpu_type_t type) { gSelectedCpuType = type; } bool isCpuTypeValid(cpu_type_t type) { // First run through set the allowed type if(gSelectedCpuType == CPU_TYPE_ANY) { gSelectedCpuType = type; return true; } else return gSelectedCpuType == type; } // Return true if the kernel is true 64-bit (e.g. Snow Leopard) BOOL isKernel64() { return gSelectedCpuType == CPU_TYPE_X86_64; } // Public Functions long ThinFatFileToArch(void **binary, unsigned long *length, cpu_type_t desiredArch); long ThinFatFile(void **binary, unsigned long *length) { struct fat_header *fhp = (struct fat_header*)*binary; if(!cpuHasIA32e()) gSelectedCpuType = CPU_TYPE_I386; if (gSelectedCpuType != CPU_TYPE_ANY) { return ThinFatFileToArch(binary, length, gSelectedCpuType); } // No arch was specified by user, see what the binary has. // NOTE: The below code only runs for the first binary (the kernel) that is thinned. // Prefer i386 to x86-64 for now static const cpu_type_t s_cpuTypeOrder[] = { CPU_TYPE_I386 , CPU_TYPE_X86_64 }; int typeI; long result; // Should always be initialized by call to ThinFatFileToArch. for(typeI = 0; typeI < sizeof(s_cpuTypeOrder)/sizeof(*s_cpuTypeOrder); ++typeI) { result = ThinFatFileToArch(binary, length, s_cpuTypeOrder[typeI]); if(result == 0) { // File is fat, check to see if it contained the x86_64 arch by seeing if // *binary was updated to something other than the original pointer. if(*binary != fhp) { // FIXME: Do we need to set this? Won't isCpuTypeValid be called? gSelectedCpuType = s_cpuTypeOrder[typeI]; return result; } // If it didn't contain the arch we wanted, continue. // FIXME: Old code would have still set gSelectedCpuType here... } else { // File is non-fat, return the result now, no point checking other arch. // We don't set the selected CPU type at this point because the call to // isCpuTypeValid will set it. break; } } // We get here if the file is non-fat or if we exhausted all architectures. // If non-fat and not of a correct arch our caller will see this by examining // the cpu type within the mach header. // If fat and the correct arch was not found then we return 0 (indicating fat) // but *binary isn't updated and our caller will see this. return result; } /* Updates *binary to point to the portion of the binary for the desired architecture. If the binary is non-fat, returns -1. If the binary is fat but the desired architecture is not present, returns 0 but does not update *binary. */ long ThinFatFileToArch(void **binary, unsigned long *length, cpu_type_t desiredArch) { unsigned long nfat, swapped, size = 0; struct fat_header *fhp = (struct fat_header *)*binary; struct fat_arch const *in_fap = (struct fat_arch *)((unsigned long)*binary + sizeof(struct fat_header)); if (fhp->magic == FAT_MAGIC) { nfat = fhp->nfat_arch; swapped = 0; } else if (fhp->magic == FAT_CIGAM) { nfat = OSSwapInt32(fhp->nfat_arch); swapped = 1; } else { return -1; } struct fat_arch fa; struct fat_arch const *fap; for (; nfat > 0; nfat--, in_fap++) { if (swapped) { fa.cputype = OSSwapInt32(in_fap->cputype); fa.offset = OSSwapInt32(in_fap->offset); fa.size = OSSwapInt32(in_fap->size); fap = &fa; } else { fap = in_fap; } if (fap->cputype == desiredArch) { *binary = (void *) ((unsigned long)*binary + fap->offset); size = fap->size; break; } } if (length != 0) *length = size; return 0; } extern long DecodeMachO64(void *binary, entry_t *rentry, char **raddr, int *rsize); long DecodeMachO(void *binary, entry_t *rentry, char **raddr, int *rsize) { struct mach_header *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); mH = (struct mach_header *)(gBinaryAddress); if(mH->magic == MH_MAGIC_64) return DecodeMachO64(binary, rentry, raddr, rsize); if (mH->magic != MH_MAGIC) { 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: ret = DecodeSegment(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 = DecodeUnixThread(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; } // Private Functions static long DecodeSegment(long cmdBase, unsigned int *load_addr, unsigned int *load_size) { struct segment_command *segCmd; unsigned long vmaddr, fileaddr; long vmsize, filesize; segCmd = (struct segment_command *)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; } // 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 DecodeUnixThread(long cmdBase, unsigned int *entry) { i386_thread_state_t *i386ThreadState; i386ThreadState = (i386_thread_state_t *) (cmdBase + sizeof(struct thread_command) + 8); #if defined(__DARWIN_UNIX03) && __DARWIN_UNIX03 *entry = i386ThreadState->__eip; #else *entry = i386ThreadState->eip; #endif return 0; }