-/*
- * 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 <mach-o/fat.h>
-#include <mach-o/loader.h>
-#include <mach-o/nlist.h>
-#include <mach-o/reloc.h>
-
-#if KERNEL
-
-#include <stdarg.h>
-#include <string.h>
-
-#include <sys/systm.h>
-
-#include <libkern/OSTypes.h>
-
-#include <libsa/stdlib.h>
-#include <libsa/mach/mach.h>
-
-#include "mach_loader.h"
-
-#include <vm/vm_kern.h>
-
-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);
-
-__private_extern__ char *strstr(const char *in, const char *str);
-
-#else /* !KERNEL */
-
-#include <unistd.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/vm.h>
-
-#include <mach/mach.h>
-#include <mach/mach_error.h>
-
-#include <mach-o/arch.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-#define PAGE_SIZE vm_page_size
-#define PAGE_MASK (PAGE_SIZE - 1)
-
-#endif /* KERNEL */
-
-#include "kld_patch.h"
-#include "c++rem3.h"
-
-#if 0
-#define DIE() do { for (;;) ; } while(0)
-
-#if KERNEL
-# define LOG_DELAY() /* IODelay(200000) */
-# define DEBUG_LOG(x) do { IOLog x; LOG_DELAY(); } while(0)
-#else
-# define LOG_DELAY()
-# define DEBUG_LOG(x) do { printf x; } while(0)
-#endif
-
-#else
-
-#define DIE()
-#define LOG_DELAY()
-#define DEBUG_LOG(x)
-
-#endif
-
-// OSObject symbol prefixes and suffixes
-#define kCPPSymbolPrefix "_Z"
-#define kVTablePrefix "_" kCPPSymbolPrefix "TV"
-#define kOSObjPrefix "_" kCPPSymbolPrefix "N"
-#define kReservedNamePrefix "_RESERVED"
-#define k29SuperClassSuffix "superClass"
-#define k31SuperClassSuffix "10superClassE"
-#define kGMetaSuffix "10gMetaClassE"
-#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;
- unsigned char *fMap, *fMachO, *fPadEnd;
- DataRef fClassList;
- DataRef fSectData;
- DataRef fNewSymbols, fNewStringBlocks;
- DataRef fSym2Strings;
- struct symtab_command *fSymtab;
- struct sectionRecord *fSections;
- struct segment_command *fLinkEditSeg;
- const char **fSymbToStringTable;
- char *fStringBase;
- struct nlist *fSymbolBase;
- const struct nlist *fLocalSyms;
- unsigned int fNSects;
- int fNLocal;
- Boolean fIsKernel, fNoKernelExecutable, fIsKmem;
- Boolean fImageDirty, fSymbolsDirty;
- Boolean fRemangled, fFoundOSObject;
- const char fPath[1];
-};
-
-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__ unsigned char *DataGetEndPtr(DataRef data)
-{
- return data->fData + data->fLength;
-}
-
-static __inline__ unsigned long DataRemaining(DataRef data)
-{
- return data->fCapacity - data->fLength;
-}
-
-static __inline__ Boolean DataContainsAddr(DataRef data, void *vAddr)
-{
- vm_offset_t offset = (vm_address_t) vAddr;
-
- if (!data)
- return false;
-
- offset = (vm_address_t) vAddr - (vm_address_t) data->fData;
- return (offset < data->fLength);
-}
-
-static Boolean DataEnsureCapacity(DataRef data, unsigned long capacity)
-{
- // Don't bother to ever shrink a data object.
- if (capacity > data->fCapacity) {
- unsigned char *newData;
-
- capacity += kDataCapacityIncrement - 1;
- capacity &= ~(kDataCapacityIncrement - 1);
- newData = (unsigned char *) realloc(data->fData, capacity);
- if (!newData)
- return false;
-
- bzero(newData + data->fCapacity, capacity - data->fCapacity);
- data->fData = newData;
- data->fCapacity = capacity;
- }
-
- return true;
-}
-
-static __inline__ Boolean DataSetLength(DataRef data, unsigned long length)
-{
- if (DataEnsureCapacity(data, length)) {
- data->fLength = length;
- return true;
- }
- else
- return false;
-}
-
-static __inline__ Boolean DataAddLength(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 DataRef DataCreate(unsigned long capacity)
-{
- DataRef data = (DataRef) malloc(sizeof(Data));
-
- if (data) {
- if (!capacity)
- data->fCapacity = kDataCapacityIncrement;
- else {
- data->fCapacity = capacity + 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 = 0;
- }
- return data;
-}
-
-static void DataRelease(DataRef data)
-{
- if (data) {
- if (data->fData)
- free(data->fData);
- data->fData = 0;
- free(data);
- }
-}
-
-static __inline__ const char *
-symNameByIndex(const struct fileRecord *file, unsigned int symInd)
-{
- return file->fSymbToStringTable[symInd];
-}
-
-static __inline__ const char *
-symbolname(const struct fileRecord *file, const struct nlist *sym)
-{
- unsigned int index;
-
- index = sym - file->fSymbolBase;
- if (index < file->fSymtab->nsyms)
- return symNameByIndex(file, index);
-
- if (-1 == sym->n_un.n_strx)
- return (const char *) sym->n_value;
-
- // If the preceding tests fail then we have a getNewSymbol patch and
- // the file it refers to has already been patched as the n_strx is set
- // to -1 temporarily while we are still processing a file.
- // Once we have finished with a file then we repair the 'strx' offset
- // to be valid for the repaired file's string table.
- return file->fStringBase + sym->n_un.n_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, const char *path)
-{
- struct fileRecord *newFile;
-
- if (!sFilesTable) {
- sFilesTable = DataCreate(0);
- if (!sFilesTable)
- return NULL;
- }
-
- newFile = (struct fileRecord *)
- malloc(sizeof(struct fileRecord) + strlen(path));
- if (!newFile)
- return NULL;
-
- if (!DataAppendBytes(sFilesTable, &newFile, sizeof(newFile))) {
- free(newFile);
- return NULL;
- }
-
- bcopy(file, newFile, sizeof(struct fileRecord) - 1);
- strcpy((char *) newFile->fPath, path);
-
- return newFile;
-}
-
-// @@@ gvdl: need to clean up the sMergeMetaClasses
-// @@@ gvdl: I had better fix the object file up again
-static void unmapFile(struct fileRecord *file)
-{
- 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->fSym2Strings) {
- DataRelease(file->fSym2Strings);
- file->fSym2Strings = 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;
- }
-}
-
-static void removeFile(struct fileRecord *file)
-{
- if (file->fClassList) {
- DataRelease(file->fClassList);
- file->fClassList = 0;
- }
-
- unmapFile(file);
-
- free(file);
-}
-
-#if !KERNEL
-static Boolean
-mapObjectFile(struct fileRecord *file, const char *pathName)
-{
- Boolean result = false;
- static unsigned char *sFileMapBaseAddr = 0;
-
- 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(pathName, O_RDONLY, 0);
- return_if(fd == -1, false, ("Can't open %s for reading - %s\n",
- pathName, 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",
- pathName, 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, const char *pathName)
-{
- 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", pathName));
-
-#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", pathName));
-
- 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", pathName));
-
- 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", pathName, myArch->name));
- return_if(arch->offset + arch->size > file->fMapSize,
- false, ("%s's %s arch is incomplete\n", pathName, 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", pathName));
-
- 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 (!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;
-}
-
-static Boolean
-remangleExternSymbols(struct fileRecord *file, const char *pathName)
-{
- const struct nlist *sym;
- int i, nsyms, len;
- DataRef strings = NULL;
-
- DEBUG_LOG(("Remangling %s\n", pathName));
-
- file->fNewStringBlocks = DataCreate(0);
- return_if(!file->fNewStringBlocks, false,
- ("Unable to allocate new string table for %s\n", pathName));
-
- nsyms = file->fSymtab->nsyms;
- for (i = 0, sym = file->fSymbolBase; i < nsyms; i++, sym++) {
- Rem3Return ret;
- const char *symname;
- char *newname;
- unsigned char n_type = sym->n_type;
-
- // Not an external symbol or it is a stab in any case don't bother
- if ((n_type ^ N_EXT) & (N_STAB | N_EXT))
- continue;
-
- symname = symNameByIndex(file, i);
-
-tryRemangleAgain:
- if (!strings) {
- strings = DataCreate(16 * 1024); // Arbitrary block size
- return_if(!strings, false,
- ("Unable to allocate new string block for %s\n", pathName));
- }
-
- len = DataRemaining(strings);
- newname = DataGetEndPtr(strings);
- ret = rem3_remangle_name(newname, &len, symname);
- switch (ret) {
- case kR3InternalNotRemangled:
- errprintf("Remangler fails on %s in %s\n", symname, pathName);
- /* No break */
- case kR3NotRemangled:
- break;
-
- case kR3Remangled:
- file->fSymbToStringTable[i] = newname;
- file->fRemangled = file->fSymbolsDirty = true;
- DataAddLength(strings, len + 1); // returns strlen
- break;
-
- case kR3BufferTooSmallRemangled:
- return_if(!DataAppendBytes
- (file->fNewStringBlocks, &strings, sizeof(strings)),
- false, ("Unable to allocate string table for %s\n", pathName));
- strings = NULL;
- goto tryRemangleAgain;
-
- case kR3BadArgument:
- default:
- return_if(true, false,
- ("Internal error - remangle of %s\n", pathName));
- }
- }
-
- if (strings) {
- return_if(!DataAppendBytes
- (file->fNewStringBlocks, &strings, sizeof(strings)),
- false, ("Unable to allocate string table for %s\n", pathName));
- }
-
- return true;
-}
-
-static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
-{
- const struct nlist *sym;
- unsigned int i, firstlocal, nsyms;
- unsigned long strsize;
- const char *strbase;
- Boolean foundOSObject, found295CPP;
-
- // we found a link edit segment so recompute the bases
- if (file->fLinkEditSeg) {
- struct segment_command *link = file->fLinkEditSeg;
-
- file->fSymbolBase = (struct nlist *)
- (link->vmaddr + (file->fSymtab->symoff - link->fileoff));
- file->fStringBase = (char *)
- (link->vmaddr + (file->fSymtab->stroff - link->fileoff));
- return_if( ( (caddr_t) file->fStringBase + file->fSymtab->strsize
- > (caddr_t) link->vmaddr + link->vmsize ), false,
- ("%s isn't a valid mach-o le, bad symbols\n", pathName));
- }
- else {
- file->fSymbolBase = (struct nlist *)
- (file->fMachO + file->fSymtab->symoff);
- file->fStringBase = (char *)
- (file->fMachO + file->fSymtab->stroff);
- return_if( ( file->fSymtab->stroff + file->fSymtab->strsize
- > file->fMachOSize ), false,
- ("%s isn't a valid mach-o, bad symbols\n", pathName));
- }
-
- nsyms = file->fSymtab->nsyms;
-
- // If this file the kernel and do we have an executable image
- file->fNoKernelExecutable = (vm_page_size == file->fSymtab->symoff)
- && (file->fSections[0].fSection->size == 0);
-
- // Generate a table of pointers to strings indexed by the symbol number
-
- file->fSym2Strings = DataCreate(nsyms * sizeof(const char *));
- DataSetLength(file->fSym2Strings, nsyms * sizeof(const char *));
- return_if(!file->fSym2Strings, false,
- ("Unable to allocate memory - symbol string trans\n", pathName));
- file->fSymbToStringTable = (const char **) DataGetPtr(file->fSym2Strings);
-
- // Search for the first non-stab symbol in table
- strsize = file->fSymtab->strsize;
- strbase = file->fStringBase;
- firstlocal = 0;
- found295CPP = foundOSObject = false;
- for (i = 0, sym = file->fSymbolBase; i < nsyms; i++, sym++) {
- long strx = sym->n_un.n_strx;
- const char *symname = strbase + strx;
- unsigned char n_type;
-
- return_if(((unsigned long) strx > strsize), false,
- ("%s has an illegal string offset in symbol %d\n", pathName, i));
-
- // Load up lookup symbol look table with sym names
- file->fSymbToStringTable[i] = symname;
-
- n_type = sym->n_type & (N_TYPE | N_EXT);
-
- // Find the first exported symbol
- if ( !firstlocal && (n_type & N_EXT) ) {
- firstlocal = i;
- file->fLocalSyms = sym;
- }
-
- // Find the a OSObject based subclass by searching for symbols
- // that have a suffix of '10superClassE'
- symname++; // Skip leading '_'
-
- if (!foundOSObject
- && (n_type == (N_SECT | N_EXT) || n_type == (N_ABS | N_EXT))
- && strx) {
- const char *suffix, *endSym;
-
- endSym = symname + strlen(symname);
-
- // Find out if this symbol has the superclass suffix.
- if (symname[0] == kCPPSymbolPrefix[0]
- && symname[1] == kCPPSymbolPrefix[1]) {
-
- suffix = endSym - sizeof(k31SuperClassSuffix) + 1;
-
- // Check for a gcc3 OSObject subclass
- if (suffix > symname
- && !strcmp(suffix, k31SuperClassSuffix))
- foundOSObject = true;
- }
- else {
- suffix = endSym - sizeof(k29SuperClassSuffix);
-
- // Check for a gcc295 OSObject subclass
- if (suffix > symname
- && ('.' == *suffix || '$' == *suffix)
- && !strcmp(suffix+1, k29SuperClassSuffix)) {
- found295CPP = foundOSObject = true;
- }
- else if (!found295CPP) {
- // Finally just check if we need to remangle
- symname++; // skip leading '__'
- while (*symname) {
- if ('_' == *symname++ && '_' == *symname++) {
- found295CPP = true;
- break;
- }
- }
- }
- }
- }
- else if (sym->n_type == (N_EXT | N_UNDF)) {
- if ( !file->fNLocal) // Find the last local symbol
- file->fNLocal = i - firstlocal;
- if (!found295CPP) {
- symname++; // Skip possible second '_' at start.
- while (*symname) {
- if ('_' == *symname++ && '_' == *symname++) {
- found295CPP = true;
- break;
- }
- }
- }
- }
- // Note symname is trashed at this point
- }
- return_if(i < nsyms, false,
- ("%s isn't a valid mach-o, bad symbol strings\n", pathName));
-
- return_if(!file->fLocalSyms, false, ("%s has no symbols?\n", pathName));
-
- // 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;
-
- file->fFoundOSObject = foundOSObject;
-
- if (found295CPP && !remangleExternSymbols(file, pathName))
- return false;
-
- 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 struct fileRecord *fFile;
-};
-
-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 + 1, symbolname(key->fFile, sym) + 1);
-}
-
-static const struct nlist *
-findSymbolByName(struct fileRecord *file, const char *symname)
-{
- if (file->fRemangled) {
- // @@@ gvdl: Performance problem
- // Linear search as we don't sort after remangling
- const struct nlist *sym;
- int i = file->fLocalSyms - file->fSymbolBase;
- int nLocal = file->fNLocal + i;
-
- for (sym = file->fLocalSyms; i < nLocal; i++, sym++)
- if (!strcmp(symNameByIndex(file, i) + 1, symname + 1))
- return sym;
- return NULL;
- }
- else {
- struct searchContext context;
-
- context.fSymname = symname;
- context.fFile = file;
- 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
- }
-
- DataSetLength(sectionRec->fRelocCache, i * sizeof(struct relocRecord));
- ((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)
-{
- Boolean result = false;
- struct metaClassRecord *newClass = NULL;
- struct metaClassRecord **fileClasses = NULL;
- int len;
-
- 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 result;
-}
-
-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 <cname>10superClassE symbol.
-static Boolean
-recordClass(struct fileRecord *file, const char *cname, const struct nlist *sym)
-{
- Boolean result = false;
- char *supername = NULL;
- const char *classname = NULL;
- struct metaClassRecord newClass;
- char strbuffer[1024];
-
- // Only do the work to find the super class if we are
- // not currently working on the kernel. The kernel is the end
- // of all superclass chains by definition as the kernel must be binary
- // compatible with itself.
- if (!file->fIsKernel) {
- const char *suffix;
- const struct nlist *supersym;
- const struct section *section;
- struct sectionRecord *sectionRec;
- unsigned char sectind = sym->n_sect;
- const char *superstr;
- void **location;
- int snamelen;
-
- // 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) {
- result = true;
- goto finish;
- }
- 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) {
- result = true; // No superclass symbol then it isn't an OSObject.
- goto finish;
- }
-
- // Find string in file and skip leading '_' and then find the suffix
- superstr = symbolname(file, supersym) + 1;
- suffix = superstr + strlen(superstr) - sizeof(kGMetaSuffix) + 1;
- if (suffix <= superstr || strcmp(suffix, kGMetaSuffix)) {
- result = true; // Not an OSObject superclass so ignore it..
- goto finish;
- }
-
- // Got a candidate so hand it over for class processing.
- snamelen = suffix - superstr - sizeof(kOSObjPrefix) + 2;
- supername = (char *) malloc(snamelen + 1);
- bcopy(superstr + sizeof(kOSObjPrefix) - 2, supername, snamelen);
- supername[snamelen] = '\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"));
-
- supername = NULL;
- result = true;
- } while (0);
-
-finish:
- if (supername)
- free(supername);
-
- return result;
-}
-
-
-static Boolean getMetaClassGraph(struct fileRecord *file)
-{
- const struct nlist *sym;
- 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.
- //
- // <ClassName>.metaClass A pointer to the meta class structure.
- // <ClassName>.superClass A pointer to the super class's meta class.
- // <ClassName>.gMetaClass The meta class structure itself.
- // ___vt<ClassName> The VTable for the class <ClassName>.
- //
- // In this code I'm going to search for any symbols that
- // ends in k31SuperClassSuffix 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.
- sym = file->fLocalSyms;
- for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) {
- const char *symname;
- const char *suffix;
- char classname[1024];
- unsigned char n_type = sym->n_type & (N_TYPE | N_EXT);
- int cnamelen;
-
- // 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 *sep* in the symbol.
- // but skip the leading '_' in all symbols first.
- symname = symbolname(file, sym) + 1;
- if (symname[0] != kCPPSymbolPrefix[0]
- || symname[1] != kCPPSymbolPrefix[1])
- continue;
-
- suffix = symname + strlen(symname) - sizeof(k31SuperClassSuffix) + 1;
- if (suffix <= symname || strcmp(suffix, k31SuperClassSuffix))
- continue;
-
- // Got a candidate so hand it over for class processing.
- cnamelen = suffix - symname - sizeof(kOSObjPrefix) + 2;
- return_if(cnamelen + 1 >= (int) sizeof(classname),
- false, ("Symbol %s is too long", symname));
-
- bcopy(symname + sizeof(kOSObjPrefix) - 2, classname, cnamelen);
- classname[cnamelen] = '\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 %ld classes in %p 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;
-}
-
-static const char *addNewString(struct fileRecord *file,
- const char *strname, int namelen)
-{
- DataRef strings = 0;
- const char *newStr;
-
- namelen++; // Include terminating '\0';
-
- // Make sure we have a string table as well for this symbol
- if (file->fNewStringBlocks) {
- DataRef *blockTable = (DataRef *) DataGetPtr(file->fNewStringBlocks);
- int index = DataGetLength(file->fNewStringBlocks) / sizeof(DataRef*);
- strings = blockTable[index - 1];
- if (DataRemaining(strings) < namelen)
- strings = 0;
- }
- else
- {
- file->fNewStringBlocks = DataCreate(0);
- return_if(!file->fNewStringBlocks, NULL,
- ("Unable to allocate new string table %s\n", file->fPath));
- }
-
- if (!strings) {
- int size = (namelen + 1023) & ~1023;
- if (size < 16 * 1024)
- size = 16 * 1024;
- strings = DataCreate(size);
- return_if(!strings, NULL,
- ("Unable to allocate new string block %s\n", file->fPath));
- return_if(
- !DataAppendBytes(file->fNewStringBlocks, &strings, sizeof(strings)),
- false, ("Unable to allocate string table for %s\n", file->fPath));
- }
-
- newStr = DataGetEndPtr(strings);
- DataAppendBytes(strings, strname, namelen);
- return newStr;
-}
-
-// reloc->fPatch must contain a valid pointer
-static struct nlist *
-getNewSymbol(struct fileRecord *file,
- const struct relocRecord *reloc, const char *supername)
-{
- unsigned int size, i;
- struct nlist **sym;
- struct nlist *msym;
- struct relocation_info *rinfo;
- const char *newStr;
-
- if (!file->fNewSymbols) {
- file->fNewSymbols = DataCreate(0);
- return_if(!file->fNewSymbols, NULL,
- ("Unable to allocate new symbol table for %s\n", file->fPath));
- }
-
- rinfo = (struct relocation_info *) reloc->fRInfo;
- size = DataGetLength(file->fNewSymbols) / sizeof(struct nlist *);
- sym = (struct nlist **) DataGetPtr(file->fNewSymbols);
- for (i = 0; i < size; i++, sym++) {
- int symnum = i + file->fSymtab->nsyms;
- newStr = symNameByIndex(file, symnum);
- if (!strcmp(newStr, supername)) {
- rinfo->r_symbolnum = symnum;
- 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)));
-
- // If we are here we didn't find the symbol so create a new one now
- msym = (struct nlist *) malloc(sizeof(struct nlist));
- return_if(!msym,
- NULL, ("Unable to create symbol table entry for %s", file->fPath));
- return_if(!DataAppendBytes(file->fNewSymbols, &msym, sizeof(msym)),
- NULL, ("Unable to grow symbol table for %s\n", file->fPath));
-
- newStr = addNewString(file, supername, strlen(supername));
- if (!newStr)
- return NULL;
- // If we are here we didn't find the symbol so create a new one now
- return_if(!DataAppendBytes(file->fSym2Strings, &newStr, sizeof(newStr)),
- NULL, ("Unable to grow symbol table for %s\n", file->fPath));
- file->fSymbToStringTable = (const char **) DataGetPtr(file->fSym2Strings);
-
- // 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 = -1;
- msym->n_type = (N_EXT | N_UNDF);
- msym->n_sect = NO_SECT;
- msym->n_desc = 0;
- msym->n_value = (unsigned long) newStr;
-
- // 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);
-
- sym->n_un.n_strx = -sym->n_un.n_strx;
- if (oldname && namelen < strlen(oldname))
- {
- // Overwrite old string in string table
- strcpy((char *) oldname, supername);
- file->fSymbolsDirty = true;
- return sym;
- }
-
- oldname = addNewString(file, supername, namelen);
- if (!oldname)
- return NULL;
-
- file->fSymbToStringTable[sym - file->fSymbolBase] = oldname;
- 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;
-
- // 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 (strstr(supername, kReservedNamePrefix))
- 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 (strstr(classname, kReservedNamePrefix))
- 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 superclass, "
- "%s superclass changed?\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, last = 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)) {
- DEBUG_LOG(("Growing image %s by moving\n", file->fPath));
-
- // 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;
- }
-
- DEBUG_LOG(("Growing image %s by reallocing\n", file->fPath));
- // We have relocated the kmem image so we are going to have to
- // move all of the pointers into the image around.
- }
- else {
- DEBUG_LOG(("Growing image %s by allocating\n", file->fPath));
- // 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, last = file->fNSects; i < last; 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);
- last = DataGetLength(file->fClassList)
- / sizeof(struct metaClassRecord *);
- classes = (struct metaClassRecord **) DataGetPtr(file->fClassList);
- for (i = 0; i < last; i++) {
- struct patchRecord *patch;
-
- for (patch = classes[i]->fPatchedVTable; patch->fSymbol; patch++) {
- vm_address_t symAddr = (vm_address_t) patch->fSymbol;
-
- // Only need to rebase if the symbol is part of the image
- // If this is a new symbol then it was independantly allocated
- if (symAddr >= startMachO && symAddr < endMachO)
- REBASE(patch->fSymbol, delta);
- }
- }
-
- // Finally rebase all of the string table pointers
- last = file->fSymtab->nsyms;
- for (i = 0; i < last; i++)
- REBASE(file->fSymbToStringTable[i], 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;
- DataRef newStrings, *stringBlocks;
-
- // 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) {
- // It is in use so we better clear the mark
- 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
- if (file->fNewSymbols) {
- numnewsyms = DataGetLength(file->fNewSymbols);
- symp = (struct nlist **) DataGetPtr(file->fNewSymbols);
- }
- else {
- numnewsyms = 0;
- symp = 0;
- }
- numnewsyms /= sizeof(struct nlist *);
- file->fSymtab->nsyms += numnewsyms;
-
- // old sting size + 30% rounded up to nearest page
- newstrsize = file->fSymtab->strsize * 21 / 16;
- newstrsize = (newstrsize + PAGE_MASK) & ~PAGE_MASK;
- newStrings = DataCreate(newstrsize);
- return_if(!newStrings, false,
- ("Unable to allocate a copy aside buffer, no memory\n"));
-
- newsymsize = numnewsyms * sizeof(struct nlist);
- file->fStringBase += newsymsize;
- file->fSymtab->stroff += newsymsize;
-
- last = file->fSymtab->nsyms - numnewsyms;
- newstrsize = 0;
- DataAppendBytes(newStrings, &newstrsize, 4); // Leading nuls
- sym = file->fSymbolBase;
-
- // Pre-compute an already offset new symbol pointer. The offset is the
- // orignal symbol table.
- symp -= last;
- for (i = 0; i < file->fSymtab->nsyms; i++, sym++) {
- const char *str = symNameByIndex(file, i);
- int len = strlen(str) + 1;
- unsigned int strx;
-
- // Rebase sym in the new symbol region
- if (i >= last)
- sym = symp[i];
-
- if (sym->n_un.n_strx < 0 && sym->n_type == (N_EXT | N_UNDF)
- && (unsigned char) -1 == sym->n_sect) {
- // after patching we find that this symbol is no longer in
- // use. So invalidate it by converting it into an N_ABS
- // symbol, remove the external bit and null out the name.
- bzero(sym, sizeof(*sym));
- sym->n_type = N_ABS;
- }
- else {
- // Repair the symbol for the getNewSymbol case.
- if (-1 == sym->n_un.n_strx)
- sym->n_value = 0;
-
- // Record the offset of the string in the new table
- strx = DataGetLength(newStrings);
- return_if(!DataAppendBytes(newStrings, str, len), false,
- ("Unable to append string, no memory\n"));
-
- sym->n_un.n_strx = strx;
- file->fSymbToStringTable[i] = file->fStringBase + strx;
- }
- }
-
- // Don't need the new strings any more
- last = DataGetLength(file->fNewStringBlocks) / sizeof(DataRef);
- stringBlocks = (DataRef *) DataGetPtr(file->fNewStringBlocks);
- for (i = 0; i < last; i++)
- DataRelease(stringBlocks[i]);
-
- DataRelease(file->fNewStringBlocks);
- file->fNewStringBlocks = 0;
-
- newstrsize = DataGetLength(newStrings);
- newstrsize = (newstrsize + 3) & ~3; // Round to nearest word
- return_if(
- !growImage(file, newsymsize + newstrsize - file->fSymtab->strsize),
- false, ("Unable to patch the extension, no memory\n", file->fPath));
-
- // Push out the new symbol table if necessary
- if (numnewsyms) {
- caddr_t base;
-
- // Append the new symbols to the original symbol table.
- base = (caddr_t) file->fSymbolBase
- + (file->fSymtab->nsyms - numnewsyms) * 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));
-
- DataRelease(file->fNewSymbols);
- file->fNewSymbols = 0;
- }
-
- // Push out the new string table if necessary
- if (newStrings) {
- unsigned long *base = (unsigned long *) file->fStringBase;
- unsigned long actuallen = DataGetLength(newStrings);
-
- // Set the last word in string table to zero before copying data
- base[(newstrsize / sizeof(unsigned long)) - 1] = 0;
-
- // Now copy the new strings back to the end of the file
- bcopy((caddr_t) DataGetPtr(newStrings), file->fStringBase, actuallen);
-
- file->fSymtab->strsize = newstrsize;
-
- DataRelease(newStrings);
- }
-
- 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));
-
-#if KERNEL
- file.fMap = map;
- file.fMapSize = mapSize;
- file.fIsKmem = isKmem;
-#else
- if (!mapObjectFile(&file, pathName))
- return false;
-#endif /* KERNEL */
-
- do {
- const struct machOMapping {
- struct mach_header h;
- struct load_command c[1];
- } *machO;
- const struct load_command *cmd;
- int i;
-
- if (!findBestArch(&file, pathName))
- break;
-
- machO = (const struct machOMapping *) file.fMachO;
- if (file.fMachOSize < machO->h.sizeofcmds)
- break;
-
- file.fIsKernel = (MH_EXECUTE == machO->h.filetype);
-
- // 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_SYMTAB)
- file.fSymtab = (struct symtab_command *) cmd;
- else if (cmd->cmd == LC_SEGMENT) {
- struct segment_command *seg = (struct segment_command *) cmd;
- int nsects = seg->nsects;
-
- if (nsects)
- return_if(!parseSegments(&file, seg),
- false, ("%s isn't a valid mach-o, bad segment",
- pathName));
- else if (file.fIsKernel) {
-#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))
- file.fLinkEditSeg = seg;
-#endif
- }
- }
-
- cmd = (struct load_command *) ((UInt8 *) cmd + cmd->cmdsize);
- }
- break_if(!file.fSymtab,
- ("%s isn't a valid mach-o, no symbols\n", pathName));
-
- if (!parseSymtab(&file, pathName))
- break;
-
- fp = addFile(&file, pathName);
- if (!fp)
- break;
-
- if (file.fFoundOSObject && !getMetaClassGraph(fp))
- break;
-
- if (file.fIsKernel)
- sKernelFile = fp;
-
-#if KERNEL
- // Automatically load the kernel's link edit segment if we are
- // attempting to load a driver.
- 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);
-
- // Failure path, then clean up
- if (fp)
- // @@@ gvdl: for the time being leak the file ref in the file table
- removeFile(fp);
- else
- unmapFile(&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) {
- unsigned int i, nsyms;
-
- sym = file->fSymbolBase;
- 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, symNameByIndex(file, i)) )
- 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]);
-
- DataRelease(sFilesTable);
- sFilesTable = NULL;
-
- // 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
-#if 0
-static const struct fileRecord *sortFile;
-static int symCompare(const void *vSym1, const void *vSym2)
-{
- const struct nlist *sym1 = vSym1;
- const struct nlist *sym2 = vSym2;
-
- {
- unsigned int ind1, ind2;
-
- ind1 = sym1->n_type & N_TYPE;
- ind2 = sym2->n_type & N_TYPE;
- if (ind1 != ind2) {
- // if sym1 is undefined then sym1 must come later than sym2
- if (ind1 == N_UNDF)
- return 1;
- // if sym2 is undefined then sym1 must come earlier than sym2
- if (ind2 == N_UNDF)
- return -1;
- /* drop out if neither are undefined */
- }
- }
-
- {
- const struct fileRecord *file = sortFile;
- const char *name1, *name2;
-
- name1 = file->fStringBase + sym1->n_un.n_strx;
- name2 = file->fStringBase + sym2->n_un.n_strx;
- return strcmp(name1, name2);
- }
-}
-#endif /* 0 */
-
-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 {
-#if 0
- // Sorting doesn't work until I fix the relocs too?
-
- // sort the symbol table appropriately
- unsigned int nsyms = file->fSymtab->nsyms
- - (file->fLocalSyms - file->fSymbolBase);
- sortFile = file;
- heapsort((void *) file->fLocalSyms, nsyms, sizeof(struct nlist),
- symCompare);
-#endif
-
- 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 */
-