diff options
Diffstat (limited to 'ldso')
-rw-r--r-- | ldso/ldso/frv/dl-startup.h | 89 | ||||
-rw-r--r-- | ldso/ldso/frv/dl-syscalls.h | 191 | ||||
-rw-r--r-- | ldso/ldso/frv/dl-sysdep.h | 628 | ||||
-rw-r--r-- | ldso/ldso/frv/elfinterp.c | 495 | ||||
-rw-r--r-- | ldso/ldso/frv/resolve.S | 71 |
5 files changed, 1474 insertions, 0 deletions
diff --git a/ldso/ldso/frv/dl-startup.h b/ldso/ldso/frv/dl-startup.h new file mode 100644 index 000000000..017efc0ad --- /dev/null +++ b/ldso/ldso/frv/dl-startup.h @@ -0,0 +1,89 @@ + /* Copyright (C) 2003 Red Hat, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com> + +This file is part of uClibc. + +uClibc is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +uClibc is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with uClibc; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, +USA. */ + +/* Any assembly language/system dependent hacks needed to setup + * boot1.c so it will work as expected and cope with whatever platform + * specific wierdness is needed for this architecture. + + * We override the default _dl_boot function, and replace it with a + * bit of asm. Then call the real _dl_boot function, which is now + * named _dl_boot2. */ + +/* At program start-up, gr16 contains a pointer to a + elf32_fdpic_loadmap that describes how the executable was loaded + into memory. gr17 contains a pointer to the interpreter (our!) + loadmap, if there is an interpreter, or 0 if we're being run as an + executable. gr18 holds a pointer to the interpreter's dynamic + section, if there is an interpreter, or to the executable's dynamic + section, otherwise. If the executable is not dynamic, gr18 is 0. + + We rely on the fact that the linker adds a pointer to the + _GLOBAL_OFFSET_TABLE_ as the last ROFIXUP entry, and that + __self_reloc returns the relocated pointer to us, so that we can + use this value to initialize the PIC register. */ + +asm("" \ +" .text\n" \ +" .global _dl_boot\n" \ +" .type _dl_boot,@function\n" \ +"_dl_boot:\n" \ +" call .Lcall\n" \ +".Lcall:\n" \ +" movsg lr, gr4\n" \ +" sethi.p #gprelhi(.Lcall), gr5\n"\ +" setlo #gprello(.Lcall), gr5\n"\ +" mov.p gr17, gr8\n" \ +" cmp gr17, gr0, icc0\n" \ +" sub.p gr4, gr5, gr4\n" \ +" ckeq icc0, cc4\n" \ +" cmov.p gr16, gr8, cc4, 1\n" \ +" sethi #gprelhi(__ROFIXUP_LIST__), gr9\n" \ +" sethi.p #gprelhi(__ROFIXUP_END__), gr10\n" \ +" setlo #gprello(__ROFIXUP_LIST__), gr9\n" \ +" setlo.p #gprello(__ROFIXUP_END__), gr10\n" \ +" add gr9, gr4, gr9\n" \ +" add.p gr10, gr4, gr10\n" \ +" call __self_reloc\n" \ +" mov.p gr8, gr15\n" \ +" mov gr16, gr9\n" \ +" mov.p gr17, gr10\n" \ +" mov gr18, gr11\n" \ +" addi.p sp, #4, gr13\n" \ +" addi sp, #-8, sp\n" \ +" mov.p sp, gr12\n" \ +" call _dl_boot2\n" \ +" ldd.p @(sp, gr0), gr14\n" \ +" addi sp, #8, sp\n" \ +" movgs gr0, lr\n" \ +" jmpl @(gr14, gr0)\n" \ +" .size _dl_boot,.-_dl_boot\n" \ +); + +#define _dl_boot _dl_boot2 +#define DL_BOOT(X) \ +static void __attribute__ ((used)) \ +_dl_boot (void *dl_boot_got_pointer, \ + struct elf32_fdpic_loadmap *dl_boot_progmap, \ + struct elf32_fdpic_loadmap *dl_boot_ldsomap, \ + Elf32_Dyn *dl_boot_ldso_dyn_pointer, \ + struct funcdesc_value *dl_main_funcdesc, \ + X) + +struct elf32_fdpic_loadmap; diff --git a/ldso/ldso/frv/dl-syscalls.h b/ldso/ldso/frv/dl-syscalls.h new file mode 100644 index 000000000..bef4f1720 --- /dev/null +++ b/ldso/ldso/frv/dl-syscalls.h @@ -0,0 +1,191 @@ +/* Copyright (C) 2003 Red Hat, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com> + +This file is part of uClibc. + +uClibc is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +uClibc is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with uClibc; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, +USA. */ + +/* Define the __set_errno macro as nothing so that INLINE_SYSCALL + * won't set errno, which is important since we make system calls + * before the errno symbol is dynamicly linked. */ + +#define __set_errno(X) {(void)(X);} +#include "sys/syscall.h" +#include <sys/mman.h> + +/* The code below is extracted from libc/sysdeps/linux/frv/_mmap.c */ + +#define __NR___syscall_mmap2 __NR_mmap2 +static inline _syscall6(__ptr_t, __syscall_mmap2, __ptr_t, addr, + size_t, len, int, prot, int, flags, int, fd, off_t, offset); + +/* This is always 12, even on architectures where PAGE_SHIFT != 12. */ +# ifndef MMAP2_PAGE_SHIFT +# define MMAP2_PAGE_SHIFT 12 +# endif + +#if DYNAMIC_LOADER_IN_SIMULATOR +#include <asm/page.h> /* for PAGE_SIZE */ +inline static void *_dl_memset(void*,int,size_t); +inline static ssize_t _dl_pread(int fd, void *buf, size_t count, off_t offset); +#endif + +#ifndef DYNAMIC_LOADER_IN_SIMULATOR +inline +#endif +static __ptr_t +_dl_mmap(__ptr_t addr, size_t len, int prot, int flags, int fd, __off_t offset) +{ +#ifdef DYNAMIC_LOADER_IN_SIMULATOR + size_t plen = (len + PAGE_SIZE - 1) & -PAGE_SIZE; + +/* This is a hack to enable the dynamic loader to run within a + simulator that doesn't support mmap, with a number of very ugly + tricks. Also, it's not as useful as it sounds, since only dynamic + executables without DT_NEEDED dependencies can be run. AFAIK, they + can only be created with -pie. This trick suffices to enable the + dynamic loader to obtain a blank page that it maps early in the + bootstrap. */ + if ((flags & MAP_FIXED) == 0) + { + void *_dl_mmap_base = 0; + __ptr_t *ret = 0; + + if (! _dl_mmap_base) + { + void *stack; + asm ("mov sp, %0" : "=r" (stack)); + _dl_mmap_base = (void *)(((long)stack + 2 * PAGE_SIZE) & -PAGE_SIZE); + retry: + if (((void **)_dl_mmap_base)[0] == _dl_mmap_base + && ((void **)_dl_mmap_base)[1023] == _dl_mmap_base + && (((void **)_dl_mmap_base)[177] + == ((void **)_dl_mmap_base)[771])) + { + while (((void**)_dl_mmap_base)[177]) + { + _dl_mmap_base = ((void**)_dl_mmap_base)[177]; + if (!(((void **)_dl_mmap_base)[0] == _dl_mmap_base + && ((void **)_dl_mmap_base)[1023] == _dl_mmap_base + && (((void **)_dl_mmap_base)[177] + == ((void**)_dl_mmap_base)[771]))) + ((void(*)())0)(); + } + } + else + { + int i; + for (i = 0; i < (int)PAGE_SIZE; i++) + if (*(char*)(_dl_mmap_base + i)) + break; + if (i != PAGE_SIZE) + { + _dl_mmap_base = (void*)((long)_dl_mmap_base + PAGE_SIZE); + goto retry; + } + ((void**)_dl_mmap_base)[-1] = + ((void**)_dl_mmap_base)[0] = + ((void**)_dl_mmap_base)[1023] = + _dl_mmap_base; + } + } + + if (_dl_mmap_base) + { + if (!(((void **)_dl_mmap_base)[0] == _dl_mmap_base + && ((void **)_dl_mmap_base)[1023] == _dl_mmap_base + && (((void **)_dl_mmap_base)[177] + == ((void**)_dl_mmap_base)[771]))) + ((void(*)())0)(); + ret = (__ptr_t)((char*)_dl_mmap_base + PAGE_SIZE); + _dl_mmap_base = + ((void**)_dl_mmap_base)[177] = + ((void**)_dl_mmap_base)[771] = + (char*)_dl_mmap_base + plen + PAGE_SIZE; + ((void**)_dl_mmap_base)[0] = + ((void**)_dl_mmap_base)[1023] = + _dl_mmap_base; + } + + if ((flags & MAP_ANONYMOUS) != 0) + { + _dl_memset (ret, 0, plen); + return ret; + } + + flags |= MAP_FIXED; + addr = ret; + } +#endif + if (offset & ((1 << MMAP2_PAGE_SHIFT) - 1)) { +#if 0 + __set_errno (EINVAL); +#endif + return MAP_FAILED; + } +#ifdef DYNAMIC_LOADER_IN_SIMULATOR + if ((flags & MAP_FIXED) != 0) + { + if (_dl_pread(fd, addr, len, offset) != (ssize_t)len) + return (void*)MAP_FAILED; + if (plen != len) + _dl_memset (addr + len, 0, plen - len); + return addr; + } +#endif + return(__syscall_mmap2(addr, len, prot, flags, fd, (off_t) (offset >> MMAP2_PAGE_SHIFT))); +} + +#ifdef __NR_pread +#ifdef DYNAMIC_LOADER_IN_SIMULATOR +#include <unistd.h> + +#define __NR___syscall_lseek __NR_lseek +inline static unsigned long _dl_read(int fd, const void *buf, unsigned long count); + +inline static _syscall3(__off_t, __syscall_lseek, int, fd, __off_t, offset, + int, whence); +inline static ssize_t +_dl_pread(int fd, void *buf, size_t count, off_t offset) +{ + __off_t orig = __syscall_lseek (fd, 0, SEEK_CUR); + ssize_t ret; + + if (orig == -1) + return -1; + + if (__syscall_lseek (fd, offset, SEEK_SET) != offset) + return -1; + + ret = _dl_read (fd, buf, count); + + if (__syscall_lseek (fd, orig, SEEK_SET) != orig) + ((void(*)())0)(); + + return ret; +} +#else +#define __NR___syscall_pread __NR_pread +inline static _syscall5(ssize_t, __syscall_pread, int, fd, void *, buf, + size_t, count, off_t, offset_hi, off_t, offset_lo); + +inline static ssize_t +_dl_pread(int fd, void *buf, size_t count, off_t offset) +{ + return(__syscall_pread(fd,buf,count,__LONG_LONG_PAIR (offset >> 31, offset))); +} +#endif +#endif diff --git a/ldso/ldso/frv/dl-sysdep.h b/ldso/ldso/frv/dl-sysdep.h new file mode 100644 index 000000000..ab3b09cd1 --- /dev/null +++ b/ldso/ldso/frv/dl-sysdep.h @@ -0,0 +1,628 @@ + /* Copyright (C) 2003, 2004 Red Hat, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com> + Based on ../i386/dl-sysdep.h + +This file is part of uClibc. + +uClibc is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +uClibc is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with uClibc; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, +USA. */ + +/* + * Various assembly language/system dependent hacks that are required + * so that we can minimize the amount of platform specific code. + */ + +/* + * Define this if the system uses RELOCA. + */ +#undef ELF_USES_RELOCA + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. + */ +#define GET_ARGV(ARGVP, ARGS) ARGVP = ((unsigned long*) ARGS) + +/* + * Compute the GOT address. On several platforms, we use assembly + * here. on FR-V FDPIC, there's no way to compute the GOT address, + * since the offset between text and data is not fixed, so we arrange + * for the assembly _dl_boot to pass this value as an argument to + * _dl_boot. */ +#define DL_BOOT_COMPUTE_GOT(got) ((got) = dl_boot_got_pointer) + +#define DL_BOOT_COMPUTE_DYN(dpnt, got, load_addr) \ + ((dpnt) = dl_boot_ldso_dyn_pointer) + +/* + * Initialization sequence for a GOT. Copy the resolver function + * descriptor and the pointer to the elf_resolve/link_map data + * structure. Initialize the got_value in the module while at that. + */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + (MODULE)->loadaddr.got_value = (GOT_BASE); \ + GOT_BASE[0] = ((unsigned long *)&_dl_linux_resolve)[0]; \ + GOT_BASE[1] = ((unsigned long *)&_dl_linux_resolve)[1]; \ + GOT_BASE[2] = (unsigned long) MODULE; \ +} + +/* + * Here is a macro to perform a relocation. This is only used when + * bootstrapping the dynamic loader. RELP is the relocation that we + * are performing, REL is the pointer to the address we are relocating. + * SYMBOL is the symbol involved in the relocation, and LOAD is the + * load address. + */ +#define PERFORM_BOOTSTRAP_RELOC(RELP,REL,SYMBOL,LOAD,SYMTAB) \ + switch(ELF32_R_TYPE((RELP)->r_info)){ \ + case R_FRV_32: \ + *(REL) += (SYMBOL); \ + break; \ + case R_FRV_FUNCDESC_VALUE: \ + { \ + struct funcdesc_value fv = { \ + (void*)((SYMBOL) + *(REL)), \ + (LOAD).got_value \ + }; \ + *(struct funcdesc_value volatile *)(REL) = fv; \ + break; \ + } \ + default: \ + _dl_exit(1); \ + } + +/* + * Transfer control to the user's application, once the dynamic loader + * is done. We return the address of the function's entry point to + * _dl_boot, see boot1_arch.h. + */ +#define START() do { \ + struct elf_resolve *exec_mod = _dl_loaded_modules; \ + dl_main_funcdesc->entry_point = _dl_elf_main; \ + while (exec_mod->libtype != elf_executable) \ + exec_mod = exec_mod->next; \ + dl_main_funcdesc->got_value = exec_mod->loadaddr.got_value; \ + /* _dl_dprintf(2, "entry point is (%x,%x)\n", dl_main_funcdesc->entry_point, dl_main_funcdesc->got_value); */ \ + return; \ +} while (0) + + + +/* Here we define the magic numbers that this dynamic loader should accept */ + +#define MAGIC1 EM_CYGNUS_FRV +#undef MAGIC2 +/* Used for error messages */ +#define ELF_TARGET "FR-V" + +struct elf_resolve; + +struct funcdesc_value +{ + void *entry_point; + void *got_value; +} __attribute__((__aligned__(8))); + + +extern int _dl_linux_resolve(void) __attribute__((__visibility__("hidden"))); + +#define do_rem(result, n, base) result = (n % base) + +/* 4096 bytes alignment */ +#define PAGE_ALIGN 0xfffff000 +#define ADDR_ALIGN 0xfff +#define OFFS_ALIGN 0x7ffff000 + +struct funcdesc_ht; + +/* We must force strings used early in the bootstrap into the data + segment, such that they are referenced with GOTOFF instead of + GPREL, because GPREL needs the GOT to have already been + relocated. */ +#define SEND_EARLY_STDERR(S) \ + do { static char __s[] = (S); SEND_STDERR (__s); } while (0) + +#include <bits/elf-fdpic.h> +#ifdef __USE_GNU +# include <link.h> +#else +# define __USE_GNU +# include <link.h> +# undef __USE_GNU +#endif +#include <dl-syscall.h> +#include <dl-string.h> + +/* These are declared in ldso.h, after it includes dl-elf.h that + includes ourselves. */ +extern void *_dl_malloc(int size); +extern void _dl_free(void *); +extern void _dl_dprintf(int, const char *, ...); + + +#ifndef _dl_assert +# define _dl_assert(expr) +#endif + +/* Initialize a DL_LOADADDR_TYPE given a got pointer and a complete + load map. */ +inline static void +__dl_init_loadaddr_map (struct elf32_fdpic_loadaddr *loadaddr, void *got_value, + struct elf32_fdpic_loadmap *map) +{ + if (map->version != 0) + { + SEND_EARLY_STDERR ("Invalid loadmap version number\n"); + _dl_exit(-1); + } + if (map->nsegs == 0) + { + SEND_EARLY_STDERR ("Invalid segment count in loadmap\n"); + _dl_exit(-1); + } + loadaddr->got_value = got_value; + loadaddr->map = map; +} + +/* Figure out how many LOAD segments there are in the given headers, + and allocate a block for the load map big enough for them. + got_value will be properly initialized later on, with INIT_GOT. */ +inline static int +__dl_init_loadaddr (struct elf32_fdpic_loadaddr *loadaddr, Elf32_Phdr *ppnt, + int pcnt) +{ + int count = 0, i; + size_t size; + + for (i = 0; i < pcnt; i++) + if (ppnt[i].p_type == PT_LOAD) + count++; + + loadaddr->got_value = 0; + + size = sizeof (struct elf32_fdpic_loadmap) + + sizeof (struct elf32_fdpic_loadseg) * count; + loadaddr->map = _dl_malloc (size); + if (! loadaddr->map) + _dl_exit (-1); + + loadaddr->map->version = 0; + loadaddr->map->nsegs = 0; + + return count; +} + +/* Incrementally initialize a load map. */ +inline static void +__dl_init_loadaddr_hdr (struct elf32_fdpic_loadaddr loadaddr, void *addr, + Elf32_Phdr *phdr, int maxsegs) +{ + struct elf32_fdpic_loadseg *segdata; + + if (loadaddr.map->nsegs == maxsegs) + _dl_exit (-1); + + segdata = &loadaddr.map->segs[loadaddr.map->nsegs++]; + segdata->addr = (Elf32_Addr) addr; + segdata->p_vaddr = phdr->p_vaddr; + segdata->p_memsz = phdr->p_memsz; + +#if defined (__SUPPORT_LD_DEBUG__) + { + extern char *_dl_debug; + extern int _dl_debug_file; + if (_dl_debug) + _dl_dprintf(_dl_debug_file, "%i: mapped %x at %x, size %x\n", + loadaddr.map->nsegs-1, + segdata->p_vaddr, segdata->addr, segdata->p_memsz); + } +#endif +} + +inline static void __dl_loadaddr_unmap +(struct elf32_fdpic_loadaddr loadaddr, struct funcdesc_ht *funcdesc_ht); + +/* Figure out whether the given address is in one of the mapped + segments. */ +inline static int +__dl_addr_in_loadaddr (void *p, struct elf32_fdpic_loadaddr loadaddr) +{ + struct elf32_fdpic_loadmap *map = loadaddr.map; + int c; + + for (c = 0; c < map->nsegs; c++) + if ((void*)map->segs[c].addr <= p + && (char*)p < (char*)map->segs[c].addr + map->segs[c].p_memsz) + return 1; + + return 0; +} + +inline static void * _dl_funcdesc_for (void *entry_point, void *got_value); + +#define DL_LOADADDR_TYPE struct elf32_fdpic_loadaddr + +#define DL_RELOC_ADDR(ADDR, LOADADDR) \ + (__reloc_pointer ((void*)(ADDR), (LOADADDR).map)) + +#define DL_ADDR_TO_FUNC_PTR(ADDR, LOADADDR) \ + ((void(*)(void)) _dl_funcdesc_for ((void*)(ADDR), (LOADADDR).got_value)) + +#define _dl_stabilize_funcdesc(val) \ + ({ asm ("" : "+m" (*(val))); (val); }) + +#define DL_CALL_FUNC_AT_ADDR(ADDR, LOADADDR, SIGNATURE, ...) \ + ({ struct funcdesc_value fd = { (void*)(ADDR), (LOADADDR).got_value }; \ + void (*pf)(void) = (void*) _dl_stabilize_funcdesc (&fd); \ + (* SIGNATURE pf)(__VA_ARGS__); }) + +#define DL_INIT_LOADADDR_BOOT(LOADADDR, BASEADDR) \ + (__dl_init_loadaddr_map (&(LOADADDR), dl_boot_got_pointer, \ + dl_boot_ldsomap ?: dl_boot_progmap)) + +#define DL_INIT_LOADADDR_PROG(LOADADDR, BASEADDR) \ + (__dl_init_loadaddr_map (&(LOADADDR), 0, dl_boot_progmap)) + +#define DL_INIT_LOADADDR_EXTRA_DECLS \ + int dl_init_loadaddr_load_count; +#define DL_INIT_LOADADDR(LOADADDR, BASEADDR, PHDR, PHDRCNT) \ + (dl_init_loadaddr_load_count = \ + __dl_init_loadaddr (&(LOADADDR), (PHDR), (PHDRCNT))) +#define DL_INIT_LOADADDR_HDR(LOADADDR, ADDR, PHDR) \ + (__dl_init_loadaddr_hdr ((LOADADDR), (ADDR), (PHDR), \ + dl_init_loadaddr_load_count)) +#define DL_LOADADDR_UNMAP(LOADADDR, LEN) \ + (__dl_loadaddr_unmap ((LOADADDR), (NULL))) +#define DL_LIB_UNMAP(LIB, LEN) \ + (__dl_loadaddr_unmap ((LIB)->loadaddr, (LIB)->funcdesc_ht)) +#define DL_LOADADDR_BASE(LOADADDR) \ + ((LOADADDR).got_value) + +#define DL_ADDR_IN_LOADADDR(ADDR, TPNT, TFROM) \ + (! (TFROM) && __dl_addr_in_loadaddr ((void*)(ADDR), (TPNT)->loadaddr)) + +/* We only support loading FDPIC independently-relocatable shared + libraries. It probably wouldn't be too hard to support loading + shared libraries that require relocation by the same amount, but we + don't know that they exist or would be useful, and the dynamic + loader code could leak the whole-library map unless we keeping a + bit more state for DL_LOADADDR_UNMAP and DL_LIB_UNMAP, so let's + keep things simple for now. */ +#define DL_CHECK_LIB_TYPE(epnt, piclib, _dl_progname, libname) \ +do \ +{ \ + if (((epnt)->e_flags & EF_FRV_FDPIC) && ! ((epnt)->e_flags & EF_FRV_PIC)) \ + (piclib) = 2; \ + else \ + { \ + _dl_internal_error_number = LD_ERROR_NOTDYN; \ + _dl_dprintf(2, "%s: '%s' is not an FDPIC shared library" \ + "\n", (_dl_progname), (libname)); \ + _dl_close(infile); \ + return NULL; \ + } \ +} \ +while (0) + +/* We want want to apply all relocations in the interpreter during + bootstrap. Because of this, we have to skip the interpreter + relocations in _dl_parse_relocation_information(), see + elfinterp.c. */ +#define DL_SKIP_BOOTSTRAP_RELOC(SYMTAB, INDEX, STRTAB) 0 + +#ifdef __NR_pread +#define _DL_PREAD(FD, BUF, SIZE, OFFSET) \ + (_dl_pread((FD), (BUF), (SIZE), (OFFSET))) +#endif + +#include <dl-hash.h> + +/* The hashcode handling code below is heavily inspired in libiberty's + hashtab code, but with most adaptation points and support for + deleting elements removed. + + Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Contributed by Vladimir Makarov (vmakarov@cygnus.com). */ + +inline static unsigned long +higher_prime_number (unsigned long n) +{ + /* These are primes that are near, but slightly smaller than, a + power of two. */ + static const unsigned long primes[] = { + (unsigned long) 7, + (unsigned long) 13, + (unsigned long) 31, + (unsigned long) 61, + (unsigned long) 127, + (unsigned long) 251, + (unsigned long) 509, + (unsigned long) 1021, + (unsigned long) 2039, + (unsigned long) 4093, + (unsigned long) 8191, + (unsigned long) 16381, + (unsigned long) 32749, + (unsigned long) 65521, + (unsigned long) 131071, + (unsigned long) 262139, + (unsigned long) 524287, + (unsigned long) 1048573, + (unsigned long) 2097143, + (unsigned long) 4194301, + (unsigned long) 8388593, + (unsigned long) 16777213, + (unsigned long) 33554393, + (unsigned long) 67108859, + (unsigned long) 134217689, + (unsigned long) 268435399, + (unsigned long) 536870909, + (unsigned long) 1073741789, + (unsigned long) 2147483647, + /* 4294967291L */ + ((unsigned long) 2147483647) + ((unsigned long) 2147483644), + }; + + const unsigned long *low = &primes[0]; + const unsigned long *high = &primes[sizeof(primes) / sizeof(primes[0])]; + + while (low != high) + { + const unsigned long *mid = low + (high - low) / 2; + if (n > *mid) + low = mid + 1; + else + high = mid; + } + +#if 0 + /* If we've run out of primes, abort. */ + if (n > *low) + { + fprintf (stderr, "Cannot find prime bigger than %lu\n", n); + abort (); + } +#endif + + return *low; +} + +struct funcdesc_ht +{ + /* Table itself. */ + struct funcdesc_value **entries; + + /* Current size (in entries) of the hash table */ + size_t size; + + /* Current number of elements. */ + size_t n_elements; +}; + +inline static int +hash_pointer (const void *p) +{ + return (int) ((long)p >> 3); +} + +inline static struct funcdesc_ht * +htab_create (void) +{ + struct funcdesc_ht *ht = _dl_malloc (sizeof (struct funcdesc_ht)); + + if (! ht) + return NULL; + ht->size = 3; + ht->entries = _dl_malloc (sizeof (struct funcdesc_ht_value *) * ht->size); + if (! ht->entries) + return NULL; + + ht->n_elements = 0; + + _dl_memset (ht->entries, 0, sizeof (struct funcdesc_ht_value *) * ht->size); + + return ht; +} + +inline static void +htab_delete (struct funcdesc_ht *htab) +{ + int i; + + for (i = htab->size - 1; i >= 0; i--) + if (htab->entries[i]) + _dl_free (htab->entries[i]); + + _dl_free (htab->entries); + _dl_free (htab); +} + +/* Similar to htab_find_slot, but without several unwanted side effects: + - Does not call htab->eq_f when it finds an existing entry. + - Does not change the count of elements/searches/collisions in the + hash table. + This function also assumes there are no deleted entries in the table. + HASH is the hash value for the element to be inserted. */ + +inline static struct funcdesc_value ** +find_empty_slot_for_expand (struct funcdesc_ht *htab, int hash) +{ + size_t size = htab->size; + unsigned int index = hash % size; + struct funcdesc_value **slot = htab->entries + index; + int hash2; + + if (! *slot) + return slot; + + hash2 = 1 + hash % (size - 2); + for (;;) + { + index += hash2; + if (index >= size) + index -= size; + + slot = htab->entries + index; + if (! *slot) + return slot; + } +} + +/* The following function changes size of memory allocated for the + entries and repeatedly inserts the table elements. The occupancy + of the table after the call will be about 50%. Naturally the hash + table must already exist. Remember also that the place of the + table entries is changed. If memory allocation failures are allowed, + this function will return zero, indicating that the table could not be + expanded. If all goes well, it will return a non-zero value. */ + +inline static int +htab_expand (struct funcdesc_ht *htab) +{ + struct funcdesc_value **oentries; + struct funcdesc_value **olimit; + struct funcdesc_value **p; + struct funcdesc_value **nentries; + size_t nsize; + + oentries = htab->entries; + olimit = oentries + htab->size; + + /* Resize only when table after removal of unused elements is either + too full or too empty. */ + if (htab->n_elements * 2 > htab->size) + nsize = higher_prime_number (htab->n_elements * 2); + else + nsize = htab->size; + + nentries = _dl_malloc (sizeof (struct funcdesc_value *) * nsize); + _dl_memset (nentries, 0, sizeof (struct funcdesc_value *) * nsize); + if (nentries == NULL) + return 0; + htab->entries = nentries; + htab->size = nsize; + + p = oentries; + do + { + if (*p) + *find_empty_slot_for_expand (htab, hash_pointer ((*p)->entry_point)) + = *p; + + p++; + } + while (p < olimit); + + _dl_free (oentries); + return 1; +} + +/* This function searches for a hash table slot containing an entry + equal to the given element. To delete an entry, call this with + INSERT = 0, then call htab_clear_slot on the slot returned (possibly + after doing some checks). To insert an entry, call this with + INSERT = 1, then write the value you want into the returned slot. + When inserting an entry, NULL may be returned if memory allocation + fails. */ + +inline static struct funcdesc_value ** +htab_find_slot (struct funcdesc_ht *htab, void *ptr) +{ + unsigned int index; + int hash, hash2; + size_t size; + struct funcdesc_value **entry; + + if (htab->size * 3 <= htab->n_elements * 4 + && htab_expand (htab) == 0) + return NULL; + + hash = hash_pointer (ptr); + + size = htab->size; + index = hash % size; + + entry = &htab->entries[index]; + if (!*entry) + goto empty_entry; + else if ((*entry)->entry_point == ptr) + return entry; + + hash2 = 1 + hash % (size - 2); + for (;;) + { + index += hash2; + if (index >= size) + index -= size; + + entry = &htab->entries[index]; + if (!*entry) + goto empty_entry; + else if ((*entry)->entry_point == ptr) + return entry; + } + + empty_entry: + htab->n_elements++; + return entry; +} + +void * +_dl_funcdesc_for (void *entry_point, void *got_value) +{ + struct elf_resolve *tpnt = ((void**)got_value)[2]; + struct funcdesc_ht *ht = tpnt->funcdesc_ht; + struct funcdesc_value **entry; + + _dl_assert (got_value == tpnt->loadaddr.got_value); + + if (! ht) + { + ht = htab_create (); + if (! ht) + return (void*)-1; + tpnt->funcdesc_ht = ht; + } + + entry = htab_find_slot (ht, entry_point); + if (*entry) + { + _dl_assert ((*entry)->entry_point == entry_point); + return _dl_stabilize_funcdesc (*entry); + } + + *entry = _dl_malloc (sizeof (struct funcdesc_value)); + (*entry)->entry_point = entry_point; + (*entry)->got_value = got_value; + + return _dl_stabilize_funcdesc (*entry); +} + +void +__dl_loadaddr_unmap (struct elf32_fdpic_loadaddr loadaddr, + struct funcdesc_ht *funcdesc_ht) +{ + int i; + + for (i = 0; i < loadaddr.map->nsegs; i++) + _dl_munmap ((void*)loadaddr.map->segs[i].addr, + loadaddr.map->segs[i].p_memsz); + + _dl_free (loadaddr.map); + if (funcdesc_ht) + htab_delete (funcdesc_ht); +} diff --git a/ldso/ldso/frv/elfinterp.c b/ldso/ldso/frv/elfinterp.c new file mode 100644 index 000000000..270a5a60e --- /dev/null +++ b/ldso/ldso/frv/elfinterp.c @@ -0,0 +1,495 @@ +/* FR-V FDPIC ELF shared library loader suppport + Copyright (C) 2003 Red Hat, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com> + Lots of code copied from ../i386/elfinterp.c, so: + Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald, + David Engel, Hongjiu Lu and Mitch D'Souza + Copyright (C) 2001-2002, Erik Andersen + All rights reserved. + +This file is part of uClibc. + +uClibc is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +uClibc is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with uClibc; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, +USA. */ + +#ifndef ATTRIBUTE_UNUSED +# define ATTRIBUTE_UNUSED __attribute__((__unused__)) +#endif + +#if defined (__SUPPORT_LD_DEBUG__) +static const char *_dl_reltypes_tab[] = +{ + [0] "R_FRV_NONE", "R_FRV_32", + [2] "R_FRV_LABEL16", "R_FRV_LABEL24", + [4] "R_FRV_LO16", "R_FRV_HI16", + [6] "R_FRV_GPREL12", "R_FRV_GPRELU12", + [8] "R_FRV_GPREL32", "R_FRV_GPRELHI", "R_FRV_GPRELLO", + [11] "R_FRV_GOT12", "R_FRV_GOTHI", "R_FRV_GOTLO", + [14] "R_FRV_FUNCDESC", + [15] "R_FRV_FUNCDESC_GOT12", "R_FRV_FUNCDESC_GOTHI", "R_FRV_FUNCDESC_GOTLO", + [18] "R_FRV_FUNCDESC_VALUE", "R_FRV_FUNCDESC_GOTOFF12", + [20] "R_FRV_FUNCDESC_GOTOFFHI", "R_FRV_FUNCDESC_GOTOFFLO", + [22] "R_FRV_GOTOFF12", "R_FRV_GOTOFFHI", "R_FRV_GOTOFFLO", +#if 0 + [200] "R_FRV_GNU_VTINHERIT", "R_FRV_GNU_VTENTRY" +#endif +}; + +static const char * +_dl_reltypes(int type) +{ + static char buf[22]; + const char *str; + + if (type >= (int)(sizeof (_dl_reltypes_tab)/sizeof(_dl_reltypes_tab[0])) || + NULL == (str = _dl_reltypes_tab[type])) + { + str =_dl_simple_ltoa( buf, (unsigned long)(type)); + } + return str; +} + +static +void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index) +{ + if(_dl_debug_symbols) + { + if(symtab_index){ + _dl_dprintf(_dl_debug_file, "\n%s\n\tvalue=%x\tsize=%x\tinfo=%x\tother=%x\tshndx=%x", + strtab + symtab[symtab_index].st_name, + symtab[symtab_index].st_value, + symtab[symtab_index].st_size, + symtab[symtab_index].st_info, + symtab[symtab_index].st_other, + symtab[symtab_index].st_shndx); + } + } +} + +static void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt) +{ + if(_dl_debug_reloc) + { + int symtab_index; + const char *sym; + symtab_index = ELF32_R_SYM(rpnt->r_info); + sym = symtab_index ? strtab + symtab[symtab_index].st_name : "sym=0x0"; + + if(_dl_debug_symbols) + _dl_dprintf(_dl_debug_file, "\n\t"); + else + _dl_dprintf(_dl_debug_file, "\n%s\n\t", sym); +#ifdef ELF_USES_RELOCA + _dl_dprintf(_dl_debug_file, "%s\toffset=%x\taddend=%x", + _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)), + rpnt->r_offset, + rpnt->r_addend); +#else + _dl_dprintf(_dl_debug_file, "%s\toffset=%x\n", + _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)), + rpnt->r_offset); +#endif + } +} +#endif + +/* Program to load an ELF binary on a linux system, and run it. + References to symbols in sharable libraries can be resolved by either + an ELF sharable library or a linux style of shared library. */ + +/* Disclaimer: I have never seen any AT&T source code for SVr4, nor have + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +struct funcdesc_value volatile *__attribute__((__visibility__("hidden"))) +_dl_linux_resolver (struct elf_resolve *tpnt, int reloc_entry) +{ + int reloc_type; + ELF_RELOC *this_reloc; + char *strtab; + Elf32_Sym *symtab; + int symtab_index; + char *rel_addr; + struct elf_resolve *new_tpnt; + char *new_addr; + struct funcdesc_value funcval; + struct funcdesc_value volatile *got_entry; + char *symname; + + rel_addr = DL_RELOC_ADDR (tpnt->dynamic_info[DT_JMPREL], + tpnt->loadaddr); + + this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry); + reloc_type = ELF32_R_TYPE(this_reloc->r_info); + symtab_index = ELF32_R_SYM(this_reloc->r_info); + + symtab = (Elf32_Sym *)(intptr_t) + DL_RELOC_ADDR (tpnt->dynamic_info[DT_SYMTAB], + tpnt->loadaddr); + strtab = DL_RELOC_ADDR (tpnt->dynamic_info[DT_STRTAB], tpnt->loadaddr); + symname= strtab + symtab[symtab_index].st_name; + + if (reloc_type != R_FRV_FUNCDESC_VALUE) { + _dl_dprintf(2, "%s: Incorrect relocation type in jump relocations\n", + _dl_progname); + _dl_exit(1); + } + + /* Address of GOT entry fix up */ + got_entry = (struct funcdesc_value *) + DL_RELOC_ADDR (this_reloc->r_offset, tpnt->loadaddr); + + /* Get the address to be used to fill in the GOT entry. */ + new_addr = __dl_find_hash(symname, tpnt->symbol_scope, tpnt, resolver, + &new_tpnt); + if (!new_addr) { + new_addr = __dl_find_hash(symname, NULL, NULL, resolver, + &new_tpnt); + if (!new_addr) { + _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", + _dl_progname, symname); + _dl_exit(1); + } + } + + funcval.entry_point = new_addr; + funcval.got_value = new_tpnt->loadaddr.got_value; + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_bindings) + { + _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname); + if(_dl_debug_detail) + _dl_dprintf(_dl_debug_file, + "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n", + got_entry->entry_point, got_entry->got_value, + funcval.entry_point, funcval.got_value, + got_entry); + } + if (!_dl_debug_nofixups) { + *got_entry = funcval; + } +#else + *got_entry = funcval; +#endif + + return got_entry; +} + +static int +_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, + unsigned long rel_addr, unsigned long rel_size, + int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)) +{ + unsigned int i; + char *strtab; + Elf32_Sym *symtab; + ELF_RELOC *rpnt; + int symtab_index; + + /* Now parse the relocation information */ + rpnt = (ELF_RELOC *)(intptr_t) DL_RELOC_ADDR (rel_addr, tpnt->loadaddr); + rel_size = rel_size / sizeof(ELF_RELOC); + + symtab = (Elf32_Sym *)(intptr_t) + DL_RELOC_ADDR (tpnt->dynamic_info[DT_SYMTAB], tpnt->loadaddr); + strtab = DL_RELOC_ADDR (tpnt->dynamic_info[DT_STRTAB], tpnt->loadaddr); + + for (i = 0; i < rel_size; i++, rpnt++) { + int res; + + symtab_index = ELF32_R_SYM(rpnt->r_info); + + /* When the dynamic linker bootstrapped itself, it resolved some symbols. + Make sure we do not do them again */ + if (!symtab_index && tpnt->libtype == program_interpreter) + continue; + if (symtab_index && tpnt->libtype == program_interpreter && + _dl_symbol(strtab + symtab[symtab_index].st_name)) + continue; + +#if defined (__SUPPORT_LD_DEBUG__) + debug_sym(symtab,strtab,symtab_index); + debug_reloc(symtab,strtab,rpnt); +#endif + + res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab); + + if (res==0) continue; + + _dl_dprintf(2, "\n%s: ",_dl_progname); + + if (symtab_index) + _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name); + + if (res <0) + { + int reloc_type = ELF32_R_TYPE(rpnt->r_info); +#if defined (__SUPPORT_LD_DEBUG__) + _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type)); +#else + _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type); +#endif + _dl_exit(-res); + } + else if (res >0) + { + _dl_dprintf(2, "can't resolve symbol\n"); + return res; + } + } + return 0; +} + +static int +_dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope, + ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab) +{ + int reloc_type; + int symtab_index; + char *symname; + unsigned long reloc_value = 0, *reloc_addr; + struct { unsigned long v; } __attribute__((__packed__)) + *reloc_addr_packed; + unsigned long symbol_addr; + struct elf_resolve *symbol_tpnt; + struct funcdesc_value funcval; +#if defined (__SUPPORT_LD_DEBUG__) + unsigned long old_val; +#endif + + reloc_addr = (unsigned long *)(intptr_t) + DL_RELOC_ADDR (rpnt->r_offset, tpnt->loadaddr); + asm ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr)); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + symname = strtab + symtab[symtab_index].st_name; + + if (ELF32_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) { + symbol_addr = (unsigned long) + DL_RELOC_ADDR (symtab[symtab_index].st_value, + tpnt->loadaddr); + symbol_tpnt = tpnt; + } else { + + symbol_addr = (unsigned long) + __dl_find_hash(symname, scope, + (reloc_type == R_FRV_FUNCDESC_VALUE + ? tpnt : NULL), symbolrel, + &symbol_tpnt); + + /* + * We want to allow undefined references to weak symbols - this might + * have been intentional. We should not be linking local symbols + * here, so all bases should be covered. + */ + + if (!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_GLOBAL) { +#if defined (__SUPPORT_LD_DEBUG__) + _dl_dprintf(2, "\tglobal symbol '%s' already defined in '%s'\n", + symname, tpnt->libname); +#endif + return 0; + } + } + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail) + { + if ((long)reloc_addr_packed & 3) + old_val = reloc_addr_packed->v; + else + old_val = *reloc_addr; + } + else + old_val = 0; +#endif + switch (reloc_type) { + case R_FRV_NONE: + break; + case R_FRV_32: + if ((long)reloc_addr_packed & 3) + reloc_value = reloc_addr_packed->v += symbol_addr; + else + reloc_value = *reloc_addr += symbol_addr; + break; + case R_FRV_FUNCDESC_VALUE: + funcval.entry_point = (void*)symbol_addr; + /* The addend of FUNCDESC_VALUE + relocations referencing global + symbols must be ignored, because it + may hold the address of a lazy PLT + entry. */ + if (ELF32_ST_BIND + (symtab[symtab_index].st_info) + == STB_LOCAL) + funcval.entry_point += *reloc_addr; + reloc_value = (unsigned long)funcval.entry_point; + if (symbol_addr) + funcval.got_value + = symbol_tpnt->loadaddr.got_value; + else + funcval.got_value = 0; + asm ("std%I0\t%1, %M0" + : "=m" (*(struct funcdesc_value *)reloc_addr) + : "r" (funcval)); + break; + case R_FRV_FUNCDESC: + if ((long)reloc_addr_packed & 3) + reloc_value = reloc_addr_packed->v; + else + reloc_value = *reloc_addr; + if (symbol_addr) + reloc_value = (unsigned long)_dl_funcdesc_for + ((char *)symbol_addr + reloc_value, + symbol_tpnt->loadaddr.got_value); + else + reloc_value = 0; + if ((long)reloc_addr_packed & 3) + reloc_addr_packed->v = reloc_value; + else + *reloc_addr = reloc_value; + break; + default: + return -1; /*call _dl_exit(1) */ + } +#if defined (__SUPPORT_LD_DEBUG__) + if(_dl_debug_reloc && _dl_debug_detail) { + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, reloc_value, reloc_addr); + switch (reloc_type) { + case R_FRV_FUNCDESC_VALUE: + _dl_dprintf(_dl_debug_file, " got %x", ((struct funcdesc_value *)reloc_value)->got_value); + break; + case R_FRV_FUNCDESC: + if (! reloc_value) + break; + _dl_dprintf(_dl_debug_file, " funcdesc (%x,%x)", + ((struct funcdesc_value *)reloc_value)->entry_point, + ((struct funcdesc_value *)reloc_value)->got_value); + break; + } + } +#endif + + return 0; +} + +static int +_dl_do_lazy_reloc (struct elf_resolve *tpnt, + struct dyn_elf *scope ATTRIBUTE_UNUSED, + ELF_RELOC *rpnt, Elf32_Sym *symtab ATTRIBUTE_UNUSED, + char *strtab ATTRIBUTE_UNUSED) +{ + int reloc_type; + struct funcdesc_value volatile *reloc_addr; + struct funcdesc_value funcval; +#if defined (__SUPPORT_LD_DEBUG__) + unsigned long old_val; +#endif + + reloc_addr = (struct funcdesc_value *)(intptr_t) + DL_RELOC_ADDR (rpnt->r_offset, tpnt->loadaddr); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + +#if defined (__SUPPORT_LD_DEBUG__) + old_val = (unsigned long)reloc_addr->entry_point; +#endif + switch (reloc_type) { + case R_FRV_NONE: + break; + case R_FRV_FUNCDESC_VALUE: + funcval = *reloc_addr; + funcval.entry_point = + DL_RELOC_ADDR (funcval.entry_point, + tpnt->loadaddr); + funcval.got_value = tpnt->loadaddr.got_value; + *reloc_addr = funcval; + break; + default: + return -1; /*call _dl_exit(1) */ + } +#if defined (__SUPPORT_LD_DEBUG__) + if(_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, reloc_addr->entry_point, reloc_addr); +#endif + return 0; + +} + +void +_dl_parse_lazy_relocation_information +(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size, + int type ATTRIBUTE_UNUSED) +{ + _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc); +} + +int +_dl_parse_relocation_information +(struct dyn_elf *rpnt, unsigned long rel_addr, unsigned long rel_size, + int type ATTRIBUTE_UNUSED) +{ + /* The interpreter initial self-relocation is complete, and we + can't re-apply relocations. */ + if (rpnt->dyn->libtype == program_interpreter) + return 0; + + return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc); +} + +/* We don't have copy relocs. */ + +int +_dl_parse_copy_information +(struct dyn_elf *rpnt ATTRIBUTE_UNUSED, + unsigned long rel_addr ATTRIBUTE_UNUSED, + unsigned long rel_size ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED) +{ + return 0; +} + +#ifndef LIBDL +# include "../../libc/sysdeps/linux/frv/crtreloc.c" +#endif + +#if ! defined LIBDL || (! defined PIC && ! defined __PIC__) +int +__dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, + size_t size, void *data), void *data) +{ + struct elf_resolve *l; + struct dl_phdr_info info; + int ret = 0; + + for (l = _dl_loaded_modules; l != NULL; l = l->next) + { + info.dlpi_addr = l->loadaddr; + info.dlpi_name = l->libname; + info.dlpi_phdr = l->ppnt; + info.dlpi_phnum = l->n_phent; + ret = callback (&info, sizeof (struct dl_phdr_info), data); + if (ret) + break; + } + + return ret; +} +#endif diff --git a/ldso/ldso/frv/resolve.S b/ldso/ldso/frv/resolve.S new file mode 100644 index 000000000..d9706c746 --- /dev/null +++ b/ldso/ldso/frv/resolve.S @@ -0,0 +1,71 @@ + /* Copyright (C) 2003 Red Hat, Inc. + Contributed by Alexandre Oliva <aoliva@redhat.com> + +This file is part of uClibc. + +uClibc is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +uClibc is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with uClibc; see the file COPYING.LIB. If not, write to +the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, +USA. */ + + /* The function below is tail-called by resolver stubs when a + lazily-bound function is called. It must preserve all + registers that could be used to pass arguments to the actual + function. Upon _dl_linux_resolve entry, GR14 holds the + address of a lazy PLT entry, so @(GR14,-4) is the lazy + relocation number that we have to pass to _dl_linux_resolver. + GR15 holds the caller's GOT, from which we extract the + elf_resolve* that _dl_linux_resolver needs as well. + + _dl_linux_resolver() figures out where the jump symbol is + _really_ supposed to have jumped to and returns that to us. + Once we have that, we prepare to tail-call the actual + function, clean up after ourselves, restoring the original + arguments, then jump to the fixed up address. */ + + .text + .p2align 4 + + .hidden _dl_linux_resolve + .global _dl_linux_resolve + .type _dl_linux_resolve,@function + +_dl_linux_resolve: + /* Preserve arguments. */ + addi sp, -8*4, sp + stdi gr8, @(sp, 8) + stdi gr10, @(sp, 16) + stdi gr12, @(sp, 24) + movsg lr,gr8 + st gr8, @(sp,gr0) + + /* Prepare to call _dl_linux_resolver. */ + ldi @(gr15, 8), gr8 + ldi @(gr14, -4), gr9 + mov.p gr5, gr15 + call _dl_linux_resolver + + /* Move aside return value that contains the FUNCDESC_VALUE. */ + ldd @(gr8,gr0),gr14 + + /* Restore arguments. */ + ld @(sp, gr0), gr8 + movgs gr8,lr + lddi @(sp, 24), gr12 + lddi @(sp, 16), gr10 + lddi @(sp, 8), gr8 + addi sp, 8*4, sp + + /* Now jump to the actual function. */ + jmpl @(gr14, gr0) + .size _dl_linux_resolve, . - _dl_linux_resolve |