X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/de355530ae67247cbd0da700edb3a2a1dae884c2..0c530ab8987f0ae6a1a3d9284f40182b88852816:/libsa/kld_patch.c diff --git a/libsa/kld_patch.c b/libsa/kld_patch.c index 862be7f96..18892a019 100644 --- a/libsa/kld_patch.c +++ b/libsa/kld_patch.c @@ -31,11 +31,15 @@ #include #include #include +#if !KERNEL +#include +#include +#endif #if KERNEL #include -#include +//#include #include @@ -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;