about summary refs log tree commit diff
path: root/sysdeps/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/powerpc')
-rw-r--r--sysdeps/powerpc/bsd-setjmp.S7
-rw-r--r--sysdeps/powerpc/dl-machine.h201
-rw-r--r--sysdeps/powerpc/elf/start.c1
-rw-r--r--sysdeps/powerpc/strlen.S52
4 files changed, 142 insertions, 119 deletions
diff --git a/sysdeps/powerpc/bsd-setjmp.S b/sysdeps/powerpc/bsd-setjmp.S
index 19c2fe308c..1a6300660e 100644
--- a/sysdeps/powerpc/bsd-setjmp.S
+++ b/sysdeps/powerpc/bsd-setjmp.S
@@ -23,11 +23,14 @@
 
 #include <sysdep.h>
 
-ENTRY (setjmp)
+ENTRY (__setjmp)
 	li 4,1				/* Set second argument to 1.  */
 #ifdef PIC
 	b __sigsetjmp@plt
 #else
 	b __sigsetjmp
 #endif
-END (setjmp)
+END (__setjmp)
+
+	.globl setjmp
+	.set setjmp,__setjmp
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;					      \
 } )
 
diff --git a/sysdeps/powerpc/elf/start.c b/sysdeps/powerpc/elf/start.c
index 001d6aa97c..d32aeee6c7 100644
--- a/sysdeps/powerpc/elf/start.c
+++ b/sysdeps/powerpc/elf/start.c
@@ -29,6 +29,7 @@ asm ("\
 	.section \".text\"
 	.align 2
 	.globl _start
+	.type _start,@function
 _start:
  # save the stack pointer, in case we're statically linked under Linux
 	mr 8,1
diff --git a/sysdeps/powerpc/strlen.S b/sysdeps/powerpc/strlen.S
deleted file mode 100644
index 95eaf94de1..0000000000
--- a/sysdeps/powerpc/strlen.S
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Optimized strlen implementation for PowerPC.
-   Copyright (C) 1997 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
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
-
-	.section	".text"
-	.align 2
-	.globl strlen
-	.type	 strlen,@function
-strlen:
-	rlwinm 7,3,0,0,29
-	lis 8,0x7f7f
-	lwz 11,0(7)
-	ori 8,8,32639
-	rlwinm 4,3,3,27,28
-	li 9,-1
-	or 10,11,8
-	and 0,11,8
-	srw 9,9,4
-	add 0,0,8
-	nor 0,10,0
-	and. 11,0,9
-	bc 4,2,.L2
-.L3:
-	lwzu 11,4(7)
-	or 10,11,8
-	and 0,11,8
-	add 0,0,8
-	nor. 11,10,0
-	bc 12,2,.L3
-.L2:
-	subf 0,3,7
-	cntlzw 3,11
-	srwi 3,3,3
-	add 3,0,3
-	blr
-.Lfe1:
-	.size	 strlen,.Lfe1-strlen