/* $XFree86: xc/programs/Xserver/hw/xfree86/loader/coffloader.c,v 1.15 2001/01/06 21:29:16 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 #include #include #ifdef QNX #include #else #include #endif #include #ifdef DBMALLOC #include #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: #ifdef COFFDEBUG #endif 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: #if defined(i386) ErrorF( "COFF_RelocateEntry() Unsupported relocation type %o\n", rel->r_type ); #endif #if defined(__powerpc__) ErrorF( "COFF_RelocateEntry() Unsupported relocation type %o\n", rel->r_type ); #endif 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; inumsh; i++ ) { if( cofffile->saddr[i] == NULL ) continue; /* Section not loaded!! */ sec=&(cofffile->sections[i]); for(j=0;js_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; isymtab)+(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; inumsh; 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("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; inumsh; i++) { if( address >= (unsigned long)cofffile->saddr[i] && address <= (unsigned long)cofffile->saddr[i]+SecSize(i) ) { return cofffile->sections[i].s_name; } } return NULL; }