Logo Search packages:      
Sourcecode: xorg-server version File versions  Download package

elfloader.c

/* $XdotOrg: xserver/xorg/hw/xfree86/loader/elfloader.c,v 1.10 2005/07/03 08:53:46 daniels Exp $ */
/* $XFree86: xc/programs/Xserver/hw/xfree86/loader/elfloader.c,v 1.61tsi 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.
 */
#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#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 <X11/Xos.h>
#include "os.h"
#include "elf.h"

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

#include "compiler.h"

#ifndef LOADERDEBUG
#define LOADERDEBUG 0
#endif

#if LOADERDEBUG
# 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__) || !defined(__linux__)
# 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(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(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(void *voidptr, itemPtr item)
{
    ELFModulePtr module = (ELFModulePtr) voidptr;

    return (module->handle == item->handle);
}

/*
 * Manage listResolv
 */
static ELFRelocPtr
ElfDelayRelocation(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 %p: file %p, sec %d,"
           " r_offset 0x%lx, r_info 0x%x",
           (void *)reloc, (void *)elffile, secn,
           (unsigned long)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(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(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 %p %s\n",
             (void *)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(ELFModulePtr file, int offset, int index)
{
    if (!offset || !index)
      return "";

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

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

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

/*
 * Symbol Table
 */

/*
 * Get symbol name
 */
static char *
ElfGetSymbolNameIndex(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", (unsigned long)syms[index].st_value);
#endif

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

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

static Elf_Addr
ElfGetSymbolValue(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("%p\t", (void *)symbol);
      ELFDEBUG("%lx\t", (unsigned long)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", (unsigned long)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(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(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(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(__OpenBSD__) || defined(sun)
      {
          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(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(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(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(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;
    }
#   if defined(linux) || defined(__OpenBSD__) || defined(sun)
    {
      unsigned long page_size = getpagesize();
      unsigned long round;

      round = (unsigned long)elffile->plt & (page_size - 1);
      mprotect(elffile->plt - round,
             (elffile->pltsize + round + page_size - 1) & ~(page_size - 1),
             PROT_READ | PROT_WRITE | PROT_EXEC);
    }
#   endif
                                                            
    elffile->sections[elffile->pltndx].sh_size = elffile->pltsize;
# ifdef ELFDEBUG
    ELFDEBUG("ELFCreatePLT: PLT address %lx\n", elffile->plt);
# endif

    return;
}

static void
IA64InstallReloc(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(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 */
    unsigned long *dest64;
#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", (unsigned long)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=%p\t", (void *)dest32);
      ELFDEBUG("*dest32=%8.8x\t", (unsigned int)*dest32);
# endif
      *dest32 = symval + (*dest32); /* S + A */
# ifdef ELFDEBUG
      ELFDEBUG("*dest32=%8.8x\n", (unsigned int)*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=%p\t", secp);
      ELFDEBUG("symval=%lx\t", (unsigned long)symval);
      ELFDEBUG("dest32=%p\t", (void *)dest32);
      ELFDEBUG("*dest32=%8.8x\t", (unsigned int)*dest32);
# endif

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

# ifdef ELFDEBUG
      ELFDEBUG("*dest32=%8.8x\n", (unsigned int)*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;
      }

      case R_ALPHA_SREL32:
          {
            dest32 = (unsigned int *)(secp + rel->r_offset);
            symval += rel->r_addend;
            symval -= (unsigned long) dest32;
            if ((long)symval >= 0x80000000
                || (long)symval < -(long)0x80000000)
                FatalError("R_ALPHA_SREL32 overflow for %s: %lx\n",
                         ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info)),
                         symval);
            *dest32 = symval;
            break;
          }
 
    case R_ALPHA_BRSGP:
       {
          Elf_Sym *syms;
          int     Delta;
          
          dest32 = (unsigned int *)((secp + rel->r_offset) + rel->r_addend);

# ifdef ELFDEBUG
          ELFDEBUF("R_ALPHA_BRSGP %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
          syms = (Elf_Sym *) elffile->saddr[elffile->symndx];
        
          if (syms[ELF_R_SYM(rel->r_info)].st_other & 0x8)
            Delta = -4;
          else
              Delta = 4;
          
          symval -= (Elf_Addr) (((unsigned char *)dest32) + Delta);
          if (symval % 4) {
             ErrorF("R_ALPHA_BRSGP bad alignment of offset\n");
          }
          symval = symval >> 2;

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

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

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

# ifdef ELFDEBUG
          ELFDEBUG("*dest32=%8.8x\n", *dest32);
# endif
          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_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_GLOB_DAT:    /* 20 */
    case R_SPARC_64:          /* 32 */
      dest64 = (unsigned long *)(secp + rel->r_offset);
      symval += rel->r_addend;
      *dest64 = symval;
      break;

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

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

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

    case R_SPARC_WDISP30:     /*  7 */
      dest32 = (unsigned int *)(secp + rel->r_offset);
      symval += rel->r_addend;
      *dest32 = ((*dest32 & 0xc0000000) |
               (((symval - (Elf_Addr) dest32) >> 2) & 0x3fffffff));
      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 */
      dest64 = (unsigned long *)(secp + rel->r_offset);
      *dest64 = (unsigned long)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",
             (int)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];
    ELFRelocPtr reloc_head = NULL;
    ELFRelocPtr tmp;

    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) {
          Elf_Sym *syms = (Elf_Sym *) elffile->saddr[elffile->symndx];

          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(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",
             (unsigned long)syms[i].st_value,
             (unsigned long)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",
                   (unsigned long)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(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;
          if (!strcmp(name, ".stab.indexstr")) /* ignore more 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 %p size: %lx\n",
             name, elffile->saddr[i], (unsigned long)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)) {
#if defined(linux) || defined(__OpenBSD__) || defined(sun)
      case SHT_PROGBITS:
          {
            unsigned long page_size = getpagesize();
            unsigned long round;

            round = (unsigned long)elffile->lsection[j].saddr & (page_size -1);
            mprotect( (char *)elffile->lsection[j].saddr - round,
                   SecSize(i) + round, PROT_READ | PROT_WRITE | PROT_EXEC);
          }
#ifdef __ia64__
          {
            int k;
            for (k = 0; k < SecSize(i); k += 32)
                ia64_flush_cache(elffile->lsection[j].saddr+k);
            ia64_flush_cache(elffile->lsection[j].saddr+SecSize(i)-1);
          }
#endif
          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(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(__OpenBSD__) || defined(sun)
    {
      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 %p %s\n",
                 (void *)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(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
      ELFDEBUG("ResolveSymbols: "
             "file %p, sec %d, r_offset 0x%x, r_info 0x%p\n",
             (void *)p->file, p->secn, p->rel->r_offset,
             (void *)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(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(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;
}

Generated by  Doxygen 1.6.0   Back to index