diff options
Diffstat (limited to 'hw/xfree86/loader/coffloader.c')
-rw-r--r-- | hw/xfree86/loader/coffloader.c | 1358 |
1 files changed, 1358 insertions, 0 deletions
diff --git a/hw/xfree86/loader/coffloader.c b/hw/xfree86/loader/coffloader.c new file mode 100644 index 000000000..7a1ef28ad --- /dev/null +++ b/hw/xfree86/loader/coffloader.c @@ -0,0 +1,1358 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/loader/coffloader.c,v 1.18 2002/09/16 18:06:10 eich 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 LDTEST +#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); +static unsigned char *COFFGetSymbolValue(COFFModulePtr, int); +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(voidptr, item) +void *voidptr; +itemPtr item ; +{ + COFFModulePtr module = (COFFModulePtr) voidptr; + return ( module->handle == item->handle ) ; +} + + +/* + * Manage listResolv + */ +static COFFRelocPtr +COFFDelayRelocation(cofffile,secndx,rel) +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(sym,index) +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(cofffile) +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 %x %s\n", 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(cofffile, index) +COFFModulePtr cofffile; +int index; +{ + char *name; + SYMENT *sym; + + sym=(SYMENT *)(((unsigned char *)cofffile->symtab)+(index*SYMESZ)); + +#ifdef COFFDEBUG + COFFDEBUG("COFFGetSymbolName(%x,%x) %x",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(file, index) +COFFModulePtr file; +int index; +{ + return (SYMENT *)(((unsigned char *)file->symtab)+(index*SYMESZ)); +} + +static unsigned char * +COFFGetSymbolValue(cofffile, index) +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("%x\n", symval ); +#endif + + xf86loaderfree(symname); + return symval; +} + +#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(cofffile, index) +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(cofffile, secndx, rel) +COFFModulePtr cofffile; +int secndx; /* index of the target section */ +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("%x %d %o ", + 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 %x %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=%x\t", dest32 ); + COFFDEBUG( "symval=%x\t", symval ); + COFFDEBUG( "*dest32=%8.8x\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=%x\t", dest32 ); + COFFDEBUG( "symval=%x\t", symval ); + COFFDEBUG( "*dest32=%8.8x\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=%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])); + break; + case N_DATA: +#ifdef COFFDEBUG + COFFDEBUG( "R_DIR32 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); + break; + case N_BSS: +#ifdef COFFDEBUG + COFFDEBUG( "R_DIR32 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)); + break; + default: + ErrorF("R_DIR32 with unexpected section %d\n", + symbol->n_scnum ); + } + + } +#ifdef COFFDEBUG + COFFDEBUG( "*dest32=%8.8x\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=%x\t", dest32 ); + COFFDEBUG( "symval=%x\t", symval ); + COFFDEBUG( "*dest32=%8.8x\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.8x\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(cofffile) +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(cofffile) +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 %x %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%d %x %x %x %x %x %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=%x\n", cofffile->toc ); + COFFDEBUG("TOCaddr=%x\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 %x %s\n", + 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 %x %s\n", + 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 %x %s\n", + 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(cofffile) +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 %x (%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 %x (%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 %x (%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(modrec, cofffd, ppLookup) +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(mod) +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( mod) +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(modptr) +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; +} |