/* $XFree86: xc/programs/Xserver/hw/xfree86/loader/coffloader.c,v 1.21tsi 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. */ #ifdef HAVE_XORG_CONFIG_H #include #endif #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 #include #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; }