summaryrefslogtreecommitdiff
path: root/ldso
diff options
context:
space:
mode:
Diffstat (limited to 'ldso')
-rw-r--r--ldso/ldso/frv/dl-startup.h89
-rw-r--r--ldso/ldso/frv/dl-syscalls.h191
-rw-r--r--ldso/ldso/frv/dl-sysdep.h628
-rw-r--r--ldso/ldso/frv/elfinterp.c495
-rw-r--r--ldso/ldso/frv/resolve.S71
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