2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
26 * 2001-05-30 gvdl Initial implementation of the vtable patcher.
28 // 45678901234567890123456789012345678901234567890123456789012345678901234567890
30 #include <mach-o/fat.h>
31 #include <mach-o/loader.h>
32 #include <mach-o/nlist.h>
33 #include <mach-o/reloc.h>
40 #include <sys/systm.h>
42 #include <libkern/OSTypes.h>
44 #include <libsa/stdlib.h>
45 #include <libsa/mach/mach.h>
47 #include "mach_loader.h"
49 #include <vm/vm_kern.h>
51 enum { false = 0, true = 1 };
53 #define vm_page_size page_size
55 extern load_return_t
fatfile_getarch(
56 void * vp
, // normally a (struct vnode *)
58 struct fat_arch
* archret
);
67 #include <sys/errno.h>
68 #include <sys/fcntl.h>
72 #include <mach/mach.h>
73 #include <mach/mach_error.h>
75 #include <mach-o/arch.h>
77 #include <CoreFoundation/CoreFoundation.h>
81 #include "kld_patch.h"
84 static __inline__
void DIE(void) { IODelay(2000000000); }
86 #define LOG_DELAY() IODelay(200000)
87 #define DEBUG_LOG(x) do { IOLog x; LOG_DELAY(); } while(0)
96 // OSObject symbol prefixes and suffixes
97 #define kVTablePrefix "___vt"
98 #define kReservedPrefix "__RESERVED"
99 #define kSuperClassSuffix ".superClass"
100 #define kGMetaSuffix ".gMetaClass"
101 #define kLinkEditSegName SEG_LINKEDIT
103 // GCC 2.95 drops 2 leading constants in the vtable
104 #define kVTablePreambleLen 2
106 // Last address that I'm willing to try find vm in
107 #define kTopAddr ((unsigned char *) (1024 * 1024 * 1024))
109 // Size in bytes that Data Ref object's get increased in size
110 // Must be a power of 2
111 #define kDataCapacityIncrement 128
113 // My usual set of helper macros. I personally find these macros
114 // easier to read in the code rather than an explicit error condition
115 // check. If I don't make it easy then I may get lazy ond not check
116 // everything. I'm sorry if you find this code harder to read.
118 // break_if will evaluate the expression and if it is true
119 // then it will print the msg, which is enclosed in parens
120 // and then break. Usually used in loops are do { } while (0)
121 #define break_if(expr, msg) \
127 // return_if will evaluate expr and if true it will log the
128 // msg, which is enclosed in parens, and then it will return
129 // with the return code of ret.
130 #define return_if(expr, ret, msg) do { \
138 #define MIN(a,b) (((a)<(b))?(a):(b))
141 #define MAX(a,b) (((a)>(b))?(a):(b))
144 typedef struct Data
{
145 unsigned long fLength
, fCapacity
;
146 unsigned char *fData
;
149 struct sectionRecord
{
150 const struct section
*fSection
;
163 struct nlist
*fSymbol
;
164 enum patchState fType
;
169 const struct nlist
*fSymbol
;
170 struct relocation_info
*fRInfo
;
174 struct metaClassRecord
{
176 struct fileRecord
*fFile
;
177 const struct nlist
*fVTableSym
;
178 struct patchRecord
*fPatchedVTable
;
183 size_t fMapSize
, fMachOSize
;
185 unsigned char *fMap
, *fMachO
, *fPadEnd
;
188 DataRef fNewSymbols
, fNewStrings
;
189 struct symtab_command
*fSymtab
;
190 struct sectionRecord
*fSections
;
192 struct nlist
*fSymbolBase
;
193 const struct nlist
*fLocalSyms
;
194 unsigned int fNSects
;
197 Boolean fIsKernel
, fNoKernelExecutable
, fIsKmem
;
198 Boolean fImageDirty
, fSymbolsDirty
;
201 static DataRef sFilesTable
;
202 static struct fileRecord
*sKernelFile
;
204 static DataRef sMergedFiles
;
205 static DataRef sMergeMetaClasses
;
206 static Boolean sMergedKernel
;
208 static void errprintf(const char *fmt
, ...)
210 extern void kld_error_vprintf(const char *format
, va_list ap
);
215 kld_error_vprintf(fmt
, ap
);
221 static __inline__
unsigned long DataGetLength(DataRef data
)
223 return data
->fLength
;
226 static __inline__
unsigned char *DataGetPtr(DataRef data
)
232 static __inline__ Boolean
DataContainsAddr(DataRef data
, void *vAddr
)
234 unsigned char *addr
= vAddr
;
236 return (data
->fData
<= addr
) && (addr
< data
->fData
+ data
->fLength
);
239 static __inline__ Boolean
DataAddLength(DataRef data
, unsigned long length
)
241 static Boolean
DataSetLength(DataRef data
, unsigned long length
);
242 return DataSetLength(data
, data
->fLength
+ length
);
245 static __inline__ Boolean
246 DataAppendBytes(DataRef data
, const void *addr
, unsigned int len
)
248 unsigned long size
= DataGetLength(data
);
250 if (!DataAddLength(data
, len
))
253 bcopy(addr
, DataGetPtr(data
) + size
, len
);
257 static __inline__ Boolean
DataAppendData(DataRef dst
, DataRef src
)
259 return DataAppendBytes(dst
, DataGetPtr(src
), DataGetLength(src
));
262 static Boolean
DataSetLength(DataRef data
, unsigned long length
)
264 // Don't bother to ever shrink a data object.
265 if (length
> data
->fCapacity
) {
266 unsigned char *newData
;
267 unsigned long newCapacity
;
269 newCapacity
= length
+ kDataCapacityIncrement
- 1;
270 newCapacity
&= ~(kDataCapacityIncrement
- 1);
271 newData
= (unsigned char *) realloc(data
->fData
, newCapacity
);
275 bzero(newData
+ data
->fCapacity
, newCapacity
- data
->fCapacity
);
276 data
->fData
= newData
;
277 data
->fCapacity
= newCapacity
;
280 data
->fLength
= length
;
284 static DataRef
DataCreate(unsigned long length
)
286 DataRef data
= (DataRef
) malloc(sizeof(Data
));
290 data
->fCapacity
= kDataCapacityIncrement
;
292 data
->fCapacity
= length
+ kDataCapacityIncrement
- 1;
293 data
->fCapacity
&= ~(kDataCapacityIncrement
- 1);
296 data
->fData
= (unsigned char *) malloc(data
->fCapacity
);
302 bzero(data
->fData
, data
->fCapacity
);
303 data
->fLength
= length
;
308 static void DataRelease(DataRef data
)
319 symbolname(const struct fileRecord
*file
, const struct nlist
*sym
)
321 unsigned long strsize
;
322 long strx
= sym
->n_un
.n_strx
;
325 return file
->fStringBase
+ strx
;
327 strsize
= file
->fSymtab
->strsize
;
330 return file
->fStringBase
+ strx
;
333 return (char *) DataGetPtr(file
->fNewStrings
) + strx
;
336 static struct fileRecord
*getFile(const char *path
)
340 struct fileRecord
**files
;
342 // Check to see if we have already merged this file
343 nfiles
= DataGetLength(sFilesTable
) / sizeof(struct fileRecord
*);
344 files
= (struct fileRecord
**) DataGetPtr(sFilesTable
);
345 for (i
= 0; i
< nfiles
; i
++) {
346 if (!strcmp(path
, files
[i
]->fPath
))
354 static struct fileRecord
* addFile(struct fileRecord
*file
)
356 struct fileRecord
*newFile
;
359 sFilesTable
= DataCreate(0);
364 newFile
= (struct fileRecord
*) malloc(sizeof(struct fileRecord
));
368 if (!DataAppendBytes(sFilesTable
, &newFile
, sizeof(newFile
))) {
373 bcopy(file
, newFile
, sizeof(struct fileRecord
));
377 // @@@ gvdl: need to clean up the sMergeMetaClasses
378 // @@@ gvdl: I had better fix the object file up again
379 static void removeFile(struct fileRecord
*file
)
381 if (file
->fClassList
) {
382 DataRelease(file
->fClassList
);
383 file
->fClassList
= 0;
386 if (file
->fSectData
) {
387 struct sectionRecord
*section
;
388 unsigned int i
, nsect
;
390 nsect
= file
->fNSects
;
391 section
= file
->fSections
;
392 for (i
= 0; i
< nsect
; i
++, section
++) {
393 if (section
->fRelocCache
) {
394 DataRelease(section
->fRelocCache
);
395 section
->fRelocCache
= 0;
399 DataRelease(file
->fSectData
);
408 kmem_free(kernel_map
, (vm_address_t
) file
->fMap
, file
->fMapSize
);
414 padVM
= round_page((vm_address_t
) file
->fMap
+ file
->fMapSize
);
415 padSize
= (vm_size_t
) ((vm_address_t
) file
->fPadEnd
- padVM
);
416 (void) vm_deallocate(mach_task_self(), padVM
, padSize
);
420 (void) munmap((caddr_t
) file
->fMap
, file
->fMapSize
);
430 mapObjectFile(struct fileRecord
*file
)
432 Boolean result
= false;
433 static unsigned char *sFileMapBaseAddr
;
437 if (!sFileMapBaseAddr
) {
439 vm_address_t probeAddr
;
441 // If we don't already have a base addr find any random chunk
442 // of 32 meg of VM and to use the 16 meg boundrary as a base.
443 ret
= vm_allocate(mach_task_self(), &probeAddr
,
444 32 * 1024 * 1024, VM_FLAGS_ANYWHERE
);
445 return_if(KERN_SUCCESS
!= ret
, false,
446 ("Unable to allocate base memory %s\n", mach_error_string(ret
)));
447 (void) vm_deallocate(mach_task_self(), probeAddr
, 32 * 1024 * 1024);
449 // Now round to the next 16 Meg boundrary
450 probeAddr
= (probeAddr
+ (16 * 1024 * 1024 - 1))
451 & ~(16 * 1024 * 1024 - 1);
452 sFileMapBaseAddr
= (unsigned char *) probeAddr
;
455 fd
= open(file
->fPath
, O_RDONLY
, 0);
456 return_if(fd
== -1, false, ("Can't open %s for reading - %s\n",
457 file
->fPath
, strerror(errno
)));
464 break_if(fstat(fd
, &sb
) == -1,
465 ("Can't stat %s - %s\n", file
->fPath
, strerror(errno
)));
467 file
->fMapSize
= sb
.st_size
;
468 file
->fMap
= sFileMapBaseAddr
;
470 while (file
->fMap
< kTopAddr
) {
472 vm_address_t padVMEnd
;
475 padVM
= round_page((vm_address_t
) file
->fMap
+ file
->fMapSize
);
476 retaddr
= (int) mmap(file
->fMap
, file
->fMapSize
,
477 PROT_READ
|PROT_WRITE
,
478 MAP_FIXED
|MAP_FILE
|MAP_PRIVATE
,
481 break_if(ENOMEM
!= errno
,
482 ("mmap failed %d - %s\n", errno
, strerror(errno
)));
484 file
->fMap
= (unsigned char *) padVM
;
489 // Round up padVM to the next page after the file and assign at
490 // least another fMapSize more room rounded up to the next page
492 padVMEnd
= round_page(padVM
+ file
->fMapSize
);
493 padSize
= padVMEnd
- padVM
;
495 mach_task_self(), &padVM
, padSize
, VM_FLAGS_FIXED
);
496 if (KERN_SUCCESS
== ret
) {
497 file
->fPadEnd
= (unsigned char *) padVMEnd
;
501 munmap(file
->fMap
, file
->fMapSize
);
502 break_if(KERN_INVALID_ADDRESS
!= ret
,
503 ("Unable to allocate pad vm for %s - %s\n",
504 file
->fPath
, mach_error_string(ret
)));
506 file
->fMap
= (unsigned char *) padVMEnd
;
507 continue; // try again wherever the vm system wants
511 if (-1 == retaddr
|| KERN_SUCCESS
!= ret
)
514 break_if(file
->fMap
>= kTopAddr
,
515 ("Unable to map memory %s\n", file
->fPath
));
517 sFileMapBaseAddr
= file
->fPadEnd
;
526 static Boolean
findBestArch(struct fileRecord
*file
)
529 struct fat_header
*fat
;
532 file
->fMachOSize
= file
->fMapSize
;
533 file
->fMachO
= file
->fMap
;
534 magic
= ((const struct mach_header
*) file
->fMachO
)->magic
;
535 fat
= (struct fat_header
*) file
->fMachO
;
537 // Try to figure out what type of file this is
538 return_if(file
->fMapSize
< sizeof(unsigned long), false,
539 ("%s isn't a valid object file - no magic\n", file
->fPath
));
543 // CIGAM is byte-swapped MAGIC
544 if (magic
== FAT_MAGIC
|| magic
== FAT_CIGAM
) {
546 load_return_t load_return
;
547 struct fat_arch fatinfo
;
549 load_return
= fatfile_getarch(NULL
, (vm_address_t
) fat
, &fatinfo
);
550 return_if(load_return
!= LOAD_SUCCESS
, false,
551 ("Extension \"%s\": has no code for this computer\n", file
->fPath
));
553 file
->fMachO
= file
->fMap
+ fatinfo
.offset
;
554 file
->fMachOSize
= fatinfo
.size
;
555 magic
= ((const struct mach_header
*) file
->fMachO
)->magic
;
560 // Do we need to in-place swap the endianness of the fat header?
561 if (magic
== FAT_CIGAM
) {
563 struct fat_arch
*arch
;
565 fat
->nfat_arch
= NXSwapBigLongToHost(fat
->nfat_arch
);
566 return_if(file
->fMapSize
< sizeof(struct fat_header
)
567 + fat
->nfat_arch
* sizeof(struct fat_arch
),
568 false, ("%s is too fat\n", file
->fPath
));
570 arch
= (struct fat_arch
*) &fat
[1];
571 for (i
= 0; i
< fat
->nfat_arch
; i
++) {
572 arch
[i
].cputype
= NXSwapBigLongToHost(arch
[i
].cputype
);
573 arch
[i
].cpusubtype
= NXSwapBigLongToHost(arch
[i
].cpusubtype
);
574 arch
[i
].offset
= NXSwapBigLongToHost(arch
[i
].offset
);
575 arch
[i
].size
= NXSwapBigLongToHost(arch
[i
].size
);
576 arch
[i
].align
= NXSwapBigLongToHost(arch
[i
].align
);
579 magic
= NXSwapBigLongToHost(fat
->magic
);
582 // Now see if we can find any valid architectures
583 if (magic
== FAT_MAGIC
) {
584 const NXArchInfo
*myArch
;
585 unsigned long fatsize
;
586 struct fat_arch
*arch
;
588 fatsize
= sizeof(struct fat_header
)
589 + fat
->nfat_arch
* sizeof(struct fat_arch
);
590 return_if(file
->fMapSize
< fatsize
,
591 false, ("%s isn't a valid fat file\n", file
->fPath
));
593 myArch
= NXGetLocalArchInfo();
594 arch
= NXFindBestFatArch(myArch
->cputype
, myArch
->cpusubtype
,
595 (struct fat_arch
*) &fat
[1], fat
->nfat_arch
);
597 false, ("%s hasn't got arch for %s\n", file
->fPath
, myArch
->name
));
598 return_if(arch
->offset
+ arch
->size
> file
->fMapSize
,
599 false, ("%s's %s arch is incomplete\n", file
->fPath
, myArch
->name
));
600 file
->fMachO
= file
->fMap
+ arch
->offset
;
601 file
->fMachOSize
= arch
->size
;
602 magic
= ((const struct mach_header
*) file
->fMachO
)->magic
;
607 return_if(magic
!= MH_MAGIC
,
608 false, ("%s isn't a valid mach-o\n", file
->fPath
));
614 parseSegments(struct fileRecord
*file
, struct segment_command
*seg
)
616 struct sectionRecord
*sections
;
617 int i
, nsects
= seg
->nsects
;
618 const struct segmentMap
{
619 struct segment_command seg
;
620 const struct section sect
[1];
625 // We don't need to look for the LinkEdit segment unless
626 // we are running in the kernel environment.
627 if (!strcmp(kLinkEditSegName
, seg
->segname
)) {
628 // Grab symbol table from linkedit we will need this later
629 file
->fSymbolBase
= (void *) seg
;
633 return true; // Nothing more to do, so that was easy.
636 if (!file
->fSectData
) {
637 file
->fSectData
= DataCreate(0);
638 if (!file
->fSectData
)
642 // Increase length of section DataRef and cache data pointer
643 if (!DataAddLength(file
->fSectData
, nsects
* sizeof(struct sectionRecord
)))
645 file
->fSections
= (struct sectionRecord
*) DataGetPtr(file
->fSectData
);
647 // Initialise the new sections
648 sections
= &file
->fSections
[file
->fNSects
];
649 file
->fNSects
+= nsects
;
650 for (i
= 0, segMap
= (struct segmentMap
*) seg
; i
< nsects
; i
++)
651 sections
[i
].fSection
= &segMap
->sect
[i
];
656 // @@@ gvdl: These functions need to be hashed they are
657 // going to be way too slow for production code.
658 static const struct nlist
*
659 findSymbolByAddress(const struct fileRecord
*file
, void *entry
)
661 // not quite so dumb linear search of all symbols
662 const struct nlist
*sym
;
665 // First try to find the symbol in the most likely place which is the
667 sym
= file
->fLocalSyms
;
668 for (i
= 0, nsyms
= file
->fNLocal
; i
< nsyms
; i
++, sym
++) {
669 if (sym
->n_value
== (unsigned long) entry
&& !(sym
->n_type
& N_STAB
) )
673 // Didn't find it in the external symbols so try to local symbols before
675 sym
= file
->fSymbolBase
;
676 for (i
= 0, nsyms
= file
->fSymtab
->nsyms
; i
< nsyms
; i
++, sym
++) {
677 if ( (sym
->n_type
& N_EXT
) )
679 if ( sym
->n_value
== (unsigned long) entry
&& !(sym
->n_type
& N_STAB
) )
686 struct searchContext
{
687 const char *fSymname
;
688 const char *fStrbase
;
691 static int symbolSearch(const void *vKey
, const void *vSym
)
693 const struct searchContext
*key
= (const struct searchContext
*) vKey
;
694 const struct nlist
*sym
= (const struct nlist
*) vSym
;
696 return strcmp(key
->fSymname
, key
->fStrbase
+ sym
->n_un
.n_strx
);
699 static const struct nlist
*
700 findSymbolByName(struct fileRecord
*file
, const char *symname
)
702 struct searchContext context
;
704 context
.fSymname
= symname
;
705 context
.fStrbase
= file
->fStringBase
;
706 return (struct nlist
*)
708 file
->fLocalSyms
, file
->fNLocal
, sizeof(struct nlist
),
713 relocateSection(const struct fileRecord
*file
, struct sectionRecord
*sectionRec
)
715 const struct nlist
*symbol
;
716 const struct section
*section
;
717 struct relocRecord
*rec
;
718 struct relocation_info
*rinfo
;
720 unsigned long r_address
, r_symbolnum
, r_length
;
721 enum reloc_type_generic r_type
;
725 sectionRec
->fRelocCache
= DataCreate(
726 sectionRec
->fSection
->nreloc
* sizeof(struct relocRecord
));
727 if (!sectionRec
->fRelocCache
)
730 section
= sectionRec
->fSection
;
731 sectionBase
= file
->fMachO
+ section
->offset
;
733 rec
= (struct relocRecord
*) DataGetPtr(sectionRec
->fRelocCache
);
734 rinfo
= (struct relocation_info
*) (file
->fMachO
+ section
->reloff
);
735 for (i
= 0; i
< section
->nreloc
; i
++, rec
++, rinfo
++) {
737 // Totally uninterested in scattered relocation entries
738 if ( (rinfo
->r_address
& R_SCATTERED
) )
741 r_address
= rinfo
->r_address
;
742 entry
= (void **) (sectionBase
+ r_address
);
745 * The r_address field is really an offset into the contents of the
746 * section and must reference something inside the section (Note
747 * that this is not the case for PPC_RELOC_PAIR entries but this
748 * can't be one with the above checks).
750 return_if(r_address
>= section
->size
, false,
751 ("Invalid relocation entry in %s - not in section\n", file
->fPath
));
753 // If we don't have a VANILLA entry or the Vanilla entry isn't
754 // a 'long' then ignore the entry and try the next.
755 r_type
= (enum reloc_type_generic
) rinfo
->r_type
;
756 r_length
= rinfo
->r_length
;
757 if (r_type
!= GENERIC_RELOC_VANILLA
|| r_length
!= 2)
760 r_symbolnum
= rinfo
->r_symbolnum
;
763 * If rinfo->r_extern is set this relocation entry is an external entry
764 * else it is a local entry.
766 if (rinfo
->r_extern
) {
768 * This is an external relocation entry.
769 * r_symbolnum is an index into the input file's symbol table
770 * of the symbol being refered to. The symbol must be
771 * undefined to be used in an external relocation entry.
773 return_if(r_symbolnum
>= file
->fSymtab
->nsyms
, false,
774 ("Invalid relocation entry in %s - no symbol\n", file
->fPath
));
777 * If this is an indirect symbol resolve indirection (all chains
778 * of indirect symbols have been resolved so that they point at
779 * a symbol that is not an indirect symbol).
781 symbol
= file
->fSymbolBase
;
782 if ((symbol
[r_symbolnum
].n_type
& N_TYPE
) == N_INDR
)
783 r_symbolnum
= symbol
[r_symbolnum
].n_value
;
784 symbol
= &symbol
[r_symbolnum
];
786 return_if(symbol
->n_type
!= (N_EXT
| N_UNDF
), false,
787 ("Invalid relocation entry in %s - extern\n", file
->fPath
));
791 * If the symbol is not in any section then it can't be a
792 * pointer to a local segment and I don't care about it.
794 if (r_symbolnum
== R_ABS
)
797 // Note segment references are offset by 1 from 0.
798 return_if(r_symbolnum
> file
->fNSects
, false,
799 ("Invalid relocation entry in %s - local\n", file
->fPath
));
801 // Find the symbol, if any, that backs this entry
802 symbol
= findSymbolByAddress(file
, *entry
);
805 rec
->fValue
= *entry
; // Save the previous value
806 rec
->fRInfo
= rinfo
; // Save a pointer to the reloc
807 rec
->fSymbol
= symbol
; // Record the current symbol
809 *entry
= (void *) rec
; // Save pointer to record in object image
812 ((struct fileRecord
*) file
)->fImageDirty
= true;
817 static const struct nlist
*
818 findSymbolRefAtLocation(const struct fileRecord
*file
,
819 struct sectionRecord
*sctn
, void **loc
)
821 if (file
->fIsKernel
) {
823 return findSymbolByAddress(file
, *loc
);
825 else if (sctn
->fRelocCache
|| relocateSection(file
, sctn
)) {
826 struct relocRecord
*reloc
= (struct relocRecord
*) *loc
;
828 if (DataContainsAddr(sctn
->fRelocCache
, reloc
))
829 return reloc
->fSymbol
;
836 addClass(struct fileRecord
*file
,
837 struct metaClassRecord
*inClass
,
840 struct metaClassRecord
*newClass
= NULL
;
841 struct metaClassRecord
**fileClasses
= NULL
;
844 if (!file
->fIsKernel
) { // @@@ gvdl:
845 DEBUG_LOG(("Adding Class %s\n", cname
));
848 if (!file
->fClassList
) {
849 file
->fClassList
= DataCreate(0);
850 if (!file
->fClassList
)
855 // Attempt to allocate all necessary resource first
856 len
= strlen(cname
) + 1
857 + (int) (&((struct metaClassRecord
*) 0)->fClassName
);
858 newClass
= (struct metaClassRecord
*) malloc(len
);
862 if (!DataAddLength(file
->fClassList
, sizeof(struct metaClassRecord
*)))
864 fileClasses
= (struct metaClassRecord
**)
865 (DataGetPtr(file
->fClassList
) + DataGetLength(file
->fClassList
));
867 // Copy the meta Class structure and string name into newClass
868 // and insert object at end of the file->fClassList and sMergeMetaClasses
869 *newClass
= *inClass
;
870 strcpy(newClass
->fClassName
, cname
);
871 fileClasses
[-1] = newClass
;
877 DataAddLength(file
->fClassList
, -sizeof(struct metaClassRecord
*));
885 static struct metaClassRecord
*getClass(DataRef classList
, const char *cname
)
889 struct metaClassRecord
**classes
, *thisClass
;
891 nclass
= DataGetLength(classList
) / sizeof(struct metaClassRecord
*);
892 classes
= (struct metaClassRecord
**) DataGetPtr(classList
);
893 for (i
= 0; i
< nclass
; i
++) {
894 thisClass
= classes
[i
];
895 if (!strcmp(thisClass
->fClassName
, cname
))
903 // Add the class 'cname' to the list of known OSObject based classes
904 // Note 'sym' is the <cname>.superClass symbol.
906 recordClass(struct fileRecord
*file
, const char *cname
, const struct nlist
*sym
)
908 char *supername
= NULL
;
909 const char *classname
= NULL
;
910 struct metaClassRecord newClass
;
911 char strbuffer
[1024];
913 // Only do the work actual work to find the super class if we are
914 // not currently working on the kernel. The kernel is the end
915 // of all superclass chains as by definition the kernel is binary
916 // compatible with itself.
917 if (!file
->fIsKernel
) {
919 const struct nlist
*supersym
;
920 const struct section
*section
;
921 struct sectionRecord
*sectionRec
;
922 unsigned char sectind
= sym
->n_sect
;
923 const char *superstr
;
926 // We can't resolve anything that isn't in a real section
927 // Note that the sectind is starts at one to make room for the
928 // NO_SECT flag but the fNSects field isn't offset so we have a
929 // '>' test. Which means this isn't an OSObject based class
930 if (sectind
== NO_SECT
|| sectind
> file
->fNSects
)
933 sectionRec
= file
->fSections
+ sectind
- 1;
934 section
= sectionRec
->fSection
;
935 location
= (void **) ( file
->fMachO
+ section
->offset
936 + sym
->n_value
- section
->addr
);
938 supersym
= findSymbolRefAtLocation(file
, sectionRec
, location
);
940 return true; // No superclass symbol then it isn't an OSObject.
942 // Find string in file and skip leading '_' and find last '.'
943 superstr
= symbolname(file
, supersym
) + 1;
944 dot
= strrchr(superstr
, '.');
945 if (!dot
|| strcmp(dot
, kGMetaSuffix
))
946 return true; // Not an OSObject superclass so ignore it.
948 supername
= (char *) malloc(dot
- superstr
+ 1);
949 strncpy(supername
, superstr
, dot
- superstr
);
950 supername
[dot
- superstr
] = '\0';
954 break_if(getClass(file
->fClassList
, cname
),
955 ("Duplicate class %s in %s\n", cname
, file
->fPath
));
957 snprintf(strbuffer
, sizeof(strbuffer
), "%s%s", kVTablePrefix
, cname
);
958 newClass
.fVTableSym
= findSymbolByName(file
, strbuffer
);
959 break_if(!newClass
.fVTableSym
,
960 ("Can't find vtable %s in %s\n", cname
, file
->fPath
));
962 newClass
.fFile
= file
;
963 newClass
.fSuperName
= supername
;
964 newClass
.fPatchedVTable
= NULL
;
966 // Can't use cname as it may be a stack variable
967 // However the vtable's string has the class name as a suffix
968 // so why don't we use that rather than mallocing a string.
969 classname
= symbolname(file
, newClass
.fVTableSym
)
970 + sizeof(kVTablePrefix
) - 1;
971 break_if(!addClass(file
, &newClass
, classname
),
972 ("recordClass - no memory?\n"));
983 static Boolean
getMetaClassGraph(struct fileRecord
*file
)
985 const struct nlist
*sym
;
989 // Search the symbol table for the local symbols that are generated
990 // by the metaclass system. There are three metaclass variables
991 // that are relevant.
993 // <ClassName>.metaClass A pointer to the meta class structure.
994 // <ClassName>.superClass A pointer to the super class's meta class.
995 // <ClassName>.gMetaClass The meta class structure itself.
996 // ___vt<ClassName> The VTable for the class <ClassName>.
998 // In this code I'm going to search for any symbols that
999 // ends in kSuperClassSuffix as this indicates this class is a conforming
1000 // OSObject subclass and will need to be patched, and it also
1001 // contains a pointer to the super class's meta class structure.
1002 strbase
= file
->fStringBase
;
1003 sym
= file
->fLocalSyms
;
1004 for (i
= 0, nsyms
= file
->fNLocal
; i
< nsyms
; i
++, sym
++) {
1005 const char *symname
;
1007 char classname
[1024];
1008 unsigned char n_type
= sym
->n_type
& (N_TYPE
| N_EXT
);
1010 // Check that the symbols is a global and that it has a name.
1011 if (((N_SECT
| N_EXT
) != n_type
&& (N_ABS
| N_EXT
) != n_type
)
1012 || !sym
->n_un
.n_strx
)
1015 // Only search from the last '.' in the symbol.
1016 // but skip the leading '_' in all symbols first.
1017 symname
= strbase
+ sym
->n_un
.n_strx
+ 1;
1018 dot
= strrchr(symname
, '.');
1019 if (!dot
|| strcmp(dot
, kSuperClassSuffix
))
1022 // Got a candidate so hand it over for class processing.
1023 return_if(dot
- symname
>= (int) sizeof(classname
),
1024 false, ("Symbol %s is too long\n", symname
));
1026 bcopy(symname
, classname
, dot
- symname
);
1027 classname
[dot
- symname
] = '\0';
1028 if (!recordClass(file
, classname
, sym
))
1032 return_if(!file
->fClassList
, false, ("Internal error, "
1033 "getMetaClassGraph(%s) found no classes", file
->fPath
));
1035 DEBUG_LOG(("Found %d classes in %x for %s\n",
1036 DataGetLength(file
->fClassList
)/sizeof(void*),
1037 file
->fClassList
, file
->fPath
));
1042 static Boolean
mergeOSObjectsForFile(const struct fileRecord
*file
)
1045 Boolean foundDuplicates
= false;
1047 DEBUG_LOG(("Merging file %s\n", file
->fPath
)); // @@@ gvdl:
1049 if (!file
->fClassList
)
1052 if (!sMergedFiles
) {
1053 sMergedFiles
= DataCreate(0);
1054 return_if(!sMergedFiles
, false,
1055 ("Unable to allocate memory metaclass list\n", file
->fPath
));
1058 // Check to see if we have already merged this file
1059 nmerged
= DataGetLength(sMergedFiles
) / sizeof(struct fileRecord
*);
1060 for (i
= 0; i
< nmerged
; i
++) {
1061 if (file
== ((void **) DataGetPtr(sMergedFiles
))[i
])
1065 if (!sMergeMetaClasses
) {
1066 sMergeMetaClasses
= DataCreate(0);
1067 return_if(!sMergeMetaClasses
, false,
1068 ("Unable to allocate memory metaclass list\n", file
->fPath
));
1070 else { /* perform a duplicate check */
1071 int i
, j
, cnt1
, cnt2
;
1072 struct metaClassRecord
**list1
, **list2
;
1074 list1
= (struct metaClassRecord
**) DataGetPtr(file
->fClassList
);
1075 cnt1
= DataGetLength(file
->fClassList
) / sizeof(*list1
);
1076 list2
= (struct metaClassRecord
**) DataGetPtr(sMergeMetaClasses
);
1077 cnt2
= DataGetLength(sMergeMetaClasses
) / sizeof(*list2
);
1079 for (i
= 0; i
< cnt1
; i
++) {
1080 for (j
= 0; j
< cnt2
; j
++) {
1081 if (!strcmp(list1
[i
]->fClassName
, list2
[j
]->fClassName
)) {
1082 errprintf("duplicate class %s in %s & %s\n",
1083 list1
[i
]->fClassName
,
1084 file
->fPath
, list2
[j
]->fFile
->fPath
);
1089 if (foundDuplicates
)
1092 return_if(!DataAppendBytes(sMergedFiles
, &file
, sizeof(file
)), false,
1093 ("Unable to allocate memory to merge %s\n", file
->fPath
));
1095 return_if(!DataAppendData(sMergeMetaClasses
, file
->fClassList
), false,
1096 ("Unable to allocate memory to merge %s\n", file
->fPath
));
1098 if (file
== sKernelFile
)
1099 sMergedKernel
= true;
1104 // Returns a pointer to the base of the section offset by the sections
1105 // base address. The offset is so that we can add nlist::n_values directly
1106 // to this address and get a valid pointer in our memory.
1107 static unsigned char *
1108 getSectionForSymbol(const struct fileRecord
*file
, const struct nlist
*symb
,
1111 const struct section
*section
;
1112 unsigned char sectind
;
1113 unsigned char *base
;
1115 sectind
= symb
->n_sect
; // Default to symbols section
1116 if ((symb
->n_type
& N_TYPE
) == N_ABS
&& file
->fIsKernel
) {
1117 // Absolute symbol so we have to iterate over our sections
1118 for (sectind
= 1; sectind
<= file
->fNSects
; sectind
++) {
1119 unsigned long start
, end
;
1121 section
= file
->fSections
[sectind
- 1].fSection
;
1122 start
= section
->addr
;
1123 end
= start
+ section
->size
;
1124 if (start
<= symb
->n_value
&& symb
->n_value
< end
) {
1125 // Found the relevant section
1131 // Is the vtable in a valid section?
1132 return_if(sectind
== NO_SECT
|| sectind
> file
->fNSects
,
1133 (unsigned char *) -1,
1134 ("%s isn't a valid kext, bad section reference\n", file
->fPath
));
1136 section
= file
->fSections
[sectind
- 1].fSection
;
1138 // for when we start walking the vtable so compute offset's now.
1139 base
= file
->fMachO
+ section
->offset
;
1140 *endP
= (void **) (base
+ section
->size
);
1142 return base
- section
->addr
; // return with addr offset
1145 static Boolean
resolveKernelVTable(struct metaClassRecord
*metaClass
)
1147 const struct fileRecord
*file
;
1148 struct patchRecord
*patchedVTable
;
1149 void **curEntry
, **vtableEntries
, **endSection
;
1150 unsigned char *sectionBase
;
1151 struct patchRecord
*curPatch
;
1154 // Should never occur but it doesn't cost us anything to check.
1155 if (metaClass
->fPatchedVTable
)
1158 DEBUG_LOG(("Kernel vtable %s\n", metaClass
->fClassName
)); // @@@ gvdl:
1160 // Do we have a valid vtable to patch?
1161 return_if(!metaClass
->fVTableSym
,
1162 false, ("Internal error - no class vtable symbol?\n"));
1164 file
= metaClass
->fFile
;
1166 // If the metaClass we are being to ask is in the kernel then we
1167 // need to do a quick scan to grab the fPatchList in a reliable format
1168 // however we don't need to check the superclass in the kernel
1169 // as the kernel vtables are always correct wrt themselves.
1170 // Note this ends the superclass chain recursion.
1171 return_if(!file
->fIsKernel
,
1172 false, ("Internal error - resolveKernelVTable not kernel\n"));
1174 if (file
->fNoKernelExecutable
) {
1175 // Oh dear attempt to map the kernel's VM into my memory space
1176 return_if(file
->fNoKernelExecutable
, false,
1177 ("Internal error - fNoKernelExecutable not implemented yet\n"));
1180 // We are going to need the base and the end
1181 sectionBase
= getSectionForSymbol(file
, metaClass
->fVTableSym
, &endSection
);
1182 if (-1 == (long) sectionBase
)
1185 vtableEntries
= (void **) (sectionBase
+ metaClass
->fVTableSym
->n_value
);
1186 curEntry
= vtableEntries
+ kVTablePreambleLen
;
1187 for (classSize
= 0; curEntry
< endSection
&& *curEntry
; classSize
++)
1190 return_if(*curEntry
, false, ("Bad kernel image, short section\n"));
1192 patchedVTable
= (struct patchRecord
*)
1193 malloc((classSize
+ 1) * sizeof(struct patchRecord
));
1194 return_if(!patchedVTable
, false, ("resolveKernelVTable - no memory\n"));
1196 // Copy the vtable of this class into the patch table
1197 curPatch
= patchedVTable
;
1198 curEntry
= vtableEntries
+ kVTablePreambleLen
;
1199 for (; *curEntry
; curEntry
++, curPatch
++) {
1200 curPatch
->fSymbol
= (struct nlist
*)
1201 findSymbolByAddress(file
, *curEntry
);
1202 curPatch
->fType
= kSymbolLocal
;
1205 // Tag the end of the patch vtable
1206 curPatch
->fSymbol
= NULL
;
1207 metaClass
->fPatchedVTable
= patchedVTable
;
1212 // reloc->fPatch must contain a valid pointer on entry
1213 static struct nlist
*
1214 getNewSymbol(struct fileRecord
*file
,
1215 const struct relocRecord
*reloc
, const char *supername
)
1217 unsigned int size
, i
, namelen
;
1220 const char *strbase
;
1221 struct relocation_info
*rinfo
;
1224 if (!file
->fNewSymbols
) {
1225 file
->fNewSymbols
= DataCreate(0);
1226 return_if(!file
->fNewSymbols
, NULL
,
1227 ("Unable to allocate new symbol table for %s\n", file
->fPath
));
1230 // Make sure we have a string table as well for the new symbol
1231 if (!file
->fNewStrings
) {
1232 file
->fNewStrings
= DataCreate(0);
1233 return_if(!file
->fNewStrings
, NULL
,
1234 ("Unable to allocate string table for %s\n", file
->fPath
));
1237 rinfo
= (struct relocation_info
*) reloc
->fRInfo
;
1238 size
= DataGetLength(file
->fNewSymbols
) / sizeof(struct nlist
*);
1239 sym
= (const struct nlist
**) DataGetPtr(file
->fNewSymbols
);
1240 // remember that the n_strx for new symbols names is negated
1241 strbase
= (const char *)
1242 DataGetPtr(file
->fNewStrings
) - file
->fSymtab
->strsize
;
1243 for (i
= 0; i
< size
; i
++, sym
++) {
1244 const char *symname
= strbase
- (*sym
)->n_un
.n_strx
;
1246 if (!strcmp(symname
, supername
)) {
1247 rinfo
->r_symbolnum
= i
+ file
->fSymtab
->nsyms
;
1248 file
->fSymbolsDirty
= true;
1253 // Assert that this is a vaild symbol. I need this condition to be true
1254 // for the later code to make non-zero. So the first time through I'd
1255 // better make sure that it is 0.
1256 return_if(reloc
->fSymbol
->n_sect
, NULL
,
1257 ("Undefined symbol entry with non-zero section %s:%s\n",
1258 file
->fPath
, symbolname(file
, reloc
->fSymbol
)));
1260 msym
= (struct nlist
*) malloc(sizeof(struct nlist
));
1262 NULL
, ("Unable to create symbol table entry for %s\n", file
->fPath
));
1264 // If we are here we didn't find the symbol so create a new one now
1265 if (!DataAppendBytes(file
->fNewSymbols
, &msym
, sizeof(msym
))) {
1268 NULL
, ("Unable to grow symbol table for %s\n", file
->fPath
));
1271 namelen
= strlen(supername
) + 1;
1272 strx
= DataGetLength(file
->fNewStrings
);
1273 if (!DataAppendBytes(file
->fNewStrings
, supername
, namelen
)) {
1275 DataAddLength(file
->fNewSymbols
, -sizeof(struct nlist
)); // Undo harm
1276 return_if(true, NULL
,
1277 ("Unable to grow string table for %s\n", file
->fPath
));
1280 // Offset the string index by the original string table size
1281 // and negate the address to indicate that this is a 'new' symbol
1282 msym
->n_un
.n_strx
= -(strx
+ file
->fSymtab
->strsize
);
1283 msym
->n_type
= (N_EXT
| N_UNDF
);
1284 msym
->n_sect
= NO_SECT
;
1288 // Mark the old symbol as being potentially deletable I can use the
1289 // n_sect field as the input symbol must be of type N_UNDF which means
1290 // that the n_sect field must be set to NO_SECT otherwise it is an
1291 // in valid input file.
1292 ((struct nlist
*) reloc
->fSymbol
)->n_un
.n_strx
1293 = -reloc
->fSymbol
->n_un
.n_strx
;
1294 ((struct nlist
*) reloc
->fSymbol
)->n_sect
= (unsigned char) -1;
1296 rinfo
->r_symbolnum
= i
+ file
->fSymtab
->nsyms
;
1297 file
->fSymbolsDirty
= true;
1301 static struct nlist
*
1302 fixOldSymbol(struct fileRecord
*file
,
1303 const struct relocRecord
*reloc
, const char *supername
)
1305 unsigned int namelen
;
1306 struct nlist
*sym
= (struct nlist
*) reloc
->fSymbol
;
1307 const char *oldname
= symbolname(file
, sym
);
1309 // assert(sym->n_un.n_strx >= 0);
1311 namelen
= strlen(supername
);
1312 if (namelen
< strlen(oldname
)) {
1313 // Overwrite old string in string table
1314 strcpy((char *) oldname
, supername
);
1319 // Make sure we have a string table as well for this symbol
1320 if (!file
->fNewStrings
) {
1321 file
->fNewStrings
= DataCreate(0);
1322 return_if(!file
->fNewStrings
, NULL
,
1323 ("Unable to allocate string table for %s\n", file
->fPath
));
1326 // Find the end of the fNewStrings data structure;
1327 strx
= DataGetLength(file
->fNewStrings
);
1328 return_if(!DataAppendBytes(file
->fNewStrings
, supername
, namelen
+ 1),
1329 NULL
, ("Unable to grow string table for %s\n", file
->fPath
));
1331 // now add the current table size to the offset
1332 sym
->n_un
.n_strx
= strx
+ file
->fSymtab
->strsize
;
1335 // Mark the symbol as having been processed by negating it.
1336 // Also note that we have dirtied the file and need to repair the
1338 sym
->n_un
.n_strx
= -sym
->n_un
.n_strx
;
1339 file
->fSymbolsDirty
= true;
1343 static enum patchState
1344 symbolCompare(const struct fileRecord
*file
,
1345 const struct nlist
*classsym
,
1346 const char *supername
)
1348 const char *classname
;
1351 // Check to see if the target function is locally defined
1352 // if it is then we can assume this is a local vtable override
1353 if ((classsym
->n_type
& N_TYPE
) != N_UNDF
)
1354 return kSymbolLocal
;
1356 // Check to see if both symbols point to the same symbol name
1357 // if so then we are still identical.
1358 classname
= symbolname(file
, classsym
);
1359 if (!strcmp(classname
, supername
))
1360 return kSymbolIdentical
;
1362 // Right now we know that the target's vtable entry is different from the
1363 // superclass' vtable entry. This means that we will have to apply a
1364 // patch to the current entry, however before returning lets check to
1365 // see if we have a _RESERVEDnnn field 'cause we can use this as a
1366 // registration point that must align between vtables.
1367 if (!strncmp(supername
, kReservedPrefix
, sizeof(kReservedPrefix
) - 1))
1368 return kSymbolMismatch
;
1370 // OK, we have a superclass difference where the superclass doesn't
1371 // reference a pad function so assume that the superclass is correct.
1372 if (!strncmp(classname
, kReservedPrefix
, sizeof(kReservedPrefix
) - 1))
1373 return kSymbolPadUpdate
;
1375 return kSymbolSuperUpdate
;
1378 static Boolean
patchVTable(struct metaClassRecord
*metaClass
)
1380 struct metaClassRecord
*super
= NULL
;
1381 struct fileRecord
*file
;
1382 struct patchRecord
*patchedVTable
;
1383 struct relocRecord
**curReloc
, **vtableRelocs
, **endSection
;
1384 unsigned char *sectionBase
;
1387 // Should never occur but it doesn't cost us anything to check.
1388 if (metaClass
->fPatchedVTable
)
1391 // Do we have a valid vtable to patch?
1392 return_if(!metaClass
->fVTableSym
,
1393 false, ("Internal error - no class vtable symbol?\n"));
1395 file
= metaClass
->fFile
;
1397 // If the metaClass we are being to ask is in the kernel then we
1398 // need to do a quick scan to grab the fPatchList in a reliable format
1399 // however we don't need to check the superclass in the kernel
1400 // as the kernel vtables are always correct wrt themselves.
1401 // Note this ends the superclass chain recursion.
1402 return_if(file
->fIsKernel
,
1403 false, ("Internal error - patchVTable shouldn't used for kernel\n"));
1405 if (!metaClass
->fSuperName
)
1408 // The class isn't in the kernel so make sure that the super class
1409 // is patched before patching ouselves.
1410 super
= getClass(sMergeMetaClasses
, metaClass
->fSuperName
);
1411 return_if(!super
, false, ("Can't find superclass for %s : %s \n",
1412 metaClass
->fClassName
, metaClass
->fSuperName
));
1414 // Superclass recursion if necessary
1415 if (!super
->fPatchedVTable
) {
1418 if (super
->fFile
->fIsKernel
)
1419 res
= resolveKernelVTable(super
);
1421 res
= patchVTable(super
);
1426 DEBUG_LOG(("Patching %s\n", metaClass
->fClassName
)); // @@@ gvdl:
1428 // We are going to need the base and the end
1430 sectionBase
= getSectionForSymbol(file
,
1431 metaClass
->fVTableSym
, (void ***) &endSection
);
1432 if (-1 == (long) sectionBase
)
1435 vtableRelocs
= (struct relocRecord
**)
1436 (sectionBase
+ metaClass
->fVTableSym
->n_value
);
1437 curReloc
= vtableRelocs
+ kVTablePreambleLen
;
1438 for (classSize
= 0; curReloc
< endSection
&& *curReloc
; classSize
++)
1441 return_if(*curReloc
, false,
1442 ("%s isn't a valid kext, short section\n", file
->fPath
));
1444 patchedVTable
= (struct patchRecord
*)
1445 malloc((classSize
+ 1) * sizeof(struct patchRecord
));
1446 return_if(!patchedVTable
, false, ("patchedVTable - no memory\n"));
1449 struct patchRecord
*curPatch
;
1450 struct nlist
*symbol
;
1452 curPatch
= patchedVTable
;
1453 curReloc
= vtableRelocs
+ kVTablePreambleLen
;
1455 // Grab the super table patches if necessary
1456 // Can't be patching a kernel table as we don't walk super
1457 // class chains in the kernel symbol space.
1458 if (super
&& super
->fPatchedVTable
) {
1459 const struct patchRecord
*spp
;
1461 spp
= super
->fPatchedVTable
;
1463 for ( ; spp
->fSymbol
; curReloc
++, spp
++, curPatch
++) {
1464 const char *supername
=
1465 symbolname(super
->fFile
, spp
->fSymbol
);
1467 symbol
= (struct nlist
*) (*curReloc
)->fSymbol
;
1469 curPatch
->fType
= symbolCompare(file
, symbol
, supername
);
1470 switch (curPatch
->fType
) {
1471 case kSymbolIdentical
:
1475 case kSymbolSuperUpdate
:
1476 symbol
= getNewSymbol(file
, (*curReloc
), supername
);
1479 case kSymbolPadUpdate
:
1480 symbol
= fixOldSymbol(file
, (*curReloc
), supername
);
1483 case kSymbolMismatch
:
1484 errprintf("%s is not compatible with its %s superclass, "
1485 "broken superclass?\n",
1486 metaClass
->fClassName
, super
->fClassName
);
1490 errprintf("Internal error - unknown patch type\n");
1494 curPatch
->fSymbol
= symbol
;
1495 (*curReloc
)->fSymbol
= symbol
;
1502 // Copy the remainder of this class' vtable into the patch table
1503 for (; *curReloc
; curReloc
++, curPatch
++) {
1504 // Local reloc symbols
1505 curPatch
->fType
= kSymbolLocal
;
1506 curPatch
->fSymbol
= (struct nlist
*) (*curReloc
)->fSymbol
;
1509 // Tag the end of the patch vtable
1510 curPatch
->fSymbol
= NULL
;
1512 metaClass
->fPatchedVTable
= patchedVTable
;
1518 free(patchedVTable
);
1523 static Boolean
growImage(struct fileRecord
*file
, vm_size_t delta
)
1526 file
->fMachOSize
+= delta
;
1527 return (file
->fMachO
+ file
->fMachOSize
<= file
->fPadEnd
);
1529 vm_address_t startMachO
, endMachO
, endMap
;
1530 vm_offset_t newMachO
;
1532 unsigned long i
, nsect
, nclass
= 0;
1533 struct metaClassRecord
**classes
= NULL
;
1534 struct sectionRecord
*section
;
1537 startMachO
= (vm_address_t
) file
->fMachO
;
1538 endMachO
= startMachO
+ file
->fMachOSize
+ delta
;
1539 endMap
= (vm_address_t
) file
->fMap
+ file
->fMapSize
;
1541 // Do we have room in the current mapped image
1542 if (endMachO
< round_page(endMap
)) {
1543 file
->fMachOSize
+= delta
;
1547 newsize
= endMachO
- startMachO
;
1548 if (newsize
< round_page(file
->fMapSize
)) {
1549 // We have room in the map if we shift the macho image within the
1550 // current map. We will have to patch up pointers into the object.
1551 newMachO
= (vm_offset_t
) file
->fMap
;
1552 bcopy((char *) startMachO
, (char *) newMachO
, file
->fMachOSize
);
1554 else if (file
->fIsKmem
) {
1555 // kmem_alloced mapping so we can try a kmem_realloc
1556 ret
= kmem_realloc(kernel_map
,
1557 (vm_address_t
) file
->fMap
,
1558 (vm_size_t
) file
->fMapSize
,
1561 if (KERN_SUCCESS
!= ret
)
1564 // If the mapping didn't move then just return
1565 if ((vm_address_t
) file
->fMap
== newMachO
) {
1566 file
->fMachOSize
= file
->fMapSize
= newsize
;
1570 // We have relocated the kmem image so we are going to have to
1571 // move all of the pointers into the image around.
1574 // The image doesn't have room for us and I can't kmem_realloc
1575 // then I just have to bite the bullet and copy the object code
1576 // into a bigger memory segment
1577 ret
= kmem_alloc(kernel_map
, &newMachO
, newsize
);
1579 if (KERN_SUCCESS
!= ret
)
1581 bcopy((char *) startMachO
, (void *) newMachO
, file
->fMachOSize
);
1582 file
->fIsKmem
= true;
1586 file
->fMap
= file
->fMachO
= (unsigned char *) newMachO
;
1587 file
->fMapSize
= newsize
;
1588 file
->fMachOSize
+= delta
; // Increment the image size
1590 // If we are here then we have shifted the object image in memory
1591 // I really should change all of my pointers into the image to machO offsets
1592 // but I have run out of time. So I'm going to very quickly go over the
1593 // cached data structures and add adjustments to the addresses that are
1594 // affected. I wonder how long it will take me to get them all.
1596 // For every pointer into the MachO I need to add an adjustment satisfying
1597 // the following simultanous equations
1598 // addr_old = macho_old + fixed_offset
1599 // addr_new = macho_new + fixed_offset therefore:
1600 // addr_new = addr_old + (macho_new - macho_old)
1601 #define REBASE(addr, delta) ( ((vm_address_t) (addr)) += (delta) )
1602 delta
= newMachO
- startMachO
;
1604 // Rebase the cached in object 'struct symtab_command' pointer
1605 REBASE(file
->fSymtab
, delta
);
1607 // Rebase the cached in object 'struct nlist' pointer for all symbols
1608 REBASE(file
->fSymbolBase
, delta
);
1610 // Rebase the cached in object 'struct nlist' pointer for local symbols
1611 REBASE(file
->fLocalSyms
, delta
);
1613 // Rebase the cached in object 'char' pointer for the string table
1614 REBASE(file
->fStringBase
, delta
);
1616 // Ok now we have to go over all of the relocs one last time
1617 // to clean up the pad updates which had their string index negated
1618 // to indicate that we have finished with them.
1619 section
= file
->fSections
;
1620 for (i
= 0, nsect
= file
->fNSects
; i
< nsect
; i
++, section
++)
1621 REBASE(section
->fSection
, delta
);
1623 // We only ever grow images that contain class lists so dont bother
1624 // the check if file->fClassList is non-zero 'cause it can't be
1625 // assert(file->fClassList);
1626 nclass
= DataGetLength(file
->fClassList
)
1627 / sizeof(struct metaClassRecord
*);
1628 classes
= (struct metaClassRecord
**) DataGetPtr(file
->fClassList
);
1629 for (i
= 0; i
< nclass
; i
++) {
1630 struct patchRecord
*patch
;
1632 for (patch
= classes
[i
]->fPatchedVTable
; patch
->fSymbol
; patch
++) {
1633 vm_address_t symAddr
= (vm_address_t
) patch
->fSymbol
;
1634 if (symAddr
>= startMachO
&& symAddr
< endMachO
)
1635 REBASE(patch
->fSymbol
, delta
);
1648 prepareFileForLink(struct fileRecord
*file
)
1650 unsigned long i
, last
, numnewsyms
, newsymsize
, newstrsize
;
1651 struct sectionRecord
*section
;
1652 struct nlist
**symp
, *sym
;
1654 // If we didn't even do a pseudo 'relocate' and dirty the image
1655 // then we can just return now.
1656 if (!file
->fImageDirty
)
1659 DEBUG_LOG(("Linking 2 %s\n", file
->fPath
)); // @@@ gvdl:
1661 // We have to go over all of the relocs to repair the damage
1662 // that we have done to the image when we did our 'relocation'
1663 section
= file
->fSections
;
1664 for (i
= 0, last
= file
->fNSects
; i
< last
; i
++, section
++) {
1665 unsigned char *sectionBase
;
1666 struct relocRecord
*rec
;
1667 unsigned long j
, nreloc
;
1669 if (section
->fRelocCache
) {
1670 sectionBase
= file
->fMachO
+ section
->fSection
->offset
;
1671 nreloc
= section
->fSection
->nreloc
;
1672 rec
= (struct relocRecord
*) DataGetPtr(section
->fRelocCache
);
1674 // We will need to repair the reloc list
1675 for (j
= 0; j
< nreloc
; j
++, rec
++) {
1679 // Repair Damage to object image
1680 entry
= (void **) (sectionBase
+ rec
->fRInfo
->r_address
);
1681 *entry
= rec
->fValue
;
1683 // Check if the symbol that this relocation entry points
1684 // to is marked as erasable
1685 sym
= (struct nlist
*) rec
->fSymbol
;
1686 if (sym
&& sym
->n_type
== (N_EXT
| N_UNDF
)
1687 && sym
->n_sect
== (unsigned char) -1) {
1689 sym
->n_un
.n_strx
= -sym
->n_un
.n_strx
;
1690 sym
->n_sect
= NO_SECT
;
1694 // Clean up the fRelocCache we don't need it any more.
1695 DataRelease(section
->fRelocCache
);
1696 section
->fRelocCache
= 0;
1699 file
->fImageDirty
= false; // Image is clean
1701 // If we didn't dirty the symbol table then just return
1702 if (!file
->fSymbolsDirty
)
1705 // calculate total file size increase and check against padding
1706 numnewsyms
= (file
->fNewSymbols
)? DataGetLength(file
->fNewSymbols
) : 0;
1707 numnewsyms
/= sizeof(struct nlist
*);
1708 newsymsize
= numnewsyms
* sizeof(struct nlist
);
1709 newstrsize
= (file
->fNewStrings
)? DataGetLength(file
->fNewStrings
) : 0;
1710 newstrsize
= (newstrsize
+ 3) & ~3; // Round to nearest word
1712 return_if(!growImage(file
, newsymsize
+ newstrsize
),
1713 false, ("Unable to patch the extension, no memory\n", file
->fPath
));
1715 // Push out the new symbol table if necessary
1719 // Move the string table out of the way of the grown symbol table
1720 // Don't forget the '\0' from end of string table.
1721 base
= (caddr_t
) file
->fStringBase
;
1722 bcopy(base
, base
+ newsymsize
, file
->fSymtab
->strsize
);
1723 file
->fStringBase
+= newsymsize
;
1724 file
->fSymtab
->stroff
+= newsymsize
;
1726 // Now append the new symbols to the symbol table.
1727 base
= (caddr_t
) file
->fSymbolBase
1728 + file
->fSymtab
->nsyms
* sizeof(struct nlist
);
1729 symp
= (struct nlist
**) DataGetPtr(file
->fNewSymbols
);
1730 for (i
= 0; i
< numnewsyms
; i
++, base
+= sizeof(struct nlist
), symp
++)
1731 bcopy(*symp
, base
, sizeof(struct nlist
));
1732 file
->fSymtab
->nsyms
+= numnewsyms
;
1734 DataRelease(file
->fNewSymbols
);
1735 file
->fNewSymbols
= 0;
1738 // Push out the new string table if necessary
1740 caddr_t base
= (caddr_t
) file
->fStringBase
+ file
->fSymtab
->strsize
;
1741 unsigned long actuallen
= DataGetLength(file
->fNewStrings
);
1743 // Set the last word in string table to zero before copying data
1744 *((unsigned long *) ((char *) base
+ newstrsize
- 4)) = 0;
1746 // Now append the new strings to the end of the file
1747 bcopy((caddr_t
) DataGetPtr(file
->fNewStrings
), base
, actuallen
);
1749 file
->fSymtab
->strsize
+= newstrsize
;
1751 DataRelease(file
->fNewStrings
);
1752 file
->fNewStrings
= 0;
1755 // Repair the symbol table string index values
1756 // I used negative strx's to indicate symbol has been processed
1757 sym
= file
->fSymbolBase
;
1758 for (i
= 0, last
= file
->fSymtab
->nsyms
; i
< last
; i
++, sym
++) {
1759 if (sym
->n_un
.n_strx
< 0) {
1760 if ( sym
->n_type
!= (N_EXT
| N_UNDF
)
1761 || (unsigned char) -1 != sym
->n_sect
)
1762 sym
->n_un
.n_strx
= -sym
->n_un
.n_strx
;
1764 // This symbol isn't being used by any vtable's reloc so
1765 // convert it into an N_ABS style of symbol, remove the
1766 // external bit and null out the symbol name.
1767 bzero(sym
, sizeof(*sym
));
1768 sym
->n_type
= N_ABS
; /* type flag, see below */
1772 file
->fSymbolsDirty
= false;
1779 kld_file_map(const char *pathName
,
1784 kld_file_map(const char *pathName
)
1787 struct fileRecord file
, *fp
= 0;
1789 // Already done no need to repeat
1790 fp
= getFile(pathName
);
1794 bzero(&file
, sizeof(file
));
1795 file
.fPath
= pathName
;
1799 file
.fMapSize
= mapSize
;
1800 file
.fIsKmem
= isKmem
;
1802 if (!mapObjectFile(&file
))
1807 const struct machOMapping
{
1808 struct mach_header h
;
1809 struct load_command c
[1];
1811 const struct load_command
*cmd
;
1812 const struct nlist
*sym
;
1813 unsigned int i
, firstlocal
, nsyms
;
1814 unsigned long strsize
;
1815 const char *strbase
;
1816 Boolean foundOSObject
;
1818 if (!findBestArch(&file
))
1821 machO
= (const struct machOMapping
*) file
.fMachO
;
1822 if (file
.fMachOSize
< machO
->h
.sizeofcmds
)
1825 // If the file type is MH_EXECUTE then this must be a kernel
1826 // as all Kernel extensions must be of type MH_OBJECT
1827 for (i
= 0, cmd
= &machO
->c
[0]; i
< machO
->h
.ncmds
; i
++) {
1828 if (cmd
->cmd
== LC_SEGMENT
) {
1829 return_if(!parseSegments(&file
, (struct segment_command
*) cmd
),
1830 false, ("%s isn't a valid mach-o, bad segment\n",
1833 else if (cmd
->cmd
== LC_SYMTAB
)
1834 file
.fSymtab
= (struct symtab_command
*) cmd
;
1836 cmd
= (struct load_command
*) ((UInt8
*) cmd
+ cmd
->cmdsize
);
1838 break_if(!file
.fSymtab
,
1839 ("%s isn't a valid mach-o, no symbols\n", file
.fPath
));
1841 // we found a link edit segment so recompute the bases
1842 if (file
.fSymbolBase
) {
1843 struct segment_command
*link
=
1844 (struct segment_command
*) file
.fSymbolBase
;
1846 file
.fSymbolBase
= (struct nlist
*)
1847 (link
->vmaddr
+ (file
.fSymtab
->symoff
- link
->fileoff
));
1848 file
.fStringBase
= (char *)
1849 (link
->vmaddr
+ (file
.fSymtab
->stroff
- link
->fileoff
));
1850 break_if( ( (caddr_t
) file
.fStringBase
+ file
.fSymtab
->strsize
1851 > (caddr_t
) link
->vmaddr
+ link
->vmsize
),
1852 ("%s isn't a valid mach-o le, bad symbols\n", file
.fPath
));
1855 file
.fSymbolBase
= (struct nlist
*)
1856 (file
.fMachO
+ file
.fSymtab
->symoff
);
1857 file
.fStringBase
= (char *)
1858 (file
.fMachO
+ file
.fSymtab
->stroff
);
1859 break_if( ( file
.fSymtab
->stroff
+ file
.fSymtab
->strsize
1860 > file
.fMachOSize
),
1861 ("%s isn't a valid mach-o, bad symbols\n", file
.fPath
));
1864 // If this file the kernel and do we have an executable image
1865 file
.fIsKernel
= (MH_EXECUTE
== machO
->h
.filetype
);
1866 file
.fNoKernelExecutable
= (vm_page_size
== file
.fSymtab
->symoff
)
1867 && (file
.fSections
[0].fSection
->size
== 0);
1869 // Search for the first non-stab symbol in table
1870 strsize
= file
.fSymtab
->strsize
;
1871 strbase
= file
.fStringBase
;
1872 sym
= file
.fSymbolBase
;
1874 foundOSObject
= false;
1875 for (i
= 0, nsyms
= file
.fSymtab
->nsyms
; i
< nsyms
; i
++, sym
++) {
1876 if ((unsigned long) sym
->n_un
.n_strx
> strsize
)
1879 // Find the first exported symbol
1880 if ( !file
.fLocalSyms
&& (sym
->n_type
& N_EXT
) ) {
1881 file
.fLocalSyms
= sym
;
1885 // Find the a OSObject based subclass by searching for symbols
1886 // that have a suffix of '.superClass'
1888 && ((sym
->n_type
& (N_TYPE
| N_EXT
)) == (N_SECT
| N_EXT
)
1889 || (sym
->n_type
& (N_TYPE
| N_EXT
)) == (N_ABS
| N_EXT
))
1890 && sym
->n_un
.n_strx
) {
1893 // Only search from the last '.' in the symbol.
1894 // but skip the leading '_' in all symbols first.
1895 dot
= strrchr(strbase
+ sym
->n_un
.n_strx
+ 1, '.');
1896 if (dot
&& !strcmp(dot
, kSuperClassSuffix
))
1897 foundOSObject
= true;
1900 // Find the last local symbol
1901 if ( !file
.fNLocal
&& sym
->n_type
== (N_EXT
| N_UNDF
) )
1902 file
.fNLocal
= i
- firstlocal
;
1906 ("%s isn't a valid mach-o, bad symbol strings\n", file
.fPath
));
1908 break_if(!file
.fLocalSyms
, ("%s has no symbols?\n", file
.fPath
));
1910 // If we don't have any undefined symbols then all symbols
1911 // must be local so just compute it now if necessary.
1912 if ( !file
.fNLocal
)
1913 file
.fNLocal
= i
- firstlocal
;
1915 fp
= addFile(&file
);
1919 if (foundOSObject
&& !getMetaClassGraph(fp
))
1926 extern struct mach_header _mh_execute_header
;
1927 extern struct segment_command
*getsegbyname(char *seg_name
);
1929 struct segment_command
*sg
;
1933 sg
= (struct segment_command
*) getsegbyname(kLinkEditSegName
);
1934 break_if(!sg
, ("Can't find kernel link edit segment\n"));
1936 kernelSize
= sg
->vmaddr
+ sg
->vmsize
- (size_t) &_mh_execute_header
;
1937 ret
= kld_file_map(kld_basefile_name
,
1938 (unsigned char *) &_mh_execute_header
, kernelSize
,
1939 /* isKmem */ false);
1940 break_if(!ret
, ("kld can't map kernel file"));
1952 void *kld_file_getaddr(const char *pathName
, long *size
)
1954 struct fileRecord
*file
= getFile(pathName
);
1960 *size
= file
->fMachOSize
;
1962 return file
->fMachO
;
1965 void *kld_file_lookupsymbol(const char *pathName
, const char *symname
)
1967 struct fileRecord
*file
= getFile(pathName
);
1968 const struct nlist
*sym
;
1969 const struct section
*section
;
1970 unsigned char *sectionBase
;
1971 unsigned char sectind
;
1974 NULL
, ("Unknown file %s\n", pathName
));
1976 sym
= findSymbolByName(file
, symname
);
1978 // May be a non-extern symbol so look for it there
1980 const char *strbase
;
1981 unsigned int i
, nsyms
;
1983 sym
= file
->fSymbolBase
;
1984 strbase
= file
->fStringBase
;
1985 for (i
= 0, nsyms
= file
->fSymtab
->nsyms
; i
< nsyms
; i
++, sym
++) {
1986 if ( (sym
->n_type
& N_EXT
) ) {
1988 break; // Terminate search when we hit an extern
1990 if ( (sym
->n_type
& N_STAB
) )
1992 if ( !strcmp(symname
, strbase
+ sym
->n_un
.n_strx
) )
1998 NULL
, ("Unknown symbol %s in %s\n", symname
, pathName
));
2000 // Is the vtable in a valid section?
2001 sectind
= sym
->n_sect
;
2002 return_if(sectind
== NO_SECT
|| sectind
> file
->fNSects
, NULL
,
2003 ("Malformed object file, invalid section reference for %s in %s\n",
2004 symname
, pathName
));
2006 section
= file
->fSections
[sectind
- 1].fSection
;
2007 sectionBase
= file
->fMachO
+ section
->offset
- section
->addr
;
2009 return (void *) (sectionBase
+ sym
->n_value
);
2012 Boolean
kld_file_merge_OSObjects(const char *pathName
)
2014 struct fileRecord
*file
= getFile(pathName
);
2017 false, ("Internal error - unable to find file %s\n", pathName
));
2019 return mergeOSObjectsForFile(file
);
2022 Boolean
kld_file_patch_OSObjects(const char *pathName
)
2024 struct fileRecord
*file
= getFile(pathName
);
2025 struct metaClassRecord
**classes
;
2026 unsigned long i
, last
;
2029 false, ("Internal error - unable to find file %s\n", pathName
));
2031 DEBUG_LOG(("Patch file %s\n", pathName
)); // @@@ gvdl:
2033 // If we don't have any classes we can return now.
2034 if (!file
->fClassList
)
2037 // If we haven't alread merged the kernel then do it now
2038 if (!sMergedKernel
&& sKernelFile
)
2039 mergeOSObjectsForFile(sKernelFile
);
2040 return_if(!sMergedKernel
, false, ("Internal error no kernel?\n"));
2042 if (!mergeOSObjectsForFile(file
))
2045 // Patch all of the classes in this executable
2046 last
= DataGetLength(file
->fClassList
) / sizeof(void *);
2047 classes
= (struct metaClassRecord
**) DataGetPtr(file
->fClassList
);
2048 for (i
= 0; i
< last
; i
++) {
2049 if (!patchVTable(classes
[i
]))
2056 Boolean
kld_file_prepare_for_link()
2059 unsigned long i
, nmerged
= 0;
2060 struct fileRecord
**files
;
2062 // Check to see if we have already merged this file
2063 nmerged
= DataGetLength(sMergedFiles
) / sizeof(struct fileRecord
*);
2064 files
= (struct fileRecord
**) DataGetPtr(sMergedFiles
);
2065 for (i
= 0; i
< nmerged
; i
++) {
2066 if (!prepareFileForLink(files
[i
]))
2071 // Clear down the meta class table and merged file lists
2072 DataRelease(sMergeMetaClasses
);
2073 DataRelease(sMergedFiles
);
2074 sMergedFiles
= sMergeMetaClasses
= NULL
;
2075 sMergedKernel
= false;
2080 void kld_file_cleanup_all_resources()
2082 unsigned long i
, nfiles
;
2084 #if KERNEL // @@@ gvdl:
2085 // Debugger("kld_file_cleanup_all_resources");
2088 if (!sFilesTable
|| !(nfiles
= DataGetLength(sFilesTable
)))
2089 return; // Nothing to do just return now
2091 nfiles
/= sizeof(struct fileRecord
*);
2092 for (i
= 0; i
< nfiles
; i
++)
2093 removeFile(((void **) DataGetPtr(sFilesTable
))[i
]);
2095 // Don't really have to clean up anything more as the whole
2096 // malloc engine is going to be released and I couldn't be bothered.
2100 Boolean
kld_file_debug_dump(const char *pathName
, const char *outName
)
2102 const struct fileRecord
*file
= getFile(pathName
);
2104 Boolean ret
= false;
2106 return_if(!file
, false, ("Unknown file %s for dumping\n", pathName
));
2108 fd
= open(outName
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
2109 return_if(-1 == fd
, false, ("Can't create output file %s - %s(%d)\n",
2110 outName
, strerror(errno
), errno
));
2113 break_if(-1 == write(fd
, file
->fMachO
, file
->fMachOSize
),
2114 ("Can't dump output file %s - %s(%d)\n",
2115 outName
, strerror(errno
), errno
));
2123 #endif /* !KERNEL */