]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/powerpc/powerpc32/dl-machine.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / sysdeps / powerpc / powerpc32 / dl-machine.c
index 644360888697a80a3a600c7bbb867bc07a38145b..aeb23e99a4b46699ee071db46159f0cf7a16205a 100644 (file)
@@ -1,5 +1,5 @@
 /* Machine-dependent ELF dynamic relocation functions.  PowerPC version.
-   Copyright (C) 1995-2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1995-2015 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
@@ -13,9 +13,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/>.  */
 
 #include <unistd.h>
 #include <string.h>
 #include <ldsodefs.h>
 #include <elf/dynamic-link.h>
 #include <dl-machine.h>
-#include <stdio-common/_itoa.h>
+#include <_itoa.h>
 
-/* Because ld.so is now versioned, these functions can be in their own file;
-   no relocations need to be done to call them.
-   Of course, if ld.so is not versioned...  */
-#if !(DO_VERSIONING - 0)
-#error This will not work with versioning turned off, sorry.
-#endif
+/* The value __cache_line_size is defined in dl-sysdep.c and is initialised
+   by _dl_sysdep_start via DL_PLATFORM_INIT.  */
+extern int __cache_line_size attribute_hidden;
 
 
 /* Stuff for the PLT.  */
    mapped somewhere else.  */
 
 ElfW(Addr)
-__elf_preferred_address(struct link_map *loader, size_t maplength,
-                       ElfW(Addr) mapstartpref)
+__elf_preferred_address (struct link_map *loader, size_t maplength,
+                        ElfW(Addr) mapstartpref)
 {
   ElfW(Addr) low, high;
   struct link_map *l;
+  Lmid_t nsid;
 
   /* If the object has a preference, load it there!  */
   if (mapstartpref != 0)
@@ -109,35 +106,36 @@ __elf_preferred_address(struct link_map *loader, size_t maplength,
   /* Otherwise, quickly look for a suitable gap between 0x3FFFF and
      0x70000000.  0x3FFFF is so that references off NULL pointers will
      cause a segfault, 0x70000000 is just paranoia (it should always
-     be superceded by the program's load address).  */
+     be superseded by the program's load address).  */
   low =  0x0003FFFF;
   high = 0x70000000;
-  for (l = GL(dl_loaded); l; l = l->l_next)
-    {
-      ElfW(Addr) mapstart, mapend;
-      mapstart = l->l_map_start & ~(GL(dl_pagesize) - 1);
-      mapend = l->l_map_end | (GL(dl_pagesize) - 1);
-      assert (mapend > mapstart);
-
-      /* Prefer gaps below the main executable, note that l ==
-        _dl_loaded does not work for static binaries loading
-        e.g. libnss_*.so.  */
-      if ((mapend >= high || l->l_type == lt_executable)
-         && high >= mapstart)
-       high = mapstart;
-      else if (mapend >= low && low >= mapstart)
-       low = mapend;
-      else if (high >= mapend && mapstart >= low)
-       {
-         if (high - mapend >= mapstart - low)
-           low = mapend;
-         else
-           high = mapstart;
-       }
-    }
+  for (nsid = 0; nsid < DL_NNS; ++nsid)
+    for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
+      {
+       ElfW(Addr) mapstart, mapend;
+       mapstart = l->l_map_start & ~(GLRO(dl_pagesize) - 1);
+       mapend = l->l_map_end | (GLRO(dl_pagesize) - 1);
+       assert (mapend > mapstart);
+
+       /* Prefer gaps below the main executable, note that l ==
+          _dl_loaded does not work for static binaries loading
+          e.g. libnss_*.so.  */
+       if ((mapend >= high || l->l_type == lt_executable)
+           && high >= mapstart)
+         high = mapstart;
+       else if (mapend >= low && low >= mapstart)
+         low = mapend;
+       else if (high >= mapend && mapstart >= low)
+         {
+           if (high - mapend >= mapstart - low)
+             low = mapend;
+           else
+             high = mapstart;
+         }
+      }
 
   high -= 0x10000; /* Allow some room between objects.  */
-  maplength = (maplength | (GL(dl_pagesize) - 1)) + 1;
+  maplength = (maplength | (GLRO(dl_pagesize) - 1)) + 1;
   if (high <= low || high - low < maplength )
     return 0;
   return high - maplength;  /* Both high and maplength are page-aligned.  */
@@ -198,7 +196,7 @@ __elf_preferred_address(struct link_map *loader, size_t maplength,
    (1) and (3), this is obvious because only one instruction is
    changed and the PPC architecture guarantees that aligned stores are
    atomic.  For (5), this is more tricky.  When changing (4) to (5),
-   the `b' instruction is first changed to to `mtctr'; this is safe
+   the `b' instruction is first changed to `mtctr'; this is safe
    and is why the `lwzu' instruction is not just a simple `addi'.
    Once this is done, and is visible to all processors, the `lwzu' can
    safely be changed to a `lwz'.  */
@@ -230,15 +228,21 @@ __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
       if (lazy)
        {
          Elf32_Word *tramp = plt + PLT_TRAMPOLINE_ENTRY_WORDS;
-         Elf32_Word dlrr = (Elf32_Word)(profile
-                                        ? _dl_prof_resolve
-                                        : _dl_runtime_resolve);
+         Elf32_Word dlrr;
          Elf32_Word offset;
 
-         if (profile && _dl_name_match_p (GL(dl_profile), map))
+#ifndef PROF
+         dlrr = (Elf32_Word) (profile
+                              ? _dl_prof_resolve
+                              : _dl_runtime_resolve);
+         if (profile && GLRO(dl_profile) != NULL
+             && _dl_name_match_p (GLRO(dl_profile), map))
            /* This is the object we are looking for.  Say that we really
               want profiling and the timers are started.  */
            GL(dl_profile_map) = map;
+#else
+         dlrr = (Elf32_Word) _dl_runtime_resolve;
+#endif
 
          /* For the long entries, subtract off data_words.  */
          tramp[0] = OPCODE_ADDIS_HI (11, 11, -data_words);
@@ -304,14 +308,24 @@ __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
         there may be a little overlap at the start and the end.
 
         Assumes that dcbst and icbi apply to lines of 16 bytes or
-        more.  Current known line sizes are 16, 32, and 128 bytes.  */
+        more.  Current known line sizes are 16, 32, and 128 bytes.
+        The following gets the __cache_line_size, when available.  */
+
+      /* Default minimum 4 words per cache line.  */
+      int line_size_words = 4;
+
+      if (lazy && __cache_line_size != 0)
+       /* Convert bytes to words.  */
+       line_size_words = __cache_line_size / 4;
 
       size_modified = lazy ? rel_offset_words : 6;
-      for (i = 0; i < size_modified; i += 4)
-       PPC_DCBST (plt + i);
+      for (i = 0; i < size_modified; i += line_size_words)
+        PPC_DCBST (plt + i);
       PPC_DCBST (plt + size_modified - 1);
       PPC_SYNC;
-      PPC_ICBI (plt);
+
+      for (i = 0; i < size_modified; i += line_size_words)
+        PPC_ICBI (plt + i);
       PPC_ICBI (plt + size_modified - 1);
       PPC_ISYNC;
     }
@@ -320,8 +334,8 @@ __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
 }
 
 Elf32_Addr
-__elf_machine_fixup_plt(struct link_map *map, const Elf32_Rela *reloc,
-                       Elf32_Addr *reloc_addr, Elf32_Addr finaladdr)
+__elf_machine_fixup_plt (struct link_map *map,
+                        Elf32_Addr *reloc_addr, Elf32_Addr finaladdr)
 {
   Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr;
   if (delta << 6 >> 6 == delta)
@@ -368,26 +382,24 @@ __elf_machine_fixup_plt(struct link_map *map, const Elf32_Rela *reloc,
   return finaladdr;
 }
 
-static void
-dl_reloc_overflow (struct link_map *map,
-                  const char *name,
-                  Elf32_Addr *const reloc_addr,
-                  const Elf32_Sym *sym,
-                  const Elf32_Sym *refsym)
+void
+_dl_reloc_overflow (struct link_map *map,
+                   const char *name,
+                   Elf32_Addr *const reloc_addr,
+                   const Elf32_Sym *refsym)
 {
   char buffer[128];
   char *t;
-  const Elf32_Sym *errsym = sym ?: refsym;
   t = stpcpy (buffer, name);
   t = stpcpy (t, " relocation at 0x00000000");
   _itoa_word ((unsigned) reloc_addr, t, 16, 0);
-  if (errsym)
+  if (refsym)
     {
       const char *strtab;
 
       strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
       t = stpcpy (t, " for symbol `");
-      t = stpcpy (t, strtab + errsym->st_name);
+      t = stpcpy (t, strtab + refsym->st_name);
       t = stpcpy (t, "'");
     }
   t = stpcpy (t, " out of range");
@@ -397,12 +409,19 @@ dl_reloc_overflow (struct link_map *map,
 void
 __process_machine_rela (struct link_map *map,
                        const Elf32_Rela *reloc,
+                       struct link_map *sym_map,
                        const Elf32_Sym *sym,
                        const Elf32_Sym *refsym,
                        Elf32_Addr *const reloc_addr,
                        Elf32_Addr const finaladdr,
                        int rinfo)
 {
+  union unaligned
+    {
+      uint16_t u2;
+      uint32_t u4;
+    } __attribute__((__packed__));
+
   switch (rinfo)
     {
     case R_PPC_NONE:
@@ -414,30 +433,30 @@ __process_machine_rela (struct link_map *map,
       *reloc_addr = finaladdr;
       return;
 
+    case R_PPC_IRELATIVE:
+      *reloc_addr = ((Elf32_Addr (*) (void)) finaladdr) ();
+      return;
+
     case R_PPC_UADDR32:
-      ((char *) reloc_addr)[0] = finaladdr >> 24;
-      ((char *) reloc_addr)[1] = finaladdr >> 16;
-      ((char *) reloc_addr)[2] = finaladdr >> 8;
-      ((char *) reloc_addr)[3] = finaladdr;
+      ((union unaligned *) reloc_addr)->u4 = finaladdr;
       break;
 
     case R_PPC_ADDR24:
-      if (__builtin_expect (finaladdr > 0x01fffffc && finaladdr < 0xfe000000, 0))
-       dl_reloc_overflow (map,  "R_PPC_ADDR24", reloc_addr, sym, refsym);
+      if (__glibc_unlikely (finaladdr > 0x01fffffc && finaladdr < 0xfe000000))
+       _dl_reloc_overflow (map,  "R_PPC_ADDR24", reloc_addr, refsym);
       *reloc_addr = (*reloc_addr & 0xfc000003) | (finaladdr & 0x3fffffc);
       break;
 
     case R_PPC_ADDR16:
-      if (__builtin_expect (finaladdr > 0x7fff && finaladdr < 0xffff8000, 0))
-       dl_reloc_overflow (map,  "R_PPC_ADDR16", reloc_addr, sym, refsym);
+      if (__glibc_unlikely (finaladdr > 0x7fff && finaladdr < 0xffff8000))
+       _dl_reloc_overflow (map,  "R_PPC_ADDR16", reloc_addr, refsym);
       *(Elf32_Half*) reloc_addr = finaladdr;
       break;
 
     case R_PPC_UADDR16:
-      if (__builtin_expect (finaladdr > 0x7fff && finaladdr < 0xffff8000, 0))
-       dl_reloc_overflow (map,  "R_PPC_UADDR16", reloc_addr, sym, refsym);
-      ((char *) reloc_addr)[0] = finaladdr >> 8;
-      ((char *) reloc_addr)[1] = finaladdr;
+      if (__glibc_unlikely (finaladdr > 0x7fff && finaladdr < 0xffff8000))
+       _dl_reloc_overflow (map,  "R_PPC_UADDR16", reloc_addr, refsym);
+      ((union unaligned *) reloc_addr)->u2 = finaladdr;
       break;
 
     case R_PPC_ADDR16_LO:
@@ -455,8 +474,8 @@ __process_machine_rela (struct link_map *map,
     case R_PPC_ADDR14:
     case R_PPC_ADDR14_BRTAKEN:
     case R_PPC_ADDR14_BRNTAKEN:
-      if (__builtin_expect (finaladdr > 0x7fff && finaladdr < 0xffff8000, 0))
-       dl_reloc_overflow (map,  "R_PPC_ADDR14", reloc_addr, sym, refsym);
+      if (__glibc_unlikely (finaladdr > 0x7fff && finaladdr < 0xffff8000))
+       _dl_reloc_overflow (map,  "R_PPC_ADDR14", reloc_addr, refsym);
       *reloc_addr = (*reloc_addr & 0xffff0003) | (finaladdr & 0xfffc);
       if (rinfo != R_PPC_ADDR14)
        *reloc_addr = ((*reloc_addr & 0xffdfffff)
@@ -468,7 +487,7 @@ __process_machine_rela (struct link_map *map,
       {
        Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr;
        if (delta << 6 >> 6 != delta)
-         dl_reloc_overflow (map,  "R_PPC_REL24", reloc_addr, sym, refsym);
+         _dl_reloc_overflow (map,  "R_PPC_REL24", reloc_addr, refsym);
        *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
       }
       break;
@@ -479,15 +498,14 @@ __process_machine_rela (struct link_map *map,
           found.  */
        return;
       if (sym->st_size > refsym->st_size
-         || (GL(dl_verbose) && sym->st_size < refsym->st_size))
+         || (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
        {
          const char *strtab;
 
          strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
          _dl_error_printf ("\
-%s: Symbol `%s' has different size in shared object, onsider re-linking\n",
-                           rtld_progname ?: "<program name unknown>",
-                           strtab + refsym->st_name);
+%s: Symbol `%s' has different size in shared object, consider re-linking\n",
+                           RTLD_PROGNAME, strtab + refsym->st_name);
        }
       memcpy (reloc_addr, (char *) finaladdr, MIN (sym->st_size,
                                                   refsym->st_size));
@@ -541,6 +559,46 @@ __process_machine_rela (struct link_map *map,
       }
       break;
 
+#define DO_TLS_RELOC(suffix)                                                 \
+    case R_PPC_DTPREL##suffix:                                               \
+      /* During relocation all TLS symbols are defined and used.             \
+        Therefore the offset is already correct.  */                         \
+      if (sym_map != NULL)                                                   \
+       do_reloc##suffix ("R_PPC_DTPREL"#suffix,                              \
+                         TLS_DTPREL_VALUE (sym, reloc));                     \
+      break;                                                                 \
+    case R_PPC_TPREL##suffix:                                                \
+      if (sym_map != NULL)                                                   \
+       {                                                                     \
+         CHECK_STATIC_TLS (map, sym_map);                                    \
+         do_reloc##suffix ("R_PPC_TPREL"#suffix,                             \
+                           TLS_TPREL_VALUE (sym_map, sym, reloc));           \
+       }                                                                     \
+      break;
+
+    inline void do_reloc16 (const char *r_name, Elf32_Addr value)
+      {
+       if (__glibc_unlikely (value > 0x7fff && value < 0xffff8000))
+         _dl_reloc_overflow (map, r_name, reloc_addr, refsym);
+       *(Elf32_Half *) reloc_addr = value;
+      }
+    inline void do_reloc16_LO (const char *r_name, Elf32_Addr value)
+      {
+       *(Elf32_Half *) reloc_addr = value;
+      }
+    inline void do_reloc16_HI (const char *r_name, Elf32_Addr value)
+      {
+       *(Elf32_Half *) reloc_addr = value >> 16;
+      }
+    inline void do_reloc16_HA (const char *r_name, Elf32_Addr value)
+      {
+       *(Elf32_Half *) reloc_addr = (value + 0x8000) >> 16;
+      }
+    DO_TLS_RELOC (16)
+    DO_TLS_RELOC (16_LO)
+    DO_TLS_RELOC (16_HI)
+    DO_TLS_RELOC (16_HA)
+
     default:
       _dl_reloc_bad_type (map, rinfo, 0);
       return;