Blame sysdeps/powerpc/powerpc32/dl-machine.c

Packit 6c4009
/* Machine-dependent ELF dynamic relocation functions.  PowerPC 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
#include <unistd.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <link.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <elf/dynamic-link.h>
Packit 6c4009
#include <dl-machine.h>
Packit 6c4009
#include <_itoa.h>
Packit 6c4009
Packit 6c4009
/* The value __cache_line_size is defined in dl-sysdep.c and is initialised
Packit 6c4009
   by _dl_sysdep_start via DL_PLATFORM_INIT.  */
Packit 6c4009
extern int __cache_line_size attribute_hidden;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Stuff for the PLT.  */
Packit 6c4009
#define PLT_INITIAL_ENTRY_WORDS 18
Packit 6c4009
#define PLT_LONGBRANCH_ENTRY_WORDS 0
Packit 6c4009
#define PLT_TRAMPOLINE_ENTRY_WORDS 6
Packit 6c4009
#define PLT_DOUBLE_SIZE (1<<13)
Packit 6c4009
#define PLT_ENTRY_START_WORDS(entry_number) \
Packit 6c4009
  (PLT_INITIAL_ENTRY_WORDS + (entry_number)*2				\
Packit 6c4009
   + ((entry_number) > PLT_DOUBLE_SIZE					\
Packit 6c4009
      ? ((entry_number) - PLT_DOUBLE_SIZE)*2				\
Packit 6c4009
      : 0))
Packit 6c4009
#define PLT_DATA_START_WORDS(num_entries) PLT_ENTRY_START_WORDS(num_entries)
Packit 6c4009
Packit 6c4009
/* Macros to build PowerPC opcode words.  */
Packit 6c4009
#define OPCODE_ADDI(rd,ra,simm) \
Packit 6c4009
  (0x38000000 | (rd) << 21 | (ra) << 16 | ((simm) & 0xffff))
Packit 6c4009
#define OPCODE_ADDIS(rd,ra,simm) \
Packit 6c4009
  (0x3c000000 | (rd) << 21 | (ra) << 16 | ((simm) & 0xffff))
Packit 6c4009
#define OPCODE_ADD(rd,ra,rb) \
Packit 6c4009
  (0x7c000214 | (rd) << 21 | (ra) << 16 | (rb) << 11)
Packit 6c4009
#define OPCODE_B(target) (0x48000000 | ((target) & 0x03fffffc))
Packit 6c4009
#define OPCODE_BA(target) (0x48000002 | ((target) & 0x03fffffc))
Packit 6c4009
#define OPCODE_BCTR() 0x4e800420
Packit 6c4009
#define OPCODE_LWZ(rd,d,ra) \
Packit 6c4009
  (0x80000000 | (rd) << 21 | (ra) << 16 | ((d) & 0xffff))
Packit 6c4009
#define OPCODE_LWZU(rd,d,ra) \
Packit 6c4009
  (0x84000000 | (rd) << 21 | (ra) << 16 | ((d) & 0xffff))
Packit 6c4009
#define OPCODE_MTCTR(rd) (0x7C0903A6 | (rd) << 21)
Packit 6c4009
#define OPCODE_RLWINM(ra,rs,sh,mb,me) \
Packit 6c4009
  (0x54000000 | (rs) << 21 | (ra) << 16 | (sh) << 11 | (mb) << 6 | (me) << 1)
Packit 6c4009
Packit 6c4009
#define OPCODE_LI(rd,simm)    OPCODE_ADDI(rd,0,simm)
Packit 6c4009
#define OPCODE_ADDIS_HI(rd,ra,value) \
Packit 6c4009
  OPCODE_ADDIS(rd,ra,((value) + 0x8000) >> 16)
Packit 6c4009
#define OPCODE_LIS_HI(rd,value) OPCODE_ADDIS_HI(rd,0,value)
Packit 6c4009
#define OPCODE_SLWI(ra,rs,sh) OPCODE_RLWINM(ra,rs,sh,0,31-sh)
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define PPC_DCBST(where) asm volatile ("dcbst 0,%0" : : "r"(where) : "memory")
Packit 6c4009
#define PPC_SYNC asm volatile ("sync" : : : "memory")
Packit 6c4009
#define PPC_ISYNC asm volatile ("sync; isync" : : : "memory")
Packit 6c4009
#define PPC_ICBI(where) asm volatile ("icbi 0,%0" : : "r"(where) : "memory")
Packit 6c4009
#define PPC_DIE asm volatile ("tweq 0,0")
Packit 6c4009
Packit 6c4009
/* Use this when you've modified some code, but it won't be in the
Packit 6c4009
   instruction fetch queue (or when it doesn't matter if it is). */
Packit 6c4009
#define MODIFIED_CODE_NOQUEUE(where) \
Packit 6c4009
     do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); } while (0)
Packit 6c4009
/* Use this when it might be in the instruction queue. */
Packit 6c4009
#define MODIFIED_CODE(where) \
Packit 6c4009
     do { PPC_DCBST(where); PPC_SYNC; PPC_ICBI(where); PPC_ISYNC; } while (0)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The idea here is that to conform to the ABI, we are supposed to try
Packit 6c4009
   to load dynamic objects between 0x10000 (we actually use 0x40000 as
Packit 6c4009
   the lower bound, to increase the chance of a memory reference from
Packit 6c4009
   a null pointer giving a segfault) and the program's load address;
Packit 6c4009
   this may allow us to use a branch instruction in the PLT rather
Packit 6c4009
   than a computed jump.  The address is only used as a preference for
Packit 6c4009
   mmap, so if we get it wrong the worst that happens is that it gets
Packit 6c4009
   mapped somewhere else.  */
Packit 6c4009
Packit 6c4009
ElfW(Addr)
Packit 6c4009
__elf_preferred_address (struct link_map *loader, size_t maplength,
Packit 6c4009
			 ElfW(Addr) mapstartpref)
Packit 6c4009
{
Packit 6c4009
  ElfW(Addr) low, high;
Packit 6c4009
  struct link_map *l;
Packit 6c4009
  Lmid_t nsid;
Packit 6c4009
Packit 6c4009
  /* If the object has a preference, load it there!  */
Packit 6c4009
  if (mapstartpref != 0)
Packit 6c4009
    return mapstartpref;
Packit 6c4009
Packit 6c4009
  /* Otherwise, quickly look for a suitable gap between 0x3FFFF and
Packit 6c4009
     0x70000000.  0x3FFFF is so that references off NULL pointers will
Packit 6c4009
     cause a segfault, 0x70000000 is just paranoia (it should always
Packit 6c4009
     be superseded by the program's load address).  */
Packit 6c4009
  low =  0x0003FFFF;
Packit 6c4009
  high = 0x70000000;
Packit 6c4009
  for (nsid = 0; nsid < DL_NNS; ++nsid)
Packit 6c4009
    for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
Packit 6c4009
      {
Packit 6c4009
	ElfW(Addr) mapstart, mapend;
Packit 6c4009
	mapstart = l->l_map_start & ~(GLRO(dl_pagesize) - 1);
Packit 6c4009
	mapend = l->l_map_end | (GLRO(dl_pagesize) - 1);
Packit 6c4009
	assert (mapend > mapstart);
Packit 6c4009
Packit 6c4009
	/* Prefer gaps below the main executable, note that l ==
Packit 6c4009
	   _dl_loaded does not work for static binaries loading
Packit 6c4009
	   e.g. libnss_*.so.  */
Packit 6c4009
	if ((mapend >= high || l->l_type == lt_executable)
Packit 6c4009
	    && high >= mapstart)
Packit 6c4009
	  high = mapstart;
Packit 6c4009
	else if (mapend >= low && low >= mapstart)
Packit 6c4009
	  low = mapend;
Packit 6c4009
	else if (high >= mapend && mapstart >= low)
Packit 6c4009
	  {
Packit 6c4009
	    if (high - mapend >= mapstart - low)
Packit 6c4009
	      low = mapend;
Packit 6c4009
	    else
Packit 6c4009
	      high = mapstart;
Packit 6c4009
	  }
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  high -= 0x10000; /* Allow some room between objects.  */
Packit 6c4009
  maplength = (maplength | (GLRO(dl_pagesize) - 1)) + 1;
Packit 6c4009
  if (high <= low || high - low < maplength )
Packit 6c4009
    return 0;
Packit 6c4009
  return high - maplength;  /* Both high and maplength are page-aligned.  */
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
   Also install a small trampoline to be used by entries that have
Packit 6c4009
   been relocated to an address too far away for a single branch.  */
Packit 6c4009
Packit 6c4009
/* There are many kinds of PLT entries:
Packit 6c4009
Packit 6c4009
   (1)	A direct jump to the actual routine, either a relative or
Packit 6c4009
	absolute branch.  These are set up in __elf_machine_fixup_plt.
Packit 6c4009
Packit 6c4009
   (2)	Short lazy entries.  These cover the first 8192 slots in
Packit 6c4009
        the PLT, and look like (where 'index' goes from 0 to 8191):
Packit 6c4009
Packit 6c4009
	li %r11, index*4
Packit 6c4009
	b  &plt[PLT_TRAMPOLINE_ENTRY_WORDS+1]
Packit 6c4009
Packit 6c4009
   (3)	Short indirect jumps.  These replace (2) when a direct jump
Packit 6c4009
	wouldn't reach.  They look the same except that the branch
Packit 6c4009
	is 'b &plt[PLT_LONGBRANCH_ENTRY_WORDS]'.
Packit 6c4009
Packit 6c4009
   (4)  Long lazy entries.  These cover the slots when a short entry
Packit 6c4009
	won't fit ('index*4' overflows its field), and look like:
Packit 6c4009
Packit 6c4009
	lis %r11, %hi(index*4 + &plt[PLT_DATA_START_WORDS])
Packit 6c4009
	lwzu %r12, %r11, %lo(index*4 + &plt[PLT_DATA_START_WORDS])
Packit 6c4009
	b  &plt[PLT_TRAMPOLINE_ENTRY_WORDS]
Packit 6c4009
	bctr
Packit 6c4009
Packit 6c4009
   (5)	Long indirect jumps.  These replace (4) when a direct jump
Packit 6c4009
	wouldn't reach.  They look like:
Packit 6c4009
Packit 6c4009
	lis %r11, %hi(index*4 + &plt[PLT_DATA_START_WORDS])
Packit 6c4009
	lwz %r12, %r11, %lo(index*4 + &plt[PLT_DATA_START_WORDS])
Packit 6c4009
	mtctr %r12
Packit 6c4009
	bctr
Packit 6c4009
Packit 6c4009
   (6) Long direct jumps.  These are used when thread-safety is not
Packit 6c4009
       required.  They look like:
Packit 6c4009
Packit 6c4009
       lis %r12, %hi(finaladdr)
Packit 6c4009
       addi %r12, %r12, %lo(finaladdr)
Packit 6c4009
       mtctr %r12
Packit 6c4009
       bctr
Packit 6c4009
Packit 6c4009
Packit 6c4009
   The lazy entries, (2) and (4), are set up here in
Packit 6c4009
   __elf_machine_runtime_setup.  (1), (3), and (5) are set up in
Packit 6c4009
   __elf_machine_fixup_plt.  (1), (3), and (6) can also be constructed
Packit 6c4009
   in __process_machine_rela.
Packit 6c4009
Packit 6c4009
   The reason for the somewhat strange construction of the long
Packit 6c4009
   entries, (4) and (5), is that we need to ensure thread-safety.  For
Packit 6c4009
   (1) and (3), this is obvious because only one instruction is
Packit 6c4009
   changed and the PPC architecture guarantees that aligned stores are
Packit 6c4009
   atomic.  For (5), this is more tricky.  When changing (4) to (5),
Packit 6c4009
   the `b' instruction is first changed to `mtctr'; this is safe
Packit 6c4009
   and is why the `lwzu' instruction is not just a simple `addi'.
Packit 6c4009
   Once this is done, and is visible to all processors, the `lwzu' can
Packit 6c4009
   safely be changed to a `lwz'.  */
Packit 6c4009
int
Packit 6c4009
__elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
Packit 6c4009
{
Packit 6c4009
  if (map->l_info[DT_JMPREL])
Packit 6c4009
    {
Packit 6c4009
      Elf32_Word i;
Packit 6c4009
      Elf32_Word *plt = (Elf32_Word *) D_PTR (map, l_info[DT_PLTGOT]);
Packit 6c4009
      Elf32_Word num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
Packit 6c4009
				    / sizeof (Elf32_Rela));
Packit 6c4009
      Elf32_Word rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries);
Packit 6c4009
      Elf32_Word data_words = (Elf32_Word) (plt + rel_offset_words);
Packit 6c4009
      Elf32_Word size_modified;
Packit 6c4009
Packit 6c4009
      extern void _dl_runtime_resolve (void);
Packit 6c4009
      extern void _dl_prof_resolve (void);
Packit 6c4009
Packit 6c4009
      /* Convert the index in r11 into an actual address, and get the
Packit 6c4009
	 word at that address.  */
Packit 6c4009
      plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI (11, 11, data_words);
Packit 6c4009
      plt[PLT_LONGBRANCH_ENTRY_WORDS + 1] = OPCODE_LWZ (11, data_words, 11);
Packit 6c4009
Packit 6c4009
      /* Call the procedure at that address.  */
Packit 6c4009
      plt[PLT_LONGBRANCH_ENTRY_WORDS + 2] = OPCODE_MTCTR (11);
Packit 6c4009
      plt[PLT_LONGBRANCH_ENTRY_WORDS + 3] = OPCODE_BCTR ();
Packit 6c4009
Packit 6c4009
      if (lazy)
Packit 6c4009
	{
Packit 6c4009
	  Elf32_Word *tramp = plt + PLT_TRAMPOLINE_ENTRY_WORDS;
Packit 6c4009
	  Elf32_Word dlrr;
Packit 6c4009
	  Elf32_Word offset;
Packit 6c4009
Packit 6c4009
#ifndef PROF
Packit 6c4009
	  dlrr = (Elf32_Word) (profile
Packit 6c4009
			       ? _dl_prof_resolve
Packit 6c4009
			       : _dl_runtime_resolve);
Packit 6c4009
	  if (profile && GLRO(dl_profile) != NULL
Packit 6c4009
	      && _dl_name_match_p (GLRO(dl_profile), map))
Packit 6c4009
	    /* This is the object we are looking for.  Say that we really
Packit 6c4009
	       want profiling and the timers are started.  */
Packit 6c4009
	    GL(dl_profile_map) = map;
Packit 6c4009
#else
Packit 6c4009
	  dlrr = (Elf32_Word) _dl_runtime_resolve;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  /* For the long entries, subtract off data_words.  */
Packit 6c4009
	  tramp[0] = OPCODE_ADDIS_HI (11, 11, -data_words);
Packit 6c4009
	  tramp[1] = OPCODE_ADDI (11, 11, -data_words);
Packit 6c4009
Packit 6c4009
	  /* Multiply index of entry by 3 (in r11).  */
Packit 6c4009
	  tramp[2] = OPCODE_SLWI (12, 11, 1);
Packit 6c4009
	  tramp[3] = OPCODE_ADD (11, 12, 11);
Packit 6c4009
	  if (dlrr <= 0x01fffffc || dlrr >= 0xfe000000)
Packit 6c4009
	    {
Packit 6c4009
	      /* Load address of link map in r12.  */
Packit 6c4009
	      tramp[4] = OPCODE_LI (12, (Elf32_Word) map);
Packit 6c4009
	      tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) map);
Packit 6c4009
Packit 6c4009
	      /* Call _dl_runtime_resolve.  */
Packit 6c4009
	      tramp[6] = OPCODE_BA (dlrr);
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* Get address of _dl_runtime_resolve in CTR.  */
Packit 6c4009
	      tramp[4] = OPCODE_LI (12, dlrr);
Packit 6c4009
	      tramp[5] = OPCODE_ADDIS_HI (12, 12, dlrr);
Packit 6c4009
	      tramp[6] = OPCODE_MTCTR (12);
Packit 6c4009
Packit 6c4009
	      /* Load address of link map in r12.  */
Packit 6c4009
	      tramp[7] = OPCODE_LI (12, (Elf32_Word) map);
Packit 6c4009
	      tramp[8] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) map);
Packit 6c4009
Packit 6c4009
	      /* Call _dl_runtime_resolve.  */
Packit 6c4009
	      tramp[9] = OPCODE_BCTR ();
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Set up the lazy PLT entries.  */
Packit 6c4009
	  offset = PLT_INITIAL_ENTRY_WORDS;
Packit 6c4009
	  i = 0;
Packit 6c4009
	  while (i < num_plt_entries && i < PLT_DOUBLE_SIZE)
Packit 6c4009
	    {
Packit 6c4009
	      plt[offset  ] = OPCODE_LI (11, i * 4);
Packit 6c4009
	      plt[offset+1] = OPCODE_B ((PLT_TRAMPOLINE_ENTRY_WORDS + 2
Packit 6c4009
					 - (offset+1))
Packit 6c4009
					* 4);
Packit 6c4009
	      i++;
Packit 6c4009
	      offset += 2;
Packit 6c4009
	    }
Packit 6c4009
	  while (i < num_plt_entries)
Packit 6c4009
	    {
Packit 6c4009
	      plt[offset  ] = OPCODE_LIS_HI (11, i * 4 + data_words);
Packit 6c4009
	      plt[offset+1] = OPCODE_LWZU (12, i * 4 + data_words, 11);
Packit 6c4009
	      plt[offset+2] = OPCODE_B ((PLT_TRAMPOLINE_ENTRY_WORDS
Packit 6c4009
					 - (offset+2))
Packit 6c4009
					* 4);
Packit 6c4009
	      plt[offset+3] = OPCODE_BCTR ();
Packit 6c4009
	      i++;
Packit 6c4009
	      offset += 4;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Now, we've modified code.  We need to write the changes from
Packit 6c4009
	 the data cache to a second-level unified cache, then make
Packit 6c4009
	 sure that stale data in the instruction cache is removed.
Packit 6c4009
	 (In a multiprocessor system, the effect is more complex.)
Packit 6c4009
	 Most of the PLT shouldn't be in the instruction cache, but
Packit 6c4009
	 there may be a little overlap at the start and the end.
Packit 6c4009
Packit 6c4009
	 Assumes that dcbst and icbi apply to lines of 16 bytes or
Packit 6c4009
	 more.  Current known line sizes are 16, 32, and 128 bytes.
Packit 6c4009
	 The following gets the __cache_line_size, when available.  */
Packit 6c4009
Packit 6c4009
      /* Default minimum 4 words per cache line.  */
Packit 6c4009
      int line_size_words = 4;
Packit 6c4009
Packit 6c4009
      if (lazy && __cache_line_size != 0)
Packit 6c4009
	/* Convert bytes to words.  */
Packit 6c4009
	line_size_words = __cache_line_size / 4;
Packit 6c4009
Packit 6c4009
      size_modified = lazy ? rel_offset_words : 6;
Packit 6c4009
      for (i = 0; i < size_modified; i += line_size_words)
Packit 6c4009
        PPC_DCBST (plt + i);
Packit 6c4009
      PPC_DCBST (plt + size_modified - 1);
Packit 6c4009
      PPC_SYNC;
Packit 6c4009
Packit 6c4009
      for (i = 0; i < size_modified; i += line_size_words)
Packit 6c4009
        PPC_ICBI (plt + i);
Packit 6c4009
      PPC_ICBI (plt + size_modified - 1);
Packit 6c4009
      PPC_ISYNC;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return lazy;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Elf32_Addr
Packit 6c4009
__elf_machine_fixup_plt (struct link_map *map,
Packit 6c4009
			 Elf32_Addr *reloc_addr, Elf32_Addr finaladdr)
Packit 6c4009
{
Packit 6c4009
  Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr;
Packit 6c4009
  if (delta << 6 >> 6 == delta)
Packit 6c4009
    *reloc_addr = OPCODE_B (delta);
Packit 6c4009
  else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000)
Packit 6c4009
    *reloc_addr = OPCODE_BA (finaladdr);
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      Elf32_Word *plt, *data_words;
Packit 6c4009
      Elf32_Word index, offset, num_plt_entries;
Packit 6c4009
Packit 6c4009
      num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
Packit 6c4009
			 / sizeof(Elf32_Rela));
Packit 6c4009
      plt = (Elf32_Word *) D_PTR (map, l_info[DT_PLTGOT]);
Packit 6c4009
      offset = reloc_addr - plt;
Packit 6c4009
      index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
Packit 6c4009
      data_words = plt + PLT_DATA_START_WORDS (num_plt_entries);
Packit 6c4009
Packit 6c4009
      reloc_addr += 1;
Packit 6c4009
Packit 6c4009
      if (index < PLT_DOUBLE_SIZE)
Packit 6c4009
	{
Packit 6c4009
	  data_words[index] = finaladdr;
Packit 6c4009
	  PPC_SYNC;
Packit 6c4009
	  *reloc_addr = OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS - (offset+1))
Packit 6c4009
				  * 4);
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  index -= (index - PLT_DOUBLE_SIZE)/2;
Packit 6c4009
Packit 6c4009
	  data_words[index] = finaladdr;
Packit 6c4009
	  PPC_SYNC;
Packit 6c4009
Packit 6c4009
	  reloc_addr[1] = OPCODE_MTCTR (12);
Packit 6c4009
	  MODIFIED_CODE_NOQUEUE (reloc_addr + 1);
Packit 6c4009
	  PPC_SYNC;
Packit 6c4009
Packit 6c4009
	  reloc_addr[0] = OPCODE_LWZ (12,
Packit 6c4009
				      (Elf32_Word) (data_words + index), 11);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  MODIFIED_CODE (reloc_addr);
Packit 6c4009
  return finaladdr;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_reloc_overflow (struct link_map *map,
Packit 6c4009
		    const char *name,
Packit 6c4009
		    Elf32_Addr *const reloc_addr,
Packit 6c4009
		    const Elf32_Sym *refsym)
Packit 6c4009
{
Packit 6c4009
  char buffer[128];
Packit 6c4009
  char *t;
Packit 6c4009
  t = stpcpy (buffer, name);
Packit 6c4009
  t = stpcpy (t, " relocation at 0x00000000");
Packit 6c4009
  _itoa_word ((unsigned) reloc_addr, t, 16, 0);
Packit 6c4009
  if (refsym)
Packit 6c4009
    {
Packit 6c4009
      const char *strtab;
Packit 6c4009
Packit 6c4009
      strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit 6c4009
      t = stpcpy (t, " for symbol `");
Packit 6c4009
      t = stpcpy (t, strtab + refsym->st_name);
Packit 6c4009
      t = stpcpy (t, "'");
Packit 6c4009
    }
Packit 6c4009
  t = stpcpy (t, " out of range");
Packit 6c4009
  _dl_signal_error (0, map->l_name, NULL, buffer);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__process_machine_rela (struct link_map *map,
Packit 6c4009
			const Elf32_Rela *reloc,
Packit 6c4009
			struct link_map *sym_map,
Packit 6c4009
			const Elf32_Sym *sym,
Packit 6c4009
			const Elf32_Sym *refsym,
Packit 6c4009
			Elf32_Addr *const reloc_addr,
Packit 6c4009
			Elf32_Addr const finaladdr,
Packit 6c4009
			int rinfo)
Packit 6c4009
{
Packit 6c4009
  union unaligned
Packit 6c4009
    {
Packit 6c4009
      uint16_t u2;
Packit 6c4009
      uint32_t u4;
Packit 6c4009
    } __attribute__((__packed__));
Packit 6c4009
Packit 6c4009
  switch (rinfo)
Packit 6c4009
    {
Packit 6c4009
    case R_PPC_NONE:
Packit 6c4009
      return;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR32:
Packit 6c4009
    case R_PPC_GLOB_DAT:
Packit 6c4009
    case R_PPC_RELATIVE:
Packit 6c4009
      *reloc_addr = finaladdr;
Packit 6c4009
      return;
Packit 6c4009
Packit 6c4009
    case R_PPC_IRELATIVE:
Packit 6c4009
      *reloc_addr = ((Elf32_Addr (*) (void)) finaladdr) ();
Packit 6c4009
      return;
Packit 6c4009
Packit 6c4009
    case R_PPC_UADDR32:
Packit 6c4009
      ((union unaligned *) reloc_addr)->u4 = finaladdr;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR24:
Packit 6c4009
      if (__glibc_unlikely (finaladdr > 0x01fffffc && finaladdr < 0xfe000000))
Packit 6c4009
	_dl_reloc_overflow (map,  "R_PPC_ADDR24", reloc_addr, refsym);
Packit 6c4009
      *reloc_addr = (*reloc_addr & 0xfc000003) | (finaladdr & 0x3fffffc);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR16:
Packit 6c4009
      if (__glibc_unlikely (finaladdr > 0x7fff && finaladdr < 0xffff8000))
Packit 6c4009
	_dl_reloc_overflow (map,  "R_PPC_ADDR16", reloc_addr, refsym);
Packit 6c4009
      *(Elf32_Half*) reloc_addr = finaladdr;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_UADDR16:
Packit 6c4009
      if (__glibc_unlikely (finaladdr > 0x7fff && finaladdr < 0xffff8000))
Packit 6c4009
	_dl_reloc_overflow (map,  "R_PPC_UADDR16", reloc_addr, refsym);
Packit 6c4009
      ((union unaligned *) reloc_addr)->u2 = finaladdr;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR16_LO:
Packit 6c4009
      *(Elf32_Half*) reloc_addr = finaladdr;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR16_HI:
Packit 6c4009
      *(Elf32_Half*) reloc_addr = finaladdr >> 16;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR16_HA:
Packit 6c4009
      *(Elf32_Half*) reloc_addr = (finaladdr + 0x8000) >> 16;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_ADDR14:
Packit 6c4009
    case R_PPC_ADDR14_BRTAKEN:
Packit 6c4009
    case R_PPC_ADDR14_BRNTAKEN:
Packit 6c4009
      if (__glibc_unlikely (finaladdr > 0x7fff && finaladdr < 0xffff8000))
Packit 6c4009
	_dl_reloc_overflow (map,  "R_PPC_ADDR14", reloc_addr, refsym);
Packit 6c4009
      *reloc_addr = (*reloc_addr & 0xffff0003) | (finaladdr & 0xfffc);
Packit 6c4009
      if (rinfo != R_PPC_ADDR14)
Packit 6c4009
	*reloc_addr = ((*reloc_addr & 0xffdfffff)
Packit 6c4009
		       | ((rinfo == R_PPC_ADDR14_BRTAKEN)
Packit 6c4009
			  ^ (finaladdr >> 31)) << 21);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_REL24:
Packit 6c4009
      {
Packit 6c4009
	Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr;
Packit 6c4009
	if (delta << 6 >> 6 != delta)
Packit 6c4009
	  _dl_reloc_overflow (map,  "R_PPC_REL24", reloc_addr, refsym);
Packit 6c4009
	*reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case R_PPC_COPY:
Packit 6c4009
      if (sym == NULL)
Packit 6c4009
	/* This can happen in trace mode when an object could not be
Packit 6c4009
	   found.  */
Packit 6c4009
	return;
Packit 6c4009
      if (sym->st_size > refsym->st_size
Packit 6c4009
	  || (GLRO(dl_verbose) && sym->st_size < refsym->st_size))
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, strtab + refsym->st_name);
Packit 6c4009
	}
Packit 6c4009
      memcpy (reloc_addr, (char *) finaladdr, MIN (sym->st_size,
Packit 6c4009
						   refsym->st_size));
Packit 6c4009
      return;
Packit 6c4009
Packit 6c4009
    case R_PPC_REL32:
Packit 6c4009
      *reloc_addr = finaladdr - (Elf32_Word) reloc_addr;
Packit 6c4009
      return;
Packit 6c4009
Packit 6c4009
    case R_PPC_JMP_SLOT:
Packit 6c4009
      /* It used to be that elf_machine_fixup_plt was used here,
Packit 6c4009
	 but that doesn't work when ld.so relocates itself
Packit 6c4009
	 for the second time.  On the bright side, there's
Packit 6c4009
         no need to worry about thread-safety here.  */
Packit 6c4009
      {
Packit 6c4009
	Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr;
Packit 6c4009
	if (delta << 6 >> 6 == delta)
Packit 6c4009
	  *reloc_addr = OPCODE_B (delta);
Packit 6c4009
	else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000)
Packit 6c4009
	  *reloc_addr = OPCODE_BA (finaladdr);
Packit 6c4009
	else
Packit 6c4009
	  {
Packit 6c4009
	    Elf32_Word *plt, *data_words;
Packit 6c4009
	    Elf32_Word index, offset, num_plt_entries;
Packit 6c4009
Packit 6c4009
	    plt = (Elf32_Word *) D_PTR (map, l_info[DT_PLTGOT]);
Packit 6c4009
	    offset = reloc_addr - plt;
Packit 6c4009
Packit 6c4009
	    if (offset < PLT_DOUBLE_SIZE*2 + PLT_INITIAL_ENTRY_WORDS)
Packit 6c4009
	      {
Packit 6c4009
		index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
Packit 6c4009
		num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
Packit 6c4009
				   / sizeof(Elf32_Rela));
Packit 6c4009
		data_words = plt + PLT_DATA_START_WORDS (num_plt_entries);
Packit 6c4009
		data_words[index] = finaladdr;
Packit 6c4009
		reloc_addr[0] = OPCODE_LI (11, index * 4);
Packit 6c4009
		reloc_addr[1] = OPCODE_B ((PLT_LONGBRANCH_ENTRY_WORDS
Packit 6c4009
					   - (offset+1))
Packit 6c4009
					  * 4);
Packit 6c4009
		MODIFIED_CODE_NOQUEUE (reloc_addr + 1);
Packit 6c4009
	      }
Packit 6c4009
	    else
Packit 6c4009
	      {
Packit 6c4009
		reloc_addr[0] = OPCODE_LIS_HI (12, finaladdr);
Packit 6c4009
		reloc_addr[1] = OPCODE_ADDI (12, 12, finaladdr);
Packit 6c4009
		reloc_addr[2] = OPCODE_MTCTR (12);
Packit 6c4009
		reloc_addr[3] = OPCODE_BCTR ();
Packit 6c4009
		MODIFIED_CODE_NOQUEUE (reloc_addr + 3);
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
#define DO_TLS_RELOC(suffix)						      \
Packit 6c4009
    case R_PPC_DTPREL##suffix:						      \
Packit 6c4009
      /* During relocation all TLS symbols are defined and used.	      \
Packit 6c4009
	 Therefore the offset is already correct.  */			      \
Packit 6c4009
      if (sym_map != NULL)						      \
Packit 6c4009
	do_reloc##suffix ("R_PPC_DTPREL"#suffix,			      \
Packit 6c4009
			  TLS_DTPREL_VALUE (sym, reloc));		      \
Packit 6c4009
      break;								      \
Packit 6c4009
    case R_PPC_TPREL##suffix:						      \
Packit 6c4009
      if (sym_map != NULL)						      \
Packit 6c4009
	{								      \
Packit 6c4009
	  CHECK_STATIC_TLS (map, sym_map);				      \
Packit 6c4009
	  do_reloc##suffix ("R_PPC_TPREL"#suffix,			      \
Packit 6c4009
			    TLS_TPREL_VALUE (sym_map, sym, reloc));	      \
Packit 6c4009
	}								      \
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    inline void do_reloc16 (const char *r_name, Elf32_Addr value)
Packit 6c4009
      {
Packit 6c4009
	if (__glibc_unlikely (value > 0x7fff && value < 0xffff8000))
Packit 6c4009
	  _dl_reloc_overflow (map, r_name, reloc_addr, refsym);
Packit 6c4009
	*(Elf32_Half *) reloc_addr = value;
Packit 6c4009
      }
Packit 6c4009
    inline void do_reloc16_LO (const char *r_name, Elf32_Addr value)
Packit 6c4009
      {
Packit 6c4009
	*(Elf32_Half *) reloc_addr = value;
Packit 6c4009
      }
Packit 6c4009
    inline void do_reloc16_HI (const char *r_name, Elf32_Addr value)
Packit 6c4009
      {
Packit 6c4009
	*(Elf32_Half *) reloc_addr = value >> 16;
Packit 6c4009
      }
Packit 6c4009
    inline void do_reloc16_HA (const char *r_name, Elf32_Addr value)
Packit 6c4009
      {
Packit 6c4009
	*(Elf32_Half *) reloc_addr = (value + 0x8000) >> 16;
Packit 6c4009
      }
Packit 6c4009
    DO_TLS_RELOC (16)
Packit 6c4009
    DO_TLS_RELOC (16_LO)
Packit 6c4009
    DO_TLS_RELOC (16_HI)
Packit 6c4009
    DO_TLS_RELOC (16_HA)
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      _dl_reloc_bad_type (map, rinfo, 0);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  MODIFIED_CODE_NOQUEUE (reloc_addr);
Packit 6c4009
}