xserver-multidpi/hw/xfree86/loader/coffloader.c
2003-11-14 16:48:57 +00:00

1359 lines
36 KiB
C

/* $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;
}