]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/hppa/dl-machine.h
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / sysdeps / hppa / dl-machine.h
index 15e1c85b392499a4a6693b948f21a607259c45c0..387c554256a93e4a05165d08e356c9a68f52c667 100644 (file)
@@ -1,5 +1,5 @@
 /* Machine-dependent ELF dynamic relocation inline functions.  PA-RISC version.
-   Copyright (C) 1995,1996,1997,1999,2000,2001 Free Software Foundation, Inc.
+   Copyright (C) 1995-2019 Free Software Foundation, Inc.
    Contributed by David Huggins-Daines <dhd@debian.org>
    This file is part of the GNU C Library.
 
@@ -14,9 +14,8 @@
    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.  */
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifndef dl_machine_h
 #define dl_machine_h 1
 #define ELF_MACHINE_NAME "hppa"
 
 #include <sys/param.h>
+#include <assert.h>
 #include <string.h>
 #include <link.h>
-#include <assert.h>
+#include <errno.h>
+#include <dl-fptr.h>
+#include <abort-instr.h>
+#include <tls.h>
 
-/* These must match the definition of the stub in bfd/elf32-hppa.c. */
-#define SIZEOF_PLT_STUB (4*4)
-#define GOT_FROM_PLT_STUB (4*4)
+/* These two definitions must match the definition of the stub in
+   bfd/elf32-hppa.c (see plt_stub[]).
 
-/* A PLABEL is a function descriptor.  Properly they consist of just
-   FUNC and GP.  But we want to traverse a binary tree too.  See
-   dl-fptr.c for the code (it may be made common between HPPA and
-   IA-64 in the future).
+   a. Define the size of the *entire* stub we place at the end of the PLT
+   table (right up against the GOT).
 
-   We call these 'fptr' to make it easier to steal code from IA-64. */
+   b. Define the number of bytes back from the GOT to the entry point of
+   the PLT stub. You see the PLT stub must be entered in the middle
+   so it can depwi to find it's own address (long jump stub)
 
-/* ld.so currently has 12 PLABEL32 relocs.  We'll keep this constant
-   large for now in case we require more, as the rest of these will be
-   used by the dynamic program itself (libc.so has quite a few
-   PLABEL32 relocs in it). */
-#define HPPA_BOOT_FPTR_SIZE    256
+   c. Define the size of a single PLT entry so we can jump over the
+   last entry to get the stub address */
+
+#define SIZEOF_PLT_STUB (7*4)
+#define GOT_FROM_PLT_STUB (4*4)
+#define PLT_ENTRY_SIZE (2*4)
 
-struct hppa_fptr
+/* Initialize the function descriptor table before relocations */
+static inline void
+__hppa_init_bootstrap_fdesc_table (struct link_map *map)
 {
-  Elf32_Addr func;
-  Elf32_Addr gp;
-  struct hppa_fptr *next;
-};
+  ElfW(Addr) *boot_table;
+
+  /* Careful: this will be called before got has been relocated... */
+  ELF_MACHINE_LOAD_ADDRESS(boot_table,_dl_boot_fptr_table);
 
-extern struct hppa_fptr __boot_ldso_fptr[];
-extern struct hppa_fptr *__fptr_root;
-extern int __fptr_count;
+  map->l_mach.fptr_table_len = ELF_MACHINE_BOOT_FPTR_TABLE_LEN;
+  map->l_mach.fptr_table = boot_table;
+}
 
-extern Elf32_Addr __hppa_make_fptr (const struct link_map *, Elf32_Addr,
-                                   struct hppa_fptr **, struct hppa_fptr *);
+#define ELF_MACHINE_BEFORE_RTLD_RELOC(dynamic_info)            \
+       __hppa_init_bootstrap_fdesc_table (BOOTSTRAP_MAP);      \
+       _dl_fptr_init();
 
 /* Return nonzero iff ELF header is compatible with the running host.  */
 static inline int
@@ -66,69 +72,65 @@ elf_machine_matches_host (const Elf32_Ehdr *ehdr)
   return ehdr->e_machine == EM_PARISC;
 }
 
-
 /* Return the link-time address of _DYNAMIC.  */
+static inline Elf32_Addr
+elf_machine_dynamic (void) __attribute__ ((const));
+
 static inline Elf32_Addr
 elf_machine_dynamic (void)
 {
   Elf32_Addr dynamic;
 
-#if 0
-  /* Use this method if GOT address not yet set up.  */
-  asm (
-"      b,l     1f,%0\n"
-"      depi    0,31,2,%0\n"
-"1:    addil   L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 8),%0\n"
-"      ldw     R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 12)(%%r1),%0\n"
-      : "=r" (dynamic) : : "r1");
-#else
-  /* This works because we already have our GOT address available.  */
-  dynamic = (Elf32_Addr) &_DYNAMIC;
-#endif
+  asm ("bl     1f,%0\n"
+"      addil   L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 1),%0\n"
+"1:    ldw     R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 5)(%%r1),%0\n"
+       : "=r" (dynamic) : : "r1");
 
   return dynamic;
 }
 
 /* Return the run-time load address of the shared object.  */
+static inline Elf32_Addr
+elf_machine_load_address (void) __attribute__ ((const));
+
 static inline Elf32_Addr
 elf_machine_load_address (void)
 {
-  Elf32_Addr dynamic, dynamic_linkaddress;
+  Elf32_Addr dynamic;
 
   asm (
-"      b,l     1f,%0\n"
-"      depi    0,31,2,%0\n"
-"1:    addil   L'_DYNAMIC - ($PIC_pcrel$0 - 8),%0\n"
-"      ldo     R'_DYNAMIC - ($PIC_pcrel$0 - 12)(%%r1),%1\n"
-"      addil   L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 16),%0\n"
-"      ldw     R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 20)(%%r1),%0\n"
-   : "=r" (dynamic_linkaddress), "=r" (dynamic) : : "r1");
-
-  return dynamic - dynamic_linkaddress;
+"      bl      1f,%0\n"
+"      addil   L'_DYNAMIC - ($PIC_pcrel$0 - 1),%0\n"
+"1:    ldo     R'_DYNAMIC - ($PIC_pcrel$0 - 5)(%%r1),%0\n"
+   : "=r" (dynamic) : : "r1");
+
+  return dynamic - elf_machine_dynamic ();
 }
 
-/* Fixup a PLT entry to bounce directly to the function at VALUE.  */
-static inline Elf32_Addr
+/* Fixup a PLT entry to bounce directly to the function at VALUE. */
+static inline struct fdesc __attribute__ ((always_inline))
 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
+                      const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
                       const Elf32_Rela *reloc,
-                      Elf32_Addr *reloc_addr, Elf32_Addr value)
+                      Elf32_Addr *reloc_addr, struct fdesc value)
 {
-  /* l is the link_map for the caller, t is the link_map for the object
-   * being called */
-  reloc_addr[1] = D_PTR (t, l_info[DT_PLTGOT]);
-  reloc_addr[0] = value;
-  /* Return the PLT slot rather than the function value so that the
-     trampoline can load the new LTP. */
-  return (Elf32_Addr) reloc_addr;
+  volatile Elf32_Addr *rfdesc = reloc_addr;
+  /* map is the link_map for the caller, t is the link_map for the object
+     being called */
+  rfdesc[1] = value.gp;
+  /* Need to ensure that the gp is visible before the code
+     entry point is updated */
+  rfdesc[0] = value.ip;
+  return value;
 }
 
 /* Return the final value of a plt relocation.  */
-static inline Elf32_Addr
+static inline struct fdesc
 elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
-                      Elf32_Addr value)
+                      struct fdesc value)
 {
-  /* We are rela only */
-  return value + reloc->r_addend;
+  /* We are rela only, return a function descriptor as a plt entry. */
+  return (struct fdesc) { value.ip + reloc->r_addend, value.gp };
 }
 
 /* Set up the loaded object described by L so its unrelocated PLT
@@ -137,120 +139,179 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
 static inline int
 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
 {
+  Elf32_Addr *got = NULL;
+  Elf32_Addr l_addr, iplt, jmprel, end_jmprel, r_type, r_sym;
+  const Elf32_Rela *reloc;
+  struct fdesc *fptr;
+  static union {
+    unsigned char c[8];
+    Elf32_Addr i[2];
+  } sig = {{0x00,0xc0,0xff,0xee, 0xde,0xad,0xbe,0xef}};
+
+  /* If we don't have a PLT we can just skip all this... */
+  if (__builtin_expect (l->l_info[DT_JMPREL] == NULL,0))
+    return lazy;
+
+  /* All paths use these values */
+  l_addr = l->l_addr;
+  jmprel = D_PTR(l, l_info[DT_JMPREL]);
+  end_jmprel = jmprel + l->l_info[DT_PLTRELSZ]->d_un.d_val;
+
   extern void _dl_runtime_resolve (void);
   extern void _dl_runtime_profile (void);
-  Elf32_Addr jmprel = D_PTR(l, l_info[DT_JMPREL]);
 
-  if (lazy && jmprel)
+  /* Linking lazily */
+  if (lazy)
     {
-      Elf32_Addr *got = NULL;
-      Elf32_Addr l_addr;
-      Elf32_Addr end_jmprel;
-      Elf32_Addr iplt;
-
-      /* Relocate all the PLT slots.  */
-      l_addr = l->l_addr;
-      end_jmprel = jmprel + l->l_info[DT_PLTRELSZ]->d_un.d_val;
-      for (iplt = jmprel; iplt < end_jmprel; iplt += sizeof (Elf32_Rela))
+      /* FIXME: Search for the got, but backwards through the relocs, technically we should
+        find it on the first try. However, assuming the relocs got out of order the
+        routine is made a bit more robust by searching them all in case of failure. */
+      for (iplt = (end_jmprel - sizeof(Elf32_Rela)); iplt >= jmprel; iplt -= sizeof (Elf32_Rela))
        {
-         const Elf32_Rela *reloc;
-         Elf32_Word r_type;
-         Elf32_Word r_sym;
-         struct hppa_fptr *fptr;
 
          reloc = (const Elf32_Rela *) iplt;
          r_type = ELF32_R_TYPE (reloc->r_info);
          r_sym = ELF32_R_SYM (reloc->r_info);
 
+         got = (Elf32_Addr *) (reloc->r_offset + l_addr + PLT_ENTRY_SIZE + SIZEOF_PLT_STUB);
+
+         /* If we aren't an IPLT, and we aren't NONE then it's a bad reloc */
+         if (__builtin_expect (r_type != R_PARISC_IPLT, 0))
+           {
+             if (__builtin_expect (r_type != R_PARISC_NONE, 0))
+               _dl_reloc_bad_type (l, r_type, 1);
+             continue;
+           }
+
+         /* Check for the plt_stub that binutils placed here for us
+            to use with _dl_runtime_resolve  */
+         if (got[-2] != sig.i[0] || got[-1] != sig.i[1])
+           {
+             got = NULL; /* Not the stub... keep looking */
+           }
+         else
+           {
+             /* Found the GOT! */
+             register Elf32_Addr ltp __asm__ ("%r19");
+
+             /* Identify this shared object. Second entry in the got. */
+             got[1] = (Elf32_Addr) l;
+
+             /* This function will be called to perform the relocation. */
+             if (__builtin_expect (!profile, 1))
+               {
+                 /* If a static application called us, then _dl_runtime_resolve is not
+                    a function descriptor, but the *real* address of the function... */
+                 if((unsigned long) &_dl_runtime_resolve & 3)
+                   {
+                     got[-2] = (Elf32_Addr) ((struct fdesc *)
+                                 ((unsigned long) &_dl_runtime_resolve & ~3))->ip;
+                   }
+                 else
+                   {
+                     /* Static executable! */
+                     got[-2] = (Elf32_Addr) &_dl_runtime_resolve;
+                   }
+               }
+             else
+               {
+                 if (GLRO(dl_profile) != NULL
+                     && _dl_name_match_p (GLRO(dl_profile), l))
+                   {
+                     /* This is the object we are looking for.  Say that
+                        we really want profiling and the timers are
+                        started.  */
+                     GL(dl_profile_map) = l;
+                   }
+
+                 if((unsigned long) &_dl_runtime_profile & 3)
+                   {
+                     got[-2] = (Elf32_Addr) ((struct fdesc *)
+                                 ((unsigned long) &_dl_runtime_profile & ~3))->ip;
+                   }
+                 else
+                   {
+                     /* Static executable */
+                     got[-2] = (Elf32_Addr) &_dl_runtime_profile;
+                   }
+               }
+             /* Plunk in the gp of this function descriptor so we
+                can make the call to _dl_runtime_xxxxxx */
+             got[-1] = ltp;
+             break;
+             /* Done looking for the GOT, and stub is setup */
+           } /* else we found the GOT */
+       } /* for, walk the relocs backwards */
+
+      if(!got)
+       return 0; /* No lazy linking for you! */
+
+      /* Process all the relocs, now that we know the GOT... */
+      for (iplt = jmprel; iplt < end_jmprel; iplt += sizeof (Elf32_Rela))
+       {
+         reloc = (const Elf32_Rela *) iplt;
+         r_type = ELF32_R_TYPE (reloc->r_info);
+         r_sym = ELF32_R_SYM (reloc->r_info);
+
          if (__builtin_expect (r_type == R_PARISC_IPLT, 1))
            {
-             fptr = (struct hppa_fptr *) (reloc->r_offset + l_addr);
+             fptr = (struct fdesc *) (reloc->r_offset + l_addr);
              if (r_sym != 0)
                {
                  /* Relocate the pointer to the stub.  */
-                 fptr->func += l_addr;
+                 fptr->ip = (Elf32_Addr) got - GOT_FROM_PLT_STUB;
+
                  /* Instead of the LTP value, we put the reloc offset
                     here.  The trampoline code will load the proper
                     LTP and pass the reloc offset to the fixup
                     function.  */
                  fptr->gp = iplt - jmprel;
-                 if (!got)
-                   {
-                     static union {
-                       unsigned char c[8];
-                       Elf32_Addr i[2];
-                     } sig = {{0x00,0xc0,0xff,0xee, 0xde,0xad,0xbe,0xef}};
-
-                     /* Find our .got section.  It's right after the
-                        stub.  */
-                     got = (Elf32_Addr *) (fptr->func + GOT_FROM_PLT_STUB);
-
-                     /* Sanity check to see if the address we are
-                         going to check below is within a reasonable
-                         approximation of the bounds of the PLT (or,
-                         at least, is at an address that won't fault
-                         on read).  Then check for the magic signature
-                         above. */
-                     if (fptr->func < (Elf32_Addr) fptr + sizeof(*fptr))
-                         return 0;
-                     if (fptr->func >
-                         ((Elf32_Addr) fptr
-                          + SIZEOF_PLT_STUB
-                          + ((l->l_info[DT_PLTRELSZ]->d_un.d_val / sizeof (Elf32_Rela))
-                             * 8)))
-                       return 0;
-                     if (got[-2] != sig.i[0] || got[-1] != sig.i[1])
-                       return 0; /* No lazy linking for you! */
-                   }
-               }
+               } /* r_sym != 0 */
              else
                {
                  /* Relocate this *ABS* entry.  */
-                 fptr->func = reloc->r_addend + l_addr;
+                 fptr->ip = reloc->r_addend + l_addr;
                  fptr->gp = D_PTR (l, l_info[DT_PLTGOT]);
                }
-           }
-         else if (__builtin_expect (r_type != R_PARISC_NONE, 0))
-           _dl_reloc_bad_type (l, r_type, 1);
-       }
-
-      if (got)
+           } /* r_type == R_PARISC_IPLT */
+       } /* for all the relocations */
+    } /* if lazy */
+  else
+    {
+      for (iplt = jmprel; iplt < end_jmprel; iplt += sizeof (Elf32_Rela))
        {
-         register Elf32_Addr ltp __asm__ ("%r19");
-         /* Identify this shared object. */
-         got[1] = (Elf32_Addr) l;
-
-         /* This function will be called to perform the relocation. */
-         if (__builtin_expect (!profile, 1))
-           got[-2] =
-             (Elf32_Addr) ((struct hppa_fptr *)
-                           ((unsigned long) &_dl_runtime_resolve & ~3))->func;
-         else
+         reloc = (const Elf32_Rela *) iplt;
+         r_type = ELF32_R_TYPE (reloc->r_info);
+         r_sym = ELF32_R_SYM (reloc->r_info);
+
+         if (__builtin_expect ((r_type == R_PARISC_IPLT) && (r_sym == 0), 1))
            {
-             if (_dl_name_match_p (_dl_profile, l))
-               {
-                 /* This is the object we are looking for.  Say that
-                    we really want profiling and the timers are
-                    started.  */
-                 _dl_profile_map = l;
-               }
-             got[-2] =
-               (Elf32_Addr) ((struct hppa_fptr *)
-                             ((unsigned long) &_dl_runtime_profile & ~3))->func;
-           }
-         got[-1] = ltp;
-       }
+             fptr = (struct fdesc *) (reloc->r_offset + l_addr);
+             /* Relocate this *ABS* entry, set only the gp, the rest is set later
+                when elf_machine_rela_relative is called (WITHOUT the linkmap)  */
+             fptr->gp = D_PTR (l, l_info[DT_PLTGOT]);
+           } /* r_type == R_PARISC_IPLT */
+       } /* for all the relocations */
     }
   return lazy;
 }
 
+
+/* Names of the architecture-specific auditing callback functions.  */
+#define ARCH_LA_PLTENTER hppa_gnu_pltenter
+#define ARCH_LA_PLTEXIT hppa_gnu_pltexit
+
+/* Adjust DL_STACK_END to get value we want in __libc_stack_end.  */
+#define DL_STACK_END(cookie) \
+  ((void *) (((long) (cookie)) + 0x160))
+
 /* 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.  */
 
 #define RTLD_START \
 /* Set up dp for any non-PIC lib constructors that may be called.  */  \
-static struct link_map *                                               \
+static struct link_map * __attribute__((used))                         \
 set_dp (struct link_map *map)                                          \
 {                                                                      \
   register Elf32_Addr dp asm ("%r27");                                 \
@@ -271,29 +332,30 @@ asm (                                                                     \
 "      stw     %r25,-40(%sp)\n" /* argc */                             \
 "      stw     %r24,-44(%sp)\n" /* argv */                             \
                                                                        \
-       /* We need the LTP, and we need it now. */                      \
-       /* $PIC_pcrel$0 points 8 bytes past the current instruction,    \
-          just like a branch reloc.  This sequence gets us the runtime \
-          address of _DYNAMIC. */                                      \
+       /* We need the LTP, and we need it now.                         \
+          $PIC_pcrel$0 points 8 bytes past the current instruction,    \
+          just like a branch reloc.  This sequence gets us the         \
+          runtime address of _DYNAMIC. */                              \
 "      bl      0f,%r19\n"                                              \
-"      depi    0,31,2,%r19\n"  /* clear priviledge bits */             \
-"0:    addil   L'_DYNAMIC - ($PIC_pcrel$0 - 8),%r19\n"                 \
-"      ldo     R'_DYNAMIC - ($PIC_pcrel$0 - 12)(%r1),%r26\n"           \
+"      addil   L'_DYNAMIC - ($PIC_pcrel$0 - 1),%r19\n"                 \
+"0:    ldo     R'_DYNAMIC - ($PIC_pcrel$0 - 5)(%r1),%r26\n"            \
                                                                        \
-       /* Also get the link time address from the first entry of the GOT.  */ \
-"      addil   L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 16),%r19\n"   \
-"      ldw     R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 20)(%r1),%r20\n" \
+       /* The link time address is stored in the first entry of the    \
+          GOT.  */                                                     \
+"      addil   L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 9),%r19\n"    \
+"      ldw     R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 13)(%r1),%r20\n" \
                                                                        \
 "      sub     %r26,%r20,%r20\n"       /* Calculate load offset */     \
                                                                        \
-       /* Rummage through the dynamic entries, looking for DT_PLTGOT.  */ \
+       /* Rummage through the dynamic entries, looking for             \
+          DT_PLTGOT.  */                                               \
 "      ldw,ma  8(%r26),%r19\n"                                         \
 "1:    cmpib,=,n 3,%r19,2f\n"  /* tag == DT_PLTGOT? */                 \
 "      cmpib,<>,n 0,%r19,1b\n"                                         \
 "      ldw,ma  8(%r26),%r19\n"                                         \
                                                                        \
        /* Uh oh!  We didn't find one.  Abort. */                       \
-"      iitlbp  %r0,(%r0)\n"                                            \
+"      iitlbp  %r0,(%sr0,%r0)\n"                                       \
                                                                        \
 "2:    ldw     -4(%r26),%r19\n"        /* Found it, load value. */     \
 "      add     %r19,%r20,%r19\n"       /* And add the load offset. */  \
@@ -306,8 +368,8 @@ asm (                                                                       \
           |         32 bytes of magic       |                          \
           |---------------------------------|                          \
           | 32 bytes argument/sp save area  |                          \
-          |---------------------------------|  ((current->mm->env_end) + 63 & ~63) \
-          |         N bytes of slack        |                          \
+          |---------------------------------|  ((current->mm->env_end) \
+          |         N bytes of slack        |   + 63 & ~63)            \
           |---------------------------------|                          \
           |      envvar and arg strings     |                          \
           |---------------------------------|                          \
@@ -341,11 +403,6 @@ asm (                                                                      \
        /* Save the entry point in %r3. */                              \
 "      copy    %ret0,%r3\n"                                            \
                                                                        \
-       /* Remember the lowest stack address. */                        \
-"      addil   LT'__libc_stack_end,%r19\n"                             \
-"      ldw     RT'__libc_stack_end(%r1),%r20\n"                        \
-"      stw     %sp,0(%r20)\n"                                          \
-                                                                       \
        /* See if we were called as a command with the executable file  \
           name as an extra leading argument. */                        \
 "      addil   LT'_dl_skip_args,%r19\n"                                \
@@ -353,7 +410,7 @@ asm (                                                                       \
 "      ldw     0(%r20),%r20\n"                                         \
                                                                        \
 "      ldw     -40(%sp),%r25\n"        /* argc */                      \
-"      comib,= 0,%r20,.Lnofix\n"       /* FIXME: will be mispredicted */ \
+"      comib,= 0,%r20,.Lnofix\n"       /* FIXME: Mispredicted branch */\
 "      ldw     -44(%sp),%r24\n"        /* argv (delay slot) */         \
                                                                        \
 "      sub     %r25,%r20,%r25\n"                                       \
@@ -362,8 +419,8 @@ asm (                                                                       \
 "      stw     %r24,-44(%sp)\n"                                        \
                                                                        \
 ".Lnofix:\n"                                                           \
-"      addil   LT'_dl_loaded,%r19\n"                                   \
-"      ldw     RT'_dl_loaded(%r1),%r26\n"                              \
+"      addil   LT'_rtld_local,%r19\n"                                  \
+"      ldw     RT'_rtld_local(%r1),%r26\n"                             \
 "      bl      set_dp, %r2\n"                                          \
 "      ldw     0(%r26),%r26\n"                                         \
                                                                        \
@@ -375,20 +432,22 @@ asm (                                                                     \
 "      bl      _dl_init,%r2\n"                                         \
 "      ldo     4(%r23),%r23\n" /* delay slot */                        \
                                                                        \
-       /* Reload argc, argv  to the registers start.S expects them in (feh) */ \
+       /* Reload argc, argv to the registers start.S expects.  */      \
 "      ldw     -40(%sp),%r25\n"                                        \
 "      ldw     -44(%sp),%r24\n"                                        \
                                                                        \
-       /* _dl_fini does have a PLT slot now.  I don't know how to get  \
-          to it though, so this hack will remain. */                   \
+       /* _dl_fini is a local function in the loader, so we construct  \
+          a false OPD here and pass this to the application.  */       \
+       /* FIXME: Should be able to use P%, and LR RR to have the       \
+          the linker construct a proper OPD.  */                       \
 "      .section .data\n"                                               \
 "__dl_fini_plabel:\n"                                                  \
 "      .word   _dl_fini\n"                                             \
 "      .word   0xdeadbeef\n"                                           \
 "      .previous\n"                                                    \
                                                                        \
-       /* %r3 contains a function pointer, we need to mask out the lower \
-        * bits and load the gp and jump address. */                    \
+       /* %r3 contains a function pointer, we need to mask out the     \
+          lower bits and load the gp and jump address. */              \
 "      depi    0,31,2,%r3\n"                                           \
 "      ldw     0(%r3),%r2\n"                                           \
 "      addil   LT'__dl_fini_plabel,%r19\n"                             \
@@ -397,105 +456,95 @@ asm (                                                                    \
 "      ldw     4(%r3),%r19\n"  /* load the object's gp */              \
 "      bv      %r0(%r2)\n"                                             \
 "      depi    2,31,2,%r23\n"  /* delay slot */                        \
-       );
-
-
-/* This code gets called via the .plt stub, and is used in
-   dl-runtime.c to call the `fixup' function and then redirect to the
-   address it returns.
-   Enter with r19 = reloc offset, r20 = got-8, r21 = fixup ltp.  */
-#define TRAMPOLINE_TEMPLATE(tramp_name, fixup_name) \
-  extern void tramp_name (void);                   \
-  asm ( "\
-       /* Trampoline for " #tramp_name " */
-       .globl " #tramp_name "
-       .type " #tramp_name ",@function
-" #tramp_name ":
-       /* Save return pointer */
-       stw     %r2,-20(%sp)
-       /* Save argument registers in the call stack frame. */
-       stw     %r26,-36(%sp)
-       stw     %r25,-40(%sp)
-       stw     %r24,-44(%sp)
-       stw     %r23,-48(%sp)
-       /* Build a call frame. */
-       stwm    %sp,64(%sp)
-
-       /* Set up args to fixup func.  */
-       ldw     8+4(%r20),%r26  /* got[1] == struct link_map *  */
-       copy    %r19,%r25       /* reloc offset  */
-
-       /* Call the real address resolver. */
-       bl      " #fixup_name ",%r2
-       copy    %r21,%r19       /* delay slot, set fixup func ltp */
-
-       ldwm    -64(%sp),%sp
-       /* Arguments. */
-       ldw     -36(%sp),%r26
-       ldw     -40(%sp),%r25
-       ldw     -44(%sp),%r24
-       ldw     -48(%sp),%r23
-       /* Return pointer. */
-       ldw     -20(%sp),%r2
-       /* Call the real function. */
-       ldw     0(%r28),%r22
-       bv      %r0(%r22)
-       ldw     4(%r28),%r19
-");
-
-#ifndef PROF
-#define ELF_MACHINE_RUNTIME_TRAMPOLINE                 \
-  TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);    \
-  TRAMPOLINE_TEMPLATE (_dl_runtime_profile, profile_fixup);
+);
+
+/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
+   a TLS variable, so references should not be allowed to define the value.
+   ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one
+   of the main executable's symbols, as for a COPY reloc.  */
+#if !defined RTLD_BOOTSTRAP
+# define elf_machine_type_class(type)                          \
+  ((((type) == R_PARISC_IPLT                                   \
+  || (type) == R_PARISC_EPLT                                   \
+  || (type) == R_PARISC_TLS_DTPMOD32                           \
+  || (type) == R_PARISC_TLS_DTPOFF32                           \
+  || (type) == R_PARISC_TLS_TPREL32)                           \
+  * ELF_RTYPE_CLASS_PLT)                                       \
+  | (((type) == R_PARISC_COPY) * ELF_RTYPE_CLASS_COPY))
 #else
-#define ELF_MACHINE_RUNTIME_TRAMPOLINE                 \
-  TRAMPOLINE_TEMPLATE (_dl_runtime_resolve, fixup);    \
-  strong_alias (_dl_runtime_resolve, _dl_runtime_profile);
+#define elf_machine_type_class(type)                           \
+ ((((type) == R_PARISC_IPLT                                    \
+   || (type) == R_PARISC_EPLT)                                 \
+   * ELF_RTYPE_CLASS_PLT)                                      \
+   | (((type) == R_PARISC_COPY) * ELF_RTYPE_CLASS_COPY))
 #endif
 
-
-/* Nonzero iff TYPE describes a relocation that should
-   skip the executable when looking up the symbol value.  */
-#define elf_machine_lookup_noexec_p(type) ((type) == R_PARISC_COPY)
-
-/* 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_PARISC_IPLT \
-                                         || (type) == R_PARISC_EPLT)
-
-/* Used by ld.so for ... something ... */
+/* Used by the runtime in fixup to figure out if reloc is *really* PLT */
 #define ELF_MACHINE_JMP_SLOT R_PARISC_IPLT
+#define ELF_MACHINE_SIZEOF_JMP_SLOT PLT_ENTRY_SIZE
 
 /* We only use RELA. */
 #define ELF_MACHINE_NO_REL 1
+#define ELF_MACHINE_NO_RELA 0
 
 /* Return the address of the entry point. */
-#define ELF_MACHINE_START_ADDRESS(map, start) \
-  DL_FUNCTION_ADDRESS (map, start)
+#define ELF_MACHINE_START_ADDRESS(map, start)                  \
+({                                                             \
+       ElfW(Addr) addr;                                        \
+       DL_DT_FUNCTION_ADDRESS(map, start, static, addr)        \
+       addr;                                                   \
+})
+
+/* We define an initialization functions.  This is called very early in
+ *    _dl_sysdep_start.  */
+#define DL_PLATFORM_INIT dl_platform_init ()
+
+static inline void __attribute__ ((unused))
+dl_platform_init (void)
+{
+       if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
+       /* Avoid an empty string which would disturb us.  */
+               GLRO(dl_platform) = NULL;
+}
 
 #endif /* !dl_machine_h */
 
 /* These are only actually used where RESOLVE_MAP is defined, anyway. */
 #ifdef RESOLVE_MAP
 
-static inline void
-elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
-                 const Elf32_Sym *sym, const struct r_found_version *version,
-                 Elf32_Addr *const reloc_addr)
+#define reassemble_21(as21) \
+  (  (((as21) & 0x100000) >> 20) \
+   | (((as21) & 0x0ffe00) >> 8) \
+   | (((as21) & 0x000180) << 7) \
+   | (((as21) & 0x00007c) << 14) \
+   | (((as21) & 0x000003) << 12))
+
+#define reassemble_14(as14) \
+  (  (((as14) & 0x1fff) << 1) \
+   | (((as14) & 0x2000) >> 13))
+
+auto void __attribute__((always_inline))
+elf_machine_rela (struct link_map *map,
+                 const Elf32_Rela *reloc,
+                 const Elf32_Sym *sym,
+                 const struct r_found_version *version,
+                 void *const reloc_addr_arg,
+                 int skip_ifunc)
 {
+  Elf32_Addr *const reloc_addr = reloc_addr_arg;
   const Elf32_Sym *const refsym = sym;
   unsigned long const r_type = ELF32_R_TYPE (reloc->r_info);
   struct link_map *sym_map;
   Elf32_Addr value;
 
-#ifndef RTLD_BOOTSTRAP
+# if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC && !defined SHARED
   /* 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
+  weak_extern (GL(dl_rtld_map));
+# endif
 
   /* RESOLVE_MAP will return a null value for undefined syms, and
      non-null for all other syms.  In particular, relocs with no
@@ -503,16 +552,17 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
      resolved to MAP.  (The first entry in a symbol table is all
      zeros, and an all zero Elf32_Sym has a binding of STB_LOCAL.)
      See RESOLVE_MAP definition in elf/dl-reloc.c  */
-#ifdef RTLD_BOOTSTRAP
+# ifdef RTLD_BOOTSTRAP
   /* RESOLVE_MAP in rtld.c doesn't have the local sym test.  */
   sym_map = (ELF32_ST_BIND (sym->st_info) != STB_LOCAL
             ? RESOLVE_MAP (&sym, version, r_type) : map);
-#else
+# else
   sym_map = RESOLVE_MAP (&sym, version, r_type);
-#endif
+# endif
+
   if (sym_map)
     {
-      value = sym ? sym_map->l_addr + sym->st_value : 0;
+      value = SYMBOL_ADDRESS (sym_map, sym, true);
       value += reloc->r_addend;
     }
   else
@@ -521,19 +571,10 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
   switch (r_type)
     {
     case R_PARISC_DIR32:
-#ifndef RTLD_BOOTSTRAP
-      /* All hell breaks loose if we try to relocate these twice,
-         because any initialized variables in ld.so that refer to
-         other ones will have their values reset.  In particular,
-         __fptr_next will be reset, sometimes causing endless loops in
-         __hppa_make_fptr().  So don't do that. */
-      if (map == &_dl_rtld_map)
-       return;
-#endif
       /* .eh_frame can have unaligned relocs.  */
-      if (reloc_addr & 3)
+      if ((unsigned long) reloc_addr_arg & 3)
        {
-         char *rel_addr = (char *) reloc_addr;
+         char *rel_addr = (char *) reloc_addr_arg;
          rel_addr[0] = value >> 24;
          rel_addr[1] = value >> 16;
          rel_addr[2] = value >> 8;
@@ -542,58 +583,81 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
        }
       break;
 
+    case R_PARISC_DIR21L:
+      {
+       unsigned int insn = *(unsigned int *)reloc_addr;
+       value = (SYMBOL_ADDRESS (sym_map, sym, true)
+                + ((reloc->r_addend + 0x1000) & -0x2000));
+       value = value >> 11;
+       insn = (insn &~ 0x1fffff) | reassemble_21 (value);
+       *(unsigned int *)reloc_addr = insn;
+      }
+      return;
+
+    case R_PARISC_DIR14R:
+      {
+       unsigned int insn = *(unsigned int *)reloc_addr;
+       value = ((SYMBOL_ADDRESS (sym_map, sym, true) & 0x7ff)
+                + (((reloc->r_addend & 0x1fff) ^ 0x1000) - 0x1000));
+       insn = (insn &~ 0x3fff) | reassemble_14 (value);
+       *(unsigned int *)reloc_addr = insn;
+      }
+      return;
+
     case R_PARISC_PLABEL32:
       /* Easy rule: If there is a symbol and it is global, then we
-         need to make a dynamic function descriptor.  Otherwise we
-         have the address of a PLT slot for a local symbol which we
-         know to be unique. */
+        need to make a dynamic function descriptor.  Otherwise we
+        have the address of a PLT slot for a local symbol which we
+        know to be unique. */
       if (sym == NULL
          || sym_map == NULL
          || ELF32_ST_BIND (sym->st_info) == STB_LOCAL)
-       break;
+       {
+         break;
+       }
+      /* Set bit 30 to indicate to $$dyncall that this is a PLABEL.
+        We have to do this outside of the generic function descriptor
+        code, since it doesn't know about our requirement for setting
+        protection bits */
+      value = (Elf32_Addr)((unsigned int)_dl_make_fptr (sym_map, sym, value) | 2);
+      break;
 
-      /* Okay, we need to make ourselves a PLABEL then.  See the IA64
-         code for an explanation of how this works.  */
-#ifndef RTLD_BOOTSTRAP
-      value = __hppa_make_fptr (sym_map, value, &__fptr_root, NULL);
-#else
+    case R_PARISC_PLABEL21L:
+    case R_PARISC_PLABEL14R:
       {
-       struct hppa_fptr *p_boot_ldso_fptr;
-       struct hppa_fptr **p_fptr_root;
-       int *p_fptr_count;
-       unsigned long dot;
-
-       /* Go from the top of __boot_ldso_fptr.  As on IA64, we
-          probably haven't relocated the necessary values by this
-          point so we have to find them ourselves. */
-
-       asm ("bl        0f,%0
-             depi      0,31,2,%0
-0:           addil     L'__boot_ldso_fptr - ($PIC_pcrel$0 - 8),%0
-             ldo       R'__boot_ldso_fptr - ($PIC_pcrel$0 - 12)(%%r1),%1
-             addil     L'__fptr_root - ($PIC_pcrel$0 - 16),%0
-             ldo       R'__fptr_root - ($PIC_pcrel$0 - 20)(%%r1),%2
-             addil     L'__fptr_count - ($PIC_pcrel$0 - 24),%0
-             ldo       R'__fptr_count - ($PIC_pcrel$0 - 28)(%%r1),%3"
-            :
-            "=r" (dot),
-            "=r" (p_boot_ldso_fptr),
-            "=r" (p_fptr_root),
-            "=r" (p_fptr_count));
-
-       value = __hppa_make_fptr (sym_map, value, p_fptr_root,
-                                 &p_boot_ldso_fptr[--*p_fptr_count]);
+       unsigned int insn = *(unsigned int *)reloc_addr;
+
+       if (__builtin_expect (sym == NULL, 0))
+         break;
+
+       value = (Elf32_Addr)((unsigned int)_dl_make_fptr (sym_map, sym, value) | 2);
+
+       if (r_type == R_PARISC_PLABEL21L)
+         {
+           value >>= 11;
+           insn = (insn &~ 0x1fffff) | reassemble_21 (value);
+         }
+       else
+         {
+           value &= 0x7ff;
+           insn = (insn &~ 0x3fff) | reassemble_14 (value);
+         }
+
+       *(unsigned int *)reloc_addr = insn;
       }
-#endif
-      break;
+      return;
 
     case R_PARISC_IPLT:
       if (__builtin_expect (sym_map != NULL, 1))
-       elf_machine_fixup_plt (NULL, sym_map, reloc, reloc_addr, value);
+       {
+         elf_machine_fixup_plt (NULL, sym_map, NULL, NULL, reloc, reloc_addr,
+                                DL_FIXUP_MAKE_VALUE(sym_map, value));
+       }
       else
        {
          /* If we get here, it's a (weak) undefined sym.  */
-         elf_machine_fixup_plt (NULL, map, reloc, reloc_addr, value);
+         elf_machine_fixup_plt (NULL, map, NULL, NULL, reloc, reloc_addr,
+                                DL_FIXUP_MAKE_VALUE(map, value));
        }
       return;
 
@@ -604,20 +668,41 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
        break;
       if (__builtin_expect (sym->st_size > refsym->st_size, 0)
          || (__builtin_expect (sym->st_size < refsym->st_size, 0)
-             && __builtin_expect (_dl_verbose, 0)))
+             && __builtin_expect (GLRO(dl_verbose), 0)))
        {
          const char *strtab;
 
          strtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
-         _dl_error_printf ("\
-%s: Symbol `%s' has different size in shared object, consider re-linking\n",
-                           _dl_argv[0] ?: "<program name unknown>",
-                           strtab + refsym->st_name);
+         _dl_error_printf ("%s: Symbol `%s' has different size in shared object, "
+                           "consider re-linking\n",
+                           RTLD_PROGNAME, strtab + refsym->st_name);
        }
-      memcpy (reloc_addr, (void *) value,
+      memcpy (reloc_addr_arg, (void *) value,
              MIN (sym->st_size, refsym->st_size));
       return;
 
+#if !defined RTLD_BOOTSTRAP
+    case R_PARISC_TLS_DTPMOD32:
+      value = sym_map->l_tls_modid;
+      break;
+
+    case R_PARISC_TLS_DTPOFF32:
+      /* During relocation all TLS symbols are defined and used.
+        Therefore the offset is already correct.  */
+      if (sym != NULL)
+       *reloc_addr = sym->st_value + reloc->r_addend;
+      return;
+
+    case R_PARISC_TLS_TPREL32:
+      /* The offset is negative, forward from the thread pointer */
+      if (sym != NULL)
+       {
+         CHECK_STATIC_TLS (map, sym_map);
+         value = sym_map->l_tls_offset + sym->st_value + reloc->r_addend;
+       }
+      break;
+#endif /* use TLS */
+
     case R_PARISC_NONE:        /* Alright, Wilbur. */
       return;
 
@@ -628,16 +713,65 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
   *reloc_addr = value;
 }
 
-static inline void
-elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc,
-                          Elf32_Addr *const reloc_addr)
+/* hppa doesn't have an R_PARISC_RELATIVE reloc, but uses relocs with
+   ELF32_R_SYM (info) == 0 for a similar purpose.  */
+auto void __attribute__((always_inline))
+elf_machine_rela_relative (Elf32_Addr l_addr,
+                          const Elf32_Rela *reloc,
+                          void *const reloc_addr_arg)
 {
-  /* XXX Nothing to do.  There is no relative relocation, right?  */
+  unsigned long const r_type = ELF32_R_TYPE (reloc->r_info);
+  Elf32_Addr *const reloc_addr = reloc_addr_arg;
+  static char msgbuf[] = { "Unknown" };
+  struct link_map map;
+  Elf32_Addr value;
+
+  value = l_addr + reloc->r_addend;
+
+  if (ELF32_R_SYM (reloc->r_info) != 0){
+    _dl_error_printf ("%s: In elf_machine_rela_relative "
+                     "ELF32_R_SYM (reloc->r_info) != 0. Aborting.",
+                     RTLD_PROGNAME);
+    ABORT_INSTRUCTION;  /* Crash. */
+  }
+
+  switch (r_type)
+    {
+    case R_PARISC_DIR32:
+      /* .eh_frame can have unaligned relocs.  */
+      if ((unsigned long) reloc_addr_arg & 3)
+       {
+         char *rel_addr = (char *) reloc_addr_arg;
+         rel_addr[0] = value >> 24;
+         rel_addr[1] = value >> 16;
+         rel_addr[2] = value >> 8;
+         rel_addr[3] = value;
+         return;
+       }
+      break;
+
+    case R_PARISC_PLABEL32:
+      break;
+
+    case R_PARISC_IPLT: /* elf_machine_runtime_setup already set gp */
+      break;
+
+    case R_PARISC_NONE:
+      return;
+
+    default: /* Bad reloc, map unknown (really it's the current map) */
+      map.l_name = msgbuf;
+      _dl_reloc_bad_type (&map, r_type, 0);
+      return;
+    }
+
+  *reloc_addr = value;
 }
 
-static inline void
+auto void __attribute__((always_inline))
 elf_machine_lazy_rel (struct link_map *map,
-                     Elf32_Addr l_addr, const Elf32_Rela *reloc)
+                     Elf32_Addr l_addr, const Elf32_Rela *reloc,
+                     int skip_ifunc)
 {
   /* We don't have anything to do here.  elf_machine_runtime_setup has
      done all the relocs already.  */