/* $XdotOrg$ */ /* $XFree86: xc/programs/Xserver/hw/xfree86/loader/elfloader.c,v 1.61tsi Exp $ */ /* * * Copyright 1995-1998 by Metro Link, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Metro Link, Inc. not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Metro Link, Inc. makes no * representations about the suitability of this software for any purpose. * It is provided "as is" without express or implied warranty. * * METRO LINK, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL METRO LINK, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include #ifndef __UNIXOS2__ #include #endif #include #include #ifdef __QNX__ # include #else # include #endif #include #if defined(linux) && defined (__ia64__) #include #endif #ifdef DBMALLOC # include # 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 # define MergeSectionAlloc # define MMAP_PROT (PROT_READ | PROT_WRITE | PROT_EXEC) # if !defined(linux) # error No MAP_ANON? # endif # if !defined (__amd64__) || !defined(__linux__) # define MMAP_FLAGS (MAP_PRIVATE | MAP_ANON) # else # define MMAP_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_32BIT) # endif # if defined (MmapPageAlign) # define MMAP_ALIGN(size) do { \ int pagesize = getpagesize(); \ size = ( size + pagesize - 1) / pagesize; \ size *= pagesize; \ } while (0); # else # define MMAP_ALIGN(size) # endif #endif #if defined (__alpha__) || \ defined (__ia64__) || \ defined (__amd64__) || \ (defined (__sparc__) && \ (defined (__arch64__) || \ defined (__sparcv9))) typedef Elf64_Ehdr Elf_Ehdr; typedef Elf64_Shdr Elf_Shdr; typedef Elf64_Sym Elf_Sym; typedef Elf64_Rel Elf_Rel; typedef Elf64_Rela Elf_Rela; typedef Elf64_Addr Elf_Addr; typedef Elf64_Half Elf_Half; typedef Elf64_Off Elf_Off; typedef Elf64_Sword Elf_Sword; typedef Elf64_Word Elf_Word; #define ELF_ST_BIND ELF64_ST_BIND #define ELF_ST_TYPE ELF64_ST_TYPE #define ELF_R_SYM ELF64_R_SYM #define ELF_R_TYPE ELF64_R_TYPE # if defined (__alpha__) || defined (__ia64__) /* * The GOT is allocated dynamically. We need to keep a list of entries that * have already been added to the GOT. * */ typedef struct _elf_GOT_Entry { Elf_Rela *rel; int offset; struct _elf_GOT_Entry *next; } ELFGotEntryRec, *ELFGotEntryPtr; typedef struct _elf_GOT { unsigned int size; unsigned int nuses; unsigned char *freeptr; struct _elf_GOT *next; unsigned char section[1]; } ELFGotRec, *ELFGotPtr; # ifdef MergeSectionAlloc static ELFGotPtr ELFSharedGOTs; # endif # endif # if defined (__ia64__) /* * The PLT is allocated dynamically. We need to keep a list of entries that * have already been added to the PLT. */ typedef struct _elf_PLT_Entry { Elf_Rela *rel; int offset; int gotoffset; struct _elf_PLT_Entry *next; } ELFPltEntryRec, *ELFPltEntryPtr; /* * The OPD is allocated dynamically within the GOT. We need to keep a list * of entries that have already been added to the OPD. */ typedef struct _elf_OPD { LOOKUP *l; int index; int offset; struct _elf_OPD *next; } ELFOpdRec, *ELFOpdPtr; # endif #else typedef Elf32_Ehdr Elf_Ehdr; typedef Elf32_Shdr Elf_Shdr; typedef Elf32_Sym Elf_Sym; typedef Elf32_Rel Elf_Rel; typedef Elf32_Rela Elf_Rela; typedef Elf32_Addr Elf_Addr; typedef Elf32_Half Elf_Half; typedef Elf32_Off Elf_Off; typedef Elf32_Sword Elf_Sword; typedef Elf32_Word Elf_Word; #define ELF_ST_BIND ELF32_ST_BIND #define ELF_ST_TYPE ELF32_ST_TYPE #define ELF_R_SYM ELF32_R_SYM #define ELF_R_TYPE ELF32_R_TYPE #endif #if defined(__powerpc__) || \ defined(__mc68000__) || \ defined(__alpha__) || \ defined(__sparc__) || \ defined(__ia64__) || \ defined(__amd64__) typedef Elf_Rela Elf_Rel_t; #else typedef Elf_Rel Elf_Rel_t; #endif typedef struct { void *saddr; char *name; int ndx; int size; int flags; } LoadSection; #define RELOC_SECTION 0x1 #define LOADED_SECTION 0x2 /* * This structure contains all of the information about a module * that has been loaded. */ typedef struct { int handle; int module; int fd; loader_funcs *funcs; Elf_Ehdr *header; /* file header */ int numsh; Elf_Shdr *sections; /* Address of the section header table */ int secsize; /* size of the section table */ unsigned char **saddr; /* Start addresss of the section pointer table */ unsigned char *shstraddr; /* Start address of the section header string table */ int shstrndx; /* index of the section header string table */ int shstrsize; /* size of the section header string table */ #if defined(__alpha__) || defined(__ia64__) unsigned char *got; /* Start address of the .got section */ ELFGotEntryPtr got_entries; /* List of entries in the .got section */ int gotndx; /* index of the .got section */ int gotsize; /* actual size of the .got section */ ELFGotPtr shared_got; /* Pointer to ELFGotRec if shared */ #endif /*(__alpha__) || (__ia64__) */ #if defined(__ia64__) ELFOpdPtr opd_entries; /* List of entries in the .opd section */ unsigned char *plt; /* Start address of the .plt section */ ELFPltEntryPtr plt_entries; /* List of entries in the .plt section */ int pltndx; /* index of the .plt section */ int pltsize; /* size of the .plt section */ #endif /*__ia64__*/ Elf_Sym *symtab; /* Start address of the .symtab section */ int symndx; /* index of the .symtab section */ unsigned char *common; /* Start address of the SHN_COMMON space */ int comsize; /* size of the SHN_COMMON space */ unsigned char *base; /* Alloced address of section block */ unsigned long baseptr; /* Pointer to next free space in base */ int basesize; /* Size of that allocation */ unsigned char *straddr; /* Start address of the string table */ int strndx; /* index of the string table */ int strsize; /* size of the string table */ LoadSection *lsection; int lsectidx; } ELFModuleRec, *ELFModulePtr; /* * If a relocation is unable to be satisfied, then put it on a list * to try later after more modules have been loaded. */ typedef struct _elf_reloc { Elf_Rel_t *rel; ELFModulePtr file; Elf_Word secn; struct _elf_reloc *next; } ELFRelocRec; /* * symbols with a st_shndx of COMMON need to have space allocated for them. * * Gather all of these symbols together, and allocate one chunk when we * are done. */ typedef struct _elf_COMMON { Elf_Sym *sym; struct _elf_COMMON *next; } ELFCommonRec; static ELFCommonPtr listCOMMON = NULL; /* Prototypes for static functions */ static int ELFhashCleanOut(void *, itemPtr); static char *ElfGetStringIndex(ELFModulePtr, int, int); static char *ElfGetString(ELFModulePtr, int); static char *ElfGetSectionName(ELFModulePtr, int); static ELFRelocPtr ElfDelayRelocation(ELFModulePtr, Elf_Word, Elf_Rel_t *); static ELFCommonPtr ElfAddCOMMON(Elf_Sym *); static int ElfCOMMONSize(void); static int ElfCreateCOMMON(ELFModulePtr, LOOKUP *); static char *ElfGetSymbolNameIndex(ELFModulePtr, int, int); static char *ElfGetSymbolName(ELFModulePtr, int); static Elf_Addr ElfGetSymbolValue(ELFModulePtr, int); static ELFRelocPtr Elf_RelocateEntry(ELFModulePtr, Elf_Word, Elf_Rel_t *, int); static ELFRelocPtr ELFCollectRelocations(ELFModulePtr, int); static LOOKUP *ELF_GetSymbols(ELFModulePtr, unsigned short **); static void ELFCollectSections(ELFModulePtr, int, int *, int *); #if defined(__alpha__) || defined(__ia64__) static void ElfAddGOT(ELFModulePtr, Elf_Rel_t *); static int ELFCreateGOT(ELFModulePtr, int); #endif #if defined(__ia64__) static void ElfAddOPD(ELFModulePtr, int, LOOKUP *); static void ELFCreateOPD(ELFModulePtr); static void ElfAddPLT(ELFModulePtr, Elf_Rel_t *); static void ELFCreatePLT(ELFModulePtr); enum ia64_operand { IA64_OPND_IMM22, IA64_OPND_TGT25C, IA64_OPND_LDXMOV }; static void IA64InstallReloc(unsigned long *, int, enum ia64_operand, long); #endif /*__ia64__*/ #ifdef MergeSectionAlloc static void * ELFLoaderSectToMem(ELFModulePtr elffile, int align, unsigned long offset, int size, char *label) { void *ret; elffile->baseptr = (elffile->baseptr + align - 1) & ~(align - 1); ret = (void *)elffile->baseptr; _LoaderFileRead(elffile->fd, offset, ret, size); elffile->baseptr += size; return ret; } static void * ELFLoaderSectCalloc(ELFModulePtr elffile, int align, int size) { void *ret; elffile->baseptr = (elffile->baseptr + align - 1) & ~(align - 1); ret = (void *)elffile->baseptr; elffile->baseptr += size; #ifndef DoMMAPedMerge memset(ret, 0, size); /* mmap() does this for us */ #endif return ret; } #else /* MergeSectionAlloc */ # define ELFLoaderSectToMem(elffile,align,offset,size,label) \ _LoaderFileToMem((elffile)->fd,offset,size,label) # define ELFLoaderSectCalloc(elffile,align,size) xf86loadercalloc(1,size) #endif /* * Utility Functions */ static int ELFhashCleanOut(void *voidptr, itemPtr item) { ELFModulePtr module = (ELFModulePtr) voidptr; return (module->handle == item->handle); } /* * Manage listResolv */ static ELFRelocPtr ElfDelayRelocation(ELFModulePtr elffile, Elf_Word secn, Elf_Rel_t *rel) { ELFRelocPtr reloc; if ((reloc = xf86loadermalloc(sizeof(ELFRelocRec))) == NULL) { ErrorF("ElfDelayRelocation() Unable to allocate memory!!!!\n"); return 0; } reloc->file = elffile; reloc->secn = secn; reloc->rel = rel; reloc->next = 0; #ifdef ELFDEBUG ELFDEBUG("ElfDelayRelocation %p: file %p, sec %d," " r_offset 0x%lx, r_info 0x%x", (void *)reloc, (void *)elffile, secn, (unsigned long)rel->r_offset, rel->r_info); # if defined(__powerpc__) || \ defined(__mc68000__) || \ defined(__alpha__) || \ defined(__sparc__) || \ defined(__ia64__) || \ defined(__amd64__) ELFDEBUG(", r_addend 0x%lx", rel->r_addend); # endif ELFDEBUG("\n"); #endif return reloc; } /* * Manage listCOMMON */ static ELFCommonPtr ElfAddCOMMON(Elf_Sym *sym) { ELFCommonPtr common; if ((common = xf86loadermalloc(sizeof(ELFCommonRec))) == NULL) { ErrorF("ElfAddCOMMON() Unable to allocate memory!!!!\n"); return 0; } common->sym = sym; common->next = 0; return common; } static int ElfCOMMONSize(void) { int size = 0; ELFCommonPtr common; for (common = listCOMMON; common; common = common->next) { size += common->sym->st_size; #if defined(__alpha__) || \ defined(__ia64__) || \ defined(__amd64__) || \ (defined(__sparc__) && \ (defined(__arch64__) || \ defined(__sparcv9))) size = (size + 7) & ~0x7; #endif } return size; } static int ElfCreateCOMMON(ELFModulePtr elffile, LOOKUP *pLookup) { int numsyms = 0, size = 0, l = 0; int offset = 0, firstcommon = 0; ELFCommonPtr common; if (listCOMMON == NULL) return TRUE; for (common = listCOMMON; common; common = common->next) { size += common->sym->st_size; #if defined(__alpha__) || \ defined(__ia64__) || \ defined(__amd64__) || \ (defined(__sparc__) && \ (defined(__arch64__) || \ defined(__sparcv9))) size = (size + 7) & ~0x7; #endif numsyms++; } #ifdef ELFDEBUG ELFDEBUG("ElfCreateCOMMON() %d entries (%d bytes) of COMMON data\n", numsyms, size); #endif elffile->comsize = size; if ((elffile->common = ELFLoaderSectCalloc(elffile, 8, size)) == NULL) { ErrorF("ElfCreateCOMMON() Unable to allocate memory!!!!\n"); return FALSE; } if (DebuggerPresent) { ldrCommons = xf86loadermalloc(numsyms * sizeof(LDRCommon)); nCommons = numsyms; } for (l = 0; pLookup[l].symName; l++) ; firstcommon = l; /* Traverse the common list and create a lookup table with all the * common symbols. Destroy the common list in the process. * See also ResolveSymbols. */ while (listCOMMON) { common = listCOMMON; /* this is xstrdup because is should be more efficient. it is freed * with xf86loaderfree */ pLookup[l].symName = xf86loaderstrdup(ElfGetString(elffile, common->sym->st_name)); pLookup[l].offset = (funcptr) (elffile->common + offset); #ifdef ELFDEBUG ELFDEBUG("Adding common %p %s\n", (void *)pLookup[l].offset, pLookup[l].symName); #endif /* Record the symbol address for gdb */ if (DebuggerPresent && ldrCommons) { ldrCommons[l - firstcommon].addr = (void *)pLookup[l].offset; ldrCommons[l - firstcommon].name = pLookup[l].symName; ldrCommons[l - firstcommon].namelen = strlen(pLookup[l].symName); } listCOMMON = common->next; offset += common->sym->st_size; #if defined(__alpha__) || \ defined(__ia64__) || \ defined(__amd64__) || \ (defined(__sparc__) && \ (defined(__arch64__) || \ defined(__sparcv9))) offset = (offset + 7) & ~0x7; #endif xf86loaderfree(common); l++; } /* listCOMMON == 0 */ pLookup[l].symName = NULL; /* Terminate the list. */ return TRUE; } /* * String Table */ static char * ElfGetStringIndex(ELFModulePtr file, int offset, int index) { if (!offset || !index) return ""; return (char *)(file->saddr[index] + offset); } static char * ElfGetString(ELFModulePtr file, int offset) { return ElfGetStringIndex(file, offset, file->strndx); } static char * ElfGetSectionName(ELFModulePtr file, int offset) { return (char *)(file->shstraddr + offset); } /* * Symbol Table */ /* * Get symbol name */ static char * ElfGetSymbolNameIndex(ELFModulePtr elffile, int index, int secndx) { Elf_Sym *syms; #ifdef ELFDEBUG ELFDEBUG("ElfGetSymbolNameIndex(%x,%x) ", index, secndx); #endif syms = (Elf_Sym *) elffile->saddr[secndx]; #ifdef ELFDEBUG ELFDEBUG("%s ", ElfGetString(elffile, syms[index].st_name)); ELFDEBUG("%x %x ", ELF_ST_BIND(syms[index].st_info), ELF_ST_TYPE(syms[index].st_info)); ELFDEBUG("%lx\n", (unsigned long)syms[index].st_value); #endif return ElfGetString(elffile, syms[index].st_name); } static char * ElfGetSymbolName(ELFModulePtr elffile, int index) { return ElfGetSymbolNameIndex(elffile, index, elffile->symndx); } static Elf_Addr ElfGetSymbolValue(ELFModulePtr elffile, int index) { Elf_Sym *syms; Elf_Addr symval = 0; /* value of the indicated symbol */ char *symname = NULL; /* name of symbol in relocation */ itemPtr symbol = NULL; /* name/value of symbol */ syms = (Elf_Sym *) elffile->saddr[elffile->symndx]; switch (ELF_ST_TYPE(syms[index].st_info)) { case STT_NOTYPE: case STT_OBJECT: case STT_FUNC: switch (ELF_ST_BIND(syms[index].st_info)) { case STB_LOCAL: symval = (Elf_Addr) (elffile->saddr[syms[index].st_shndx] + syms[index].st_value); #ifdef __ia64__ if (ELF_ST_TYPE(syms[index].st_info) == STT_FUNC) { ELFOpdPtr opdent; for (opdent = elffile->opd_entries; opdent; opdent = opdent->next) if (opdent->index == index) break; if (opdent) { ((unsigned long *)(elffile->got + opdent->offset))[0] = symval; ((unsigned long *)(elffile->got + opdent->offset))[1] = (long)elffile->got; symval = (Elf_Addr) (elffile->got + opdent->offset); } } #endif break; case STB_GLOBAL: case STB_WEAK: /* STB_WEAK seems like a hack to cover for * some other problem */ symname = ElfGetString(elffile, syms[index].st_name); symbol = LoaderHashFind(symname); if (symbol == 0) { return 0; } symval = (Elf_Addr) symbol->address; break; default: symval = 0; ErrorF("ElfGetSymbolValue(), unhandled symbol scope %x\n", ELF_ST_BIND(syms[index].st_info)); break; } #ifdef ELFDEBUG ELFDEBUG("%p\t", (void *)symbol); ELFDEBUG("%lx\t", (unsigned long)symval); ELFDEBUG("%s\n", symname ? symname : "NULL"); #endif break; case STT_SECTION: symval = (Elf_Addr) elffile->saddr[syms[index].st_shndx]; #ifdef ELFDEBUG ELFDEBUG("ST_SECTION %lx\n", (unsigned long)symval); #endif break; case STT_FILE: case STT_LOPROC: case STT_HIPROC: default: symval = 0; ErrorF("ElfGetSymbolValue(), unhandled symbol type %x\n", ELF_ST_TYPE(syms[index].st_info)); break; } return symval; } #if defined(__powerpc__) /* * This function returns the address of a pseudo PLT routine which can * be used to compute a function offset. This is needed because loaded * modules have an offset from the .text section of greater than 24 bits. * The code generated makes the assumption that all function entry points * will be within a 24 bit offset (non-PIC code). */ static Elf_Addr ElfGetPltAddr(ELFModulePtr elffile, int index) { Elf_Sym *syms; Elf_Addr symval = 0; /* value of the indicated symbol */ char *symname = NULL; /* name of symbol in relocation */ itemPtr symbol; /* name/value of symbol */ syms = (Elf_Sym *) elffile->saddr[elffile->symndx]; switch (ELF_ST_TYPE(syms[index].st_info)) { case STT_NOTYPE: case STT_OBJECT: case STT_FUNC: switch (ELF_ST_BIND(syms[index].st_info)) { case STB_GLOBAL: symname = ElfGetString(elffile, syms[index].st_name); symbol = LoaderHashFind(symname); if (symbol == 0) return 0; /* * Here we are building up a pseudo Plt function that can make a call to * a function that has an offset greater than 24 bits. The following code * is being used to implement this. 1 00000000 .extern realfunc 2 00000000 .global pltfunc 3 00000000 pltfunc: 4 00000000 3d 80 00 00 lis r12,hi16(realfunc) 5 00000004 61 8c 00 00 ori r12,r12,lo16(realfunc) 6 00000008 7d 89 03 a6 mtctr r12 7 0000000c 4e 80 04 20 bctr */ symbol->code.plt[0] = 0x3d80; /* lis r12 */ symbol->code.plt[1] = (((Elf_Addr) symbol->address) & 0xffff0000) >> 16; symbol->code.plt[2] = 0x618c; /* ori r12,r12 */ symbol->code.plt[3] = (((Elf_Addr) symbol->address) & 0xffff); symbol->code.plt[4] = 0x7d89; /* mtcr r12 */ symbol->code.plt[5] = 0x03a6; symbol->code.plt[6] = 0x4e80; /* bctr */ symbol->code.plt[7] = 0x0420; symbol->address = (char *)&symbol->code.plt[0]; symval = (Elf_Addr) symbol->address; ppc_flush_icache(&symbol->code.plt[0]); ppc_flush_icache(&symbol->code.plt[6]); break; default: symval = 0; ErrorF("ElfGetPltAddr(), unhandled symbol scope %x\n", ELF_ST_BIND(syms[index].st_info)); break; } # ifdef ELFDEBUG ELFDEBUG("ElfGetPlt: symbol=%lx\t", symbol); ELFDEBUG("newval=%lx\t", symval); ELFDEBUG("name=\"%s\"\n", symname ? symname : "NULL"); # endif break; case STT_SECTION: case STT_FILE: case STT_LOPROC: case STT_HIPROC: default: symval = 0; ErrorF("ElfGetPltAddr(), Unexpected symbol type %x", ELF_ST_TYPE(syms[index].st_info)); ErrorF("for a Plt request\n"); break; } return symval; } #endif /* __powerpc__ */ #if defined(__alpha__) || defined(__ia64__) /* * Manage GOT Entries */ static void ElfAddGOT(ELFModulePtr elffile, Elf_Rel_t *rel) { ELFGotEntryPtr gotent; # ifdef ELFDEBUG { Elf_Sym *sym; sym = (Elf_Sym *) & (elffile->symtab[ELF_R_SYM(rel->r_info)]); if (sym->st_name) { ELFDEBUG("ElfAddGOT: Adding GOT entry for %s\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); } else ELFDEBUG("ElfAddGOT: Adding GOT entry for %s\n", ElfGetSectionName(elffile, elffile->sections[sym->st_shndx]. sh_name)); } # endif for (gotent = elffile->got_entries; gotent; gotent = gotent->next) { if (ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) && gotent->rel->r_addend == rel->r_addend) break; } if (gotent) { # ifdef ELFDEBUG ELFDEBUG("Entry already present in GOT\n"); # endif return; } if ((gotent = xf86loadermalloc(sizeof(ELFGotEntryRec))) == NULL) { ErrorF("ElfAddGOT() Unable to allocate memory!!!!\n"); return; } # ifdef ELFDEBUG ELFDEBUG("Entry added with offset %x\n", elffile->gotsize); # endif gotent->rel = rel; gotent->offset = elffile->gotsize; gotent->next = elffile->got_entries; elffile->got_entries = gotent; elffile->gotsize += 8; return; } static int ELFCreateGOT(ELFModulePtr elffile, int maxalign) { # ifdef MergeSectionAlloc ELFGotPtr gots; # endif int gotsize; /* * XXX: Is it REALLY needed to ensure GOT's are non-null? */ # ifdef ELFDEBUG ELFDEBUG("ELFCreateGOT: %x entries in the GOT\n", elffile->gotsize / 8); /* * Hmmm. Someone is getting here without any got entries, but they * may still have R_ALPHA_GPDISP relocations against the got. */ if (elffile->gotsize == 0) ELFDEBUG("Module %s doesn't have any GOT entries!\n", _LoaderModuleToName(elffile->module)); # endif if (elffile->gotsize == 0) elffile->gotsize = 8; elffile->sections[elffile->gotndx].sh_size = elffile->gotsize; gotsize = elffile->gotsize; # ifdef MergeSectionAlloc # ifdef __alpha__ # define GOTDistance 0x100000 # endif # ifdef __ia64__ # define GOTDistance 0x200000 # endif for (gots = ELFSharedGOTs; gots; gots = gots->next) { if (gots->freeptr + elffile->gotsize > gots->section + gots->size) continue; if (gots->section > elffile->base) { if (gots->section + gots->size - elffile->base >= GOTDistance) continue; } else { if (elffile->base + elffile->basesize - gots->section >= GOTDistance) continue; } elffile->got = gots->freeptr; elffile->shared_got = gots; gots->freeptr = gots->freeptr + elffile->gotsize; gots->nuses++; # ifdef ELFDEBUG ELFDEBUG("ELFCreateGOT: GOT address %lx in shared GOT, nuses %d\n", elffile->got, gots->nuses); # endif return TRUE; } gotsize += 16383 + sizeof(ELFGotRec); # endif /*MergeSectionAlloc */ if ((elffile->got = xf86loadermalloc(gotsize)) == NULL) { ErrorF("ELFCreateGOT() Unable to allocate memory!!!!\n"); return FALSE; } # ifdef MergeSectionAlloc if (elffile->got > elffile->base) { if (elffile->got + elffile->gotsize - elffile->base >= GOTDistance) gotsize = 0; } else { if (elffile->base + elffile->basesize - elffile->got >= GOTDistance) gotsize = 0; } if (!gotsize) { xf86loaderfree(elffile->got); # if !defined(DoMMAPedMerge) elffile->basesize += 8 + elffile->gotsize; elffile->base = xf86loaderrealloc(elffile->base, elffile->basesize); if (elffile->base == NULL) { ErrorF("ELFCreateGOT() Unable to reallocate memory!!!!\n"); return FALSE; } # if defined(linux) || defined(__OpenBSD__) { unsigned long page_size = getpagesize(); unsigned long round; round = (unsigned long)elffile->base & (page_size - 1); mprotect(elffile->base - round, (elffile->basesize + round + page_size - 1) & ~(page_size - 1), PROT_READ | PROT_WRITE | PROT_EXEC); } # endif # else { int oldbasesize = elffile->basesize; elffile->basesize += 8 + elffile->gotsize; MMAP_ALIGN(elffile->basesize); elffile->base = mremap(elffile->base, oldbasesize, elffile->basesize, MREMAP_MAYMOVE); if (elffile->base == NULL) { ErrorF("ELFCreateGOT() Unable to remap memory!!!!\n"); return FALSE; } } # endif elffile->baseptr = ((long)elffile->base + (maxalign - 1)) & ~(maxalign - 1); elffile->got = (unsigned char *)((long)(elffile->base + elffile->basesize - elffile->gotsize) & ~7); } else { gots = (ELFGotPtr) elffile->got; elffile->got = gots->section; gots->size = gotsize - sizeof(ELFGotRec) + 1; gots->nuses = 1; gots->freeptr = gots->section + elffile->gotsize; gots->next = ELFSharedGOTs; ELFSharedGOTs = gots; elffile->shared_got = gots; # ifdef ELFDEBUG ELFDEBUG("ELFCreateGOT: Created a shareable GOT with size %d\n", gots->size); # endif } # endif /*MergeSectionAlloc */ # ifdef ELFDEBUG ELFDEBUG("ELFCreateGOT: GOT address %lx\n", elffile->got); # endif return TRUE; } #endif /* defined(__alpha__) || defined(__ia64__) */ #if defined(__ia64__) /* * Manage OPD Entries */ static void ElfAddOPD(ELFModulePtr elffile, int index, LOOKUP *l) { ELFOpdPtr opdent; if (index != -1) { for (opdent = elffile->opd_entries; opdent; opdent = opdent->next) if (opdent->index == index) return; } if ((opdent = xf86loadermalloc(sizeof(ELFOpdRec))) == NULL) { ErrorF("ElfAddOPD() Unable to allocate memory!!!!\n"); return; } # ifdef ELFDEBUG ELFDEBUG("OPD Entry %d added with offset %x\n", index, elffile->gotsize); # endif opdent->l = l; opdent->index = index; opdent->offset = elffile->gotsize; opdent->next = elffile->opd_entries; elffile->opd_entries = opdent; elffile->gotsize += 16; return; } static void ELFCreateOPD(ELFModulePtr elffile) { ELFOpdPtr opdent; if (elffile->got == NULL) ErrorF("ELFCreateOPD() Unallocated GOT!!!!\n"); for (opdent = elffile->opd_entries; opdent; opdent = opdent->next) { if (opdent->index != -1) continue; ((unsigned long *)(elffile->got + opdent->offset))[0] = (long)opdent->l->offset; ((unsigned long *)(elffile->got + opdent->offset))[1] = (long)elffile->got; opdent->l->offset = (funcptr) (elffile->got + opdent->offset); } } /* * Manage PLT Entries */ static void ElfAddPLT(ELFModulePtr elffile, Elf_Rel_t *rel) { ELFPltEntryPtr pltent; # ifdef ELFDEBUG { Elf_Sym *sym; sym = (Elf_Sym *) & (elffile->symtab[ELF_R_SYM(rel->r_info)]); if (sym->st_name) { ELFDEBUG("ElfAddPLT: Adding PLT entry for %s\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); } else ErrorF("ElfAddPLT: Add PLT entry for section??\n"); } # endif if (rel->r_addend) ErrorF("ElfAddPLT: Add PLT entry with non-zero addend??\n"); for (pltent = elffile->plt_entries; pltent; pltent = pltent->next) { if (ELF_R_SYM(pltent->rel->r_info) == ELF_R_SYM(rel->r_info)) break; } if (pltent) { # ifdef ELFDEBUG ELFDEBUG("Entry already present in PLT\n"); # endif return; } if ((pltent = xf86loadermalloc(sizeof(ELFPltEntryRec))) == NULL) { ErrorF("ElfAddPLT() Unable to allocate memory!!!!\n"); return; } # ifdef ELFDEBUG ELFDEBUG("Entry added with offset %x\n", elffile->pltsize); # endif pltent->rel = rel; pltent->offset = elffile->pltsize; pltent->gotoffset = elffile->gotsize; pltent->next = elffile->plt_entries; elffile->plt_entries = pltent; elffile->pltsize += 32; elffile->gotsize += 16; return; } static void ELFCreatePLT(ELFModulePtr elffile) { # ifdef ELFDEBUG ELFDEBUG("ELFCreatePLT: %x entries in the PLT\n", elffile->pltsize / 8); # endif if (elffile->pltsize == 0) return; if ((elffile->plt = ELFLoaderSectCalloc(elffile, 32, elffile->pltsize)) == NULL) { ErrorF("ELFCreatePLT() Unable to allocate memory!!!!\n"); return; } # if defined(linux) || defined(__OpenBSD__) { unsigned long page_size = getpagesize(); unsigned long round; round = (unsigned long)elffile->plt & (page_size - 1); mprotect(elffile->plt - round, (elffile->pltsize + round + page_size - 1) & ~(page_size - 1), PROT_READ | PROT_WRITE | PROT_EXEC); } # endif elffile->sections[elffile->pltndx].sh_size = elffile->pltsize; # ifdef ELFDEBUG ELFDEBUG("ELFCreatePLT: PLT address %lx\n", elffile->plt); # endif return; } static void IA64InstallReloc(unsigned long *data128, int slot, enum ia64_operand opnd, long value) { unsigned long data = 0; # ifdef ELFDEBUG ELFDEBUG("\nIA64InstallReloc %p %d %d %016lx\n", data128, slot, opnd, value); ELFDEBUG("Before [%016lx%016lx]\n", data128[1], data128[0]); # endif switch (slot) { case 0: data = *data128; break; case 1: memcpy(&data, (char *)data128 + 5, 8); break; case 2: memcpy(&data, (char *)data128 + 10, 6); break; default: FatalError("Unexpected slot in IA64InstallReloc()\n"); } switch (opnd) { case IA64_OPND_IMM22: data &= ~(0x3fff9fc0000UL << slot); data |= (value & 0x7f) << (18 + slot); /* [13:19] + 5 + slot */ data |= (value & 0xff80) << (25 + slot); /* [27:35] + 5 + slot */ data |= (value & 0x1f0000) << (11 + slot); /* [22:26] + 5 + slot */ data |= (value & 0x200000) << (20 + slot); /* [36:36] + 5 + slot */ if (value << 42 >> 42 != value) ErrorF("Relocation %016lx truncated to fit into IMM22\n", value); break; case IA64_OPND_TGT25C: data &= ~(0x23ffffc0000UL << slot); data |= (value & 0xfffff0) << (14 + slot); /* [13:32] + 5 + slot */ data |= (value & 0x1000000) << (17 + slot); /* [36:36] + 5 + slot */ if (value << 39 >> 39 != value || (value & 0xf)) ErrorF("Relocation %016lx truncated to fit into TGT25C\n", value); break; #ifdef IA64_LDX_OPTIMIZATION case IA64_OPND_LDXMOV: /* * Convert "ld8 t2=[t1]" to "mov t2=t1" which is really "add t2=0,t1" * Mask all but the r3,r1,qp fields, * then OR in the ALU opcode = 8 into the opcode field [40:37] * * Mask for the r3,r1,qp bit fields [26:20][12:6][5:0] = 0x7f01fff, * This mask negated only within the 41 bit wide instruction and * shifted left by 5 for the bundle template is 0x3FFF01FC0000 * * opcode field [40:37] with a value of 8 is 0x10000000000 * shifted left by 5 for the bundle template is 0x200000000000 * */ data &= ~(0x3FFF01FC0000 << slot); data |= (0x200000000000 << slot); break; #endif default: FatalError("Unhandled operand in IA64InstallReloc()\n"); } switch (slot) { case 0: *data128 = data; break; case 1: memcpy((char *)data128 + 5, &data, 8); break; case 2: memcpy((char *)data128 + 10, &data, 6); break; default: FatalError("Unexpected slot in IA64InstallReloc()\n"); } ia64_flush_cache(data128); # ifdef ELFDEBUG ELFDEBUG("After [%016lx%016lx]\n", data128[1], data128[0]); # endif } #endif /*__ia64__*/ /* * Fix all of the relocations for the given section. * If the argument 'force' is non-zero, then the relocation will be * made even if the symbol can't be found (by substituting * LoaderDefaultFunc) otherwise, the relocation will be deferred. */ static ELFRelocPtr Elf_RelocateEntry(ELFModulePtr elffile, Elf_Word secn, Elf_Rel_t *rel, int force) { unsigned char *secp = elffile->saddr[secn]; #if !defined(__ia64__) unsigned int *dest32; /* address of the 32 bit place being modified */ #endif #if defined(__powerpc__) || defined(__sparc__) unsigned short *dest16; /* address of the 16 bit place being modified */ #endif #if defined(__sparc__) unsigned char *dest8; /* address of the 8 bit place being modified */ unsigned long *dest64; #endif #if defined(__alpha__) unsigned int *dest32h; /* address of the high 32 bit place being modified */ unsigned long *dest64; unsigned short *dest16; #endif #if defined(__amd64__) unsigned long *dest64; int *dest32s; #endif #if defined(__ia64__) unsigned long *dest64; unsigned long *dest128; #endif Elf_Addr symval = 0; /* value of the indicated symbol */ #ifdef ELFDEBUG ELFDEBUG("%lx %d %d\n", (unsigned long)rel->r_offset, ELF_R_SYM(rel->r_info), ELF_R_TYPE(rel->r_info)); # if defined(__powerpc__) || \ defined(__mc68000__) || \ defined(__alpha__) || \ defined(__sparc__) || \ defined(__ia64__) || \ defined(__amd64__) ELFDEBUG("%lx", rel->r_addend); # endif ELFDEBUG("\n"); #endif /*ELFDEBUG*/ #if defined(__alpha__) if (ELF_R_SYM(rel->r_info) && ELF_R_TYPE(rel->r_info) != R_ALPHA_GPDISP) #else if (ELF_R_SYM(rel->r_info)) #endif { symval = ElfGetSymbolValue(elffile, ELF_R_SYM(rel->r_info)); if (symval == 0) { if (force) { symval = (Elf_Addr) & LoaderDefaultFunc; } else { #ifdef ELFDEBUG ELFDEBUG("***Unable to resolve symbol %s\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); #endif return ElfDelayRelocation(elffile, secn, rel); } } } switch (ELF_R_TYPE(rel->r_info)) { #if defined(i386) case R_386_32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_386_32\t"); ELFDEBUG("dest32=%p\t", (void *)dest32); ELFDEBUG("*dest32=%8.8x\t", (unsigned int)*dest32); # endif *dest32 = symval + (*dest32); /* S + A */ # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", (unsigned int)*dest32); # endif break; case R_386_PC32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_386_PC32 %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%p\t", secp); ELFDEBUG("symval=%lx\t", (unsigned long)symval); ELFDEBUG("dest32=%p\t", (void *)dest32); ELFDEBUG("*dest32=%8.8x\t", (unsigned int)*dest32); # endif *dest32 = symval + (*dest32) - (Elf_Addr) dest32; /* S + A - P */ # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", (unsigned int)*dest32); # endif break; #endif /* i386 */ #if defined(__amd64__) case R_X86_64_32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_X86_32\t"); ELFDEBUG("dest32=%x\t", dest32); ELFDEBUG("*dest32=%8.8lx\t", *dest32); ELFDEBUG("r_addend=%lx\t", rel->r_addend); # endif *dest32 = symval + rel->r_addend + (*dest32); /* S + A */ # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8lx\n", *dest32); # endif break; case R_X86_64_32S: dest32s = (int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_X86_64_32\t"); ELFDEBUG("dest32s=%x\t", dest32s); ELFDEBUG("*dest32s=%8.8lx\t", *dest32s); ELFDEBUG("r_addend=%lx\t", rel->r_addend); # endif *dest32s = symval + rel->r_addend + (*dest32s); /* S + A */ # ifdef ELFDEBUG ELFDEBUG("*dest32s=%8.8lx\n", *dest32s); # endif break; case R_X86_64_PC32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_X86_64_PC32 %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest32=%x\t", dest32); ELFDEBUG("*dest32=%8.8lx\t", *dest32); ELFDEBUG("r_addend=%lx\t", rel->r_addend); # endif *dest32 = symval + rel->r_addend + (*dest32) - (Elf_Addr) dest32; /* S + A - P */ # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8lx\n", *dest32); # endif break; case R_X86_64_64: dest64 = (unsigned long *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_AMD64_64\t"); ELFDEBUG("dest64=%x\t", dest64); ELFDEBUG("*dest64=%8.8lx\t", *dest64); ELFDEBUG("r_addend=%lx\t", rel->r_addend); # endif *dest64 = symval + rel->r_addend + (*dest64); /* S + A */ # ifdef ELFDEBUG ELFDEBUG("*dest64=%8.8lx\n", *dest64); # endif break; #endif /* __amd64__ */ #if defined(__alpha__) case R_ALPHA_NONE: case R_ALPHA_LITUSE: break; case R_ALPHA_REFQUAD: dest64 = (unsigned long *)(secp + rel->r_offset); symval = ElfGetSymbolValue(elffile, ELF_R_SYM(rel->r_info)); # ifdef ELFDEBUG ELFDEBUG("R_ALPHA_REFQUAD\t"); ELFDEBUG("dest64=%lx\t", dest64); ELFDEBUG("*dest64=%8.8lx\t", *dest64); # endif *dest64 = symval + rel->r_addend + (*dest64); /* S + A + P */ # ifdef ELFDEBUG ELFDEBUG("*dest64=%8.8lx\n", *dest64); # endif break; case R_ALPHA_GPREL32: { dest64 = (unsigned long *)(secp + rel->r_offset); dest32 = (unsigned int *)dest64; # ifdef ELFDEBUG ELFDEBUG("R_ALPHA_GPREL32 %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest32=%lx\t", dest32); ELFDEBUG("*dest32=%8.8x\t", *dest32); # endif symval += rel->r_addend; symval = ((unsigned char *)symval) - ((unsigned char *)elffile->got); # ifdef ELFDEBUG ELFDEBUG("symval=%lx\t", symval); # endif if ((symval & 0xffffffff00000000) != 0x0000000000000000 && (symval & 0xffffffff00000000) != 0xffffffff00000000) { FatalError("R_ALPHA_GPREL32 symval-got is too large for %s\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); } *dest32 = symval; # ifdef ELFDEBUG ELFDEBUG("*dest32=%x\n", *dest32); # endif break; } case R_ALPHA_GPRELLOW: { dest64 = (unsigned long *)(secp + rel->r_offset); dest16 = (unsigned short *)dest64; symval += rel->r_addend; symval = ((unsigned char *)symval) - ((unsigned char *)elffile->got); *dest16 = symval; break; } case R_ALPHA_GPRELHIGH: { dest64 = (unsigned long *)(secp + rel->r_offset); dest16 = (unsigned short *)dest64; symval += rel->r_addend; symval = ((unsigned char *)symval) - ((unsigned char *)elffile->got); symval = ((long)symval >> 16) + ((symval >> 15) & 1); if ((long)symval > 0x7fff || (long)symval < -(long)0x8000) { FatalError ("R_ALPHA_GPRELHIGH symval-got is too large for %s:%lx\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info)), symval); } *dest16 = symval; break; } case R_ALPHA_LITERAL: { ELFGotEntryPtr gotent; dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_ALPHA_LITERAL %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest32=%lx\t", dest32); ELFDEBUG("*dest32=%8.8x\t", *dest32); # endif for (gotent = elffile->got_entries; gotent; gotent = gotent->next) { if (ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) && gotent->rel->r_addend == rel->r_addend) break; } /* Set the address in the GOT */ if (gotent) { *(unsigned long *)(elffile->got + gotent->offset) = symval + rel->r_addend; # ifdef ELFDEBUG ELFDEBUG("Setting gotent[%x]=%lx\t", gotent->offset, symval + rel->r_addend); # endif if ((gotent->offset & 0xffff0000) != 0) FatalError("\nR_ALPHA_LITERAL offset %x too large\n", gotent->offset); (*dest32) |= (gotent->offset); /* The address part is always 0 */ } else { unsigned long val; /* S + A - P >> 2 */ val = ((symval + (rel->r_addend) - (Elf_Addr) dest32)); # ifdef ELFDEBUG ELFDEBUG("S+A-P=%x\t", val); # endif if ((val & 0xffff0000) != 0xffff0000 && (val & 0xffff0000) != 0x00000000) { ErrorF("\nR_ALPHA_LITERAL offset %x too large\n", val); break; } val &= 0x0000ffff; (*dest32) |= (val); /* The address part is always 0 */ } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; } case R_ALPHA_GPDISP: { long offset; dest32h = (unsigned int *)(secp + rel->r_offset); dest32 = (unsigned int *)((secp + rel->r_offset) + rel->r_addend); # ifdef ELFDEBUG ELFDEBUG("R_ALPHA_GPDISP %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("got=%lx\t", elffile->got); ELFDEBUG("dest32=%lx\t", dest32); ELFDEBUG("*dest32=%8.8x\t", *dest32); ELFDEBUG("dest32h=%lx\t", dest32h); ELFDEBUG("*dest32h=%8.8x\t", *dest32h); # endif if ((*dest32h >> 26) != 9 || (*dest32 >> 26) != 8) { ErrorF("***Bad instructions in relocating %s\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); } symval = (*dest32h & 0xffff) << 16 | (*dest32 & 0xffff); symval = (symval ^ 0x80008000) - 0x80008000; offset = ((unsigned char *)elffile->got - (unsigned char *)dest32h); # ifdef ELFDEBUG ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("got-dest32=%lx\t", offset); # endif if ((offset >= 0x7fff8000L) || (offset < -0x80000000L)) { FatalError("Offset overflow for R_ALPHA_GPDISP\n"); } symval += (unsigned long)offset; # ifdef ELFDEBUG ELFDEBUG("symval=%lx\t", symval); # endif *dest32 = (*dest32 & 0xffff0000) | (symval & 0xffff); *dest32h = (*dest32h & 0xffff0000) | (((symval >> 16) + ((symval >> 15) & 1)) & 0xffff); # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\t", *dest32); ELFDEBUG("*dest32h=%8.8x\n", *dest32h); # endif break; } case R_ALPHA_HINT: dest32 = (unsigned int *)((secp + rel->r_offset) + rel->r_addend); # ifdef ELFDEBUG ELFDEBUG("R_ALPHA_HINT %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest32=%lx\t", dest32); ELFDEBUG("*dest32=%8.8x\t", *dest32); # endif # ifdef ELFDEBUG ELFDEBUG("symval=%lx\t", symval); # endif symval -= (Elf_Addr) (((unsigned char *)dest32) + 4); if (symval % 4) { ErrorF("R_ALPHA_HINT bad alignment of offset\n"); } symval = symval >> 2; # ifdef ELFDEBUG ELFDEBUG("symval=%lx\t", symval); # endif if (symval & 0xffff8000) { # ifdef ELFDEBUG ELFDEBUG("R_ALPHA_HINT symval too large\n"); # endif } *dest32 = (*dest32 & ~0x3fff) | (symval & 0x3fff); # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_ALPHA_GPREL16: { dest64 = (unsigned long *)(secp + rel->r_offset); dest16 = (unsigned short *)dest64; symval += rel->r_addend; symval = ((unsigned char *)symval) - ((unsigned char *)elffile->got); if ((long)symval > 0x7fff || (long)symval < -(long)0x8000) { FatalError ("R_ALPHA_GPREL16 symval-got is too large for %s:%lx\n", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info)), symval); } *dest16 = symval; break; } #endif /* alpha */ #if defined(__mc68000__) case R_68K_32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_68K_32\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned long val; /* S + A */ val = symval + (rel->r_addend); # ifdef ELFDEBUG ELFDEBUG("S+A=%x\t", val); # endif *dest32 = val; /* S + A */ } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_68K_PC32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_68K_PC32\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned long val; /* S + A - P */ val = symval + (rel->r_addend); val -= *dest32; # ifdef ELFDEBUG ELFDEBUG("S+A=%x\t", val); ELFDEBUG("S+A-P=%x\t", val + (*dest32) - (Elf_Addr) dest32); # endif *dest32 = val + (*dest32) - (Elf_Addr) dest32; /* S + A - P */ } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; #endif /* __mc68000__ */ #if defined(__powerpc__) # if defined(PowerMAX_OS) case R_PPC_DISP24: /* 11 */ dest32 = (unsigned long *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_PPC_DISP24 %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("dest32=%x\t", dest32); ELFDEBUG("*dest32=%8.8x\t", *dest32); # endif { unsigned long val; /* S + A - P >> 2 */ val = ((symval + (rel->r_addend) - (Elf_Addr) dest32)); # ifdef ELFDEBUG ELFDEBUG("S+A-P=%x\t", val); # endif val = val >> 2; if ((val & 0x3f000000) != 0x3f000000 && (val & 0x3f000000) != 0x00000000) { # ifdef ELFDEBUG ELFDEBUG("R_PPC_DISP24 offset %x too large\n", val << 2); # endif symval = ElfGetPltAddr(elffile, ELF_R_SYM(rel->r_info)); val = ((symval + (rel->r_addend) - (Elf_Addr) dest32)); # ifdef ELFDEBUG ELFDEBUG("PLT offset is %x\n", val); # endif val = val >> 2; if ((val & 0x3f000000) != 0x3f000000 && (val & 0x3f000000) != 0x00000000) FatalError("R_PPC_DISP24 PLT offset %x too large\n", val << 2); } val &= 0x00ffffff; (*dest32) |= (val << 2); /* The address part is always 0 */ ppc_flush_icache(dest32); } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_16HU: /* 31 */ dest16 = (unsigned short *)(secp + rel->r_offset); # ifdef ELFDEBUG dest32 = (unsigned long *)(dest16 - 1); # endif # ifdef ELFDEBUG ELFDEBUG("R_PPC_16HU\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest16=%x\t", dest16); ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned short val; /* S + A */ val = ((symval + (rel->r_addend)) & 0xffff0000) >> 16; # ifdef ELFDEBUG ELFDEBUG("uhi16(S+A)=%x\t", val); # endif *dest16 = val; /* S + A */ ppc_flush_icache(dest16); } # ifdef ELFDEBUG ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_32: /* 32 */ dest32 = (unsigned long *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_PPC_32\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned long val; /* S + A */ val = symval + (rel->r_addend); # ifdef ELFDEBUG ELFDEBUG("S+A=%x\t", val); # endif *dest32 = val; /* S + A */ ppc_flush_icache(dest32); } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_32UA: /* 33 */ dest32 = (unsigned long *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_PPC_32UA\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned long val; unsigned char *dest8 = (unsigned char *)dest32; /* S + A */ val = symval + (rel->r_addend); # ifdef ELFDEBUG ELFDEBUG("S+A=%x\t", val); # endif *dest8++ = (val & 0xff000000) >> 24; *dest8++ = (val & 0x00ff0000) >> 16; *dest8++ = (val & 0x0000ff00) >> 8; *dest8++ = (val & 0x000000ff); ppc_flush_icache(dest32); } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_16H: /* 34 */ dest16 = (unsigned short *)(secp + rel->r_offset); # ifdef ELFDEBUG dest32 = (unsigned long *)(dest16 - 1); # endif # ifdef ELFDEBUG ELFDEBUG("R_PPC_16H\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symbol=%s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest16=%x\t", dest16); ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned short val; unsigned short loval; /* S + A */ val = ((symval + (rel->r_addend)) & 0xffff0000) >> 16; loval = (symval + (rel->r_addend)) & 0xffff; if (loval & 0x8000) { /* * This is hi16(), instead of uhi16(). Because of this, * if the lo16() will produce a negative offset, then * we have to increment this part of the address to get * the correct final result. */ val++; } # ifdef ELFDEBUG ELFDEBUG("hi16(S+A)=%x\t", val); # endif *dest16 = val; /* S + A */ ppc_flush_icache(dest16); } # ifdef ELFDEBUG ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_16L: /* 35 */ dest16 = (unsigned short *)(secp + rel->r_offset); # ifdef ELFDEBUG dest32 = (unsigned long *)(dest16 - 1); # endif # ifdef ELFDEBUG ELFDEBUG("R_PPC_16L\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest16=%x\t", dest16); ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned short val; /* S + A */ val = (symval + (rel->r_addend)) & 0xffff; # ifdef ELFDEBUG ELFDEBUG("lo16(S+A)=%x\t", val); # endif *dest16 = val; /* S + A */ ppc_flush_icache(dest16); } # ifdef ELFDEBUG ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; # else /* PowerMAX_OS */ /* Linux PPC */ case R_PPC_ADDR32: /* 1 */ dest32 = (unsigned int *)(secp + rel->r_offset); symval = ElfGetSymbolValue(elffile, ELF_R_SYM(rel->r_info)); # ifdef ELFDEBUG ELFDEBUG("R_PPC_ADDR32\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned long val; /* S + A */ val = symval + (rel->r_addend); # ifdef ELFDEBUG ELFDEBUG("S+A=%x\t", val); # endif *dest32 = val; /* S + A */ ppc_flush_icache(dest32); } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_ADDR16_LO: /* 4 */ dest16 = (unsigned short *)(secp + rel->r_offset); # ifdef ELFDEBUG dest32 = (unsigned long *)(dest16 - 1); # endif # ifdef ELFDEBUG ELFDEBUG("R_PPC_ADDR16_LO\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest16=%x\t", dest16); ELFDEBUG("*dest16=%8.8x\t", *dest16); # endif { unsigned short val; /* S + A */ val = (symval + (rel->r_addend)) & 0xffff; # ifdef ELFDEBUG ELFDEBUG("lo16(S+A)=%x\t", val); # endif *dest16 = val; /* S + A */ ppc_flush_icache(dest16); } # ifdef ELFDEBUG ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_ADDR16_HA: /* 6 */ dest16 = (unsigned short *)(secp + rel->r_offset); # ifdef ELFDEBUG dest32 = (unsigned long *)(dest16 - 1); # endif # ifdef ELFDEBUG ELFDEBUG("R_PPC_ADDR16_HA\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest16=%x\t", dest16); ELFDEBUG("*dest16=%8.8x\t", *dest16); # endif { unsigned short val; unsigned short loval; /* S + A */ val = ((symval + (rel->r_addend)) & 0xffff0000) >> 16; loval = (symval + (rel->r_addend)) & 0xffff; if (loval & 0x8000) { /* * This is hi16(), instead of uhi16(). Because of this, * if the lo16() will produce a negative offset, then * we have to increment this part of the address to get * the correct final result. */ val++; } # ifdef ELFDEBUG ELFDEBUG("hi16(S+A)=%x\t", val); # endif *dest16 = val; /* S + A */ ppc_flush_icache(dest16); } # ifdef ELFDEBUG ELFDEBUG("*dest16=%8.8x\t", *dest16); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_REL24: /* 10 */ dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_PPC_REL24 %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("dest32=%x\t", dest32); ELFDEBUG("*dest32=%8.8x\t", *dest32); # endif { unsigned long val; /* S + A - P >> 2 */ val = ((symval + (rel->r_addend) - (Elf_Addr) dest32)); # ifdef ELFDEBUG ELFDEBUG("S+A-P=%x\t", val); # endif val = val >> 2; if ((val & 0x3f000000) != 0x3f000000 && (val & 0x3f000000) != 0x00000000) { # ifdef ELFDEBUG ELFDEBUG("R_PPC_REL24 offset %x too large\n", val << 2); # endif symval = ElfGetPltAddr(elffile, ELF_R_SYM(rel->r_info)); val = ((symval + (rel->r_addend) - (Elf_Addr) dest32)); # ifdef ELFDEBUG ELFDEBUG("PLT offset is %x\n", val); # endif val = val >> 2; if ((val & 0x3f000000) != 0x3f000000 && (val & 0x3f000000) != 0x00000000) FatalError("R_PPC_REL24 PLT offset %x too large\n", val << 2); } val &= 0x00ffffff; (*dest32) |= (val << 2); /* The address part is always 0 */ ppc_flush_icache(dest32); } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; case R_PPC_REL32: /* 26 */ dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_PPC_REL32\t"); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%x\t", symval); ELFDEBUG("r_addend=%x\t", rel->r_addend); ELFDEBUG("dest32=%8.8x\t", dest32); ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif { unsigned long val; /* S + A - P */ val = symval + (rel->r_addend); val -= *dest32; # ifdef ELFDEBUG ELFDEBUG("S+A=%x\t", val); ELFDEBUG("S+A-P=%x\t", val + (*dest32) - (Elf_Addr) dest32); # endif *dest32 = val + (*dest32) - (Elf_Addr) dest32; /* S + A - P */ ppc_flush_icache(dest32); } # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8x\n", *dest32); # endif break; # endif /* PowerMAX_OS */ #endif /* __powerpc__ */ #ifdef __sparc__ case R_SPARC_NONE: /* 0 */ break; case R_SPARC_8: /* 1 */ dest8 = (unsigned char *)(secp + rel->r_offset); symval += rel->r_addend; *dest8 = symval; break; case R_SPARC_16: /* 2 */ dest16 = (unsigned short *)(secp + rel->r_offset); symval += rel->r_addend; *dest16 = symval; break; case R_SPARC_32: /* 3 */ case R_SPARC_UA32: /* 23 */ dest32 = (unsigned int *)(secp + rel->r_offset); symval += rel->r_addend; ((unsigned char *)dest32)[0] = (unsigned char)(symval >> 24); ((unsigned char *)dest32)[1] = (unsigned char)(symval >> 16); ((unsigned char *)dest32)[2] = (unsigned char)(symval >> 8); ((unsigned char *)dest32)[3] = (unsigned char)(symval); break; case R_SPARC_GLOB_DAT: /* 20 */ case R_SPARC_64: /* 32 */ dest64 = (unsigned long *)(secp + rel->r_offset); symval += rel->r_addend; *dest64 = symval; break; case R_SPARC_DISP8: /* 4 */ dest8 = (unsigned char *)(secp + rel->r_offset); symval += rel->r_addend; *dest8 = (symval - (Elf_Addr) dest8); break; case R_SPARC_DISP16: /* 5 */ dest16 = (unsigned short *)(secp + rel->r_offset); symval += rel->r_addend; *dest16 = (symval - (Elf_Addr) dest16); break; case R_SPARC_DISP32: /* 6 */ dest32 = (unsigned int *)(secp + rel->r_offset); symval += rel->r_addend; *dest32 = (symval - (Elf_Addr) dest32); break; case R_SPARC_WDISP30: /* 7 */ dest32 = (unsigned int *)(secp + rel->r_offset); symval += rel->r_addend; *dest32 = ((*dest32 & 0xc0000000) | (((symval - (Elf_Addr) dest32) >> 2) & 0x3fffffff)); break; case R_SPARC_HI22: /* 9 */ dest32 = (unsigned int *)(secp + rel->r_offset); symval += rel->r_addend; *dest32 = (*dest32 & 0xffc00000) | (symval >> 10); break; case R_SPARC_LO10: /* 12 */ dest32 = (unsigned int *)(secp + rel->r_offset); symval += rel->r_addend; *dest32 = (*dest32 & ~0x3ff) | (symval & 0x3ff); break; case R_SPARC_COPY: /* 19 */ /* Fix your code... I'd rather dish out an error here * so people will not link together PIC and non-PIC * code into a final driver object file. */ ErrorF("Elf_RelocateEntry():" " Copy relocs not supported on Sparc.\n"); break; case R_SPARC_JMP_SLOT: /* 21 */ dest32 = (unsigned int *)(secp + rel->r_offset); /* Before we change it the PLT entry looks like: * * pltent: sethi %hi(rela_plt_offset), %g1 * b,a PLT0 * nop * * We change it into: * * pltent: sethi %hi(rela_plt_offset), %g1 * sethi %hi(symval), %g1 * jmp %g1 + %lo(symval), %g0 */ symval += rel->r_addend; dest32[2] = 0x81c06000 | (symval & 0x3ff); __asm __volatile("flush %0 + 0x8"::"r"(dest32)); dest32[1] = 0x03000000 | (symval >> 10); __asm __volatile("flush %0 + 0x4"::"r"(dest32)); break; case R_SPARC_RELATIVE: /* 22 */ dest64 = (unsigned long *)(secp + rel->r_offset); *dest64 = (unsigned long)secp + rel->r_addend; break; #endif /*__sparc__*/ #ifdef __ia64__ case R_IA64_NONE: break; case R_IA64_LTOFF_FPTR22: if (rel->r_addend) FatalError("\nAddend for R_IA64_LTOFF_FPTR22 not supported\n"); # ifdef ELFDEBUG ELFDEBUG("opd=%016lx.%016lx\n", ((long *)symval)[0], ((long *)symval)[1]); # endif /* FALLTHROUGH */ case R_IA64_LTOFF22: #ifndef IA64_LDX_OPTIMIZATION case R_IA64_LTOFF22X: /* If not implementing LDXMOV optimization treat LTOFF22X as LTOFF22 */ #endif { ELFGotEntryPtr gotent; dest128 = (unsigned long *)(secp + (rel->r_offset & ~3)); # ifdef ELFDEBUG ELFDEBUG("%s %s\t", ELF_R_TYPE(rel->r_info) == R_IA64_LTOFF22 ? "R_IA64_LTOFF22" : "R_IA64_LTOFF_FPTR22", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest128=%lx\t", dest128); ELFDEBUG("slot=%d\n", rel->r_offset & 3); ELFDEBUG("*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]); # endif for (gotent = elffile->got_entries; gotent; gotent = gotent->next) { if (ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) && gotent->rel->r_addend == rel->r_addend) break; } /* Set the address in the GOT */ if (gotent) { *(unsigned long *)(elffile->got + gotent->offset) = symval + rel->r_addend; # ifdef ELFDEBUG ELFDEBUG("Setting gotent[%x]=%lx\n", gotent->offset, symval + rel->r_addend); # endif if ((gotent->offset & 0xffe00000) != 0) FatalError("\nR_IA64_LTOFF22 offset %x too large\n", gotent->offset); IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_IMM22, gotent->offset); } else FatalError("\nCould not find GOT entry\n"); } break; case R_IA64_PCREL21B: { ELFPltEntryPtr pltent; dest128 = (unsigned long *)(secp + (rel->r_offset & ~3)); # ifdef ELFDEBUG ELFDEBUG("R_IA64_PCREL21B %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("opd=%lx.%lx\t", ((long *)symval)[0], ((long *)symval)[1]); ELFDEBUG("dest128=%lx\t", dest128); ELFDEBUG("slot=%d\n", rel->r_offset & 3); ELFDEBUG("*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]); # endif if (rel->r_addend) FatalError("\nAddend for PCREL21B not supported\n"); if (((long *)symval)[1] == (long)elffile->got && (((unsigned long)dest128 - ((unsigned long *)symval)[0]) + 0x2000000 < 0x4000000)) { /* We can save the travel through PLT */ IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_TGT25C, ((unsigned long *)symval)[0] - (unsigned long)dest128); break; } for (pltent = elffile->plt_entries; pltent; pltent = pltent->next) { if (ELF_R_SYM(pltent->rel->r_info) == ELF_R_SYM(rel->r_info) && pltent->rel->r_addend == rel->r_addend) break; } /* Set the address in the PLT */ if (pltent == NULL) FatalError("\nCould not find PLT entry\n"); else { unsigned long *p = (unsigned long *)(elffile->plt + pltent->offset); unsigned long r = (unsigned long)symval - (unsigned long)elffile->got; if (r + 0x200000 >= 0x400000) { /* Too far from gp to use the official function descriptor, * so we have to make a local one. */ r = pltent->gotoffset; memcpy(elffile->got + r, (char *)symval, 16); } /* [MMI] addl r15=NNN,r1;; ld8 r16=[r15],8; mov r14=r1;; */ p[0] = 0x410024000200780bUL; p[1] = 0x84000801c028303cUL; /* [MIB] ld8 r1=[r15]; mov b6=r16; br.few b6;; */ p[2] = 0x806010181e000811UL; p[3] = 0x0080006000038004UL; IA64InstallReloc(p, 0, IA64_OPND_IMM22, r); IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_TGT25C, (unsigned long)p - (unsigned long)dest128); } } break; case R_IA64_FPTR64LSB: dest64 = (unsigned long *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_IA64_FPTR64LSB %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest64=%lx\t", dest64); ELFDEBUG("opd=%016lx.%016lx\n", ((long *)symval)[0], ((long *)symval)[1]); # endif if (rel->r_addend) FatalError("\nAddend not supported for R_IA64_FPTR64LSB\n"); *dest64 = symval; ia64_flush_cache(dest64); break; case R_IA64_DIR64LSB: dest64 = (unsigned long *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_IA64_DIR64LSB %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest64=%lx\n", dest64); # endif *dest64 = symval + rel->r_addend; ia64_flush_cache(dest64); break; case R_IA64_PCREL64LSB: dest64 = (unsigned long *)(secp + rel->r_offset); #ifdef ELFDEBUG ELFDEBUG("R_IA64_PCREL64LSB %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest64=%lx\n", dest64); #endif *dest64 = symval + rel->r_addend - (unsigned long)dest64; break; case R_IA64_GPREL22: dest128 = (unsigned long *)(secp + (rel->r_offset & ~3)); # ifdef ELFDEBUG ELFDEBUG("R_IA64_GPREL22 %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest128=%lx\t", dest128); ELFDEBUG("slot=%d\n", rel->r_offset & 3); ELFDEBUG("*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]); # endif IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_IMM22, symval + rel->r_addend - (long)elffile->got); break; #ifdef IA64_LDX_OPTIMIZATION case R_IA64_LTOFF22X: { ELFGotEntryPtr gotent; long gp_offset = symval + rel->r_addend - (long)elffile->got; dest128 = (unsigned long *)(secp + (rel->r_offset & ~3)); # ifdef ELFDEBUG ELFDEBUG("R_IA64_LTOFF22X %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest128=%lx\t", dest128); ELFDEBUG("slot=%d\n", rel->r_offset & 3); # endif if (gp_offset << 42 >> 42 != gp_offset) { /* Offset is too large for LTOFF22X, * fallback to using GOT lookup, e.g. LTOFF22. * Note: LDXMOV will fail the same test and will be ignored. */ # ifdef ELFDEBUG ELFDEBUG("gp_offset=%ld too large, using GOT instead (LTOFF22)\n", gp_offset); # endif for (gotent = elffile->got_entries; gotent; gotent = gotent->next) { if (ELF_R_SYM(gotent->rel->r_info) == ELF_R_SYM(rel->r_info) && gotent->rel->r_addend == rel->r_addend) break; } /* Set the address in the GOT */ if (gotent) { *(unsigned long *)(elffile->got + gotent->offset) = symval + rel->r_addend; # ifdef ELFDEBUG ELFDEBUG("Setting gotent[%x]=%lx\n", gotent->offset, symval + rel->r_addend); # endif if ((gotent->offset & 0xffe00000) != 0) FatalError("\nR_IA64_LTOFF22 offset %x too large\n", gotent->offset); } else { FatalError("\nCould not find GOT entry\n"); } gp_offset = gotent->offset; /* Use GOT lookup */ } else { # ifdef ELFDEBUG ELFDEBUG("using gp_offset=%ld (LTOFF22X)", gp_offset); # endif } IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_IMM22, gp_offset); } break; #endif case R_IA64_LDXMOV: # ifdef ELFDEBUG ELFDEBUG("R_IA64_LDXMOV %s\t", ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); # endif #ifdef IA64_LDX_OPTIMIZATION { long gp_offset = symval + rel->r_addend - (long)elffile->got; dest128 = (unsigned long *)(secp + (rel->r_offset & ~3)); if (gp_offset << 42 >> 42 != gp_offset) { /* Offset is too large for LTOFF22X, ignore this relocation */ # ifdef ELFDEBUG ELFDEBUG("offset = %ld too large, ignoring\n", gp_offset); # endif } else { # ifdef ELFDEBUG ELFDEBUG("secp=%lx\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest128=%lx\t", dest128); ELFDEBUG("slot=%d\n", rel->r_offset & 3); ELFDEBUG("offset=%ld\n", gp_offset); ELFDEBUG("*dest128=[%016lx%016lx]\n", dest128[1], dest128[0]); # endif IA64InstallReloc(dest128, rel->r_offset & 3, IA64_OPND_LDXMOV, 0); } } #endif break; #endif /*__ia64__*/ #if defined(__arm__) case R_ARM_ABS32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG ELFDEBUG("R_ARM_ABS32\t"); ELFDEBUG("dest32=%x\t", dest32); ELFDEBUG("*dest32=%8.8lx\t", *dest32); # endif *dest32 = symval + (*dest32); /* S + A */ # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8lx\n", *dest32); # endif break; case R_ARM_REL32: dest32 = (unsigned int *)(secp + rel->r_offset); # ifdef ELFDEBUG { char *namestr; ELFDEBUG("R_ARM_REL32 %s\t", namestr = ElfGetSymbolName(elffile, ELF_R_SYM(rel->r_info))); xf86loaderfree(namestr); ELFDEBUG("secp=%x\t", secp); ELFDEBUG("symval=%lx\t", symval); ELFDEBUG("dest32=%x\t", dest32); ELFDEBUG("*dest32=%8.8lx\t", *dest32); } # endif *dest32 = symval + (*dest32) - (Elf_Addr) dest32; /* S + A - P */ # ifdef ELFDEBUG ELFDEBUG("*dest32=%8.8lx\n", *dest32); # endif break; case R_ARM_PC24: { unsigned long val; dest32 = (unsigned int *)(secp + rel->r_offset); val = (*dest32 & 0x00ffffff) << 2; val = symval - (unsigned long)dest32 + val; val >>= 2; *dest32 = (*dest32 & 0xff000000) | (val & 0x00ffffff); #ifdef NOTYET arm_flush_cache(dest32); #endif } break; #endif /* (__arm__) */ default: ErrorF("Elf_RelocateEntry() Unsupported relocation type %d\n", (int)ELF_R_TYPE(rel->r_info)); break; } return 0; } static ELFRelocPtr ELFCollectRelocations(elffile, index) ELFModulePtr elffile; int index; /* The section to use as relocation data */ { int i, numrel; Elf_Shdr *sect = &(elffile->sections[index]); Elf_Rel_t *rel = (Elf_Rel_t *) elffile->saddr[index]; ELFRelocPtr reloc_head = NULL; ELFRelocPtr tmp; numrel = sect->sh_size / sect->sh_entsize; for (i = 0; i < numrel; i++) { #if defined(__alpha__) if (ELF_R_TYPE(rel[i].r_info) == R_ALPHA_LITERAL) { ElfAddGOT(elffile, &rel[i]); } #endif #if defined(__ia64__) if (ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF22 || ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF22X || ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF_FPTR22) { ElfAddGOT(elffile, &rel[i]); } if (ELF_R_TYPE(rel[i].r_info) == R_IA64_PCREL21B) { ElfAddPLT(elffile, &rel[i]); } if (ELF_R_TYPE(rel[i].r_info) == R_IA64_LTOFF_FPTR22 || ELF_R_TYPE(rel[i].r_info) == R_IA64_FPTR64LSB) { Elf_Sym *syms = (Elf_Sym *) elffile->saddr[elffile->symndx]; if (ELF_ST_BIND(syms[ELF_R_SYM(rel[i].r_info)].st_info) == STB_LOCAL) { ElfAddOPD(elffile, ELF_R_SYM(rel[i].r_info), NULL); } } #endif tmp = ElfDelayRelocation(elffile, sect->sh_info, &(rel[i])); tmp->next = reloc_head; reloc_head = tmp; } return reloc_head; } /* * ELF_GetSymbols() * * add the symbols to the symbol table maintained by the loader. */ static LOOKUP * ELF_GetSymbols(ELFModulePtr elffile, unsigned short **psecttable) { Elf_Sym *syms; Elf_Shdr *sect; int i, l, numsyms; LOOKUP *lookup, *p; ELFCommonPtr tmp; unsigned short *secttable; syms = elffile->symtab; sect = &(elffile->sections[elffile->symndx]); numsyms = sect->sh_size / sect->sh_entsize; if ((lookup = xf86loadermalloc((numsyms + 1) * sizeof(LOOKUP))) == NULL) return 0; if ((secttable = xf86loadercalloc(sizeof(unsigned short), (numsyms + 1))) == NULL) { xf86loaderfree(lookup); return 0; } *psecttable = secttable; for (i = 0, l = 0; i < numsyms; i++) { #ifdef ELFDEBUG ELFDEBUG("value=%lx\tsize=%lx\tBIND=%x\tTYPE=%x\tndx=%x\t%s\n", (unsigned long)syms[i].st_value, (unsigned long)syms[i].st_size, ELF_ST_BIND(syms[i].st_info), ELF_ST_TYPE(syms[i].st_info), syms[i].st_shndx, ElfGetString(elffile, syms[i].st_name)); #endif if (ELF_ST_BIND(syms[i].st_info) == STB_LOCAL) /* Don't add static symbols to the symbol table */ continue; switch (ELF_ST_TYPE(syms[i].st_info)) { case STT_OBJECT: case STT_FUNC: case STT_SECTION: case STT_NOTYPE: switch (syms[i].st_shndx) { case SHN_ABS: ErrorF("ELF_GetSymbols() Don't know how to handle SHN_ABS\n"); break; case SHN_COMMON: #ifdef ELFDEBUG ELFDEBUG("Adding COMMON space for %s\n", ElfGetString(elffile, syms[i].st_name)); #endif if (!LoaderHashFind(ElfGetString(elffile, syms[i].st_name))) { tmp = ElfAddCOMMON(&(syms[i])); if (tmp) { tmp->next = listCOMMON; listCOMMON = tmp; } } break; case SHN_UNDEF: /* * UNDEF will get resolved later, so the value * doesn't really matter here. */ /* since we don't know the value don't advertise the symbol */ break; default: lookup[l].symName = xf86loaderstrdup(ElfGetString (elffile, syms[i].st_name)); lookup[l].offset = (funcptr) syms[i].st_value; secttable[l] = syms[i].st_shndx; #ifdef ELFDEBUG ELFDEBUG("Adding symbol %lx(%d) %s\n", (unsigned long)lookup[l].offset, secttable[l], lookup[l].symName); #endif #ifdef __ia64__ if (ELF_ST_TYPE(syms[i].st_info) == STT_FUNC) { ElfAddOPD(elffile, -1, &lookup[l]); } #endif l++; break; } break; case STT_FILE: case STT_LOPROC: case STT_HIPROC: /* Skip this type */ #ifdef ELFDEBUG ELFDEBUG("Skipping TYPE %d %s\n", ELF_ST_TYPE(syms[i].st_info), ElfGetString(elffile, syms[i].st_name)); #endif break; default: ErrorF("ELF_GetSymbols(): Unepected symbol type %d\n", ELF_ST_TYPE(syms[i].st_info)); break; } } lookup[l].symName = NULL; /* Terminate the list */ /* * Remove the ELF symbols that will show up in every object module. */ for (i = 0, p = lookup; p->symName; i++, p++) { while (!strcmp(lookup[i].symName, ".text") || !strcmp(lookup[i].symName, ".data") || !strcmp(lookup[i].symName, ".bss") || !strcmp(lookup[i].symName, ".comment") || !strcmp(lookup[i].symName, ".note") ) { memmove(&(lookup[i]), &(lookup[i + 1]), (l - i) * sizeof(LOOKUP)); memmove(&(secttable[i]), &(secttable[i + 1]), (l-- - i) * sizeof(unsigned short)); } } return lookup; } #define SecOffset(index) elffile->sections[index].sh_offset #define SecSize(index) elffile->sections[index].sh_size #define SecAlign(index) elffile->sections[index].sh_addralign #define SecType(index) elffile->sections[index].sh_type #define SecFlags(index) elffile->sections[index].sh_flags #define SecInfo(index) elffile->sections[index].sh_info #define AdjustSize(i) \ if (!pass) { \ if (SecAlign(i) > *maxalign) \ *maxalign = SecAlign(i); \ *totalsize += (SecAlign(i) - 1); \ *totalsize &= ~(SecAlign(i) - 1); \ *totalsize += SecSize(i); \ continue; \ } do { } while (0) /* * ELFCollectSections * * Do the work required to load each section into memory. */ static void ELFCollectSections(ELFModulePtr elffile, int pass, int *totalsize, int *maxalign) { int i; int j; /* * Find and identify all of the Sections */ j = elffile->lsectidx; for (i = 1; i < elffile->numsh; i++) { int flags = 0; char *name = ElfGetSectionName(elffile, elffile->sections[i].sh_name); #if defined(__alpha__) || defined(__ia64__) if (!strcmp(name, ".got") /*Isn't there a more generic way to do this? */ # if defined(__ia64__) || !strcmp(name, ".plt") || !strcmp(name, ".IA_64.unwind_info") # endif ) continue; #endif switch (SecType(i)) { case SHT_STRTAB: if (!strcmp(name, ".shstrtab")) /* already loaded */ continue; if (!strcmp(name, ".stabstr")) /* ignore debug info */ continue; if (!strcmp(name, ".stab.indexstr")) /* ignore more debug info */ continue; case SHT_SYMTAB: if (pass) continue; flags = LOADED_SECTION; flags |= RELOC_SECTION; break; case SHT_REL: case SHT_RELA: if (pass) continue; if (!(SecFlags(SecInfo(i)) & SHF_ALLOC)) continue; #ifdef __ia64__ if (SecType(SecInfo(i)) == SHT_IA_64_UNWIND) continue; #endif flags = LOADED_SECTION; flags |= RELOC_SECTION; break; case SHT_PROGBITS: flags |= LOADED_SECTION; case SHT_NOBITS: if (!(elffile->sections[i].sh_flags & SHF_ALLOC)) continue; AdjustSize(i); break; default: #ifdef ELFDEBUG if (pass) ELFDEBUG("ELF: Not loading %s\n", name); #endif continue; } elffile->lsection = xf86loaderrealloc(elffile->lsection, (j + 1) * sizeof(LoadSection)); if (!(flags & RELOC_SECTION)) { if (flags & LOADED_SECTION) { elffile->lsection[j].saddr /* sect. contains data */ = ELFLoaderSectToMem(elffile, SecAlign(i), SecOffset(i), SecSize(i), name); } else { if (SecSize(i)) elffile->lsection[j].saddr = ELFLoaderSectCalloc(elffile, SecAlign(i), SecSize(i)); else elffile->lsection[j].saddr = NULL; } } else { elffile->lsection[j].saddr = (Elf_Sym *) _LoaderFileToMem(elffile->fd, SecOffset(i), SecSize(i), name); } elffile->saddr[i] = elffile->lsection[j].saddr; #ifdef ELFDEBUG ELFDEBUG("%s starts at %p size: %lx\n", name, elffile->saddr[i], (unsigned long)SecSize(i)); #endif elffile->lsection[j].name = name; elffile->lsection[j].ndx = i; elffile->lsection[j].size = SecSize(i); elffile->lsection[j].flags = flags; switch (SecType(i)) { #if defined(linux) || defined(__OpenBSD__) case SHT_PROGBITS: { unsigned long page_size = getpagesize(); unsigned long round; round = (unsigned long)elffile->lsection[j].saddr & (page_size -1); mprotect( (char *)elffile->lsection[j].saddr - round, SecSize(i) + round, PROT_READ | PROT_WRITE | PROT_EXEC); } 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(__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; }