X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/1c51fdde6f257ffe2a1b92d1a783dab947211e95..b51d5b5f06948960f0bfd4ad974e625ca2350936:/kmodload.tproj/kld_patch.c diff --git a/kmodload.tproj/kld_patch.c b/kmodload.tproj/kld_patch.c deleted file mode 100644 index 646bf14..0000000 --- a/kmodload.tproj/kld_patch.c +++ /dev/null @@ -1,2124 +0,0 @@ -/* - * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights - * Reserved. This file contains Original Code and/or Modifications of - * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.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. - * - * The 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@ - */ -/* - * History: - * 2001-05-30 gvdl Initial implementation of the vtable patcher. - */ -// 45678901234567890123456789012345678901234567890123456789012345678901234567890 - -#include -#include -#include -#include - -#if KERNEL - -#include -#include - -#include - -#include - -#include -#include - -#include "mach_loader.h" - -#include - -enum { false = 0, true = 1 }; - -#define vm_page_size page_size - -extern load_return_t fatfile_getarch( - void * vp, // normally a (struct vnode *) - vm_offset_t data_ptr, - struct fat_arch * archret); - -#else /* !KERNEL */ -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#endif /* KERNEL */ - -#include "kld_patch.h" - -#if 0 -static __inline__ void DIE(void) { IODelay(2000000000); } - -#define LOG_DELAY() IODelay(200000) -#define DEBUG_LOG(x) do { IOLog x; LOG_DELAY(); } while(0) -#else - -#define DIE() -#define LOG_DELAY() -#define DEBUG_LOG(x) - -#endif - -// OSObject symbol prefixes and suffixes -#define kVTablePrefix "___vt" -#define kReservedPrefix "__RESERVED" -#define kSuperClassSuffix ".superClass" -#define kGMetaSuffix ".gMetaClass" -#define kLinkEditSegName SEG_LINKEDIT - -// GCC 2.95 drops 2 leading constants in the vtable -#define kVTablePreambleLen 2 - -// Last address that I'm willing to try find vm in -#define kTopAddr ((unsigned char *) (1024 * 1024 * 1024)) - -// Size in bytes that Data Ref object's get increased in size -// Must be a power of 2 -#define kDataCapacityIncrement 128 - -// My usual set of helper macros. I personally find these macros -// easier to read in the code rather than an explicit error condition -// check. If I don't make it easy then I may get lazy ond not check -// everything. I'm sorry if you find this code harder to read. - -// break_if will evaluate the expression and if it is true -// then it will print the msg, which is enclosed in parens -// and then break. Usually used in loops are do { } while (0) -#define break_if(expr, msg) \ - if (expr) { \ - errprintf msg; \ - break; \ - } - -// return_if will evaluate expr and if true it will log the -// msg, which is enclosed in parens, and then it will return -// with the return code of ret. -#define return_if(expr, ret, msg) do { \ - if (expr) { \ - errprintf msg; \ - return ret; \ - } \ -} while (0) - -#ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) -#endif /* MIN */ -#ifndef MAX -#define MAX(a,b) (((a)>(b))?(a):(b)) -#endif /* MAX */ - -typedef struct Data { - unsigned long fLength, fCapacity; - unsigned char *fData; -} Data, *DataRef; - -struct sectionRecord { - const struct section *fSection; - DataRef fRelocCache; -}; - -enum patchState { - kSymbolIdentical, - kSymbolLocal, - kSymbolPadUpdate, - kSymbolSuperUpdate, - kSymbolMismatch -}; - -struct patchRecord { - struct nlist *fSymbol; - enum patchState fType; -}; - -struct relocRecord { - void *fValue; - const struct nlist *fSymbol; - struct relocation_info *fRInfo; - void *reserved; -}; - -struct metaClassRecord { - char *fSuperName; - struct fileRecord *fFile; - const struct nlist *fVTableSym; - struct patchRecord *fPatchedVTable; - char fClassName[1]; -}; - -struct fileRecord { - size_t fMapSize, fMachOSize; - const char *fPath; - unsigned char *fMap, *fMachO, *fPadEnd; - DataRef fClassList; - DataRef fSectData; - DataRef fNewSymbols, fNewStrings; - struct symtab_command *fSymtab; - struct sectionRecord *fSections; - char *fStringBase; - struct nlist *fSymbolBase; - const struct nlist *fLocalSyms; - unsigned int fNSects; - int fNLocal; - int fNewStringsLen; - Boolean fIsKernel, fNoKernelExecutable, fIsKmem; - Boolean fImageDirty, fSymbolsDirty; -}; - -static DataRef sFilesTable; -static struct fileRecord *sKernelFile; - -static DataRef sMergedFiles; -static DataRef sMergeMetaClasses; -static Boolean sMergedKernel; - -static void errprintf(const char *fmt, ...) -{ - extern void kld_error_vprintf(const char *format, va_list ap); - - va_list ap; - - va_start(ap, fmt); - kld_error_vprintf(fmt, ap); - va_end(ap); - -DIE(); -} - -static __inline__ unsigned long DataGetLength(DataRef data) -{ - return data->fLength; -} - -static __inline__ unsigned char *DataGetPtr(DataRef data) -{ - return data->fData; -} - - -static __inline__ Boolean DataContainsAddr(DataRef data, void *vAddr) -{ - unsigned char *addr = vAddr; - - return (data->fData <= addr) && (addr < data->fData + data->fLength); -} - -static __inline__ Boolean DataAddLength(DataRef data, unsigned long length) -{ - static Boolean DataSetLength(DataRef data, unsigned long length); - return DataSetLength(data, data->fLength + length); -} - -static __inline__ Boolean -DataAppendBytes(DataRef data, const void *addr, unsigned int len) -{ - unsigned long size = DataGetLength(data); - - if (!DataAddLength(data, len)) - return false; - - bcopy(addr, DataGetPtr(data) + size, len); - return true; -} - -static __inline__ Boolean DataAppendData(DataRef dst, DataRef src) -{ - return DataAppendBytes(dst, DataGetPtr(src), DataGetLength(src)); -} - -static Boolean DataSetLength(DataRef data, unsigned long length) -{ - // Don't bother to ever shrink a data object. - if (length > data->fCapacity) { - unsigned char *newData; - unsigned long newCapacity; - - newCapacity = length + kDataCapacityIncrement - 1; - newCapacity &= ~(kDataCapacityIncrement - 1); - newData = (unsigned char *) realloc(data->fData, newCapacity); - if (!newData) - return false; - - bzero(newData + data->fCapacity, newCapacity - data->fCapacity); - data->fData = newData; - data->fCapacity = newCapacity; - } - - data->fLength = length; - return true; -} - -static DataRef DataCreate(unsigned long length) -{ - DataRef data = (DataRef) malloc(sizeof(Data)); - - if (data) { - if (!length) - data->fCapacity = kDataCapacityIncrement; - else { - data->fCapacity = length + kDataCapacityIncrement - 1; - data->fCapacity &= ~(kDataCapacityIncrement - 1); - } - - data->fData = (unsigned char *) malloc(data->fCapacity); - if (!data->fData) { - free(data); - return NULL; - } - - bzero(data->fData, data->fCapacity); - data->fLength = length; - } - return data; -} - -static void DataRelease(DataRef data) -{ - if (data) { - if (data->fData) - free(data->fData); - data->fData = 0; - free(data); - } -} - -static const char * -symbolname(const struct fileRecord *file, const struct nlist *sym) -{ - unsigned long strsize; - long strx = sym->n_un.n_strx; - - if (strx >= 0) - return file->fStringBase + strx; - - strsize = file->fSymtab->strsize; - strx = -strx; - if (strx < strsize) - return file->fStringBase + strx; - - strx -= strsize; - return (char *) DataGetPtr(file->fNewStrings) + strx; -} - -static struct fileRecord *getFile(const char *path) -{ - if (sFilesTable) { - int i, nfiles; - struct fileRecord **files; - - // Check to see if we have already merged this file - nfiles = DataGetLength(sFilesTable) / sizeof(struct fileRecord *); - files = (struct fileRecord **) DataGetPtr(sFilesTable); - for (i = 0; i < nfiles; i++) { - if (!strcmp(path, files[i]->fPath)) - return files[i]; - } - } - - return NULL; -} - -static struct fileRecord * addFile(struct fileRecord *file) -{ - struct fileRecord *newFile; - - if (!sFilesTable) { - sFilesTable = DataCreate(0); - if (!sFilesTable) - return NULL; - } - - newFile = (struct fileRecord *) malloc(sizeof(struct fileRecord)); - if (!newFile) - return NULL; - - if (!DataAppendBytes(sFilesTable, &newFile, sizeof(newFile))) { - free(newFile); - return NULL; - } - - bcopy(file, newFile, sizeof(struct fileRecord)); - return newFile; -} - -// @@@ gvdl: need to clean up the sMergeMetaClasses -// @@@ gvdl: I had better fix the object file up again -static void removeFile(struct fileRecord *file) -{ - if (file->fClassList) { - DataRelease(file->fClassList); - file->fClassList = 0; - } - - if (file->fSectData) { - struct sectionRecord *section; - unsigned int i, nsect; - - nsect = file->fNSects; - section = file->fSections; - for (i = 0; i < nsect; i++, section++) { - if (section->fRelocCache) { - DataRelease(section->fRelocCache); - section->fRelocCache = 0; - } - } - - DataRelease(file->fSectData); - file->fSectData = 0; - file->fSections = 0; - file->fNSects = 0; - } - - if (file->fMap) { -#if KERNEL - if (file->fIsKmem) - kmem_free(kernel_map, (vm_address_t) file->fMap, file->fMapSize); -#else /* !KERNEL */ - if (file->fPadEnd) { - vm_address_t padVM; - vm_size_t padSize; - - padVM = round_page((vm_address_t) file->fMap + file->fMapSize); - padSize = (vm_size_t) ((vm_address_t) file->fPadEnd - padVM); - (void) vm_deallocate(mach_task_self(), padVM, padSize); - file->fPadEnd = 0; - } - - (void) munmap((caddr_t) file->fMap, file->fMapSize); -#endif /* !KERNEL */ - file->fMap = 0; - } - - file->fPath = 0; -} - -#if !KERNEL -static Boolean -mapObjectFile(struct fileRecord *file) -{ - Boolean result = false; - static unsigned char *sFileMapBaseAddr; - - int fd = 0; - - if (!sFileMapBaseAddr) { - kern_return_t ret; - vm_address_t probeAddr; - - // If we don't already have a base addr find any random chunk - // of 32 meg of VM and to use the 16 meg boundrary as a base. - ret = vm_allocate(mach_task_self(), &probeAddr, - 32 * 1024 * 1024, VM_FLAGS_ANYWHERE); - return_if(KERN_SUCCESS != ret, false, - ("Unable to allocate base memory %s\n", mach_error_string(ret))); - (void) vm_deallocate(mach_task_self(), probeAddr, 32 * 1024 * 1024); - - // Now round to the next 16 Meg boundrary - probeAddr = (probeAddr + (16 * 1024 * 1024 - 1)) - & ~(16 * 1024 * 1024 - 1); - sFileMapBaseAddr = (unsigned char *) probeAddr; - } - - fd = open(file->fPath, O_RDONLY, 0); - return_if(fd == -1, false, ("Can't open %s for reading - %s\n", - file->fPath, strerror(errno))); - - do { - kern_return_t ret; - struct stat sb; - int retaddr = -1; - - break_if(fstat(fd, &sb) == -1, - ("Can't stat %s - %s\n", file->fPath, strerror(errno))); - - file->fMapSize = sb.st_size; - file->fMap = sFileMapBaseAddr; - ret = KERN_SUCCESS; - while (file->fMap < kTopAddr) { - vm_address_t padVM; - vm_address_t padVMEnd; - vm_size_t padSize; - - padVM = round_page((vm_address_t) file->fMap + file->fMapSize); - retaddr = (int) mmap(file->fMap, file->fMapSize, - PROT_READ|PROT_WRITE, - MAP_FIXED|MAP_FILE|MAP_PRIVATE, - fd, 0); - if (-1 == retaddr) { - break_if(ENOMEM != errno, - ("mmap failed %d - %s\n", errno, strerror(errno))); - - file->fMap = (unsigned char *) padVM; - continue; - } - - - // Round up padVM to the next page after the file and assign at - // least another fMapSize more room rounded up to the next page - // boundary. - padVMEnd = round_page(padVM + file->fMapSize); - padSize = padVMEnd - padVM; - ret = vm_allocate( - mach_task_self(), &padVM, padSize, VM_FLAGS_FIXED); - if (KERN_SUCCESS == ret) { - file->fPadEnd = (unsigned char *) padVMEnd; - break; - } - else { - munmap(file->fMap, file->fMapSize); - break_if(KERN_INVALID_ADDRESS != ret, - ("Unable to allocate pad vm for %s - %s\n", - file->fPath, mach_error_string(ret))); - - file->fMap = (unsigned char *) padVMEnd; - continue; // try again wherever the vm system wants - } - } - - if (-1 == retaddr || KERN_SUCCESS != ret) - break; - - break_if(file->fMap >= kTopAddr, - ("Unable to map memory %s\n", file->fPath)); - - sFileMapBaseAddr = file->fPadEnd; - result = true; - } while(0); - - close(fd); - return result; -} -#endif /* !KERNEL */ - -static Boolean findBestArch(struct fileRecord *file) -{ - unsigned long magic; - struct fat_header *fat; - - - file->fMachOSize = file->fMapSize; - file->fMachO = file->fMap; - magic = ((const struct mach_header *) file->fMachO)->magic; - fat = (struct fat_header *) file->fMachO; - - // Try to figure out what type of file this is - return_if(file->fMapSize < sizeof(unsigned long), false, - ("%s isn't a valid object file - no magic\n", file->fPath)); - -#if KERNEL - - // CIGAM is byte-swapped MAGIC - if (magic == FAT_MAGIC || magic == FAT_CIGAM) { - - load_return_t load_return; - struct fat_arch fatinfo; - - load_return = fatfile_getarch(NULL, (vm_address_t) fat, &fatinfo); - return_if(load_return != LOAD_SUCCESS, false, - ("Extension \"%s\": has no code for this computer\n", file->fPath)); - - file->fMachO = file->fMap + fatinfo.offset; - file->fMachOSize = fatinfo.size; - magic = ((const struct mach_header *) file->fMachO)->magic; - } - -#else /* !KERNEL */ - - // Do we need to in-place swap the endianness of the fat header? - if (magic == FAT_CIGAM) { - unsigned long i; - struct fat_arch *arch; - - fat->nfat_arch = NXSwapBigLongToHost(fat->nfat_arch); - return_if(file->fMapSize < sizeof(struct fat_header) - + fat->nfat_arch * sizeof(struct fat_arch), - false, ("%s is too fat\n", file->fPath)); - - arch = (struct fat_arch *) &fat[1]; - for (i = 0; i < fat->nfat_arch; i++) { - arch[i].cputype = NXSwapBigLongToHost(arch[i].cputype); - arch[i].cpusubtype = NXSwapBigLongToHost(arch[i].cpusubtype); - arch[i].offset = NXSwapBigLongToHost(arch[i].offset); - arch[i].size = NXSwapBigLongToHost(arch[i].size); - arch[i].align = NXSwapBigLongToHost(arch[i].align); - } - - magic = NXSwapBigLongToHost(fat->magic); - } - - // Now see if we can find any valid architectures - if (magic == FAT_MAGIC) { - const NXArchInfo *myArch; - unsigned long fatsize; - struct fat_arch *arch; - - fatsize = sizeof(struct fat_header) - + fat->nfat_arch * sizeof(struct fat_arch); - return_if(file->fMapSize < fatsize, - false, ("%s isn't a valid fat file\n", file->fPath)); - - myArch = NXGetLocalArchInfo(); - arch = NXFindBestFatArch(myArch->cputype, myArch->cpusubtype, - (struct fat_arch *) &fat[1], fat->nfat_arch); - return_if(!arch, - false, ("%s hasn't got arch for %s\n", file->fPath, myArch->name)); - return_if(arch->offset + arch->size > file->fMapSize, - false, ("%s's %s arch is incomplete\n", file->fPath, myArch->name)); - file->fMachO = file->fMap + arch->offset; - file->fMachOSize = arch->size; - magic = ((const struct mach_header *) file->fMachO)->magic; - } - -#endif /* KERNEL */ - - return_if(magic != MH_MAGIC, - false, ("%s isn't a valid mach-o\n", file->fPath)); - - return true; -} - -static Boolean -parseSegments(struct fileRecord *file, struct segment_command *seg) -{ - struct sectionRecord *sections; - int i, nsects = seg->nsects; - const struct segmentMap { - struct segment_command seg; - const struct section sect[1]; - } *segMap; - - if (!nsects) { -#if KERNEL - // We don't need to look for the LinkEdit segment unless - // we are running in the kernel environment. - if (!strcmp(kLinkEditSegName, seg->segname)) { - // Grab symbol table from linkedit we will need this later - file->fSymbolBase = (void *) seg; - } -#endif - - return true; // Nothing more to do, so that was easy. - } - - if (!file->fSectData) { - file->fSectData = DataCreate(0); - if (!file->fSectData) - return false; - } - - // Increase length of section DataRef and cache data pointer - if (!DataAddLength(file->fSectData, nsects * sizeof(struct sectionRecord))) - return false; - file->fSections = (struct sectionRecord *) DataGetPtr(file->fSectData); - - // Initialise the new sections - sections = &file->fSections[file->fNSects]; - file->fNSects += nsects; - for (i = 0, segMap = (struct segmentMap *) seg; i < nsects; i++) - sections[i].fSection = &segMap->sect[i]; - - return true; -} - -// @@@ gvdl: These functions need to be hashed they are -// going to be way too slow for production code. -static const struct nlist * -findSymbolByAddress(const struct fileRecord *file, void *entry) -{ - // not quite so dumb linear search of all symbols - const struct nlist *sym; - int i, nsyms; - - // First try to find the symbol in the most likely place which is the - // extern symbols - sym = file->fLocalSyms; - for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) { - if (sym->n_value == (unsigned long) entry && !(sym->n_type & N_STAB) ) - return sym; - } - - // Didn't find it in the external symbols so try to local symbols before - // giving up. - sym = file->fSymbolBase; - for (i = 0, nsyms = file->fSymtab->nsyms; i < nsyms; i++, sym++) { - if ( (sym->n_type & N_EXT) ) - return NULL; - if ( sym->n_value == (unsigned long) entry && !(sym->n_type & N_STAB) ) - return sym; - } - - return NULL; -} - -struct searchContext { - const char *fSymname; - const char *fStrbase; -}; - -static int symbolSearch(const void *vKey, const void *vSym) -{ - const struct searchContext *key = (const struct searchContext *) vKey; - const struct nlist *sym = (const struct nlist *) vSym; - - return strcmp(key->fSymname, key->fStrbase + sym->n_un.n_strx); -} - -static const struct nlist * -findSymbolByName(struct fileRecord *file, const char *symname) -{ - struct searchContext context; - - context.fSymname = symname; - context.fStrbase = file->fStringBase; - return (struct nlist *) - bsearch(&context, - file->fLocalSyms, file->fNLocal, sizeof(struct nlist), - symbolSearch); -} - -static Boolean -relocateSection(const struct fileRecord *file, struct sectionRecord *sectionRec) -{ - const struct nlist *symbol; - const struct section *section; - struct relocRecord *rec; - struct relocation_info *rinfo; - unsigned long i; - unsigned long r_address, r_symbolnum, r_length; - enum reloc_type_generic r_type; - UInt8 *sectionBase; - void **entry; - - sectionRec->fRelocCache = DataCreate( - sectionRec->fSection->nreloc * sizeof(struct relocRecord)); - if (!sectionRec->fRelocCache) - return false; - - section = sectionRec->fSection; - sectionBase = file->fMachO + section->offset; - - rec = (struct relocRecord *) DataGetPtr(sectionRec->fRelocCache); - rinfo = (struct relocation_info *) (file->fMachO + section->reloff); - for (i = 0; i < section->nreloc; i++, rec++, rinfo++) { - - // Totally uninterested in scattered relocation entries - if ( (rinfo->r_address & R_SCATTERED) ) - continue; - - r_address = rinfo->r_address; - entry = (void **) (sectionBase + r_address); - - /* - * The r_address field is really an offset into the contents of the - * section and must reference something inside the section (Note - * that this is not the case for PPC_RELOC_PAIR entries but this - * can't be one with the above checks). - */ - return_if(r_address >= section->size, false, - ("Invalid relocation entry in %s - not in section\n", file->fPath)); - - // If we don't have a VANILLA entry or the Vanilla entry isn't - // a 'long' then ignore the entry and try the next. - r_type = (enum reloc_type_generic) rinfo->r_type; - r_length = rinfo->r_length; - if (r_type != GENERIC_RELOC_VANILLA || r_length != 2) - continue; - - r_symbolnum = rinfo->r_symbolnum; - - /* - * If rinfo->r_extern is set this relocation entry is an external entry - * else it is a local entry. - */ - if (rinfo->r_extern) { - /* - * This is an external relocation entry. - * r_symbolnum is an index into the input file's symbol table - * of the symbol being refered to. The symbol must be - * undefined to be used in an external relocation entry. - */ - return_if(r_symbolnum >= file->fSymtab->nsyms, false, - ("Invalid relocation entry in %s - no symbol\n", file->fPath)); - - /* - * If this is an indirect symbol resolve indirection (all chains - * of indirect symbols have been resolved so that they point at - * a symbol that is not an indirect symbol). - */ - symbol = file->fSymbolBase; - if ((symbol[r_symbolnum].n_type & N_TYPE) == N_INDR) - r_symbolnum = symbol[r_symbolnum].n_value; - symbol = &symbol[r_symbolnum]; - - return_if(symbol->n_type != (N_EXT | N_UNDF), false, - ("Invalid relocation entry in %s - extern\n", file->fPath)); - } - else { - /* - * If the symbol is not in any section then it can't be a - * pointer to a local segment and I don't care about it. - */ - if (r_symbolnum == R_ABS) - continue; - - // Note segment references are offset by 1 from 0. - return_if(r_symbolnum > file->fNSects, false, - ("Invalid relocation entry in %s - local\n", file->fPath)); - - // Find the symbol, if any, that backs this entry - symbol = findSymbolByAddress(file, *entry); - } - - rec->fValue = *entry; // Save the previous value - rec->fRInfo = rinfo; // Save a pointer to the reloc - rec->fSymbol = symbol; // Record the current symbol - - *entry = (void *) rec; // Save pointer to record in object image - } - - ((struct fileRecord *) file)->fImageDirty = true; - - return true; -} - -static const struct nlist * -findSymbolRefAtLocation(const struct fileRecord *file, - struct sectionRecord *sctn, void **loc) -{ - if (file->fIsKernel) { - if (*loc) - return findSymbolByAddress(file, *loc); - } - else if (sctn->fRelocCache || relocateSection(file, sctn)) { - struct relocRecord *reloc = (struct relocRecord *) *loc; - - if (DataContainsAddr(sctn->fRelocCache, reloc)) - return reloc->fSymbol; - } - - return NULL; -} - -static Boolean -addClass(struct fileRecord *file, - struct metaClassRecord *inClass, - const char *cname) -{ - struct metaClassRecord *newClass = NULL; - struct metaClassRecord **fileClasses = NULL; - int len; - -if (!file->fIsKernel) { // @@@ gvdl: - DEBUG_LOG(("Adding Class %s\n", cname)); -} - - if (!file->fClassList) { - file->fClassList = DataCreate(0); - if (!file->fClassList) - return false; - } - - do { - // Attempt to allocate all necessary resource first - len = strlen(cname) + 1 - + (int) (&((struct metaClassRecord *) 0)->fClassName); - newClass = (struct metaClassRecord *) malloc(len); - if (!newClass) - break; - - if (!DataAddLength(file->fClassList, sizeof(struct metaClassRecord *))) - break; - fileClasses = (struct metaClassRecord **) - (DataGetPtr(file->fClassList) + DataGetLength(file->fClassList)); - - // Copy the meta Class structure and string name into newClass - // and insert object at end of the file->fClassList and sMergeMetaClasses - *newClass = *inClass; - strcpy(newClass->fClassName, cname); - fileClasses[-1] = newClass; - - return true; - } while (0); - - if (fileClasses) - DataAddLength(file->fClassList, -sizeof(struct metaClassRecord *)); - - if (newClass) - free(newClass); - - return false; -} - -static struct metaClassRecord *getClass(DataRef classList, const char *cname) -{ - if (classList) { - int i, nclass; - struct metaClassRecord **classes, *thisClass; - - nclass = DataGetLength(classList) / sizeof(struct metaClassRecord *); - classes = (struct metaClassRecord **) DataGetPtr(classList); - for (i = 0; i < nclass; i++) { - thisClass = classes[i]; - if (!strcmp(thisClass->fClassName, cname)) - return thisClass; - } - } - - return NULL; -} - -// Add the class 'cname' to the list of known OSObject based classes -// Note 'sym' is the .superClass symbol. -static Boolean -recordClass(struct fileRecord *file, const char *cname, const struct nlist *sym) -{ - char *supername = NULL; - const char *classname = NULL; - struct metaClassRecord newClass; - char strbuffer[1024]; - - // Only do the work actual work to find the super class if we are - // not currently working on the kernel. The kernel is the end - // of all superclass chains as by definition the kernel is binary - // compatible with itself. - if (!file->fIsKernel) { - const char *dot; - const struct nlist *supersym; - const struct section *section; - struct sectionRecord *sectionRec; - unsigned char sectind = sym->n_sect; - const char *superstr; - void **location; - - // We can't resolve anything that isn't in a real section - // Note that the sectind is starts at one to make room for the - // NO_SECT flag but the fNSects field isn't offset so we have a - // '>' test. Which means this isn't an OSObject based class - if (sectind == NO_SECT || sectind > file->fNSects) - return true; - - sectionRec = file->fSections + sectind - 1; - section = sectionRec->fSection; - location = (void **) ( file->fMachO + section->offset - + sym->n_value - section->addr ); - - supersym = findSymbolRefAtLocation(file, sectionRec, location); - if (!supersym) - return true; // No superclass symbol then it isn't an OSObject. - - // Find string in file and skip leading '_' and find last '.' - superstr = symbolname(file, supersym) + 1; - dot = strrchr(superstr, '.'); - if (!dot || strcmp(dot, kGMetaSuffix)) - return true; // Not an OSObject superclass so ignore it. - - supername = (char *) malloc(dot - superstr + 1); - strncpy(supername, superstr, dot - superstr); - supername[dot - superstr] = '\0'; - } - - do { - break_if(getClass(file->fClassList, cname), - ("Duplicate class %s in %s\n", cname, file->fPath)); - - snprintf(strbuffer, sizeof(strbuffer), "%s%s", kVTablePrefix, cname); - newClass.fVTableSym = findSymbolByName(file, strbuffer); - break_if(!newClass.fVTableSym, - ("Can't find vtable %s in %s\n", cname, file->fPath)); - - newClass.fFile = file; - newClass.fSuperName = supername; - newClass.fPatchedVTable = NULL; - - // Can't use cname as it may be a stack variable - // However the vtable's string has the class name as a suffix - // so why don't we use that rather than mallocing a string. - classname = symbolname(file, newClass.fVTableSym) - + sizeof(kVTablePrefix) - 1; - break_if(!addClass(file, &newClass, classname), - ("recordClass - no memory?\n")); - - return true; - } while (0); - - if (supername) - free(supername); - - return false; -} - -static Boolean getMetaClassGraph(struct fileRecord *file) -{ - const struct nlist *sym; - const char *strbase; - int i, nsyms; - - // Search the symbol table for the local symbols that are generated - // by the metaclass system. There are three metaclass variables - // that are relevant. - // - // .metaClass A pointer to the meta class structure. - // .superClass A pointer to the super class's meta class. - // .gMetaClass The meta class structure itself. - // ___vt The VTable for the class . - // - // In this code I'm going to search for any symbols that - // ends in kSuperClassSuffix as this indicates this class is a conforming - // OSObject subclass and will need to be patched, and it also - // contains a pointer to the super class's meta class structure. - strbase = file->fStringBase; - sym = file->fLocalSyms; - for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) { - const char *symname; - const char *dot; - char classname[1024]; - unsigned char n_type = sym->n_type & (N_TYPE | N_EXT); - - // Check that the symbols is a global and that it has a name. - if (((N_SECT | N_EXT) != n_type && (N_ABS | N_EXT) != n_type) - || !sym->n_un.n_strx) - continue; - - // Only search from the last '.' in the symbol. - // but skip the leading '_' in all symbols first. - symname = strbase + sym->n_un.n_strx + 1; - dot = strrchr(symname, '.'); - if (!dot || strcmp(dot, kSuperClassSuffix)) - continue; - - // Got a candidate so hand it over for class processing. - return_if(dot - symname >= (int) sizeof(classname), - false, ("Symbol %s is too long\n", symname)); - - bcopy(symname, classname, dot - symname); - classname[dot - symname] = '\0'; - if (!recordClass(file, classname, sym)) - return false; - } - - return_if(!file->fClassList, false, ("Internal error, " - "getMetaClassGraph(%s) found no classes", file->fPath)); - - DEBUG_LOG(("Found %d classes in %x for %s\n", - DataGetLength(file->fClassList)/sizeof(void*), - file->fClassList, file->fPath)); - - return true; -} - -static Boolean mergeOSObjectsForFile(const struct fileRecord *file) -{ - int i, nmerged; - Boolean foundDuplicates = false; - -DEBUG_LOG(("Merging file %s\n", file->fPath)); // @@@ gvdl: - - if (!file->fClassList) - return true; - - if (!sMergedFiles) { - sMergedFiles = DataCreate(0); - return_if(!sMergedFiles, false, - ("Unable to allocate memory metaclass list\n", file->fPath)); - } - - // Check to see if we have already merged this file - nmerged = DataGetLength(sMergedFiles) / sizeof(struct fileRecord *); - for (i = 0; i < nmerged; i++) { - if (file == ((void **) DataGetPtr(sMergedFiles))[i]) - return true; - } - - if (!sMergeMetaClasses) { - sMergeMetaClasses = DataCreate(0); - return_if(!sMergeMetaClasses, false, - ("Unable to allocate memory metaclass list\n", file->fPath)); - } - else { /* perform a duplicate check */ - int i, j, cnt1, cnt2; - struct metaClassRecord **list1, **list2; - - list1 = (struct metaClassRecord **) DataGetPtr(file->fClassList); - cnt1 = DataGetLength(file->fClassList) / sizeof(*list1); - list2 = (struct metaClassRecord **) DataGetPtr(sMergeMetaClasses); - cnt2 = DataGetLength(sMergeMetaClasses) / sizeof(*list2); - - for (i = 0; i < cnt1; i++) { - for (j = 0; j < cnt2; j++) { - if (!strcmp(list1[i]->fClassName, list2[j]->fClassName)) { - errprintf("duplicate class %s in %s & %s\n", - list1[i]->fClassName, - file->fPath, list2[j]->fFile->fPath); - } - } - } - } - if (foundDuplicates) - return false; - - return_if(!DataAppendBytes(sMergedFiles, &file, sizeof(file)), false, - ("Unable to allocate memory to merge %s\n", file->fPath)); - - return_if(!DataAppendData(sMergeMetaClasses, file->fClassList), false, - ("Unable to allocate memory to merge %s\n", file->fPath)); - - if (file == sKernelFile) - sMergedKernel = true; - - return true; -} - -// Returns a pointer to the base of the section offset by the sections -// base address. The offset is so that we can add nlist::n_values directly -// to this address and get a valid pointer in our memory. -static unsigned char * -getSectionForSymbol(const struct fileRecord *file, const struct nlist *symb, - void ***endP) -{ - const struct section *section; - unsigned char sectind; - unsigned char *base; - - sectind = symb->n_sect; // Default to symbols section - if ((symb->n_type & N_TYPE) == N_ABS && file->fIsKernel) { - // Absolute symbol so we have to iterate over our sections - for (sectind = 1; sectind <= file->fNSects; sectind++) { - unsigned long start, end; - - section = file->fSections[sectind - 1].fSection; - start = section->addr; - end = start + section->size; - if (start <= symb->n_value && symb->n_value < end) { - // Found the relevant section - break; - } - } - } - - // Is the vtable in a valid section? - return_if(sectind == NO_SECT || sectind > file->fNSects, - (unsigned char *) -1, - ("%s isn't a valid kext, bad section reference\n", file->fPath)); - - section = file->fSections[sectind - 1].fSection; - - // for when we start walking the vtable so compute offset's now. - base = file->fMachO + section->offset; - *endP = (void **) (base + section->size); - - return base - section->addr; // return with addr offset -} - -static Boolean resolveKernelVTable(struct metaClassRecord *metaClass) -{ - const struct fileRecord *file; - struct patchRecord *patchedVTable; - void **curEntry, **vtableEntries, **endSection; - unsigned char *sectionBase; - struct patchRecord *curPatch; - int classSize; - - // Should never occur but it doesn't cost us anything to check. - if (metaClass->fPatchedVTable) - return true; - -DEBUG_LOG(("Kernel vtable %s\n", metaClass->fClassName)); // @@@ gvdl: - - // Do we have a valid vtable to patch? - return_if(!metaClass->fVTableSym, - false, ("Internal error - no class vtable symbol?\n")); - - file = metaClass->fFile; - - // If the metaClass we are being to ask is in the kernel then we - // need to do a quick scan to grab the fPatchList in a reliable format - // however we don't need to check the superclass in the kernel - // as the kernel vtables are always correct wrt themselves. - // Note this ends the superclass chain recursion. - return_if(!file->fIsKernel, - false, ("Internal error - resolveKernelVTable not kernel\n")); - - if (file->fNoKernelExecutable) { - // Oh dear attempt to map the kernel's VM into my memory space - return_if(file->fNoKernelExecutable, false, - ("Internal error - fNoKernelExecutable not implemented yet\n")); - } - - // We are going to need the base and the end - sectionBase = getSectionForSymbol(file, metaClass->fVTableSym, &endSection); - if (-1 == (long) sectionBase) - return false; - - vtableEntries = (void **) (sectionBase + metaClass->fVTableSym->n_value); - curEntry = vtableEntries + kVTablePreambleLen; - for (classSize = 0; curEntry < endSection && *curEntry; classSize++) - curEntry++; - - return_if(*curEntry, false, ("Bad kernel image, short section\n")); - - patchedVTable = (struct patchRecord *) - malloc((classSize + 1) * sizeof(struct patchRecord)); - return_if(!patchedVTable, false, ("resolveKernelVTable - no memory\n")); - - // Copy the vtable of this class into the patch table - curPatch = patchedVTable; - curEntry = vtableEntries + kVTablePreambleLen; - for (; *curEntry; curEntry++, curPatch++) { - curPatch->fSymbol = (struct nlist *) - findSymbolByAddress(file, *curEntry); - curPatch->fType = kSymbolLocal; - } - - // Tag the end of the patch vtable - curPatch->fSymbol = NULL; - metaClass->fPatchedVTable = patchedVTable; - - return true; -} - -// reloc->fPatch must contain a valid pointer on entry -static struct nlist * -getNewSymbol(struct fileRecord *file, - const struct relocRecord *reloc, const char *supername) -{ - unsigned int size, i, namelen; - struct nlist **sym; - struct nlist *msym; - const char *strbase; - struct relocation_info *rinfo; - long strx; - - if (!file->fNewSymbols) { - file->fNewSymbols = DataCreate(0); - return_if(!file->fNewSymbols, NULL, - ("Unable to allocate new symbol table for %s\n", file->fPath)); - } - - // Make sure we have a string table as well for the new symbol - if (!file->fNewStrings) { - file->fNewStrings = DataCreate(0); - return_if(!file->fNewStrings, NULL, - ("Unable to allocate string table for %s\n", file->fPath)); - } - - rinfo = (struct relocation_info *) reloc->fRInfo; - size = DataGetLength(file->fNewSymbols) / sizeof(struct nlist *); - sym = (const struct nlist **) DataGetPtr(file->fNewSymbols); - // remember that the n_strx for new symbols names is negated - strbase = (const char *) - DataGetPtr(file->fNewStrings) - file->fSymtab->strsize; - for (i = 0; i < size; i++, sym++) { - const char *symname = strbase - (*sym)->n_un.n_strx; - - if (!strcmp(symname, supername)) { - rinfo->r_symbolnum = i + file->fSymtab->nsyms; - file->fSymbolsDirty = true; - return *sym; - } - } - - // Assert that this is a vaild symbol. I need this condition to be true - // for the later code to make non-zero. So the first time through I'd - // better make sure that it is 0. - return_if(reloc->fSymbol->n_sect, NULL, - ("Undefined symbol entry with non-zero section %s:%s\n", - file->fPath, symbolname(file, reloc->fSymbol))); - - msym = (struct nlist *) malloc(sizeof(struct nlist)); - return_if(!msym, - NULL, ("Unable to create symbol table entry for %s\n", file->fPath)); - - // If we are here we didn't find the symbol so create a new one now - if (!DataAppendBytes(file->fNewSymbols, &msym, sizeof(msym))) { - free(msym); - return_if(true, - NULL, ("Unable to grow symbol table for %s\n", file->fPath)); - } - - namelen = strlen(supername) + 1; - strx = DataGetLength(file->fNewStrings); - if (!DataAppendBytes(file->fNewStrings, supername, namelen)) { - free(msym); - DataAddLength(file->fNewSymbols, -sizeof(struct nlist)); // Undo harm - return_if(true, NULL, - ("Unable to grow string table for %s\n", file->fPath)); - } - - // Offset the string index by the original string table size - // and negate the address to indicate that this is a 'new' symbol - msym->n_un.n_strx = -(strx + file->fSymtab->strsize); - msym->n_type = (N_EXT | N_UNDF); - msym->n_sect = NO_SECT; - msym->n_desc = 0; - msym->n_value = 0; - - // Mark the old symbol as being potentially deletable I can use the - // n_sect field as the input symbol must be of type N_UNDF which means - // that the n_sect field must be set to NO_SECT otherwise it is an - // in valid input file. - ((struct nlist *) reloc->fSymbol)->n_un.n_strx - = -reloc->fSymbol->n_un.n_strx; - ((struct nlist *) reloc->fSymbol)->n_sect = (unsigned char) -1; - - rinfo->r_symbolnum = i + file->fSymtab->nsyms; - file->fSymbolsDirty = true; - return msym; -} - -static struct nlist * -fixOldSymbol(struct fileRecord *file, - const struct relocRecord *reloc, const char *supername) -{ - unsigned int namelen; - struct nlist *sym = (struct nlist *) reloc->fSymbol; - const char *oldname = symbolname(file, sym); - - // assert(sym->n_un.n_strx >= 0); - - namelen = strlen(supername); - if (namelen < strlen(oldname)) { - // Overwrite old string in string table - strcpy((char *) oldname, supername); - } - else { - long strx; - - // Make sure we have a string table as well for this symbol - if (!file->fNewStrings) { - file->fNewStrings = DataCreate(0); - return_if(!file->fNewStrings, NULL, - ("Unable to allocate string table for %s\n", file->fPath)); - } - - // Find the end of the fNewStrings data structure; - strx = DataGetLength(file->fNewStrings); - return_if(!DataAppendBytes(file->fNewStrings, supername, namelen + 1), - NULL, ("Unable to grow string table for %s\n", file->fPath)); - - // now add the current table size to the offset - sym->n_un.n_strx = strx + file->fSymtab->strsize; - } - - // Mark the symbol as having been processed by negating it. - // Also note that we have dirtied the file and need to repair the - // symbol table. - sym->n_un.n_strx = -sym->n_un.n_strx; - file->fSymbolsDirty = true; - return sym; -} - -static enum patchState -symbolCompare(const struct fileRecord *file, - const struct nlist *classsym, - const char *supername) -{ - const char *classname; - - - // Check to see if the target function is locally defined - // if it is then we can assume this is a local vtable override - if ((classsym->n_type & N_TYPE) != N_UNDF) - return kSymbolLocal; - - // Check to see if both symbols point to the same symbol name - // if so then we are still identical. - classname = symbolname(file, classsym); - if (!strcmp(classname, supername)) - return kSymbolIdentical; - - // Right now we know that the target's vtable entry is different from the - // superclass' vtable entry. This means that we will have to apply a - // patch to the current entry, however before returning lets check to - // see if we have a _RESERVEDnnn field 'cause we can use this as a - // registration point that must align between vtables. - if (!strncmp(supername, kReservedPrefix, sizeof(kReservedPrefix) - 1)) - return kSymbolMismatch; - - // OK, we have a superclass difference where the superclass doesn't - // reference a pad function so assume that the superclass is correct. - if (!strncmp(classname, kReservedPrefix, sizeof(kReservedPrefix) - 1)) - return kSymbolPadUpdate; - else - return kSymbolSuperUpdate; -} - -static Boolean patchVTable(struct metaClassRecord *metaClass) -{ - struct metaClassRecord *super = NULL; - struct fileRecord *file; - struct patchRecord *patchedVTable; - struct relocRecord **curReloc, **vtableRelocs, **endSection; - unsigned char *sectionBase; - int classSize; - - // Should never occur but it doesn't cost us anything to check. - if (metaClass->fPatchedVTable) - return true; - - // Do we have a valid vtable to patch? - return_if(!metaClass->fVTableSym, - false, ("Internal error - no class vtable symbol?\n")); - - file = metaClass->fFile; - - // If the metaClass we are being to ask is in the kernel then we - // need to do a quick scan to grab the fPatchList in a reliable format - // however we don't need to check the superclass in the kernel - // as the kernel vtables are always correct wrt themselves. - // Note this ends the superclass chain recursion. - return_if(file->fIsKernel, - false, ("Internal error - patchVTable shouldn't used for kernel\n")); - - if (!metaClass->fSuperName) - return false; - - // The class isn't in the kernel so make sure that the super class - // is patched before patching ouselves. - super = getClass(sMergeMetaClasses, metaClass->fSuperName); - return_if(!super, false, ("Can't find superclass for %s : %s \n", - metaClass->fClassName, metaClass->fSuperName)); - - // Superclass recursion if necessary - if (!super->fPatchedVTable) { - Boolean res; - - if (super->fFile->fIsKernel) - res = resolveKernelVTable(super); - else - res = patchVTable(super); - if (!res) - return false; - } - -DEBUG_LOG(("Patching %s\n", metaClass->fClassName)); // @@@ gvdl: - - // We are going to need the base and the end - - sectionBase = getSectionForSymbol(file, - metaClass->fVTableSym, (void ***) &endSection); - if (-1 == (long) sectionBase) - return false; - - vtableRelocs = (struct relocRecord **) - (sectionBase + metaClass->fVTableSym->n_value); - curReloc = vtableRelocs + kVTablePreambleLen; - for (classSize = 0; curReloc < endSection && *curReloc; classSize++) - curReloc++; - - return_if(*curReloc, false, - ("%s isn't a valid kext, short section\n", file->fPath)); - - patchedVTable = (struct patchRecord *) - malloc((classSize + 1) * sizeof(struct patchRecord)); - return_if(!patchedVTable, false, ("patchedVTable - no memory\n")); - - do { - struct patchRecord *curPatch; - struct nlist *symbol; - - curPatch = patchedVTable; - curReloc = vtableRelocs + kVTablePreambleLen; - - // Grab the super table patches if necessary - // Can't be patching a kernel table as we don't walk super - // class chains in the kernel symbol space. - if (super && super->fPatchedVTable) { - const struct patchRecord *spp; - - spp = super->fPatchedVTable; - - for ( ; spp->fSymbol; curReloc++, spp++, curPatch++) { - const char *supername = - symbolname(super->fFile, spp->fSymbol); - - symbol = (struct nlist *) (*curReloc)->fSymbol; - - curPatch->fType = symbolCompare(file, symbol, supername); - switch (curPatch->fType) { - case kSymbolIdentical: - case kSymbolLocal: - break; - - case kSymbolSuperUpdate: - symbol = getNewSymbol(file, (*curReloc), supername); - break; - - case kSymbolPadUpdate: - symbol = fixOldSymbol(file, (*curReloc), supername); - break; - - case kSymbolMismatch: - errprintf("%s is not compatible with its %s superclass, " - "broken superclass?\n", - metaClass->fClassName, super->fClassName); - goto abortPatch; - - default: - errprintf("Internal error - unknown patch type\n"); - goto abortPatch; - } - if (symbol) { - curPatch->fSymbol = symbol; - (*curReloc)->fSymbol = symbol; - } - else - goto abortPatch; - } - } - - // Copy the remainder of this class' vtable into the patch table - for (; *curReloc; curReloc++, curPatch++) { - // Local reloc symbols - curPatch->fType = kSymbolLocal; - curPatch->fSymbol = (struct nlist *) (*curReloc)->fSymbol; - } - - // Tag the end of the patch vtable - curPatch->fSymbol = NULL; - - metaClass->fPatchedVTable = patchedVTable; - return true; - } while(0); - -abortPatch: - if (patchedVTable) - free(patchedVTable); - - return false; -} - -static Boolean growImage(struct fileRecord *file, vm_size_t delta) -{ -#if !KERNEL - file->fMachOSize += delta; - return (file->fMachO + file->fMachOSize <= file->fPadEnd); -#else /* KERNEL */ - vm_address_t startMachO, endMachO, endMap; - vm_offset_t newMachO; - vm_size_t newsize; - unsigned long i, nsect, nclass = 0; - struct metaClassRecord **classes = NULL; - struct sectionRecord *section; - kern_return_t ret; - - startMachO = (vm_address_t) file->fMachO; - endMachO = startMachO + file->fMachOSize + delta; - endMap = (vm_address_t) file->fMap + file->fMapSize; - - // Do we have room in the current mapped image - if (endMachO < round_page(endMap)) { - file->fMachOSize += delta; - return true; - } - - newsize = endMachO - startMachO; - if (newsize < round_page(file->fMapSize)) { - // We have room in the map if we shift the macho image within the - // current map. We will have to patch up pointers into the object. - newMachO = (vm_offset_t) file->fMap; - bcopy((char *) startMachO, (char *) newMachO, file->fMachOSize); - } - else if (file->fIsKmem) { - // kmem_alloced mapping so we can try a kmem_realloc - ret = kmem_realloc(kernel_map, - (vm_address_t) file->fMap, - (vm_size_t) file->fMapSize, - &newMachO, - newsize); - if (KERN_SUCCESS != ret) - return false; - - // If the mapping didn't move then just return - if ((vm_address_t) file->fMap == newMachO) { - file->fMachOSize = file->fMapSize = newsize; - return true; - } - - // We have relocated the kmem image so we are going to have to - // move all of the pointers into the image around. - } - else { - // The image doesn't have room for us and I can't kmem_realloc - // then I just have to bite the bullet and copy the object code - // into a bigger memory segment - ret = kmem_alloc(kernel_map, &newMachO, newsize); - - if (KERN_SUCCESS != ret) - return false; - bcopy((char *) startMachO, (void *) newMachO, file->fMachOSize); - file->fIsKmem = true; - } - - - file->fMap = file->fMachO = (unsigned char *) newMachO; - file->fMapSize = newsize; - file->fMachOSize += delta; // Increment the image size - - // If we are here then we have shifted the object image in memory - // I really should change all of my pointers into the image to machO offsets - // but I have run out of time. So I'm going to very quickly go over the - // cached data structures and add adjustments to the addresses that are - // affected. I wonder how long it will take me to get them all. - // - // For every pointer into the MachO I need to add an adjustment satisfying - // the following simultanous equations - // addr_old = macho_old + fixed_offset - // addr_new = macho_new + fixed_offset therefore: - // addr_new = addr_old + (macho_new - macho_old) -#define REBASE(addr, delta) ( ((vm_address_t) (addr)) += (delta) ) - delta = newMachO - startMachO; - - // Rebase the cached in object 'struct symtab_command' pointer - REBASE(file->fSymtab, delta); - - // Rebase the cached in object 'struct nlist' pointer for all symbols - REBASE(file->fSymbolBase, delta); - - // Rebase the cached in object 'struct nlist' pointer for local symbols - REBASE(file->fLocalSyms, delta); - - // Rebase the cached in object 'char' pointer for the string table - REBASE(file->fStringBase, delta); - - // Ok now we have to go over all of the relocs one last time - // to clean up the pad updates which had their string index negated - // to indicate that we have finished with them. - section = file->fSections; - for (i = 0, nsect = file->fNSects; i < nsect; i++, section++) - REBASE(section->fSection, delta); - - // We only ever grow images that contain class lists so dont bother - // the check if file->fClassList is non-zero 'cause it can't be - // assert(file->fClassList); - nclass = DataGetLength(file->fClassList) - / sizeof(struct metaClassRecord *); - classes = (struct metaClassRecord **) DataGetPtr(file->fClassList); - for (i = 0; i < nclass; i++) { - struct patchRecord *patch; - - for (patch = classes[i]->fPatchedVTable; patch->fSymbol; patch++) { - vm_address_t symAddr = (vm_address_t) patch->fSymbol; - if (symAddr >= startMachO && symAddr < endMachO) - REBASE(patch->fSymbol, delta); - } - } - - -#undef REBASE - - return true; - -#endif /* KERNEL */ -} - -static Boolean -prepareFileForLink(struct fileRecord *file) -{ - unsigned long i, last, numnewsyms, newsymsize, newstrsize; - struct sectionRecord *section; - struct nlist **symp, *sym; - - // If we didn't even do a pseudo 'relocate' and dirty the image - // then we can just return now. - if (!file->fImageDirty) - return true; - -DEBUG_LOG(("Linking 2 %s\n", file->fPath)); // @@@ gvdl: - - // We have to go over all of the relocs to repair the damage - // that we have done to the image when we did our 'relocation' - section = file->fSections; - for (i = 0, last = file->fNSects; i < last; i++, section++) { - unsigned char *sectionBase; - struct relocRecord *rec; - unsigned long j, nreloc; - - if (section->fRelocCache) { - sectionBase = file->fMachO + section->fSection->offset; - nreloc = section->fSection->nreloc; - rec = (struct relocRecord *) DataGetPtr(section->fRelocCache); - - // We will need to repair the reloc list - for (j = 0; j < nreloc; j++, rec++) { - void **entry; - struct nlist *sym; - - // Repair Damage to object image - entry = (void **) (sectionBase + rec->fRInfo->r_address); - *entry = rec->fValue; - - // Check if the symbol that this relocation entry points - // to is marked as erasable - sym = (struct nlist *) rec->fSymbol; - if (sym && sym->n_type == (N_EXT | N_UNDF) - && sym->n_sect == (unsigned char) -1) { - // clear mark now - sym->n_un.n_strx = -sym->n_un.n_strx; - sym->n_sect = NO_SECT; - } - } - - // Clean up the fRelocCache we don't need it any more. - DataRelease(section->fRelocCache); - section->fRelocCache = 0; - } - } - file->fImageDirty = false; // Image is clean - - // If we didn't dirty the symbol table then just return - if (!file->fSymbolsDirty) - return true; - - // calculate total file size increase and check against padding - numnewsyms = (file->fNewSymbols)? DataGetLength(file->fNewSymbols) : 0; - numnewsyms /= sizeof(struct nlist *); - newsymsize = numnewsyms * sizeof(struct nlist); - newstrsize = (file->fNewStrings)? DataGetLength(file->fNewStrings) : 0; - newstrsize = (newstrsize + 3) & ~3; // Round to nearest word - - return_if(!growImage(file, newsymsize + newstrsize), - false, ("Unable to patch the extension, no memory\n", file->fPath)); - - // Push out the new symbol table if necessary - if (numnewsyms) { - caddr_t base; - - // Move the string table out of the way of the grown symbol table - // Don't forget the '\0' from end of string table. - base = (caddr_t) file->fStringBase; - bcopy(base, base + newsymsize, file->fSymtab->strsize); - file->fStringBase += newsymsize; - file->fSymtab->stroff += newsymsize; - - // Now append the new symbols to the symbol table. - base = (caddr_t) file->fSymbolBase - + file->fSymtab->nsyms * sizeof(struct nlist); - symp = (struct nlist **) DataGetPtr(file->fNewSymbols); - for (i = 0; i < numnewsyms; i++, base += sizeof(struct nlist), symp++) - bcopy(*symp, base, sizeof(struct nlist)); - file->fSymtab->nsyms += numnewsyms; - - DataRelease(file->fNewSymbols); - file->fNewSymbols = 0; - } - - // Push out the new string table if necessary - if (newstrsize) { - caddr_t base = (caddr_t) file->fStringBase + file->fSymtab->strsize; - unsigned long actuallen = DataGetLength(file->fNewStrings); - - // Set the last word in string table to zero before copying data - *((unsigned long *) ((char *) base + newstrsize - 4)) = 0; - - // Now append the new strings to the end of the file - bcopy((caddr_t) DataGetPtr(file->fNewStrings), base, actuallen); - - file->fSymtab->strsize += newstrsize; - - DataRelease(file->fNewStrings); - file->fNewStrings = 0; - } - - // Repair the symbol table string index values - // I used negative strx's to indicate symbol has been processed - sym = file->fSymbolBase; - for (i = 0, last = file->fSymtab->nsyms; i < last; i++, sym++) { - if (sym->n_un.n_strx < 0) { - if ( sym->n_type != (N_EXT | N_UNDF) - || (unsigned char) -1 != sym->n_sect) - sym->n_un.n_strx = -sym->n_un.n_strx; - else { - // This symbol isn't being used by any vtable's reloc so - // convert it into an N_ABS style of symbol, remove the - // external bit and null out the symbol name. - bzero(sym, sizeof(*sym)); - sym->n_type = N_ABS; /* type flag, see below */ - } - } - } - file->fSymbolsDirty = false; - - return true; -} - -Boolean -#if KERNEL -kld_file_map(const char *pathName, - unsigned char *map, - size_t mapSize, - Boolean isKmem) -#else -kld_file_map(const char *pathName) -#endif /* KERNEL */ -{ - struct fileRecord file, *fp = 0; - - // Already done no need to repeat - fp = getFile(pathName); - if (fp) - return true; - - bzero(&file, sizeof(file)); - file.fPath = pathName; - -#if KERNEL - file.fMap = map; - file.fMapSize = mapSize; - file.fIsKmem = isKmem; -#else - if (!mapObjectFile(&file)) - return false; -#endif /* KERNEL */ - - do { - const struct machOMapping { - struct mach_header h; - struct load_command c[1]; - } *machO; - const struct load_command *cmd; - const struct nlist *sym; - unsigned int i, firstlocal, nsyms; - unsigned long strsize; - const char *strbase; - Boolean foundOSObject; - - if (!findBestArch(&file)) - break; - - machO = (const struct machOMapping *) file.fMachO; - if (file.fMachOSize < machO->h.sizeofcmds) - break; - - // If the file type is MH_EXECUTE then this must be a kernel - // as all Kernel extensions must be of type MH_OBJECT - for (i = 0, cmd = &machO->c[0]; i < machO->h.ncmds; i++) { - if (cmd->cmd == LC_SEGMENT) { - return_if(!parseSegments(&file, (struct segment_command *) cmd), - false, ("%s isn't a valid mach-o, bad segment\n", - file.fPath)); - } - else if (cmd->cmd == LC_SYMTAB) - file.fSymtab = (struct symtab_command *) cmd; - - cmd = (struct load_command *) ((UInt8 *) cmd + cmd->cmdsize); - } - break_if(!file.fSymtab, - ("%s isn't a valid mach-o, no symbols\n", file.fPath)); - - // we found a link edit segment so recompute the bases - if (file.fSymbolBase) { - struct segment_command *link = - (struct segment_command *) file.fSymbolBase; - - file.fSymbolBase = (struct nlist *) - (link->vmaddr + (file.fSymtab->symoff - link->fileoff)); - file.fStringBase = (char *) - (link->vmaddr + (file.fSymtab->stroff - link->fileoff)); - break_if( ( (caddr_t) file.fStringBase + file.fSymtab->strsize - > (caddr_t) link->vmaddr + link->vmsize ), - ("%s isn't a valid mach-o le, bad symbols\n", file.fPath)); - } - else { - file.fSymbolBase = (struct nlist *) - (file.fMachO + file.fSymtab->symoff); - file.fStringBase = (char *) - (file.fMachO + file.fSymtab->stroff); - break_if( ( file.fSymtab->stroff + file.fSymtab->strsize - > file.fMachOSize ), - ("%s isn't a valid mach-o, bad symbols\n", file.fPath)); - } - - // If this file the kernel and do we have an executable image - file.fIsKernel = (MH_EXECUTE == machO->h.filetype); - file.fNoKernelExecutable = (vm_page_size == file.fSymtab->symoff) - && (file.fSections[0].fSection->size == 0); - - // Search for the first non-stab symbol in table - strsize = file.fSymtab->strsize; - strbase = file.fStringBase; - sym = file.fSymbolBase; - firstlocal = 0; - foundOSObject = false; - for (i = 0, nsyms = file.fSymtab->nsyms; i < nsyms; i++, sym++) { - if ((unsigned long) sym->n_un.n_strx > strsize) - break; - - // Find the first exported symbol - if ( !file.fLocalSyms && (sym->n_type & N_EXT) ) { - file.fLocalSyms = sym; - firstlocal = i; - } - - // Find the a OSObject based subclass by searching for symbols - // that have a suffix of '.superClass' - if (!foundOSObject - && ((sym->n_type & (N_TYPE | N_EXT)) == (N_SECT | N_EXT) - || (sym->n_type & (N_TYPE | N_EXT)) == (N_ABS | N_EXT)) - && sym->n_un.n_strx) { - const char *dot; - - // Only search from the last '.' in the symbol. - // but skip the leading '_' in all symbols first. - dot = strrchr(strbase + sym->n_un.n_strx + 1, '.'); - if (dot && !strcmp(dot, kSuperClassSuffix)) - foundOSObject = true; - } - - // Find the last local symbol - if ( !file.fNLocal && sym->n_type == (N_EXT | N_UNDF) ) - file.fNLocal = i - firstlocal; - - } - break_if(i < nsyms, - ("%s isn't a valid mach-o, bad symbol strings\n", file.fPath)); - - break_if(!file.fLocalSyms, ("%s has no symbols?\n", file.fPath)); - - // If we don't have any undefined symbols then all symbols - // must be local so just compute it now if necessary. - if ( !file.fNLocal ) - file.fNLocal = i - firstlocal; - - fp = addFile(&file); - if (!fp) - break; - - if (foundOSObject && !getMetaClassGraph(fp)) - break; - - if (file.fIsKernel) - sKernelFile = fp; -#if KERNEL - if (!sKernelFile) { - extern struct mach_header _mh_execute_header; - extern struct segment_command *getsegbyname(char *seg_name); - - struct segment_command *sg; - size_t kernelSize; - Boolean ret; - - sg = (struct segment_command *) getsegbyname(kLinkEditSegName); - break_if(!sg, ("Can't find kernel link edit segment\n")); - - kernelSize = sg->vmaddr + sg->vmsize - (size_t) &_mh_execute_header; - ret = kld_file_map(kld_basefile_name, - (unsigned char *) &_mh_execute_header, kernelSize, - /* isKmem */ false); - break_if(!ret, ("kld can't map kernel file")); - } -#endif /* KERNEL */ - - return true; - } while(0); - - removeFile(&file); - - return false; -} - -void *kld_file_getaddr(const char *pathName, long *size) -{ - struct fileRecord *file = getFile(pathName); - - if (!file) - return 0; - - if (size) - *size = file->fMachOSize; - - return file->fMachO; -} - -void *kld_file_lookupsymbol(const char *pathName, const char *symname) -{ - struct fileRecord *file = getFile(pathName); - const struct nlist *sym; - const struct section *section; - unsigned char *sectionBase; - unsigned char sectind; - - return_if(!file, - NULL, ("Unknown file %s\n", pathName)); - - sym = findSymbolByName(file, symname); - - // May be a non-extern symbol so look for it there - if (!sym) { - const char *strbase; - unsigned int i, nsyms; - - sym = file->fSymbolBase; - strbase = file->fStringBase; - for (i = 0, nsyms = file->fSymtab->nsyms; i < nsyms; i++, sym++) { - if ( (sym->n_type & N_EXT) ) { - sym = 0; - break; // Terminate search when we hit an extern - } - if ( (sym->n_type & N_STAB) ) - continue; - if ( !strcmp(symname, strbase + sym->n_un.n_strx) ) - break; - } - } - - return_if(!sym, - NULL, ("Unknown symbol %s in %s\n", symname, pathName)); - - // Is the vtable in a valid section? - sectind = sym->n_sect; - return_if(sectind == NO_SECT || sectind > file->fNSects, NULL, - ("Malformed object file, invalid section reference for %s in %s\n", - symname, pathName)); - - section = file->fSections[sectind - 1].fSection; - sectionBase = file->fMachO + section->offset - section->addr; - - return (void *) (sectionBase + sym->n_value); -} - -Boolean kld_file_merge_OSObjects(const char *pathName) -{ - struct fileRecord *file = getFile(pathName); - - return_if(!file, - false, ("Internal error - unable to find file %s\n", pathName)); - - return mergeOSObjectsForFile(file); -} - -Boolean kld_file_patch_OSObjects(const char *pathName) -{ - struct fileRecord *file = getFile(pathName); - struct metaClassRecord **classes; - unsigned long i, last; - - return_if(!file, - false, ("Internal error - unable to find file %s\n", pathName)); - -DEBUG_LOG(("Patch file %s\n", pathName)); // @@@ gvdl: - - // If we don't have any classes we can return now. - if (!file->fClassList) - return true; - - // If we haven't alread merged the kernel then do it now - if (!sMergedKernel && sKernelFile) - mergeOSObjectsForFile(sKernelFile); - return_if(!sMergedKernel, false, ("Internal error no kernel?\n")); - - if (!mergeOSObjectsForFile(file)) - return false; - - // Patch all of the classes in this executable - last = DataGetLength(file->fClassList) / sizeof(void *); - classes = (struct metaClassRecord **) DataGetPtr(file->fClassList); - for (i = 0; i < last; i++) { - if (!patchVTable(classes[i])) - return false; - } - - return true; -} - -Boolean kld_file_prepare_for_link() -{ - if (sMergedFiles) { - unsigned long i, nmerged = 0; - struct fileRecord **files; - - // Check to see if we have already merged this file - nmerged = DataGetLength(sMergedFiles) / sizeof(struct fileRecord *); - files = (struct fileRecord **) DataGetPtr(sMergedFiles); - for (i = 0; i < nmerged; i++) { - if (!prepareFileForLink(files[i])) - return false; - } - } - - // Clear down the meta class table and merged file lists - DataRelease(sMergeMetaClasses); - DataRelease(sMergedFiles); - sMergedFiles = sMergeMetaClasses = NULL; - sMergedKernel = false; - - return true; -} - -void kld_file_cleanup_all_resources() -{ - unsigned long i, nfiles; - -#if KERNEL // @@@ gvdl: - // Debugger("kld_file_cleanup_all_resources"); -#endif - - if (!sFilesTable || !(nfiles = DataGetLength(sFilesTable))) - return; // Nothing to do just return now - - nfiles /= sizeof(struct fileRecord *); - for (i = 0; i < nfiles; i++) - removeFile(((void **) DataGetPtr(sFilesTable))[i]); - - // Don't really have to clean up anything more as the whole - // malloc engine is going to be released and I couldn't be bothered. -} - -#if !KERNEL -Boolean kld_file_debug_dump(const char *pathName, const char *outName) -{ - const struct fileRecord *file = getFile(pathName); - int fd; - Boolean ret = false; - - return_if(!file, false, ("Unknown file %s for dumping\n", pathName)); - - fd = open(outName, O_WRONLY|O_CREAT|O_TRUNC, 0666); - return_if(-1 == fd, false, ("Can't create output file %s - %s(%d)\n", - outName, strerror(errno), errno)); - - do { - break_if(-1 == write(fd, file->fMachO, file->fMachOSize), - ("Can't dump output file %s - %s(%d)\n", - outName, strerror(errno), errno)); - ret = true; - } while(0); - - close(fd); - - return ret; -} -#endif /* !KERNEL */ -