about summary refs log tree commit diff
path: root/sysdeps/powerpc/dl-machine.h
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/powerpc/dl-machine.h')
-rw-r--r--sysdeps/powerpc/dl-machine.h201
1 files changed, 136 insertions, 65 deletions
diff --git a/sysdeps/powerpc/dl-machine.h b/sysdeps/powerpc/dl-machine.h
index 526887da18..ab22e46956 100644
--- a/sysdeps/powerpc/dl-machine.h
+++ b/sysdeps/powerpc/dl-machine.h
@@ -188,14 +188,21 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
 	  finaladdr = reloc->r_addend;
 	}
       else
-	finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value +
-		     reloc->r_addend);
+	finaladdr = (loadbase + (Elf32_Word) (char *) sym->st_value
+		     + reloc->r_addend);
     }
 
   /* This is an if/else if chain because GCC 2.7.2.[012] turns case
      statements into non-PIC table lookups.  When a later version
      comes out that fixes this, this should be changed.  */
-  if (rinfo == R_PPC_ADDR16_LO)
+  if (rinfo == R_PPC_UADDR32 ||
+      rinfo == R_PPC_GLOB_DAT ||
+      rinfo == R_PPC_ADDR32 ||
+      rinfo == R_PPC_RELATIVE)
+    {
+      *reloc_addr = finaladdr;
+    }
+  else if (rinfo == R_PPC_ADDR16_LO)
     {
       *(Elf32_Half*) reloc_addr = finaladdr;
     }
@@ -205,32 +212,29 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
     }
   else if (rinfo == R_PPC_ADDR16_HA)
     {
-      *(Elf32_Half*) reloc_addr = finaladdr + 0x8000 >> 16;
+      *(Elf32_Half*) reloc_addr = (finaladdr + 0x8000) >> 16;
     }
+#ifndef RTLD_BOOTSTRAP
   else if (rinfo == R_PPC_REL24)
     {
       Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
-      assert (delta << 6 >> 6 == delta);
+      if (delta << 6 >> 6 != delta)
+	_dl_signal_error (0, map->l_name,
+			  "R_PPC_REL24 relocation out of range");
       *reloc_addr = *reloc_addr & 0xfc000003 | delta & 0x3fffffc;
     }
-  else if (rinfo == R_PPC_UADDR32 ||
-      rinfo == R_PPC_GLOB_DAT ||
-      rinfo == R_PPC_ADDR32 ||
-      rinfo == R_PPC_RELATIVE)
-    {
-      *reloc_addr = finaladdr;
-    }
   else if (rinfo == R_PPC_ADDR24)
     {
-      assert (finaladdr << 6 >> 6 == finaladdr);
+      if (finaladdr << 6 >> 6 != finaladdr)
+	_dl_signal_error (0, map->l_name,
+			  "R_PPC_ADDR24 relocation out of range");
       *reloc_addr = *reloc_addr & 0xfc000003 | finaladdr & 0x3fffffc;
     }
   else if (rinfo == R_PPC_COPY)
     {
-      /* Memcpy is safe to use here, because ld.so doesn't have any
-	 COPY relocs (it's self-contained).  */
       memcpy (reloc_addr, (char *) finaladdr, sym->st_size);
     }
+#endif
   else if (rinfo == R_PPC_REL32)
     {
       *reloc_addr = finaladdr - (Elf32_Word) (char *) reloc_addr;
@@ -239,41 +243,44 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
     {
       Elf32_Sword delta = finaladdr - (Elf32_Word) (char *) reloc_addr;
       if (delta << 6 >> 6 == delta)
-	*reloc_addr = OPCODE_B(delta);
+	*reloc_addr = OPCODE_B (delta);
       else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000)
-	*reloc_addr = OPCODE_BA(finaladdr);
+	*reloc_addr = OPCODE_BA (finaladdr);
       else
 	{
-	  Elf32_Word *plt = (Elf32_Word *)((char *)map->l_addr +
-					   map->l_info[DT_PLTGOT]->d_un.d_val);
-	  Elf32_Word index = (reloc_addr - plt - PLT_INITIAL_ENTRY_WORDS)/2;
-	  Elf32_Word offset = index * 2 + PLT_INITIAL_ENTRY_WORDS;
+	  Elf32_Word *plt;
+	  Elf32_Word index;
+
+	  plt = (Elf32_Word *)((char *)map->l_addr
+			       + map->l_info[DT_PLTGOT]->d_un.d_val);
+	  index = (reloc_addr - plt - PLT_INITIAL_ENTRY_WORDS)/2;
 
 	  if (index >= PLT_DOUBLE_SIZE)
 	    {
-	     /* Slots greater than or equal to 2^13 have 4 words
-		available instead of two.  */
-	      plt[offset  ] = OPCODE_LI (11,finaladdr);
-	      plt[offset+1] = OPCODE_ADDIS (11,11,finaladdr + 0x8000 >> 16);
-	      plt[offset+2] = OPCODE_MTCTR (11);
-	      plt[offset+3] = OPCODE_BCTR ();
+	      /* Slots greater than or equal to 2^13 have 4 words available
+		 instead of two.  */
+	      reloc_addr[0] = OPCODE_LI (11, finaladdr);
+	      reloc_addr[1] = OPCODE_ADDIS (11, 11, finaladdr + 0x8000 >> 16);
+	      reloc_addr[2] = OPCODE_MTCTR (11);
+	      reloc_addr[3] = OPCODE_BCTR ();
 	    }
 	  else
 	    {
 	      Elf32_Word num_plt_entries;
-	      Elf32_Word rel_offset_words;
 
 	      num_plt_entries = (map->l_info[DT_PLTRELSZ]->d_un.d_val
 				 / sizeof(Elf32_Rela));
-	      rel_offset_words = PLT_DATA_START_WORDS (num_plt_entries);
 
-	      plt[offset  ] = OPCODE_LI (11,index * 4);
-	      plt[offset+1] = OPCODE_B (-(4 * (offset + 1
-					       - PLT_LONGBRANCH_ENTRY_WORDS)));
-	      plt[index + rel_offset_words] = finaladdr;
+	      reloc_addr[0] = OPCODE_LI (11, index*4);
+	      reloc_addr[1] =
+		OPCODE_B (-(4*(index*2
+			       + 1
+			       - PLT_LONGBRANCH_ENTRY_WORDS
+			       + PLT_INITIAL_ENTRY_WORDS)));
+	      plt[index+PLT_DATA_START_WORDS (num_plt_entries)] = finaladdr;
 	    }
 	}
-      MODIFIED_CODE(reloc_addr);
+      MODIFIED_CODE (reloc_addr);
     }
   else
     assert (! "unexpected dynamic reloc type");
@@ -295,7 +302,28 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
 #define elf_machine_pltrel_p(type) ((type) == R_PPC_JMP_SLOT)
 
 /* Set up the loaded object described by L so its unrelocated PLT
-   entries will jump to the on-demand fixup code in dl-runtime.c.  */
+   entries will jump to the on-demand fixup code in dl-runtime.c.
+   Also install a small trampoline to be used by entries that have
+   been relocated to an address too far away for a single branch.  */
+
+/* A PLT entry does one of three things:
+   (i)   Jumps to the actual routine. Such entries are set up above, in
+         elf_machine_rela.
+
+   (ii)  Jumps to the actual routine via glue at the start of the PLT.
+         We do this by putting the address of the routine in space
+         allocated at the end of the PLT, and when the PLT entry is
+         called we load the offset of that word (from the start of the
+         space) into r11, then call the glue, which loads the word and
+         branches to that address. These entries are set up in
+         elf_machine_rela, but the glue is set up here.
+
+   (iii) Loads the index of this PLT entry (we count the double-size
+	 entries as one entry for this purpose) into r11, then
+	 branches to code at the start of the PLT. This code then
+	 calls `fixup', in dl-runtime.c, via the glue in the macro
+	 ELF_MACHINE_RUNTIME_TRAMPOLINE, which resets the PLT entry to
+	 be one of the above two types. These entries are set up here.  */
 static inline void
 elf_machine_runtime_setup (struct link_map *map, int lazy)
 {
@@ -316,54 +344,78 @@ elf_machine_runtime_setup (struct link_map *map, int lazy)
       if (lazy)
 	for (i = 0; i < num_plt_entries; i++)
 	{
-	  Elf32_Word offset = PLT_ENTRY_START_WORDS(i);
+	  Elf32_Word offset = PLT_ENTRY_START_WORDS (i);
 
 	  if (i >= PLT_DOUBLE_SIZE)
 	    {
-	      plt[offset	] = OPCODE_LI (11, i * 4);
-	      plt[offset+1] = OPCODE_ADDIS (11, 11, i * 4 + 0x8000 >> 16);
+	      plt[offset  ] = OPCODE_LI (11, i * 4);
+	      plt[offset+1] = OPCODE_ADDIS (11, 11, (i * 4 + 0x8000) >> 16);
 	      plt[offset+2] = OPCODE_B (-(4 * (offset + 2)));
 	    }
 	  else
 	    {
-	      plt[offset	] = OPCODE_LI (11, i * 4);
-	      plt[offset+1] = OPCODE_B(-(4 * (offset + 1)));
+	      plt[offset  ] = OPCODE_LI (11, i * 4);
+	      plt[offset+1] = OPCODE_B (-(4 * (offset + 1)));
 	    }
+	}
 
-	  /* Multiply index of entry, by 0xC.  */
-	  plt[0] = OPCODE_SLWI (12, 11, 1);
-	  plt[1] = OPCODE_ADD (11, 12, 11);
-	  if ((Elf32_Word) (char *) _dl_runtime_resolve <= 0x01fffffc
-	      || (Elf32_Word) (char *) _dl_runtime_resolve >= 0xfe000000)
-	    {
-	      plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) map);
-	      plt[3] = OPCODE_ADDIS (12, 12,
-				     (Elf32_Word) (char *) map + 0x8000 >> 16);
-	      plt[4] = OPCODE_BA ((Elf32_Word) (char *) _dl_runtime_resolve);
-	    }
+      /* Multiply index of entry by 3 (in r11).  */
+      plt[0] = OPCODE_SLWI (12, 11, 1);
+      plt[1] = OPCODE_ADD (11, 12, 11);
+      if ((Elf32_Word) (char *) _dl_runtime_resolve <= 0x01fffffc ||
+	  (Elf32_Word) (char *) _dl_runtime_resolve >= 0xfe000000)
+	{
+	  /* Load address of link map in r12.  */
+	  plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) map);
+	  plt[3] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map
+					   + 0x8000) >> 16));
+
+	  /* Call _dl_runtime_resolve.  */
+	  plt[4] = OPCODE_BA ((Elf32_Word) (char *) _dl_runtime_resolve);
 	}
       else
 	{
+	  /* Get address of _dl_runtime_resolve in CTR.  */
 	  plt[2] = OPCODE_LI (12, (Elf32_Word) (char *) _dl_runtime_resolve);
-	  plt[3] = OPCODE_ADDIS(12, 12, 0x8000 +
-				((Elf32_Word) (char *) _dl_runtime_resolve
-				 >> 16));
+	  plt[3] = OPCODE_ADDIS (12, 12, ((((Elf32_Word) (char *)
+					    _dl_runtime_resolve)
+					   + 0x8000) >> 16));
 	  plt[4] = OPCODE_MTCTR (12);
+
+	  /* Load address of link map in r12.  */
 	  plt[5] = OPCODE_LI (12, (Elf32_Word) (char *) map);
-	  plt[6] = OPCODE_ADDIS (12, 12, ((Elf32_Word) (char *) map
-					  + 0x8000 >> 16));
+	  plt[6] = OPCODE_ADDIS (12, 12, (((Elf32_Word) (char *) map
+					   + 0x8000) >> 16));
+
+	  /* Call _dl_runtime_resolve.  */
 	  plt[7] = OPCODE_BCTR ();
 	}
+
+
+      /* Convert the index in r11 into an actual address, and get the
+	 word at that address.  */
       plt[PLT_LONGBRANCH_ENTRY_WORDS] =
-	OPCODE_ADDIS (11, 11, (Elf32_Word) (char*) (plt + rel_offset_words)
-		      + 0x8000 >> 16);
+	OPCODE_ADDIS (11, 11, (((Elf32_Word) (char*) (plt + rel_offset_words)
+				+ 0x8000) >> 16));
       plt[PLT_LONGBRANCH_ENTRY_WORDS+1] =
-	OPCODE_LWZ(11,(Elf32_Word)(char*)(plt+rel_offset_words),11);
+	OPCODE_LWZ (11, (Elf32_Word) (char*) (plt+rel_offset_words), 11);
+
+      /* Call the procedure at that address.  */
       plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR (11);
       plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR ();
 
+
+      /* Now, we've modified code (quite a lot of code, possibly).  We
+	 need to write the changes from the data cache to a
+	 second-level unified cache, then make sure that stale data in
+	 the instruction cache is removed.  (In a multiprocessor
+	 system, the effect is more complex.)
+
+	 Assumes the cache line size is at least 32 bytes, or at least
+	 that dcbst and icbi apply to 32-byte lines. At present, all
+	 PowerPC processors have line sizes of exactly 32 bytes.  */
+
       size_modified = lazy ? rel_offset_words : PLT_INITIAL_ENTRY_WORDS;
-      /* Now we need to keep the caches in sync.  */
       for (i = 0; i < size_modified; i+=8)
 	PPC_DCBST (plt + i);
       PPC_SYNC;
@@ -411,7 +463,7 @@ _dl_runtime_resolve:
  # The code that calls this has put parameters for `fixup' in r12 and r11.
 	mr 3,12
 	mr 4,11
-	bl fixup
+	bl fixup@local
  # 'fixup' returns the address we want to branch to.
 	mtctr 3
  # Put the registers back...
@@ -440,8 +492,9 @@ _dl_runtime_resolve:
    The C function `_dl_start' is the real entry point;
    its return value is the user program's entry point.	*/
 #define RTLD_START \
+static ElfW(Addr) _dl_start (void *arg) __attribute__((unused)); \
 asm ("\
-	.text
+	.section \".text\"
 	.align 2
 	.globl _start
 	.type _start,@function
@@ -535,16 +588,34 @@ _start:
 	.previous
 ");
 
+/* The idea here is that to conform to the ABI, we are supposed to try
+   to load dynamic objects between 0x10000 (we actually use 0x40000 as
+   the lower bound, to increase the chance of a memory reference from
+   a null pointer giving a segfault) and the program's load address.
+   Regrettably, in this code we can't find the program's load address,
+   so we punt and choose 0x01800000, which is below the ABI's
+   recommended default, and what GNU ld currently chooses. We only use
+   the address as a preference for mmap, so if we get it wrong the
+   worst that happens is that it gets mapped somewhere else.
+
+   FIXME: Unfortunately, 'somewhere else' is probably right after the
+   program's break, which causes malloc to fail.  We really need more
+   information here about the way memory is mapped.  */
+
 #define ELF_PREFERRED_ADDRESS_DATA					      \
-static ElfW(Addr) _dl_preferred_address = 0;
+static ElfW(Addr) _dl_preferred_address = 1;
 
 #define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref)		      \
 ( {									      \
    ElfW(Addr) prefd;							      \
-   if (mapstartpref != 0 && _dl_preferred_address == 0)			      \
+   if (mapstartpref != 0 && _dl_preferred_address == 1)			      \
      _dl_preferred_address = mapstartpref;				      \
    if (mapstartpref != 0)						      \
      prefd = mapstartpref;						      \
+   else if (_dl_preferred_address == 1)					      \
+     prefd = _dl_preferred_address =					      \
+	  (0x01800000 - maplength - 0x10000) &				      \
+	   ~(_dl_pagesize - 1);						      \
    else if (_dl_preferred_address < maplength + 0x50000)		      \
      prefd = 0;								      \
    else									      \
@@ -556,7 +627,7 @@ static ElfW(Addr) _dl_preferred_address = 0;
 
 #define ELF_FIXED_ADDRESS(loader, mapstart)				      \
 ( {									      \
-   if (mapstart != 0 && _dl_preferred_address < mapstart)		      \
+   if (mapstart != 0 && _dl_preferred_address == 1)			      \
      _dl_preferred_address = mapstart;					      \
 } )