]> git.saurik.com Git - apple/system_cmds.git/blame - kmodload.tproj/kld_patch.c
system_cmds-433.8.tar.gz
[apple/system_cmds.git] / kmodload.tproj / kld_patch.c
CommitLineData
916eb79e
A
1/*
2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24/*
25 * History:
26 * 2001-05-30 gvdl Initial implementation of the vtable patcher.
27 */
28// 45678901234567890123456789012345678901234567890123456789012345678901234567890
29
30#include <mach-o/fat.h>
31#include <mach-o/loader.h>
32#include <mach-o/nlist.h>
33#include <mach-o/reloc.h>
34
35#if KERNEL
36
37#include <stdarg.h>
38#include <string.h>
39
40#include <sys/systm.h>
41
42#include <libkern/OSTypes.h>
43
44#include <libsa/stdlib.h>
45#include <libsa/mach/mach.h>
46
47#include "mach_loader.h"
48
49#include <vm/vm_kern.h>
50
51enum { false = 0, true = 1 };
52
53#define vm_page_size page_size
54
55extern load_return_t fatfile_getarch(
56 void * vp, // normally a (struct vnode *)
57 vm_offset_t data_ptr,
58 struct fat_arch * archret);
59
60__private_extern__ char *strstr(const char *in, const char *str);
61
62#else /* !KERNEL */
63
64#include <unistd.h>
65
66#include <stdio.h>
67#include <stdlib.h>
68#include <string.h>
69
70#include <sys/errno.h>
71#include <sys/fcntl.h>
72#include <sys/stat.h>
73#include <sys/mman.h>
74#include <sys/vm.h>
75
76#include <mach/mach.h>
77#include <mach/mach_error.h>
78
79#include <mach-o/arch.h>
80
81#include <CoreFoundation/CoreFoundation.h>
82
83#define PAGE_SIZE vm_page_size
84#define PAGE_MASK (PAGE_SIZE - 1)
85
86#endif /* KERNEL */
87
88#include "kld_patch.h"
89#include "c++rem3.h"
90
91#if 0
92#define DIE() do { for (;;) ; } while(0)
93
94#if KERNEL
95# define LOG_DELAY() /* IODelay(200000) */
96# define DEBUG_LOG(x) do { IOLog x; LOG_DELAY(); } while(0)
97#else
98# define LOG_DELAY()
99# define DEBUG_LOG(x) do { printf x; } while(0)
100#endif
101
102#else
103
104#define DIE()
105#define LOG_DELAY()
106#define DEBUG_LOG(x)
107
108#endif
109
110// OSObject symbol prefixes and suffixes
111#define kCPPSymbolPrefix "_Z"
112#define kVTablePrefix "_" kCPPSymbolPrefix "TV"
113#define kOSObjPrefix "_" kCPPSymbolPrefix "N"
114#define kReservedNamePrefix "_RESERVED"
115#define k29SuperClassSuffix "superClass"
116#define k31SuperClassSuffix "10superClassE"
117#define kGMetaSuffix "10gMetaClassE"
118#define kLinkEditSegName SEG_LINKEDIT
119
120// GCC 2.95 drops 2 leading constants in the vtable
121#define kVTablePreambleLen 2
122
123// Last address that I'm willing to try find vm in
124#define kTopAddr ((unsigned char *) (1024 * 1024 * 1024))
125
126// Size in bytes that Data Ref object's get increased in size
127// Must be a power of 2
128#define kDataCapacityIncrement 128
129
130// My usual set of helper macros. I personally find these macros
131// easier to read in the code rather than an explicit error condition
132// check. If I don't make it easy then I may get lazy ond not check
133// everything. I'm sorry if you find this code harder to read.
134
135// break_if will evaluate the expression and if it is true
136// then it will print the msg, which is enclosed in parens
137// and then break. Usually used in loops are do { } while (0)
138#define break_if(expr, msg) \
139 if (expr) { \
140 errprintf msg; \
141 break; \
142 }
143
144// return_if will evaluate expr and if true it will log the
145// msg, which is enclosed in parens, and then it will return
146// with the return code of ret.
147#define return_if(expr, ret, msg) do { \
148 if (expr) { \
149 errprintf msg; \
150 return ret; \
151 } \
152} while (0)
153
154#ifndef MIN
155#define MIN(a,b) (((a)<(b))?(a):(b))
156#endif /* MIN */
157#ifndef MAX
158#define MAX(a,b) (((a)>(b))?(a):(b))
159#endif /* MAX */
160
161typedef struct Data {
162 unsigned long fLength, fCapacity;
163 unsigned char *fData;
164} Data, *DataRef;
165
166struct sectionRecord {
167 const struct section *fSection;
168 DataRef fRelocCache;
169};
170
171enum patchState {
172 kSymbolIdentical,
173 kSymbolLocal,
174 kSymbolPadUpdate,
175 kSymbolSuperUpdate,
176 kSymbolMismatch
177};
178
179struct patchRecord {
180 struct nlist *fSymbol;
181 enum patchState fType;
182};
183
184struct relocRecord {
185 void *fValue;
186 const struct nlist *fSymbol;
187 struct relocation_info *fRInfo;
188 void *reserved;
189};
190
191struct metaClassRecord {
192 char *fSuperName;
193 struct fileRecord *fFile;
194 const struct nlist *fVTableSym;
195 struct patchRecord *fPatchedVTable;
196 char fClassName[1];
197};
198
199struct fileRecord {
200 size_t fMapSize, fMachOSize;
201 unsigned char *fMap, *fMachO, *fPadEnd;
202 DataRef fClassList;
203 DataRef fSectData;
204 DataRef fNewSymbols, fNewStringBlocks;
205 DataRef fSym2Strings;
206 struct symtab_command *fSymtab;
207 struct sectionRecord *fSections;
208 struct segment_command *fLinkEditSeg;
209 const char **fSymbToStringTable;
210 char *fStringBase;
211 struct nlist *fSymbolBase;
212 const struct nlist *fLocalSyms;
213 unsigned int fNSects;
214 int fNLocal;
215 Boolean fIsKernel, fNoKernelExecutable, fIsKmem;
216 Boolean fImageDirty, fSymbolsDirty;
217 Boolean fRemangled, fFoundOSObject;
218 const char fPath[1];
219};
220
221static DataRef sFilesTable;
222static struct fileRecord *sKernelFile;
223
224static DataRef sMergedFiles;
225static DataRef sMergeMetaClasses;
226static Boolean sMergedKernel;
227
228static void errprintf(const char *fmt, ...)
229{
230 extern void kld_error_vprintf(const char *format, va_list ap);
231
232 va_list ap;
233
234 va_start(ap, fmt);
235 kld_error_vprintf(fmt, ap);
236 va_end(ap);
237
238DIE();
239}
240
241static __inline__ unsigned long DataGetLength(DataRef data)
242{
243 return data->fLength;
244}
245
246static __inline__ unsigned char *DataGetPtr(DataRef data)
247{
248 return data->fData;
249}
250
251static __inline__ unsigned char *DataGetEndPtr(DataRef data)
252{
253 return data->fData + data->fLength;
254}
255
256static __inline__ unsigned long DataRemaining(DataRef data)
257{
258 return data->fCapacity - data->fLength;
259}
260
261static __inline__ Boolean DataContainsAddr(DataRef data, void *vAddr)
262{
263 vm_offset_t offset = (vm_address_t) vAddr;
264
265 if (!data)
266 return false;
267
268 offset = (vm_address_t) vAddr - (vm_address_t) data->fData;
269 return (offset < data->fLength);
270}
271
272static Boolean DataEnsureCapacity(DataRef data, unsigned long capacity)
273{
274 // Don't bother to ever shrink a data object.
275 if (capacity > data->fCapacity) {
276 unsigned char *newData;
277
278 capacity += kDataCapacityIncrement - 1;
279 capacity &= ~(kDataCapacityIncrement - 1);
280 newData = (unsigned char *) realloc(data->fData, capacity);
281 if (!newData)
282 return false;
283
284 bzero(newData + data->fCapacity, capacity - data->fCapacity);
285 data->fData = newData;
286 data->fCapacity = capacity;
287 }
288
289 return true;
290}
291
292static __inline__ Boolean DataSetLength(DataRef data, unsigned long length)
293{
294 if (DataEnsureCapacity(data, length)) {
295 data->fLength = length;
296 return true;
297 }
298 else
299 return false;
300}
301
302static __inline__ Boolean DataAddLength(DataRef data, unsigned long length)
303{
304 return DataSetLength(data, data->fLength + length);
305}
306
307static __inline__ Boolean
308DataAppendBytes(DataRef data, const void *addr, unsigned int len)
309{
310 unsigned long size = DataGetLength(data);
311
312 if (!DataAddLength(data, len))
313 return false;
314
315 bcopy(addr, DataGetPtr(data) + size, len);
316 return true;
317}
318
319static __inline__ Boolean DataAppendData(DataRef dst, DataRef src)
320{
321 return DataAppendBytes(dst, DataGetPtr(src), DataGetLength(src));
322}
323
324static DataRef DataCreate(unsigned long capacity)
325{
326 DataRef data = (DataRef) malloc(sizeof(Data));
327
328 if (data) {
329 if (!capacity)
330 data->fCapacity = kDataCapacityIncrement;
331 else {
332 data->fCapacity = capacity + kDataCapacityIncrement - 1;
333 data->fCapacity &= ~(kDataCapacityIncrement - 1);
334 }
335
336 data->fData = (unsigned char *) malloc(data->fCapacity);
337 if (!data->fData) {
338 free(data);
339 return NULL;
340 }
341
342 bzero(data->fData, data->fCapacity);
343 data->fLength = 0;
344 }
345 return data;
346}
347
348static void DataRelease(DataRef data)
349{
350 if (data) {
351 if (data->fData)
352 free(data->fData);
353 data->fData = 0;
354 free(data);
355 }
356}
357
358static __inline__ const char *
359symNameByIndex(const struct fileRecord *file, unsigned int symInd)
360{
361 return file->fSymbToStringTable[symInd];
362}
363
364static __inline__ const char *
365symbolname(const struct fileRecord *file, const struct nlist *sym)
366{
367 unsigned int index;
368
369 index = sym - file->fSymbolBase;
370 if (index < file->fSymtab->nsyms)
371 return symNameByIndex(file, index);
372
373 if (-1 == sym->n_un.n_strx)
374 return (const char *) sym->n_value;
375
376 // If the preceding tests fail then we have a getNewSymbol patch and
377 // the file it refers to has already been patched as the n_strx is set
378 // to -1 temporarily while we are still processing a file.
379 // Once we have finished with a file then we repair the 'strx' offset
380 // to be valid for the repaired file's string table.
381 return file->fStringBase + sym->n_un.n_strx;
382}
383
384static struct fileRecord *
385getFile(const char *path)
386{
387 if (sFilesTable) {
388 int i, nfiles;
389 struct fileRecord **files;
390
391 // Check to see if we have already merged this file
392 nfiles = DataGetLength(sFilesTable) / sizeof(struct fileRecord *);
393 files = (struct fileRecord **) DataGetPtr(sFilesTable);
394 for (i = 0; i < nfiles; i++) {
395 if (!strcmp(path, files[i]->fPath))
396 return files[i];
397 }
398 }
399
400 return NULL;
401}
402
403static struct fileRecord *
404addFile(struct fileRecord *file, const char *path)
405{
406 struct fileRecord *newFile;
407
408 if (!sFilesTable) {
409 sFilesTable = DataCreate(0);
410 if (!sFilesTable)
411 return NULL;
412 }
413
414 newFile = (struct fileRecord *)
415 malloc(sizeof(struct fileRecord) + strlen(path));
416 if (!newFile)
417 return NULL;
418
419 if (!DataAppendBytes(sFilesTable, &newFile, sizeof(newFile))) {
420 free(newFile);
421 return NULL;
422 }
423
424 bcopy(file, newFile, sizeof(struct fileRecord) - 1);
425 strcpy((char *) newFile->fPath, path);
426
427 return newFile;
428}
429
430// @@@ gvdl: need to clean up the sMergeMetaClasses
431// @@@ gvdl: I had better fix the object file up again
432static void unmapFile(struct fileRecord *file)
433{
434 if (file->fSectData) {
435 struct sectionRecord *section;
436 unsigned int i, nsect;
437
438 nsect = file->fNSects;
439 section = file->fSections;
440 for (i = 0; i < nsect; i++, section++) {
441 if (section->fRelocCache) {
442 DataRelease(section->fRelocCache);
443 section->fRelocCache = 0;
444 }
445 }
446
447 DataRelease(file->fSectData);
448 file->fSectData = 0;
449 file->fSections = 0;
450 file->fNSects = 0;
451 }
452
453 if (file->fSym2Strings) {
454 DataRelease(file->fSym2Strings);
455 file->fSym2Strings = 0;
456 }
457
458 if (file->fMap) {
459#if KERNEL
460 if (file->fIsKmem)
461 kmem_free(kernel_map, (vm_address_t) file->fMap, file->fMapSize);
462#else /* !KERNEL */
463 if (file->fPadEnd) {
464 vm_address_t padVM;
465 vm_size_t padSize;
466
467 padVM = round_page((vm_address_t) file->fMap + file->fMapSize);
468 padSize = (vm_size_t) ((vm_address_t) file->fPadEnd - padVM);
469 (void) vm_deallocate(mach_task_self(), padVM, padSize);
470 file->fPadEnd = 0;
471 }
472
473 (void) munmap((caddr_t) file->fMap, file->fMapSize);
474#endif /* !KERNEL */
475 file->fMap = 0;
476 }
477}
478
479static void removeFile(struct fileRecord *file)
480{
481 if (file->fClassList) {
482 DataRelease(file->fClassList);
483 file->fClassList = 0;
484 }
485
486 unmapFile(file);
487
488 free(file);
489}
490
491#if !KERNEL
492static Boolean
493mapObjectFile(struct fileRecord *file, const char *pathName)
494{
495 Boolean result = false;
496 static unsigned char *sFileMapBaseAddr = 0;
497
498 int fd = 0;
499
500 if (!sFileMapBaseAddr) {
501 kern_return_t ret;
502 vm_address_t probeAddr;
503
504 // If we don't already have a base addr find any random chunk
505 // of 32 meg of VM and to use the 16 meg boundrary as a base.
506 ret = vm_allocate(mach_task_self(), &probeAddr,
507 32 * 1024 * 1024, VM_FLAGS_ANYWHERE);
508 return_if(KERN_SUCCESS != ret, false,
509 ("Unable to allocate base memory %s\n", mach_error_string(ret)));
510 (void) vm_deallocate(mach_task_self(), probeAddr, 32 * 1024 * 1024);
511
512 // Now round to the next 16 Meg boundrary
513 probeAddr = (probeAddr + (16 * 1024 * 1024 - 1))
514 & ~(16 * 1024 * 1024 - 1);
515 sFileMapBaseAddr = (unsigned char *) probeAddr;
516 }
517
518 fd = open(pathName, O_RDONLY, 0);
519 return_if(fd == -1, false, ("Can't open %s for reading - %s\n",
520 pathName, strerror(errno)));
521
522 do {
523 kern_return_t ret;
524 struct stat sb;
525 int retaddr = -1;
526
527 break_if(fstat(fd, &sb) == -1,
528 ("Can't stat %s - %s\n", file->fPath, strerror(errno)));
529
530 file->fMapSize = sb.st_size;
531 file->fMap = sFileMapBaseAddr;
532 ret = KERN_SUCCESS;
533 while (file->fMap < kTopAddr) {
534 vm_address_t padVM;
535 vm_address_t padVMEnd;
536 vm_size_t padSize;
537
538 padVM = round_page((vm_address_t) file->fMap + file->fMapSize);
539 retaddr = (int) mmap(file->fMap, file->fMapSize,
540 PROT_READ|PROT_WRITE,
541 MAP_FIXED|MAP_FILE|MAP_PRIVATE,
542 fd, 0);
543 if (-1 == retaddr) {
544 break_if(ENOMEM != errno,
545 ("mmap failed %d - %s\n", errno, strerror(errno)));
546
547 file->fMap = (unsigned char *) padVM;
548 continue;
549 }
550
551
552 // Round up padVM to the next page after the file and assign at
553 // least another fMapSize more room rounded up to the next page
554 // boundary.
555 padVMEnd = round_page(padVM + file->fMapSize);
556 padSize = padVMEnd - padVM;
557 ret = vm_allocate(
558 mach_task_self(), &padVM, padSize, VM_FLAGS_FIXED);
559 if (KERN_SUCCESS == ret) {
560 file->fPadEnd = (unsigned char *) padVMEnd;
561 break;
562 }
563 else {
564 munmap(file->fMap, file->fMapSize);
565 break_if(KERN_INVALID_ADDRESS != ret,
566 ("Unable to allocate pad vm for %s - %s\n",
567 pathName, mach_error_string(ret)));
568
569 file->fMap = (unsigned char *) padVMEnd;
570 continue; // try again wherever the vm system wants
571 }
572 }
573
574 if (-1 == retaddr || KERN_SUCCESS != ret)
575 break;
576
577 break_if(file->fMap >= kTopAddr,
578 ("Unable to map memory %s\n", file->fPath));
579
580 sFileMapBaseAddr = file->fPadEnd;
581 result = true;
582 } while(0);
583
584 close(fd);
585 return result;
586}
587#endif /* !KERNEL */
588
589static Boolean findBestArch(struct fileRecord *file, const char *pathName)
590{
591 unsigned long magic;
592 struct fat_header *fat;
593
594
595 file->fMachOSize = file->fMapSize;
596 file->fMachO = file->fMap;
597 magic = ((const struct mach_header *) file->fMachO)->magic;
598 fat = (struct fat_header *) file->fMachO;
599
600 // Try to figure out what type of file this is
601 return_if(file->fMapSize < sizeof(unsigned long), false,
602 ("%s isn't a valid object file - no magic\n", pathName));
603
604#if KERNEL
605
606 // CIGAM is byte-swapped MAGIC
607 if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
608
609 load_return_t load_return;
610 struct fat_arch fatinfo;
611
612 load_return = fatfile_getarch(NULL, (vm_address_t) fat, &fatinfo);
613 return_if(load_return != LOAD_SUCCESS, false,
614 ("Extension \"%s\": has no code for this computer\n", pathName));
615
616 file->fMachO = file->fMap + fatinfo.offset;
617 file->fMachOSize = fatinfo.size;
618 magic = ((const struct mach_header *) file->fMachO)->magic;
619 }
620
621#else /* !KERNEL */
622
623 // Do we need to in-place swap the endianness of the fat header?
624 if (magic == FAT_CIGAM) {
625 unsigned long i;
626 struct fat_arch *arch;
627
628 fat->nfat_arch = NXSwapBigLongToHost(fat->nfat_arch);
629 return_if(file->fMapSize < sizeof(struct fat_header)
630 + fat->nfat_arch * sizeof(struct fat_arch),
631 false, ("%s is too fat\n", file->fPath));
632
633 arch = (struct fat_arch *) &fat[1];
634 for (i = 0; i < fat->nfat_arch; i++) {
635 arch[i].cputype = NXSwapBigLongToHost(arch[i].cputype);
636 arch[i].cpusubtype = NXSwapBigLongToHost(arch[i].cpusubtype);
637 arch[i].offset = NXSwapBigLongToHost(arch[i].offset);
638 arch[i].size = NXSwapBigLongToHost(arch[i].size);
639 arch[i].align = NXSwapBigLongToHost(arch[i].align);
640 }
641
642 magic = NXSwapBigLongToHost(fat->magic);
643 }
644
645 // Now see if we can find any valid architectures
646 if (magic == FAT_MAGIC) {
647 const NXArchInfo *myArch;
648 unsigned long fatsize;
649 struct fat_arch *arch;
650
651 fatsize = sizeof(struct fat_header)
652 + fat->nfat_arch * sizeof(struct fat_arch);
653 return_if(file->fMapSize < fatsize,
654 false, ("%s isn't a valid fat file\n", pathName));
655
656 myArch = NXGetLocalArchInfo();
657 arch = NXFindBestFatArch(myArch->cputype, myArch->cpusubtype,
658 (struct fat_arch *) &fat[1], fat->nfat_arch);
659 return_if(!arch,
660 false, ("%s hasn't got arch for %s\n", pathName, myArch->name));
661 return_if(arch->offset + arch->size > file->fMapSize,
662 false, ("%s's %s arch is incomplete\n", pathName, myArch->name));
663 file->fMachO = file->fMap + arch->offset;
664 file->fMachOSize = arch->size;
665 magic = ((const struct mach_header *) file->fMachO)->magic;
666 }
667
668#endif /* KERNEL */
669
670 return_if(magic != MH_MAGIC,
671 false, ("%s isn't a valid mach-o\n", pathName));
672
673 return true;
674}
675
676static Boolean
677parseSegments(struct fileRecord *file, struct segment_command *seg)
678{
679 struct sectionRecord *sections;
680 int i, nsects = seg->nsects;
681 const struct segmentMap {
682 struct segment_command seg;
683 const struct section sect[1];
684 } *segMap;
685
686 if (!file->fSectData) {
687 file->fSectData = DataCreate(0);
688 if (!file->fSectData)
689 return false;
690 }
691
692 // Increase length of section DataRef and cache data pointer
693 if (!DataAddLength(file->fSectData, nsects * sizeof(struct sectionRecord)))
694 return false;
695 file->fSections = (struct sectionRecord *) DataGetPtr(file->fSectData);
696
697 // Initialise the new sections
698 sections = &file->fSections[file->fNSects];
699 file->fNSects += nsects;
700 for (i = 0, segMap = (struct segmentMap *) seg; i < nsects; i++)
701 sections[i].fSection = &segMap->sect[i];
702
703 return true;
704}
705
706static Boolean
707remangleExternSymbols(struct fileRecord *file, const char *pathName)
708{
709 const struct nlist *sym;
710 int i, nsyms, len;
711 DataRef strings = NULL;
712
713 DEBUG_LOG(("Remangling %s\n", pathName));
714
715 file->fNewStringBlocks = DataCreate(0);
716 return_if(!file->fNewStringBlocks, false,
717 ("Unable to allocate new string table for %s\n", pathName));
718
719 nsyms = file->fSymtab->nsyms;
720 for (i = 0, sym = file->fSymbolBase; i < nsyms; i++, sym++) {
721 Rem3Return ret;
722 const char *symname;
723 char *newname;
724 unsigned char n_type = sym->n_type;
725
726 // Not an external symbol or it is a stab in any case don't bother
727 if ((n_type ^ N_EXT) & (N_STAB | N_EXT))
728 continue;
729
730 symname = symNameByIndex(file, i);
731
732tryRemangleAgain:
733 if (!strings) {
734 strings = DataCreate(16 * 1024); // Arbitrary block size
735 return_if(!strings, false,
736 ("Unable to allocate new string block for %s\n", pathName));
737 }
738
739 len = DataRemaining(strings);
740 newname = DataGetEndPtr(strings);
741 ret = rem3_remangle_name(newname, &len, symname);
742 switch (ret) {
743 case kR3InternalNotRemangled:
744 errprintf("Remangler fails on %s in %s\n", symname, pathName);
745 /* No break */
746 case kR3NotRemangled:
747 break;
748
749 case kR3Remangled:
750 file->fSymbToStringTable[i] = newname;
751 file->fRemangled = file->fSymbolsDirty = true;
752 DataAddLength(strings, len + 1); // returns strlen
753 break;
754
755 case kR3BufferTooSmallRemangled:
756 return_if(!DataAppendBytes
757 (file->fNewStringBlocks, &strings, sizeof(strings)),
758 false, ("Unable to allocate string table for %s\n", pathName));
759 strings = NULL;
760 goto tryRemangleAgain;
761
762 case kR3BadArgument:
763 default:
764 return_if(true, false,
765 ("Internal error - remangle of %s\n", pathName));
766 }
767 }
768
769 if (strings) {
770 return_if(!DataAppendBytes
771 (file->fNewStringBlocks, &strings, sizeof(strings)),
772 false, ("Unable to allocate string table for %s\n", pathName));
773 }
774
775 return true;
776}
777
778static Boolean parseSymtab(struct fileRecord *file, const char *pathName)
779{
780 const struct nlist *sym;
781 unsigned int i, firstlocal, nsyms;
782 unsigned long strsize;
783 const char *strbase;
784 Boolean foundOSObject, found295CPP;
785
786 // we found a link edit segment so recompute the bases
787 if (file->fLinkEditSeg) {
788 struct segment_command *link = file->fLinkEditSeg;
789
790 file->fSymbolBase = (struct nlist *)
791 (link->vmaddr + (file->fSymtab->symoff - link->fileoff));
792 file->fStringBase = (char *)
793 (link->vmaddr + (file->fSymtab->stroff - link->fileoff));
794 return_if( ( (caddr_t) file->fStringBase + file->fSymtab->strsize
795 > (caddr_t) link->vmaddr + link->vmsize ), false,
796 ("%s isn't a valid mach-o le, bad symbols\n", pathName));
797 }
798 else {
799 file->fSymbolBase = (struct nlist *)
800 (file->fMachO + file->fSymtab->symoff);
801 file->fStringBase = (char *)
802 (file->fMachO + file->fSymtab->stroff);
803 return_if( ( file->fSymtab->stroff + file->fSymtab->strsize
804 > file->fMachOSize ), false,
805 ("%s isn't a valid mach-o, bad symbols\n", pathName));
806 }
807
808 nsyms = file->fSymtab->nsyms;
809
810 // If this file the kernel and do we have an executable image
811 file->fNoKernelExecutable = (vm_page_size == file->fSymtab->symoff)
812 && (file->fSections[0].fSection->size == 0);
813
814 // Generate a table of pointers to strings indexed by the symbol number
815
816 file->fSym2Strings = DataCreate(nsyms * sizeof(const char *));
817 DataSetLength(file->fSym2Strings, nsyms * sizeof(const char *));
818 return_if(!file->fSym2Strings, false,
819 ("Unable to allocate memory - symbol string trans\n", pathName));
820 file->fSymbToStringTable = (const char **) DataGetPtr(file->fSym2Strings);
821
822 // Search for the first non-stab symbol in table
823 strsize = file->fSymtab->strsize;
824 strbase = file->fStringBase;
825 firstlocal = 0;
826 found295CPP = foundOSObject = false;
827 for (i = 0, sym = file->fSymbolBase; i < nsyms; i++, sym++) {
828 long strx = sym->n_un.n_strx;
829 const char *symname = strbase + strx;
830 unsigned char n_type;
831
832 return_if(((unsigned long) strx > strsize), false,
833 ("%s has an illegal string offset in symbol %d\n", pathName, i));
834
835 // Load up lookup symbol look table with sym names
836 file->fSymbToStringTable[i] = symname;
837
838 n_type = sym->n_type & (N_TYPE | N_EXT);
839
840 // Find the first exported symbol
841 if ( !firstlocal && (n_type & N_EXT) ) {
842 firstlocal = i;
843 file->fLocalSyms = sym;
844 }
845
846 // Find the a OSObject based subclass by searching for symbols
847 // that have a suffix of '10superClassE'
848 symname++; // Skip leading '_'
849
850 if (!foundOSObject
851 && (n_type == (N_SECT | N_EXT) || n_type == (N_ABS | N_EXT))
852 && strx) {
853 const char *suffix, *endSym;
854
855 endSym = symname + strlen(symname);
856
857 // Find out if this symbol has the superclass suffix.
858 if (symname[0] == kCPPSymbolPrefix[0]
859 && symname[1] == kCPPSymbolPrefix[1]) {
860
861 suffix = endSym - sizeof(k31SuperClassSuffix) + 1;
862
863 // Check for a gcc3 OSObject subclass
864 if (suffix > symname
865 && !strcmp(suffix, k31SuperClassSuffix))
866 foundOSObject = true;
867 }
868 else {
869 suffix = endSym - sizeof(k29SuperClassSuffix);
870
871 // Check for a gcc295 OSObject subclass
872 if (suffix > symname
873 && ('.' == *suffix || '$' == *suffix)
874 && !strcmp(suffix+1, k29SuperClassSuffix)) {
875 found295CPP = foundOSObject = true;
876 }
877 else if (!found295CPP) {
878 // Finally just check if we need to remangle
879 symname++; // skip leading '__'
880 while (*symname) {
881 if ('_' == *symname++ && '_' == *symname++) {
882 found295CPP = true;
883 break;
884 }
885 }
886 }
887 }
888 }
889 else if (sym->n_type == (N_EXT | N_UNDF)) {
890 if ( !file->fNLocal) // Find the last local symbol
891 file->fNLocal = i - firstlocal;
892 if (!found295CPP) {
893 symname++; // Skip possible second '_' at start.
894 while (*symname) {
895 if ('_' == *symname++ && '_' == *symname++) {
896 found295CPP = true;
897 break;
898 }
899 }
900 }
901 }
902 // Note symname is trashed at this point
903 }
904 return_if(i < nsyms, false,
905 ("%s isn't a valid mach-o, bad symbol strings\n", pathName));
906
907 return_if(!file->fLocalSyms, false, ("%s has no symbols?\n", pathName));
908
909 // If we don't have any undefined symbols then all symbols
910 // must be local so just compute it now if necessary.
911 if ( !file->fNLocal )
912 file->fNLocal = i - firstlocal;
913
914 file->fFoundOSObject = foundOSObject;
915
916 if (found295CPP && !remangleExternSymbols(file, pathName))
917 return false;
918
919 return true;
920}
921
922// @@@ gvdl: These functions need to be hashed they are
923// going to be way too slow for production code.
924static const struct nlist *
925findSymbolByAddress(const struct fileRecord *file, void *entry)
926{
927 // not quite so dumb linear search of all symbols
928 const struct nlist *sym;
929 int i, nsyms;
930
931 // First try to find the symbol in the most likely place which is the
932 // extern symbols
933 sym = file->fLocalSyms;
934 for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) {
935 if (sym->n_value == (unsigned long) entry && !(sym->n_type & N_STAB) )
936 return sym;
937 }
938
939 // Didn't find it in the external symbols so try to local symbols before
940 // giving up.
941 sym = file->fSymbolBase;
942 for (i = 0, nsyms = file->fSymtab->nsyms; i < nsyms; i++, sym++) {
943 if ( (sym->n_type & N_EXT) )
944 return NULL;
945 if ( sym->n_value == (unsigned long) entry && !(sym->n_type & N_STAB) )
946 return sym;
947 }
948
949 return NULL;
950}
951
952struct searchContext {
953 const char *fSymname;
954 const struct fileRecord *fFile;
955};
956
957static int symbolSearch(const void *vKey, const void *vSym)
958{
959 const struct searchContext *key = (const struct searchContext *) vKey;
960 const struct nlist *sym = (const struct nlist *) vSym;
961
962 return strcmp(key->fSymname + 1, symbolname(key->fFile, sym) + 1);
963}
964
965static const struct nlist *
966findSymbolByName(struct fileRecord *file, const char *symname)
967{
968 if (file->fRemangled) {
969 // @@@ gvdl: Performance problem
970 // Linear search as we don't sort after remangling
971 const struct nlist *sym;
972 int i = file->fLocalSyms - file->fSymbolBase;
973 int nLocal = file->fNLocal + i;
974
975 for (sym = file->fLocalSyms; i < nLocal; i++, sym++)
976 if (!strcmp(symNameByIndex(file, i) + 1, symname + 1))
977 return sym;
978 return NULL;
979 }
980 else {
981 struct searchContext context;
982
983 context.fSymname = symname;
984 context.fFile = file;
985 return (struct nlist *)
986 bsearch(&context,
987 file->fLocalSyms, file->fNLocal, sizeof(struct nlist),
988 symbolSearch);
989 }
990}
991
992static Boolean
993relocateSection(const struct fileRecord *file, struct sectionRecord *sectionRec)
994{
995 const struct nlist *symbol;
996 const struct section *section;
997 struct relocRecord *rec;
998 struct relocation_info *rinfo;
999 unsigned long i;
1000 unsigned long r_address, r_symbolnum, r_length;
1001 enum reloc_type_generic r_type;
1002 UInt8 *sectionBase;
1003 void **entry;
1004
1005 sectionRec->fRelocCache = DataCreate(
1006 sectionRec->fSection->nreloc * sizeof(struct relocRecord));
1007 if (!sectionRec->fRelocCache)
1008 return false;
1009
1010 section = sectionRec->fSection;
1011 sectionBase = file->fMachO + section->offset;
1012
1013 rec = (struct relocRecord *) DataGetPtr(sectionRec->fRelocCache);
1014 rinfo = (struct relocation_info *) (file->fMachO + section->reloff);
1015 for (i = 0; i < section->nreloc; i++, rec++, rinfo++) {
1016
1017 // Totally uninterested in scattered relocation entries
1018 if ( (rinfo->r_address & R_SCATTERED) )
1019 continue;
1020
1021 r_address = rinfo->r_address;
1022 entry = (void **) (sectionBase + r_address);
1023
1024 /*
1025 * The r_address field is really an offset into the contents of the
1026 * section and must reference something inside the section (Note
1027 * that this is not the case for PPC_RELOC_PAIR entries but this
1028 * can't be one with the above checks).
1029 */
1030 return_if(r_address >= section->size, false,
1031 ("Invalid relocation entry in %s - not in section\n", file->fPath));
1032
1033 // If we don't have a VANILLA entry or the Vanilla entry isn't
1034 // a 'long' then ignore the entry and try the next.
1035 r_type = (enum reloc_type_generic) rinfo->r_type;
1036 r_length = rinfo->r_length;
1037 if (r_type != GENERIC_RELOC_VANILLA || r_length != 2)
1038 continue;
1039
1040 r_symbolnum = rinfo->r_symbolnum;
1041
1042 /*
1043 * If rinfo->r_extern is set this relocation entry is an external entry
1044 * else it is a local entry.
1045 */
1046 if (rinfo->r_extern) {
1047 /*
1048 * This is an external relocation entry.
1049 * r_symbolnum is an index into the input file's symbol table
1050 * of the symbol being refered to. The symbol must be
1051 * undefined to be used in an external relocation entry.
1052 */
1053 return_if(r_symbolnum >= file->fSymtab->nsyms, false,
1054 ("Invalid relocation entry in %s - no symbol\n", file->fPath));
1055
1056 /*
1057 * If this is an indirect symbol resolve indirection (all chains
1058 * of indirect symbols have been resolved so that they point at
1059 * a symbol that is not an indirect symbol).
1060 */
1061 symbol = file->fSymbolBase;
1062 if ((symbol[r_symbolnum].n_type & N_TYPE) == N_INDR)
1063 r_symbolnum = symbol[r_symbolnum].n_value;
1064 symbol = &symbol[r_symbolnum];
1065
1066 return_if(symbol->n_type != (N_EXT | N_UNDF), false,
1067 ("Invalid relocation entry in %s - extern\n", file->fPath));
1068 }
1069 else {
1070 /*
1071 * If the symbol is not in any section then it can't be a
1072 * pointer to a local segment and I don't care about it.
1073 */
1074 if (r_symbolnum == R_ABS)
1075 continue;
1076
1077 // Note segment references are offset by 1 from 0.
1078 return_if(r_symbolnum > file->fNSects, false,
1079 ("Invalid relocation entry in %s - local\n", file->fPath));
1080
1081 // Find the symbol, if any, that backs this entry
1082 symbol = findSymbolByAddress(file, *entry);
1083 }
1084
1085 rec->fValue = *entry; // Save the previous value
1086 rec->fRInfo = rinfo; // Save a pointer to the reloc
1087 rec->fSymbol = symbol; // Record the current symbol
1088
1089 *entry = (void *) rec; // Save pointer to record in object image
1090 }
1091
1092 DataSetLength(sectionRec->fRelocCache, i * sizeof(struct relocRecord));
1093 ((struct fileRecord *) file)->fImageDirty = true;
1094
1095 return true;
1096}
1097
1098static const struct nlist *
1099findSymbolRefAtLocation(const struct fileRecord *file,
1100 struct sectionRecord *sctn, void **loc)
1101{
1102 if (file->fIsKernel) {
1103 if (*loc)
1104 return findSymbolByAddress(file, *loc);
1105 }
1106 else if (sctn->fRelocCache || relocateSection(file, sctn)) {
1107 struct relocRecord *reloc = (struct relocRecord *) *loc;
1108
1109 if (DataContainsAddr(sctn->fRelocCache, reloc))
1110 return reloc->fSymbol;
1111 }
1112
1113 return NULL;
1114}
1115
1116static Boolean
1117addClass(struct fileRecord *file,
1118 struct metaClassRecord *inClass,
1119 const char *cname)
1120{
1121 Boolean result = false;
1122 struct metaClassRecord *newClass = NULL;
1123 struct metaClassRecord **fileClasses = NULL;
1124 int len;
1125
1126 if (!file->fClassList) {
1127 file->fClassList = DataCreate(0);
1128 if (!file->fClassList)
1129 return false;
1130 }
1131
1132 do {
1133 // Attempt to allocate all necessary resource first
1134 len = strlen(cname) + 1
1135 + (int) (&((struct metaClassRecord *) 0)->fClassName);
1136 newClass = (struct metaClassRecord *) malloc(len);
1137 if (!newClass)
1138 break;
1139
1140 if (!DataAddLength(file->fClassList, sizeof(struct metaClassRecord *)))
1141 break;
1142 fileClasses = (struct metaClassRecord **)
1143 (DataGetPtr(file->fClassList) + DataGetLength(file->fClassList));
1144
1145 // Copy the meta Class structure and string name into newClass and
1146 // insert object at end of the file->fClassList and sMergeMetaClasses
1147 *newClass = *inClass;
1148 strcpy(newClass->fClassName, cname);
1149 fileClasses[-1] = newClass;
1150
1151 return true;
1152 } while (0);
1153
1154 if (fileClasses)
1155 DataAddLength(file->fClassList, -sizeof(struct metaClassRecord *));
1156
1157 if (newClass)
1158 free(newClass);
1159
1160 return result;
1161}
1162
1163static struct metaClassRecord *getClass(DataRef classList, const char *cname)
1164{
1165 if (classList) {
1166 int i, nclass;
1167 struct metaClassRecord **classes, *thisClass;
1168
1169 nclass = DataGetLength(classList) / sizeof(struct metaClassRecord *);
1170 classes = (struct metaClassRecord **) DataGetPtr(classList);
1171 for (i = 0; i < nclass; i++) {
1172 thisClass = classes[i];
1173 if (!strcmp(thisClass->fClassName, cname))
1174 return thisClass;
1175 }
1176 }
1177
1178 return NULL;
1179}
1180
1181// Add the class 'cname' to the list of known OSObject based classes
1182// Note 'sym' is the <cname>10superClassE symbol.
1183static Boolean
1184recordClass(struct fileRecord *file, const char *cname, const struct nlist *sym)
1185{
1186 Boolean result = false;
1187 char *supername = NULL;
1188 const char *classname = NULL;
1189 struct metaClassRecord newClass;
1190 char strbuffer[1024];
1191
1192 // Only do the work to find the super class if we are
1193 // not currently working on the kernel. The kernel is the end
1194 // of all superclass chains by definition as the kernel must be binary
1195 // compatible with itself.
1196 if (!file->fIsKernel) {
1197 const char *suffix;
1198 const struct nlist *supersym;
1199 const struct section *section;
1200 struct sectionRecord *sectionRec;
1201 unsigned char sectind = sym->n_sect;
1202 const char *superstr;
1203 void **location;
1204 int snamelen;
1205
1206 // We can't resolve anything that isn't in a real section
1207 // Note that the sectind is starts at one to make room for the
1208 // NO_SECT flag but the fNSects field isn't offset so we have a
1209 // '>' test. Which means this isn't an OSObject based class
1210 if (sectind == NO_SECT || sectind > file->fNSects) {
1211 result = true;
1212 goto finish;
1213 }
1214 sectionRec = file->fSections + sectind - 1;
1215 section = sectionRec->fSection;
1216 location = (void **) ( file->fMachO + section->offset
1217 + sym->n_value - section->addr );
1218
1219 supersym = findSymbolRefAtLocation(file, sectionRec, location);
1220 if (!supersym) {
1221 result = true; // No superclass symbol then it isn't an OSObject.
1222 goto finish;
1223 }
1224
1225 // Find string in file and skip leading '_' and then find the suffix
1226 superstr = symbolname(file, supersym) + 1;
1227 suffix = superstr + strlen(superstr) - sizeof(kGMetaSuffix) + 1;
1228 if (suffix <= superstr || strcmp(suffix, kGMetaSuffix)) {
1229 result = true; // Not an OSObject superclass so ignore it..
1230 goto finish;
1231 }
1232
1233 // Got a candidate so hand it over for class processing.
1234 snamelen = suffix - superstr - sizeof(kOSObjPrefix) + 2;
1235 supername = (char *) malloc(snamelen + 1);
1236 bcopy(superstr + sizeof(kOSObjPrefix) - 2, supername, snamelen);
1237 supername[snamelen] = '\0';
1238 }
1239
1240 do {
1241 break_if(getClass(file->fClassList, cname),
1242 ("Duplicate class %s in %s\n", cname, file->fPath));
1243
1244 snprintf(strbuffer, sizeof(strbuffer), "%s%s", kVTablePrefix, cname);
1245 newClass.fVTableSym = findSymbolByName(file, strbuffer);
1246 break_if(!newClass.fVTableSym,
1247 ("Can't find vtable %s in %s\n", cname, file->fPath));
1248
1249 newClass.fFile = file;
1250 newClass.fSuperName = supername;
1251 newClass.fPatchedVTable = NULL;
1252
1253 // Can't use cname as it may be a stack variable
1254 // However the vtable's string has the class name as a suffix
1255 // so why don't we use that rather than mallocing a string.
1256 classname = symbolname(file, newClass.fVTableSym)
1257 + sizeof(kVTablePrefix) - 1;
1258 break_if(!addClass(file, &newClass, classname),
1259 ("recordClass - no memory?\n"));
1260
1261 supername = NULL;
1262 result = true;
1263 } while (0);
1264
1265finish:
1266 if (supername)
1267 free(supername);
1268
1269 return result;
1270}
1271
1272
1273static Boolean getMetaClassGraph(struct fileRecord *file)
1274{
1275 const struct nlist *sym;
1276 int i, nsyms;
1277
1278 // Search the symbol table for the local symbols that are generated
1279 // by the metaclass system. There are three metaclass variables
1280 // that are relevant.
1281 //
1282 // <ClassName>.metaClass A pointer to the meta class structure.
1283 // <ClassName>.superClass A pointer to the super class's meta class.
1284 // <ClassName>.gMetaClass The meta class structure itself.
1285 // ___vt<ClassName> The VTable for the class <ClassName>.
1286 //
1287 // In this code I'm going to search for any symbols that
1288 // ends in k31SuperClassSuffix as this indicates this class is a conforming
1289 // OSObject subclass and will need to be patched, and it also
1290 // contains a pointer to the super class's meta class structure.
1291 sym = file->fLocalSyms;
1292 for (i = 0, nsyms = file->fNLocal; i < nsyms; i++, sym++) {
1293 const char *symname;
1294 const char *suffix;
1295 char classname[1024];
1296 unsigned char n_type = sym->n_type & (N_TYPE | N_EXT);
1297 int cnamelen;
1298
1299 // Check that the symbols is a global and that it has a name.
1300 if (((N_SECT | N_EXT) != n_type && (N_ABS | N_EXT) != n_type)
1301 || !sym->n_un.n_strx)
1302 continue;
1303
1304 // Only search from the last *sep* in the symbol.
1305 // but skip the leading '_' in all symbols first.
1306 symname = symbolname(file, sym) + 1;
1307 if (symname[0] != kCPPSymbolPrefix[0]
1308 || symname[1] != kCPPSymbolPrefix[1])
1309 continue;
1310
1311 suffix = symname + strlen(symname) - sizeof(k31SuperClassSuffix) + 1;
1312 if (suffix <= symname || strcmp(suffix, k31SuperClassSuffix))
1313 continue;
1314
1315 // Got a candidate so hand it over for class processing.
1316 cnamelen = suffix - symname - sizeof(kOSObjPrefix) + 2;
1317 return_if(cnamelen + 1 >= (int) sizeof(classname),
1318 false, ("Symbol %s is too long", symname));
1319
1320 bcopy(symname + sizeof(kOSObjPrefix) - 2, classname, cnamelen);
1321 classname[cnamelen] = '\0';
1322 if (!recordClass(file, classname, sym))
1323 return false;
1324 }
1325
1326 return_if(!file->fClassList, false, ("Internal error, "
1327 "getMetaClassGraph(%s) found no classes", file->fPath));
1328
1329 DEBUG_LOG(("Found %ld classes in %p for %s\n",
1330 DataGetLength(file->fClassList)/sizeof(void*),
1331 file->fClassList, file->fPath));
1332
1333 return true;
1334}
1335
1336static Boolean mergeOSObjectsForFile(const struct fileRecord *file)
1337{
1338 int i, nmerged;
1339 Boolean foundDuplicates = false;
1340
1341 DEBUG_LOG(("Merging file %s\n", file->fPath)); // @@@ gvdl:
1342
1343 if (!file->fClassList)
1344 return true;
1345
1346 if (!sMergedFiles) {
1347 sMergedFiles = DataCreate(0);
1348 return_if(!sMergedFiles, false,
1349 ("Unable to allocate memory metaclass list\n", file->fPath));
1350 }
1351
1352 // Check to see if we have already merged this file
1353 nmerged = DataGetLength(sMergedFiles) / sizeof(struct fileRecord *);
1354 for (i = 0; i < nmerged; i++) {
1355 if (file == ((void **) DataGetPtr(sMergedFiles))[i])
1356 return true;
1357 }
1358
1359 if (!sMergeMetaClasses) {
1360 sMergeMetaClasses = DataCreate(0);
1361 return_if(!sMergeMetaClasses, false,
1362 ("Unable to allocate memory metaclass list\n", file->fPath));
1363 }
1364 else { /* perform a duplicate check */
1365 int i, j, cnt1, cnt2;
1366 struct metaClassRecord **list1, **list2;
1367
1368 list1 = (struct metaClassRecord **) DataGetPtr(file->fClassList);
1369 cnt1 = DataGetLength(file->fClassList) / sizeof(*list1);
1370 list2 = (struct metaClassRecord **) DataGetPtr(sMergeMetaClasses);
1371 cnt2 = DataGetLength(sMergeMetaClasses) / sizeof(*list2);
1372
1373 for (i = 0; i < cnt1; i++) {
1374 for (j = 0; j < cnt2; j++) {
1375 if (!strcmp(list1[i]->fClassName, list2[j]->fClassName)) {
1376 errprintf("duplicate class %s in %s & %s\n",
1377 list1[i]->fClassName,
1378 file->fPath, list2[j]->fFile->fPath);
1379 }
1380 }
1381 }
1382 }
1383 if (foundDuplicates)
1384 return false;
1385
1386 return_if(!DataAppendBytes(sMergedFiles, &file, sizeof(file)), false,
1387 ("Unable to allocate memory to merge %s\n", file->fPath));
1388
1389 return_if(!DataAppendData(sMergeMetaClasses, file->fClassList), false,
1390 ("Unable to allocate memory to merge %s\n", file->fPath));
1391
1392 if (file == sKernelFile)
1393 sMergedKernel = true;
1394
1395 return true;
1396}
1397
1398// Returns a pointer to the base of the section offset by the sections
1399// base address. The offset is so that we can add nlist::n_values directly
1400// to this address and get a valid pointer in our memory.
1401static unsigned char *
1402getSectionForSymbol(const struct fileRecord *file, const struct nlist *symb,
1403 void ***endP)
1404{
1405 const struct section *section;
1406 unsigned char sectind;
1407 unsigned char *base;
1408
1409 sectind = symb->n_sect; // Default to symbols section
1410 if ((symb->n_type & N_TYPE) == N_ABS && file->fIsKernel) {
1411 // Absolute symbol so we have to iterate over our sections
1412 for (sectind = 1; sectind <= file->fNSects; sectind++) {
1413 unsigned long start, end;
1414
1415 section = file->fSections[sectind - 1].fSection;
1416 start = section->addr;
1417 end = start + section->size;
1418 if (start <= symb->n_value && symb->n_value < end) {
1419 // Found the relevant section
1420 break;
1421 }
1422 }
1423 }
1424
1425 // Is the vtable in a valid section?
1426 return_if(sectind == NO_SECT || sectind > file->fNSects,
1427 (unsigned char *) -1,
1428 ("%s isn't a valid kext, bad section reference\n", file->fPath));
1429
1430 section = file->fSections[sectind - 1].fSection;
1431
1432 // for when we start walking the vtable so compute offset's now.
1433 base = file->fMachO + section->offset;
1434 *endP = (void **) (base + section->size);
1435
1436 return base - section->addr; // return with addr offset
1437}
1438
1439static Boolean resolveKernelVTable(struct metaClassRecord *metaClass)
1440{
1441 const struct fileRecord *file;
1442 struct patchRecord *patchedVTable;
1443 void **curEntry, **vtableEntries, **endSection;
1444 unsigned char *sectionBase;
1445 struct patchRecord *curPatch;
1446 int classSize;
1447
1448 // Should never occur but it doesn't cost us anything to check.
1449 if (metaClass->fPatchedVTable)
1450 return true;
1451
1452 DEBUG_LOG(("Kernel vtable %s\n", metaClass->fClassName)); // @@@ gvdl:
1453
1454 // Do we have a valid vtable to patch?
1455 return_if(!metaClass->fVTableSym,
1456 false, ("Internal error - no class vtable symbol?\n"));
1457
1458 file = metaClass->fFile;
1459
1460 // If the metaClass we are being to ask is in the kernel then we
1461 // need to do a quick scan to grab the fPatchList in a reliable format
1462 // however we don't need to check the superclass in the kernel
1463 // as the kernel vtables are always correct wrt themselves.
1464 // Note this ends the superclass chain recursion.
1465 return_if(!file->fIsKernel,
1466 false, ("Internal error - resolveKernelVTable not kernel\n"));
1467
1468 if (file->fNoKernelExecutable) {
1469 // Oh dear attempt to map the kernel's VM into my memory space
1470 return_if(file->fNoKernelExecutable, false,
1471 ("Internal error - fNoKernelExecutable not implemented yet\n"));
1472 }
1473
1474 // We are going to need the base and the end
1475 sectionBase = getSectionForSymbol(file, metaClass->fVTableSym, &endSection);
1476 if (-1 == (long) sectionBase)
1477 return false;
1478
1479 vtableEntries = (void **) (sectionBase + metaClass->fVTableSym->n_value);
1480 curEntry = vtableEntries + kVTablePreambleLen;
1481 for (classSize = 0; curEntry < endSection && *curEntry; classSize++)
1482 curEntry++;
1483
1484 return_if(*curEntry, false, ("Bad kernel image, short section\n"));
1485
1486 patchedVTable = (struct patchRecord *)
1487 malloc((classSize + 1) * sizeof(struct patchRecord));
1488 return_if(!patchedVTable, false, ("resolveKernelVTable - no memory\n"));
1489
1490 // Copy the vtable of this class into the patch table
1491 curPatch = patchedVTable;
1492 curEntry = vtableEntries + kVTablePreambleLen;
1493 for (; *curEntry; curEntry++, curPatch++) {
1494 curPatch->fSymbol = (struct nlist *)
1495 findSymbolByAddress(file, *curEntry);
1496 curPatch->fType = kSymbolLocal;
1497 }
1498
1499 // Tag the end of the patch vtable
1500 curPatch->fSymbol = NULL;
1501 metaClass->fPatchedVTable = patchedVTable;
1502
1503 return true;
1504}
1505
1506static const char *addNewString(struct fileRecord *file,
1507 const char *strname, int namelen)
1508{
1509 DataRef strings = 0;
1510 const char *newStr;
1511
1512 namelen++; // Include terminating '\0';
1513
1514 // Make sure we have a string table as well for this symbol
1515 if (file->fNewStringBlocks) {
1516 DataRef *blockTable = (DataRef *) DataGetPtr(file->fNewStringBlocks);
1517 int index = DataGetLength(file->fNewStringBlocks) / sizeof(DataRef*);
1518 strings = blockTable[index - 1];
1519 if (DataRemaining(strings) < namelen)
1520 strings = 0;
1521 }
1522 else
1523 {
1524 file->fNewStringBlocks = DataCreate(0);
1525 return_if(!file->fNewStringBlocks, NULL,
1526 ("Unable to allocate new string table %s\n", file->fPath));
1527 }
1528
1529 if (!strings) {
1530 int size = (namelen + 1023) & ~1023;
1531 if (size < 16 * 1024)
1532 size = 16 * 1024;
1533 strings = DataCreate(size);
1534 return_if(!strings, NULL,
1535 ("Unable to allocate new string block %s\n", file->fPath));
1536 return_if(
1537 !DataAppendBytes(file->fNewStringBlocks, &strings, sizeof(strings)),
1538 false, ("Unable to allocate string table for %s\n", file->fPath));
1539 }
1540
1541 newStr = DataGetEndPtr(strings);
1542 DataAppendBytes(strings, strname, namelen);
1543 return newStr;
1544}
1545
1546// reloc->fPatch must contain a valid pointer
1547static struct nlist *
1548getNewSymbol(struct fileRecord *file,
1549 const struct relocRecord *reloc, const char *supername)
1550{
1551 unsigned int size, i;
1552 struct nlist **sym;
1553 struct nlist *msym;
1554 struct relocation_info *rinfo;
1555 const char *newStr;
1556
1557 if (!file->fNewSymbols) {
1558 file->fNewSymbols = DataCreate(0);
1559 return_if(!file->fNewSymbols, NULL,
1560 ("Unable to allocate new symbol table for %s\n", file->fPath));
1561 }
1562
1563 rinfo = (struct relocation_info *) reloc->fRInfo;
1564 size = DataGetLength(file->fNewSymbols) / sizeof(struct nlist *);
1565 sym = (struct nlist **) DataGetPtr(file->fNewSymbols);
1566 for (i = 0; i < size; i++, sym++) {
1567 int symnum = i + file->fSymtab->nsyms;
1568 newStr = symNameByIndex(file, symnum);
1569 if (!strcmp(newStr, supername)) {
1570 rinfo->r_symbolnum = symnum;
1571 file->fSymbolsDirty = true;
1572 return *sym;
1573 }
1574 }
1575
1576 // Assert that this is a vaild symbol. I need this condition to be true
1577 // for the later code to make non-zero. So the first time through I'd
1578 // better make sure that it is 0.
1579 return_if(reloc->fSymbol->n_sect, NULL,
1580 ("Undefined symbol entry with non-zero section %s:%s\n",
1581 file->fPath, symbolname(file, reloc->fSymbol)));
1582
1583 // If we are here we didn't find the symbol so create a new one now
1584 msym = (struct nlist *) malloc(sizeof(struct nlist));
1585 return_if(!msym,
1586 NULL, ("Unable to create symbol table entry for %s", file->fPath));
1587 return_if(!DataAppendBytes(file->fNewSymbols, &msym, sizeof(msym)),
1588 NULL, ("Unable to grow symbol table for %s\n", file->fPath));
1589
1590 newStr = addNewString(file, supername, strlen(supername));
1591 if (!newStr)
1592 return NULL;
1593 // If we are here we didn't find the symbol so create a new one now
1594 return_if(!DataAppendBytes(file->fSym2Strings, &newStr, sizeof(newStr)),
1595 NULL, ("Unable to grow symbol table for %s\n", file->fPath));
1596 file->fSymbToStringTable = (const char **) DataGetPtr(file->fSym2Strings);
1597
1598 // Offset the string index by the original string table size
1599 // and negate the address to indicate that this is a 'new' symbol
1600 msym->n_un.n_strx = -1;
1601 msym->n_type = (N_EXT | N_UNDF);
1602 msym->n_sect = NO_SECT;
1603 msym->n_desc = 0;
1604 msym->n_value = (unsigned long) newStr;
1605
1606 // Mark the old symbol as being potentially deletable I can use the
1607 // n_sect field as the input symbol must be of type N_UNDF which means
1608 // that the n_sect field must be set to NO_SECT otherwise it is an
1609 // in valid input file.
1610 ((struct nlist *) reloc->fSymbol)->n_un.n_strx
1611 = -reloc->fSymbol->n_un.n_strx;
1612 ((struct nlist *) reloc->fSymbol)->n_sect = (unsigned char) -1;
1613
1614 rinfo->r_symbolnum = i + file->fSymtab->nsyms;
1615 file->fSymbolsDirty = true;
1616 return msym;
1617}
1618
1619static struct nlist *
1620fixOldSymbol(struct fileRecord *file,
1621 const struct relocRecord *reloc, const char *supername)
1622{
1623 unsigned int namelen;
1624 struct nlist *sym = (struct nlist *) reloc->fSymbol;
1625 const char *oldname = symbolname(file, sym);
1626
1627 // assert(sym->n_un.n_strx >= 0);
1628
1629 namelen = strlen(supername);
1630
1631 sym->n_un.n_strx = -sym->n_un.n_strx;
1632 if (oldname && namelen < strlen(oldname))
1633 {
1634 // Overwrite old string in string table
1635 strcpy((char *) oldname, supername);
1636 file->fSymbolsDirty = true;
1637 return sym;
1638 }
1639
1640 oldname = addNewString(file, supername, namelen);
1641 if (!oldname)
1642 return NULL;
1643
1644 file->fSymbToStringTable[sym - file->fSymbolBase] = oldname;
1645 file->fSymbolsDirty = true;
1646 return sym;
1647}
1648
1649static enum patchState
1650symbolCompare(const struct fileRecord *file,
1651 const struct nlist *classsym,
1652 const char *supername)
1653{
1654 const char *classname;
1655
1656
1657 // Check to see if the target function is locally defined
1658 // if it is then we can assume this is a local vtable override
1659 if ((classsym->n_type & N_TYPE) != N_UNDF)
1660 return kSymbolLocal;
1661
1662 // Check to see if both symbols point to the same symbol name
1663 // if so then we are still identical.
1664 classname = symbolname(file, classsym);
1665 if (!strcmp(classname, supername))
1666 return kSymbolIdentical;
1667
1668 // We know that the target's vtable entry is different from the
1669 // superclass' vtable entry. This means that we will have to apply a
1670 // patch to the current entry, however before returning lets check to
1671 // see if we have a _RESERVEDnnn field 'cause we can use this as a
1672 // registration point that must align between vtables.
1673 if (strstr(supername, kReservedNamePrefix))
1674 return kSymbolMismatch;
1675
1676 // OK, we have a superclass difference where the superclass doesn't
1677 // reference a pad function so assume that the superclass is correct.
1678 if (strstr(classname, kReservedNamePrefix))
1679 return kSymbolPadUpdate;
1680 else
1681 return kSymbolSuperUpdate;
1682}
1683
1684static Boolean patchVTable(struct metaClassRecord *metaClass)
1685{
1686 struct metaClassRecord *super = NULL;
1687 struct fileRecord *file;
1688 struct patchRecord *patchedVTable;
1689 struct relocRecord **curReloc, **vtableRelocs, **endSection;
1690 unsigned char *sectionBase;
1691 int classSize;
1692
1693 // Should never occur but it doesn't cost us anything to check.
1694 if (metaClass->fPatchedVTable)
1695 return true;
1696
1697 // Do we have a valid vtable to patch?
1698 return_if(!metaClass->fVTableSym,
1699 false, ("Internal error - no class vtable symbol?\n"));
1700
1701 file = metaClass->fFile;
1702
1703 // If the metaClass we are being to ask is in the kernel then we
1704 // need to do a quick scan to grab the fPatchList in a reliable format
1705 // however we don't need to check the superclass in the kernel
1706 // as the kernel vtables are always correct wrt themselves.
1707 // Note this ends the superclass chain recursion.
1708 return_if(file->fIsKernel,
1709 false, ("Internal error - patchVTable shouldn't used for kernel\n"));
1710
1711 if (!metaClass->fSuperName)
1712 return false;
1713
1714 // The class isn't in the kernel so make sure that the super class
1715 // is patched before patching ouselves.
1716 super = getClass(sMergeMetaClasses, metaClass->fSuperName);
1717 return_if(!super, false, ("Can't find superclass for %s : %s\n",
1718 metaClass->fClassName, metaClass->fSuperName));
1719
1720 // Superclass recursion if necessary
1721 if (!super->fPatchedVTable) {
1722 Boolean res;
1723
1724 if (super->fFile->fIsKernel)
1725 res = resolveKernelVTable(super);
1726 else
1727 res = patchVTable(super);
1728 if (!res)
1729 return false;
1730 }
1731
1732 DEBUG_LOG(("Patching %s\n", metaClass->fClassName)); // @@@ gvdl:
1733
1734 // We are going to need the base and the end
1735
1736 sectionBase = getSectionForSymbol(file,
1737 metaClass->fVTableSym, (void ***) &endSection);
1738 if (-1 == (long) sectionBase)
1739 return false;
1740
1741 vtableRelocs = (struct relocRecord **)
1742 (sectionBase + metaClass->fVTableSym->n_value);
1743 curReloc = vtableRelocs + kVTablePreambleLen;
1744 for (classSize = 0; curReloc < endSection && *curReloc; classSize++)
1745 curReloc++;
1746
1747 return_if(*curReloc, false,
1748 ("%s isn't a valid kext, short section\n", file->fPath));
1749
1750 patchedVTable = (struct patchRecord *)
1751 malloc((classSize + 1) * sizeof(struct patchRecord));
1752 return_if(!patchedVTable, false, ("patchedVTable - no memory\n"));
1753
1754 do {
1755 struct patchRecord *curPatch;
1756 struct nlist *symbol;
1757
1758 curPatch = patchedVTable;
1759 curReloc = vtableRelocs + kVTablePreambleLen;
1760
1761 // Grab the super table patches if necessary
1762 // Can't be patching a kernel table as we don't walk super
1763 // class chains in the kernel symbol space.
1764 if (super && super->fPatchedVTable) {
1765 const struct patchRecord *spp;
1766
1767 spp = super->fPatchedVTable;
1768
1769 for ( ; spp->fSymbol; curReloc++, spp++, curPatch++) {
1770 const char *supername =
1771 symbolname(super->fFile, spp->fSymbol);
1772
1773 symbol = (struct nlist *) (*curReloc)->fSymbol;
1774
1775 curPatch->fType = symbolCompare(file, symbol, supername);
1776 switch (curPatch->fType) {
1777 case kSymbolIdentical:
1778 case kSymbolLocal:
1779 break;
1780
1781 case kSymbolSuperUpdate:
1782 symbol = getNewSymbol(file, (*curReloc), supername);
1783 break;
1784
1785 case kSymbolPadUpdate:
1786 symbol = fixOldSymbol(file, (*curReloc), supername);
1787 break;
1788
1789 case kSymbolMismatch:
1790 errprintf("%s is not compatible with its superclass, "
1791 "%s superclass changed?\n",
1792 metaClass->fClassName, super->fClassName);
1793 goto abortPatch;
1794
1795 default:
1796 errprintf("Internal error - unknown patch type\n");
1797 goto abortPatch;
1798 }
1799 if (symbol) {
1800 curPatch->fSymbol = symbol;
1801 (*curReloc)->fSymbol = symbol;
1802 }
1803 else
1804 goto abortPatch;
1805 }
1806 }
1807
1808 // Copy the remainder of this class' vtable into the patch table
1809 for (; *curReloc; curReloc++, curPatch++) {
1810 // Local reloc symbols
1811 curPatch->fType = kSymbolLocal;
1812 curPatch->fSymbol = (struct nlist *) (*curReloc)->fSymbol;
1813 }
1814
1815 // Tag the end of the patch vtable
1816 curPatch->fSymbol = NULL;
1817
1818 metaClass->fPatchedVTable = patchedVTable;
1819 return true;
1820 } while(0);
1821
1822abortPatch:
1823 if (patchedVTable)
1824 free(patchedVTable);
1825
1826 return false;
1827}
1828
1829static Boolean growImage(struct fileRecord *file, vm_size_t delta)
1830{
1831#if !KERNEL
1832 file->fMachOSize += delta;
1833 return (file->fMachO + file->fMachOSize <= file->fPadEnd);
1834#else /* KERNEL */
1835 vm_address_t startMachO, endMachO, endMap;
1836 vm_offset_t newMachO;
1837 vm_size_t newsize;
1838 unsigned long i, last = 0;
1839 struct metaClassRecord **classes = NULL;
1840 struct sectionRecord *section;
1841 kern_return_t ret;
1842
1843 startMachO = (vm_address_t) file->fMachO;
1844 endMachO = startMachO + file->fMachOSize + delta;
1845 endMap = (vm_address_t) file->fMap + file->fMapSize;
1846
1847 // Do we have room in the current mapped image
1848 if (endMachO < round_page(endMap)) {
1849 file->fMachOSize += delta;
1850 return true;
1851 }
1852
1853 newsize = endMachO - startMachO;
1854 if (newsize < round_page(file->fMapSize)) {
1855 DEBUG_LOG(("Growing image %s by moving\n", file->fPath));
1856
1857 // We have room in the map if we shift the macho image within the
1858 // current map. We will have to patch up pointers into the object.
1859 newMachO = (vm_offset_t) file->fMap;
1860 bcopy((char *) startMachO, (char *) newMachO, file->fMachOSize);
1861 }
1862 else if (file->fIsKmem) {
1863 // kmem_alloced mapping so we can try a kmem_realloc
1864 ret = kmem_realloc(kernel_map,
1865 (vm_address_t) file->fMap,
1866 (vm_size_t) file->fMapSize,
1867 &newMachO,
1868 newsize);
1869 if (KERN_SUCCESS != ret)
1870 return false;
1871
1872 // If the mapping didn't move then just return
1873 if ((vm_address_t) file->fMap == newMachO) {
1874 file->fMachOSize = file->fMapSize = newsize;
1875 return true;
1876 }
1877
1878 DEBUG_LOG(("Growing image %s by reallocing\n", file->fPath));
1879 // We have relocated the kmem image so we are going to have to
1880 // move all of the pointers into the image around.
1881 }
1882 else {
1883 DEBUG_LOG(("Growing image %s by allocating\n", file->fPath));
1884 // The image doesn't have room for us and I can't kmem_realloc
1885 // then I just have to bite the bullet and copy the object code
1886 // into a bigger memory segment
1887 ret = kmem_alloc(kernel_map, &newMachO, newsize);
1888
1889 if (KERN_SUCCESS != ret)
1890 return false;
1891 bcopy((char *) startMachO, (void *) newMachO, file->fMachOSize);
1892 file->fIsKmem = true;
1893 }
1894
1895
1896 file->fMap = file->fMachO = (unsigned char *) newMachO;
1897 file->fMapSize = newsize;
1898 file->fMachOSize += delta; // Increment the image size
1899
1900 // If we are here then we have shifted the object image in memory
1901 // I really should change all of my pointers into the image to machO offsets
1902 // but I have run out of time. So I'm going to very quickly go over the
1903 // cached data structures and add adjustments to the addresses that are
1904 // affected. I wonder how long it will take me to get them all.
1905 //
1906 // For every pointer into the MachO I need to add an adjustment satisfying
1907 // the following simultanous equations
1908 // addr_old = macho_old + fixed_offset
1909 // addr_new = macho_new + fixed_offset therefore:
1910 // addr_new = addr_old + (macho_new - macho_old)
1911#define REBASE(addr, delta) ( ((vm_address_t) (addr)) += (delta) )
1912 delta = newMachO - startMachO;
1913
1914 // Rebase the cached-in object 'struct symtab_command' pointer
1915 REBASE(file->fSymtab, delta);
1916
1917 // Rebase the cached-in object 'struct nlist' pointer for all symbols
1918 REBASE(file->fSymbolBase, delta);
1919
1920 // Rebase the cached-in object 'struct nlist' pointer for local symbols
1921 REBASE(file->fLocalSyms, delta);
1922
1923 // Rebase the cached-in object 'char' pointer for the string table
1924 REBASE(file->fStringBase, delta);
1925
1926 // Ok now we have to go over all of the relocs one last time
1927 // to clean up the pad updates which had their string index negated
1928 // to indicate that we have finished with them.
1929 section = file->fSections;
1930 for (i = 0, last = file->fNSects; i < last; i++, section++)
1931 REBASE(section->fSection, delta);
1932
1933 // We only ever grow images that contain class lists so dont bother
1934 // the check if file->fClassList is non-zero 'cause it can't be
1935 // assert(file->fClassList);
1936 last = DataGetLength(file->fClassList)
1937 / sizeof(struct metaClassRecord *);
1938 classes = (struct metaClassRecord **) DataGetPtr(file->fClassList);
1939 for (i = 0; i < last; i++) {
1940 struct patchRecord *patch;
1941
1942 for (patch = classes[i]->fPatchedVTable; patch->fSymbol; patch++) {
1943 vm_address_t symAddr = (vm_address_t) patch->fSymbol;
1944
1945 // Only need to rebase if the symbol is part of the image
1946 // If this is a new symbol then it was independantly allocated
1947 if (symAddr >= startMachO && symAddr < endMachO)
1948 REBASE(patch->fSymbol, delta);
1949 }
1950 }
1951
1952 // Finally rebase all of the string table pointers
1953 last = file->fSymtab->nsyms;
1954 for (i = 0; i < last; i++)
1955 REBASE(file->fSymbToStringTable[i], delta);
1956
1957#undef REBASE
1958
1959 return true;
1960
1961#endif /* KERNEL */
1962}
1963
1964static Boolean
1965prepareFileForLink(struct fileRecord *file)
1966{
1967 unsigned long i, last, numnewsyms, newsymsize, newstrsize;
1968 struct sectionRecord *section;
1969 struct nlist **symp, *sym;
1970 DataRef newStrings, *stringBlocks;
1971
1972 // If we didn't even do a pseudo 'relocate' and dirty the image
1973 // then we can just return now.
1974 if (!file->fImageDirty)
1975 return true;
1976
1977DEBUG_LOG(("Linking 2 %s\n", file->fPath)); // @@@ gvdl:
1978
1979 // We have to go over all of the relocs to repair the damage
1980 // that we have done to the image when we did our 'relocation'
1981 section = file->fSections;
1982 for (i = 0, last = file->fNSects; i < last; i++, section++) {
1983 unsigned char *sectionBase;
1984 struct relocRecord *rec;
1985 unsigned long j, nreloc;
1986
1987 if (section->fRelocCache) {
1988 sectionBase = file->fMachO + section->fSection->offset;
1989 nreloc = section->fSection->nreloc;
1990 rec = (struct relocRecord *) DataGetPtr(section->fRelocCache);
1991
1992 // We will need to repair the reloc list
1993 for (j = 0; j < nreloc; j++, rec++) {
1994 void **entry;
1995 struct nlist *sym;
1996
1997 // Repair Damage to object image
1998 entry = (void **) (sectionBase + rec->fRInfo->r_address);
1999 *entry = rec->fValue;
2000
2001 // Check if the symbol that this relocation entry points
2002 // to is marked as erasable
2003 sym = (struct nlist *) rec->fSymbol;
2004 if (sym && sym->n_type == (N_EXT | N_UNDF)
2005 && sym->n_sect == (unsigned char) -1) {
2006 // It is in use so we better clear the mark
2007 sym->n_un.n_strx = -sym->n_un.n_strx;
2008 sym->n_sect = NO_SECT;
2009 }
2010 }
2011
2012 // Clean up the fRelocCache we don't need it any more.
2013 DataRelease(section->fRelocCache);
2014 section->fRelocCache = 0;
2015 }
2016 }
2017 file->fImageDirty = false; // Image is clean
2018
2019 // If we didn't dirty the symbol table then just return
2020 if (!file->fSymbolsDirty)
2021 return true;
2022
2023 // calculate total file size increase and check against padding
2024 if (file->fNewSymbols) {
2025 numnewsyms = DataGetLength(file->fNewSymbols);
2026 symp = (struct nlist **) DataGetPtr(file->fNewSymbols);
2027 }
2028 else {
2029 numnewsyms = 0;
2030 symp = 0;
2031 }
2032 numnewsyms /= sizeof(struct nlist *);
2033 file->fSymtab->nsyms += numnewsyms;
2034
2035 // old sting size + 30% rounded up to nearest page
2036 newstrsize = file->fSymtab->strsize * 21 / 16;
2037 newstrsize = (newstrsize + PAGE_MASK) & ~PAGE_MASK;
2038 newStrings = DataCreate(newstrsize);
2039 return_if(!newStrings, false,
2040 ("Unable to allocate a copy aside buffer, no memory\n"));
2041
2042 newsymsize = numnewsyms * sizeof(struct nlist);
2043 file->fStringBase += newsymsize;
2044 file->fSymtab->stroff += newsymsize;
2045
2046 last = file->fSymtab->nsyms - numnewsyms;
2047 newstrsize = 0;
2048 DataAppendBytes(newStrings, &newstrsize, 4); // Leading nuls
2049 sym = file->fSymbolBase;
2050
2051 // Pre-compute an already offset new symbol pointer. The offset is the
2052 // orignal symbol table.
2053 symp -= last;
2054 for (i = 0; i < file->fSymtab->nsyms; i++, sym++) {
2055 const char *str = symNameByIndex(file, i);
2056 int len = strlen(str) + 1;
2057 unsigned int strx;
2058
2059 // Rebase sym in the new symbol region
2060 if (i >= last)
2061 sym = symp[i];
2062
2063 if (sym->n_un.n_strx < 0 && sym->n_type == (N_EXT | N_UNDF)
2064 && (unsigned char) -1 == sym->n_sect) {
2065 // after patching we find that this symbol is no longer in
2066 // use. So invalidate it by converting it into an N_ABS
2067 // symbol, remove the external bit and null out the name.
2068 bzero(sym, sizeof(*sym));
2069 sym->n_type = N_ABS;
2070 }
2071 else {
2072 // Repair the symbol for the getNewSymbol case.
2073 if (-1 == sym->n_un.n_strx)
2074 sym->n_value = 0;
2075
2076 // Record the offset of the string in the new table
2077 strx = DataGetLength(newStrings);
2078 return_if(!DataAppendBytes(newStrings, str, len), false,
2079 ("Unable to append string, no memory\n"));
2080
2081 sym->n_un.n_strx = strx;
2082 file->fSymbToStringTable[i] = file->fStringBase + strx;
2083 }
2084 }
2085
2086 // Don't need the new strings any more
2087 last = DataGetLength(file->fNewStringBlocks) / sizeof(DataRef);
2088 stringBlocks = (DataRef *) DataGetPtr(file->fNewStringBlocks);
2089 for (i = 0; i < last; i++)
2090 DataRelease(stringBlocks[i]);
2091
2092 DataRelease(file->fNewStringBlocks);
2093 file->fNewStringBlocks = 0;
2094
2095 newstrsize = DataGetLength(newStrings);
2096 newstrsize = (newstrsize + 3) & ~3; // Round to nearest word
2097 return_if(
2098 !growImage(file, newsymsize + newstrsize - file->fSymtab->strsize),
2099 false, ("Unable to patch the extension, no memory\n", file->fPath));
2100
2101 // Push out the new symbol table if necessary
2102 if (numnewsyms) {
2103 caddr_t base;
2104
2105 // Append the new symbols to the original symbol table.
2106 base = (caddr_t) file->fSymbolBase
2107 + (file->fSymtab->nsyms - numnewsyms) * sizeof(struct nlist);
2108 symp = (struct nlist **) DataGetPtr(file->fNewSymbols);
2109 for (i = 0; i < numnewsyms; i++, base += sizeof(struct nlist), symp++)
2110 bcopy(*symp, base, sizeof(struct nlist));
2111
2112 DataRelease(file->fNewSymbols);
2113 file->fNewSymbols = 0;
2114 }
2115
2116 // Push out the new string table if necessary
2117 if (newStrings) {
2118 unsigned long *base = (unsigned long *) file->fStringBase;
2119 unsigned long actuallen = DataGetLength(newStrings);
2120
2121 // Set the last word in string table to zero before copying data
2122 base[(newstrsize / sizeof(unsigned long)) - 1] = 0;
2123
2124 // Now copy the new strings back to the end of the file
2125 bcopy((caddr_t) DataGetPtr(newStrings), file->fStringBase, actuallen);
2126
2127 file->fSymtab->strsize = newstrsize;
2128
2129 DataRelease(newStrings);
2130 }
2131
2132 file->fSymbolsDirty = false;
2133
2134 return true;
2135}
2136
2137Boolean
2138#if KERNEL
2139kld_file_map(const char *pathName,
2140 unsigned char *map,
2141 size_t mapSize,
2142 Boolean isKmem)
2143#else
2144kld_file_map(const char *pathName)
2145#endif /* KERNEL */
2146{
2147 struct fileRecord file, *fp = 0;
2148
2149 // Already done no need to repeat
2150 fp = getFile(pathName);
2151 if (fp)
2152 return true;
2153
2154 bzero(&file, sizeof(file));
2155
2156#if KERNEL
2157 file.fMap = map;
2158 file.fMapSize = mapSize;
2159 file.fIsKmem = isKmem;
2160#else
2161 if (!mapObjectFile(&file, pathName))
2162 return false;
2163#endif /* KERNEL */
2164
2165 do {
2166 const struct machOMapping {
2167 struct mach_header h;
2168 struct load_command c[1];
2169 } *machO;
2170 const struct load_command *cmd;
2171 int i;
2172
2173 if (!findBestArch(&file, pathName))
2174 break;
2175
2176 machO = (const struct machOMapping *) file.fMachO;
2177 if (file.fMachOSize < machO->h.sizeofcmds)
2178 break;
2179
2180 file.fIsKernel = (MH_EXECUTE == machO->h.filetype);
2181
2182 // If the file type is MH_EXECUTE then this must be a kernel
2183 // as all Kernel extensions must be of type MH_OBJECT
2184 for (i = 0, cmd = &machO->c[0]; i < machO->h.ncmds; i++) {
2185 if (cmd->cmd == LC_SYMTAB)
2186 file.fSymtab = (struct symtab_command *) cmd;
2187 else if (cmd->cmd == LC_SEGMENT) {
2188 struct segment_command *seg = (struct segment_command *) cmd;
2189 int nsects = seg->nsects;
2190
2191 if (nsects)
2192 return_if(!parseSegments(&file, seg),
2193 false, ("%s isn't a valid mach-o, bad segment",
2194 pathName));
2195 else if (file.fIsKernel) {
2196#if KERNEL
2197 // We don't need to look for the LinkEdit segment unless
2198 // we are running in the kernel environment.
2199 if (!strcmp(kLinkEditSegName, seg->segname))
2200 file.fLinkEditSeg = seg;
2201#endif
2202 }
2203 }
2204
2205 cmd = (struct load_command *) ((UInt8 *) cmd + cmd->cmdsize);
2206 }
2207 break_if(!file.fSymtab,
2208 ("%s isn't a valid mach-o, no symbols\n", pathName));
2209
2210 if (!parseSymtab(&file, pathName))
2211 break;
2212
2213 fp = addFile(&file, pathName);
2214 if (!fp)
2215 break;
2216
2217 if (file.fFoundOSObject && !getMetaClassGraph(fp))
2218 break;
2219
2220 if (file.fIsKernel)
2221 sKernelFile = fp;
2222
2223#if KERNEL
2224 // Automatically load the kernel's link edit segment if we are
2225 // attempting to load a driver.
2226 if (!sKernelFile) {
2227 extern struct mach_header _mh_execute_header;
2228 extern struct segment_command *getsegbyname(char *seg_name);
2229
2230 struct segment_command *sg;
2231 size_t kernelSize;
2232 Boolean ret;
2233
2234 sg = (struct segment_command *) getsegbyname(kLinkEditSegName);
2235 break_if(!sg, ("Can't find kernel link edit segment\n"));
2236
2237 kernelSize = sg->vmaddr + sg->vmsize - (size_t) &_mh_execute_header;
2238 ret = kld_file_map(kld_basefile_name,
2239 (unsigned char *) &_mh_execute_header, kernelSize,
2240 /* isKmem */ false);
2241 break_if(!ret, ("kld can't map kernel file"));
2242 }
2243#endif /* KERNEL */
2244
2245 return true;
2246 } while(0);
2247
2248 // Failure path, then clean up
2249 if (fp)
2250 // @@@ gvdl: for the time being leak the file ref in the file table
2251 removeFile(fp);
2252 else
2253 unmapFile(&file);
2254
2255 return false;
2256}
2257
2258void *kld_file_getaddr(const char *pathName, long *size)
2259{
2260 struct fileRecord *file = getFile(pathName);
2261
2262 if (!file)
2263 return 0;
2264
2265 if (size)
2266 *size = file->fMachOSize;
2267
2268 return file->fMachO;
2269}
2270
2271void *kld_file_lookupsymbol(const char *pathName, const char *symname)
2272{
2273 struct fileRecord *file = getFile(pathName);
2274 const struct nlist *sym;
2275 const struct section *section;
2276 unsigned char *sectionBase;
2277 unsigned char sectind;
2278
2279 return_if(!file,
2280 NULL, ("Unknown file %s\n", pathName));
2281
2282 sym = findSymbolByName(file, symname);
2283
2284 // May be a non-extern symbol so look for it there
2285 if (!sym) {
2286 unsigned int i, nsyms;
2287
2288 sym = file->fSymbolBase;
2289 for (i = 0, nsyms = file->fSymtab->nsyms; i < nsyms; i++, sym++) {
2290 if ( (sym->n_type & N_EXT) ) {
2291 sym = 0;
2292 break; // Terminate search when we hit an extern
2293 }
2294 if ( (sym->n_type & N_STAB) )
2295 continue;
2296 if ( !strcmp(symname, symNameByIndex(file, i)) )
2297 break;
2298 }
2299 }
2300
2301 return_if(!sym,
2302 NULL, ("Unknown symbol %s in %s\n", symname, pathName));
2303
2304 // Is the vtable in a valid section?
2305 sectind = sym->n_sect;
2306 return_if(sectind == NO_SECT || sectind > file->fNSects, NULL,
2307 ("Malformed object file, invalid section reference for %s in %s\n",
2308 symname, pathName));
2309
2310 section = file->fSections[sectind - 1].fSection;
2311 sectionBase = file->fMachO + section->offset - section->addr;
2312
2313 return (void *) (sectionBase + sym->n_value);
2314}
2315
2316Boolean kld_file_merge_OSObjects(const char *pathName)
2317{
2318 struct fileRecord *file = getFile(pathName);
2319
2320 return_if(!file,
2321 false, ("Internal error - unable to find file %s\n", pathName));
2322
2323 return mergeOSObjectsForFile(file);
2324}
2325
2326Boolean kld_file_patch_OSObjects(const char *pathName)
2327{
2328 struct fileRecord *file = getFile(pathName);
2329 struct metaClassRecord **classes;
2330 unsigned long i, last;
2331
2332 return_if(!file,
2333 false, ("Internal error - unable to find file %s\n", pathName));
2334
2335 DEBUG_LOG(("Patch file %s\n", pathName)); // @@@ gvdl:
2336
2337 // If we don't have any classes we can return now.
2338 if (!file->fClassList)
2339 return true;
2340
2341 // If we haven't alread merged the kernel then do it now
2342 if (!sMergedKernel && sKernelFile)
2343 mergeOSObjectsForFile(sKernelFile);
2344 return_if(!sMergedKernel, false, ("Internal error no kernel?\n"));
2345
2346 if (!mergeOSObjectsForFile(file))
2347 return false;
2348
2349 // Patch all of the classes in this executable
2350 last = DataGetLength(file->fClassList) / sizeof(void *);
2351 classes = (struct metaClassRecord **) DataGetPtr(file->fClassList);
2352 for (i = 0; i < last; i++) {
2353 if (!patchVTable(classes[i]))
2354 return false;
2355 }
2356
2357 return true;
2358}
2359
2360Boolean kld_file_prepare_for_link()
2361{
2362 if (sMergedFiles) {
2363 unsigned long i, nmerged = 0;
2364 struct fileRecord **files;
2365
2366 // Check to see if we have already merged this file
2367 nmerged = DataGetLength(sMergedFiles) / sizeof(struct fileRecord *);
2368 files = (struct fileRecord **) DataGetPtr(sMergedFiles);
2369 for (i = 0; i < nmerged; i++) {
2370 if (!prepareFileForLink(files[i]))
2371 return false;
2372 }
2373 }
2374
2375 // Clear down the meta class table and merged file lists
2376 DataRelease(sMergeMetaClasses);
2377 DataRelease(sMergedFiles);
2378 sMergedFiles = sMergeMetaClasses = NULL;
2379 sMergedKernel = false;
2380
2381 return true;
2382}
2383
2384void kld_file_cleanup_all_resources()
2385{
2386 unsigned long i, nfiles;
2387
2388#if KERNEL // @@@ gvdl:
2389 // Debugger("kld_file_cleanup_all_resources");
2390#endif
2391
2392 if (!sFilesTable || !(nfiles = DataGetLength(sFilesTable)))
2393 return; // Nothing to do just return now
2394
2395 nfiles /= sizeof(struct fileRecord *);
2396 for (i = 0; i < nfiles; i++)
2397 removeFile(((void **) DataGetPtr(sFilesTable))[i]);
2398
2399 DataRelease(sFilesTable);
2400 sFilesTable = NULL;
2401
2402 // Don't really have to clean up anything more as the whole
2403 // malloc engine is going to be released and I couldn't be bothered.
2404}
2405
2406
2407#if !KERNEL
2408#if 0
2409static const struct fileRecord *sortFile;
2410static int symCompare(const void *vSym1, const void *vSym2)
2411{
2412 const struct nlist *sym1 = vSym1;
2413 const struct nlist *sym2 = vSym2;
2414
2415 {
2416 unsigned int ind1, ind2;
2417
2418 ind1 = sym1->n_type & N_TYPE;
2419 ind2 = sym2->n_type & N_TYPE;
2420 if (ind1 != ind2) {
2421 // if sym1 is undefined then sym1 must come later than sym2
2422 if (ind1 == N_UNDF)
2423 return 1;
2424 // if sym2 is undefined then sym1 must come earlier than sym2
2425 if (ind2 == N_UNDF)
2426 return -1;
2427 /* drop out if neither are undefined */
2428 }
2429 }
2430
2431 {
2432 const struct fileRecord *file = sortFile;
2433 const char *name1, *name2;
2434
2435 name1 = file->fStringBase + sym1->n_un.n_strx;
2436 name2 = file->fStringBase + sym2->n_un.n_strx;
2437 return strcmp(name1, name2);
2438 }
2439}
2440#endif /* 0 */
2441
2442Boolean kld_file_debug_dump(const char *pathName, const char *outName)
2443{
2444 const struct fileRecord *file = getFile(pathName);
2445 int fd;
2446 Boolean ret = false;
2447
2448 return_if(!file, false, ("Unknown file %s for dumping\n", pathName));
2449
2450 fd = open(outName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2451 return_if(-1 == fd, false, ("Can't create output file %s - %s(%d)\n",
2452 outName, strerror(errno), errno));
2453
2454 do {
2455#if 0
2456 // Sorting doesn't work until I fix the relocs too?
2457
2458 // sort the symbol table appropriately
2459 unsigned int nsyms = file->fSymtab->nsyms
2460 - (file->fLocalSyms - file->fSymbolBase);
2461 sortFile = file;
2462 heapsort((void *) file->fLocalSyms, nsyms, sizeof(struct nlist),
2463 symCompare);
2464#endif
2465
2466 break_if(-1 == write(fd, file->fMachO, file->fMachOSize),
2467 ("Can't dump output file %s - %s(%d)\n",
2468 outName, strerror(errno), errno));
2469 ret = true;
2470 } while(0);
2471
2472 close(fd);
2473
2474 return ret;
2475}
2476
2477#endif /* !KERNEL */
2478