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