#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
typedef struct {
int handle;
long module;
int fd;
loader_funcs *funcs;
FILHDR *header;
AOUTHDR *optheader;
unsigned short numsh;
SCNHDR *sections;
int secsize;
unsigned char **saddr;
unsigned char **reladdr;
unsigned char *strtab;
int strsize;
unsigned char *text;
int txtndx;
long txtaddr;
int txtsize;
int txtrelsize;
unsigned char *data;
int datndx;
long dataddr;
int datsize;
int datrelsize;
unsigned char *bss;
int bssndx;
long bssaddr;
int bsssize;
SYMENT *symtab;
int symndx;
int symsize;
unsigned char *common;
int comsize;
long toc;
unsigned char *tocaddr;
} COFFModuleRec, *COFFModulePtr;
typedef struct _coff_reloc {
COFFModulePtr file;
RELOC *rel;
int secndx;
struct _coff_reloc *next;
} COFFRelocRec;
typedef struct _coff_COMMON {
SYMENT *sym;
int index;
struct _coff_COMMON *next;
} COFFCommonRec;
static COFFCommonPtr listCOMMON = NULL;
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);
static int
COFFhashCleanOut(void *voidptr, itemPtr item)
{
COFFModulePtr module = (COFFModulePtr) voidptr;
return (module->handle == item->handle);
}
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;
}
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) {
if (common->sym->n_value != 2 && common->sym->n_value != 1)
if (common->sym->n_value % 4 != 0)
common->sym->n_value += 4 - (common->sym->n_value % 4);
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;
}
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++;
}
lookup[l].symName = NULL;
return lookup;
}
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;
itemPtr 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__)
static unsigned char *
COFFGetSymbolGlinkValue(COFFModulePtr cofffile, int index)
{
unsigned char *symval = 0;
itemPtr symbol;
char *name;
name = COFFGetSymbolName(cofffile, index);
#ifdef COFFDEBUG
COFFDEBUG("COFFGetSymbolGlinkValue() for %s=", name);
#endif
symbol = LoaderHashFind(name + 1);
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;
symbol->code.glink[1] =
((unsigned long)symbol->address & 0xffff0000) >> 16;
symbol->code.glink[2] = 0x618c;
symbol->code.glink[3] = ((unsigned long)symbol->address & 0x0000ffff);
symbol->code.glink[4] = 0x9041;
symbol->code.glink[5] = 0x0014;
symbol->code.glink[6] = 0x804c;
symbol->code.glink[7] = 0x0000;
symbol->code.glink[8] = 0x7c49;
symbol->code.glink[9] = 0x03a6;
symbol->code.glink[10] = 0x804c;
symbol->code.glink[11] = 0x0004;
symbol->code.glink[12] = 0x4e80;
symbol->code.glink[13] = 0x0420;
ppc_flush_icache(&symbol->code.glink[0]);
ppc_flush_icache(&symbol->code.glink[12]);
}
xf86loaderfree(name);
return symval;
}
#endif
static COFFRelocPtr
COFF_RelocateEntry(COFFModulePtr cofffile, int secndx, RELOC *rel)
{
SYMENT *symbol;
unsigned long *dest32;
#if defined(__powerpc__)
unsigned short *dest16;
itemPtr symitem;
char *name;
#endif
unsigned char *symval;
#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
switch (secndx + 1) {
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:
break;
#endif
#if defined(__powerpc__)
case R_POS:
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:
{
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:
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
(*dest32) = ((*dest32) & 0xfc000003) | (val << 2);
#ifdef COFFDEBUG
COFFDEBUG("*dest32=%8.8x\n", *dest32);
#endif
if (cofffile->module != symitem->module) {
(*++dest32) = 0x80410014;
}
ppc_flush_icache(--dest32);
}
break;
#endif
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;
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;
}
static LOOKUP *
COFF_GetSymbols(COFFModulePtr cofffile)
{
SYMENT *sym;
AUXENT *aux = NULL;
int i, l, numsyms;
LOOKUP *lookup, *lookup_common, *p;
char *symname;
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;
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) {
#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:
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:
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;
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;
}
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)
static void
COFFCollectSections(COFFModulePtr cofffile)
{
unsigned short i;
#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
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;
}
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;
}
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;
}
if (strncmp(cofffile->sections[i].s_name,
".comment", strlen(".comment")) == 0) {
continue;
}
if (strcmp(cofffile->sections[i].s_name, ".stab") == 0) {
continue;
}
if (strcmp(cofffile->sections[i].s_name, ".stabstr") == 0) {
continue;
}
if (strncmp(cofffile->sections[i].s_name,
".stab.", strlen(".stab.")) == 0) {
continue;
}
ErrorF("COFF: Not loading %s\n", cofffile->sections[i].s_name);
}
}
void *
COFFLoadModule(loaderPtr modrec, int cofffd, LOOKUP **ppLookup)
{
COFFModulePtr cofffile;
FILHDR *header;
int stroffset;
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;
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;
}
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 *));
COFFCollectSections(cofffile);
stroffset = header->f_symptr + (header->f_nsyms * SYMESZ);
_LoaderFileRead(cofffd, stroffset, &(cofffile->strsize), sizeof(int));
stroffset += 4;
cofffile->strsize -= sizeof(int);
cofffile->strtab =
_LoaderFileToMem(cofffd, stroffset, cofffile->strsize, "strings");
*ppLookup = COFF_GetSymbols(cofffile);
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;
newlist = 0;
for (p = _LoaderGetRelocations(mod)->coff_reloc; p;) {
tmp = COFF_RelocateEntry(p->file, p->secndx, p->rel);
if (tmp) {
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;
relptr = _LoaderGetRelocations(cofffile->funcs)->coff_reloc;
brelptr = &(_LoaderGetRelocations(cofffile->funcs)->coff_reloc);
while (relptr) {
if (relptr->file == cofffile) {
*brelptr = relptr->next;
reltptr = relptr;
relptr = relptr->next;
xf86loaderfree(reltptr);
} else {
brelptr = &(relptr->next);
relptr = relptr->next;
}
}
LoaderHashTraverse((void *)cofffile, COFFhashCleanOut);
#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);
_LoaderFreeFileMem(cofffile->sections, cofffile->secsize);
xf86loaderfree(cofffile->saddr);
xf86loaderfree(cofffile->reladdr);
_LoaderFreeFileMem(cofffile->header, sizeof(FILHDR));
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;
}