xserver-multidpi/hw/xfree86/loader/coffloader.c
2004-04-23 19:54:30 +00:00

1356 lines
36 KiB
C

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