3241 lines
		
	
	
		
			88 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			3241 lines
		
	
	
		
			88 KiB
		
	
	
	
		
			C
		
	
	
	
| /* $XdotOrg$ */
 | |
| /* $XFree86: xc/programs/Xserver/hw/xfree86/loader/elfloader.c,v 1.62 2003/11/06 18:38:13 tsi Exp $ */
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  * Copyright 1995-1998 by Metro Link, Inc.
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and its
 | |
|  * documentation for any purpose is hereby granted without fee, provided that
 | |
|  * the above copyright notice appear in all copies and that both that
 | |
|  * copyright notice and this permission notice appear in supporting
 | |
|  * documentation, and that the name of Metro Link, Inc. not be used in
 | |
|  * advertising or publicity pertaining to distribution of the software without
 | |
|  * specific, written prior permission.  Metro Link, Inc. makes no
 | |
|  * representations about the suitability of this software for any purpose.
 | |
|  *  It is provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * METRO LINK, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | |
|  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 | |
|  * EVENT SHALL METRO LINK, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
|  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 | |
|  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 | |
|  * PERFORMANCE OF THIS SOFTWARE.
 | |
|  */
 | |
| #include <sys/types.h>
 | |
| #ifndef __UNIXOS2__
 | |
| #include <sys/mman.h>
 | |
| #endif
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #ifdef __QNX__
 | |
| # include <fcntl.h>
 | |
| #else
 | |
| # include <sys/fcntl.h>
 | |
| #endif
 | |
| #include <sys/stat.h>
 | |
| #if defined(linux) && defined (__ia64__)
 | |
| #include <sys/mman.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef DBMALLOC
 | |
| # include <debug/malloc.h>
 | |
| # define Xalloc(size) malloc(size)
 | |
| # define Xcalloc(size) calloc(1,(size))
 | |
| # define Xfree(size) free(size)
 | |
| #endif
 | |
| 
 | |
| #include "Xos.h"
 | |
| #include "os.h"
 | |
| #include "elf.h"
 | |
| 
 | |
| #include "sym.h"
 | |
| #include "loader.h"
 | |
| 
 | |
| #include "compiler.h"
 | |
| 
 | |
| #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(__ia64__) || defined(__OpenBSD__)
 | |
| 	{
 | |
| 	    unsigned long page_size = getpagesize();
 | |
| 	    unsigned long round;
 | |
| 
 | |
| 	    round = (unsigned long)elffile->base & (page_size - 1);
 | |
| 	    mprotect(elffile->base - round,
 | |
| 		     (elffile->basesize + round + page_size -
 | |
| 		      1) & ~(page_size - 1),
 | |
| 		     PROT_READ | PROT_WRITE | PROT_EXEC);
 | |
| 	}
 | |
| #   endif
 | |
| #  else
 | |
| 	{
 | |
| 	    int oldbasesize = elffile->basesize;
 | |
| 
 | |
| 	    elffile->basesize += 8 + elffile->gotsize;
 | |
| 	    MMAP_ALIGN(elffile->basesize);
 | |
| 	    elffile->base = mremap(elffile->base, oldbasesize,
 | |
| 				   elffile->basesize, MREMAP_MAYMOVE);
 | |
| 	    if (elffile->base == NULL) {
 | |
| 		ErrorF("ELFCreateGOT() Unable to remap memory!!!!\n");
 | |
| 		return FALSE;
 | |
| 	    }
 | |
| 	}
 | |
| #  endif
 | |
| 
 | |
| 	elffile->baseptr =
 | |
| 		((long)elffile->base + (maxalign - 1)) & ~(maxalign - 1);
 | |
| 	elffile->got =
 | |
| 		(unsigned char
 | |
| 		 *)((long)(elffile->base + elffile->basesize -
 | |
| 			   elffile->gotsize) & ~7);
 | |
|     } else {
 | |
| 	gots = (ELFGotPtr) elffile->got;
 | |
| 	elffile->got = gots->section;
 | |
| 	gots->size = gotsize - sizeof(ELFGotRec) + 1;
 | |
| 	gots->nuses = 1;
 | |
| 	gots->freeptr = gots->section + elffile->gotsize;
 | |
| 	gots->next = ELFSharedGOTs;
 | |
| 	ELFSharedGOTs = gots;
 | |
| 	elffile->shared_got = gots;
 | |
| #  ifdef ELFDEBUG
 | |
| 	ELFDEBUG("ELFCreateGOT: Created a shareable GOT with size %d\n",
 | |
| 		 gots->size);
 | |
| #  endif
 | |
|     }
 | |
| # endif	/*MergeSectionAlloc */
 | |
| 
 | |
| # ifdef ELFDEBUG
 | |
|     ELFDEBUG("ELFCreateGOT: GOT address %lx\n", elffile->got);
 | |
| # endif
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| #endif /* defined(__alpha__) || defined(__ia64__) */
 | |
| 
 | |
| #if defined(__ia64__)
 | |
| /*
 | |
|  * Manage OPD Entries
 | |
|  */
 | |
| static void
 | |
| ElfAddOPD(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;
 | |
|     }
 | |
|     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;
 | |
| 	}
 | |
| 
 | |
| #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)) {
 | |
| #ifdef __OpenBSD__
 | |
| 	case SHT_PROGBITS:
 | |
| 	    mprotect(elffile->lsection[j].saddr, SecSize(i),
 | |
| 		     PROT_READ | PROT_WRITE | PROT_EXEC);
 | |
| 	    break;
 | |
| #endif
 | |
| 	case SHT_SYMTAB:
 | |
| 	    elffile->symtab = (Elf_Sym *) elffile->saddr[i];
 | |
| 	    elffile->symndx = i;
 | |
| 	    break;
 | |
| 	case SHT_STRTAB:
 | |
| 	    elffile->straddr = elffile->saddr[i];
 | |
| 	    elffile->strsize = elffile->lsection[j].size;
 | |
| 	    elffile->strndx = i;
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    break;
 | |
| 	}
 | |
| 	elffile->lsectidx = ++j;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Public API for the ELF implementation of the loader.
 | |
|  */
 | |
| void *
 | |
| ELFLoadModule(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, §table);
 | |
| 
 | |
| /*
 | |
|  * Do relocations
 | |
|  */
 | |
|     for (i = 0; i < elffile->lsectidx; i++) {
 | |
| 	switch (SecType(elffile->lsection[i].ndx)) {
 | |
| 	case SHT_REL:
 | |
| 	case SHT_RELA:
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    continue;
 | |
| 	}
 | |
| 	elf_reloc = ELFCollectRelocations(elffile, elffile->lsection[i].ndx);
 | |
| 	if (elf_reloc) {
 | |
| 	    for (tail = elf_reloc; tail->next; tail = tail->next) ;
 | |
| 	    tail->next = _LoaderGetRelocations(v)->elf_reloc;
 | |
| 	    _LoaderGetRelocations(v)->elf_reloc = elf_reloc;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
| #if defined(__ia64__)
 | |
|     totalsize += (elffile->sections[elffile->pltndx].sh_addralign - 1);
 | |
|     totalsize &= ~(elffile->sections[elffile->pltndx].sh_addralign - 1);
 | |
|     totalsize += elffile->pltsize;
 | |
|     if (maxalign < elffile->sections[elffile->pltndx].sh_addralign)
 | |
| 	maxalign = elffile->sections[elffile->pltndx].sh_addralign;
 | |
| #endif
 | |
| 
 | |
|     /* Space for COMMON */
 | |
|     totalsize = (totalsize + 7) & ~7;
 | |
|     totalsize += ElfCOMMONSize();
 | |
| 
 | |
| #ifdef MergeSectionAlloc
 | |
|     elffile->basesize = totalsize + maxalign;
 | |
| 
 | |
| # if !defined(DoMMAPedMerge)
 | |
|     elffile->base = xf86loadermalloc(elffile->basesize);
 | |
|     if (elffile->base == NULL) {
 | |
| 	ErrorF("Unable to allocate ELF sections\n");
 | |
| 	return NULL;
 | |
|     }
 | |
| #  if defined(linux) && defined(__ia64__) || defined(__OpenBSD__)
 | |
|     {
 | |
| 	unsigned long page_size = getpagesize();
 | |
| 	unsigned long round;
 | |
| 
 | |
| 	round = (unsigned long)elffile->base & (page_size - 1);
 | |
| 	mprotect(elffile->base - round,
 | |
| 		 (elffile->basesize + round + page_size - 1) & ~(page_size -
 | |
| 								 1),
 | |
| 		 PROT_READ | PROT_WRITE | PROT_EXEC);
 | |
|     }
 | |
| #  endif
 | |
| # else
 | |
|     MMAP_ALIGN(elffile->basesize);
 | |
|     elffile->base = mmap(0, elffile->basesize, MMAP_PROT, MMAP_FLAGS, -1,
 | |
| 			 (off_t) 0);
 | |
|     if (elffile->base == NULL) {
 | |
| 	ErrorF("Unable to mmap ELF sections\n");
 | |
| 	return NULL;
 | |
|     }
 | |
| # endif
 | |
|     elffile->baseptr =
 | |
| 	    ((long)elffile->base + (maxalign - 1)) & ~(maxalign - 1);
 | |
| #endif
 | |
| 
 | |
| #if defined(__alpha__) || defined(__ia64__)
 | |
|     if (!ELFCreateGOT(elffile, maxalign))
 | |
| 	return NULL;
 | |
| #endif
 | |
| #if defined(__ia64__)
 | |
|     ELFCreatePLT(elffile);
 | |
| #endif
 | |
| 
 | |
|     ELFCollectSections(elffile, 1, NULL, NULL);
 | |
| 
 | |
|     for (i = 0; pLookup[i].symName; i++)
 | |
| 	if (secttable[i]) {
 | |
| 	    pLookup[i].offset =
 | |
| 		    (funcptr) ((long)pLookup[i].offset +
 | |
| 			       (long)elffile->saddr[secttable[i]]);
 | |
| #ifdef ELFDEBUG
 | |
| 	    ELFDEBUG("Finalizing symbol %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;
 | |
| }
 |