/* $XFree86: xc/programs/Xserver/hw/xfree86/loader/elfloader.c,v 1.56 2003/08/22 19:27:30 eich Exp $ */

/*
 *
 * Copyright 1995-1998 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>
#ifndef __UNIXOS2__
#include <sys/mman.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#ifdef __QNX__
# include <fcntl.h>
#else
# include <sys/fcntl.h>
#endif
#include <sys/stat.h>
#if defined(linux) && defined (__ia64__)
#include <sys/mman.h>
#endif

#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 "elf.h"

#include "sym.h"
#include "loader.h"

#include "compiler.h"

#undef LDTEST

/*
#ifndef LDTEST
# define ELFDEBUG ErrorF
#endif
*/

#if defined(__ia64__)

/*
 * R_IA64_LTOFF22X and R_IA64_LDXMOV are relocation optimizations for
 * IA64. Conforming implementations must recognize them and may either
 * implement the optimization or may fallback to previous
 * non-optimized behavior by treating R_IA64_LTOFF22X as a
 * R_IA64_LTOFF22 and ignoring R_IA64_LDXMOV. The
 * IA64_LDX_OPTIMIZATION conditional controls the fallback behavior,
 * if defined the optimizations are performed.
 *
 * To implement the optimization we want to change is the sequence on
 * the left to that on the right, without regard to any intervening
 * instructions:
 * 
 * 1)  addl    t1=@ltoff(var),gp    ==>    addl    t1=@gprel(var),gp
 * 2)  ld8     t2=[t1]              ==>    mov     t2=t1
 * 3)  ld8     loc0=[t2]            ==>    ld8     loc0=[t2]
 * 
 * The relocations that match the above instructions are:
 * 
 * 1)  R_IA64_LTOFF22               ==>    R_IA64_LTOFF22X
 * 2)  --                           ==>    R_IA64_LDXMOV
 * 3)  --                           ==>    --
 *
 * First lets look at left hand column to understand the original
 * mechanism. The virtual address of a symbol is stored in the GOT,
 * when that symbol is referenced the following sequence occurs,
 * instruction 1 loads the address of the GOT entry containing the
 * virtural address of the symbol into t1. Instruction 2 loads the
 * virtual address of the symbol into t2 by dereferencing t1. Finally
 * the symbol is loaded in instruction 3 by dereferencing its virtual
 * address in t2.
 * 
 * The optimization that LTOFF22X/LDXMOV introduces is based on the
 * observation we are doing an extra load (instruction 2) if we can
 * generate the virtual address for the symbol without doing a lookup in
 * the GOT. This is possible if the virtual address of the symbol can be
 * computed via GP relative addressing. In other words the virtual
 * address of the symbol is a fixed offset from the GP. This fixed offset
 * must be within the limits of the signed 22 bit immediate offset in the
 * ld8 instruction, otherwise the original indirect GOT lookup must be
 * performed (LTOFF22).
 * 
 * If we can use GP relative addressing for the symbol then the
 * instruction that loaded the virtual address of the symbol into t2 must
 * also be patched, hence the introduction of the LDXMOV relocation. The
 * LDXMOV essentially turns the GOT lookup into a no-op by changing the
 * ld8 into a register move that preserves the register location of the
 * symbol's virtual address (e.g. t2).
 * 
 * The important point to recognize when implementing the LTOFF22X/LDXMOV
 * optimization is that relocations are interdependent, the LDXMOV is
 * only applied if the LTOFF22X is applied. It is also worth noting that
 * there is no relationship between LDXMOV relocations and LTOFF22X in
 * the ELF relocation section other than they share the same
 * symbol+addend value.
 */

#define IA64_LDX_OPTIMIZATION 1
#endif


#ifndef UseMMAP
# if defined (__ia64__) || defined (__sparc__)
#  define MergeSectionAlloc
# endif
#endif

#if defined (DoMMAPedMerge)
# include <sys/mman.h>
# define MergeSectionAlloc
# define MMAP_PROT	(PROT_READ | PROT_WRITE | PROT_EXEC)
# if !defined(linux)
#  error    No MAP_ANON?
# endif
# if !defined (__AMD64__)
# define MMAP_FLAGS     (MAP_PRIVATE | MAP_ANON)
# else
# define MMAP_FLAGS     (MAP_PRIVATE | MAP_ANON | MAP_32BIT)
# endif
# if defined (MmapPageAlign)
#  define MMAP_ALIGN(size)    do { \
     int pagesize = getpagesize(); \
     size = ( size + pagesize - 1) / pagesize; \
     size *= pagesize; \
   } while (0);
# else
#  define MMAP_ALIGN(size)
# endif
#endif

#if defined (__alpha__) || \
    defined (__ia64__) || \
    defined (__AMD64__) || \
    (defined (__sparc__) && \
     (defined (__arch64__) || \
      defined (__sparcv9)))
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Sym Elf_Sym;
typedef Elf64_Rel Elf_Rel;
typedef Elf64_Rela Elf_Rela;
typedef Elf64_Addr Elf_Addr;
typedef Elf64_Half Elf_Half;
typedef Elf64_Off Elf_Off;
typedef Elf64_Sword Elf_Sword;
typedef Elf64_Word Elf_Word;
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
#define ELF_R_SYM ELF64_R_SYM
#define ELF_R_TYPE ELF64_R_TYPE

# if defined (__alpha__) || defined (__ia64__)
/*
 * The GOT is allocated dynamically. We need to keep a list of entries that
 * have already been added to the GOT. 
 *
 */
typedef struct _elf_GOT_Entry {
	Elf_Rela   *rel;
        int	offset;
	struct _elf_GOT_Entry *next;
} ELFGotEntryRec, *ELFGotEntryPtr;

typedef struct _elf_GOT {
	unsigned int	size;
	unsigned int	nuses;
	unsigned char	*freeptr;
	struct _elf_GOT	*next;
	unsigned char	section[1];
} ELFGotRec, *ELFGotPtr;

#  ifdef MergeSectionAlloc
static ELFGotPtr ELFSharedGOTs;
#  endif
# endif

# if defined (__ia64__)
/*
 * The PLT is allocated dynamically. We need to keep a list of entries that
 * have already been added to the PLT. 
 */
typedef struct _elf_PLT_Entry {
	Elf_Rela   *rel;
        int	offset;
	int	gotoffset;
	struct _elf_PLT_Entry *next;
} ELFPltEntryRec, *ELFPltEntryPtr;

/*
 * The OPD is allocated dynamically within the GOT. We need to keep a list
 * of entries that have already been added to the OPD.
 */
typedef struct _elf_OPD {
	LOOKUP	*l;
	int	index;
        int	offset;
	struct _elf_OPD *next;
} ELFOpdRec, *ELFOpdPtr;
# endif

#else
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Sym Elf_Sym;
typedef Elf32_Rel Elf_Rel;
typedef Elf32_Rela Elf_Rela;
typedef Elf32_Addr Elf_Addr;
typedef Elf32_Half Elf_Half;
typedef Elf32_Off Elf_Off;
typedef Elf32_Sword Elf_Sword;
typedef Elf32_Word Elf_Word;
#define ELF_ST_BIND ELF32_ST_BIND
#define ELF_ST_TYPE ELF32_ST_TYPE
#define ELF_R_SYM ELF32_R_SYM
#define ELF_R_TYPE ELF32_R_TYPE
#endif

#if defined(__powerpc__) || \
    defined(__mc68000__) || \
    defined(__alpha__) || \
    defined(__sparc__) || \
    defined(__ia64__) || \
    defined(__AMD64__)
typedef Elf_Rela Elf_Rel_t;
#else
typedef Elf_Rel  Elf_Rel_t;
#endif

typedef struct {
    void *saddr;
    char *name;
    int ndx;
    int size;
    int flags;
} LoadSection;

#define RELOC_SECTION 0x1
#define LOADED_SECTION 0x2

/*
 * This structure contains all of the information about a module
 * that has been loaded.
 */

typedef	struct {
	int	handle;
	int	module;
	int	fd;
	loader_funcs	*funcs;
	Elf_Ehdr 	*header;/* file header */
	int	numsh;
	Elf_Shdr	*sections;/* Address of the section header table */
	int	secsize;	/* size of the section table */
	unsigned char	**saddr;/* Start addresss of the section pointer table */
	unsigned char	*shstraddr; /* Start address of the section header string table */
	int	shstrndx;	/* index of the section header string table */
	int	shstrsize;	/* size of the section header string table */
#if defined(__alpha__) || defined(__ia64__)
	unsigned char *got;     /* Start address of the .got section */
	ELFGotEntryPtr got_entries;  /* List of entries in the .got section */
	int     gotndx;         /* index of the .got section */
	int     gotsize;        /* actual size of the .got section */
	ELFGotPtr shared_got;	/* Pointer to ELFGotRec if shared */
#endif /*(__alpha__) || (__ia64__)*/
#if defined(__ia64__)
	ELFOpdPtr opd_entries;  /* List of entries in the .opd section */
	unsigned char *plt;     /* Start address of the .plt section */
	ELFPltEntryPtr plt_entries;  /* List of entries in the .plt section */
	int     pltndx;         /* index of the .plt section */
	int     pltsize;        /* size of the .plt section */
#endif /*__ia64__*/
	Elf_Sym *symtab;	/* Start address of the .symtab section */
	int	symndx;		/* index of the .symtab section */
	unsigned char *common;	/* Start address of the SHN_COMMON space */
	int	comsize;	/* size of the SHN_COMMON space */

	unsigned char *base;	/* Alloced address of section block */
	unsigned long baseptr;	/* Pointer to next free space in base */
	int	basesize;	/* Size of that allocation */
	unsigned char *straddr;	/* Start address of the string table */
	int	strndx;		/* index of the string table */
	int	strsize;	/* size of the string table */
	LoadSection *lsection;
	int	lsectidx;
}	ELFModuleRec, *ELFModulePtr;

/*
 * If a relocation is unable to be satisfied, then put it on a list
 * to try later after more modules have been loaded.
 */
typedef struct _elf_reloc {
	Elf_Rel_t	*rel;
	ELFModulePtr	file;
	Elf_Word	secn;
	struct _elf_reloc	*next;
} ELFRelocRec;

/*
 * symbols with a st_shndx of COMMON need to have space allocated for them.
 *
 * Gather all of these symbols together, and allocate one chunk when we
 * are done.
 */
typedef struct _elf_COMMON {
	Elf_Sym	   *sym;
	struct _elf_COMMON *next;
} ELFCommonRec;

static	ELFCommonPtr listCOMMON=NULL;

/* Prototypes for static functions */
static int ELFhashCleanOut(void *, itemPtr);
static char *ElfGetStringIndex(ELFModulePtr, int, int);
static char *ElfGetString(ELFModulePtr, int);
static char *ElfGetSectionName(ELFModulePtr, int);
static ELFRelocPtr ElfDelayRelocation(ELFModulePtr, Elf_Word, Elf_Rel_t *);
static ELFCommonPtr ElfAddCOMMON(Elf_Sym *);
static int ElfCOMMONSize(void);
static int ElfCreateCOMMON(ELFModulePtr,LOOKUP *);
static char *ElfGetSymbolNameIndex(ELFModulePtr, int, int);
static char *ElfGetSymbolName(ELFModulePtr, int);
static Elf_Addr ElfGetSymbolValue(ELFModulePtr, int);
static ELFRelocPtr Elf_RelocateEntry(ELFModulePtr, Elf_Word, Elf_Rel_t *, int);
static ELFRelocPtr ELFCollectRelocations(ELFModulePtr, int);
static LOOKUP *ELF_GetSymbols(ELFModulePtr, unsigned short **);
static void ELFCollectSections(ELFModulePtr, int, int *, int *);
#if defined(__alpha__) || defined(__ia64__)
static void ElfAddGOT(ELFModulePtr, Elf_Rel_t *);
static int ELFCreateGOT(ELFModulePtr, int);
#endif
#if defined(__ia64__)
static void ElfAddOPD(ELFModulePtr, int, LOOKUP *);
static void ELFCreateOPD(ELFModulePtr);
static void ElfAddPLT(ELFModulePtr, Elf_Rel_t *);
static void ELFCreatePLT(ELFModulePtr);
enum ia64_operand {
    IA64_OPND_IMM22,
    IA64_OPND_TGT25C,
    IA64_OPND_LDXMOV
};
static void IA64InstallReloc(unsigned long *, int, enum ia64_operand, long);
#endif /*__ia64__*/

#ifdef MergeSectionAlloc
static void *
ELFLoaderSectToMem(elffile, align, offset, size, label)
ELFModulePtr	elffile;
int		align;
unsigned long	offset;
int		size;
char		*label;
{
    void *ret;
    elffile->baseptr = (elffile->baseptr + align - 1) & ~(align - 1);
    ret = (void *)elffile->baseptr;
    _LoaderFileRead(elffile->fd, offset, ret, size);
    elffile->baseptr += size;
    return ret;
}

static void *
ELFLoaderSectCalloc(elffile, align, size)
ELFModulePtr	elffile;
int		align;
int		size;
{
    void *ret;
    elffile->baseptr = (elffile->baseptr + align - 1) & ~(align - 1);
    ret = (void *)elffile->baseptr;
    elffile->baseptr += size;
#ifndef DoMMAPedMerge
    memset(ret, 0, size); /* mmap() does this for us */
#endif
    return ret;
}
#else /* MergeSectionAlloc */
# define ELFLoaderSectToMem(elffile,align,offset,size,label)	\
_LoaderFileToMem((elffile)->fd,offset,size,label)
# define ELFLoaderSectCalloc(elffile,align,size) xf86loadercalloc(1,size)
#endif

/*
 * Utility Functions
 */


static int
ELFhashCleanOut(voidptr, item)
void *voidptr;
itemPtr item ;
{
    ELFModulePtr module = (ELFModulePtr) voidptr;
    return (module->handle == item->handle);
}

/*
 * Manage listResolv
 */
static ELFRelocPtr
ElfDelayRelocation(elffile, secn, rel)
ELFModulePtr	elffile;
Elf_Word	secn;
Elf_Rel_t	*rel;
{
    ELFRelocPtr	reloc;

    if ((reloc = xf86loadermalloc(sizeof(ELFRelocRec))) == NULL) {
	ErrorF( "ElfDelayRelocation() Unable to allocate memory!!!!\n" );
	return 0;
    }
    reloc->file=elffile;
    reloc->secn=secn;
    reloc->rel=rel;
    reloc->next=0;
#ifdef ELFDEBUG
    ELFDEBUG("ElfDelayRelocation %lx: file %lx, sec %d,"
	     " r_offset 0x%lx, r_info 0x%x",
	     reloc, elffile, secn, rel->r_offset, rel->r_info);
# if defined(__powerpc__) || \
    defined(__mc68000__) || \
    defined(__alpha__) || \
    defined(__sparc__) || \
    defined(__ia64__) || \
    defined(__AMD64__)
    ELFDEBUG(", r_addend 0x%lx", rel->r_addend);
# endif
    ELFDEBUG("\n");
#endif
    return reloc;
}

/*
 * Manage listCOMMON
 */
static ELFCommonPtr
ElfAddCOMMON(sym)
Elf_Sym	*sym;
{
    ELFCommonPtr common;

    if ((common = xf86loadermalloc(sizeof(ELFCommonRec))) == NULL) {
	ErrorF( "ElfAddCOMMON() Unable to allocate memory!!!!\n" );
	return 0;
    }
    common->sym=sym;
    common->next=0;
    return common;
}

static int
ElfCOMMONSize(void)
{
    int	size=0;
    ELFCommonPtr common;

    for (common = listCOMMON; common; common = common->next) {
	size+=common->sym->st_size;
#if defined(__alpha__) || \
    defined(__ia64__) || \
    defined(__AMD64__) || \
    (defined(__sparc__) && \
     (defined(__arch64__) || \
      defined(__sparcv9)))
	size = (size+7)&~0x7;
#endif
    }
    return size;
}

static int
ElfCreateCOMMON(elffile,pLookup)
ELFModulePtr	elffile;
LOOKUP		*pLookup;
{
    int	numsyms=0,size=0,l=0;
    int	offset=0,firstcommon=0;
    ELFCommonPtr common;

    if (listCOMMON == NULL)
	return TRUE;

    for (common = listCOMMON; common; common = common->next) {
	size+=common->sym->st_size;
#if defined(__alpha__) || \
    defined(__ia64__) || \
    defined(__AMD64__) || \
    (defined(__sparc__) && \
     (defined(__arch64__) || \
      defined(__sparcv9)))
	size = (size+7)&~0x7;
#endif
	numsyms++;
    }

#ifdef ELFDEBUG
    ELFDEBUG("ElfCreateCOMMON() %d entries (%d bytes) of COMMON data\n",
	     numsyms, size );
#endif

    elffile->comsize=size;
    if((elffile->common = ELFLoaderSectCalloc(elffile,8,size)) == NULL) {
	ErrorF( "ElfCreateCOMMON() Unable to allocate memory!!!!\n" );
	return FALSE;
    }

    if (DebuggerPresent)
    {
	ldrCommons = xf86loadermalloc(numsyms*sizeof(LDRCommon));
	nCommons = numsyms;
    }

    for (l = 0; pLookup[l].symName; l++)
	;
    firstcommon=l;
    
    /* 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;
	/* this is xstrdup because is should be more efficient. it is freed
	 * with xf86loaderfree
	 */
	pLookup[l].symName =
	    xf86loaderstrdup(ElfGetString(elffile,common->sym->st_name));
	pLookup[l].offset = (funcptr)(elffile->common + offset);
#ifdef ELFDEBUG
	ELFDEBUG("Adding common %lx %s\n",
		 pLookup[l].offset, pLookup[l].symName);
#endif
	
	/* Record the symbol address for gdb */
	if (DebuggerPresent && ldrCommons)
	{
	     ldrCommons[l-firstcommon].addr = (void *)pLookup[l].offset;
	     ldrCommons[l-firstcommon].name = pLookup[l].symName;
	     ldrCommons[l-firstcommon].namelen = strlen(pLookup[l].symName);
	}
	listCOMMON=common->next;
	offset+=common->sym->st_size;
#if defined(__alpha__) || \
    defined(__ia64__) || \
    defined(__AMD64__) || \
    (defined(__sparc__) && \
     (defined(__arch64__) || \
      defined(__sparcv9)))
	offset = (offset+7)&~0x7;  
#endif
	xf86loaderfree(common);
	l++;
    }
    /* listCOMMON == 0 */
    pLookup[l].symName=NULL; /* Terminate the list. */
    return TRUE;
}


/*
 * String Table
 */
static char *
ElfGetStringIndex(file, offset, index)
ELFModulePtr	file;
int offset;
int index;
{
    if( !offset || !index )
        return "";

    return (char *)(file->saddr[index]+offset);
}

static char *
ElfGetString(file, offset)
ELFModulePtr	file;
int offset;
{
    return ElfGetStringIndex( file, offset, file->strndx );
}

static char *
ElfGetSectionName(file, offset)
ELFModulePtr	file;
int offset;
{
    return (char *)(file->shstraddr+offset);
}



/*
 * Symbol Table
 */

/*
 * Get symbol name
 */
static char *
ElfGetSymbolNameIndex(elffile, index, secndx)
ELFModulePtr	elffile;
int index;
int secndx;
{
    Elf_Sym	*syms;

#ifdef ELFDEBUG
    ELFDEBUG("ElfGetSymbolNameIndex(%x,%x) ",index, secndx );
#endif

    syms=(Elf_Sym *)elffile->saddr[secndx];

#ifdef ELFDEBUG
    ELFDEBUG("%s ",ElfGetString(elffile, syms[index].st_name));
    ELFDEBUG("%x %x ",ELF_ST_BIND(syms[index].st_info),
	     ELF_ST_TYPE(syms[index].st_info));
    ELFDEBUG("%lx\n",syms[index].st_value);
#endif

    return ElfGetString(elffile,syms[index].st_name );
}

static char *
ElfGetSymbolName(elffile, index)
ELFModulePtr	elffile;
int index;
{
    return ElfGetSymbolNameIndex(elffile, index, elffile->symndx);
}

static Elf_Addr
ElfGetSymbolValue(elffile, index)
ELFModulePtr	elffile;
int index;
{
    Elf_Sym	*syms;
    Elf_Addr symval=0;	/* value of the indicated symbol */
    char *symname = NULL;		/* name of symbol in relocation */
    itemPtr symbol = NULL;		/* name/value of symbol */

    syms=(Elf_Sym *)elffile->saddr[elffile->symndx];

    switch( ELF_ST_TYPE(syms[index].st_info) )
	{
	case STT_NOTYPE:
	case STT_OBJECT:
	case STT_FUNC:
	    switch( ELF_ST_BIND(syms[index].st_info) )
		{
		case STB_LOCAL:
		    symval=(Elf_Addr)(
					elffile->saddr[syms[index].st_shndx]+
					syms[index].st_value);
#ifdef __ia64__
		    if( ELF_ST_TYPE(syms[index].st_info) == STT_FUNC ) {
			ELFOpdPtr opdent;
			for (opdent = elffile->opd_entries; opdent; opdent = opdent->next)
			    if (opdent->index == index)
				break;
			if(opdent) {
			    ((unsigned long *)(elffile->got+opdent->offset))[0] = symval;
			    ((unsigned long *)(elffile->got+opdent->offset))[1] = (long)elffile->got;
			    symval = (Elf_Addr)(elffile->got+opdent->offset);
			} 
		    }
#endif
		    break;
		case STB_GLOBAL:
		case STB_WEAK: /* STB_WEAK seems like a hack to cover for
					some other problem */
		    symname=
			ElfGetString(elffile,syms[index].st_name);
		    symbol = LoaderHashFind(symname);
		    if( symbol == 0 ) {
			return 0;
			}
		    symval=(Elf_Addr)symbol->address;
		    break;
		default:
		    symval=0;
		    ErrorF(
			   "ElfGetSymbolValue(), unhandled symbol scope %x\n",
			   ELF_ST_BIND(syms[index].st_info) );
		    break;
		}
#ifdef ELFDEBUG
	    ELFDEBUG( "%lx\t", symbol );
	    ELFDEBUG( "%lx\t", symval );
	    ELFDEBUG( "%s\n", symname ? symname : "NULL");
#endif
	    break;
	case STT_SECTION:
	    symval=(Elf_Addr)elffile->saddr[syms[index].st_shndx];
#ifdef ELFDEBUG
	    ELFDEBUG( "ST_SECTION %lx\n", symval );
#endif
	    break;
	case STT_FILE:
	case STT_LOPROC:
	case STT_HIPROC:
	default:
	    symval=0;
	    ErrorF( "ElfGetSymbolValue(), unhandled symbol type %x\n",
		    ELF_ST_TYPE(syms[index].st_info) );
	    break;
	}
    return symval;
}

#if defined(__powerpc__)
/*
 * This function returns the address of a pseudo PLT routine which can
 * be used to compute a function offset. This is needed because loaded
 * modules have an offset from the .text section of greater than 24 bits.
 * The code generated makes the assumption that all function entry points
 * will be within a 24 bit offset (non-PIC code).
 */
static Elf_Addr
ElfGetPltAddr(elffile, index)
ELFModulePtr	elffile;
int index;
{
    Elf_Sym	*syms;
    Elf_Addr symval=0;	/* value of the indicated symbol */
    char *symname = NULL;	/* name of symbol in relocation */
    itemPtr symbol;		/* name/value of symbol */

    syms=(Elf_Sym *)elffile->saddr[elffile->symndx];

    switch( ELF_ST_TYPE(syms[index].st_info) )
	{
	case STT_NOTYPE:
	case STT_OBJECT:
	case STT_FUNC:
	    switch( ELF_ST_BIND(syms[index].st_info) )
		{
		case STB_GLOBAL:
		    symname=
			ElfGetString(elffile,syms[index].st_name);
		    symbol=LoaderHashFind(symname);
		    if( symbol == 0 )
			return 0;
/*
 * Here we are building up a pseudo Plt function that can make a call to
 * a function that has an offset greater than 24 bits. The following code
 * is being used to implement this.

     1  00000000                                .extern realfunc
     2  00000000                                .global pltfunc
     3  00000000                        pltfunc:
     4  00000000  3d 80 00 00                   lis     r12,hi16(realfunc)
     5  00000004  61 8c 00 00                   ori     r12,r12,lo16(realfunc)
     6  00000008  7d 89 03 a6                   mtctr   r12
     7  0000000c  4e 80 04 20                   bctr

 */

		    symbol->code.plt[0]=0x3d80; /* lis     r12 */
		    symbol->code.plt[1]=(((Elf_Addr)symbol->address)&0xffff0000)>>16;
		    symbol->code.plt[2]=0x618c; /* ori     r12,r12 */
		    symbol->code.plt[3]=(((Elf_Addr)symbol->address)&0xffff);
		    symbol->code.plt[4]=0x7d89; /* mtcr    r12 */
		    symbol->code.plt[5]=0x03a6;
		    symbol->code.plt[6]=0x4e80; /* bctr */
		    symbol->code.plt[7]=0x0420;
		    symbol->address=(char *)&symbol->code.plt[0];
		    symval=(Elf_Addr)symbol->address;
		    ppc_flush_icache(&symbol->code.plt[0]);
		    ppc_flush_icache(&symbol->code.plt[6]);
		    break;
		default:
		    symval=0;
		    ErrorF(
			   "ElfGetPltAddr(), unhandled symbol scope %x\n",
			   ELF_ST_BIND(syms[index].st_info) );
		    break;
		}
# ifdef ELFDEBUG
	    ELFDEBUG( "ElfGetPlt: symbol=%lx\t", symbol );
	    ELFDEBUG( "newval=%lx\t", symval );
	    ELFDEBUG( "name=\"%s\"\n", symname ? symname : "NULL");
# endif
	    break;
	case STT_SECTION:
	case STT_FILE:
	case STT_LOPROC:
	case STT_HIPROC:
	default:
	    symval=0;
	    ErrorF( "ElfGetPltAddr(), Unexpected symbol type %x",
		    ELF_ST_TYPE(syms[index].st_info) );
	    ErrorF( "for a Plt request\n" );
	    break;
	}
    return symval;
}
#endif /* __powerpc__ */

#if defined(__alpha__) || defined(__ia64__)
/*
 * Manage GOT Entries
 */
static void
ElfAddGOT(elffile,rel)
ELFModulePtr	elffile;
Elf_Rel_t	*rel;
{
    ELFGotEntryPtr gotent;

# ifdef ELFDEBUG
    {
    Elf_Sym *sym;

    sym=(Elf_Sym *)&(elffile->symtab[ELF_R_SYM(rel->r_info)]);
    if( sym->st_name) {
	ELFDEBUG("ElfAddGOT: Adding GOT entry for %s\n", 
	    ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	}
    else
	ELFDEBUG("ElfAddGOT: Adding GOT entry for %s\n", 
	   ElfGetSectionName(elffile,elffile->sections[sym->st_shndx].sh_name));
    }
# endif

    for (gotent=elffile->got_entries;gotent;gotent=gotent->next) {
	if ( ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) &&
	     gotent->rel->r_addend == rel->r_addend )
		break;
    }

    if( gotent ) {
# ifdef ELFDEBUG
	ELFDEBUG("Entry already present in GOT\n");
# endif
	return;
    }

    if ((gotent = xf86loadermalloc(sizeof(ELFGotEntryRec))) == NULL) {
	ErrorF( "ElfAddGOT() Unable to allocate memory!!!!\n" );
	return;
    }

# ifdef ELFDEBUG
    ELFDEBUG("Entry added with offset %x\n",elffile->gotsize);
# endif
    gotent->rel=rel;
    gotent->offset=elffile->gotsize;
    gotent->next=elffile->got_entries;
    elffile->got_entries=gotent;
    elffile->gotsize+=8;
    return;
}

static int
ELFCreateGOT(elffile, maxalign)
ELFModulePtr	elffile;
int		maxalign;
{
# ifdef MergeSectionAlloc
    ELFGotPtr gots;
# endif
    int gotsize;

    /*
     * XXX:  Is it REALLY needed to ensure GOT's are non-null?
     */
# ifdef ELFDEBUG
    ELFDEBUG( "ELFCreateGOT: %x entries in the GOT\n", elffile->gotsize/8 );

    /*
     * Hmmm. Someone is getting here without any got entries, but they
     * may still have R_ALPHA_GPDISP relocations against the got.
     */
    if( elffile->gotsize == 0 ) 
	ELFDEBUG( "Module %s doesn't have any GOT entries!\n",
		_LoaderModuleToName(elffile->module) );
# endif
    if( elffile->gotsize == 0 ) elffile->gotsize=8;
    elffile->sections[elffile->gotndx].sh_size=elffile->gotsize;
    gotsize = elffile->gotsize;

# ifdef MergeSectionAlloc
#  ifdef __alpha__
#   define GOTDistance 0x100000
#  endif
#  ifdef __ia64__
#   define GOTDistance 0x200000
#  endif
    for (gots = ELFSharedGOTs; gots; gots = gots->next) {
	if (gots->freeptr + elffile->gotsize > gots->section + gots->size)
	    continue;
	if (gots->section > elffile->base) {
	    if (gots->section + gots->size - elffile->base >= GOTDistance)
		continue;
	} else {
	    if (elffile->base + elffile->basesize - gots->section >= GOTDistance)
		continue;
	}
	elffile->got = gots->freeptr;
	elffile->shared_got = gots;
	gots->freeptr = gots->freeptr + elffile->gotsize;
	gots->nuses++;
#  ifdef ELFDEBUG
	ELFDEBUG( "ELFCreateGOT: GOT address %lx in shared GOT, nuses %d\n",
		  elffile->got, gots->nuses );
#  endif
	return TRUE;
    }

    gotsize += 16383 + sizeof(ELFGotRec);
# endif /*MergeSectionAlloc*/

    if ((elffile->got = xf86loadermalloc(gotsize)) == NULL) {
	ErrorF( "ELFCreateGOT() Unable to allocate memory!!!!\n" );
	return FALSE;
    }

# ifdef MergeSectionAlloc
    if (elffile->got > elffile->base) {
	if (elffile->got + elffile->gotsize - elffile->base >= GOTDistance)
	    gotsize = 0;
    } else {
	if (elffile->base + elffile->basesize - elffile->got >= GOTDistance)
	    gotsize = 0;
    }

    if (!gotsize) {
	xf86loaderfree(elffile->got);
#  if !defined(DoMMAPedMerge)
	elffile->basesize += 8 + elffile->gotsize;
	elffile->base = xf86loaderrealloc(elffile->base, elffile->basesize);
	if (elffile->base == NULL) {
	    ErrorF( "ELFCreateGOT() Unable to reallocate memory!!!!\n" );
	    return FALSE;
	}
#   if defined(linux) && defined(__ia64__) || defined(__OpenBSD__)
	{
	    unsigned long page_size = getpagesize();
	    unsigned long round;

	    round = (unsigned long)elffile->base & (page_size-1);
	    mprotect(elffile->base - round, (elffile->basesize+round+page_size-1) & ~(page_size-1),
		     PROT_READ|PROT_WRITE|PROT_EXEC);
	}
#   endif
#  else
	{
	    int oldbasesize = elffile->basesize;
	    elffile->basesize += 8 + elffile->gotsize;
	    MMAP_ALIGN(elffile->basesize);
	    elffile->base = mremap(elffile->base,oldbasesize,
				   elffile->basesize,MREMAP_MAYMOVE);
	    if (elffile->base == NULL) {
		ErrorF( "ELFCreateGOT() Unable to remap memory!!!!\n" );
		return FALSE;
	    }
	}
#  endif

	elffile->baseptr = ((long)elffile->base + (maxalign - 1)) & ~(maxalign - 1);
	elffile->got = (unsigned char *)((long)(elffile->base + elffile->basesize - elffile->gotsize) & ~7);
    } else {
	gots = (ELFGotPtr)elffile->got;
	elffile->got = gots->section;
	gots->size = gotsize - sizeof(ELFGotRec) + 1;
	gots->nuses = 1;
	gots->freeptr = gots->section + elffile->gotsize;
	gots->next = ELFSharedGOTs;
	ELFSharedGOTs = gots;
	elffile->shared_got = gots;
#  ifdef ELFDEBUG
        ELFDEBUG( "ELFCreateGOT: Created a shareable GOT with size %d\n", gots->size);
#  endif
    }
# endif/*MergeSectionAlloc*/

# ifdef ELFDEBUG
    ELFDEBUG( "ELFCreateGOT: GOT address %lx\n", elffile->got );
# endif

    return TRUE;
}
#endif /* defined(__alpha__) || defined(__ia64__)*/

#if defined(__ia64__)
/*
 * Manage OPD Entries
 */
static void
ElfAddOPD(elffile,index,l)
ELFModulePtr	elffile;
int		index;
LOOKUP		*l;
{
    ELFOpdPtr opdent;

    if (index != -1) {
	for (opdent = elffile->opd_entries; opdent; opdent = opdent->next)
	    if (opdent->index == index)
		return;
    }

    if ((opdent = xf86loadermalloc(sizeof(ELFOpdRec))) == NULL) {
	ErrorF( "ElfAddOPD() Unable to allocate memory!!!!\n" );
	return;
    }

# ifdef ELFDEBUG
    ELFDEBUG("OPD Entry %d added with offset %x\n",index,elffile->gotsize);
# endif
    opdent->l=l;
    opdent->index=index;
    opdent->offset=elffile->gotsize;
    opdent->next=elffile->opd_entries;
    elffile->opd_entries=opdent;
    elffile->gotsize+=16;
    return ;
}

static void
ELFCreateOPD(elffile)
ELFModulePtr	elffile;
{
    ELFOpdPtr opdent;

    if (elffile->got == NULL)
	ErrorF( "ELFCreateOPD() Unallocated GOT!!!!\n" );

    for (opdent = elffile->opd_entries; opdent; opdent = opdent->next) {
	if (opdent->index != -1)
	    continue;
	((unsigned long *)(elffile->got+opdent->offset))[0] = (long)opdent->l->offset;
	((unsigned long *)(elffile->got+opdent->offset))[1] = (long)elffile->got;
	opdent->l->offset = (funcptr)(elffile->got+opdent->offset);
    }
}

/*
 * Manage PLT Entries
 */
static void
ElfAddPLT(elffile,rel)
ELFModulePtr	elffile;
Elf_Rel_t	*rel;
{
    ELFPltEntryPtr pltent;

# ifdef ELFDEBUG
    {
    Elf_Sym *sym;

    sym=(Elf_Sym *)&(elffile->symtab[ELF_R_SYM(rel->r_info)]);
    if( sym->st_name) {
	ELFDEBUG("ElfAddPLT: Adding PLT entry for %s\n", 
	    ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	}
    else
	ErrorF("ElfAddPLT: Add PLT entry for section??\n");
    }
# endif

    if (rel->r_addend)
	ErrorF("ElfAddPLT: Add PLT entry with non-zero addend??\n");

    for (pltent=elffile->plt_entries;pltent;pltent=pltent->next) {
	if ( ELF_R_SYM(pltent->rel->r_info) == ELF_R_SYM(rel->r_info) )
		break;
    }

    if( pltent ) {
# ifdef ELFDEBUG
	ELFDEBUG("Entry already present in PLT\n");
# endif
	return;
    }

    if ((pltent = xf86loadermalloc(sizeof(ELFPltEntryRec))) == NULL) {
	ErrorF( "ElfAddPLT() Unable to allocate memory!!!!\n" );
	return;
    }

# ifdef ELFDEBUG
    ELFDEBUG("Entry added with offset %x\n",elffile->pltsize);
# endif
    pltent->rel=rel;
    pltent->offset=elffile->pltsize;
    pltent->gotoffset=elffile->gotsize;
    pltent->next=elffile->plt_entries;
    elffile->plt_entries=pltent;
    elffile->pltsize+=32;
    elffile->gotsize+=16;
    return ;
}

static void
ELFCreatePLT(elffile)
ELFModulePtr	elffile;
{
# ifdef ELFDEBUG
    ELFDEBUG( "ELFCreatePLT: %x entries in the PLT\n", elffile->pltsize/8 );
# endif

    if( elffile->pltsize == 0 ) return;

    if ((elffile->plt = ELFLoaderSectCalloc(elffile,32,elffile->pltsize)) == NULL) {
	ErrorF( "ELFCreatePLT() Unable to allocate memory!!!!\n" );
	return;
    }
    elffile->sections[elffile->pltndx].sh_size=elffile->pltsize;
# ifdef ELFDEBUG
    ELFDEBUG( "ELFCreatePLT: PLT address %lx\n", elffile->plt );
# endif

    return;
}

static void
IA64InstallReloc(data128, slot, opnd, value)
unsigned long		*data128;
int			slot;
enum ia64_operand	opnd;
long			value;
{
    unsigned long data = 0;

# ifdef ELFDEBUG
    ELFDEBUG( "\nIA64InstallReloc %p %d %d %016lx\n", data128, slot, opnd, value);
    ELFDEBUG( "Before [%016lx%016lx]\n", data128[1], data128[0]);
# endif
    switch (slot) {
    case 0: data = *data128; break;
    case 1: memcpy(&data, (char *)data128 + 5, 8); break;
    case 2: memcpy(&data, (char *)data128 + 10, 6); break;
    default: FatalError("Unexpected slot in IA64InstallReloc()\n");
    }
    switch (opnd) {
    case IA64_OPND_IMM22:
	data &= ~(0x3fff9fc0000UL << slot);
	data |= (value & 0x7f) << (18 + slot);		/* [13:19] + 5 + slot */
	data |= (value & 0xff80) << (25 + slot);	/* [27:35] + 5 + slot */
	data |= (value & 0x1f0000) << (11 + slot);	/* [22:26] + 5 + slot */
	data |= (value & 0x200000) << (20 + slot);	/* [36:36] + 5 + slot */
	if (value << 42 >> 42 != value)
	    ErrorF("Relocation %016lx truncated to fit into IMM22\n", value);
	break;
    case IA64_OPND_TGT25C:
	data &= ~(0x23ffffc0000UL << slot);
	data |= (value & 0xfffff0) << (14 + slot);	/* [13:32] + 5 + slot */
	data |= (value & 0x1000000) << (17 + slot);	/* [36:36] + 5 + slot */
	if (value << 39 >> 39 != value || (value & 0xf))
	    ErrorF("Relocation %016lx truncated to fit into TGT25C\n", value);
	break;
#ifdef IA64_LDX_OPTIMIZATION
    case IA64_OPND_LDXMOV:
        /*
         * Convert "ld8 t2=[t1]" to "mov t2=t1" which is really "add t2=0,t1"
         * Mask all but the r3,r1,qp fields, 
	 * then OR in the ALU opcode = 8 into the opcode field [40:37]
	 * 
	 * Mask for the r3,r1,qp bit fields [26:20][12:6][5:0] = 0x7f01fff,
	 * This mask negated only within the 41 bit wide instruction and
	 * shifted left by 5 for the bundle template is 0x3FFF01FC0000
	 *
	 * opcode field [40:37] with a value of 8 is 0x10000000000
	 * shifted left by 5 for the bundle template is 0x200000000000
	 *
	 */
        data &= ~(0x3FFF01FC0000 << slot);
        data |=  (0x200000000000 << slot);
        break;
#endif
    default:
	FatalError("Unhandled operand in IA64InstallReloc()\n");
    }
    switch (slot) {
    case 0: *data128 = data; break;
    case 1: memcpy((char *)data128 + 5, &data, 8); break;
    case 2: memcpy((char *)data128 + 10, &data, 6); break;
    default: FatalError("Unexpected slot in IA64InstallReloc()\n");
    }
    ia64_flush_cache(data128);
# ifdef ELFDEBUG
    ELFDEBUG( "After  [%016lx%016lx]\n", data128[1], data128[0]);
# endif
}

#endif /*__ia64__*/

/*
 * Fix all of the relocations for the given section.
 * If the argument 'force' is non-zero, then the relocation will be
 * made even if the symbol can't be found (by substituting
 * LoaderDefaultFunc) otherwise, the relocation will be deferred.
 */

static ELFRelocPtr
Elf_RelocateEntry(elffile, secn, rel, force)
ELFModulePtr	elffile;
Elf_Word	secn;
Elf_Rel_t	*rel;
int		force;
{
    unsigned char *secp = elffile->saddr[secn];
#if !defined(__ia64__)
    unsigned int *dest32;	/* address of the 32 bit place being modified */
#endif
#if defined(__powerpc__) || defined(__sparc__)
    unsigned short *dest16;	/* address of the 16 bit place being modified */
#endif
#if defined(__sparc__)
    unsigned char *dest8;	/* address of the 8 bit place being modified */
#endif
#if defined(__alpha__) 
    unsigned int *dest32h;	/* address of the high 32 bit place being modified */
    unsigned long *dest64;
    unsigned short *dest16;
#endif
#if  defined(__AMD64__)
    unsigned long *dest64;
    int *dest32s;
#endif
#if defined(__ia64__)
    unsigned long *dest64;
    unsigned long *dest128;
#endif
    Elf_Addr symval = 0;	/* value of the indicated symbol */

#ifdef ELFDEBUG
    ELFDEBUG( "%lx %d %d\n", rel->r_offset,
	      ELF_R_SYM(rel->r_info), ELF_R_TYPE(rel->r_info) );
# if defined(__powerpc__) || \
    defined(__mc68000__) || \
    defined(__alpha__) || \
    defined(__sparc__) || \
    defined(__ia64__) || \
    defined(__AMD64__)
    ELFDEBUG( "%lx", rel->r_addend );
# endif
    ELFDEBUG("\n");
#endif /*ELFDEBUG*/
#if defined(__alpha__)
    if (ELF_R_SYM(rel->r_info) && ELF_R_TYPE(rel->r_info) != R_ALPHA_GPDISP) 
#else
    if (ELF_R_SYM(rel->r_info))
#endif
    {
	symval = ElfGetSymbolValue(elffile, ELF_R_SYM(rel->r_info));
	if (symval == 0) {
	    if (force) {
		symval = (Elf_Addr) &LoaderDefaultFunc;
	    } else {
#ifdef ELFDEBUG
		ELFDEBUG("***Unable to resolve symbol %s\n",
			 ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info)));
#endif
		return ElfDelayRelocation(elffile, secn, rel);
	    }
	}
    }

    switch( ELF_R_TYPE(rel->r_info) )
	{
#if defined(i386)
	case R_386_32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_386_32\t");
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8lx\t", *dest32 );
# endif
	    *dest32=symval+(*dest32); /* S + A */
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8lx\n", *dest32 );
# endif
	    break;
	case R_386_PC32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_386_PC32 %s\t",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8lx\t", *dest32 );
# endif

	    *dest32=symval+(*dest32)-(Elf_Addr)dest32; /* S + A - P */

# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8lx\n", *dest32 );
# endif

	    break;
#endif /* i386 */
#if defined(__AMD64__)
	case R_X86_64_32:
	    dest32=(unsigned int *)(secp+rel->r_offset );
# ifdef ELFDEBUG
	    ELFDEBUG( "R_X86_32\t");
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8lx\t", *dest32 );
	    ELFDEBUG( "r_addend=%lx\t", rel->r_addend);
# endif
	    *dest32=symval + rel->r_addend + (*dest32); /* S + A */
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8lx\n", *dest32 );
# endif
	    break;
	case R_X86_64_32S:
	    dest32s=(int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_X86_64_32\t");
	    ELFDEBUG( "dest32s=%x\t", dest32s );
	    ELFDEBUG( "*dest32s=%8.8lx\t", *dest32s );
	    ELFDEBUG( "r_addend=%lx\t", rel->r_addend);
# endif
	    *dest32s=symval + rel->r_addend + (*dest32s); /* S + A */
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32s=%8.8lx\n", *dest32s );
# endif
	    break;
	case R_X86_64_PC32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_X86_64_PC32 %s\t",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8lx\t", *dest32 );
	    ELFDEBUG( "r_addend=%lx\t", rel->r_addend);
# endif
	    *dest32 = symval + rel->r_addend + (*dest32)-(Elf_Addr)dest32; /* S + A - P */

# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8lx\n", *dest32 );
# endif
	    break;
	case R_X86_64_64:
	    dest64=(unsigned long *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_AMD64_64\t");
	    ELFDEBUG( "dest64=%x\t", dest64 );
	    ELFDEBUG( "*dest64=%8.8lx\t", *dest64 );
	    ELFDEBUG( "r_addend=%lx\t", rel->r_addend);
# endif
	    *dest64=symval + rel->r_addend + (*dest64); /* S + A */
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest64=%8.8lx\n", *dest64 );
# endif
	    break;
#endif /* __AMD64__ */
#if defined(__alpha__)
	case R_ALPHA_NONE:
	case R_ALPHA_LITUSE:
	  break;
	  
	case R_ALPHA_REFQUAD:
	    dest64=(unsigned long *)(secp+rel->r_offset);
	    symval=ElfGetSymbolValue(elffile,
				     ELF_R_SYM(rel->r_info));
# ifdef ELFDEBUG
	    ELFDEBUG( "R_ALPHA_REFQUAD\t");
	    ELFDEBUG( "dest64=%lx\t", dest64 );
	    ELFDEBUG( "*dest64=%8.8lx\t", *dest64 );
# endif
	    *dest64=symval+rel->r_addend+(*dest64); /* S + A + P */
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest64=%8.8lx\n", *dest64 );
# endif
	  break;
	  
	case R_ALPHA_GPREL32:
	    {
	    dest64=(unsigned long *)(secp+rel->r_offset);
	    dest32=(unsigned int *)dest64;

# ifdef ELFDEBUG
	    ELFDEBUG( "R_ALPHA_GPREL32 %s\t", 
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest32=%lx\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
# endif
	    symval += rel->r_addend;
	    symval = ((unsigned char *)symval)-((unsigned char *)elffile->got);
# ifdef ELFDEBUG
	    ELFDEBUG( "symval=%lx\t", symval );
# endif
	    if( (symval&0xffffffff00000000) != 0x0000000000000000 &&
	        (symval&0xffffffff00000000) != 0xffffffff00000000 ) {
		FatalError("R_ALPHA_GPREL32 symval-got is too large for %s\n",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)));
	    }

	    *dest32=symval;
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%x\n", *dest32 );
# endif
	    break;
	    }

	case R_ALPHA_GPRELLOW:
	    {
	    dest64=(unsigned long *)(secp+rel->r_offset);
	    dest16=(unsigned short *)dest64;

	    symval += rel->r_addend;
	    symval = ((unsigned char *)symval)-((unsigned char *)elffile->got);

	    *dest16=symval;
	    break;
	    }

	case R_ALPHA_GPRELHIGH:
	    {
	    dest64=(unsigned long *)(secp+rel->r_offset);
	    dest16=(unsigned short *)dest64;

	    symval += rel->r_addend;
	    symval = ((unsigned char *)symval)-((unsigned char *)elffile->got);
	    symval = ((long)symval >> 16) + ((symval >> 15) & 1);
	    if( (long)symval > 0x7fff || (long)symval < -(long)0x8000 ) {
		FatalError("R_ALPHA_GPRELHIGH symval-got is too large for %s:%lx\n",
		ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)),symval);
	    }

	    *dest16=symval;
	    break;
	    }

	case R_ALPHA_LITERAL:
	    {
	    ELFGotEntryPtr gotent;
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_ALPHA_LITERAL %s\t", 
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest32=%lx\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
# endif

	    for (gotent=elffile->got_entries;gotent;gotent=gotent->next) {
		if ( ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) &&
	     	gotent->rel->r_addend == rel->r_addend )
			break;
	    }

	    /* Set the address in the GOT */
	    if( gotent ) {
		*(unsigned long *)(elffile->got+gotent->offset) =
							symval+rel->r_addend;
# ifdef ELFDEBUG
		ELFDEBUG("Setting gotent[%x]=%lx\t",
				gotent->offset, symval+rel->r_addend);
# endif
		if ((gotent->offset & 0xffff0000) != 0)
		    FatalError("\nR_ALPHA_LITERAL offset %x too large\n",
			       gotent->offset);
		(*dest32)|=(gotent->offset);	/* The address part is always 0 */
		}
	    else	{
		unsigned long val;
		
		/* S + A - P >> 2 */
		val=((symval+(rel->r_addend)-(Elf_Addr)dest32));
# ifdef ELFDEBUG
		ELFDEBUG("S+A-P=%x\t",val);
# endif
		if( (val & 0xffff0000) != 0xffff0000 &&
		    (val & 0xffff0000) != 0x00000000 )  {
		    ErrorF("\nR_ALPHA_LITERAL offset %x too large\n", val);
			break;
		}
		val &= 0x0000ffff;
		(*dest32)|=(val);	/* The address part is always 0 */
	    }
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
# endif

	    break;
	    }
	  
	case R_ALPHA_GPDISP:
	    {
	    long offset;

	    dest32h=(unsigned int *)(secp+rel->r_offset);
	    dest32=(unsigned int *)((secp+rel->r_offset)+rel->r_addend);

# ifdef ELFDEBUG
	    ELFDEBUG( "R_ALPHA_GPDISP %s\t", 
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "got=%lx\t", elffile->got );
	    ELFDEBUG( "dest32=%lx\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
	    ELFDEBUG( "dest32h=%lx\t", dest32h );
	    ELFDEBUG( "*dest32h=%8.8x\t", *dest32h );
# endif
	    if ((*dest32h >> 26) != 9 || (*dest32 >> 26) != 8) {
	        ErrorF( "***Bad instructions in relocating %s\n",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    }

	    symval = (*dest32h & 0xffff) << 16 | (*dest32 & 0xffff);
	    symval = (symval ^ 0x80008000) - 0x80008000;
	    
	    offset = ((unsigned char *)elffile->got - (unsigned char *)dest32h);
# ifdef ELFDEBUG
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "got-dest32=%lx\t", offset );
# endif

	    if( (offset >= 0x7fff8000L) || (offset < -0x80000000L) ) {
		FatalError( "Offset overflow for R_ALPHA_GPDISP\n");
	    }

	    symval += (unsigned long)offset;
# ifdef ELFDEBUG
	    ELFDEBUG( "symval=%lx\t", symval );
# endif
	    *dest32=(*dest32&0xffff0000) | (symval&0xffff);
	    *dest32h=(*dest32h&0xffff0000)|
			(((symval>>16)+((symval>>15)&1))&0xffff);
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
	    ELFDEBUG( "*dest32h=%8.8x\n", *dest32h );
# endif
	  break;
	  }
	  
	case R_ALPHA_HINT:
	    dest32=(unsigned int *)((secp+rel->r_offset)+rel->r_addend);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_ALPHA_HINT %s\t", 
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest32=%lx\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
# endif

# ifdef ELFDEBUG
	    ELFDEBUG( "symval=%lx\t", symval );
# endif
	    symval -= (Elf_Addr)(((unsigned char *)dest32)+4);
	    if (symval % 4 ) {
		ErrorF( "R_ALPHA_HINT bad alignment of offset\n");
		}
	    symval=symval>>2;

# ifdef ELFDEBUG
	    ELFDEBUG( "symval=%lx\t", symval );
# endif

	    if( symval & 0xffff8000 ) {
# ifdef ELFDEBUG
		ELFDEBUG("R_ALPHA_HINT symval too large\n" );
# endif
	    }

	    *dest32 = (*dest32&~0x3fff) | (symval&0x3fff);

# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
# endif
	  break;

 	case R_ALPHA_GPREL16:
 	    {
 	    dest64=(unsigned long *)(secp+rel->r_offset);
 	    dest16=(unsigned short *)dest64;
 
 	    symval += rel->r_addend;
 	    symval = ((unsigned char *)symval)-((unsigned char *)elffile->got);
 	    if( (long)symval > 0x7fff ||
 	        (long)symval < -(long)0x8000 ) {
 		FatalError("R_ALPHA_GPREL16 symval-got is too large for %s:%lx\n",
 			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)),symval);
 	    }
 
 	    *dest16=symval;
 	    break;
  	    }
	  
#endif /* alpha */
#if defined(__mc68000__)
	case R_68K_32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_68K_32\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
# endif
	    {
		unsigned long val;
		/* S + A */
		val=symval+(rel->r_addend);
# ifdef ELFDEBUG
		ELFDEBUG("S+A=%x\t",val);
# endif
		*dest32=val; /* S + A */
	    }
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
# endif
	    break;
	case R_68K_PC32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_68K_PC32\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
# endif
	    {
		unsigned long val;
		/* S + A - P */
		val=symval+(rel->r_addend);
		val-=*dest32;
# ifdef ELFDEBUG
		ELFDEBUG("S+A=%x\t",val);
		ELFDEBUG("S+A-P=%x\t",val+(*dest32)-(Elf_Addr)dest32);
# endif
	        *dest32=val+(*dest32)-(Elf_Addr)dest32; /* S + A - P */
	    }
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
# endif
	    break;
#endif /* __mc68000__ */
#if defined(__powerpc__)
# if defined(PowerMAX_OS)
	case R_PPC_DISP24: /* 11 */
	    dest32=(unsigned long *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_DISP24 %s\t", ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
#  endif

	    {
		unsigned long val;
		
		/* S + A - P >> 2 */
		val=((symval+(rel->r_addend)-(Elf_Addr)dest32));
#  ifdef ELFDEBUG
		ELFDEBUG("S+A-P=%x\t",val);
#  endif
		val = val>>2;
		if( (val & 0x3f000000) != 0x3f000000 &&
		    (val & 0x3f000000) != 0x00000000 )  {
#  ifdef ELFDEBUG
		    ELFDEBUG("R_PPC_DISP24 offset %x too large\n", val<<2);
#  endif
		    symval = ElfGetPltAddr(elffile,ELF_R_SYM(rel->r_info));
		    val=((symval+(rel->r_addend)-(Elf_Addr)dest32));
#  ifdef ELFDEBUG
		    ELFDEBUG("PLT offset is %x\n", val);
#  endif
		    val=val>>2;
		    if( (val & 0x3f000000) != 0x3f000000 &&
		         (val & 0x3f000000) != 0x00000000 )
			   FatalError("R_PPC_DISP24 PLT offset %x too large\n", val<<2);
		}
		val &= 0x00ffffff;
		(*dest32)|=(val<<2);	/* The address part is always 0 */
		ppc_flush_icache(dest32);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_16HU: /* 31 */
	    dest16=(unsigned short *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    dest32=(unsigned long *)(dest16-1);

#  endif
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_16HU\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest16=%x\t", dest16 );
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned short val;
		/* S + A */
		val=((symval+(rel->r_addend))&0xffff0000)>>16;
#  ifdef ELFDEBUG
		ELFDEBUG("uhi16(S+A)=%x\t",val);
#  endif
		*dest16=val; /* S + A */
		ppc_flush_icache(dest16);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_32: /* 32 */
	    dest32=(unsigned long *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_32\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned long val;
		/* S + A */
		val=symval+(rel->r_addend);
#  ifdef ELFDEBUG
		ELFDEBUG("S+A=%x\t",val);
#  endif
		*dest32=val; /* S + A */
		ppc_flush_icache(dest32);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_32UA: /* 33 */
	    dest32=(unsigned long *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_32UA\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned long val;
		unsigned char *dest8 = (unsigned char *)dest32;
		/* S + A */
		val=symval+(rel->r_addend);
#  ifdef ELFDEBUG
		ELFDEBUG("S+A=%x\t",val);
#  endif
		*dest8++=(val&0xff000000)>>24;
		*dest8++=(val&0x00ff0000)>>16;
		*dest8++=(val&0x0000ff00)>> 8;
		*dest8++=(val&0x000000ff);
		ppc_flush_icache(dest32);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_16H: /* 34 */
	    dest16=(unsigned short *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    dest32=(unsigned long *)(dest16-1);
#  endif
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_16H\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symbol=%s\t", ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest16=%x\t", dest16 );
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned short val;
		unsigned short loval;
		/* S + A */
		val=((symval+(rel->r_addend))&0xffff0000)>>16;
		loval=(symval+(rel->r_addend))&0xffff;
		if( loval & 0x8000 ) {
		    /*
		     * This is hi16(), instead of uhi16(). Because of this,
		     * if the lo16() will produce a negative offset, then
		     * we have to increment this part of the address to get
		     * the correct final result.
		     */
		    val++;
		}
#  ifdef ELFDEBUG
		ELFDEBUG("hi16(S+A)=%x\t",val);
#  endif
		*dest16=val; /* S + A */
		ppc_flush_icache(dest16);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_16L: /* 35 */
	    dest16=(unsigned short *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    dest32=(unsigned long *)(dest16-1);
#  endif
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_16L\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest16=%x\t", dest16 );
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned short val;
		/* S + A */
		val=(symval+(rel->r_addend))&0xffff;
#  ifdef ELFDEBUG
		ELFDEBUG("lo16(S+A)=%x\t",val);
#  endif
		*dest16=val; /* S + A */
		ppc_flush_icache(dest16);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
# else /* PowerMAX_OS */
 /* Linux PPC */
	case R_PPC_ADDR32: /* 1 */
	    dest32=(unsigned int *)(secp+rel->r_offset);
	    symval=ElfGetSymbolValue(elffile,ELF_R_SYM(rel->r_info));
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_ADDR32\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned long val;
		/* S + A */
		val=symval+(rel->r_addend);
#  ifdef ELFDEBUG
		ELFDEBUG("S+A=%x\t",val);
#  endif
		*dest32=val; /* S + A */
		ppc_flush_icache(dest32);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_ADDR16_LO: /* 4 */
	    dest16=(unsigned short *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    dest32=(unsigned long *)(dest16-1);
#  endif
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_ADDR16_LO\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest16=%x\t", dest16 );
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
#  endif
	    {
		unsigned short val;
		/* S + A */
		val=(symval+(rel->r_addend))&0xffff;
#  ifdef ELFDEBUG
		ELFDEBUG("lo16(S+A)=%x\t",val);
#  endif
		*dest16=val; /* S + A */
		ppc_flush_icache(dest16);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_ADDR16_HA: /* 6 */
	    dest16=(unsigned short *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    dest32=(unsigned long *)(dest16-1);
#  endif
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_ADDR16_HA\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest16=%x\t", dest16 );
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
#  endif
	    {
		unsigned short val;
		unsigned short loval;
		/* S + A */
		val=((symval+(rel->r_addend))&0xffff0000)>>16;
		loval=(symval+(rel->r_addend))&0xffff;
		if( loval & 0x8000 ) {
		    /*
		     * This is hi16(), instead of uhi16(). Because of this,
		     * if the lo16() will produce a negative offset, then
		     * we have to increment this part of the address to get
		     * the correct final result.
		     */
		    val++;
		}
#  ifdef ELFDEBUG
		ELFDEBUG("hi16(S+A)=%x\t",val);
#  endif
		*dest16=val; /* S + A */
		ppc_flush_icache(dest16);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest16=%8.8x\t", *dest16 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_REL24: /* 10 */
	    dest32=(unsigned int *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_REL24 %s\t", ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\t", *dest32 );
#  endif

	    {
		unsigned long val;
		
		/* S + A - P >> 2 */
		val=((symval+(rel->r_addend)-(Elf_Addr)dest32));
#  ifdef ELFDEBUG
		ELFDEBUG("S+A-P=%x\t",val);
#  endif
		val = val>>2;
		if( (val & 0x3f000000) != 0x3f000000 &&
		    (val & 0x3f000000) != 0x00000000 )  {
#  ifdef ELFDEBUG
		    ELFDEBUG("R_PPC_REL24 offset %x too large\n", val<<2);
#  endif
		    symval = ElfGetPltAddr(elffile,ELF_R_SYM(rel->r_info));
		    val=((symval+(rel->r_addend)-(Elf_Addr)dest32));
#  ifdef ELFDEBUG
		    ELFDEBUG("PLT offset is %x\n", val);
#  endif
		    val=val>>2;
		    if( (val & 0x3f000000) != 0x3f000000 &&
		         (val & 0x3f000000) != 0x00000000 )
			   FatalError("R_PPC_REL24 PLT offset %x too large\n", val<<2);
		}
		val &= 0x00ffffff;
		(*dest32)|=(val<<2);	/* The address part is always 0 */
		ppc_flush_icache(dest32);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
	case R_PPC_REL32: /* 26 */
	    dest32=(unsigned int *)(secp+rel->r_offset);
#  ifdef ELFDEBUG
	    ELFDEBUG( "R_PPC_REL32\t" );
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%x\t", symval );
	    ELFDEBUG( "r_addend=%x\t", rel->r_addend );
	    ELFDEBUG( "dest32=%8.8x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    {
		unsigned long val;
		/* S + A - P */
		val=symval+(rel->r_addend);
		val-=*dest32;
#  ifdef ELFDEBUG
		ELFDEBUG("S+A=%x\t",val);
		ELFDEBUG("S+A-P=%x\t",val+(*dest32)-(Elf_Addr)dest32);
#  endif
	        *dest32=val+(*dest32)-(Elf_Addr)dest32; /* S + A - P */
		ppc_flush_icache(dest32);
	    }
#  ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8x\n", *dest32 );
#  endif
	    break;
# endif /* PowerMAX_OS */
#endif /* __powerpc__ */
#ifdef __sparc__
	case R_SPARC_NONE:	/*  0 */
		break;

	case R_SPARC_8:		/*  1 */
		dest8 = (unsigned char *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest8 = symval;
		break;

	case R_SPARC_16:	/*  2 */
		dest16 = (unsigned short *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest16 = symval;
		break;

	case R_SPARC_32:	/*  3 */
	case R_SPARC_GLOB_DAT:	/* 20 */
	case R_SPARC_UA32:	/* 23 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		symval += rel->r_addend;
		((unsigned char *)dest32)[0] = (unsigned char)(symval >> 24);
		((unsigned char *)dest32)[1] = (unsigned char)(symval >> 16);
		((unsigned char *)dest32)[2] = (unsigned char)(symval >>  8);
		((unsigned char *)dest32)[3] = (unsigned char)(symval      );
		break;

	case R_SPARC_DISP8:	/*  4 */
		dest8 = (unsigned char *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest8 = (symval - (Elf32_Addr) dest8);
		break;

	case R_SPARC_DISP16:	/*  5 */
		dest16 = (unsigned short *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest16 = (symval - (Elf32_Addr) dest16);
		break;

	case R_SPARC_DISP32:	/*  6 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest32 = (symval - (Elf32_Addr) dest32);
		break;

	case R_SPARC_WDISP30:	/*  7 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest32 = ((*dest32 & 0xc0000000) |
			   ((symval - (Elf32_Addr) dest32) >> 2));
		break;

	case R_SPARC_HI22:	/*  9 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest32 = (*dest32 & 0xffc00000) | (symval >> 10);
		break;

	case R_SPARC_LO10:	/* 12 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		symval += rel->r_addend;
		*dest32 = (*dest32 & ~0x3ff) | (symval & 0x3ff);
		break;

	case R_SPARC_COPY:	/* 19 */
		/* Fix your code...  I'd rather dish out an error here
		 * so people will not link together PIC and non-PIC
		 * code into a final driver object file.
		 */
		ErrorF("Elf_RelocateEntry():"
		       "  Copy relocs not supported on Sparc.\n");
		break;

	case R_SPARC_JMP_SLOT:	/* 21 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		/* Before we change it the PLT entry looks like:
		 *
		 * pltent:	sethi	%hi(rela_plt_offset), %g1
		 *		b,a	PLT0
		 *		nop
		 *
		 * We change it into:
		 *
		 * pltent:	sethi	%hi(rela_plt_offset), %g1
		 *		sethi	%hi(symval), %g1
		 *		jmp	%g1 + %lo(symval), %g0
		 */
		symval += rel->r_addend;
		dest32[2] = 0x81c06000 | (symval & 0x3ff);
		__asm __volatile("flush %0 + 0x8" : : "r" (dest32));
		dest32[1] = 0x03000000 | (symval >> 10);
		__asm __volatile("flush %0 + 0x4" : : "r" (dest32));
		break;

	case R_SPARC_RELATIVE:	/* 22 */
		dest32 = (unsigned int *)(secp + rel->r_offset);
		*dest32 += (unsigned int)secp + rel->r_addend;
		break;
#endif /*__sparc__*/
#ifdef __ia64__
	case R_IA64_NONE:
		break;

	case R_IA64_LTOFF_FPTR22:
	    if (rel->r_addend)
		FatalError("\nAddend for R_IA64_LTOFF_FPTR22 not supported\n");
# ifdef ELFDEBUG
	    ELFDEBUG( "opd=%016lx.%016lx\n",
		((long *)symval)[0], ((long *)symval)[1] );
# endif
	    /* FALLTHROUGH */
	case R_IA64_LTOFF22:
#ifndef IA64_LDX_OPTIMIZATION
	case R_IA64_LTOFF22X: /* If not implementing LDXMOV optimization treat LTOFF22X as LTOFF22 */
#endif
	    {
	    ELFGotEntryPtr gotent;
	    dest128=(unsigned long *)(secp+(rel->r_offset&~3));
# ifdef ELFDEBUG
	    ELFDEBUG( "%s %s\t", ELF_R_TYPE(rel->r_info) == R_IA64_LTOFF22 ?
			"R_IA64_LTOFF22" : "R_IA64_LTOFF_FPTR22",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest128=%lx\t", dest128 );
	    ELFDEBUG( "slot=%d\n", rel->r_offset & 3);
	    ELFDEBUG( "*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]);
# endif

	    for (gotent=elffile->got_entries;gotent;gotent=gotent->next) {
		if ( ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) &&
	     	gotent->rel->r_addend == rel->r_addend )
			break;
	    }

	    /* Set the address in the GOT */
	    if( gotent ) {
		*(unsigned long *)(elffile->got+gotent->offset) =
							symval+rel->r_addend;
# ifdef ELFDEBUG
		ELFDEBUG("Setting gotent[%x]=%lx\n",
				gotent->offset, symval+rel->r_addend);
# endif
		if ((gotent->offset & 0xffe00000) != 0)
		    FatalError("\nR_IA64_LTOFF22 offset %x too large\n",
			       gotent->offset);
		IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_IMM22, gotent->offset);
	    }
	    else
		FatalError("\nCould not find GOT entry\n");
	    }
	    break;

	case R_IA64_PCREL21B:
	    {
	    ELFPltEntryPtr pltent;
	    dest128=(unsigned long *)(secp+(rel->r_offset&~3));
# ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_PCREL21B %s\t",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "opd=%lx.%lx\t", ((long *)symval)[0], ((long *)symval)[1]);
	    ELFDEBUG( "dest128=%lx\t", dest128 );
	    ELFDEBUG( "slot=%d\n", rel->r_offset & 3);
	    ELFDEBUG( "*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]);
# endif
	    if (rel->r_addend)
		FatalError("\nAddend for PCREL21B not supported\n");
	    if (((long *)symval)[1] == (long)elffile->got
		&& (((unsigned long)dest128 - ((unsigned long *)symval)[0]) + 0x2000000 < 0x4000000)) {
		/* We can save the travel through PLT */
		IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_TGT25C,
				 ((unsigned long *)symval)[0] - (unsigned long)dest128);
		break;
	    }
	    for (pltent=elffile->plt_entries;pltent;pltent=pltent->next) {
		if ( ELF_R_SYM(pltent->rel->r_info) == ELF_R_SYM(rel->r_info) &&
	     	pltent->rel->r_addend == rel->r_addend )
			break;
	    }

	    /* Set the address in the PLT */
	    if (pltent == NULL)
		FatalError("\nCould not find PLT entry\n");
	    else {
		unsigned long *p = (unsigned long *)(elffile->plt+pltent->offset);
		unsigned long r = (unsigned long)symval - (unsigned long)elffile->got;

		if (r + 0x200000 >= 0x400000) {
			/* Too far from gp to use the official function descriptor,
			 * so we have to make a local one.
			 */
			r = pltent->gotoffset;
			memcpy(elffile->got+r, (char *)symval, 16);
		}

		/* [MMI] addl r15=NNN,r1;; ld8 r16=[r15],8; mov r14=r1;; */
		p[0] = 0x410024000200780bUL;
		p[1] = 0x84000801c028303cUL;
		/* [MIB] ld8 r1=[r15]; mov b6=r16; br.few b6;; */
		p[2] = 0x806010181e000811UL;
		p[3] = 0x0080006000038004UL;
		IA64InstallReloc(p, 0, IA64_OPND_IMM22, r);
		IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_TGT25C,
				 (unsigned long)p - (unsigned long)dest128);
	    }
	    }
	    break;

	case R_IA64_FPTR64LSB:
	    dest64=(unsigned long *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_FPTR64LSB %s\t",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest64=%lx\t", dest64 );
	    ELFDEBUG( "opd=%016lx.%016lx\n", ((long *)symval)[0], ((long *)symval)[1] );
# endif

	    if (rel->r_addend)
		FatalError("\nAddend not supported for R_IA64_FPTR64LSB\n");
	    *dest64 = symval;
	    ia64_flush_cache(dest64);
	    break;

	case R_IA64_DIR64LSB:
	    dest64=(unsigned long *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_DIR64LSB %s\t",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest64=%lx\n", dest64 );
# endif
	    *dest64 = symval + rel->r_addend;
	    ia64_flush_cache(dest64);
	    break;

	case R_IA64_PCREL64LSB:
	    dest64=(unsigned long *)(secp+rel->r_offset);
#ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_PCREL64LSB %s\t",
		      ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest64=%lx\n", dest64 );
#endif
	    *dest64 = symval + rel->r_addend - (unsigned long)dest64;
	    break;

	case R_IA64_GPREL22:
	    dest128=(unsigned long *)(secp+(rel->r_offset&~3));
# ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_GPREL22 %s\t",
			ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest128=%lx\t", dest128 );
	    ELFDEBUG( "slot=%d\n", rel->r_offset & 3);
	    ELFDEBUG( "*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]);
# endif
	    IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_IMM22,
		symval + rel->r_addend - (long)elffile->got);
	    break;

#ifdef IA64_LDX_OPTIMIZATION
	case R_IA64_LTOFF22X:
	  {
	    ELFGotEntryPtr gotent;
	    long gp_offset = symval+rel->r_addend-(long)elffile->got;
	    dest128=(unsigned long *)(secp+(rel->r_offset&~3));

# ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_LTOFF22X %s\t", ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    ELFDEBUG( "secp=%lx\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest128=%lx\t", dest128 );
	    ELFDEBUG( "slot=%d\n", rel->r_offset & 3);
# endif

	    if (gp_offset << 42 >> 42 != gp_offset) {
	      /* Offset is too large for LTOFF22X, 
	       * fallback to using GOT lookup, e.g. LTOFF22. 
	       * Note: LDXMOV will fail the same test and will be ignored. */

# ifdef ELFDEBUG
	    ELFDEBUG( "gp_offset=%ld too large, using GOT instead (LTOFF22)\n", gp_offset);
# endif

	      for (gotent=elffile->got_entries;gotent;gotent=gotent->next) {
		if ( ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) &&
		     gotent->rel->r_addend == rel->r_addend )
		  break;
	      }
	      
	      /* Set the address in the GOT */
	      if( gotent ) {
		*(unsigned long *)(elffile->got+gotent->offset) = symval+rel->r_addend;
# ifdef ELFDEBUG
		ELFDEBUG("Setting gotent[%x]=%lx\n", gotent->offset, symval+rel->r_addend);
# endif
		if ((gotent->offset & 0xffe00000) != 0)
		  FatalError("\nR_IA64_LTOFF22 offset %x too large\n", gotent->offset);
	      } else {
		FatalError("\nCould not find GOT entry\n");
	      }
	      gp_offset = gotent->offset; /* Use GOT lookup */
	    } else {
# ifdef ELFDEBUG
	      ELFDEBUG( "using gp_offset=%ld (LTOFF22X)", gp_offset);
# endif
	    }
	    IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_IMM22, gp_offset);
	    }
	    break;
#endif

	case R_IA64_LDXMOV:
# ifdef ELFDEBUG
	    ELFDEBUG( "R_IA64_LDXMOV %s\t", ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
# endif

#ifdef IA64_LDX_OPTIMIZATION
	  {
	    long gp_offset = symval+rel->r_addend-(long)elffile->got;
	    dest128=(unsigned long *)(secp+(rel->r_offset&~3));

	    if (gp_offset << 42 >> 42 != gp_offset) {
	      /* Offset is too large for LTOFF22X, ignore this relocation */ 
# ifdef ELFDEBUG
	      ELFDEBUG( "offset = %ld too large, ignoring\n", gp_offset);
# endif
	    } else {

# ifdef ELFDEBUG
	      ELFDEBUG( "secp=%lx\t", secp );
	      ELFDEBUG( "symval=%lx\t", symval );
	      ELFDEBUG( "dest128=%lx\t", dest128 );
	      ELFDEBUG( "slot=%d\n", rel->r_offset & 3);
	      ELFDEBUG( "offset=%ld\n", gp_offset);
	      ELFDEBUG( "*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]);
# endif

	      IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_LDXMOV, 0);
	    }
	  }
#endif
	    break;

#endif /*__ia64__*/

#if defined(__arm__)
	case R_ARM_ABS32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    ELFDEBUG( "R_ARM_ABS32\t");
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8lx\t", *dest32 );
# endif
	    *dest32=symval+(*dest32); /* S + A */
# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8lx\n", *dest32 );
# endif
	    break;

	case R_ARM_REL32:
	    dest32=(unsigned int *)(secp+rel->r_offset);
# ifdef ELFDEBUG
	    {
	    char *namestr;
	    ELFDEBUG( "R_ARM_REL32 %s\t",
			namestr=ElfGetSymbolName(elffile,ELF_R_SYM(rel->r_info)) );
	    xf86loaderfree(namestr);
	    ELFDEBUG( "secp=%x\t", secp );
	    ELFDEBUG( "symval=%lx\t", symval );
	    ELFDEBUG( "dest32=%x\t", dest32 );
	    ELFDEBUG( "*dest32=%8.8lx\t", *dest32 );
	    }
# endif

	    *dest32=symval+(*dest32)-(Elf_Addr)dest32; /* S + A - P */

# ifdef ELFDEBUG
	    ELFDEBUG( "*dest32=%8.8lx\n", *dest32 );
# endif

	    break;

	case R_ARM_PC24:	
	    {
	    unsigned long val;
            dest32=(unsigned int *)(secp+rel->r_offset);
	    val = (*dest32 & 0x00ffffff) << 2;
            val = symval - (unsigned long)dest32 + val;	
            val >>= 2;
	    *dest32 = (*dest32 & 0xff000000) | (val & 0x00ffffff); 
#ifdef NOTYET
	    arm_flush_cache(dest32);
#endif
	    }
	    break;

#endif /* (__arm__) */

	default:
	    ErrorF("Elf_RelocateEntry() Unsupported relocation type %d\n",
		   ELF_R_TYPE(rel->r_info));
	    break;
	    }
    return 0;
}

static ELFRelocPtr
ELFCollectRelocations(elffile, index)
ELFModulePtr	elffile;
int	index; /* The section to use as relocation data */
{
    int	i, numrel;
    Elf_Shdr	*sect=&(elffile->sections[index]);
    Elf_Rel_t	*rel=(Elf_Rel_t *)elffile->saddr[index];
    Elf_Sym	*syms;
    ELFRelocPtr reloc_head = NULL;
    ELFRelocPtr tmp;

    syms = (Elf_Sym *) elffile->saddr[elffile->symndx];

    numrel=sect->sh_size/sect->sh_entsize;

    for(i=0; i<numrel; i++ ) {
#if defined(__alpha__)
	if( ELF_R_TYPE(rel[i].r_info) == R_ALPHA_LITERAL) {
	    ElfAddGOT(elffile,&rel[i]);
	}   
#endif
#if defined(__ia64__)
	if (ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF22 || ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF22X
	    || ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF_FPTR22) {
	    ElfAddGOT(elffile,&rel[i]);
	}
	if (ELF_R_TYPE(rel[i].r_info) == R_IA64_PCREL21B) {
	    ElfAddPLT(elffile,&rel[i]);
	}
	if (ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF_FPTR22
	    || ELF_R_TYPE(rel[i].r_info) == R_IA64_FPTR64LSB) {
	    if (ELF_ST_BIND(syms[ELF_R_SYM(rel[i].r_info)].st_info) == STB_LOCAL) {
		ElfAddOPD(elffile, ELF_R_SYM(rel[i].r_info), NULL);
	    }
	}
#endif
	tmp = ElfDelayRelocation(elffile, sect->sh_info, &(rel[i]));
	tmp->next = reloc_head;
	reloc_head = tmp;
    }

    return reloc_head;
}

/*
 * ELF_GetSymbols()
 *
 * add the symbols to the symbol table maintained by the loader.
 */

static LOOKUP *
ELF_GetSymbols(elffile, psecttable)
ELFModulePtr	elffile;
unsigned short  **psecttable;
{
    Elf_Sym	*syms;
    Elf_Shdr	*sect;
    int		i, l, numsyms;
    LOOKUP	*lookup, *p;
    ELFCommonPtr tmp;
    unsigned short *secttable;

    syms=elffile->symtab;
    sect=&(elffile->sections[elffile->symndx]);
    numsyms=sect->sh_size/sect->sh_entsize;

    if ((lookup = xf86loadermalloc((numsyms+1)*sizeof(LOOKUP))) == NULL)
	return 0;

    if ((secttable = xf86loadercalloc(sizeof(unsigned short),(numsyms+1))) == NULL) {
	xf86loaderfree(lookup);
	return 0;
    }
    *psecttable = secttable;

    for(i=0,l=0; i<numsyms; i++)
	{
#ifdef ELFDEBUG
	    ELFDEBUG("value=%lx\tsize=%lx\tBIND=%x\tTYPE=%x\tndx=%x\t%s\n",
		     syms[i].st_value,syms[i].st_size,
		     ELF_ST_BIND(syms[i].st_info),ELF_ST_TYPE(syms[i].st_info),
		     syms[i].st_shndx,ElfGetString(elffile,syms[i].st_name) );
#endif

	    if( ELF_ST_BIND(syms[i].st_info) == STB_LOCAL )
		/* Don't add static symbols to the symbol table */
		continue;

	    switch( ELF_ST_TYPE(syms[i].st_info) )
		{
		case STT_OBJECT:
		case STT_FUNC:
		case STT_SECTION:
		case STT_NOTYPE:
		    switch(syms[i].st_shndx)
			{
			case SHN_ABS:
			    ErrorF("ELF_GetSymbols() Don't know how to handle SHN_ABS\n" );
			    break;
			case SHN_COMMON:
#ifdef ELFDEBUG
			    ELFDEBUG("Adding COMMON space for %s\n",
				     ElfGetString(elffile,syms[i].st_name) );
#endif
			    if (!LoaderHashFind(ElfGetString(elffile,
							syms[i].st_name))) {
				tmp = ElfAddCOMMON(&(syms[i]));
				if (tmp) {
				    tmp->next = listCOMMON;
				    listCOMMON = tmp;
				}
			    }			    
			    break;
			case SHN_UNDEF:
			    /*
			     * UNDEF will get resolved later, so the value
			     * doesn't really matter here.
			     */
			    /* since we don't know the value don't advertise the symbol */
			    break;
			default:
			    lookup[l].symName=xf86loaderstrdup(ElfGetString(elffile,syms[i].st_name));
			    lookup[l].offset=(funcptr)syms[i].st_value;
			    secttable[l] = syms[i].st_shndx;
#ifdef ELFDEBUG
			    ELFDEBUG("Adding symbol %lx(%d) %s\n",
				     lookup[l].offset, secttable[l], lookup[l].symName );
#endif
#ifdef __ia64__
			    if ( ELF_ST_TYPE(syms[i].st_info) == STT_FUNC ) {
				ElfAddOPD(elffile, -1, &lookup[l]);
			    }
#endif
			    l++;
			    break;
			}
		    break;
		case STT_FILE:
		case STT_LOPROC:
		case STT_HIPROC:
		    /* Skip this type */
#ifdef ELFDEBUG
		    ELFDEBUG("Skipping TYPE %d %s\n",
			     ELF_ST_TYPE(syms[i].st_info),
			     ElfGetString(elffile,syms[i].st_name));
#endif
		    break;
		default:
		    ErrorF("ELF_GetSymbols(): Unepected symbol type %d\n",
			   ELF_ST_TYPE(syms[i].st_info) );
		    break;
		}
	}

    lookup[l].symName=NULL; /* Terminate the list */

/*
 * Remove the ELF symbols that will show up in every object module.
 */
    for (i = 0, p = lookup; p->symName; i++, p++) {
	while (!strcmp(lookup[i].symName, ".text")
	       || !strcmp(lookup[i].symName, ".data")
	       || !strcmp(lookup[i].symName, ".bss")
	       || !strcmp(lookup[i].symName, ".comment")
	       || !strcmp(lookup[i].symName, ".note")
	       ) {
	    memmove(&(lookup[i]), &(lookup[i+1]), (l - i) * sizeof (LOOKUP));
	    memmove(&(secttable[i]), &(secttable[i+1]), (l-- - i) * sizeof (unsigned short));
	}
    }
    return lookup;
}

#define SecOffset(index) elffile->sections[index].sh_offset
#define SecSize(index) elffile->sections[index].sh_size
#define SecAlign(index) elffile->sections[index].sh_addralign
#define SecType(index) elffile->sections[index].sh_type
#define SecFlags(index) elffile->sections[index].sh_flags
#define SecInfo(index) elffile->sections[index].sh_info

#define AdjustSize(i)				\
    if (!pass) {				\
	if (SecAlign(i) > *maxalign)		\
	    *maxalign = SecAlign(i);		\
	*totalsize += (SecAlign(i) - 1);	\
	*totalsize &= ~(SecAlign(i) - 1);	\
	*totalsize += SecSize(i);		\
	continue;				\
    } do { } while (0)

/*
 * ELFCollectSections
 *
 * Do the work required to load each section into memory.
 */
static void
ELFCollectSections(elffile,pass,totalsize,maxalign)
ELFModulePtr	elffile;
int		pass;
int		*totalsize;
int		*maxalign;
{
    int	i;
    int j;
/*
 * Find and identify all of the Sections
 */
    j = elffile->lsectidx;
    for( i=1; i<elffile->numsh; i++) {
	int flags = 0;
	char *name = ElfGetSectionName(elffile, elffile->sections[i].sh_name);

#if defined(__alpha__) || defined(__ia64__)
	if (!strcmp(name,".got") /*Isn't there a more generic way to do this?*/
# if defined(__ia64__)
	    || !strcmp(name,".plt") || !strcmp(name, ".IA_64.unwind_info")
# endif
	    ) continue;
#endif
	switch (SecType(i)) {
	case SHT_STRTAB:
	    if (!strcmp(name,".shstrtab")) /* already loaded */
		continue;
	    if (!strcmp(name,".stabstr"))  /* ignore debug info */
		continue;
	case SHT_SYMTAB:
	    if (pass) continue;
	    flags = LOADED_SECTION;
	    flags |= RELOC_SECTION;
	    break;
	case SHT_REL:
	case SHT_RELA:
  	    if (pass) continue;
	    if (! (SecFlags(SecInfo(i)) & SHF_ALLOC))
	      continue;
#ifdef __ia64__
	    if (SecType(SecInfo(i)) == SHT_IA_64_UNWIND)
	      continue;
#endif
  	    flags = LOADED_SECTION;
  	    flags |= RELOC_SECTION;
  	    break;
	case SHT_PROGBITS:
	    flags |= LOADED_SECTION;
	case SHT_NOBITS:
	    if (! (elffile->sections[i].sh_flags & SHF_ALLOC))
		continue;
	    AdjustSize(i);
	    break;
	default:
#ifdef ELFDEBUG
	    if (pass)
		ELFDEBUG("ELF: Not loading %s\n",name);
#endif
	    continue;
	}
	
	elffile->lsection = xf86loaderrealloc(elffile->lsection,
					      (j + 1) * sizeof (LoadSection));
	if (!(flags & RELOC_SECTION)) {
	    if (flags & LOADED_SECTION) { 
		elffile->lsection[j].saddr /* sect. contains data */
		    = ELFLoaderSectToMem(elffile,SecAlign(i),SecOffset(i),
					 SecSize(i), name);
	    } else {
		if( SecSize(i) )
		    elffile->lsection[j].saddr  
			= ELFLoaderSectCalloc(elffile,SecAlign(i),
					      SecSize(i));
	        else
		    elffile->lsection[j].saddr = NULL;
	    }
	} else {
	    elffile->lsection[j].saddr =
		(Elf_Sym *)_LoaderFileToMem(elffile->fd,SecOffset(i),
					    SecSize(i),name);
	}
	elffile->saddr[i]=elffile->lsection[j].saddr;
#ifdef ELFDEBUG
	ELFDEBUG("%s starts at %lx size: %lx\n",
		 name, elffile->saddr[i], SecSize(i));
#endif	
	elffile->lsection[j].name= name;
	elffile->lsection[j].ndx = i;
	elffile->lsection[j].size=SecSize(i);
	elffile->lsection[j].flags=flags;
	switch (SecType(i)) {
#ifdef __OpenBSD__
	case SHT_PROGBITS:
	    mprotect(elffile->lsection[j].saddr, SecSize(i), 
		     PROT_READ|PROT_WRITE|PROT_EXEC);
	    break;
#endif
	case SHT_SYMTAB:
	    elffile->symtab = (Elf_Sym *)elffile->saddr[i];
	    elffile->symndx = i;
	    break;
	case SHT_STRTAB:
	    elffile->straddr = elffile->saddr[i];
	    elffile->strsize = elffile->lsection[j].size;
	    elffile->strndx = i;
	    break;
	default:
	    break;
	}
	elffile->lsectidx = ++j;
    }
}

/*
 * Public API for the ELF implementation of the loader.
 */
void *
ELFLoadModule(modrec, elffd, ppLookup)
loaderPtr	modrec;
int	elffd;
LOOKUP **ppLookup;
{
    ELFModulePtr elffile;
    Elf_Ehdr   *header;
    ELFRelocPtr  elf_reloc, tail;
    void	*v;
    LDRModulePtr elfmod;
    int		totalsize, maxalign, i;
    unsigned short *secttable;
    LOOKUP	*pLookup;
    
    ldrCommons = 0;
    nCommons = 0;

#ifdef ELFDEBUG
    ELFDEBUG("Loading %s %s\n", modrec->name, modrec->cname);
#endif
    if ((elffile = xf86loadercalloc(1, sizeof(ELFModuleRec))) == NULL) {
	ErrorF( "Unable to allocate ELFModuleRec\n" );
	return NULL;
    }

    elffile->handle=modrec->handle;
    elffile->module=modrec->module;
    elffile->fd=elffd;
    v=elffile->funcs=modrec->funcs;

/*
 *  Get the ELF header
 */
    elffile->header=
	(Elf_Ehdr*)_LoaderFileToMem(elffd, 0, sizeof(Elf_Ehdr), "header");
    header=(Elf_Ehdr *)elffile->header;

/*
 * Get the section table
 */
    elffile->numsh=header->e_shnum;
    elffile->secsize=(header->e_shentsize * header->e_shnum);
    elffile->sections=
	(Elf_Shdr *)_LoaderFileToMem(elffd, header->e_shoff, elffile->secsize,
				     "sections");
#if defined(__alpha__) || defined(__ia64__)
    /*
     * Need to allocate space for the .got section which will be
     * fabricated later
     */
    elffile->gotndx=header->e_shnum;
    header->e_shnum++;
# if defined(__ia64__)
    elffile->pltndx=header->e_shnum;
    header->e_shnum++;
# endif
    elffile->numsh=header->e_shnum;
    elffile->secsize=(header->e_shentsize*header->e_shnum);
    elffile->sections=xf86loaderrealloc(elffile->sections,elffile->secsize);
#endif /*defined(__alpha__) || defined(__ia64__)*/
    elffile->saddr=xf86loadercalloc(elffile->numsh, sizeof(unsigned char *));

#if defined(__alpha__) || defined(__ia64__)
    /*
     * Manually fill in the entry for the .got section so ELFCollectSections()
     * will be able to find it.
     */
    elffile->sections[elffile->gotndx].sh_name=SecSize(header->e_shstrndx)+1;
    elffile->sections[elffile->gotndx].sh_type=SHT_PROGBITS;
    elffile->sections[elffile->gotndx].sh_flags=SHF_WRITE|SHF_ALLOC;
    elffile->sections[elffile->gotndx].sh_size=0;
    elffile->sections[elffile->gotndx].sh_addralign=8;
    /* Add room to copy ".got", and maintain alignment */
    SecSize(header->e_shstrndx)+=8;
#endif
#if defined(__ia64__)
    /*
     * Manually fill in the entry for the .plt section so ELFCollectSections()
     * will be able to find it.
     */
    elffile->sections[elffile->pltndx].sh_name=SecSize(header->e_shstrndx)+1;
    elffile->sections[elffile->pltndx].sh_type=SHT_PROGBITS;
    elffile->sections[elffile->pltndx].sh_flags=SHF_EXECINSTR|SHF_ALLOC;
    elffile->sections[elffile->pltndx].sh_size=0;
    elffile->sections[elffile->pltndx].sh_addralign=32;
    /* Add room to copy ".plt", and maintain alignment */
    SecSize(header->e_shstrndx)+=32;
#endif

/*
 * Get the section header string table
 */
    elffile->shstrsize = SecSize(header->e_shstrndx);
    elffile->shstraddr =
	_LoaderFileToMem(elffd, SecOffset(header->e_shstrndx),
			 SecSize(header->e_shstrndx), ".shstrtab");
    elffile->shstrndx = header->e_shstrndx;
#if defined(__alpha__) || defined(__ia64__)
    /*
     * Add the string for the .got section
     */
    strcpy((char*)(elffile->shstraddr+elffile->sections[elffile->gotndx].sh_name),
	   ".got");
#endif
#if defined(__ia64__)
    /*
     * Add the string for the .plt section
     */
    strcpy((char*)(elffile->shstraddr+elffile->sections[elffile->pltndx].sh_name),
	   ".plt");
#endif

/*
 * Load some desired sections, compute size of the remaining ones
 */
    totalsize = 0;
    maxalign = 0;
    ELFCollectSections(elffile, 0, &totalsize, &maxalign);
    if( elffile->straddr == NULL || elffile->strsize == 0 ) {
#if 0
	ErrorF("No symbols found in this module\n");
#endif
	ELFUnloadModule(elffile);
	return (void *) -1L;
    }
/*
 * add symbols
 */
    *ppLookup = pLookup = ELF_GetSymbols(elffile, &secttable);

/*
 * Do relocations
 */
    for (i = 0; i < elffile->lsectidx; i++) {
	switch (SecType(elffile->lsection[i].ndx)) {
	case SHT_REL:
	case SHT_RELA:
	    break;
	default:
	    continue;
	}
	elf_reloc = ELFCollectRelocations(elffile,elffile->lsection[i].ndx);
	if (elf_reloc) {
	    for (tail = elf_reloc; tail->next; tail = tail->next)
		;
	    tail->next = _LoaderGetRelocations(v)->elf_reloc;
	    _LoaderGetRelocations(v)->elf_reloc = elf_reloc;
	}
    }

#if defined(__ia64__)
    totalsize += (elffile->sections[elffile->pltndx].sh_addralign - 1);
    totalsize &= ~(elffile->sections[elffile->pltndx].sh_addralign - 1);
    totalsize += elffile->pltsize;
    if (maxalign < elffile->sections[elffile->pltndx].sh_addralign)
	maxalign = elffile->sections[elffile->pltndx].sh_addralign;
#endif

    /* Space for COMMON */
    totalsize = (totalsize + 7) & ~7;
    totalsize += ElfCOMMONSize();

#ifdef MergeSectionAlloc
    elffile->basesize = totalsize + maxalign;
 
# if !defined(DoMMAPedMerge)
    elffile->base = xf86loadermalloc(elffile->basesize);
    if (elffile->base == NULL) {
	ErrorF( "Unable to allocate ELF sections\n" );
	return NULL;
    }
#  if defined(linux) && defined(__ia64__) || defined(__OpenBSD__)
    {
	unsigned long page_size = getpagesize();
	unsigned long round;

	round = (unsigned long)elffile->base & (page_size-1);
	mprotect(elffile->base - round, (elffile->basesize+round+page_size-1) & ~(page_size-1),
		 PROT_READ|PROT_WRITE|PROT_EXEC);
    }
#  endif
# else 
    MMAP_ALIGN(elffile->basesize);
    elffile->base = mmap( 0,elffile->basesize,MMAP_PROT,MMAP_FLAGS,-1,
			  (off_t)0);
    if (elffile->base == NULL) {
	ErrorF( "Unable to mmap ELF sections\n" );
	return NULL;
    }
# endif
    elffile->baseptr = ((long)elffile->base + (maxalign - 1)) & ~(maxalign - 1);
#endif

#if defined(__alpha__) || defined(__ia64__)
    if (! ELFCreateGOT(elffile, maxalign))
	return NULL;
#endif
#if defined(__ia64__)
    ELFCreatePLT(elffile);
#endif

    ELFCollectSections(elffile, 1, NULL, NULL);

    for (i = 0; pLookup[i].symName; i++)
	if (secttable[i]) {
	    pLookup[i].offset = (funcptr)((long)pLookup[i].offset + (long)elffile->saddr[secttable[i]]);
#ifdef ELFDEBUG
	    ELFDEBUG("Finalizing symbol %lx %s\n",
		     pLookup[i].offset, pLookup[i].symName);
#endif
	}
    xf86loaderfree(secttable);

#if defined(__ia64__)
    ELFCreateOPD(elffile);
#endif

    if (! ElfCreateCOMMON(elffile, *ppLookup))
	return NULL;

    /* Record info for gdb - if we can't allocate the loader record fail
       silently (the user will find out soon enough that there's no VM left */
    if ((elfmod = xf86loadercalloc(1, sizeof(LDRModuleRec))) != NULL) {
	elfmod->name = strdup(modrec->name);
	elfmod->namelen = strlen(modrec->name);
	elfmod->version = 1;
	for (i=0; i < elffile->lsectidx;i++) {
	    char *name = elffile->lsection[i].name;
	    if (!strcmp(name,".text"))
		elfmod->text = elffile->lsection[i].saddr;
	    else if (!strcmp(name,".data"))
		elfmod->data = elffile->lsection[i].saddr;
	    else if (!strcmp(name,".rodata"))
		elfmod->rodata = elffile->lsection[i].saddr;
	    else if (!strcmp(name,".bss"))
		elfmod->bss = elffile->lsection[i].saddr;
	}
	elfmod->next = ModList;
	elfmod->commons = ldrCommons;
	elfmod->commonslen = nCommons;

	ModList = elfmod;

	/* Tell GDB something interesting happened */
	_loader_debug_state();
    }
    return (void *)elffile;
}

void
ELFResolveSymbols(mod)
void *mod;
{
    ELFRelocPtr 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)->elf_reloc; p; ) {
#ifdef ELFDEBUG
	ErrorF("ResolveSymbols: file %lx, sec %d, r_offset 0x%x, r_info 0x%lx\n",
	       p->file, p->secn, p->rel->r_offset, p->rel->r_info);
#endif
	tmp = Elf_RelocateEntry(p->file, p->secn, p->rel, FALSE);
	if (tmp) {
	    /* Failed to relocate.  Keep it in the list. */
	    tmp->next = newlist;
	    newlist = tmp;
	}
	tmp = p;
	p = p->next;
	xf86loaderfree(tmp);
    }
    _LoaderGetRelocations(mod)->elf_reloc=newlist;
}

int
ELFCheckForUnresolved(mod)
void	*mod;
{
    ELFRelocPtr	erel;
    char	*name;
    int flag, fatalsym=0;

    if ((erel = _LoaderGetRelocations(mod)->elf_reloc) == NULL)
	return 0;

    while( erel ) {
	Elf_RelocateEntry(erel->file, erel->secn, erel->rel, TRUE);
	name = ElfGetSymbolName(erel->file, ELF_R_SYM(erel->rel->r_info));
	flag = _LoaderHandleUnresolved(
	    name, _LoaderHandleToName(erel->file->handle));
	if(flag) fatalsym = 1;
	erel=erel->next;
    }
    return fatalsym;
}

void
ELFUnloadModule(modptr)
void *modptr;
{
    ELFModulePtr elffile = (ELFModulePtr)modptr;
    ELFRelocPtr  relptr, reltptr, *brelptr;
    int i;
    
/*
 * Delete any unresolved relocations
 */

    relptr=_LoaderGetRelocations(elffile->funcs)->elf_reloc;
    brelptr=&(_LoaderGetRelocations(elffile->funcs)->elf_reloc);

    while(relptr) {
	if( relptr->file == elffile ) {
	    *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 *)elffile, ELFhashCleanOut);

/*
 * Free the sections that were allocated.
 */
#if !defined (DoMMAPedMerge)
# define CheckandFree(ptr,size)  if(ptr) xf86loaderfree(ptr)
#else 
# define CheckandFree(ptr,size) if (ptr) munmap(ptr,size)
#endif
#define CheckandFreeFile(ptr,size)  if(ptr) _LoaderFreeFileMem((ptr),(size))

#ifdef MergeSectionAlloc
    CheckandFree(elffile->base,elffile->basesize);
# if defined(__alpha__) || defined(__ia64__)
    if (elffile->shared_got) {
	elffile->shared_got->nuses--;
	if (!elffile->shared_got->nuses) {
	    ELFGotPtr *pgot = &ELFSharedGOTs;
	    while (*pgot && *pgot != elffile->shared_got)
		pgot = &(*pgot)->next;
	    if (*pgot)
		*pgot = elffile->shared_got->next;
	    xf86loaderfree(elffile->shared_got);
	}
    }
# endif
#else /*MergeSectionAlloc*/
    CheckandFree(elffile->common,elffile->comsize);
# if defined(__alpha__) || defined(__ia64__)
    CheckandFree(elffile->got,elffile->gotsize);
# endif
# if defined(__ia64__)
    CheckandFree(elffile->plt,elffile->pltsize);
# endif
#endif
#if defined(__alpha__) || defined(__ia64__)
    {
	ELFGotEntryPtr gotent;
	while((gotent = elffile->got_entries)) {
	    elffile->got_entries = gotent->next;
	    xf86loaderfree(gotent);
	}
    }
#endif
#if defined(__ia64__)
    {
	ELFPltEntryPtr pltent;
	while ((pltent = elffile->plt_entries)) {
	    elffile->plt_entries = pltent->next;
	    xf86loaderfree(pltent);
	}
    }
    {
	ELFOpdPtr opdent;
	while ((opdent = elffile->opd_entries)) {
	    elffile->opd_entries = opdent->next;
	    xf86loaderfree(opdent);
	}
    }
#endif

    for (i = 0; i < elffile->lsectidx; i++) {
#ifdef MergeSectionAlloc
	if (!(elffile->lsection[i].flags & RELOC_SECTION))
	    continue;
#endif
	if (elffile->lsection[i].flags & LOADED_SECTION) {
	    CheckandFreeFile(elffile->lsection[i].saddr,
			     elffile->lsection[i].size);
	} else {
	    CheckandFree(elffile->lsection[i].saddr,
			 elffile->lsection[i].size);
	}
    }
    xf86loaderfree(elffile->lsection);

/*
 * Free the section table, section pointer array, and section names
 */
    _LoaderFreeFileMem(elffile->sections,elffile->secsize);
    xf86loaderfree(elffile->saddr);
    _LoaderFreeFileMem(elffile->header,sizeof(Elf_Ehdr));
    _LoaderFreeFileMem(elffile->shstraddr,elffile->shstrsize);
	
    
/*
 * Free the ELFModuleRec
 */
    xf86loaderfree(elffile);

    return;
}

char *
ELFAddressToSection(void *modptr, unsigned long address)
{
    ELFModulePtr elffile = (ELFModulePtr)modptr;
    int i;

    for( i=1; i<elffile->numsh; i++) {
	if( address >= (unsigned long)elffile->saddr[i] &&
	    address <= (unsigned long)elffile->saddr[i]+SecSize(i) ) {
		return ElfGetSectionName(elffile, elffile->sections[i].sh_name);
		}
	}
    return NULL;
}
