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