/* Machine-dependent ELF dynamic relocation inline functions.  FR-V version.
   Copyright (C) 1995-1997, 2000-2002, 2003, 2004 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library 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.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifndef dl_machine_h
#define dl_machine_h 1

#define ELF_MACHINE_NAME "FR-V"

#include <assert.h>
#include <string.h>
#include <link.h>
#include <errno.h>
#include <dl-fptr.h>
#include <tls.h>

#define ELF_MACHINE_PLTREL_OVERLAP

extern struct frv_local_t {
  struct elf32_fdpic_loadmap *prog_load_map;
  struct elf32_fdpic_loadmap *ldso_load_map;
  Elf32_Addr ldso_dynamic_ptr;
} *frv_local(void) __attribute__ ((pure, visibility ("hidden")));
#define frv_prog_load_map (frv_local ()->prog_load_map)
#define frv_ldso_load_map (frv_local ()->ldso_load_map)
#define frv_ldso_dynamic_ptr (frv_local ()->ldso_dynamic_ptr)

/* Return nonzero iff ELF header is compatible with the running host.  */
static inline int __attribute__ ((unused))
elf_machine_matches_host (const Elf32_Ehdr *ehdr)
{
  return ehdr->e_machine == EM_CYGNUS_FRV
    && ehdr->e_flags & EF_FRV_FDPIC;
}

#undef elf_machine_relocated_dynamic
#define elf_machine_relocated_dynamic(l_addr) ((void*)frv_ldso_dynamic_ptr)

/* Return the run-time load address of the shared object.  */
static inline DL_LOADADDR_TYPE __attribute__ ((unused))
elf_machine_load_address (void)
{
  DL_LOADADDR_TYPE ret = { frv_ldso_load_map
			   ? frv_ldso_load_map
			   : frv_prog_load_map,
			   0 };
  return ret;
}

/* We used to set got_value in elf_machine_runtime_setup, but that's
   too late if we have to resolve a FUNCDESC relocation from one
   library to a symbol in another library that still hasn't had
   elf_machine_runtime_setup run.  */

# define ELF_MACHINE_DYN_FINISH(l) \
  ((l)->l_addr.got_value = (void *) D_PTR ((l), l_info[DT_PLTGOT]))

/* Set up the loaded object described by L so its unrelocated PLT
   entries will jump to the on-demand fixup code in dl-runtime.c.  */

static inline int __attribute__ ((unused, always_inline))
elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
{
  extern void _dl_runtime_resolve (void)
    __attribute__ ((visibility ("hidden")));

  *(frv_fdesc_t*)l->l_addr.got_value = *(frv_fdesc_t*)&_dl_runtime_resolve;
  ((void**)l->l_addr.got_value)[2] = l;
  return lazy;
}

#define ELF_MACHINE_RUNTIME_TRAMPOLINE asm (		\
"	.text\n"					\
"	.p2align 4\n"					\
\
"	.hidden	_dl_runtime_resolve\n"			\
"	.global	_dl_runtime_resolve\n"			\
"	.type	_dl_runtime_resolve,@function\n"	\
\
"_dl_runtime_resolve:\n"				\
	/* Preserve arguments.  */			\
"	addi	sp, -8*4, sp\n"				\
"	stdi	gr8, @(sp, 8)\n"			\
"	stdi	gr10, @(sp, 16)\n"			\
"	stdi	gr12, @(sp, 24)\n"			\
"	movsg	lr,gr8\n"				\
"	st	gr8, @(sp,gr0)\n"			\
\
	/* Prepare to call fixup.  */			\
"	ldi	@(gr15, 8), gr8\n"			\
"	ldi	@(gr14, -4), gr9\n"			\
"	mov.p	gr5, gr15\n"				\
"	call	fixup\n"				\
\
	/* Move aside return value that contains the FUNCDESC_VALUE.  */ \
"	ldd	@(gr8,gr0),gr14\n"			\
\
	/* Restore arguments.  */			\
"	ld	@(sp, gr0), gr8\n"			\
"	movgs	gr8,lr\n"				\
"	lddi	@(sp, 24), gr12\n"			\
"	lddi	@(sp, 16), gr10\n"			\
"	lddi	@(sp, 8), gr8\n"			\
"	addi	sp, 8*4, sp\n"				\
\
	/* Now jump to the actual function.  */		\
"	jmpl	@(gr14, gr0)\n"				\
"	.size	_dl_runtime_resolve, . - _dl_runtime_resolve\n"	\
);

/* Initial entry point code for the dynamic linker.
   The C function `_dl_start' is the real entry point;
   its return value is the user program's entry point.  */

/* At interpreter start up, GR16 and GR17 point to the program's and
   interpreter's load map, respectively, and GR18 points to the
   interpreter's PT_DYNAMIC section (or the program's, if the
   interpreter was run as a program).  */

#define RTLD_START asm (		\
".data\n"				\
".p2align 3\n"				\
".Lfrv_local: .word 0, 0, 0\n"		\
".text\n"				\
"	.hidden frv_local\n"		\
"	.type frv_local,@function\n"	\
"frv_local:\n"				\
"	sethi.p	#gotoffhi(.Lfrv_local), gr8\n" \
"	setlo	#gotofflo(.Lfrv_local), gr8\n" \
"	add.p	gr8, gr15, gr8\n"	\
"	bralr\n"			\
"	.global _start\n"		\
"	.type _start,@function\n"	\
"_start:\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"		\
"	sethi.p	#gotoffhi(.Lfrv_local), gr4\n"	\
"	setlo	#gotofflo(.Lfrv_local), gr4\n"	\
"	std.p	gr16, @(gr4, gr8)\n"	\
"	add	gr4, gr8, gr4\n"	\
"	mov.p	gr8, gr15\n"		\
"	mov	sp, gr8\n"		\
"	sti.p	gr18, @(gr4, 8)\n"	\
"	mov	gr15, gr20\n"		\
"	call	_dl_start\n"		\
"	mov	gr20, gr15\n"		\
"	.size _start, .-_start\n"	\
"	.global _dl_start_user\n"	\
"	.type _dl_start_user,@function\n" \
"_dl_start_user:\n"			\
"	mov.p	gr8, gr19\n"	\
"	mov	gr15, gr20\n"	\
"	sethi.p	#gotoffhi(_dl_skip_args), gr4\n"	\
"	setlo	#gotofflo(_dl_skip_args), gr4\n"	\
"	ld.p	@(sp, gr0), gr5\n"	\
"	sethi	#gotoffhi(_rtld_local), gr8\n"	\
"	ld.p	@(gr4, gr15), gr6\n"	\
"	setlo	#gotofflo(_rtld_local), gr8\n"	\
"	ld.p	@(gr15, gr8), gr8\n"	\
"	slli	gr5, #2, gr11\n"	\
"	slli.p	gr6, #2, gr4\n"	\
"	sub	gr5, gr6, gr5\n"	\
"	add.p	gr11, sp, gr11\n"	\
"	add	gr4, sp, sp\n"	\
"	mov.p	gr5, gr9\n"	\
"	addi	gr11, #8, gr11\n"	\
"	st.p	gr5, @(sp,gr0)\n"	\
"	addi	sp, #4, gr10\n"	\
"	call	_dl_init_internal\n"	\
/* The code above may leave sp mis-aligned.  That's sort-of ok as long
   as crtstart.S compensates for that in case ld.so runs as the main
   program starting another program.  If we must keep the stack
   properly aligned at the program entry point even in this corner
   case, the code below might work, but it doesn't copy auxvec.
"	sethi.p #gotoffhi(_dl_skip_args), gr7\n"	\
"	sethi #gotoffhi(_rtld_local), gr8\n"	\
"	ld.p @(sp,gr0), gr9\n"	\
"	setlo #gotofflo(_dl_skip_args), gr7\n"	\
"	ld.p @(gr7,gr15), gr5\n"	\
"	addi sp,#4,gr10\n"	\
"	slli.p gr9,#2,gr4\n"	\
"	setlo #gotofflo(_rtld_local), gr8\n"	\
"	add.p gr4,gr10,gr4\n"	\
"	cmpi gr5,#0,icc0\n"	\
"	addi.p gr4,#4,gr11\n"	\
"	beq icc0,2,2f\n"	\
"	sub.p gr9,gr5,gr9\n"	\
"	slli gr5,#2,gr4\n"	\
"	st.p gr9, @(sp,gr0)\n"	\
"	add gr4,gr10,gr6\n"	\
"	ld.p @(gr7,gr15), gr5\n"	\
"	mov gr10, gr7\n"	\
"	slli gr5,#2,gr5\n"	\
"	sub gr11,gr5,gr11\n"	\
"	cmp gr6,gr11,icc0\n"	\
"	bnc icc0,0,1f\n"	\
"0:\n"	\
"	ld.p @(gr6,gr0), gr4\n"	\
"	addi gr6,#4,gr6\n"	\
"	cmp gr6,gr11,icc0\n"	\
"	st.p gr4, @(gr7,gr0)\n"	\
"	addi gr7,#4,gr7\n"	\
"	bc icc0,2,0b\n"	\
"1:\n"	\
"	ld.p @(gr6,gr0), gr4\n"	\
"	addi gr6,#4,gr6\n"	\
"	st.p gr4, @(gr7,gr0)\n"	\
"	cmpi gr4,#0,icc0\n"	\
"	addi.p gr7,#4,gr7\n"	\
"	bne icc0,2,1b\n"	\
"2:\n"	\
"	ld.p @(gr20,gr8), gr8\n"	\
"	call _dl_init_internal\n"	\
*/ \
"	ldd.p	@(gr19, gr0), gr14\n"	\
"	sethi	#gotofffuncdeschi(_dl_fini), gr8\n"	\
"	ldi.p	@(gr15, 8), gr16\n"	\
"	setlo	#gotofffuncdesclo(_dl_fini), gr8\n"	\
/* Get program's load map into GR16.  */ \
"	ld.p	@(gr16, gr0), gr16\n"	\
"	add	gr8, gr20, gr20\n"	\
"	movgs	gr0, lr\n"		\
"	jmpl	@(gr14, gr0)\n"		\
"	.size _dl_start_user, .-_dl_start_user\n" \
".previous\n");


/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry
   or TLS variables, so undefined references should not be allowed
   to define the value.

   ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve
   to one of the main executable's symbols, as for a COPY reloc.
   This is unused on FRV.  */

#if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
#define elf_machine_type_class(type)	\
  (((type) == R_FRV_FUNCDESC_VALUE	\
    || (type) == R_FRV_TLSOFF		\
    || (type) == R_FRV_TLSDESC_VALUE) * ELF_RTYPE_CLASS_PLT)
#else
#define elf_machine_type_class(type)	\
  (((type) == R_FRV_FUNCDESC_VALUE) * ELF_RTYPE_CLASS_PLT)
#endif

/* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
#define ELF_MACHINE_JMP_SLOT	 R_FRV_FUNCDESC_VALUE

/* Return the address of the entry point. */
#define ELF_MACHINE_START_ADDRESS(map, start)	\
  DL_STATIC_FUNCTION_ADDRESS (map, start)

#define elf_machine_profile_fixup_plt(l, reloc, rel_addr, value) \
  elf_machine_fixup_plt (l, reloc, rel_addr, value)

#define elf_machine_profile_plt(reloc_addr) ((Elf32_Addr) (reloc_addr))

/* Fixup a PLT entry to bounce directly to the function at VALUE.  */
static inline Elf32_Addr
elf_machine_fixup_plt (struct link_map *l, lookup_t t,
		       const Elf32_Rel *reloc,
		       Elf32_Addr *reloc_addr, Elf32_Addr value)
{
  frv_fdesc_t funcval;

  funcval.desc.ip = value;
  funcval.desc.gp = (ElfW(Addr)) t->l_addr.got_value;

  asm ("std%I0\t%1, %M0"
       : "=m" (*(struct fdesc *)reloc_addr)
       : "e" (funcval.asint));

  return (Elf32_Addr) reloc_addr;
}

/* Return the final value of a plt relocation.  */
static inline Elf32_Addr
elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc,
		       Elf32_Addr value)
{
  /* The addend of lazily-resolved PLT entries must be disregarded.  */
  return value;
}

/* FR-V never uses Elfxx_Rela dynamic relocations.  */
#define ELF_MACHINE_NO_RELA 1

#endif /* !dl_machine_h */

#ifdef RESOLVE_MAP

static inline void
elf_machine_rel_relative (DL_LOADADDR_TYPE l_addr,
			  const Elf32_Rel *reloc,
			  void *const reloc_addr_arg)
{
  /* We don't have any RELATIVE relocations.  */
}

extern void __attribute__((visibility("hidden")))
     _dl_tlsdesc_return(void), _dl_tlsdesc_dynamic(void);

/* Perform the relocation specified by RELOC and SYM (which is fully
   resolved).  MAP is the object containing the reloc.  */
static inline void
elf_machine_rel (struct link_map *map,
		 const Elf32_Rel *reloc,
		 const Elf32_Sym *sym,
		 const struct r_found_version *version,
		 void *const reloc_addr_arg)
{
  const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);

#ifndef RTLD_BOOTSTRAP
  if (__builtin_expect (r_type == R_FRV_NONE, 0))
    return;
  else
#endif
    {
      ElfW(Addr) *reloc_addr = reloc_addr_arg;
#ifndef RTLD_BOOTSTRAP
      struct { ElfW(Addr) v; } __attribute__((__packed__)) *reloc_addr_packed;
      const Elf32_Sym *refsym = sym;
      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
      Elf32_Addr raw_value = sym_map ? sym->st_value : 0;
      Elf32_Addr value = sym_map
	? DL_RELOC_ADDR (sym->st_value, sym_map->l_addr) : 0;
#else
      Elf32_Addr value = sym
	? DL_RELOC_ADDR (sym->st_value, map->l_addr) : 0;
#endif
      frv_fdesc_t funcval;

      switch (r_type)
	{
	case R_FRV_32:
#ifndef RTLD_BOOTSTRAP
	  asm ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr));

# ifndef SHARED
	  weak_extern (_dl_rtld_map);
# endif
	  if (map == &GL(dl_rtld_map))
	    /* Undo the relocation done here during bootstrapping.
	       Now we will relocate it anew, possibly using a
	       binding found in the user program or a loaded library
	       rather than the dynamic linker's built-in definitions
	       used while loading those libraries.  */
	    value -= DL_RELOC_ADDR (refsym->st_value, map->l_addr);

	  if ((long)reloc_addr_packed & 3)
	    reloc_addr_packed->v += value;
	  else
#endif
	    *reloc_addr += value;
	  break;

	case R_FRV_FUNCDESC_VALUE:
	  funcval.desc.ip = value;

	  /* The addend of FUNCDESC_VALUE relocations referencing
	     global symbols must be ignored, because it may hold the
	     address of a lazy PLT entry.  */
	  if (sym && ELF32_ST_BIND (sym->st_info) == STB_LOCAL)
	    {
#ifndef RTLD_BOOTSTRAP
	      /* If we've already applied this relocation to a local
		 symbol during bootstrapping, don't do it again.  */
	      if (map == &GL(dl_rtld_map))
		  break;
#endif
	      funcval.desc.ip += *reloc_addr;
	    }

#ifndef RTLD_BOOTSTRAP
	  if (sym_map)
	    funcval.desc.gp = (Elf32_Addr) sym_map->l_addr.got_value;
	  else
	    funcval.desc.gp = 0;
#else
	  if (sym)
	    funcval.desc.gp = (Elf32_Addr) map->l_addr.got_value;
	  else
	    funcval.desc.gp = 0;
#endif

	  asm ("std%I0\t%1, %M0"
	       : "=m" (*(struct fdesc *)reloc_addr)
	       : "e" (funcval.asint));

	  break;

	case R_FRV_FUNCDESC:
	  /* We don't apply FUNCDESC relocations during bootstrapping
	     because they're only used to reference weak pointers to
	     functions that are only going to be resolved when we load
	     additional libraries.  */
#ifndef RTLD_BOOTSTRAP
	  asm ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr));

	  if ((long)reloc_addr_packed & 3)
	    value += reloc_addr_packed->v;
	  else
	    value += *reloc_addr;

	  if (sym)
	    value = _dl_make_fptr (sym_map, sym, value);
	  else
	    value = 0;

	  if ((long)reloc_addr_packed & 3)
	    reloc_addr_packed->v = value;
	  else
	    *reloc_addr = value;
#endif

	  break;

#if defined USE_TLS && ! defined RTLD_BOOTSTRAP
	case R_FRV_TLSOFF:
	  if (sym_map)
	    {
	      CHECK_STATIC_TLS (map, sym_map);

	      asm ("" : "=r" (reloc_addr_packed) : "0" (reloc_addr));
	      
	      if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
		raw_value = value - (Elf32_Addr)sym_map->l_tls_initimage;
	      raw_value += sym_map->l_tls_offset - TP_BIAS;

	      if ((long)reloc_addr_packed & 3)
		reloc_addr_packed->v += raw_value;
	      else
		*reloc_addr += raw_value;
	    }

	  break;

	case R_FRV_TLSDESC_VALUE:
	  if (sym_map)
	    {
	      asm ("ldd%I1\t%M1, %0"
		   : "=e" (funcval.asint)
		   : "m" (*(struct fdesc *)reloc_addr));

	      if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
		raw_value = value - (Elf32_Addr)sym_map->l_tls_initimage;

#ifndef SHARED
	      CHECK_STATIC_TLS (map, sym_map);
#else
	      if (!TRY_STATIC_TLS (map, sym_map))
		{
		  funcval.desc.ip = ((struct fdesc *)&_dl_tlsdesc_dynamic)->ip;
		  funcval.desc.gp = _dl_make_tlsdesc
		    (sym_map, raw_value + funcval.desc.gp,
		     ((struct fdesc *)&_dl_tlsdesc_dynamic)->gp);
		}
	      else
#endif
		{
		  funcval.desc.ip = ((struct fdesc *)&_dl_tlsdesc_return)->ip;
		  funcval.desc.gp += raw_value
		    + sym_map->l_tls_offset - TP_BIAS;
		}

	      asm ("std%I0\t%1, %M0"
		   : "=m" (*(struct fdesc *)reloc_addr)
		   : "e" (funcval.asint));
	    }

	  break;

#endif /* USE_TLS && ! RTLD_BOOTSTRAP */

	default:
	  _dl_reloc_bad_type (map, r_type, 0);
	  break;
	}
    }
}

/* Perform a RELATIVE reloc on the .got entry that transfers to the .plt.  */
static inline void
elf_machine_lazy_rel (struct link_map *map,
		      DL_LOADADDR_TYPE l_addr,
		      const Elf32_Rel *reloc)
{
  int r_type;
  frv_fdesc_t volatile *reloc_addr;
  frv_fdesc_t funcval;

  reloc_addr = (frv_fdesc_t *) DL_RELOC_ADDR (reloc->r_offset, l_addr);
  r_type = ELF32_R_TYPE (reloc->r_info);

  if (r_type == R_FRV_FUNCDESC_VALUE)
    {
      funcval = *reloc_addr;
      funcval.desc.ip = DL_RELOC_ADDR (funcval.desc.ip, l_addr);
      funcval.desc.gp = (Elf32_Addr) l_addr.got_value;
      *reloc_addr = funcval;
    }
  else if (r_type != R_FRV_NONE)
    _dl_reloc_bad_type (map, r_type, 1);
}

#endif /* RESOLVE_MAP */
