[uClibc-cvs] uClibc/ldso/ldso/frv dl-startup.h, NONE, 1.1 dl-syscalls.h, NONE, 1.1 dl-sysdep.h, NONE, 1.1 elfinterp.c, NONE, 1.1 resolve.S, NONE, 1.1

Erik Andersen andersen at uclibc.org
Wed Feb 18 08:04:51 UTC 2004


Update of /var/cvs/uClibc/ldso/ldso/frv
In directory nail:/tmp/cvs-serv7699/ldso/ldso/frv

Added Files:
	dl-startup.h dl-syscalls.h dl-sysdep.h elfinterp.c resolve.S 
Log Message:
Alexandre Oliva writes:

This patch adds code to uClibc to support a new ABI designed for the
FR-V architecture, that enables text segments of executables and
shared libraries to be shared by multiple processes on an OS such as
uClinux, that can run on FR-V processors without an MMU.

Patches for binutils and GCC have just been posted in the
corresponding mailing lists.  The binutils patch was approved,
but there's one additional patch pending review, that I posted
this week.  An updated GCC patch will be posted to
gcc-patches at gcc.gnu.org as soon as I complete testing (I used a
known-good compiler to test the uClibc patch below).

Since the existing dynamic loader code didn't support independent
relocation of segments, it required changes that were somewhat
extensive.  I've added a number of new machine-specific macros to try
to keep the platform and ABI-specific details outside the generic
code.  I hope this is not a problem.



--- NEW FILE: dl-syscalls.h ---
/* Copyright (C) 2003 Red Hat, Inc.
   Contributed by Alexandre Oliva <aoliva at 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

--- NEW FILE: dl-sysdep.h ---
     /* Copyright (C) 2003, 2004 Red Hat, Inc.
	Contributed by Alexandre Oliva <aoliva at 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 at 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);
}

--- NEW FILE: resolve.S ---
     /* Copyright (C) 2003 Red Hat, Inc.
	Contributed by Alexandre Oliva <aoliva at 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, at 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

--- NEW FILE: dl-startup.h ---
     /* Copyright (C) 2003 Red Hat, Inc.
	Contributed by Alexandre Oliva <aoliva at 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, at 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;

--- NEW FILE: elfinterp.c ---
/* FR-V FDPIC ELF shared library loader suppport
   Copyright (C) 2003 Red Hat, Inc.
   Contributed by Alexandre Oliva <aoliva at 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




More information about the uClibc-cvs mailing list