Blame sysdeps/nios2/dl-machine.h

Packit 6c4009
/* Machine-dependent ELF dynamic relocation inline functions.  Nios II version.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library.  If not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#ifndef dl_machine_h
Packit 6c4009
#define dl_machine_h
Packit 6c4009
Packit 6c4009
#define ELF_MACHINE_NAME "nios2"
Packit 6c4009
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <link.h>
Packit 6c4009
#include <dl-tls.h>
Packit 6c4009
Packit 6c4009
/* Return nonzero iff ELF header is compatible with the running host.  */
Packit 6c4009
static inline int
Packit 6c4009
elf_machine_matches_host (const Elf32_Ehdr *ehdr)
Packit 6c4009
{
Packit 6c4009
  return ehdr->e_machine == EM_ALTERA_NIOS2;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Return the link-time address of _DYNAMIC.  Conveniently, this is the
Packit 6c4009
   first element of the GOT.  */
Packit 6c4009
static inline Elf32_Addr
Packit 6c4009
elf_machine_dynamic (void)
Packit 6c4009
{
Packit 6c4009
  Elf32_Addr *dynamic;
Packit 6c4009
  int tmp;
Packit 6c4009
  asm ("nextpc\t%0\n\t"
Packit 6c4009
       "1: movhi\t%1, %%hiadj(_GLOBAL_OFFSET_TABLE_ - 1b)\n\t"
Packit 6c4009
       "addi\t%1, %1, %%lo(_GLOBAL_OFFSET_TABLE_ - 1b)\n\t"
Packit 6c4009
       "add\t%0, %0, %1\n"
Packit 6c4009
       : "=r" (dynamic), "=r" (tmp));
Packit 6c4009
  return *dynamic;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Return the run-time load address of the shared object.  */
Packit 6c4009
static inline Elf32_Addr
Packit 6c4009
elf_machine_load_address (void)
Packit 6c4009
{
Packit 6c4009
  Elf32_Addr result;
Packit 6c4009
  int tmp;
Packit 6c4009
  asm ("nextpc\t%0\n\t"
Packit 6c4009
       "1: movhi\t%1, %%hiadj(1b)\n\t"
Packit 6c4009
       "addi\t%1, %1, %%lo(1b)\n\t"
Packit 6c4009
       "sub\t%0, %0, %1\n"
Packit 6c4009
       : "=r" (result), "=r" (tmp));
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Set up the loaded object described by L so its unrelocated PLT
Packit 6c4009
   entries will jump to the on-demand fixup code in dl-runtime.c.  */
Packit 6c4009
Packit 6c4009
static inline int __attribute__ ((always_inline))
Packit 6c4009
elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
Packit 6c4009
{
Packit 6c4009
  extern void _dl_runtime_resolve (Elf32_Word);
Packit 6c4009
Packit 6c4009
  if (l->l_info[DT_JMPREL] && lazy)
Packit 6c4009
    {
Packit 6c4009
      /* The GOT entries for functions in the PLT have not yet been filled
Packit 6c4009
         in.  Their initial contents will arrange when called to load r15 with
Packit 6c4009
         an offset into the .got section, load r14 with
Packit 6c4009
	 _GLOBAL_OFFSET_TABLE_[1], and then jump to _GLOBAL_OFFSET_TABLE[2].
Packit 6c4009
      */
Packit 6c4009
      Elf32_Addr *got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
Packit 6c4009
      got[1] = (Elf32_Addr) l;	/* Identify this shared object.  */
Packit 6c4009
Packit 6c4009
      /* This function will get called to fix up the GOT entry indicated by
Packit 6c4009
	 the offset on the stack, and then jump to the resolved address.  */
Packit 6c4009
      got[2] = (Elf32_Addr) &_dl_runtime_resolve;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return lazy;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initial entry point code for the dynamic linker.
Packit 6c4009
   The C function `_dl_start' is the real entry point;
Packit 6c4009
   its return value is the user program's entry point.  */
Packit 6c4009
Packit 6c4009
#define RTLD_START asm ("\
Packit 6c4009
.text\n\
Packit 6c4009
.globl _start\n\
Packit 6c4009
.type _start, %function\n\
Packit 6c4009
_start:\n\
Packit 6c4009
        /* At start time, all the args are on the stack.  */\n\
Packit 6c4009
        mov r4, sp\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Start the calculation of the GOT pointer.  */\n\
Packit 6c4009
        nextpc r22\n\
Packit 6c4009
1:      movhi r8, %hiadj(_gp_got - 1b)\n\
Packit 6c4009
        addi r8, r8, %lo(_gp_got - 1b)\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Figure out where _dl_start will need to return to.  */\n\
Packit 6c4009
        movhi ra, %hiadj(2f - 1b)\n\
Packit 6c4009
        addi ra, ra, %lo(2f - 1b)\n\
Packit 6c4009
        add ra, ra, r22\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Finish the calculation of the GOT pointer.  */\n\
Packit 6c4009
        add r22, r22, r8\n\
Packit 6c4009
\n\
Packit 6c4009
        br _dl_start\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Save the returned user entry point.  */\n\
Packit 6c4009
2:      mov r16, r2\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Initialize gp.  */\n\
Packit 6c4009
        ldw r4, %got(_rtld_local)(r22)\n\
Packit 6c4009
        ldw r4, 0(r4)\n\
Packit 6c4009
        ldw r8, %call(_dl_nios2_get_gp_value)(r22)\n\
Packit 6c4009
        callr r8\n\
Packit 6c4009
        mov gp, r2\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Find the number of arguments to skip.  */\n\
Packit 6c4009
        ldw r8, %got(_dl_skip_args)(r22)\n\
Packit 6c4009
        ldw r8, 0(r8)\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Find the main_map from the GOT.  */\n\
Packit 6c4009
        ldw r4, %got(_rtld_local)(r22)\n\
Packit 6c4009
        ldw r4, 0(r4)\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Find argc.  */\n\
Packit 6c4009
        ldw r5, 0(sp)\n\
Packit 6c4009
        sub r5, r5, r8\n\
Packit 6c4009
        stw r5, 0(sp)\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Find the first unskipped argument.  */\n\
Packit 6c4009
        slli r8, r8, 2\n\
Packit 6c4009
        addi r6, sp, 4\n\
Packit 6c4009
        add r9, r6, r8\n\
Packit 6c4009
        mov r10, r6\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Shuffle argv down.  */\n\
Packit 6c4009
3:      ldw r11, 0(r9)\n\
Packit 6c4009
        stw r11, 0(r10)\n\
Packit 6c4009
        addi r9, r9, 4\n\
Packit 6c4009
        addi r10, r10, 4\n\
Packit 6c4009
        bne r11, zero, 3b\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Shuffle envp down.  */\n\
Packit 6c4009
        mov r7, r10\n\
Packit 6c4009
4:      ldw r11, 0(r9)\n\
Packit 6c4009
        stw r11, 0(r10)\n\
Packit 6c4009
        addi r9, r9, 4\n\
Packit 6c4009
        addi r10, r10, 4\n\
Packit 6c4009
        bne r11, zero, 4b\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Shuffle auxv down.  */\n\
Packit 6c4009
5:      ldw r11, 4(r9)\n\
Packit 6c4009
        stw r11, 4(r10)\n\
Packit 6c4009
        ldw r11, 0(r9)\n\
Packit 6c4009
        stw r11, 0(r10)\n\
Packit 6c4009
        addi r9, r9, 8\n\
Packit 6c4009
        addi r10, r10, 8\n\
Packit 6c4009
        bne r11, zero, 5b\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Update _dl_argv.  */\n\
Packit 6c4009
        ldw r2, %got(_dl_argv)(r22)\n\
Packit 6c4009
        stw r6, 0(r2)\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Call _dl_init through the PLT.  */\n\
Packit 6c4009
        ldw r8, %call(_dl_init)(r22)\n\
Packit 6c4009
        callr r8\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Find the finalization function.  */\n\
Packit 6c4009
        ldw r4, %got(_dl_fini)(r22)\n\
Packit 6c4009
\n\
Packit 6c4009
        /* Jump to the user's entry point.  */\n\
Packit 6c4009
        jmp r16\n\
Packit 6c4009
");
Packit 6c4009
Packit 6c4009
/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
Packit 6c4009
   PLT entries should not be allowed to define the value.
Packit 6c4009
   ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one
Packit 6c4009
   of the main executable's symbols, as for a COPY reloc.  */
Packit 6c4009
#define elf_machine_type_class(type)				\
Packit 6c4009
  ((((type) == R_NIOS2_JUMP_SLOT				\
Packit 6c4009
     || (type) == R_NIOS2_TLS_DTPMOD				\
Packit 6c4009
     || (type) == R_NIOS2_TLS_DTPREL				\
Packit 6c4009
     || (type) == R_NIOS2_TLS_TPREL) * ELF_RTYPE_CLASS_PLT)	\
Packit 6c4009
   | (((type) == R_NIOS2_COPY) * ELF_RTYPE_CLASS_COPY)		\
Packit 6c4009
   | (((type) == R_NIOS2_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA))
Packit 6c4009
Packit 6c4009
/* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
Packit 6c4009
#define ELF_MACHINE_JMP_SLOT  R_NIOS2_JUMP_SLOT
Packit 6c4009
Packit 6c4009
/* The Nios II never uses Elf32_Rel relocations.  */
Packit 6c4009
#define ELF_MACHINE_NO_REL 1
Packit 6c4009
#define ELF_MACHINE_NO_RELA 0
Packit 6c4009
Packit 6c4009
/* Fixup a PLT entry to bounce directly to the function at VALUE.  */
Packit 6c4009
Packit 6c4009
static inline Elf32_Addr
Packit 6c4009
elf_machine_fixup_plt (struct link_map *map, lookup_t t,
Packit 6c4009
		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
Packit 6c4009
		       const Elf32_Rela *reloc,
Packit 6c4009
		       Elf32_Addr *reloc_addr, Elf32_Addr value)
Packit 6c4009
{
Packit 6c4009
  return *reloc_addr = value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return the final value of a plt relocation.  */
Packit 6c4009
static inline Elf32_Addr
Packit 6c4009
elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
Packit 6c4009
                       Elf32_Addr value)
Packit 6c4009
{
Packit 6c4009
  return value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Names of the architecture-specific auditing callback functions.  */
Packit 6c4009
#define ARCH_LA_PLTENTER nios2_gnu_pltenter
Packit 6c4009
#define ARCH_LA_PLTEXIT nios2_gnu_pltexit
Packit 6c4009
Packit 6c4009
#endif /* dl_machine_h */
Packit 6c4009
Packit 6c4009
#ifdef RESOLVE_MAP
Packit 6c4009
Packit 6c4009
/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
Packit 6c4009
   LOADADDR is the load address of the object; INFO is an array indexed
Packit 6c4009
   by DT_* of the .dynamic section info.  */
Packit 6c4009
Packit 6c4009
auto inline void __attribute__ ((always_inline))
Packit 6c4009
elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
Packit 6c4009
                  const ElfW(Sym) *sym, const struct r_found_version *version,
Packit 6c4009
                  void *const reloc_addr_arg, int skip_ifunc)
Packit 6c4009
{
Packit 6c4009
  Elf32_Addr *const reloc_addr = reloc_addr_arg;
Packit 6c4009
  const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (r_type == R_NIOS2_RELATIVE))
Packit 6c4009
    *reloc_addr = map->l_addr + reloc->r_addend;
Packit 6c4009
  else if (__glibc_unlikely (r_type == R_NIOS2_NONE))
Packit 6c4009
    return;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      const Elf32_Sym *const refsym = sym;
Packit 6c4009
      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
Packit 6c4009
      Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
Packit 6c4009
Packit 6c4009
      switch (r_type)
Packit 6c4009
	{
Packit 6c4009
        case R_NIOS2_COPY:
Packit 6c4009
          if (sym == NULL)
Packit 6c4009
            /* This can happen in trace mode if an object could not be
Packit 6c4009
               found.  */
Packit 6c4009
            break;
Packit 6c4009
          if (sym->st_size > refsym->st_size
Packit 6c4009
              || (sym->st_size < refsym->st_size && GLRO(dl_verbose)))
Packit 6c4009
            {
Packit 6c4009
              const char *strtab;
Packit 6c4009
Packit 6c4009
              strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit 6c4009
              _dl_error_printf ("\
Packit 6c4009
%s: Symbol `%s' has different size in shared object, consider re-linking\n",
Packit 6c4009
				rtld_progname ?: "<program name unknown>",
Packit 6c4009
				strtab + refsym->st_name);
Packit 6c4009
            }
Packit 6c4009
          memcpy (reloc_addr_arg, (void *) value,
Packit 6c4009
                  MIN (sym->st_size, refsym->st_size));
Packit 6c4009
          break;
Packit 6c4009
	case R_NIOS2_GLOB_DAT:
Packit 6c4009
	case R_NIOS2_JUMP_SLOT:
Packit 6c4009
# ifdef RTLD_BOOTSTRAP
Packit 6c4009
          /* Fix weak undefined references.  */
Packit 6c4009
          if (sym != NULL && sym->st_value == 0)
Packit 6c4009
            *reloc_addr = 0;
Packit 6c4009
          else
Packit 6c4009
# endif
Packit 6c4009
            *reloc_addr = value;
Packit 6c4009
          break;
Packit 6c4009
#ifndef RTLD_BOOTSTRAP
Packit 6c4009
        case R_NIOS2_TLS_DTPMOD:
Packit 6c4009
          /* Get the information from the link map returned by the
Packit 6c4009
             resolv function.  */
Packit 6c4009
          if (sym_map != NULL)
Packit 6c4009
            *reloc_addr = sym_map->l_tls_modid;
Packit 6c4009
          break;
Packit 6c4009
Packit 6c4009
        case R_NIOS2_TLS_DTPREL:
Packit 6c4009
          *reloc_addr = reloc->r_addend + TLS_DTPREL_VALUE(sym);
Packit 6c4009
          break;
Packit 6c4009
Packit 6c4009
        case R_NIOS2_TLS_TPREL:
Packit 6c4009
          if (sym != NULL)
Packit 6c4009
            {
Packit 6c4009
              CHECK_STATIC_TLS (map, sym_map);
Packit 6c4009
              *reloc_addr = reloc->r_addend + TLS_TPREL_VALUE(sym_map, sym);
Packit 6c4009
            }
Packit 6c4009
          break;
Packit 6c4009
#endif
Packit 6c4009
        case R_NIOS2_BFD_RELOC_32:
Packit 6c4009
          *reloc_addr = value + reloc->r_addend;
Packit 6c4009
          break;
Packit 6c4009
Packit 6c4009
	default:
Packit 6c4009
          _dl_reloc_bad_type (map, r_type, 0);
Packit 6c4009
          break;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
auto inline void __attribute__((always_inline))
Packit 6c4009
elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
Packit 6c4009
			   void *const reloc_addr_arg)
Packit 6c4009
{
Packit 6c4009
  Elf32_Addr *const reloc_addr = reloc_addr_arg;
Packit 6c4009
  *reloc_addr = l_addr + reloc->r_addend;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
auto inline void __attribute__((always_inline))
Packit 6c4009
elf_machine_lazy_rel (struct link_map *map,
Packit 6c4009
		      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
Packit 6c4009
		      int skip_ifunc)
Packit 6c4009
{
Packit 6c4009
  Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
Packit 6c4009
  if (ELF32_R_TYPE (reloc->r_info) == R_NIOS2_JUMP_SLOT)
Packit 6c4009
    *reloc_addr += l_addr;
Packit 6c4009
  else
Packit 6c4009
    _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#endif /* RESOLVE_MAP */