/* 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
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)
/* 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. */
(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'. */
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);
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;
}
}
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)
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");
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:
*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:
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)
{
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;
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));
}
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;