]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/alpha/dl-machine.h
Update.
[thirdparty/glibc.git] / sysdeps / alpha / dl-machine.h
index c751936776b76b7c90eea743f1c01905dfa3dcd1..e42ed3db6811aa23637bfc0fa72826c54b4aab10 100644 (file)
@@ -1,31 +1,33 @@
 /* Machine-dependent ELF dynamic relocation inline functions.  Alpha version.
-Copyright (C) 1996 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
-Contributed by Richard Henderson <rth@tamu.edu>.
-
-The GNU C Library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 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
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
-
-/* This was written in the absense of an ABI -- don't expect
+   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson <rth@tamu.edu>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This was written in the absence of an ABI -- don't expect
    it to remain unchanged.  */
 
+#ifndef dl_machine_h
+#define dl_machine_h 1
+
 #define ELF_MACHINE_NAME "alpha"
 
 #include <assert.h>
 #include <string.h>
-#include <link.h>
 
 
 /* Return nonzero iff E_MACHINE is compatible with the running host.  */
@@ -35,17 +37,20 @@ elf_machine_matches_host (Elf64_Word e_machine)
   return e_machine == EM_ALPHA;
 }
 
-
-/* Return the run-time address of the _GLOBAL_OFFSET_TABLE_.
-   Must be inlined in a function which uses global data.  */
-static inline Elf64_Addr *
-elf_machine_got (void)
+/* Return the link-time address of _DYNAMIC.  The multiple-got-capable
+   linker no longer allocates the first .got entry for this.  But not to
+   worry, no special tricks are needed.  */
+static inline Elf64_Addr
+elf_machine_dynamic (void)
 {
-  register Elf64_Addr gp __asm__("$29");
-  return (Elf64_Addr *)(gp - 0x8000);
+#ifndef NO_AXP_MULTI_GOT_LD
+  return (Elf64_Addr) &_DYNAMIC;
+#else
+  register Elf64_Addr *gp __asm__ ("$29");
+  return gp[-4096];
+#endif
 }
 
-
 /* Return the run-time load address of the shared object.  */
 static inline Elf64_Addr
 elf_machine_load_address (void)
@@ -78,180 +83,15 @@ elf_machine_load_address (void)
   return dot + 4 + zero_disp;
 }
 
-
-/* Fix up the instructions of a PLT entry to invoke the function
-   rather than the dynamic linker.  */
-static inline void
-elf_alpha_fix_plt(struct link_map *l,
-                 const Elf64_Rela *reloc,
-                 Elf64_Addr got_addr,
-                 Elf64_Addr value)
-{
-  const Elf64_Rela *rela_plt;
-  Elf64_Word *plte;
-  long edisp;
-
-  /* Recover the PLT entry address by calculating reloc's index into the
-     .rela.plt, and finding that entry in the .plt.  */
-
-  rela_plt = (void *)(l->l_addr + l->l_info[DT_JMPREL]->d_un.d_ptr);
-
-  plte = (void *)(l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr + 32);
-  plte += 3 * (reloc - rela_plt);
-
-  /* Find the displacement from the plt entry to the function.  */
-
-  edisp = (long)(value - (Elf64_Addr)&plte[3]) / 4;
-
-  if (edisp >= -0x100000 && edisp < 0x100000)
-    {
-      /* If we are in range, use br to perfect branch prediction and
-        elide the dependancy on the address load.  This case happens,
-        e.g., when a shared library call is resolved to the same library.  */
-
-      int hi, lo;
-      hi = value - (Elf64_Addr)&plte[0];
-      lo = (short)hi;
-      hi = (hi - lo) >> 16;
-
-      /* Emit "ldah $27,H($27)" */
-      plte[0] = 0x277b0000 | (hi & 0xffff);
-
-      /* Emit "lda $27,L($27)" */
-      plte[1] = 0x237b0000 | (lo & 0xffff);
-
-      /* Emit "br $31,function" */
-      plte[2] = 0xc3e00000 | (edisp & 0x1fffff);
-    }
-  else
-    {
-      /* Don't bother with the hint since we already know the hint is
-        wrong.  Eliding it prevents the wrong page from getting pulled
-        into the cache.  */
-
-      int hi, lo;
-      hi = got_addr - (Elf64_Addr)&plte[0];
-      lo = (short)hi;
-      hi = (hi - lo) >> 16;
-
-      /* Emit "ldah $27,H($27)" */
-      plte[0] = 0x277b0000 | (hi & 0xffff);
-
-      /* Emit "ldq $27,L($27)" */
-      plte[1] = 0xa77b0000 | (lo & 0xffff);
-
-      /* Emit "jmp $31,($27)" */
-      plte[2] = 0x6bfb0000;
-    }
-
-  /* Flush the instruction cache now that we've diddled.   Tag it as
-     modifying memory to checkpoint memory writes during optimization.  */
-  asm volatile("call_pal 0x86" : : : "memory");
-}
-
-/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
-   MAP is the object containing the reloc.  */
-static inline void
-elf_machine_rela (struct link_map *map,
-                 const Elf64_Rela *reloc,
-                 const Elf64_Sym *sym,
-                 Elf64_Addr (*resolve) (const Elf64_Sym **ref,
-                                        Elf64_Addr reloc_addr,
-                                        int noplt))
-{
-  Elf64_Addr *const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
-  unsigned long r_info = ELF64_R_TYPE (reloc->r_info);
-
-  /* We cannot use a switch here because we cannot locate the switch
-     jump table until we've self-relocated.  */
-
-  if (r_info == R_ALPHA_RELATIVE)
-    {
-      /* Already done in dynamic linker.  */
-      if (!resolve || map != &_dl_rtld_map)
-       *reloc_addr += map->l_addr;
-    }
-  else if (r_info == R_ALPHA_NONE)
-    ;
-  else
-    {
-      Elf64_Addr loadbase, sym_value;
-
-      if (resolve)
-       {
-          loadbase = (*resolve)(&sym, (Elf64_Addr)reloc_addr,
-                               r_info == R_ALPHA_JMP_SLOT);
-       }
-      else
-       loadbase = map->l_addr;
-
-      sym_value = sym ? loadbase + sym->st_value : 0;
-
-      if (r_info == R_ALPHA_GLOB_DAT)
-       {
-         *reloc_addr = sym_value;
-       }
-      else if (r_info == R_ALPHA_JMP_SLOT)
-       {
-         *reloc_addr = sym_value;
-         elf_alpha_fix_plt(map, reloc, (Elf64_Addr)reloc_addr, sym_value);
-       }
-      else if (r_info == R_ALPHA_REFQUAD)
-       {
-         sym_value += *reloc_addr;
-         if (resolve && map == &_dl_rtld_map)
-           {
-             /* Undo the relocation done here during bootstrapping.
-                Now we will relocate 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.  */
-             const Elf64_Sym *const dlsymtab
-               = (void *)(map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
-             sym_value -= map->l_addr;
-             sym_value -= dlsymtab[ELF64_R_SYM(reloc->r_info)].st_value;
-           }
-         else
-           sym_value += reloc->r_addend;
-         *reloc_addr = sym_value;
-       }
-      else if (r_info == R_ALPHA_COPY)
-       memcpy (reloc_addr, (void *) sym_value, sym->st_size);
-      else
-       assert (! "unexpected dynamic reloc type");
-    }
-}
-
-static inline void
-elf_machine_lazy_rel (struct link_map *map, const Elf64_Rela *reloc)
-{
-  Elf64_Addr *const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
-  unsigned long r_info = ELF64_R_TYPE (reloc->r_info);
-
-  if (r_info == R_ALPHA_JMP_SLOT)
-    {
-      /* Perform a RELATIVE reloc on the .got entry that transfers
-        to the .plt.  */
-      *reloc_addr += map->l_addr;
-    }
-  else if (r_info == R_ALPHA_NONE)
-    ;
-  else
-    assert (! "unexpected PLT reloc type");
-}
-
-/* The alpha never uses Elf_Rel relocations.  */
-#define ELF_MACHINE_NO_REL 1
-
-
 /* 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 void
-elf_machine_runtime_setup (struct link_map *l, int lazy)
+static inline int
+elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
 {
   Elf64_Addr plt;
   extern void _dl_runtime_resolve (void);
+  extern void _dl_runtime_profile (void);
 
   if (l->l_info[DT_JMPREL] && lazy)
     {
@@ -261,20 +101,32 @@ elf_machine_runtime_setup (struct link_map *l, int lazy)
       plt = l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr;
 
       /* This function will be called to perform the relocation.  */
-      *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_resolve;
+      if (!profile)
+        *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_resolve;
+      else
+       {
+         *(Elf64_Addr *)(plt + 16) = (Elf64_Addr) &_dl_runtime_profile;
+         /* Say that we really want profiling and the timers are started.  */
+         _dl_profile_map = l;
+       }
 
       /* Identify this shared object */
       *(Elf64_Addr *)(plt + 24) = (Elf64_Addr) l;
+
+      /* If the first instruction of the plt entry is not
+        "br $28, plt0", we cannot do lazy relocation.  */
+      lazy = (*(unsigned *)(plt + 32) == 0xc39ffff7);
     }
+
+  return lazy;
 }
 
 /* This code is used in dl-runtime.c to call the `fixup' function
    and then redirect to the address it returns.  */
-#define ELF_MACHINE_RUNTIME_TRAMPOLINE asm ( \
-"/* Trampoline for _dl_runtime_resolver */
-       .globl _dl_runtime_resolve
-       .ent _dl_runtime_resolve
-_dl_runtime_resolve:
+#define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name, IMB) asm ( "\
+       .globl " #tramp_name "
+       .ent " #tramp_name "
+" #tramp_name ":
        lda     $sp, -168($sp)
        .frame  $sp, 168, $26
        /* Preserve all registers that C normally doesn't.  */
@@ -304,14 +156,18 @@ _dl_runtime_resolve:
        br      $gp, .+4
        ldgp    $gp, 0($gp)
        .prologue 1
-       /* Set up the arguments for _dl_runtime_resolve. */
+       /* Set up the arguments for fixup: */
        /* $16 = link_map out of plt0 */
+       /* $17 = offset of reloc entry = ($28 - $27 - 20) /12 * 24 */
+       /* $18 = return address */
+       subq    $28, $27, $17
        ldq     $16, 8($27)
-       /* $17 = offset of reloc entry */
-       mov     $28, $17
+       subq    $17, 20, $17
+       mov     $26, $18
+       addq    $17, $17, $17
        /* Do the fixup */
-       bsr     $26, fixup..ng
-       /* Move the destination address to a safe place.  */
+       bsr     $26, " ASM_ALPHA_NG_SYMBOL_PREFIX #fixup_name "..ng
+       /* Move the destination address into position.  */
        mov     $0, $27
        /* Restore program registers.  */
        ldq     $26, 0($sp)
@@ -335,18 +191,24 @@ _dl_runtime_resolve:
        ldq     $24, 144($sp)
        ldq     $25, 152($sp)
        ldq     $29, 160($sp)
+       /* Flush the Icache after having modified the .plt code.  */
+       " #IMB "
        /* Clean up and turn control to the destination */
        lda     $sp, 168($sp)
        jmp     $31, ($27)
-       .end _dl_runtime_resolve");
-
-/* The PLT uses Elf_Rel relocs.  */
-#define elf_machine_relplt elf_machine_rela
-
-/* Mask identifying addresses reserved for the user program,
-   where the dynamic linker should not map anything.  */
-/* FIXME */
-#define ELF_MACHINE_USER_ADDRESS_MASK  (~0x1FFFFFFFFUL)
+       .end " #tramp_name)
+
+#ifndef PROF
+#define ELF_MACHINE_RUNTIME_TRAMPOLINE                                 \
+  TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup, imb);       \
+  TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup, #nop);
+#else
+#define ELF_MACHINE_RUNTIME_TRAMPOLINE                         \
+  TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup, imb);       \
+  extern void _dl_runtime_resolve (void);                      \
+  extern void _dl_runtime_profile (void);                      \
+  strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
+#endif
 
 /* Initial entry point code for the dynamic linker.
    The C function `_dl_start' is the real entry point;
@@ -355,13 +217,17 @@ _dl_runtime_resolve:
 #define RTLD_START asm ("\
 .text
        .globl _start
-       .globl _dl_start_user
+       .ent _start
 _start:
-       br      $gp,.+4
-       ldgp    $gp, 0($gp)
+       br      $gp, 0f
+0:     ldgp    $gp, 0($gp)
        /* Pass pointer to argument block to _dl_start.  */
        mov     $sp, $16
-       bsr     $26, _dl_start..ng
+       bsr     $26, "ASM_ALPHA_NG_SYMBOL_PREFIX"_dl_start..ng
+       .end _start
+       /* FALLTHRU */
+       .globl _dl_start_user
+       .ent _dl_start_user
 _dl_start_user:
        /* Save the user entry point address in s0.  */
        mov     $0, $9
@@ -376,7 +242,7 @@ _dl_start_user:
        stq     $2, 0($sp)
        /* Load _dl_default_scope[2] into s1 to pass to _dl_init_next.  */
 0:     ldq     $10, _dl_default_scope+16
-       /* Call _dl_init_next to return the address of an initalizer
+       /* Call _dl_init_next to return the address of an initializer
           function to run.  */
 1:     mov     $10, $16
        jsr     $26, _dl_init_next
@@ -386,8 +252,207 @@ _dl_start_user:
        jsr     $26, ($0)
        ldgp    $gp, 0($26)
        br      1b
-2:     /* Pass our finalizer function to the user in $0. */
+2:     /* Clear the startup flag.  */
+       .set at
+       stl     $31, _dl_starting_up
+       .set noat
+       /* Pass our finalizer function to the user in $0. */
        lda     $0, _dl_fini
        /* Jump to the user's entry point.  */
        mov     $9, $27
-       jmp     ($9)");
+       jmp     ($9)
+       .end _dl_start_user
+.previous");
+
+/* Nonzero iff TYPE describes relocation of a PLT entry, so
+   PLT entries should not be allowed to define the value.  */
+#define elf_machine_lookup_noplt_p(type)  ((type) == R_ALPHA_JMP_SLOT)
+
+/* Nonzero iff TYPE should not be allowed to resolve to one of
+   the main executable's symbols, as for a COPY reloc, which we don't use.  */
+#define elf_machine_lookup_noexec_p(type)  (0)
+
+/* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
+#define ELF_MACHINE_JMP_SLOT    R_ALPHA_JMP_SLOT
+
+/* The alpha never uses Elf64_Rel relocations.  */
+#define ELF_MACHINE_NO_REL 1
+
+/* Fix up the instructions of a PLT entry to invoke the function
+   rather than the dynamic linker.  */
+static inline void
+elf_machine_fixup_plt(struct link_map *l, const Elf64_Rela *reloc,
+                     Elf64_Addr *got_addr, Elf64_Addr value)
+{
+  const Elf64_Rela *rela_plt;
+  Elf64_Word *plte;
+  long edisp;
+
+  /* Store the value we are going to load.  */
+  *got_addr = value;
+
+  /* Recover the PLT entry address by calculating reloc's index into the
+     .rela.plt, and finding that entry in the .plt.  */
+  rela_plt = (void *)(l->l_addr + l->l_info[DT_JMPREL]->d_un.d_ptr);
+  plte = (void *)(l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr + 32);
+  plte += 3 * (reloc - rela_plt);
+
+  /* Find the displacement from the plt entry to the function.  */
+  edisp = (long)(value - (Elf64_Addr)&plte[3]) / 4;
+
+  if (edisp >= -0x100000 && edisp < 0x100000)
+    {
+      /* If we are in range, use br to perfect branch prediction and
+        elide the dependency on the address load.  This case happens,
+        e.g., when a shared library call is resolved to the same library.  */
+
+      int hi, lo;
+      hi = value - (Elf64_Addr)&plte[0];
+      lo = (short)hi;
+      hi = (hi - lo) >> 16;
+
+      /* Emit "lda $27,lo($27)" */
+      plte[1] = 0x237b0000 | (lo & 0xffff);
+
+      /* Emit "br $31,function" */
+      plte[2] = 0xc3e00000 | (edisp & 0x1fffff);
+
+      /* Think about thread-safety -- the previous instructions must be
+        committed to memory before the first is overwritten.  */
+      __asm__ __volatile__("wmb" : : : "memory");
+
+      /* Emit "ldah $27,hi($27)" */
+      plte[0] = 0x277b0000 | (hi & 0xffff);
+    }
+  else
+    {
+      /* Don't bother with the hint since we already know the hint is
+        wrong.  Eliding it prevents the wrong page from getting pulled
+        into the cache.  */
+
+      int hi, lo;
+      hi = (Elf64_Addr)got_addr - (Elf64_Addr)&plte[0];
+      lo = (short)hi;
+      hi = (hi - lo) >> 16;
+
+      /* Emit "ldq $27,lo($27)" */
+      plte[1] = 0xa77b0000 | (lo & 0xffff);
+
+      /* Emit "jmp $31,($27)" */
+      plte[2] = 0x6bfb0000;
+
+      /* Think about thread-safety -- the previous instructions must be
+        committed to memory before the first is overwritten.  */
+      __asm__ __volatile__("wmb" : : : "memory");
+
+      /* Emit "ldah $27,hi($27)" */
+      plte[0] = 0x277b0000 | (hi & 0xffff);
+    }
+
+  /* At this point, if we've been doing runtime resolution, Icache is dirty.
+     This will be taken care of in _dl_runtime_resolve.  If instead we are
+     doing this as part of non-lazy startup relocation, that bit of code
+     hasn't made it into Icache yet, so there's nothing to clean up.  */
+}
+
+/* Return the final value of a plt relocation.  */
+static inline Elf64_Addr
+elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
+                      Elf64_Addr value)
+{
+  return value + reloc->r_addend;
+}
+
+#endif /* !dl_machine_h */
+
+#ifdef RESOLVE
+
+/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
+   MAP is the object containing the reloc.  */
+static inline void
+elf_machine_rela (struct link_map *map,
+                 const Elf64_Rela *reloc,
+                 const Elf64_Sym *sym,
+                 const struct r_found_version *version,
+                 Elf64_Addr *const reloc_addr)
+{
+  unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
+
+#ifndef RTLD_BOOTSTRAP
+  /* This is defined in rtld.c, but nowhere in the static libc.a; make the
+     reference weak so static programs can still link.  This declaration
+     cannot be done when compiling rtld.c (i.e.  #ifdef RTLD_BOOTSTRAP)
+     because rtld.c contains the common defn for _dl_rtld_map, which is
+     incompatible with a weak decl in the same file.  */
+  weak_extern (_dl_rtld_map);
+#endif
+
+  /* We cannot use a switch here because we cannot locate the switch
+     jump table until we've self-relocated.  */
+
+  if (r_type == R_ALPHA_RELATIVE)
+    {
+#ifndef RTLD_BOOTSTRAP
+      /* Already done in dynamic linker.  */
+      if (map != &_dl_rtld_map)
+#endif
+       *reloc_addr += map->l_addr;
+    }
+  else if (r_type == R_ALPHA_NONE)
+    return;
+  else
+    {
+      Elf64_Addr loadbase, sym_value;
+
+      loadbase = RESOLVE (&sym, version, r_type);
+      sym_value = sym ? loadbase + sym->st_value : 0;
+      sym_value += reloc->r_addend;
+
+      if (r_type == R_ALPHA_GLOB_DAT)
+       *reloc_addr = sym_value;
+      else if (r_type == R_ALPHA_JMP_SLOT)
+       elf_machine_fixup_plt (map, reloc, reloc_addr, sym_value);
+      else if (r_type == R_ALPHA_REFQUAD)
+       {
+         sym_value += *reloc_addr;
+#ifndef RTLD_BOOTSTRAP
+         if (map == &_dl_rtld_map)
+           {
+             /* Undo the relocation done here during bootstrapping.
+                Now we will relocate 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.  */
+             const Elf64_Sym *const dlsymtab
+               = (void *)(map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
+             sym_value -= map->l_addr;
+             sym_value -= dlsymtab[ELF64_R_SYM(reloc->r_info)].st_value;
+             sym_value -= reloc->r_addend;
+           }
+#endif
+         *reloc_addr = sym_value;
+       }
+      else
+       assert (! "unexpected dynamic reloc type");
+    }
+}
+
+static inline void
+elf_machine_lazy_rel (struct link_map *map, const Elf64_Rela *reloc)
+{
+  Elf64_Addr * const reloc_addr = (void *)(map->l_addr + reloc->r_offset);
+  unsigned long const r_type = ELF64_R_TYPE (reloc->r_info);
+
+  if (r_type == R_ALPHA_JMP_SLOT)
+    {
+      /* Perform a RELATIVE reloc on the .got entry that transfers
+        to the .plt.  */
+      *reloc_addr += map->l_addr;
+    }
+  else if (r_type == R_ALPHA_NONE)
+    return;
+  else
+    assert (! "unexpected PLT reloc type");
+}
+
+#endif /* RESOLVE */