]> git.saurik.com Git - apple/xnu.git/blobdiff - libsa/kld_patch.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / libsa / kld_patch.c
index 862be7f9646ec497ff17d59a38916115f33d5113..18892a019d7e03bef891e4341af146ac533e9909 100644 (file)
 #include <mach-o/loader.h>
 #include <mach-o/nlist.h>
 #include <mach-o/reloc.h>
+#if !KERNEL
+#include <mach-o/swap.h>
+#include <libkern/OSByteOrder.h>
+#endif
 
 #if KERNEL
 
 #include <stdarg.h>
-#include <string.h>
+//#include <string.h>
 
 #include <sys/systm.h>
 
@@ -52,12 +56,11 @@ 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);
+extern void kld_error_vprintf(const char *format, va_list ap);
 
 __private_extern__ char *strstr(const char *in, const char *str);
+extern struct mach_header _mh_execute_header;
+extern struct segment_command *getsegbyname(char *seg_name);   // 32 bit only
 
 #else /* !KERNEL */
 
@@ -164,7 +167,7 @@ typedef struct Data {
 } Data, *DataRef;
 
 struct sectionRecord {
-    const struct section *fSection;
+    const struct section *fSection;    // 32 bit mach object section
     DataRef fRelocCache;
 };
 
@@ -178,6 +181,7 @@ enum patchState {
 
 struct patchRecord {
     struct nlist *fSymbol;
+    const struct fileRecord *fFile;
     enum patchState fType;
 };
 
@@ -205,6 +209,7 @@ struct fileRecord {
     DataRef fSym2Strings;
     struct symtab_command *fSymtab;
     struct sectionRecord *fSections;
+    vm_offset_t fVMAddr, fVMEnd;
     struct segment_command *fLinkEditSeg;
     const char **fSymbToStringTable;
     char *fStringBase;
@@ -212,24 +217,30 @@ struct fileRecord {
     const struct nlist *fLocalSyms;
     unsigned int fNSects;
     int fNLocal;
-    Boolean fIsKernel, fNoKernelExecutable, fIsKmem;
+    Boolean fIsKernel, fIsReloc, fIsIncrLink, fNoKernelExecutable, fIsKmem;
     Boolean fImageDirty, fSymbolsDirty;
     Boolean fRemangled, fFoundOSObject;
     Boolean fIgnoreFile;
+#if !KERNEL
+    Boolean fSwapped;
+#endif
     const char fPath[1];
 };
 
 static DataRef sFilesTable;
 static struct fileRecord *sKernelFile;
 
-static DataRef sMergedFiles;
-static DataRef sMergeMetaClasses;
-static Boolean sMergedKernel;
+static DataRef    sMergedFiles;
+static DataRef    sMergeMetaClasses;
+static Boolean    sMergedKernel;
+#if !KERNEL
+static const NXArchInfo * sPreferArchInfo;
+#endif
+static const struct nlist *
+findSymbolByName(struct fileRecord *file, const char *symname);
 
 static void errprintf(const char *fmt, ...)
 {
-    extern void kld_error_vprintf(const char *format, va_list ap);
-
     va_list ap;
 
     va_start(ap, fmt);
@@ -368,6 +379,10 @@ symbolname(const struct fileRecord *file, const struct nlist *sym)
     unsigned int index;
 
     index = sym - file->fSymbolBase;
+
+    if (index && !sym->n_un.n_strx)
+       return file->fStringBase + sym->n_value;
+
     if (index < file->fSymtab->nsyms)
         return symNameByIndex(file,  index);
 
@@ -585,8 +600,114 @@ mapObjectFile(struct fileRecord *file, const char *pathName)
     close(fd);
     return result;
 }
+
+void
+kld_set_architecture(const NXArchInfo * arch)
+{
+    sPreferArchInfo = arch;
+}
+
+// This function can only operate on 32 bit mach-o files
+Boolean
+kld_macho_swap(struct mach_header * mh)
+{
+    struct segment_command * seg;
+    struct section *        section;
+    CFIndex                 ncmds, cmd, sect;
+    enum NXByteOrder        hostOrder = NXHostByteOrder();
+
+    if (MH_CIGAM != mh->magic)
+       return (false);
+
+    swap_mach_header(mh, hostOrder);
+
+    ncmds = mh->ncmds;
+    seg = (struct segment_command *)(mh + 1);
+    for (cmd = 0;
+            cmd < ncmds;
+            cmd++, seg = (struct segment_command *)(((vm_offset_t)seg) + seg->cmdsize))
+    {
+        if (OSSwapConstInt32(LC_SYMTAB) == seg->cmd) {
+           swap_symtab_command((struct symtab_command *) seg, hostOrder);
+           swap_nlist((struct nlist *) (((vm_offset_t) mh) + ((struct symtab_command *) seg)->symoff),
+                      ((struct symtab_command *) seg)->nsyms, hostOrder);
+           continue;
+       }
+        if (OSSwapConstInt32(LC_SEGMENT) != seg->cmd) {
+           swap_load_command((struct load_command *) seg, hostOrder);
+            continue;
+       }
+       swap_segment_command(seg, hostOrder);
+       swap_section((struct section *) (seg + 1), seg->nsects, hostOrder);
+
+       section = (struct section *) (seg + 1);
+       for (sect = 0; sect < seg->nsects; sect++, section++) {
+           if (section->nreloc)
+               swap_relocation_info((struct relocation_info *) (((vm_offset_t) mh) + section->reloff),
+                                     section->nreloc, hostOrder);
+       }
+    }
+
+    return (true);
+}
+
+// This function can only operate on 32 bit mach-o files
+void
+kld_macho_unswap(struct mach_header * mh, Boolean didSwap, int symbols)
+{
+    // symbols ==  0 => everything
+    // symbols ==  1 => just nlists
+    // symbols == -1 => everything but nlists
+
+    struct segment_command * seg;
+    struct section *        section;
+    unsigned long           cmdsize;
+    CFIndex                 ncmds, cmd, sect;
+    enum NXByteOrder        hostOrder = (NXHostByteOrder() == NX_LittleEndian)
+                                       ? NX_BigEndian : NX_LittleEndian;
+    if (!didSwap)
+       return;
+
+    ncmds = mh->ncmds;
+    seg = (struct segment_command *)(mh + 1);
+    for (cmd = 0;
+            cmd < ncmds;
+            cmd++, seg = (struct segment_command *)(((vm_offset_t)seg) + cmdsize))
+    {
+       cmdsize = seg->cmdsize;
+        if (LC_SYMTAB == seg->cmd) {
+           if (symbols >= 0)
+               swap_nlist((struct nlist *) (((vm_offset_t) mh) + ((struct symtab_command *) seg)->symoff),
+                       ((struct symtab_command *) seg)->nsyms, hostOrder);
+           if (symbols > 0)
+               break;
+           swap_symtab_command((struct symtab_command *) seg, hostOrder);
+           continue;
+       }
+       if (symbols > 0)
+           continue;
+        if (LC_SEGMENT != seg->cmd) {
+           swap_load_command((struct load_command *) seg, hostOrder);
+            continue;
+       }
+
+       section = (struct section *) (seg + 1);
+       for (sect = 0; sect < seg->nsects; sect++, section++) {
+           if (section->nreloc)
+               swap_relocation_info((struct relocation_info *) (((vm_offset_t) mh) + section->reloff),
+                                     section->nreloc, hostOrder);
+       }
+       swap_section((struct section *) (seg + 1), seg->nsects, hostOrder);
+       swap_segment_command(seg, hostOrder);
+    }
+    if (symbols <= 0)
+       swap_mach_header(mh, hostOrder);
+}
+
 #endif /* !KERNEL */
 
+// Note: This functions is only called from kld_file_map()
+// This function can only operate on 32 bit mach-o files
 static Boolean findBestArch(struct fileRecord *file, const char *pathName)
 {
     unsigned long magic;
@@ -626,21 +747,21 @@ static Boolean findBestArch(struct fileRecord *file, const char *pathName)
        unsigned long i;
        struct fat_arch *arch;
 
-       fat->nfat_arch = NXSwapBigLongToHost(fat->nfat_arch);
+       fat->nfat_arch = OSSwapBigToHostInt32(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);
+           arch[i].cputype    = OSSwapBigToHostInt32(arch[i].cputype);
+           arch[i].cpusubtype = OSSwapBigToHostInt32(arch[i].cpusubtype);
+           arch[i].offset     = OSSwapBigToHostInt32(arch[i].offset);
+           arch[i].size       = OSSwapBigToHostInt32(arch[i].size);
+           arch[i].align      = OSSwapBigToHostInt32(arch[i].align);
        }
 
-       magic = NXSwapBigLongToHost(fat->magic);
+       magic = OSSwapBigToHostInt32(fat->magic);
     }
 
     // Now see if we can find any valid architectures
@@ -654,7 +775,11 @@ static Boolean findBestArch(struct fileRecord *file, const char *pathName)
        return_if(file->fMapSize < fatsize,
            false, ("%s isn't a valid fat file\n", pathName));
 
-       myArch = NXGetLocalArchInfo();
+       if (sPreferArchInfo)
+           myArch = sPreferArchInfo;
+       else
+           myArch = NXGetLocalArchInfo();
+    
        arch = NXFindBestFatArch(myArch->cputype, myArch->cpusubtype,
                (struct fat_arch *) &fat[1], fat->nfat_arch);
        return_if(!arch,
@@ -666,6 +791,10 @@ static Boolean findBestArch(struct fileRecord *file, const char *pathName)
        magic = ((const struct mach_header *) file->fMachO)->magic;
     }
 
+    file->fSwapped = kld_macho_swap((struct mach_header *) file->fMachO);
+    if (file->fSwapped)
+       magic = ((const struct mach_header *) file->fMachO)->magic;
+
 #endif /* KERNEL */
 
     return_if(magic != MH_MAGIC,
@@ -674,6 +803,7 @@ static Boolean findBestArch(struct fileRecord *file, const char *pathName)
     return true;
 }
 
+// This function can only operate on segments from 32 bit mach-o files
 static Boolean
 parseSegments(struct fileRecord *file, struct segment_command *seg)
 {
@@ -699,7 +829,10 @@ parseSegments(struct fileRecord *file, struct segment_command *seg)
     sections = &file->fSections[file->fNSects];
     file->fNSects += nsects;
     for (i = 0, segMap = (struct segmentMap *) seg; i < nsects; i++)
+    {
        sections[i].fSection = &segMap->sect[i];
+       file->fIsReloc |= (0 != segMap->sect[i].nreloc);
+    }
 
     return true;
 }
@@ -776,13 +909,15 @@ tryRemangleAgain:
     return true;
 }
 
+// This function can only operate on symbol table files from  32 bit
+// mach-o files
 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;
+    Boolean foundOSObject, found295CPP, havelocal;
 
     // we found a link edit segment so recompute the bases
     if (file->fLinkEditSeg) {
@@ -824,6 +959,7 @@ static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
     strsize = file->fSymtab->strsize;
     strbase = file->fStringBase;
     firstlocal = 0;
+    havelocal = false;
     found295CPP = foundOSObject = false;
     for (i = 0, sym = file->fSymbolBase; i < nsyms; i++, sym++) {
         long strx = sym->n_un.n_strx;
@@ -832,6 +968,54 @@ static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
 
         return_if(((unsigned long) strx > strsize), false,
             ("%s has an illegal string offset in symbol %d\n", pathName, i));
+#if 0
+        // Make all syms abs
+       if (file->fIsIncrLink) {
+           if ( (sym->n_type & N_TYPE) == N_SECT) {
+               sym->n_sect = NO_SECT;
+               sym->n_type = (sym->n_type & ~N_TYPE) | N_ABS;
+           }
+       }
+#endif
+
+       if (file->fIsIncrLink && !file->fNSects)
+       {
+           // symbol set
+           struct nlist *patchsym = (struct nlist *) sym;
+           const char * lookname;
+           const struct nlist * realsym;
+
+           if ( (patchsym->n_type & N_TYPE) == N_INDR)
+               lookname = strbase + patchsym->n_value;
+           else
+               lookname = symname;
+           realsym = findSymbolByName(sKernelFile, lookname);
+
+           patchsym->n_sect  = NO_SECT;
+           if (realsym)
+           {
+               patchsym->n_type  = realsym->n_type;
+               patchsym->n_desc  = realsym->n_desc;
+               patchsym->n_value = realsym->n_value;
+               if ((patchsym->n_type & N_TYPE) == N_SECT)
+                   patchsym->n_type = (patchsym->n_type & ~N_TYPE) | N_ABS;
+           }
+           else
+           {
+               errprintf("%s: Undefined in symbol set: %s\n", pathName, symname);
+               patchsym->n_type = N_ABS;
+               patchsym->n_desc  = 0;
+               patchsym->n_value = patchsym->n_un.n_strx;
+               patchsym->n_un.n_strx = 0;
+           }
+
+           if (!havelocal && (patchsym->n_type & N_EXT)) {
+               firstlocal = i;
+               havelocal = true;
+               file->fLocalSyms = patchsym;
+           }
+           continue;
+       } /* symbol set */
 
         // Load up lookup symbol look table with sym names
        file->fSymbToStringTable[i] = symname;
@@ -841,6 +1025,7 @@ static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
         // Find the first exported symbol
         if ( !firstlocal && (n_type & N_EXT) ) {
             firstlocal = i;
+           havelocal = true;
             file->fLocalSyms = sym;
         }
 
@@ -879,10 +1064,11 @@ static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
                     // Finally just check if we need to remangle
                     symname++; // skip leading '__'
                     while (*symname) {
-                        if ('_' == *symname++ && '_' == *symname++) {
+                        if ('_' == symname[0] && '_' == symname[1]) {
                             found295CPP = true;
                             break;
                         }
+                       symname++;
                     }
                 }
             }
@@ -893,10 +1079,11 @@ static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
             if (!found295CPP) {
                 symname++;     // Skip possible second '_' at start.
                 while (*symname) {
-                    if ('_' == *symname++ && '_' == *symname++) {
+                    if ('_' == symname[0] && '_' == symname[1]) {
                         found295CPP = true;
                         break;
                     }
+                   symname++;
                 }
             }
         }
@@ -950,6 +1137,34 @@ findSymbolByAddress(const struct fileRecord *file, void *entry)
     return NULL;
 }
 
+static const struct nlist *
+findSymbolByAddressInAllFiles(__unused const struct fileRecord * fromFile, 
+                           void *entry, const struct fileRecord **resultFile)
+{
+    int i, nfiles = 0;
+    struct fileRecord **files;
+
+    if (sFilesTable) {
+
+        // 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 ((((vm_offset_t)entry) >= files[i]->fVMAddr)
+            && (((vm_offset_t)entry) <  files[i]->fVMEnd))
+           {
+               const struct nlist * result;
+               if (resultFile)
+                   *resultFile = files[i];
+               result = findSymbolByAddress(files[i], entry);
+               return result;
+           }
+       }
+    }
+
+    return NULL;
+}
+
 struct searchContext {
     const char *fSymname;
     const struct fileRecord *fFile;
@@ -960,7 +1175,7 @@ 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);
+    return strcmp(key->fSymname, symbolname(key->fFile, sym));
 }
 
 static const struct nlist *
@@ -974,7 +1189,7 @@ findSymbolByName(struct fileRecord *file, const char *symname)
         int nLocal = file->fNLocal + i;
 
         for (sym = file->fLocalSyms; i < nLocal; i++, sym++)
-            if (!strcmp(symNameByIndex(file, i) + 1, symname + 1))
+            if (!strcmp(symNameByIndex(file, i), symname))
                 return sym;
         return NULL;
     }
@@ -1080,7 +1295,12 @@ relocateSection(const struct fileRecord *file, struct sectionRecord *sectionRec)
                ("Invalid relocation entry in %s - local\n", file->fPath));
 
            // Find the symbol, if any, that backs this entry 
-           symbol = findSymbolByAddress(file, *entry);
+           void * addr = *entry;
+#if !KERNEL
+           if (file->fSwapped)
+               addr = (void *) OSSwapInt32((uint32_t) addr);
+#endif
+           symbol = findSymbolByAddress(file, addr);
        }
 
        rec->fValue  = *entry;          // Save the previous value
@@ -1098,11 +1318,24 @@ relocateSection(const struct fileRecord *file, struct sectionRecord *sectionRec)
 
 static const struct nlist *
 findSymbolRefAtLocation(const struct fileRecord *file,
-                       struct sectionRecord *sctn, void **loc)
+                       struct sectionRecord *sctn, void **loc, const struct fileRecord **foundInFile)
 {
-    if (file->fIsKernel) {
-       if (*loc)
-           return findSymbolByAddress(file, *loc);
+    const struct nlist * result;
+
+    *foundInFile = file;
+
+    if (!file->fIsReloc) {
+       if (*loc) {
+           void * addr = *loc;
+#if !KERNEL
+           if (file->fSwapped)
+               addr = (void *) OSSwapInt32((uint32_t) addr);
+#endif
+           result = findSymbolByAddress(file, addr);
+           if (!result)
+               result = findSymbolByAddressInAllFiles(file, addr, foundInFile);
+           return result;
+       }
     }
     else if (sctn->fRelocCache || relocateSection(file, sctn)) {
        struct relocRecord *reloc = (struct relocRecord *) *loc;
@@ -1191,11 +1424,12 @@ recordClass(struct fileRecord *file, const char *cname, const struct nlist *sym)
     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
+    // 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) {
+    if (file->fIsReloc) {
        const char *suffix;
+       const struct fileRecord *superfile;
        const struct nlist *supersym;
        const struct section *section;
        struct sectionRecord *sectionRec;
@@ -1216,15 +1450,15 @@ recordClass(struct fileRecord *file, const char *cname, const struct nlist *sym)
        section = sectionRec->fSection;
        location = (void **) ( file->fMachO + section->offset
                            + sym->n_value - section->addr );
-    
-       supersym = findSymbolRefAtLocation(file, sectionRec, location);
+       
+       supersym = findSymbolRefAtLocation(file, sectionRec, location, &superfile);
        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;
+       superstr = symbolname(superfile, supersym) + 1;
        suffix = superstr + strlen(superstr) - sizeof(kGMetaSuffix) + 1;
        if (suffix <= superstr || strcmp(suffix, kGMetaSuffix)) {
            result = true;      // Not an OSObject superclass so ignore it..
@@ -1363,7 +1597,7 @@ static Boolean mergeOSObjectsForFile(const struct fileRecord *file)
            ("Unable to allocate memory metaclass list\n", file->fPath));
     }
     else {     /* perform a duplicate check */
-       int i, j, cnt1, cnt2;
+       int k, j, cnt1, cnt2;
        struct metaClassRecord **list1, **list2;
 
        list1 = (struct metaClassRecord **) DataGetPtr(file->fClassList);
@@ -1371,11 +1605,11 @@ static Boolean mergeOSObjectsForFile(const struct fileRecord *file)
        list2 = (struct metaClassRecord **) DataGetPtr(sMergeMetaClasses);
        cnt2  = DataGetLength(sMergeMetaClasses) / sizeof(*list2);
 
-       for (i = 0; i < cnt1; i++) {
+       for (k = 0; k < cnt1; k++) {
            for (j = 0; j < cnt2; j++) {
-               if (!strcmp(list1[i]->fClassName, list2[j]->fClassName)) {
+               if (!strcmp(list1[k]->fClassName, list2[j]->fClassName)) {
                    errprintf("duplicate class %s in %s & %s\n",
-                             list1[i]->fClassName,
+                             list1[k]->fClassName,
                              file->fPath, list2[j]->fFile->fPath);
                }
            }
@@ -1408,7 +1642,7 @@ getSectionForSymbol(const struct fileRecord *file, const struct nlist *symb,
     unsigned char *base;
 
     sectind = symb->n_sect;    // Default to symbols section
-    if ((symb->n_type & N_TYPE) == N_ABS && file->fIsKernel) {
+    if ((symb->n_type & N_TYPE) == N_ABS && !file->fIsReloc) {
        // Absolute symbol so we have to iterate over our sections
        for (sectind = 1; sectind <= file->fNSects; sectind++) {
            unsigned long start, end;
@@ -1463,8 +1697,8 @@ static Boolean resolveKernelVTable(struct metaClassRecord *metaClass)
     // 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"));
+    return_if(file->fIsReloc,
+       false, ("Internal error - resolveKernelVTable is relocateable\n"));
 
     if (file->fNoKernelExecutable) {
        // Oh dear attempt to map the kernel's VM into my memory space
@@ -1492,9 +1726,29 @@ static Boolean resolveKernelVTable(struct metaClassRecord *metaClass)
     curPatch = patchedVTable;
     curEntry = vtableEntries + kVTablePreambleLen;
     for (; *curEntry; curEntry++, curPatch++) {
+       void * addr = *curEntry;
+#if !KERNEL
+       if (file->fSwapped)
+           addr = (void *) OSSwapInt32((uint32_t) addr);
+#endif
        curPatch->fSymbol = (struct nlist *) 
-           findSymbolByAddress(file, *curEntry);
-       curPatch->fType = kSymbolLocal;
+           findSymbolByAddress(file, addr);
+       if (curPatch->fSymbol)
+       {
+           curPatch->fType = kSymbolLocal;
+           curPatch->fFile = file;
+       }
+       else
+       {
+           curPatch->fSymbol = (struct nlist *) 
+               findSymbolByAddressInAllFiles(file, addr, &curPatch->fFile);
+           if (!curPatch->fSymbol) {
+               errprintf("%s: !findSymbolByAddressInAllFiles(%p)\n",
+                           file->fPath, addr);
+               return false;
+           }
+           curPatch->fType = kSymbolLocal;
+       }
     }
 
     // Tag the end of the patch vtable
@@ -1574,12 +1828,28 @@ getNewSymbol(struct fileRecord *file,
        }
     }
 
-    // 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 (reloc->fSymbol->n_un.n_strx >= 0) {
+        // This symbol has not been previously processed, so assert that it
+        // is a valid non-local symbol.  I need this condition to be true for
+       // the later code to set to -1.  Now, being the first time through,
+       // I'd better make sure that n_sect is NO_SECT.
+
+        return_if(reloc->fSymbol->n_sect != NO_SECT, NULL,
+            ("Undefined symbol entry with non-zero section %s:%s\n",
+            file->fPath, symbolname(file, reloc->fSymbol)));
+
+       // Mark the original symbol entry as having been processed.
+       // This means that we wont attempt to create the symbol again
+       // in the future if we come through a different path.
+        ((struct nlist *) reloc->fSymbol)->n_un.n_strx =
+           -reloc->fSymbol->n_un.n_strx;    
+
+        // 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
+        // invalid input file.
+        ((struct nlist *) reloc->fSymbol)->n_sect = (unsigned char) -1;
+    }
 
     // If we are here we didn't find the symbol so create a new one now
     msym = (struct nlist *) malloc(sizeof(struct nlist));
@@ -1591,6 +1861,7 @@ getNewSymbol(struct fileRecord *file,
     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));
@@ -1604,20 +1875,6 @@ getNewSymbol(struct fileRecord *file,
     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
-    // invalid input file.
-    //
-    // However the symbol may have been just inserted by the fixOldSymbol path.
-    // If this is the case then we know it is in use and we don't have to
-    // mark it as a deletable symbol.
-    if (reloc->fSymbol->n_un.n_strx >= 0) {
-        ((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;
@@ -1707,13 +1964,17 @@ static Boolean patchVTable(struct metaClassRecord *metaClass)
 
     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 (!file->fIsReloc)
+    {
+       // If the metaClass we are being to ask is already relocated 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 already linked
+       // modules as the vtables are always correct wrt themselves.
+       // Note this ends the superclass chain recursion.
+       Boolean res;
+       res = resolveKernelVTable(metaClass);
+       return res;
+    }
 
     if (!metaClass->fSuperName)
        return false;
@@ -1727,11 +1988,7 @@ static Boolean patchVTable(struct metaClassRecord *metaClass)
     // Superclass recursion if necessary
     if (!super->fPatchedVTable) {
        Boolean res;
-
-       if (super->fFile->fIsKernel)
-           res = resolveKernelVTable(super);
-       else
-           res = patchVTable(super);
+       res = patchVTable(super);
        if (!res)
            return false;
     }
@@ -1775,7 +2032,7 @@ static Boolean patchVTable(struct metaClassRecord *metaClass)
 
            for ( ; spp->fSymbol; curReloc++, spp++, curPatch++) {
                const char *supername =
-                   symbolname(super->fFile, spp->fSymbol);
+                   symbolname(spp->fFile, spp->fSymbol);
 
                 symbol = (struct nlist *) (*curReloc)->fSymbol;
 
@@ -1806,6 +2063,7 @@ static Boolean patchVTable(struct metaClassRecord *metaClass)
                if (symbol) {
                    curPatch->fSymbol = symbol;
                    (*curReloc)->fSymbol = symbol;
+                   curPatch->fFile = file;
                }
                else
                    goto abortPatch;
@@ -1817,6 +2075,7 @@ static Boolean patchVTable(struct metaClassRecord *metaClass)
            // Local reloc symbols
            curPatch->fType = kSymbolLocal;
            curPatch->fSymbol = (struct nlist *) (*curReloc)->fSymbol;
+           curPatch->fFile = file;
        }
 
        // Tag the end of the patch vtable
@@ -1852,13 +2111,13 @@ static Boolean growImage(struct fileRecord *file, vm_size_t delta)
     endMap   = (vm_address_t) file->fMap + file->fMapSize;
 
     // Do we have room in the current mapped image
-    if (endMachO < round_page(endMap)) {
+    if (endMachO < round_page_32(endMap)) {
        file->fMachOSize += delta;
        return true;
     }
 
     newsize = endMachO - startMachO;
-    if (newsize < round_page(file->fMapSize)) {
+    if (newsize < round_page_32(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
@@ -1968,6 +2227,8 @@ static Boolean growImage(struct fileRecord *file, vm_size_t delta)
 #endif /* KERNEL */
 }
 
+// Note: This function is only called from kld_file_prepare_for_link()
+// This function can only operate on 32 bit mach-o files
 static Boolean
 prepareFileForLink(struct fileRecord *file)
 {
@@ -1978,8 +2239,15 @@ prepareFileForLink(struct fileRecord *file)
 
     // If we didn't even do a pseudo 'relocate' and dirty the image
     // then we can just return now.
-    if (!file->fImageDirty)
+    if (!file->fImageDirty) {
+#if !KERNEL
+       if (file->fSwapped) {
+           kld_macho_unswap((struct mach_header *) file->fMachO, file->fSwapped, false);
+           file->fSwapped = false;
+       }
+#endif
        return true;
+    }
 
 DEBUG_LOG(("Linking 2 %s\n", file->fPath));    // @@@ gvdl:
 
@@ -1999,7 +2267,7 @@ DEBUG_LOG(("Linking 2 %s\n", file->fPath));       // @@@ gvdl:
            // We will need to repair the reloc list 
            for (j = 0; j < nreloc; j++, rec++) {
                void **entry;
-               struct nlist *sym;
+               struct nlist *repairSym;
     
                // Repair Damage to object image
                entry = (void **) (sectionBase + rec->fRInfo->r_address);
@@ -2007,12 +2275,12 @@ DEBUG_LOG(("Linking 2 %s\n", file->fPath));     // @@@ gvdl:
 
                // 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) {
+               repairSym = (struct nlist *) rec->fSymbol;
+               if (repairSym && repairSym->n_type == (N_EXT | N_UNDF)
+               &&  repairSym->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;
+                   repairSym->n_un.n_strx = -repairSym->n_un.n_strx;
+                   repairSym->n_sect = NO_SECT;
                }
            }
 
@@ -2024,8 +2292,15 @@ DEBUG_LOG(("Linking 2 %s\n", file->fPath));      // @@@ gvdl:
     file->fImageDirty = false; // Image is clean
 
     // If we didn't dirty the symbol table then just return
-    if (!file->fSymbolsDirty)
+    if (!file->fSymbolsDirty) {
+#if !KERNEL
+       if (file->fSwapped) {
+           kld_macho_unswap((struct mach_header *) file->fMachO, file->fSwapped, false);
+           file->fSwapped = false;
+       }
+#endif
        return true;
+    }
 
     // calculate total file size increase and check against padding
     if (file->fNewSymbols) {
@@ -2091,8 +2366,16 @@ DEBUG_LOG(("Linking 2 %s\n", file->fPath));      // @@@ gvdl:
     }
 
     // Don't need the new strings any more
-    last = DataGetLength(file->fNewStringBlocks) / sizeof(DataRef);
-    stringBlocks = (DataRef *) DataGetPtr(file->fNewStringBlocks);
+    
+    if (file->fNewStringBlocks){
+        last = DataGetLength(file->fNewStringBlocks) / sizeof(DataRef);
+        stringBlocks = (DataRef *) DataGetPtr(file->fNewStringBlocks);
+    }
+    else{
+        last =0;
+        stringBlocks=0;
+    }
+    
     for (i = 0; i < last; i++)
         DataRelease(stringBlocks[i]);
 
@@ -2137,10 +2420,16 @@ DEBUG_LOG(("Linking 2 %s\n", file->fPath));     // @@@ gvdl:
     }
 
     file->fSymbolsDirty = false;
-
+#if !KERNEL
+    if (file->fSwapped) {
+       kld_macho_unswap((struct mach_header *) file->fMachO, file->fSwapped, false);
+       file->fSwapped = false;
+    }
+#endif
     return true;
 }
 
+// This function can only operate on 32 bit mach-o files
 Boolean
 #if KERNEL
 kld_file_map(const char *pathName,
@@ -2175,7 +2464,8 @@ kld_file_map(const char *pathName)
            struct load_command c[1];
        } *machO;
        const struct load_command *cmd;
-        int i;
+       boolean_t lookVMRange;
+        unsigned long i;
 
        if (!findBestArch(&file, pathName))
            break;
@@ -2184,22 +2474,38 @@ kld_file_map(const char *pathName)
        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++) {
+        file.fIsKernel = (MH_EXECUTE == machO->h.filetype);
+
+       for (i = 0, cmd = &machO->c[0], lookVMRange = true; 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 (lookVMRange) {
+                   if (!strcmp("__PRELINK", seg->segname))
+                       // segments following __PRELINK are going to move, so ignore them
+                       lookVMRange = false;
+                   else if (!file.fVMAddr && !file.fVMEnd) {
+                       file.fVMAddr = seg->vmaddr;
+                       file.fVMEnd = seg->vmaddr + seg->vmsize;
+                   } else {
+                       if (seg->vmaddr < file.fVMAddr)
+                           file.fVMAddr = seg->vmaddr;
+                       if ((seg->vmaddr + seg->vmsize) > file.fVMEnd)
+                           file.fVMEnd = seg->vmaddr + seg->vmsize;
+                   }
+               }
+
                 if (nsects)
                     return_if(!parseSegments(&file, seg),
                               false, ("%s isn't a valid mach-o, bad segment",
                              pathName));
-                else if (file.fIsKernel) {
+
+                if (file.fIsKernel) {
 #if KERNEL
                     // We don't need to look for the LinkEdit segment unless
                     // we are running in the kernel environment.
@@ -2208,12 +2514,33 @@ kld_file_map(const char *pathName)
 #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 (machO->h.flags & MH_INCRLINK) {
+
+           file.fIsIncrLink = true;
+           ((struct machOMapping *) machO)->h.flags &= ~MH_INCRLINK;
+
+#if !KERNEL
+           // the symtab fileoffset is the end of seg0's vmsize,
+           // which can be (rarely) unaligned.
+           unsigned int
+           align = file.fSymtab->symoff % sizeof(long);
+           if (align != 0) {
+               align = sizeof(long) - align;
+               growImage(&file, align);
+               bcopy(file.fMachO + file.fSymtab->symoff,
+                       file.fMachO + file.fSymtab->symoff + align,
+                       file.fSymtab->stroff + file.fSymtab->strsize - file.fSymtab->symoff);
+               file.fSymtab->symoff += align;
+               file.fSymtab->stroff += align;
+           }
+#endif
+       }
+
         if (!parseSymtab(&file, pathName))
             break;
 
@@ -2231,9 +2558,6 @@ kld_file_map(const char *pathName)
         // 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;
@@ -2370,7 +2694,7 @@ Boolean kld_file_patch_OSObjects(const char *pathName)
     return true;
 }
 
-Boolean kld_file_prepare_for_link()
+Boolean kld_file_prepare_for_link(void)
 {
     if (sMergedFiles) {
        unsigned long i, nmerged = 0;
@@ -2394,7 +2718,7 @@ Boolean kld_file_prepare_for_link()
     return true;
 }
 
-void kld_file_cleanup_all_resources()
+void kld_file_cleanup_all_resources(void)
 {
     unsigned long i, nfiles;