1356 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1356 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
| /* $XFree86: xc/programs/Xserver/hw/xfree86/loader/coffloader.c,v 1.22 2003/12/22 17:48:11 tsi Exp $ */
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  * Copyright 1995,96 by Metro Link, Inc.
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and its
 | |
|  * documentation for any purpose is hereby granted without fee, provided that
 | |
|  * the above copyright notice appear in all copies and that both that
 | |
|  * copyright notice and this permission notice appear in supporting
 | |
|  * documentation, and that the name of Metro Link, Inc. not be used in
 | |
|  * advertising or publicity pertaining to distribution of the software without
 | |
|  * specific, written prior permission.  Metro Link, Inc. makes no
 | |
|  * representations about the suitability of this software for any purpose.
 | |
|  *  It is provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * METRO LINK, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | |
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 | |
|  * EVENT SHALL METRO LINK, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | |
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 | |
|  * PERFORMANCE OF THIS SOFTWARE.
 | |
|  */
 | |
| #include <sys/types.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #ifdef __QNX__
 | |
| #include <fcntl.h>
 | |
| #else
 | |
| #include <sys/fcntl.h>
 | |
| #endif
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| #ifdef DBMALLOC
 | |
| #include <debug/malloc.h>
 | |
| #define Xalloc(size) malloc(size)
 | |
| #define Xcalloc(size) calloc(1,(size))
 | |
| #define Xfree(size) free(size)
 | |
| #endif
 | |
| 
 | |
| #include "Xos.h"
 | |
| #include "os.h"
 | |
| #include "coff.h"
 | |
| 
 | |
| #include "sym.h"
 | |
| #include "loader.h"
 | |
| #include "coffloader.h"
 | |
| 
 | |
| #include "compiler.h"
 | |
| 
 | |
| #ifndef LOADERDEBUG
 | |
| #define LOADERDEBUG 0
 | |
| #endif
 | |
| 
 | |
| #if LOADERDEBUG
 | |
| #define COFFDEBUG ErrorF
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * This structure contains all of the information about a module
 | |
|  * that has been loaded.
 | |
|  */
 | |
| 
 | |
| typedef struct {
 | |
|     int handle;
 | |
|     long module;		/* Id of the module used to find inter module calls */
 | |
|     int fd;
 | |
|     loader_funcs *funcs;
 | |
|     FILHDR *header;		/* file header */
 | |
|     AOUTHDR *optheader;		/* optional file header */
 | |
|     unsigned short numsh;
 | |
|     SCNHDR *sections;		/* Start address of the section table */
 | |
|     int secsize;		/* size of the section table */
 | |
|     unsigned char **saddr;	/* Start addresss of the sections table */
 | |
|     unsigned char **reladdr;	/* Start addresss of the relocation table */
 | |
|     unsigned char *strtab;	/* Start address of the string table */
 | |
|     int strsize;		/* size of the string table */
 | |
|     unsigned char *text;	/* Start address of the .text section */
 | |
|     int txtndx;			/* index of the .text section */
 | |
|     long txtaddr;		/* offset of the .text section */
 | |
|     int txtsize;		/* size of the .text section */
 | |
|     int txtrelsize;		/* size of the .rel.text section */
 | |
|     unsigned char *data;	/* Start address of the .data section */
 | |
|     int datndx;			/* index of the .data section */
 | |
|     long dataddr;		/* offset of the .data section */
 | |
|     int datsize;		/* size of the .data section */
 | |
|     int datrelsize;		/* size of the .rel.data section */
 | |
|     unsigned char *bss;		/* Start address of the .bss section */
 | |
|     int bssndx;			/* index of the .bss section */
 | |
|     long bssaddr;		/* offset of the .bss section */
 | |
|     int bsssize;		/* size of the .bss section */
 | |
|     SYMENT *symtab;		/* Start address of the .symtab section */
 | |
|     int symndx;			/* index of the .symtab section */
 | |
|     int symsize;		/* size of the .symtab section */
 | |
|     unsigned char *common;	/* Start address of the .common section */
 | |
|     int comsize;		/* size of the .common section */
 | |
|     long toc;			/* Offset of the TOC csect */
 | |
|     unsigned char *tocaddr;	/* Address of the TOC csect */
 | |
| } COFFModuleRec, *COFFModulePtr;
 | |
| 
 | |
| /*
 | |
|  * If any relocation is unable to be satisfied, then put it on a list
 | |
|  * to try later after more modules have been loaded.
 | |
|  */
 | |
| typedef struct _coff_reloc {
 | |
|     COFFModulePtr file;
 | |
|     RELOC *rel;
 | |
|     int secndx;
 | |
|     struct _coff_reloc *next;
 | |
| } COFFRelocRec;
 | |
| 
 | |
| /*
 | |
|  * Symbols with a section number of 0 (N_UNDEF) but a value of non-zero
 | |
|  * need to have space allocated for them.
 | |
|  *
 | |
|  * Gather all of these symbols together, and allocate one chunk when we
 | |
|  * are done.
 | |
|  */
 | |
| 
 | |
| typedef struct _coff_COMMON {
 | |
|     SYMENT *sym;
 | |
|     int index;
 | |
|     struct _coff_COMMON *next;
 | |
| } COFFCommonRec;
 | |
| 
 | |
| static COFFCommonPtr listCOMMON = NULL;
 | |
| 
 | |
| /* Prototypes for static functions */
 | |
| static int COFFhashCleanOut(void *, itemPtr);
 | |
| static char *COFFGetSymbolName(COFFModulePtr, int);
 | |
| static COFFCommonPtr COFFAddCOMMON(SYMENT *, int);
 | |
| static LOOKUP *COFFCreateCOMMON(COFFModulePtr);
 | |
| static COFFRelocPtr COFFDelayRelocation(COFFModulePtr, int, RELOC *);
 | |
| static SYMENT *COFFGetSymbol(COFFModulePtr, int);
 | |
| #if defined(i386) || defined(__powerpc__)
 | |
| static unsigned char *COFFGetSymbolValue(COFFModulePtr, int);
 | |
| #endif
 | |
| static COFFRelocPtr COFF_RelocateEntry(COFFModulePtr, int, RELOC *);
 | |
| static LOOKUP *COFF_GetSymbols(COFFModulePtr);
 | |
| static void COFFCollectSections(COFFModulePtr);
 | |
| static COFFRelocPtr COFFCollectRelocations(COFFModulePtr);
 | |
| 
 | |
| /*
 | |
|  * Utility Functions
 | |
|  */
 | |
| 
 | |
| static int
 | |
| COFFhashCleanOut(void *voidptr, itemPtr item)
 | |
| {
 | |
|     COFFModulePtr module = (COFFModulePtr) voidptr;
 | |
| 
 | |
|     return (module->handle == item->handle);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Manage listResolv
 | |
|  */
 | |
| static COFFRelocPtr
 | |
| COFFDelayRelocation(COFFModulePtr cofffile, int secndx, RELOC *rel)
 | |
| {
 | |
|     COFFRelocPtr reloc;
 | |
| 
 | |
|     if ((reloc = xf86loadermalloc(sizeof(COFFRelocRec))) == NULL) {
 | |
| 	ErrorF("COFFDelayRelocation() Unable to allocate memory!!!!\n");
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     reloc->file = cofffile;
 | |
|     reloc->secndx = secndx;
 | |
|     reloc->rel = rel;
 | |
|     reloc->next = 0;
 | |
| 
 | |
|     return reloc;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Manage listCOMMON
 | |
|  */
 | |
| 
 | |
| static COFFCommonPtr
 | |
| COFFAddCOMMON(SYMENT *sym, int index)
 | |
| {
 | |
|     COFFCommonPtr common;
 | |
| 
 | |
|     if ((common = xf86loadermalloc(sizeof(COFFCommonRec))) == NULL) {
 | |
| 	ErrorF("COFFAddCOMMON() Unable to allocate memory!!!!\n");
 | |
| 	return 0;
 | |
|     }
 | |
|     common->sym = sym;
 | |
|     common->index = index;
 | |
|     common->next = 0;
 | |
| 
 | |
|     return common;
 | |
| }
 | |
| 
 | |
| static LOOKUP *
 | |
| COFFCreateCOMMON(COFFModulePtr cofffile)
 | |
| {
 | |
|     int numsyms = 0, size = 0, l = 0;
 | |
|     int offset = 0;
 | |
|     LOOKUP *lookup;
 | |
|     COFFCommonPtr common;
 | |
| 
 | |
|     if (listCOMMON == NULL)
 | |
| 	return NULL;
 | |
| 
 | |
|     common = listCOMMON;
 | |
|     for (common = listCOMMON; common; common = common->next) {
 | |
| 	/* Ensure long word alignment */
 | |
| 	if (common->sym->n_value != 2 && common->sym->n_value != 1)	/* But not for short and char ;-)(mr) */
 | |
| 	    if (common->sym->n_value % 4 != 0)
 | |
| 		common->sym->n_value += 4 - (common->sym->n_value % 4);
 | |
| 
 | |
| 	/* accumulate the sizes */
 | |
| 	size += common->sym->n_value;
 | |
| 	numsyms++;
 | |
|     }
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFFCreateCOMMON() %d entries (%d bytes) of COMMON data\n",
 | |
| 	      numsyms, size);
 | |
| #endif
 | |
| 
 | |
|     if ((lookup = xf86loadermalloc((numsyms + 1) * sizeof(LOOKUP))) == NULL) {
 | |
| 	ErrorF("COFFCreateCOMMON() Unable to allocate memory!!!!\n");
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     cofffile->comsize = size;
 | |
|     if ((cofffile->common = xf86loadercalloc(1, size)) == NULL) {
 | |
| 	ErrorF("COFFCreateCOMMON() Unable to allocate memory!!!!\n");
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     /* Traverse the common list and create a lookup table with all the
 | |
|      * common symbols.  Destroy the common list in the process.
 | |
|      * See also ResolveSymbols.
 | |
|      */
 | |
|     while (listCOMMON) {
 | |
| 	common = listCOMMON;
 | |
| 	lookup[l].symName = COFFGetSymbolName(cofffile, common->index);
 | |
| 	lookup[l].offset = (funcptr) (cofffile->common + offset);
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("Adding %p %s\n", (void *)lookup[l].offset,
 | |
| 		  lookup[l].symName);
 | |
| #endif
 | |
| 	listCOMMON = common->next;
 | |
| 	offset += common->sym->n_value;
 | |
| 	xf86loaderfree(common);
 | |
| 	l++;
 | |
|     }
 | |
|     /* listCOMMON == NULL */
 | |
| 
 | |
|     lookup[l].symName = NULL;	/* Terminate the list */
 | |
|     return lookup;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Symbol Table
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Get symbol name
 | |
|  */
 | |
| static char *
 | |
| COFFGetSymbolName(COFFModulePtr cofffile, int index)
 | |
| {
 | |
|     char *name;
 | |
|     SYMENT *sym;
 | |
| 
 | |
|     sym = (SYMENT *) (((unsigned char *)cofffile->symtab) + (index * SYMESZ));
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFFGetSymbolName(%p,%x) %lx", (void *)cofffile, index,
 | |
| 	      sym->n_zeroes);
 | |
| #endif
 | |
| 
 | |
|     name = xf86loadermalloc(sym->n_zeroes ? SYMNMLEN + 1
 | |
| 			    : strlen((const char *)&cofffile->
 | |
| 				     strtab[(int)sym->n_offset - 4]) + 1);
 | |
|     if (!name)
 | |
| 	FatalError("COFFGetSymbolName: Out of memory\n");
 | |
| 
 | |
|     if (sym->n_zeroes) {
 | |
| 	strncpy(name, sym->n_name, SYMNMLEN);
 | |
| 	name[SYMNMLEN] = '\000';
 | |
|     } else {
 | |
| 	strcpy(name, (const char *)&cofffile->strtab[(int)sym->n_offset - 4]);
 | |
|     }
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG(" %s\n", name);
 | |
| #endif
 | |
|     return name;
 | |
| }
 | |
| 
 | |
| static SYMENT *
 | |
| COFFGetSymbol(COFFModulePtr file, int index)
 | |
| {
 | |
|     return (SYMENT *) (((unsigned char *)file->symtab) + (index * SYMESZ));
 | |
| }
 | |
| 
 | |
| #if defined(i386) || defined(__powerpc__)
 | |
| static unsigned char *
 | |
| COFFGetSymbolValue(COFFModulePtr cofffile, int index)
 | |
| {
 | |
|     unsigned char *symval = 0;	/* value of the indicated symbol */
 | |
|     itemPtr symbol;		/* name/value of symbol */
 | |
|     char *symname;
 | |
| 
 | |
|     symname = COFFGetSymbolName(cofffile, index);
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFFGetSymbolValue() for %s=", symname);
 | |
| #endif
 | |
| 
 | |
|     symbol = LoaderHashFind(symname);
 | |
| 
 | |
|     if (symbol)
 | |
| 	symval = (unsigned char *)symbol->address;
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("%p\n", symval);
 | |
| #endif
 | |
| 
 | |
|     xf86loaderfree(symname);
 | |
|     return symval;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if defined(__powerpc__)
 | |
| /*
 | |
|  * This function returns the address of the glink routine for a symbol. This
 | |
|  * address is used in cases where the function being called is not in the
 | |
|  * same module as the calling function.
 | |
|  */
 | |
| static unsigned char *
 | |
| COFFGetSymbolGlinkValue(COFFModulePtr cofffile, int index)
 | |
| {
 | |
|     unsigned char *symval = 0;	/* value of the indicated symbol */
 | |
|     itemPtr symbol;		/* name/value of symbol */
 | |
|     char *name;
 | |
| 
 | |
|     name = COFFGetSymbolName(cofffile, index);
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFFGetSymbolGlinkValue() for %s=", name);
 | |
| #endif
 | |
| 
 | |
|     symbol = LoaderHashFind(name + 1);	/* Eat the '.' so we get the
 | |
| 					 * Function descriptor instead */
 | |
| 
 | |
| /* Here we are building up a glink function that will change the TOC
 | |
|  * pointer before calling a function that resides in a different module.
 | |
|  * The following code is being used to implement this.
 | |
| 
 | |
| 	1 00000000 3d80xxxx	lis   r12,hi16(funcdesc)
 | |
| 	2 00000004 618cxxxx	ori   r12,r12,lo16(funcdesc)
 | |
| 	3 00000008 90410014	st    r2,20(r1)	# save old TOC pointer
 | |
| 	4 0000000c 804c0000 	l     r2,0(r12)	# Get address of functions
 | |
| 	5 00000010 7c4903a6	mtctr  r2	# load destination address
 | |
| 	6 00000014 804c0004	l     r2,4(r12)	# get TOC of function
 | |
| 	7 00000018 4e800420	bctr		# branch to it
 | |
| 
 | |
|  */
 | |
|     if (symbol) {
 | |
| 	symval = (unsigned char *)&symbol->code.glink;
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("%x\n", symval);
 | |
| 	COFFDEBUG("glink_%s=%x\n", name, symval);
 | |
| #endif
 | |
| 	symbol->code.glink[0] = 0x3d80;	/* lis r12 */
 | |
| 	symbol->code.glink[1] =
 | |
| 		((unsigned long)symbol->address & 0xffff0000) >> 16;
 | |
| 	symbol->code.glink[2] = 0x618c;	/* ori r12 */
 | |
| 	symbol->code.glink[3] = ((unsigned long)symbol->address & 0x0000ffff);
 | |
| 	symbol->code.glink[4] = 0x9041;	/* st r2,20(r1) */
 | |
| 	symbol->code.glink[5] = 0x0014;
 | |
| 	symbol->code.glink[6] = 0x804c;	/* l r2,0(r12) */
 | |
| 	symbol->code.glink[7] = 0x0000;
 | |
| 	symbol->code.glink[8] = 0x7c49;	/* mtctr r2 */
 | |
| 	symbol->code.glink[9] = 0x03a6;
 | |
| 	symbol->code.glink[10] = 0x804c;	/* l r2,4(r12) */
 | |
| 	symbol->code.glink[11] = 0x0004;
 | |
| 	symbol->code.glink[12] = 0x4e80;	/* bctr */
 | |
| 	symbol->code.glink[13] = 0x0420;
 | |
| 	ppc_flush_icache(&symbol->code.glink[0]);
 | |
| 	ppc_flush_icache(&symbol->code.glink[12]);
 | |
|     }
 | |
| 
 | |
|     xf86loaderfree(name);
 | |
|     return symval;
 | |
| }
 | |
| #endif /* __powerpc__ */
 | |
| 
 | |
| /*
 | |
|  * Fix all of the relocation for the given section.
 | |
|  */
 | |
| static COFFRelocPtr
 | |
| COFF_RelocateEntry(COFFModulePtr cofffile, int secndx, RELOC *rel)
 | |
| {
 | |
|     SYMENT *symbol;		/* value of the indicated symbol */
 | |
|     unsigned long *dest32;	/* address of the place being modified */
 | |
| 
 | |
| #if defined(__powerpc__)
 | |
|     unsigned short *dest16;	/* address of the place being modified */
 | |
|     itemPtr symitem;		/* symbol structure from has table */
 | |
|     char *name;
 | |
| #endif
 | |
|     unsigned char *symval;	/* value of the indicated symbol */
 | |
| 
 | |
| /*
 | |
|  * Note: Section numbers are 1 biased, while the cofffile->saddr[] array
 | |
|  * of pointer is 0 biased, so alway have to account for the difference.
 | |
|  */
 | |
| 
 | |
| /* 
 | |
|  * Reminder: secndx is the section to which the relocation is applied.
 | |
|  *           symbol->n_scnum is the section in which the symbol value resides.
 | |
|  */
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("%lx %ld %o ", (unsigned long)rel->r_vaddr,
 | |
| 	      rel->r_symndx, rel->r_type);
 | |
| #if defined(__powerpc__)
 | |
|     COFFDEBUG("[%x %x %x] ",
 | |
| 	      RELOC_RSIGN(*rel), RELOC_RFIXUP(*rel), RELOC_RLEN(*rel));
 | |
| #endif
 | |
| #endif
 | |
|     symbol = COFFGetSymbol(cofffile, rel->r_symndx);
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("%d %lx %d-%d\n", symbol->n_sclass, symbol->n_value,
 | |
| 	      symbol->n_scnum, secndx);
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Check to see if the relocation offset is part of the .text segment.
 | |
|  * If not, we must change the offset to be relative to the .data section
 | |
|  * which is NOT contiguous.
 | |
|  */
 | |
|     switch (secndx + 1) {	/* change the bias */
 | |
|     case N_TEXT:
 | |
| 	if ((long)rel->r_vaddr < cofffile->txtaddr ||
 | |
| 	    (long)rel->r_vaddr >
 | |
| 	    (long)(cofffile->txtaddr + cofffile->txtsize)) {
 | |
| 	    FatalError("Relocation against N_TEXT not in .text section\n");
 | |
| 	}
 | |
| 	dest32 = (unsigned long *)((long)(cofffile->saddr[secndx]) +
 | |
| 				   ((unsigned char *)rel->r_vaddr -
 | |
| 				    cofffile->txtaddr));
 | |
| 	break;
 | |
|     case N_DATA:
 | |
| 	if ((long)rel->r_vaddr < cofffile->dataddr ||
 | |
| 	    (long)rel->r_vaddr >
 | |
| 	    (long)(cofffile->dataddr + cofffile->datsize)) {
 | |
| 	    FatalError("Relocation against N_DATA not in .data section\n");
 | |
| 	}
 | |
| 	dest32 = (unsigned long *)((long)(cofffile->saddr[secndx]) +
 | |
| 				   ((unsigned char *)rel->r_vaddr -
 | |
| 				    cofffile->dataddr));
 | |
| 	break;
 | |
|     case N_BSS:
 | |
| 	if ((long)rel->r_vaddr < cofffile->bssaddr ||
 | |
| 	    (long)rel->r_vaddr >
 | |
| 	    (long)(cofffile->bssaddr + cofffile->bsssize)) {
 | |
| 	    FatalError("Relocation against N_TEXT not in .bss section\n");
 | |
| 	}
 | |
| 	dest32 = (unsigned long *)((long)(cofffile->saddr[secndx]) +
 | |
| 				   ((unsigned char *)rel->r_vaddr -
 | |
| 				    cofffile->bssaddr));
 | |
| 	break;
 | |
|     default:
 | |
| 	FatalError("Relocation against unknown section %d\n", secndx);
 | |
|     }
 | |
| 
 | |
|     if (symbol->n_sclass == 0) {
 | |
| 	symval = (unsigned char *)(symbol->n_value + (*dest32) -
 | |
| 				   symbol->n_type);
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("symbol->n_sclass==0\n");
 | |
| 	COFFDEBUG("dest32=%p\t", (void *)dest32);
 | |
| 	COFFDEBUG("symval=%p\t", symval);
 | |
| 	COFFDEBUG("*dest32=%8.8lx\t", *dest32);
 | |
| #endif
 | |
| 	*dest32 = (unsigned long)symval;
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     switch (rel->r_type) {
 | |
| #if defined(i386)
 | |
|     case R_DIR32:
 | |
| 	symval = COFFGetSymbolValue(cofffile, rel->r_symndx);
 | |
| 	if (symval) {
 | |
| #ifdef COFFDEBUG
 | |
| 	    char *namestr;
 | |
| 
 | |
| 	    COFFDEBUG("R_DIR32 %s\n",
 | |
| 		      namestr = COFFGetSymbolName(cofffile, rel->r_symndx));
 | |
| 	    xf86loaderfree(namestr);
 | |
| 	    COFFDEBUG("txtsize=%x\t", cofffile->txtsize);
 | |
| 	    COFFDEBUG("dest32=%p\t", (void *)dest32);
 | |
| 	    COFFDEBUG("symval=%p\t", symval);
 | |
| 	    COFFDEBUG("*dest32=%8.8lx\t", *dest32);
 | |
| #endif
 | |
| 	    *dest32 = (unsigned long)(symval + (*dest32) - symbol->n_value);
 | |
| 	} else {
 | |
| 	    switch (symbol->n_scnum) {
 | |
| 	    case N_UNDEF:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_UNDEF\n");
 | |
| #endif
 | |
| 		return COFFDelayRelocation(cofffile, secndx, rel);
 | |
| 	    case N_ABS:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_ABS\n");
 | |
| #endif
 | |
| 		return 0;
 | |
| 	    case N_DEBUG:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_DEBUG\n");
 | |
| #endif
 | |
| 		return 0;
 | |
| 	    case N_COMMENT:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_COMMENT\n");
 | |
| #endif
 | |
| 		return 0;
 | |
| 	    case N_TEXT:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_TEXT\n");
 | |
| 		COFFDEBUG("dest32=%p\t", (void *)dest32);
 | |
| 		COFFDEBUG("symval=%p\t", symval);
 | |
| 		COFFDEBUG("*dest32=%8.8lx\t", *dest32);
 | |
| #endif
 | |
| 		*dest32 = (unsigned long)((*dest32) +
 | |
| 					  (unsigned long)(cofffile->
 | |
| 							  saddr[N_TEXT - 1]));
 | |
| 		break;
 | |
| 	    case N_DATA:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_DATA\n");
 | |
| 		COFFDEBUG("txtsize=%x\t", cofffile->txtsize);
 | |
| 		COFFDEBUG("dest32=%p\t", (void *)dest32);
 | |
| 		COFFDEBUG("symval=%p\t", symval);
 | |
| 		COFFDEBUG("*dest32=%8.8lx\t", *dest32);
 | |
| #endif
 | |
| 		*dest32 = (unsigned long)((*dest32) +
 | |
| 					  ((unsigned long)(cofffile->
 | |
| 							   saddr[N_DATA -
 | |
| 								 1])) -
 | |
| 					  cofffile->dataddr);
 | |
| 		break;
 | |
| 	    case N_BSS:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_DIR32 N_BSS\n");
 | |
| 		COFFDEBUG("dest32=%p\t", (void *)dest32);
 | |
| 		COFFDEBUG("symval=%p\t", symval);
 | |
| 		COFFDEBUG("*dest32=%8.8lx\t", *dest32);
 | |
| #endif
 | |
| 		*dest32 = (unsigned long)((*dest32) +
 | |
| 					  (unsigned long)(cofffile->
 | |
| 							  saddr[N_BSS - 1]) -
 | |
| 					  (cofffile->bssaddr));
 | |
| 		break;
 | |
| 	    default:
 | |
| 		ErrorF("R_DIR32 with unexpected section %d\n",
 | |
| 		       symbol->n_scnum);
 | |
| 	    }
 | |
| 
 | |
| 	}
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("*dest32=%8.8lx\n", *dest32);
 | |
| #endif
 | |
| 	break;
 | |
|     case R_PCRLONG:
 | |
| 	if (symbol->n_scnum == N_TEXT)
 | |
| 	    break;
 | |
| 
 | |
| 	symval = COFFGetSymbolValue(cofffile, rel->r_symndx);
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("R_PCRLONG ");
 | |
| 	COFFDEBUG("dest32=%p\t", (void *)dest32);
 | |
| 	COFFDEBUG("symval=%p\t", symval);
 | |
| 	COFFDEBUG("*dest32=%8.8lx\t", *dest32);
 | |
| #endif
 | |
| 	if (symval == 0) {
 | |
| #ifdef COFFDEBUG
 | |
| 	    char *name;
 | |
| 
 | |
| 	    COFFDEBUG("***Unable to resolve symbol %s\n",
 | |
| 		      name = COFFGetSymbolName(cofffile, rel->r_symndx));
 | |
| 	    xf86loaderfree(name);
 | |
| #endif
 | |
| 	    return COFFDelayRelocation(cofffile, secndx, rel);
 | |
| 	}
 | |
| 	*dest32 = (unsigned long)(symval - ((long)dest32 + sizeof(long)));
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("*dest32=%8.8lx\n", *dest32);
 | |
| #endif
 | |
| 	break;
 | |
|     case R_ABS:
 | |
| 	/*
 | |
| 	 * Nothing to really do here.
 | |
| 	 * Usually, a dummy relocation for .file
 | |
| 	 */
 | |
| 	break;
 | |
| #endif /* i386 */
 | |
| #if defined(__powerpc__)
 | |
|     case R_POS:
 | |
| 	/*
 | |
| 	 * Positive Relocation
 | |
| 	 */
 | |
| 	if (RELOC_RLEN(*rel) != 0x1f)
 | |
| 	    FatalError("R_POS with size != 32 bits");
 | |
| 	symval = COFFGetSymbolValue(cofffile, rel->r_symndx);
 | |
| 	if (symval) {
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("R_POS ");
 | |
| 	    COFFDEBUG("dest32=%x\t", dest32);
 | |
| 	    COFFDEBUG("symval=%x\t", symval);
 | |
| 	    COFFDEBUG("*dest32=%8.8x\t", *dest32);
 | |
| #endif
 | |
| 	    *dest32 = (unsigned long)(symval + (*dest32) - symbol->n_value);
 | |
| 	    ppc_flush_icache(dest32);
 | |
| 	} else {
 | |
| 	    switch (symbol->n_scnum) {
 | |
| 	    case N_UNDEF:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_UNDEF\n");
 | |
| #endif
 | |
| 		return COFFDelayRelocation(cofffile, secndx, rel);
 | |
| 	    case N_ABS:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_ABS\n");
 | |
| #endif
 | |
| 		return 0;
 | |
| 	    case N_DEBUG:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_DEBUG\n");
 | |
| #endif
 | |
| 		return 0;
 | |
| 	    case N_COMMENT:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_COMMENT\n");
 | |
| #endif
 | |
| 		return 0;
 | |
| 	    case N_TEXT:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_TEXT\n");
 | |
| 		COFFDEBUG("dest32=%x\t", dest32);
 | |
| 		COFFDEBUG("symval=%x\t", symval);
 | |
| 		COFFDEBUG("*dest32=%8.8x\t", *dest32);
 | |
| #endif
 | |
| 		*dest32 = (unsigned long)((*dest32) +
 | |
| 					  ((unsigned long)(cofffile->
 | |
| 							   saddr[N_TEXT -
 | |
| 								 1])) -
 | |
| 					  cofffile->txtaddr);
 | |
| 		ppc_flush_icache(dest32);
 | |
| 		break;
 | |
| 	    case N_DATA:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_DATA\n");
 | |
| 		COFFDEBUG("txtsize=%x\t", cofffile->txtsize);
 | |
| 		COFFDEBUG("dest32=%x\t", dest32);
 | |
| 		COFFDEBUG("symval=%x\t", symval);
 | |
| 		COFFDEBUG("*dest32=%8.8x\t", *dest32);
 | |
| #endif
 | |
| 		*dest32 = (unsigned long)((*dest32) +
 | |
| 					  ((unsigned long)(cofffile->
 | |
| 							   saddr[N_DATA -
 | |
| 								 1])) -
 | |
| 					  cofffile->dataddr);
 | |
| 		ppc_flush_icache(dest32);
 | |
| 		break;
 | |
| 	    case N_BSS:
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("R_POS N_BSS\n");
 | |
| 		COFFDEBUG("dest32=%x\t", dest32);
 | |
| 		COFFDEBUG("symval=%x\t", symval);
 | |
| 		COFFDEBUG("*dest32=%8.8x\t", *dest32);
 | |
| #endif
 | |
| 		*dest32 = (unsigned long)((*dest32) +
 | |
| 					  (unsigned long)(cofffile->
 | |
| 							  saddr[N_BSS - 1]) -
 | |
| 					  (cofffile->bssaddr));
 | |
| 		ppc_flush_icache(dest32);
 | |
| 		break;
 | |
| 	    default:
 | |
| 		ErrorF("R_POS with unexpected section %d\n", symbol->n_scnum);
 | |
| 	    }
 | |
| 	}
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("*dest32=%8.8x\t", *dest32);
 | |
| 	COFFDEBUG("\n");
 | |
| #endif
 | |
| 	break;
 | |
|     case R_TOC:
 | |
| 	/*
 | |
| 	 * Relative to TOC
 | |
| 	 */
 | |
| 	{
 | |
| 	    dest16 = (unsigned short *)dest32;
 | |
| 	    if (RELOC_RLEN(*rel) != 0x0f)
 | |
| 		FatalError("R_TOC with size != 16 bits");
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("R_TOC ");
 | |
| 	    COFFDEBUG("dest16=%x\t", dest16);
 | |
| 	    COFFDEBUG("symbol=%x\t", symbol);
 | |
| 	    COFFDEBUG("symbol->n_value=%x\t", symbol->n_value);
 | |
| 	    COFFDEBUG("cofffile->toc=%x\t", cofffile->toc);
 | |
| 	    COFFDEBUG("*dest16=%8.8x\t", *dest16);
 | |
| #endif
 | |
| 	    *dest16 = (unsigned long)((symbol->n_value - cofffile->toc));
 | |
| 	    ppc_flush_icache(dest16);
 | |
| 	}
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("*dest16=%8.8x\t", *dest16);
 | |
| 	COFFDEBUG("\n");
 | |
| #endif
 | |
| 	break;
 | |
|     case R_BR:
 | |
| 	/*
 | |
| 	 * Branch relative to self, non-modifiable
 | |
| 	 */
 | |
| 
 | |
| 	if (RELOC_RLEN(*rel) != 0x19)
 | |
| 	    FatalError("R_BR with size != 24 bits");
 | |
| 	name = COFFGetSymbolName(cofffile, rel->r_symndx);
 | |
| 	symitem = LoaderHashFind(name);
 | |
| 	if (symitem == 0) {
 | |
| 	    name++;
 | |
| 	    symitem = LoaderHashFind(name);
 | |
| 	}
 | |
| 	if (symitem && cofffile->module != symitem->module) {
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("Symbol module %d != file module %d\n",
 | |
| 		      symitem->module, cofffile->module);
 | |
| #endif
 | |
| 	    symval = COFFGetSymbolGlinkValue(cofffile, rel->r_symndx);
 | |
| 	} else
 | |
| 	    symval = COFFGetSymbolValue(cofffile, rel->r_symndx);
 | |
| 	if (symval == 0) {
 | |
| #ifdef COFFDEBUG
 | |
| 	    char *name;
 | |
| 
 | |
| 	    COFFDEBUG("***Unable to resolve symbol %s\n",
 | |
| 		      name = COFFGetSymbolName(cofffile, rel->r_symndx));
 | |
| 	    xf86loaderfree(name);
 | |
| #endif
 | |
| 	    return COFFDelayRelocation(cofffile, secndx, rel);
 | |
| 	}
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("R_BR ");
 | |
| 	COFFDEBUG("dest32=%x\t", dest32);
 | |
| 	COFFDEBUG("symval=%x\t", symval);
 | |
| 	COFFDEBUG("*dest32=%8.8x\t", *dest32);
 | |
| #endif
 | |
| 	{
 | |
| 	    unsigned long val;
 | |
| 
 | |
| 	    val = ((unsigned long)symval - (unsigned long)dest32);
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("val=%8.8x\n", val);
 | |
| #endif
 | |
| 	    val = val >> 2;
 | |
| 	    if ((val & 0x3f000000) != 0x3f000000 &&
 | |
| 		(val & 0x3f000000) != 0x00000000) {
 | |
| 		FatalError("R_BR offset %x too large\n", val << 2);
 | |
| 		break;
 | |
| 	    }
 | |
| 	    val &= 0x00ffffff;
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("val=%8.8x\n", val);
 | |
| #endif
 | |
| 	    /*
 | |
| 	     * The address part contains the offset to the beginning
 | |
| 	     * of the .text section. Disreguard this since we have
 | |
| 	     * calculated the correct offset already.
 | |
| 	     */
 | |
| 	    (*dest32) = ((*dest32) & 0xfc000003) | (val << 2);
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("*dest32=%8.8x\n", *dest32);
 | |
| #endif
 | |
| 	    if (cofffile->module != symitem->module) {
 | |
| 		(*++dest32) = 0x80410014;	/* lwz r2,20(r1) */
 | |
| 	    }
 | |
| 	    ppc_flush_icache(--dest32);
 | |
| 	}
 | |
| 
 | |
| 	break;
 | |
| #endif /* __powerpc__ */
 | |
|     default:
 | |
| 	ErrorF("COFF_RelocateEntry() Unsupported relocation type %o\n",
 | |
| 	       rel->r_type);
 | |
| 	break;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static COFFRelocPtr
 | |
| COFFCollectRelocations(COFFModulePtr cofffile)
 | |
| {
 | |
|     unsigned short i, j;
 | |
|     RELOC *rel;
 | |
|     SCNHDR *sec;
 | |
|     COFFRelocPtr reloc_head = NULL;
 | |
|     COFFRelocPtr tmp;
 | |
| 
 | |
|     for (i = 0; i < cofffile->numsh; i++) {
 | |
| 	if (cofffile->saddr[i] == NULL)
 | |
| 	    continue;		/* Section not loaded!! */
 | |
| 	sec = &(cofffile->sections[i]);
 | |
| 	for (j = 0; j < sec->s_nreloc; j++) {
 | |
| 	    rel = (RELOC *) (cofffile->reladdr[i] + (j * RELSZ));
 | |
| 	    tmp = COFFDelayRelocation(cofffile, i, rel);
 | |
| 	    tmp->next = reloc_head;
 | |
| 	    reloc_head = tmp;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return reloc_head;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * COFF_GetSymbols()
 | |
|  *
 | |
|  * add the symbols to the symbol table maintained by the loader.
 | |
|  */
 | |
| 
 | |
| static LOOKUP *
 | |
| COFF_GetSymbols(COFFModulePtr cofffile)
 | |
| {
 | |
|     SYMENT *sym;
 | |
|     AUXENT *aux = NULL;
 | |
|     int i, l, numsyms;
 | |
|     LOOKUP *lookup, *lookup_common, *p;
 | |
|     char *symname;
 | |
| 
 | |
| /*
 | |
|  * Load the symbols into memory
 | |
|  */
 | |
|     numsyms = cofffile->header->f_nsyms;
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFF_GetSymbols(): %d symbols\n", numsyms);
 | |
| #endif
 | |
| 
 | |
|     cofffile->symsize = (numsyms * SYMESZ);
 | |
|     cofffile->symtab =
 | |
| 	    (SYMENT *) _LoaderFileToMem(cofffile->fd,
 | |
| 					cofffile->header->f_symptr,
 | |
| 					(numsyms * SYMESZ), "symbols");
 | |
| 
 | |
|     if ((lookup = xf86loadermalloc((numsyms + 1) * sizeof(LOOKUP))) == NULL)
 | |
| 	return NULL;
 | |
| 
 | |
|     for (i = 0, l = 0; i < numsyms; i++) {
 | |
| 	sym = (SYMENT *) (((unsigned char *)cofffile->symtab) + (i * SYMESZ));
 | |
| 	symname = COFFGetSymbolName(cofffile, i);
 | |
| 	if (sym->n_numaux > 0)
 | |
| 	    aux = (AUXENT *) (((unsigned char *)cofffile->symtab) +
 | |
| 			      ((i + 1) * SYMESZ));
 | |
| 	else
 | |
| 	    aux = NULL;
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("\t%d %d %lx %x %d %d %s\n",
 | |
| 		  i, sym->n_scnum, sym->n_value, sym->n_type,
 | |
| 		  sym->n_sclass, sym->n_numaux, symname);
 | |
| 	if (aux)
 | |
| 	    COFFDEBUG("aux=\t%ld %lx %x %x %x %lx %x\n",
 | |
| 		      aux->x_scnlen, aux->x_parmhash, aux->x_snhash,
 | |
| 		      aux->x_smtyp, aux->x_smclas, aux->x_stab,
 | |
| 		      aux->x_snstab);
 | |
| #endif
 | |
| 	i += sym->n_numaux;
 | |
| 	/*
 | |
| 	 * check for TOC csect before discarding C_HIDEXT below
 | |
| 	 */
 | |
| 	if (aux && aux->x_smclas == XMC_TC0) {
 | |
| 	    if (sym->n_scnum != N_DATA)
 | |
| 		FatalError("TOC not in N_DATA section");
 | |
| 	    cofffile->toc = sym->n_value;
 | |
| 	    cofffile->tocaddr = (cofffile->saddr[sym->n_scnum - 1] +
 | |
| 				 sym->n_value - (cofffile->dataddr));
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("TOC=%lx\n", cofffile->toc);
 | |
| 	    COFFDEBUG("TOCaddr=%p\n", cofffile->tocaddr);
 | |
| #endif
 | |
| 	    continue;
 | |
| 	}
 | |
| 	if (sym->n_sclass == C_HIDEXT) {
 | |
| /*
 | |
| 		&& aux && !(aux->x_smclas == XMC_DS
 | |
| 		&& aux->x_smtyp == XTY_SD) ) ) {
 | |
| */
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("Skipping C_HIDEXT class symbol %s\n", symname);
 | |
| #endif
 | |
| 	    continue;
 | |
| 	}
 | |
| 	switch (sym->n_scnum) {
 | |
| 	case N_UNDEF:
 | |
| 	    if (sym->n_value != 0) {
 | |
| 		char *name;
 | |
| 		COFFCommonPtr tmp;
 | |
| 
 | |
| 		name = COFFGetSymbolName(cofffile, i);
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("Adding COMMON space for %s\n", name);
 | |
| #endif
 | |
| 		if (!LoaderHashFind(name)) {
 | |
| 		    tmp = COFFAddCOMMON(sym, i);
 | |
| 		    if (tmp) {
 | |
| 			tmp->next = listCOMMON;
 | |
| 			listCOMMON = tmp;
 | |
| 		    }
 | |
| 		}
 | |
| 		xf86loaderfree(name);
 | |
| 	    }
 | |
| 	    xf86loaderfree(symname);
 | |
| 	    break;
 | |
| 	case N_ABS:
 | |
| 	case N_DEBUG:
 | |
| 	case N_COMMENT:
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG("Freeing %s, section %d\n", symname, sym->n_scnum);
 | |
| #endif
 | |
| 	    xf86loaderfree(symname);
 | |
| 	    break;
 | |
| 	case N_TEXT:
 | |
| 	    if ((sym->n_sclass == C_EXT || sym->n_sclass == C_HIDEXT)
 | |
| 		&& cofffile->saddr[sym->n_scnum - 1]) {
 | |
| 		lookup[l].symName = symname;
 | |
| 		lookup[l].offset = (funcptr)
 | |
| 			(cofffile->saddr[sym->n_scnum - 1] +
 | |
| 			 sym->n_value - cofffile->txtaddr);
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("Adding %p %s\n",
 | |
| 			  (void *)lookup[l].offset, lookup[l].symName);
 | |
| #endif
 | |
| 		l++;
 | |
| 	    } else {
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("TEXT Section not loaded %d\n", sym->n_scnum - 1);
 | |
| #endif
 | |
| 		xf86loaderfree(symname);
 | |
| 	    }
 | |
| 	    break;
 | |
| 	case N_DATA:
 | |
| 	    /*
 | |
| 	     * Note: COFF expects .data to be contiguous with
 | |
| 	     * .data, so that offsets for .data are relative to
 | |
| 	     * .text. We need to adjust for this, and make them
 | |
| 	     * relative to .data so that the relocation can be
 | |
| 	     * properly applied. This is needed becasue we allocate
 | |
| 	     * .data seperately from .text.
 | |
| 	     */
 | |
| 	    if ((sym->n_sclass == C_EXT || sym->n_sclass == C_HIDEXT)
 | |
| 		&& cofffile->saddr[sym->n_scnum - 1]) {
 | |
| 		lookup[l].symName = symname;
 | |
| 		lookup[l].offset = (funcptr)
 | |
| 			(cofffile->saddr[sym->n_scnum - 1] +
 | |
| 			 sym->n_value - cofffile->dataddr);
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("Adding %p %s\n",
 | |
| 			  (void *)lookup[l].offset, lookup[l].symName);
 | |
| #endif
 | |
| 		l++;
 | |
| 	    } else {
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("DATA Section not loaded %d\n", sym->n_scnum - 1);
 | |
| #endif
 | |
| 		xf86loaderfree(symname);
 | |
| 	    }
 | |
| 	    break;
 | |
| 	case N_BSS:
 | |
| 	    /*
 | |
| 	     * Note: COFF expects .bss to be contiguous with
 | |
| 	     * .data, so that offsets for .bss are relative to
 | |
| 	     * .text. We need to adjust for this, and make them
 | |
| 	     * relative to .bss so that the relocation can be
 | |
| 	     * properly applied. This is needed becasue we allocate
 | |
| 	     * .bss seperately from .text and .data.
 | |
| 	     */
 | |
| 	    if ((sym->n_sclass == C_EXT || sym->n_sclass == C_HIDEXT)
 | |
| 		&& cofffile->saddr[sym->n_scnum - 1]) {
 | |
| 		lookup[l].symName = symname;
 | |
| 		lookup[l].offset = (funcptr)
 | |
| 			(cofffile->saddr[sym->n_scnum - 1] +
 | |
| 			 sym->n_value - cofffile->bssaddr);
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("Adding %p %s\n",
 | |
| 			  (void *)lookup[l].offset, lookup[l].symName);
 | |
| #endif
 | |
| 		l++;
 | |
| 	    } else {
 | |
| #ifdef COFFDEBUG
 | |
| 		COFFDEBUG("BSS Section not loaded %d\n", sym->n_scnum - 1);
 | |
| #endif
 | |
| 		xf86loaderfree(symname);
 | |
| 	    }
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    ErrorF("Unknown Section number %d\n", sym->n_scnum);
 | |
| 	    xf86loaderfree(symname);
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     lookup[l].symName = NULL;	/* Terminate the list */
 | |
| 
 | |
|     lookup_common = COFFCreateCOMMON(cofffile);
 | |
|     if (lookup_common) {
 | |
| 	for (i = 0, p = lookup_common; p->symName; i++, p++) ;
 | |
| 	memcpy(&(lookup[l]), lookup_common, i * sizeof(LOOKUP));
 | |
| 
 | |
| 	xf86loaderfree(lookup_common);
 | |
| 	l += i;
 | |
| 	lookup[l].symName = NULL;
 | |
|     }
 | |
| 
 | |
| /*
 | |
|  * remove the COFF symbols that will show up in every module
 | |
|  */
 | |
|     for (i = 0, p = lookup; p->symName; i++, p++) {
 | |
| 	while (p->symName && (!strcmp(lookup[i].symName, ".text")
 | |
| 			      || !strcmp(lookup[i].symName, ".data")
 | |
| 			      || !strcmp(lookup[i].symName, ".bss")
 | |
| 	       )) {
 | |
| 	    memmove(&(lookup[i]), &(lookup[i + 1]),
 | |
| 		    (l-- - i) * sizeof(LOOKUP));
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return lookup;
 | |
| }
 | |
| 
 | |
| #define SecOffset(index) cofffile->sections[index].s_scnptr
 | |
| #define SecSize(index) cofffile->sections[index].s_size
 | |
| #define SecAddr(index) cofffile->sections[index].s_paddr
 | |
| #define RelOffset(index) cofffile->sections[index].s_relptr
 | |
| #define RelSize(index) (cofffile->sections[index].s_nreloc*RELSZ)
 | |
| 
 | |
| /*
 | |
|  * COFFCollectSections
 | |
|  *
 | |
|  * Do the work required to load each section into memory.
 | |
|  */
 | |
| static void
 | |
| COFFCollectSections(COFFModulePtr cofffile)
 | |
| {
 | |
|     unsigned short i;
 | |
| 
 | |
| /*
 | |
|  * Find and identify all of the Sections
 | |
|  */
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFFCollectSections(): %d sections\n", cofffile->numsh);
 | |
| #endif
 | |
| 
 | |
|     for (i = 0; i < cofffile->numsh; i++) {
 | |
| #ifdef COFFDEBUG
 | |
| 	COFFDEBUG("%d %s\n", i, cofffile->sections[i].s_name);
 | |
| #endif
 | |
| 	/* .text */
 | |
| 	if (strcmp(cofffile->sections[i].s_name, ".text") == 0) {
 | |
| 	    cofffile->text = _LoaderFileToMem(cofffile->fd,
 | |
| 					      SecOffset(i), SecSize(i),
 | |
| 					      ".text");
 | |
| 	    cofffile->saddr[i] = cofffile->text;
 | |
| 	    cofffile->txtndx = i;
 | |
| 	    cofffile->txtaddr = SecAddr(i);
 | |
| 	    cofffile->txtsize = SecSize(i);
 | |
| 	    cofffile->txtrelsize = RelSize(i);
 | |
| 	    cofffile->reladdr[i] = _LoaderFileToMem(cofffile->fd,
 | |
| 						    RelOffset(i), RelSize(i),
 | |
| 						    ".rel.text");
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG(".text starts at %p (%x bytes)\n", cofffile->text,
 | |
| 		      cofffile->txtsize);
 | |
| #endif
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/* .data */
 | |
| 	if (strcmp(cofffile->sections[i].s_name, ".data") == 0) {
 | |
| 	    cofffile->data = _LoaderFileToMem(cofffile->fd,
 | |
| 					      SecOffset(i), SecSize(i),
 | |
| 					      ".data");
 | |
| 	    cofffile->saddr[i] = cofffile->data;
 | |
| 	    cofffile->datndx = i;
 | |
| 	    cofffile->dataddr = SecAddr(i);
 | |
| 	    cofffile->datsize = SecSize(i);
 | |
| 	    cofffile->datrelsize = RelSize(i);
 | |
| 	    cofffile->reladdr[i] = _LoaderFileToMem(cofffile->fd,
 | |
| 						    RelOffset(i), RelSize(i),
 | |
| 						    ".rel.data");
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG(".data starts at %p (%x bytes)\n", cofffile->data,
 | |
| 		      cofffile->datsize);
 | |
| #endif
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/* .bss */
 | |
| 	if (strcmp(cofffile->sections[i].s_name, ".bss") == 0) {
 | |
| 	    if (SecSize(i))
 | |
| 		cofffile->bss = xf86loadercalloc(1, SecSize(i));
 | |
| 	    else
 | |
| 		cofffile->bss = NULL;
 | |
| 	    cofffile->saddr[i] = cofffile->bss;
 | |
| 	    cofffile->bssndx = i;
 | |
| 	    cofffile->bssaddr = SecAddr(i);
 | |
| 	    cofffile->bsssize = SecSize(i);
 | |
| #ifdef COFFDEBUG
 | |
| 	    COFFDEBUG(".bss starts at %p (%x bytes)\n", cofffile->bss,
 | |
| 		      cofffile->bsssize);
 | |
| #endif
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/* .comment */
 | |
| 	if (strncmp(cofffile->sections[i].s_name,
 | |
| 		    ".comment", strlen(".comment")) == 0) {
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/* .stab */
 | |
| 	if (strcmp(cofffile->sections[i].s_name, ".stab") == 0) {
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/* .stabstr */
 | |
| 	if (strcmp(cofffile->sections[i].s_name, ".stabstr") == 0) {
 | |
| 	    continue;
 | |
| 	}
 | |
| 	/* .stab.* */
 | |
| 	if (strncmp(cofffile->sections[i].s_name,
 | |
| 		    ".stab.", strlen(".stab.")) == 0) {
 | |
| 	    continue;
 | |
| 	}
 | |
| 	ErrorF("COFF: Not loading %s\n", cofffile->sections[i].s_name);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Public API for the COFF implementation of the loader.
 | |
|  */
 | |
| void *
 | |
| COFFLoadModule(loaderPtr modrec, int cofffd, LOOKUP **ppLookup)
 | |
| {
 | |
|     COFFModulePtr cofffile;
 | |
|     FILHDR *header;
 | |
|     int stroffset;		/* offset of string table */
 | |
|     COFFRelocPtr coff_reloc, tail;
 | |
|     void *v;
 | |
| 
 | |
| #ifdef COFFDEBUG
 | |
|     COFFDEBUG("COFFLoadModule(%s,%x,%x)\n", modrec->name, modrec->handle,
 | |
| 	      cofffd);
 | |
| #endif
 | |
| 
 | |
|     if ((cofffile = xf86loadercalloc(1, sizeof(COFFModuleRec))) == NULL) {
 | |
| 	ErrorF("Unable to allocate COFFModuleRec\n");
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     cofffile->handle = modrec->handle;
 | |
|     cofffile->module = modrec->module;
 | |
|     cofffile->fd = cofffd;
 | |
|     v = cofffile->funcs = modrec->funcs;
 | |
| 
 | |
| /*
 | |
|  *  Get the COFF header
 | |
|  */
 | |
|     cofffile->header =
 | |
| 	    (FILHDR *) _LoaderFileToMem(cofffd, 0, sizeof(FILHDR), "header");
 | |
|     header = (FILHDR *) cofffile->header;
 | |
| 
 | |
|     if (header->f_symptr == 0 || header->f_nsyms == 0) {
 | |
| 	ErrorF("No symbols found in module\n");
 | |
| 	_LoaderFreeFileMem(header, sizeof(FILHDR));
 | |
| 	xf86loaderfree(cofffile);
 | |
| 	return NULL;
 | |
|     }
 | |
| /*
 | |
|  * Get the section table
 | |
|  */
 | |
|     cofffile->numsh = header->f_nscns;
 | |
|     cofffile->secsize = (header->f_nscns * SCNHSZ);
 | |
|     cofffile->sections =
 | |
| 	    (SCNHDR *) _LoaderFileToMem(cofffd, FILHSZ + header->f_opthdr,
 | |
| 					cofffile->secsize, "sections");
 | |
|     cofffile->saddr =
 | |
| 	    xf86loadercalloc(cofffile->numsh, sizeof(unsigned char *));
 | |
|     cofffile->reladdr =
 | |
| 	    xf86loadercalloc(cofffile->numsh, sizeof(unsigned char *));
 | |
| 
 | |
| /*
 | |
|  * Load the optional header if we need it ?????
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Load the rest of the desired sections
 | |
|  */
 | |
|     COFFCollectSections(cofffile);
 | |
| 
 | |
| /*
 | |
|  * load the string table (must be done before we process symbols).
 | |
|  */
 | |
|     stroffset = header->f_symptr + (header->f_nsyms * SYMESZ);
 | |
| 
 | |
|     _LoaderFileRead(cofffd, stroffset, &(cofffile->strsize), sizeof(int));
 | |
| 
 | |
|     stroffset += 4;		/* Move past the size */
 | |
|     cofffile->strsize -= sizeof(int);	/* size includes itself, so reduce by 4 */
 | |
|     cofffile->strtab =
 | |
| 	    _LoaderFileToMem(cofffd, stroffset, cofffile->strsize, "strings");
 | |
| 
 | |
| /*
 | |
|  * add symbols
 | |
|  */
 | |
|     *ppLookup = COFF_GetSymbols(cofffile);
 | |
| 
 | |
| /*
 | |
|  * Do relocations
 | |
|  */
 | |
|     coff_reloc = COFFCollectRelocations(cofffile);
 | |
|     if (coff_reloc) {
 | |
| 	for (tail = coff_reloc; tail->next; tail = tail->next) ;
 | |
| 	tail->next = _LoaderGetRelocations(v)->coff_reloc;
 | |
| 	_LoaderGetRelocations(v)->coff_reloc = coff_reloc;
 | |
|     }
 | |
| 
 | |
|     return (void *)cofffile;
 | |
| }
 | |
| 
 | |
| void
 | |
| COFFResolveSymbols(void *mod)
 | |
| {
 | |
|     COFFRelocPtr newlist, p, tmp;
 | |
| 
 | |
|     /* Try to relocate everything.  Build a new list containing entries
 | |
|      * which we failed to relocate.  Destroy the old list in the process.
 | |
|      */
 | |
|     newlist = 0;
 | |
|     for (p = _LoaderGetRelocations(mod)->coff_reloc; p;) {
 | |
| 	tmp = COFF_RelocateEntry(p->file, p->secndx, p->rel);
 | |
| 	if (tmp) {
 | |
| 	    /* Failed to relocate.  Keep it in the list. */
 | |
| 	    tmp->next = newlist;
 | |
| 	    newlist = tmp;
 | |
| 	}
 | |
| 	tmp = p;
 | |
| 	p = p->next;
 | |
| 	xf86loaderfree(tmp);
 | |
|     }
 | |
|     _LoaderGetRelocations(mod)->coff_reloc = newlist;
 | |
| }
 | |
| 
 | |
| int
 | |
| COFFCheckForUnresolved(void *mod)
 | |
| {
 | |
|     char *name;
 | |
|     COFFRelocPtr crel;
 | |
|     int flag, fatalsym = 0;
 | |
| 
 | |
|     if ((crel = _LoaderGetRelocations(mod)->coff_reloc) == NULL)
 | |
| 	return 0;
 | |
| 
 | |
|     while (crel) {
 | |
| 	name = COFFGetSymbolName(crel->file, crel->rel->r_symndx);
 | |
| 	flag = _LoaderHandleUnresolved(name,
 | |
| 				       _LoaderHandleToName(crel->file->
 | |
| 							   handle));
 | |
| 	if (flag)
 | |
| 	    fatalsym = 1;
 | |
| 	xf86loaderfree(name);
 | |
| 	crel = crel->next;
 | |
|     }
 | |
|     return fatalsym;
 | |
| }
 | |
| 
 | |
| void
 | |
| COFFUnloadModule(void *modptr)
 | |
| {
 | |
|     COFFModulePtr cofffile = (COFFModulePtr) modptr;
 | |
|     COFFRelocPtr relptr, reltptr, *brelptr;
 | |
| 
 | |
| /*
 | |
|  * Delete any unresolved relocations
 | |
|  */
 | |
| 
 | |
|     relptr = _LoaderGetRelocations(cofffile->funcs)->coff_reloc;
 | |
|     brelptr = &(_LoaderGetRelocations(cofffile->funcs)->coff_reloc);
 | |
| 
 | |
|     while (relptr) {
 | |
| 	if (relptr->file == cofffile) {
 | |
| 	    *brelptr = relptr->next;	/* take it out of the list */
 | |
| 	    reltptr = relptr;	/* save pointer to this node */
 | |
| 	    relptr = relptr->next;	/* advance the pointer */
 | |
| 	    xf86loaderfree(reltptr);	/* free the node */
 | |
| 	} else {
 | |
| 	    brelptr = &(relptr->next);
 | |
| 	    relptr = relptr->next;	/* advance the pointer */
 | |
| 	}
 | |
|     }
 | |
| 
 | |
| /*
 | |
|  * Delete any symbols in the symbols table.
 | |
|  */
 | |
| 
 | |
|     LoaderHashTraverse((void *)cofffile, COFFhashCleanOut);
 | |
| 
 | |
| /*
 | |
|  * Free the sections that were allocated.
 | |
|  */
 | |
| #define CheckandFree(ptr,size)	if(ptr) _LoaderFreeFileMem((ptr),(size))
 | |
| 
 | |
|     CheckandFree(cofffile->strtab, cofffile->strsize);
 | |
|     CheckandFree(cofffile->symtab, cofffile->symsize);
 | |
|     CheckandFree(cofffile->text, cofffile->txtsize);
 | |
|     CheckandFree(cofffile->reladdr[cofffile->txtndx], cofffile->txtrelsize);
 | |
|     CheckandFree(cofffile->data, cofffile->datsize);
 | |
|     CheckandFree(cofffile->reladdr[cofffile->datndx], cofffile->datrelsize);
 | |
|     CheckandFree(cofffile->bss, cofffile->bsssize);
 | |
|     if (cofffile->common)
 | |
| 	xf86loaderfree(cofffile->common);
 | |
| /*
 | |
|  * Free the section table, and section pointer array
 | |
|  */
 | |
|     _LoaderFreeFileMem(cofffile->sections, cofffile->secsize);
 | |
|     xf86loaderfree(cofffile->saddr);
 | |
|     xf86loaderfree(cofffile->reladdr);
 | |
|     _LoaderFreeFileMem(cofffile->header, sizeof(FILHDR));
 | |
| /*
 | |
|  * Free the COFFModuleRec
 | |
|  */
 | |
|     xf86loaderfree(cofffile);
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| char *
 | |
| COFFAddressToSection(void *modptr, unsigned long address)
 | |
| {
 | |
|     COFFModulePtr cofffile = (COFFModulePtr) modptr;
 | |
|     int i;
 | |
| 
 | |
|     for (i = 1; i < cofffile->numsh; i++) {
 | |
| 	if (address >= (unsigned long)cofffile->saddr[i] &&
 | |
| 	    address <= (unsigned long)cofffile->saddr[i] + SecSize(i)) {
 | |
| 	    return cofffile->sections[i].s_name;
 | |
| 	}
 | |
|     }
 | |
|     return NULL;
 | |
| }
 |