[PATCH] New s390* relocations.

Martin Schwidefsky schwidefsky@de.ibm.com
Mon Jan 20 11:43:00 GMT 2003


Hi
our compiler guys asked me to implement some more relocations
that use the got pointer to find the target. I've implemented
@gotoff, @gotplt and @pltoff with different bit sizes. @gotoff has
been there before but now it works with the standard meaning,
@gotplt calculates the offset in the got to the entry used in the
plt code for plt inlining and @pltoff gives you the offset of a plt
entry from the got. All these new relocations aren't used by the
current gcc backend for s390 but future versions might use them.

blue skies,
  Martin.

bfd/ChangeLog:
2003-01-20  Martin Schwidefsky  <schwidefsky@de.ibm.com>

	* bfd-in2.h: Regenerate.
	* elf32-s390.c (elf_s390_adjust_gotplt): New prototype.
	(elf_howto_table): Rename R_390_GOTOFF to R_390_GOTOFF32. Add
	R_390_GOTOFF16, R_390_GOTOFF64, R_390_GOTPLT12, R_390_GOTPLT16,
	R_390_GOTPLT32, R_390_GOTPLT64, R_390_GOTPLTENT, R_390_PLTOFF16,
	R_390_PLTOFF32 and R_390_PLTOFF64.
	(elf_s390_reloc_type_lookup): Likewise.
	(struct elf_s390_link_hash_entry): Add gotplt_refcount to keep track
	of GOTPLT references to a function.
	(link_hash_newfunc): Initialize gotplt_refcount.
	(elf_s390_check_relocs): Move allocation of local_got_refcounts array
	and creation of the got section out of the main switch. Add support
	for the gotoff, gotplt and pltoff relocations.
	(elf_s390_gc_sweep_hook): Add reference counting for gotoff, gotplt
	and pltoff.
	(elf_s390_adjust_gotplt): New function.
	(elf_s390_adjust_dynamic_symbol): Adjust gotplt refcount for removed
	plt entries.
	(allocate_dynrelocs): Add comment.
	(elf_s390_relocate_section): Change r_type to unsigned. Add support
	for gotoff, gotplt and pltoff relocations.
	* elf64-s390.c: Same changes as for elf32-s390.c.
	* libbfd.h: Regenerate.
	* reloc.c: Add BFD_RELOC_390_GOTOFF64, BFD_RELOC_390_GOTPLT12,
	BFD_RELOC_390_GOTPLT16, BFD_RELOC_390_GOTPLT32, BFD_RELOC_390_GOTPLT64,
	BFD_RELOC_390_GOTPLTENT, BFD_RELOC_390_PLTOFF16, BFD_RELOC_390_PLTOFF32
	and BFD_RELOC_390_PLTOFF64.

gas/ChangeLog:
2003-01-20  Martin Schwidefsky  <schwidefsky@de.ibm.com>

	* config/tc-s390.c (elf_suffix_type): Add suffix enums for gotoff,
	gotplt and pltoff relocations.
	(s390_elf_suffix): Add suffix strings for gotoff, gotplt and pltoff.
	(s390_elf_cons): Map new lenght/elf suffix combinations for gotoff,
	gotplt and pltoff to bfd relocations.
	(md_gather_operands): Map new instruction operand/elf suffix
	combinations to bfd relocations.
	(tc_s390_fix_adjustable): Add new gotoff, gotplt and pltoff relocations
	to the list of unadjustable relocations.
	(tc_s390_force_relocation): Always emit relocations for gotoff, gotplt
	and pltoff relocations.
	(md_apply_fix3): Add the new relocations.

gas/testsuite/ChangeLog:
2003-01-20  Martin Schwidefsky  <schwidefsky@de.ibm.com>

	* gas/s390/reloc.d: Add tests for the new gotoff, gotplt and pltoff
	relocations.
	* gas/s390/reloc.s: Likewise.
	* gas/s390/reloc64.d: Likewise.
	* gas/s390/reloc64.s: Likewise.

include/elf/ChangeLog:
2003-01-20  Martin Schwidefsky  <schwidefsky@de.ibm.com>

	* s390.h: Rename R_390_GOTOFF to R_390_GOTOFF32. Add new gotoff,
	gotplt and pltoff relocations.

diff -urN src/bfd/bfd-in2.h src-s390/bfd/bfd-in2.h
--- src/bfd/bfd-in2.h	Fri Jan 17 16:26:55 2003
+++ src-s390/bfd/bfd-in2.h	Mon Jan 20 12:29:08 2003
@@ -2996,6 +2996,33 @@
 /* 32 bit rel. offset to GOT entry.  */
   BFD_RELOC_390_GOTENT,
 
+/* 64 bit offset to GOT.  */
+  BFD_RELOC_390_GOTOFF64,
+
+/* 12-bit offset to symbol-entry within GOT, with PLT handling.  */
+  BFD_RELOC_390_GOTPLT12,
+
+/* 16-bit offset to symbol-entry within GOT, with PLT handling.  */
+  BFD_RELOC_390_GOTPLT16,
+
+/* 32-bit offset to symbol-entry within GOT, with PLT handling.  */
+  BFD_RELOC_390_GOTPLT32,
+
+/* 64-bit offset to symbol-entry within GOT, with PLT handling.  */
+  BFD_RELOC_390_GOTPLT64,
+
+/* 32-bit rel. offset to symbol-entry within GOT, with PLT handling.  */
+  BFD_RELOC_390_GOTPLTENT,
+
+/* 16-bit rel. offset from the GOT to a PLT entry.  */
+  BFD_RELOC_390_PLTOFF16,
+
+/* 32-bit rel. offset from the GOT to a PLT entry.  */
+  BFD_RELOC_390_PLTOFF32,
+
+/* 64-bit rel. offset from the GOT to a PLT entry.  */
+  BFD_RELOC_390_PLTOFF64,
+
 /* Scenix IP2K - 9-bit register number / data address  */
   BFD_RELOC_IP2K_FR9,
 
diff -urN src/bfd/elf32-s390.c src-s390/bfd/elf32-s390.c
--- src/bfd/elf32-s390.c	Fri Jan 17 16:26:55 2003
+++ src-s390/bfd/elf32-s390.c	Mon Jan 20 12:29:08 2003
@@ -51,6 +51,9 @@
 static bfd_boolean elf_s390_gc_sweep_hook
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
 	   const Elf_Internal_Rela *));
+struct elf_s390_link_hash_entry;
+static void elf_s390_adjust_gotplt
+  PARAMS ((struct elf_s390_link_hash_entry *));
 static bfd_boolean elf_s390_adjust_dynamic_symbol
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static bfd_boolean allocate_dynrelocs
@@ -94,28 +97,71 @@
 	 0,			/* dst_mask */
 	 FALSE),		/* pcrel_offset */
 
-  HOWTO(R_390_8,         0, 0,  8, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_8",       FALSE, 0,0x000000ff, FALSE),
-  HOWTO(R_390_12,        0, 1, 12, FALSE, 0, complain_overflow_dont, bfd_elf_generic_reloc, "R_390_12",      FALSE, 0,0x00000fff, FALSE),
-  HOWTO(R_390_16,        0, 1, 16, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_16",      FALSE, 0,0x0000ffff, FALSE),
-  HOWTO(R_390_32,        0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_32",      FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_PC32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC32",    FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_GOT12,	 0, 1, 12, FALSE, 0, complain_overflow_dont, bfd_elf_generic_reloc, "R_390_GOT12",   FALSE, 0,0x00000fff, FALSE),
-  HOWTO(R_390_GOT32,	 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT32",   FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_PLT32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT32",   FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_COPY,      0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_COPY",    FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_GLOB_DAT,  0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GLOB_DAT",FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_JMP_SLOT,  0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_JMP_SLOT",FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_RELATIVE,  0, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_RELATIVE",FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_GOTOFF,    0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTOFF",  FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_GOTPC,     0, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTPC",   FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_GOT16,     0, 1, 16, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT16",   FALSE, 0,0x0000ffff, FALSE),
-  HOWTO(R_390_PC16,      0, 1, 16,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC16",    FALSE, 0,0x0000ffff,  TRUE),
-  HOWTO(R_390_PC16DBL,   1, 1, 16,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC16DBL", FALSE, 0,0x0000ffff,  TRUE),
-  HOWTO(R_390_PLT16DBL,  1, 1, 16,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT16DBL", FALSE, 0,0x0000ffff,  TRUE),
-  HOWTO(R_390_PC32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC32DBL", FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_PLT32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT32DBL", FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_GOTPCDBL,  1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTPCDBL", FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_GOTENT,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTENT",   FALSE, 0,0xffffffff,  TRUE),
+  HOWTO(R_390_8,         0, 0,  8, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_8",        FALSE, 0,0x000000ff, FALSE),
+  HOWTO(R_390_12,        0, 1, 12, FALSE, 0, complain_overflow_dont,
+	bfd_elf_generic_reloc, "R_390_12",       FALSE, 0,0x00000fff, FALSE),
+  HOWTO(R_390_16,        0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_16",       FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_32,        0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_32",       FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_PC32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC32",     FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_GOT12,	 0, 1, 12, FALSE, 0, complain_overflow_dont,
+	bfd_elf_generic_reloc, "R_390_GOT12",    FALSE, 0,0x00000fff, FALSE),
+  HOWTO(R_390_GOT32,	 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOT32",    FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_PLT32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT32",    FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_COPY,      0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_COPY",     FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_GLOB_DAT,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GLOB_DAT", FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_JMP_SLOT,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_JMP_SLOT", FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_RELATIVE,  0, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_RELATIVE", FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_GOTOFF32,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTOFF32", FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_GOTPC,     0, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPC",    FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_GOT16,     0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOT16",    FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_PC16,      0, 1, 16,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC16",     FALSE, 0,0x0000ffff, TRUE),
+  HOWTO(R_390_PC16DBL,   1, 1, 16,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC16DBL",  FALSE, 0,0x0000ffff, TRUE),
+  HOWTO(R_390_PLT16DBL,  1, 1, 16,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT16DBL", FALSE, 0,0x0000ffff, TRUE),
+  HOWTO(R_390_PC32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC32DBL",  FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_PLT32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT32DBL", FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_GOTPCDBL,  1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPCDBL", FALSE, 0,0xffffffff, TRUE),
+  EMPTY_HOWTO (R_390_64),	/* Empty entry for R_390_64.  */
+  EMPTY_HOWTO (R_390_PC64),	/* Empty entry for R_390_PC64.  */
+  EMPTY_HOWTO (R_390_GOT64),	/* Empty entry for R_390_GOT64.  */
+  EMPTY_HOWTO (R_390_PLT64),	/* Empty entry for R_390_PLT64.  */
+  HOWTO(R_390_GOTENT,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTENT",   FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_GOTOFF16,  0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTOFF16", FALSE, 0,0x0000ffff, FALSE),
+  EMPTY_HOWTO (R_390_GOTOFF64),	/* Empty entry for R_390_GOTOFF64.  */
+  HOWTO(R_390_GOTPLT12,  0, 1, 12, FALSE, 0, complain_overflow_dont,
+	bfd_elf_generic_reloc, "R_390_GOTPLT12", FALSE, 0,0x00000fff, FALSE),
+  HOWTO(R_390_GOTPLT16,  0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLT16", FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_GOTPLT32,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLT32", FALSE, 0,0xffffffff, FALSE),
+  EMPTY_HOWTO (R_390_GOTPLT64),	/* Empty entry for R_390_GOTPLT64.  */
+  HOWTO(R_390_GOTPLTENT, 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLTENT",FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_PLTOFF16,  0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLTOFF16", FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_PLTOFF32,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLTOFF32", FALSE, 0,0xffffffff, FALSE),
+  EMPTY_HOWTO (R_390_PLTOFF64),	/* Empty entry for R_390_PLTOFF64.  */
 };
 
 /* GNU extension to record C++ vtable hierarchy.  */
@@ -160,7 +206,7 @@
     case BFD_RELOC_390_RELATIVE:
       return &elf_howto_table[(int) R_390_RELATIVE];
     case BFD_RELOC_32_GOTOFF:
-      return &elf_howto_table[(int) R_390_GOTOFF];
+      return &elf_howto_table[(int) R_390_GOTOFF32];
     case BFD_RELOC_390_GOTPC:
       return &elf_howto_table[(int) R_390_GOTPC];
     case BFD_RELOC_390_GOT16:
@@ -179,6 +225,20 @@
       return &elf_howto_table[(int) R_390_GOTPCDBL];
     case BFD_RELOC_390_GOTENT:
       return &elf_howto_table[(int) R_390_GOTENT];
+    case BFD_RELOC_16_GOTOFF:
+      return &elf_howto_table[(int) R_390_GOTOFF16];
+    case BFD_RELOC_390_GOTPLT12:
+      return &elf_howto_table[(int) R_390_GOTPLT12];
+    case BFD_RELOC_390_GOTPLT16:
+      return &elf_howto_table[(int) R_390_GOTPLT16];
+    case BFD_RELOC_390_GOTPLT32:
+      return &elf_howto_table[(int) R_390_GOTPLT32];
+    case BFD_RELOC_390_GOTPLTENT:
+      return &elf_howto_table[(int) R_390_GOTPLTENT];
+    case BFD_RELOC_390_PLTOFF16:
+      return &elf_howto_table[(int) R_390_PLTOFF16];
+    case BFD_RELOC_390_PLTOFF32:
+      return &elf_howto_table[(int) R_390_PLTOFF32];
     case BFD_RELOC_VTABLE_INHERIT:
       return &elf32_s390_vtinherit_howto;
     case BFD_RELOC_VTABLE_ENTRY:
@@ -413,6 +473,9 @@
 
   /* Track dynamic relocs copied for this symbol.  */
   struct elf_s390_dyn_relocs *dyn_relocs;
+
+  /* Number of GOTPLT references for a function.  */
+  bfd_signed_vma gotplt_refcount;
 };
 
 /* s390 ELF linker hash table.  */
@@ -465,6 +528,7 @@
 
       eh = (struct elf_s390_link_hash_entry *) entry;
       eh->dyn_relocs = NULL;
+      eh->gotplt_refcount = 0;
     }
 
   return entry;
@@ -628,6 +692,7 @@
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
+  bfd_signed_vma *local_got_refcounts;
 
   if (info->relocateable)
     return TRUE;
@@ -635,6 +700,7 @@
   htab = elf_s390_hash_table (info);
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
+  local_got_refcounts = elf_local_got_refcounts (abfd);
 
   sreloc = NULL;
 
@@ -659,40 +725,34 @@
       else
 	h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
+      /* Create got section and local_got_refcounts array if they
+	 are needed.  */
       switch (ELF32_R_TYPE (rel->r_info))
 	{
 	case R_390_GOT12:
-        case R_390_GOT16:
+	case R_390_GOT16:
 	case R_390_GOT32:
 	case R_390_GOTENT:
-	  /* This symbol requires a global offset table entry.  */
-	  if (h != NULL)
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLTENT:
+	  if (h == NULL
+	      && local_got_refcounts == NULL)
 	    {
-	      h->got.refcount += 1;
-	    }
-	  else
-	    {
-	      bfd_signed_vma *local_got_refcounts;
-
-     	      /* This is a global offset table entry for a local symbol.  */
-	      local_got_refcounts = elf_local_got_refcounts (abfd);
+	      bfd_size_type size;
+	      
+	      size = symtab_hdr->sh_info;
+	      size *= sizeof (bfd_signed_vma);
+	      local_got_refcounts = ((bfd_signed_vma *)
+				     bfd_zalloc (abfd, size));
 	      if (local_got_refcounts == NULL)
-		{
-		  bfd_size_type size;
-
-		  size = symtab_hdr->sh_info;
-		  size *= sizeof (bfd_signed_vma);
-		  local_got_refcounts = ((bfd_signed_vma *)
-					 bfd_zalloc (abfd, size));
-		  if (local_got_refcounts == NULL)
-		    return FALSE;
-		  elf_local_got_refcounts (abfd) = local_got_refcounts;
-		}
-	      local_got_refcounts[r_symndx] += 1;
+		return FALSE;
+	      elf_local_got_refcounts (abfd) = local_got_refcounts;
 	    }
-	  /* Fall through */
-
-	case R_390_GOTOFF:
+	  /* Fall through.  */
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
 	case R_390_GOTPC:
 	case R_390_GOTPCDBL:
 	  if (htab->sgot == NULL)
@@ -702,11 +762,33 @@
 	      if (!create_got_section (htab->elf.dynobj, info))
 		return FALSE;
 	    }
+	}
+
+      switch (ELF32_R_TYPE (rel->r_info))
+	{
+	case R_390_GOT12:
+        case R_390_GOT16:
+	case R_390_GOT32:
+	case R_390_GOTENT:
+	  /* This symbol requires a global offset table entry.  */
+	  if (h != NULL)
+	    h->got.refcount += 1;
+	  else
+	    local_got_refcounts[r_symndx] += 1;
+	  break;
+
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
+	case R_390_GOTPC:
+	case R_390_GOTPCDBL:
+	  /* Got is created, nothing to be done.  */
 	  break;
 
         case R_390_PLT16DBL:
         case R_390_PLT32DBL:
 	case R_390_PLT32:
+	case R_390_PLTOFF16:
+	case R_390_PLTOFF32:
 	  /* This symbol requires a procedure linkage table entry.  We
              actually build the entry in adjust_dynamic_symbol,
              because this might be a case of linking PIC code which is
@@ -716,11 +798,33 @@
 
 	  /* If this is a local symbol, we resolve it directly without
              creating a procedure linkage table entry.  */
-	  if (h == NULL)
-	    continue;
+	  if (h != NULL)
+	    {
+	      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+	      h->plt.refcount += 1;
+	    }
+	  break;
 
-	  h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-	  h->plt.refcount += 1;
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLTENT:
+	  /* This symbol requires either a procedure linkage table entry
+	     or an entry in the local got. We actually build the entry
+	     in adjust_dynamic_symbol because whether this is really a
+	     global reference can change and with it the fact if we have
+	     to create a plt entry or a local got entry. To be able to
+	     make a once global symbol a local one we have to keep track
+	     of the number of gotplt references that exist for this
+	     symbol.  */
+	  if (h != NULL)
+	    {
+	      ((struct elf_s390_link_hash_entry *) h)->gotplt_refcount++;
+	      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+	      h->plt.refcount += 1;
+	    }
+	  else
+	    local_got_refcounts[r_symndx] += 1;
 	  break;
 
         case R_390_8:
@@ -965,86 +1069,133 @@
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
-    switch (ELF32_R_TYPE (rel->r_info))
-      {
-      case R_390_GOT12:
-      case R_390_GOT16:
-      case R_390_GOT32:
-      case R_390_GOTOFF:
-      case R_390_GOTPC:
-      case R_390_GOTPCDBL:
-      case R_390_GOTENT:
-	r_symndx = ELF32_R_SYM (rel->r_info);
-	if (r_symndx >= symtab_hdr->sh_info)
-	  {
-	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	    if (h->got.refcount > 0)
-	      h->got.refcount -= 1;
-	  }
-	else if (local_got_refcounts != NULL)
-	  {
-	    if (local_got_refcounts[r_symndx] > 0)
-	      local_got_refcounts[r_symndx] -= 1;
-	  }
-	break;
-
-      case R_390_8:
-      case R_390_12:
-      case R_390_16:
-      case R_390_32:
-      case R_390_PC16:
-      case R_390_PC16DBL:
-      case R_390_PC32DBL:
-      case R_390_PC32:
-	r_symndx = ELF32_R_SYM (rel->r_info);
-	if (r_symndx >= symtab_hdr->sh_info)
-	  {
-	    struct elf_s390_link_hash_entry *eh;
-	    struct elf_s390_dyn_relocs **pp;
-	    struct elf_s390_dyn_relocs *p;
-
-	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+    {
+      r_symndx = ELF32_R_SYM (rel->r_info);
 
-	    if (!info->shared && h->plt.refcount > 0)
-	      h->plt.refcount -= 1;
+      if (r_symndx < symtab_hdr->sh_info)
+	h = NULL;
+      else
+	h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
-	    eh = (struct elf_s390_link_hash_entry *) h;
+      switch (ELF32_R_TYPE (rel->r_info))
+	{
+	case R_390_GOT12:
+	case R_390_GOT16:
+	case R_390_GOT32:
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
+	case R_390_GOTPC:
+	case R_390_GOTPCDBL:
+	case R_390_GOTENT:
+	  if (h != NULL)
+	    {
+	      if (h->got.refcount > 0)
+		h->got.refcount -= 1;
+	    }
+	  else if (local_got_refcounts != NULL)
+	    {
+	      if (local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx] -= 1;
+	    }
+	  break;
+	  
+	case R_390_PLT16DBL:
+	case R_390_PLT32DBL:
+	case R_390_PLT32:
+	case R_390_PLTOFF16:
+	case R_390_PLTOFF32:
+	  if (h != NULL)
+	    {
+	      if (h->plt.refcount > 0)
+		h->plt.refcount -= 1;
+	    }
+	  break;
 
-	    for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
-	      if (p->sec == sec)
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLTENT:
+	  if (h != NULL)
+	    {
+	      if (h->plt.refcount > 0)
 		{
-		  if (ELF32_R_TYPE (rel->r_info) == R_390_PC16
-		      || ELF32_R_TYPE (rel->r_info) == R_390_PC16DBL
-		      || ELF32_R_TYPE (rel->r_info) == R_390_PC32DBL
-		      || ELF32_R_TYPE (rel->r_info) == R_390_PC32)
-		    p->pc_count -= 1;
-		  p->count -= 1;
-		  if (p->count == 0)
-		    *pp = p->next;
-		  break;
+		  ((struct elf_s390_link_hash_entry *) h)->gotplt_refcount--;
+		  h->plt.refcount -= 1;
 		}
-	  }
-	break;
-
-      case R_390_PLT16DBL:
-      case R_390_PLT32DBL:
-      case R_390_PLT32:
-	r_symndx = ELF32_R_SYM (rel->r_info);
-	if (r_symndx >= symtab_hdr->sh_info)
-	  {
-	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	    if (h->plt.refcount > 0)
-	      h->plt.refcount -= 1;
-	  }
-	break;
+	    }
+	  else if (local_got_refcounts != NULL)
+	    {
+	      if (local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx] -= 1;
+	    }
+	  break;
 
-      default:
-	break;
-      }
+	case R_390_8:
+	case R_390_12:
+	case R_390_16:
+	case R_390_32:
+	case R_390_PC16:
+	case R_390_PC16DBL:
+	case R_390_PC32DBL:
+	case R_390_PC32:
+	  if (h != NULL)
+	    {
+	      struct elf_s390_link_hash_entry *eh;
+	      struct elf_s390_dyn_relocs **pp;
+	      struct elf_s390_dyn_relocs *p;
+	      
+	      if (!info->shared && h->plt.refcount > 0)
+		h->plt.refcount -= 1;
+	      
+	      eh = (struct elf_s390_link_hash_entry *) h;
+	      
+	      for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+		if (p->sec == sec)
+		  {
+		    if (ELF32_R_TYPE (rel->r_info) == R_390_PC16
+			|| ELF32_R_TYPE (rel->r_info) == R_390_PC16DBL
+			|| ELF32_R_TYPE (rel->r_info) == R_390_PC32DBL
+			|| ELF32_R_TYPE (rel->r_info) == R_390_PC32)
+		      p->pc_count -= 1;
+		    p->count -= 1;
+		    if (p->count == 0)
+		      *pp = p->next;
+		    break;
+		  }
+	    }
+	  break;
+	  
+	default:
+	  break;
+	}
+    }
 
   return TRUE;
 }
 
+/* Make sure we emit a GOT entry if the symbol was supposed to have a PLT
+   entry but we found we will not create any.  Called when we find we will
+   not have any PLT for this symbol, by for example
+   elf_s390_adjust_dynamic_symbol when we're doing a proper dynamic link,
+   or elf_s390_size_dynamic_sections if no dynamic sections will be
+   created (we're only linking static objects).  */
+
+static void
+elf_s390_adjust_gotplt (h)
+     struct elf_s390_link_hash_entry *h;
+{
+  if (h->elf.root.type == bfd_link_hash_warning)
+    h = (struct elf_s390_link_hash_entry *) h->elf.root.u.i.link;
+
+  if (h->gotplt_refcount <= 0)
+    return;
+
+  /* We simply add the number of gotplt references to the number
+   * of got references for this symbol.  */
+  h->elf.got.refcount += h->gotplt_refcount;
+  h->gotplt_refcount = -1;
+}
+
 /* Adjust a symbol defined by a dynamic object and referenced by a
    regular object.  The current definition is in some section of the
    dynamic object, but we're not including those sections.  We have to
@@ -1082,6 +1233,7 @@
 	     linkage table, and we can just do a PC32 reloc instead.  */
 	  h->plt.offset = (bfd_vma) -1;
 	  h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+	  elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
 	}
 
       return TRUE;
@@ -1218,6 +1370,9 @@
     return TRUE;
 
   if (h->root.type == bfd_link_hash_warning)
+    /* When warning symbols are created, they **replace** the "real"
+       entry in the hash table, thus we never get to see the real
+       symbol in a hash traversal.  So look at it now.  */
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   info = (struct bfd_link_info *) inf;
@@ -1272,12 +1427,14 @@
 	{
  	  h->plt.offset = (bfd_vma) -1;
 	  h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+	  elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
 	}
     }
   else
     {
       h->plt.offset = (bfd_vma) -1;
       h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+      elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
     }
 
   if (h->got.refcount > 0)
@@ -1644,7 +1801,7 @@
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      int r_type;
+      unsigned int r_type;
       reloc_howto_type *howto;
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
@@ -1659,7 +1816,7 @@
       if (r_type == (int) R_390_GNU_VTINHERIT
           || r_type == (int) R_390_GNU_VTENTRY)
         continue;
-      if (r_type < 0 || r_type >= (int) R_390_max)
+      if (r_type >= (int) R_390_max)
 	{
 	  bfd_set_error (bfd_error_bad_value);
 	  return FALSE;
@@ -1667,6 +1824,8 @@
 
       howto = elf_howto_table + r_type;
       r_symndx = ELF32_R_SYM (rel->r_info);
+
+      /* This is a final link.  */
       h = NULL;
       sym = NULL;
       sec = NULL;
@@ -1723,6 +1882,39 @@
 
       switch (r_type)
 	{
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLTENT:
+	  /* There are three cases for a GOTPLT relocation. 1) The
+	     relocation is against the jump slot entry of a plt that
+	     will get emitted to the output file. 2) The relocation
+	     is against the jump slot of a plt entry that has been
+	     removed. elf_s390_adjust_gotplt has created a GOT entry
+	     as replacement. 3) The relocation is against a local symbol.
+	     Cases 2) and 3) are the same as the GOT relocation code
+	     so we just have to test for case 1 and fall through for
+	     the other two.  */
+	  if (h != NULL && h->plt.offset != (bfd_vma) -1)
+	    {
+	      bfd_vma plt_index;
+
+	      /* Calc. index no.
+		 Current offset - size first entry / entry size.  */
+	      plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
+		PLT_ENTRY_SIZE;
+	      
+	      /* Offset in GOT is PLT index plus GOT headers(3) times 4,
+		 addr & GOT addr.  */
+	      relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+	      unresolved_reloc = FALSE;
+
+	      if (r_type == R_390_GOTPLTENT)
+		relocation += htab->sgot->output_section->vma;
+	      break;
+	    }
+	  /* Fall through.  */
+
         case R_390_GOT12:
         case R_390_GOT16:
         case R_390_GOT32:
@@ -1821,12 +2013,14 @@
 	   * between the start of the GOT and the symbols entry. We
 	   * add the vma of the GOT to get the correct value.
 	   */
-	  if (r_type == R_390_GOTENT)
+	  if (   r_type == R_390_GOTENT
+	      || r_type == R_390_GOTPLTENT)
 	    relocation += htab->sgot->output_section->vma;
 
           break;
 
-        case R_390_GOTOFF:
+	case R_390_GOTOFF16:
+        case R_390_GOTOFF32:
           /* Relocation is relative to the start of the global offset
              table.  */
 
@@ -1868,6 +2062,28 @@
           relocation = (htab->splt->output_section->vma
                         + htab->splt->output_offset
                         + h->plt.offset);
+	  unresolved_reloc = FALSE;
+          break;
+
+	case R_390_PLTOFF16:
+	case R_390_PLTOFF32:
+          /* Relocation is to the entry for this symbol in the
+             procedure linkage table relative to the start of the GOT.  */
+
+	  /* For local symbols or if we didn't make a PLT entry for
+	     this symbol resolve the symbol directly.  */
+          if (   h == NULL
+	      || h->plt.offset == (bfd_vma) -1
+	      || htab->splt == NULL)
+	    {
+	      relocation -= htab->sgot->output_section->vma;
+	      break;
+	    }
+
+          relocation = (htab->splt->output_section->vma
+                        + htab->splt->output_offset
+                        + h->plt.offset
+			- htab->sgot->output_section->vma);
 	  unresolved_reloc = FALSE;
           break;
 
diff -urN src/bfd/elf64-s390.c src-s390/bfd/elf64-s390.c
--- src/bfd/elf64-s390.c	Fri Jan 17 16:26:55 2003
+++ src-s390/bfd/elf64-s390.c	Mon Jan 20 12:29:08 2003
@@ -51,6 +51,9 @@
 static bfd_boolean elf_s390_gc_sweep_hook
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
 	   const Elf_Internal_Rela *));
+struct elf_s390_link_hash_entry;
+static void elf_s390_adjust_gotplt
+  PARAMS ((struct elf_s390_link_hash_entry *));
 static bfd_boolean elf_s390_adjust_dynamic_symbol
   PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static bfd_boolean allocate_dynrelocs
@@ -69,7 +72,8 @@
   PARAMS ((const Elf_Internal_Rela *));
 static bfd_boolean elf_s390_finish_dynamic_sections
   PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_object_p PARAMS ((bfd *));
+static bfd_boolean elf_s390_object_p
+  PARAMS ((bfd *));
 
 #include "elf/s390.h"
 
@@ -94,32 +98,78 @@
 	 0,			/* dst_mask */
 	 FALSE),		/* pcrel_offset */
 
-  HOWTO(R_390_8,         0, 0,  8, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_8",       FALSE, 0,0x000000ff, FALSE),
-  HOWTO(R_390_12,        0, 1, 12, FALSE, 0, complain_overflow_dont, bfd_elf_generic_reloc, "R_390_12",      FALSE, 0,0x00000fff, FALSE),
-  HOWTO(R_390_16,        0, 1, 16, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_16",      FALSE, 0,0x0000ffff, FALSE),
-  HOWTO(R_390_32,        0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_32",      FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_PC32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC32",    FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_GOT12,	 0, 1, 12, FALSE, 0, complain_overflow_dont, bfd_elf_generic_reloc, "R_390_GOT12",   FALSE, 0,0x00000fff, FALSE),
-  HOWTO(R_390_GOT32,	 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT32",   FALSE, 0,0xffffffff, FALSE),
-  HOWTO(R_390_PLT32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT32",   FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_COPY,      0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_COPY",    FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_GLOB_DAT,  0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GLOB_DAT",FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_JMP_SLOT,  0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_JMP_SLOT",FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_RELATIVE,  0, 4, 64,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_RELATIVE",FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_GOTOFF,    0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTOFF",  FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_GOTPC,     0, 4, 64,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTPC",   FALSE, 0,MINUS_ONE,  TRUE),
-  HOWTO(R_390_GOT16,     0, 1, 16, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT16",   FALSE, 0,0x0000ffff, FALSE),
-  HOWTO(R_390_PC16,      0, 1, 16,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC16",    FALSE, 0,0x0000ffff,  TRUE),
-  HOWTO(R_390_PC16DBL,   1, 1, 16,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC16DBL", FALSE, 0,0x0000ffff,  TRUE),
-  HOWTO(R_390_PLT16DBL,  1, 1, 16,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT16DBL", FALSE, 0,0x0000ffff,  TRUE),
-  HOWTO(R_390_PC32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC32DBL", FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_PLT32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT32DBL", FALSE, 0,0xffffffff,  TRUE),
-  HOWTO(R_390_GOTPCDBL,  1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTPCDBL", FALSE, 0,MINUS_ONE,  TRUE),
-  HOWTO(R_390_64,        0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_64",      FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_PC64,	 0, 4, 64,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC64",    FALSE, 0,MINUS_ONE,  TRUE),
-  HOWTO(R_390_GOT64,	 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT64",   FALSE, 0,MINUS_ONE, FALSE),
-  HOWTO(R_390_PLT64,	 0, 4, 64,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT64",   FALSE, 0,MINUS_ONE,  TRUE),
-  HOWTO(R_390_GOTENT,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTENT",   FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_8,         0, 0,  8, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_8",        FALSE, 0,0x000000ff, FALSE),
+  HOWTO(R_390_12,        0, 1, 12, FALSE, 0, complain_overflow_dont,
+	bfd_elf_generic_reloc, "R_390_12",       FALSE, 0,0x00000fff, FALSE),
+  HOWTO(R_390_16,        0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_16",       FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_32,        0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_32",       FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_PC32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC32",     FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_GOT12,	 0, 1, 12, FALSE, 0, complain_overflow_dont,
+	bfd_elf_generic_reloc, "R_390_GOT12",    FALSE, 0,0x00000fff, FALSE),
+  HOWTO(R_390_GOT32,	 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOT32",    FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_PLT32,	 0, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT32",    FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_COPY,      0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_COPY",     FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_GLOB_DAT,  0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GLOB_DAT", FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_JMP_SLOT,  0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_JMP_SLOT", FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_RELATIVE,  0, 4, 64,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_RELATIVE", FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_GOTOFF32,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTOFF32", FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_GOTPC,     0, 4, 64,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPC",    FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_GOT16,     0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOT16",    FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_PC16,      0, 1, 16,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC16",     FALSE, 0,0x0000ffff, TRUE),
+  HOWTO(R_390_PC16DBL,   1, 1, 16,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC16DBL",  FALSE, 0,0x0000ffff, TRUE),
+  HOWTO(R_390_PLT16DBL,  1, 1, 16,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT16DBL", FALSE, 0,0x0000ffff, TRUE),
+  HOWTO(R_390_PC32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC32DBL",  FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_PLT32DBL,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT32DBL", FALSE, 0,0xffffffff, TRUE),
+  HOWTO(R_390_GOTPCDBL,  1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPCDBL", FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_64,        0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_64",       FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_PC64,	 0, 4, 64,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PC64",     FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_GOT64,	 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOT64",    FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_PLT64,	 0, 4, 64,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLT64",    FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_GOTENT,	 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTENT",   FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_GOTOFF16,  0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTOFF16", FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_GOTOFF64,  0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTOFF64", FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_GOTPLT12,	 0, 1, 12, FALSE, 0, complain_overflow_dont,
+	bfd_elf_generic_reloc, "R_390_GOTPLT12", FALSE, 0,0x00000fff, FALSE),
+  HOWTO(R_390_GOTPLT16,  0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLT16", FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_GOTPLT32,	 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLT32", FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_GOTPLT64,	 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLT64", FALSE, 0,MINUS_ONE,  FALSE),
+  HOWTO(R_390_GOTPLTENT, 1, 2, 32,  TRUE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_GOTPLTENT",FALSE, 0,MINUS_ONE,  TRUE),
+  HOWTO(R_390_PLTOFF16,  0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLTOFF16", FALSE, 0,0x0000ffff, FALSE),
+  HOWTO(R_390_PLTOFF32,  0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLTOFF32", FALSE, 0,0xffffffff, FALSE),
+  HOWTO(R_390_PLTOFF64,  0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+	bfd_elf_generic_reloc, "R_390_PLTOFF64", FALSE, 0,MINUS_ONE,  FALSE),
 };
 
 /* GNU extension to record C++ vtable hierarchy.  */
@@ -164,7 +214,7 @@
     case BFD_RELOC_390_RELATIVE:
       return &elf_howto_table[(int) R_390_RELATIVE];
     case BFD_RELOC_32_GOTOFF:
-      return &elf_howto_table[(int) R_390_GOTOFF];
+      return &elf_howto_table[(int) R_390_GOTOFF32];
     case BFD_RELOC_390_GOTPC:
       return &elf_howto_table[(int) R_390_GOTPC];
     case BFD_RELOC_390_GOT16:
@@ -175,10 +225,6 @@
       return &elf_howto_table[(int) R_390_PC16DBL];
     case BFD_RELOC_390_PLT16DBL:
       return &elf_howto_table[(int) R_390_PLT16DBL];
-    case BFD_RELOC_VTABLE_INHERIT:
-      return &elf64_s390_vtinherit_howto;
-    case BFD_RELOC_VTABLE_ENTRY:
-      return &elf64_s390_vtentry_howto;
     case BFD_RELOC_390_PC32DBL:
       return &elf_howto_table[(int) R_390_PC32DBL];
     case BFD_RELOC_390_PLT32DBL:
@@ -195,6 +241,30 @@
       return &elf_howto_table[(int) R_390_PLT64];
     case BFD_RELOC_390_GOTENT:
       return &elf_howto_table[(int) R_390_GOTENT];
+    case BFD_RELOC_16_GOTOFF:
+      return &elf_howto_table[(int) R_390_GOTOFF16];
+    case BFD_RELOC_390_GOTOFF64:
+      return &elf_howto_table[(int) R_390_GOTOFF64];
+    case BFD_RELOC_390_GOTPLT12:
+      return &elf_howto_table[(int) R_390_GOTPLT12];
+    case BFD_RELOC_390_GOTPLT16:
+      return &elf_howto_table[(int) R_390_GOTPLT16];
+    case BFD_RELOC_390_GOTPLT32:
+      return &elf_howto_table[(int) R_390_GOTPLT32];
+    case BFD_RELOC_390_GOTPLT64:
+      return &elf_howto_table[(int) R_390_GOTPLT64];
+    case BFD_RELOC_390_GOTPLTENT:
+      return &elf_howto_table[(int) R_390_GOTPLTENT];
+    case BFD_RELOC_390_PLTOFF16:
+      return &elf_howto_table[(int) R_390_PLTOFF16];
+    case BFD_RELOC_390_PLTOFF32:
+      return &elf_howto_table[(int) R_390_PLTOFF32];
+    case BFD_RELOC_390_PLTOFF64:
+      return &elf_howto_table[(int) R_390_PLTOFF64];
+    case BFD_RELOC_VTABLE_INHERIT:
+      return &elf64_s390_vtinherit_howto;
+    case BFD_RELOC_VTABLE_ENTRY:
+      return &elf64_s390_vtentry_howto;
     default:
       break;
     }
@@ -357,6 +427,9 @@
 
   /* Track dynamic relocs copied for this symbol.  */
   struct elf_s390_dyn_relocs *dyn_relocs;
+
+  /* Number of GOTPLT references for a function.  */
+  bfd_signed_vma gotplt_refcount;
 };
 
 /* s390 ELF linker hash table.  */
@@ -409,6 +482,7 @@
 
       eh = (struct elf_s390_link_hash_entry *) entry;
       eh->dyn_relocs = NULL;
+      eh->gotplt_refcount = 0;
     }
 
   return entry;
@@ -572,6 +646,7 @@
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
+  bfd_signed_vma *local_got_refcounts;
 
   if (info->relocateable)
     return TRUE;
@@ -579,6 +654,7 @@
   htab = elf_s390_hash_table (info);
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
+  local_got_refcounts = elf_local_got_refcounts (abfd);
 
   sreloc = NULL;
 
@@ -603,6 +679,8 @@
       else
 	h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
+      /* Create got section and local_got_refcounts array if they
+	 are needed.  */
       switch (ELF64_R_TYPE (rel->r_info))
 	{
 	case R_390_GOT12:
@@ -610,34 +688,28 @@
 	case R_390_GOT32:
 	case R_390_GOT64:
 	case R_390_GOTENT:
-	  /* This symbol requires a global offset table entry.  */
-	  if (h != NULL)
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLT64:
+	case R_390_GOTPLTENT:
+	  if (h == NULL
+	      && local_got_refcounts == NULL)
 	    {
-	      h->got.refcount += 1;
-	    }
-	  else
-	    {
-	      bfd_signed_vma *local_got_refcounts;
+	      bfd_size_type size;
 
-	      /* This is a global offset table entry for a local symbol.  */
-	      local_got_refcounts = elf_local_got_refcounts (abfd);
+	      size = symtab_hdr->sh_info;
+	      size *= sizeof (bfd_signed_vma);
+	      local_got_refcounts = ((bfd_signed_vma *)
+				     bfd_zalloc (abfd, size));
 	      if (local_got_refcounts == NULL)
-		{
-		  bfd_size_type size;
-
-		  size = symtab_hdr->sh_info;
-		  size *= sizeof (bfd_signed_vma);
-		  local_got_refcounts = ((bfd_signed_vma *)
-					 bfd_zalloc (abfd, size));
-		  if (local_got_refcounts == NULL)
-		    return FALSE;
-		  elf_local_got_refcounts (abfd) = local_got_refcounts;
-		}
-	      local_got_refcounts[r_symndx] += 1;
+		return FALSE;
+	      elf_local_got_refcounts (abfd) = local_got_refcounts;
 	    }
-	  /* Fall through */
-
-	case R_390_GOTOFF:
+	  /* Fall through.  */
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
+	case R_390_GOTOFF64:
 	case R_390_GOTPC:
 	case R_390_GOTPCDBL:
 	  if (htab->sgot == NULL)
@@ -647,12 +719,41 @@
 	      if (!create_got_section (htab->elf.dynobj, info))
 		return FALSE;
 	    }
+	}
+
+      switch (ELF64_R_TYPE (rel->r_info))
+	{
+	case R_390_GOT12:
+        case R_390_GOT16:
+	case R_390_GOT32:
+	case R_390_GOT64:
+	case R_390_GOTENT:
+	  /* This symbol requires a global offset table entry.  */
+	  if (h != NULL)
+	    {
+	      h->got.refcount += 1;
+	    }
+	  else
+	    {
+	      local_got_refcounts[r_symndx] += 1;
+	    }
+	  /* Fall through */
+
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
+	case R_390_GOTOFF64:
+	case R_390_GOTPC:
+	case R_390_GOTPCDBL:
+	  /* Got is created, nothing to be done.  */
 	  break;
 
 	case R_390_PLT16DBL:
 	case R_390_PLT32:
 	case R_390_PLT32DBL:
 	case R_390_PLT64:
+	case R_390_PLTOFF16:
+	case R_390_PLTOFF32:
+	case R_390_PLTOFF64:
 	  /* This symbol requires a procedure linkage table entry.  We
 	     actually build the entry in adjust_dynamic_symbol,
 	     because this might be a case of linking PIC code which is
@@ -662,11 +763,36 @@
 
 	  /* If this is a local symbol, we resolve it directly without
 	     creating a procedure linkage table entry.  */
-	  if (h == NULL)
-	    continue;
+	  if (h != NULL)
+	    {
+	      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+	      h->plt.refcount += 1;
+	    }
+	  break;
 
-	  h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-	  h->plt.refcount += 1;
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLT64:
+	case R_390_GOTPLTENT:
+	  /* This symbol requires either a procedure linkage table entry
+	     or an entry in the local got. We actually build the entry
+	     in adjust_dynamic_symbol because whether this is really a
+	     global reference can change and with it the fact if we have
+	     to create a plt entry or a local got entry. To be able to
+	     make a once global symbol a local one we have to keep track
+	     of the number of gotplt references that exist for this
+	     symbol.  */
+	  if (h != NULL)
+	    {
+	      ((struct elf_s390_link_hash_entry *) h)->gotplt_refcount++;
+	      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+	      h->plt.refcount += 1;
+	    }
+	  else
+	    {
+	      local_got_refcounts[r_symndx] += 1;
+	    }
 	  break;
 
 	case R_390_8:
@@ -915,89 +1041,141 @@
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
-    switch (ELF64_R_TYPE (rel->r_info))
-      {
-      case R_390_GOT12:
-      case R_390_GOT16:
-      case R_390_GOT32:
-      case R_390_GOT64:
-      case R_390_GOTOFF:
-      case R_390_GOTPC:
-      case R_390_GOTPCDBL:
-      case R_390_GOTENT:
-	r_symndx = ELF64_R_SYM (rel->r_info);
-	if (r_symndx >= symtab_hdr->sh_info)
-	  {
-	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	    if (h->got.refcount > 0)
-	      h->got.refcount -= 1;
-	  }
-	else if (local_got_refcounts != NULL)
-	  {
-	    if (local_got_refcounts[r_symndx] > 0)
-	      local_got_refcounts[r_symndx] -= 1;
-	  }
-	break;
-
-      case R_390_8:
-      case R_390_12:
-      case R_390_16:
-      case R_390_32:
-      case R_390_64:
-      case R_390_PC16:
-      case R_390_PC16DBL:
-      case R_390_PC32:
-      case R_390_PC32DBL:
-      case R_390_PC64:
-	r_symndx = ELF64_R_SYM (rel->r_info);
-	if (r_symndx >= symtab_hdr->sh_info)
-	  {
-	    struct elf_s390_link_hash_entry *eh;
-	    struct elf_s390_dyn_relocs **pp;
-	    struct elf_s390_dyn_relocs *p;
-
-	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+    {
+      r_symndx = ELF64_R_SYM (rel->r_info);
 
-	    if (!info->shared && h->plt.refcount > 0)
-	      h->plt.refcount -= 1;
+      if (r_symndx < symtab_hdr->sh_info)
+	h = NULL;
+      else
+	h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
-	    eh = (struct elf_s390_link_hash_entry *) h;
+      switch (ELF64_R_TYPE (rel->r_info))
+	{
+	case R_390_GOT12:
+	case R_390_GOT16:
+	case R_390_GOT32:
+	case R_390_GOT64:
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
+	case R_390_GOTOFF64:
+	case R_390_GOTPC:
+	case R_390_GOTPCDBL:
+	case R_390_GOTENT:
+	  if (h != NULL)
+	    {
+	      if (h->got.refcount > 0)
+		h->got.refcount -= 1;
+	    }
+	  else if (local_got_refcounts != NULL)
+	    {
+	      if (local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx] -= 1;
+	    }
+	  break;
+	  
+	case R_390_PLT16DBL:
+	case R_390_PLT32:
+	case R_390_PLT32DBL:
+	case R_390_PLT64:
+	case R_390_PLTOFF16:
+	case R_390_PLTOFF32:
+	case R_390_PLTOFF64:
+	  if (h != NULL)
+	    {
+	      if (h->plt.refcount > 0)
+		h->plt.refcount -= 1;
+	    }
+	  break;
 
-	    for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
-	      if (p->sec == sec)
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLT64:
+	case R_390_GOTPLTENT:
+	  if (h != NULL)
+	    {
+	      if (h->plt.refcount > 0)
 		{
-		  if (ELF64_R_TYPE (rel->r_info) == R_390_PC16
-		      || ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
-		      || ELF64_R_TYPE (rel->r_info) == R_390_PC32)
-		    p->pc_count -= 1;
-		  p->count -= 1;
-		  if (p->count == 0)
-		    *pp = p->next;
-		  break;
+		  ((struct elf_s390_link_hash_entry *) h)->gotplt_refcount--;
+		  h->plt.refcount -= 1;
 		}
-	  }
-	break;
+	    }
+	  else if (local_got_refcounts != NULL)
+	    {
+	      if (local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx] -= 1;
+	    }
+	  break;
 
-      case R_390_PLT16DBL:
-      case R_390_PLT32:
-      case R_390_PLT32DBL:
-      case R_390_PLT64:
-	r_symndx = ELF64_R_SYM (rel->r_info);
-	if (r_symndx >= symtab_hdr->sh_info)
-	  {
-	    h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	    if (h->plt.refcount > 0)
-	      h->plt.refcount -= 1;
-	  }
-	break;
+	case R_390_8:
+	case R_390_12:
+	case R_390_16:
+	case R_390_32:
+	case R_390_64:
+	case R_390_PC16:
+	case R_390_PC16DBL:
+	case R_390_PC32:
+	case R_390_PC32DBL:
+	case R_390_PC64:
+	  if (h != NULL)
+	    {
+	      struct elf_s390_link_hash_entry *eh;
+	      struct elf_s390_dyn_relocs **pp;
+	      struct elf_s390_dyn_relocs *p;
+	      
+	      if (!info->shared && h->plt.refcount > 0)
+		h->plt.refcount -= 1;
+	      
+	      eh = (struct elf_s390_link_hash_entry *) h;
+	      
+	      for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+		if (p->sec == sec)
+		  {
+		    if (ELF64_R_TYPE (rel->r_info) == R_390_PC16
+			|| ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
+			|| ELF64_R_TYPE (rel->r_info) == R_390_PC32
+			|| ELF64_R_TYPE (rel->r_info) == R_390_PC32DBL
+			|| ELF64_R_TYPE (rel->r_info) == R_390_PC64)
+		      p->pc_count -= 1;
+		    p->count -= 1;
+		    if (p->count == 0)
+		      *pp = p->next;
+		    break;
+		  }
+	    }
+	  break;
 
-      default:
-	break;
-      }
+	default:
+	  break;
+	}
+    }
 
   return TRUE;
 }
 
+/* Make sure we emit a GOT entry if the symbol was supposed to have a PLT
+   entry but we found we will not create any.  Called when we find we will
+   not have any PLT for this symbol, by for example
+   elf_s390_adjust_dynamic_symbol when we're doing a proper dynamic link,
+   or elf_s390_size_dynamic_sections if no dynamic sections will be
+   created (we're only linking static objects).  */
+
+static void
+elf_s390_adjust_gotplt (h)
+     struct elf_s390_link_hash_entry *h;
+{
+  if (h->elf.root.type == bfd_link_hash_warning)
+    h = (struct elf_s390_link_hash_entry *) h->elf.root.u.i.link;
+
+  if (h->gotplt_refcount <= 0)
+    return;
+
+  /* We simply add the number of gotplt references to the number
+   * of got references for this symbol.  */
+  h->elf.got.refcount += h->gotplt_refcount;
+  h->gotplt_refcount = -1;
+}
+
 /* Adjust a symbol defined by a dynamic object and referenced by a
    regular object.  The current definition is in some section of the
    dynamic object, but we're not including those sections.  We have to
@@ -1035,6 +1213,7 @@
 	     linkage table, and we can just do a PC32 reloc instead.  */
 	  h->plt.offset = (bfd_vma) -1;
 	  h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+	  elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
 	}
 
       return TRUE;
@@ -1171,6 +1350,9 @@
     return TRUE;
 
   if (h->root.type == bfd_link_hash_warning)
+    /* When warning symbols are created, they **replace** the "real"
+       entry in the hash table, thus we never get to see the real
+       symbol in a hash traversal.  So look at it now.  */
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   info = (struct bfd_link_info *) inf;
@@ -1225,12 +1407,14 @@
 	{
 	  h->plt.offset = (bfd_vma) -1;
 	  h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+	  elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
 	}
     }
   else
     {
       h->plt.offset = (bfd_vma) -1;
       h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+      elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
     }
 
   if (h->got.refcount > 0)
@@ -1597,7 +1781,7 @@
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      int r_type;
+      unsigned int r_type;
       reloc_howto_type *howto;
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
@@ -1612,7 +1796,7 @@
       if (r_type == (int) R_390_GNU_VTINHERIT
 	  || r_type == (int) R_390_GNU_VTENTRY)
 	continue;
-      if (r_type < 0 || r_type >= (int) R_390_max)
+      if (r_type >= (int) R_390_max)
 	{
 	  bfd_set_error (bfd_error_bad_value);
 	  return FALSE;
@@ -1620,6 +1804,8 @@
 
       howto = elf_howto_table + r_type;
       r_symndx = ELF64_R_SYM (rel->r_info);
+
+      /* This is a final link.  */
       h = NULL;
       sym = NULL;
       sec = NULL;
@@ -1676,6 +1862,40 @@
 
       switch (r_type)
 	{
+	case R_390_GOTPLT12:
+	case R_390_GOTPLT16:
+	case R_390_GOTPLT32:
+	case R_390_GOTPLT64:
+	case R_390_GOTPLTENT:
+	  /* There are three cases for a GOTPLT relocation. 1) The
+	     relocation is against the jump slot entry of a plt that
+	     will get emitted to the output file. 2) The relocation
+	     is against the jump slot of a plt entry that has been
+	     removed. elf_s390_adjust_gotplt has created a GOT entry
+	     as replacement. 3) The relocation is against a local symbol.
+	     Cases 2) and 3) are the same as the GOT relocation code
+	     so we just have to test for case 1 and fall through for
+	     the other two.  */
+	  if (h != NULL && h->plt.offset != (bfd_vma) -1)
+	    {
+	      bfd_vma plt_index;
+
+	      /* Calc. index no.
+		 Current offset - size first entry / entry size.  */
+	      plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
+		PLT_ENTRY_SIZE;
+	      
+	      /* Offset in GOT is PLT index plus GOT headers(3) times 4,
+		 addr & GOT addr.  */
+	      relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+	      unresolved_reloc = FALSE;
+
+	      if (r_type == R_390_GOTPLTENT)
+		relocation += htab->sgot->output_section->vma;	    
+	      break;
+	    }
+	  /* Fall through.  */
+
 	case R_390_GOT12:
 	case R_390_GOT16:
 	case R_390_GOT32:
@@ -1775,12 +1995,15 @@
 	   * between the start of the GOT and the symbols entry. We
 	   * add the vma of the GOT to get the correct value.
 	   */
-	  if (r_type == R_390_GOTENT)
+	  if (   r_type == R_390_GOTENT
+	      || r_type == R_390_GOTPLTENT)
 	    relocation += htab->sgot->output_section->vma;
 
 	  break;
 
-	case R_390_GOTOFF:
+	case R_390_GOTOFF16:
+	case R_390_GOTOFF32:
+	case R_390_GOTOFF64:
 	  /* Relocation is relative to the start of the global offset
 	     table.  */
 
@@ -1790,7 +2013,6 @@
 	     permitted by the ABI, we might have to change this
 	     calculation.  */
 	  relocation -= htab->sgot->output_section->vma;
-
 	  break;
 
 	case R_390_GOTPC:
@@ -1824,6 +2046,29 @@
 	  relocation = (htab->splt->output_section->vma
 			+ htab->splt->output_offset
 			+ h->plt.offset);
+	  unresolved_reloc = FALSE;
+          break;
+
+	case R_390_PLTOFF16:
+	case R_390_PLTOFF32:
+	case R_390_PLTOFF64:
+          /* Relocation is to the entry for this symbol in the
+             procedure linkage table relative to the start of the GOT.  */
+
+	  /* For local symbols or if we didn't make a PLT entry for
+	     this symbol resolve the symbol directly.  */
+          if (   h == NULL
+	      || h->plt.offset == (bfd_vma) -1
+	      || htab->splt == NULL)
+	    {
+	      relocation -= htab->sgot->output_section->vma;
+	      break;
+	    }
+
+          relocation = (htab->splt->output_section->vma
+                        + htab->splt->output_offset
+                        + h->plt.offset
+			- htab->sgot->output_section->vma);
 	  unresolved_reloc = FALSE;
 	  break;
 
diff -urN src/bfd/libbfd.h src-s390/bfd/libbfd.h
--- src/bfd/libbfd.h	Fri Jan 17 16:26:55 2003
+++ src-s390/bfd/libbfd.h	Mon Jan 20 12:29:08 2003
@@ -1208,6 +1208,15 @@
   "BFD_RELOC_390_GOT64",
   "BFD_RELOC_390_PLT64",
   "BFD_RELOC_390_GOTENT",
+  "BFD_RELOC_390_GOTOFF64",
+  "BFD_RELOC_390_GOTPLT12",
+  "BFD_RELOC_390_GOTPLT16",
+  "BFD_RELOC_390_GOTPLT32",
+  "BFD_RELOC_390_GOTPLT64",
+  "BFD_RELOC_390_GOTPLTENT",
+  "BFD_RELOC_390_PLTOFF16",
+  "BFD_RELOC_390_PLTOFF32",
+  "BFD_RELOC_390_PLTOFF64",
   "BFD_RELOC_IP2K_FR9",
   "BFD_RELOC_IP2K_BANK",
   "BFD_RELOC_IP2K_ADDR16CJP",
diff -urN src/bfd/reloc.c src-s390/bfd/reloc.c
--- src/bfd/reloc.c	Fri Jan 17 16:26:55 2003
+++ src-s390/bfd/reloc.c	Mon Jan 20 12:29:08 2003
@@ -3169,6 +3169,42 @@
   BFD_RELOC_390_GOTENT
 ENUMDOC
   32 bit rel. offset to GOT entry.
+ENUM
+  BFD_RELOC_390_GOTOFF64
+ENUMDOC
+  64 bit offset to GOT.
+ENUM
+  BFD_RELOC_390_GOTPLT12
+ENUMDOC
+  12-bit offset to symbol-entry within GOT, with PLT handling.
+ENUM
+  BFD_RELOC_390_GOTPLT16
+ENUMDOC
+  16-bit offset to symbol-entry within GOT, with PLT handling.
+ENUM
+  BFD_RELOC_390_GOTPLT32
+ENUMDOC
+  32-bit offset to symbol-entry within GOT, with PLT handling.
+ENUM
+  BFD_RELOC_390_GOTPLT64
+ENUMDOC
+  64-bit offset to symbol-entry within GOT, with PLT handling.
+ENUM
+  BFD_RELOC_390_GOTPLTENT
+ENUMDOC
+  32-bit rel. offset to symbol-entry within GOT, with PLT handling.
+ENUM
+  BFD_RELOC_390_PLTOFF16
+ENUMDOC
+  16-bit rel. offset from the GOT to a PLT entry.
+ENUM
+  BFD_RELOC_390_PLTOFF32
+ENUMDOC
+  32-bit rel. offset from the GOT to a PLT entry.
+ENUM
+  BFD_RELOC_390_PLTOFF64
+ENUMDOC
+  64-bit rel. offset from the GOT to a PLT entry.
 
 ENUM
   BFD_RELOC_IP2K_FR9
diff -urN src/gas/config/tc-s390.c src-s390/gas/config/tc-s390.c
--- src/gas/config/tc-s390.c	Mon Jan 20 11:17:27 2003
+++ src-s390/gas/config/tc-s390.c	Mon Jan 20 12:29:08 2003
@@ -606,7 +606,10 @@
     ELF_SUFFIX_NONE = 0,
     ELF_SUFFIX_GOT,
     ELF_SUFFIX_PLT,
-    ELF_SUFFIX_GOTENT
+    ELF_SUFFIX_GOTENT,
+    ELF_SUFFIX_GOTOFF,
+    ELF_SUFFIX_GOTPLT,
+    ELF_SUFFIX_PLTOFF
   }
 elf_suffix_type;
 
@@ -635,6 +638,9 @@
     { "got12", 5, ELF_SUFFIX_GOT  },
     { "plt", 3, ELF_SUFFIX_PLT  },
     { "gotent", 6, ELF_SUFFIX_GOTENT },
+    { "gotoff", 6, ELF_SUFFIX_GOTOFF },
+    { "gotplt", 6, ELF_SUFFIX_GOTPLT },
+    { "pltoff", 6, ELF_SUFFIX_PLTOFF },
     { NULL,  0, ELF_SUFFIX_NONE }
   };
 
@@ -956,10 +962,26 @@
 	    reloc = BFD_RELOC_32_GOT_PCREL;
 	  else if (nbytes == 8 && suffix == ELF_SUFFIX_GOT)
 	    reloc = BFD_RELOC_390_GOT64;
+	  else if (nbytes == 2 && suffix == ELF_SUFFIX_GOTOFF)
+	    reloc = BFD_RELOC_16_GOTOFF;
+	  else if (nbytes == 4 && suffix == ELF_SUFFIX_GOTOFF)
+	    reloc = BFD_RELOC_32_GOTOFF;
+	  else if (nbytes == 8 && suffix == ELF_SUFFIX_GOTOFF)
+	    reloc = BFD_RELOC_390_GOTOFF64;
+	  else if (nbytes == 2 && suffix == ELF_SUFFIX_PLTOFF)
+	    reloc = BFD_RELOC_390_PLTOFF16;
+	  else if (nbytes == 4 && suffix == ELF_SUFFIX_PLTOFF)
+	    reloc = BFD_RELOC_390_PLTOFF32;
+	  else if (nbytes == 8 && suffix == ELF_SUFFIX_PLTOFF)
+	    reloc = BFD_RELOC_390_PLTOFF64;
 	  else if (nbytes == 4 && suffix == ELF_SUFFIX_PLT)
 	    reloc = BFD_RELOC_390_PLT32;
 	  else if (nbytes == 8 && suffix == ELF_SUFFIX_PLT)
 	    reloc = BFD_RELOC_390_PLT64;
+	  else if (nbytes == 4 && suffix == ELF_SUFFIX_GOTPLT)
+	    reloc = BFD_RELOC_390_GOTPLT32;
+	  else if (nbytes == 8 && suffix == ELF_SUFFIX_GOTPLT)
+	    reloc = BFD_RELOC_390_GOTPLT64;
 	  else
 	    reloc = BFD_RELOC_UNUSED;
 
@@ -1121,6 +1143,30 @@
 		  && (operand->bits == 32))
 		reloc = BFD_RELOC_390_GOTENT;
 	    }
+	  else if (suffix == ELF_SUFFIX_GOTOFF)
+	    {
+	      if ((operand->flags & S390_OPERAND_SIGNED)
+		  && (operand->bits == 16))
+		reloc = BFD_RELOC_16_GOTOFF;
+	    }
+	  else if (suffix == ELF_SUFFIX_PLTOFF)
+	    {
+	      if ((operand->flags & S390_OPERAND_SIGNED)
+		  && (operand->bits == 16))
+		reloc = BFD_RELOC_390_PLTOFF16;
+	    }
+	  else if (suffix == ELF_SUFFIX_GOTPLT)
+	    {
+	      if ((operand->flags & S390_OPERAND_DISP)
+		  && (operand->bits == 12))
+		reloc = BFD_RELOC_390_GOTPLT12;
+	      else if ((operand->flags & S390_OPERAND_SIGNED)
+		       && (operand->bits == 16))
+		reloc = BFD_RELOC_390_GOTPLT16;
+	      else if ((operand->flags & S390_OPERAND_PCREL)
+		       && (operand->bits == 32))
+		reloc = BFD_RELOC_390_GOTPLTENT;
+	    }
 
 	  if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
 	    as_bad (_("invalid operand suffix"));
@@ -1633,7 +1679,12 @@
   if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0)
     return 0;
   /* adjust_reloc_syms doesn't know about the GOT.  */
-  if (   fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+  if (   fixP->fx_r_type == BFD_RELOC_16_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_390_GOTOFF64
+      || fixP->fx_r_type == BFD_RELOC_390_PLTOFF16
+      || fixP->fx_r_type == BFD_RELOC_390_PLTOFF32
+      || fixP->fx_r_type == BFD_RELOC_390_PLTOFF64
       || fixP->fx_r_type == BFD_RELOC_390_PLT16DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT32
       || fixP->fx_r_type == BFD_RELOC_390_PLT32DBL
@@ -1643,6 +1694,11 @@
       || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
       || fixP->fx_r_type == BFD_RELOC_390_GOT64
       || fixP->fx_r_type == BFD_RELOC_390_GOTENT
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT12
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT16
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT32
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT64
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLTENT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 0;
@@ -1662,6 +1718,10 @@
     case BFD_RELOC_390_GOT12:
     case BFD_RELOC_32_GOT_PCREL:
     case BFD_RELOC_32_GOTOFF:
+    case BFD_RELOC_390_GOTOFF64:
+    case BFD_RELOC_390_PLTOFF16:
+    case BFD_RELOC_390_PLTOFF32:
+    case BFD_RELOC_390_PLTOFF64:
     case BFD_RELOC_390_GOTPC:
     case BFD_RELOC_390_GOT16:
     case BFD_RELOC_390_GOTPCDBL:
@@ -1671,6 +1731,11 @@
     case BFD_RELOC_390_PLT16DBL:
     case BFD_RELOC_390_PLT32DBL:
     case BFD_RELOC_390_PLT64:
+    case BFD_RELOC_390_GOTPLT12:
+    case BFD_RELOC_390_GOTPLT16:
+    case BFD_RELOC_390_GOTPLT32:
+    case BFD_RELOC_390_GOTPLT64:
+    case BFD_RELOC_390_GOTPLTENT:
     case BFD_RELOC_VTABLE_INHERIT:
     case BFD_RELOC_VTABLE_ENTRY:
       return 1;
@@ -1802,6 +1867,7 @@
 	  break;
 	case BFD_RELOC_390_12:
 	case BFD_RELOC_390_GOT12:
+	case BFD_RELOC_390_GOTPLT12:
 	  if (fixP->fx_done)
 	    {
 	      unsigned short mop;
@@ -1828,6 +1894,8 @@
 	    md_number_to_chars (where, value, 2);
 	  break;
 	case BFD_RELOC_390_GOT16:
+	case BFD_RELOC_390_PLTOFF16:
+	case BFD_RELOC_390_GOTPLT16:
 	  if (fixP->fx_done)
 	    md_number_to_chars (where, value, 2);
 	  break;
@@ -1853,7 +1921,9 @@
 	    md_number_to_chars (where, value, 4);
 	  break;
 	case BFD_RELOC_32_GOT_PCREL:
+	case BFD_RELOC_390_PLTOFF32:
 	case BFD_RELOC_390_PLT32:
+	case BFD_RELOC_390_GOTPLT32:
 	  if (fixP->fx_done)
 	    md_number_to_chars (where, value, 4);
 	  break;
@@ -1861,6 +1931,7 @@
 	case BFD_RELOC_390_PLT32DBL:
 	case BFD_RELOC_390_GOTPCDBL:
 	case BFD_RELOC_390_GOTENT:
+	case BFD_RELOC_390_GOTPLTENT:
 	  value += 2;
 	  if (fixP->fx_done)
 	    md_number_to_chars (where, (offsetT) value >> 1, 4);
@@ -1871,8 +1942,15 @@
 	    md_number_to_chars (where, value, sizeof (int));
 	  break;
 
+	case BFD_RELOC_390_GOTOFF64:
+	  if (fixP->fx_done)
+	    md_number_to_chars (where, value, 8);
+	  break;
+
 	case BFD_RELOC_390_GOT64:
+	case BFD_RELOC_390_PLTOFF64:
 	case BFD_RELOC_390_PLT64:
+	case BFD_RELOC_390_GOTPLT64:
 	  if (fixP->fx_done)
 	    md_number_to_chars (where, value, 8);
 	  break;
diff -urN src/gas/testsuite/gas/s390/reloc.d src-s390/gas/testsuite/gas/s390/reloc.d
--- src/gas/testsuite/gas/s390/reloc.d	Fri Jan 17 16:26:55 2003
+++ src-s390/gas/testsuite/gas/s390/reloc.d	Mon Jan 20 12:29:08 2003
@@ -28,4 +28,18 @@
 [ 	]*2c: R_390_PC16DBL	test_R_390_PC16DBL\+0x2
   2e:	a7 e5 00 00 [ 	]*bras	%r14,2e <foo\+0x2e>
 [ 	]*30: R_390_PC16DBL	test_R_390_PLT16DBL\+0x2
-  32:	07 07 [ 	]*bcr	0,%r7
+  32:	a7 08 00 00 [ 	]*lhi	%r0,0
+[ 	]*34: R_390_GOTOFF16	test_R_390_GOTOFF16
+  36:	00 00 00 00 [ 	]*.long	0x00000000
+[ 	]*36: R_390_GOTOFF32	test_R_390_GOTOFF32
+  3a:	a7 08 00 00 [ 	]*lhi	%r0,0
+[ 	]*3c: R_390_PLTOFF16	test_R_390_PLTOFF16
+  3e:	00 00 00 00 [ 	]*.long	0x00000000
+[ 	]*3e: R_390_PLTOFF32	test_R_390_PLTOFF32
+  42:	58 01 20 00 [ 	]*l	%r0,0\(%r1,%r2\)
+[ 	]*44: R_390_GOTPLT12	test_R_390_GOTPLT12
+  46:	a7 08 00 00 [ 	]*lhi	%r0,0
+[ 	]*48: R_390_GOTPLT16	test_R_390_GOTPLT16
+  4a:	00 00 00 00 [ 	]*.long	0x00000000
+[ 	]*4a: R_390_GOTPLT32	test_R_390_GOTPLT32
+  4e:	07 07 [ 	]*bcr	0,%r7
diff -urN src/gas/testsuite/gas/s390/reloc.s src-s390/gas/testsuite/gas/s390/reloc.s
--- src/gas/testsuite/gas/s390/reloc.s	Fri Jan 17 16:26:55 2003
+++ src-s390/gas/testsuite/gas/s390/reloc.s	Mon Jan 20 12:29:08 2003
@@ -12,3 +12,10 @@
 	lhi	%r0,test_R_390_PC16-foo
 	bras	%r14,test_R_390_PC16DBL
 	bras	%r14,test_R_390_PLT16DBL
+	lhi	%r0,test_R_390_GOTOFF16@GOTOFF
+	.long	test_R_390_GOTOFF32@GOTOFF
+	lhi	%r0,test_R_390_PLTOFF16@PLTOFF
+	.long	test_R_390_PLTOFF32@PLTOFF
+	l	%r0,test_R_390_GOTPLT12@GOTPLT(%r1,%r2)
+	lhi	%r0,test_R_390_GOTPLT16@GOTPLT
+	.long	test_R_390_GOTPLT32@GOTPLT
diff -urN src/gas/testsuite/gas/s390/reloc64.d src-s390/gas/testsuite/gas/s390/reloc64.d
--- src/gas/testsuite/gas/s390/reloc64.d	Fri Jan 17 16:26:55 2003
+++ src-s390/gas/testsuite/gas/s390/reloc64.d	Mon Jan 20 12:29:08 2003
@@ -17,4 +17,10 @@
 [ 	]*24: R_390_PLT64	test_R_390_PLT64
   2c:	c0 10 00 00 00 00 [ 	]*larl	%r1,2c <foo\+0x2c>
 [ 	]*2e: R_390_GOTENT	test_R_390_GOTENT\+0x2
-  32:	07 07 [ 	]*bcr	0,%r7
+[ 	]*...
+[ 	]*32: R_390_GOTOFF64	test_R_390_GOTOFF64
+[ 	]*3a: R_390_PLTOFF64	test_R_390_PLTOFF64
+[ 	]*42: R_390_GOTPLT64	test_R_390_GOTPLT64
+  4a:	c0 10 00 00 00 00 [ 	]*larl	%r1,4a <foo\+0x4a>
+[ 	]*4c: R_390_GOTPLTENT	test_R_390_GOTPLTENT\+0x2
+
diff -urN src/gas/testsuite/gas/s390/reloc64.s src-s390/gas/testsuite/gas/s390/reloc64.s
--- src/gas/testsuite/gas/s390/reloc64.s	Fri Jan 17 16:26:55 2003
+++ src-s390/gas/testsuite/gas/s390/reloc64.s	Mon Jan 20 12:29:08 2003
@@ -7,3 +7,7 @@
 	.quad	test_R_390_GOT64@GOT
 	.quad	test_R_390_PLT64@PLT
 	larl	%r1,test_R_390_GOTENT@GOT
+	.quad	test_R_390_GOTOFF64@GOTOFF
+	.quad	test_R_390_PLTOFF64@PLTOFF
+	.quad	test_R_390_GOTPLT64@GOTPLT
+	larl	%r1,test_R_390_GOTPLTENT@GOTPLT
diff -urN src/include/elf/s390.h src-s390/include/elf/s390.h
--- src/include/elf/s390.h	Fri Jan 17 16:26:55 2003
+++ src-s390/include/elf/s390.h	Mon Jan 20 12:29:08 2003
@@ -49,7 +49,7 @@
     RELOC_NUMBER (R_390_GLOB_DAT, 10)	/* Create GOT entry.  */
     RELOC_NUMBER (R_390_JMP_SLOT, 11)	/* Create PLT entry.  */
     RELOC_NUMBER (R_390_RELATIVE, 12)	/* Adjust by program base.  */
-    RELOC_NUMBER (R_390_GOTOFF, 13)	/* 32 bit offset to GOT.  */
+    RELOC_NUMBER (R_390_GOTOFF32, 13)	/* 32 bit offset to GOT.  */
     RELOC_NUMBER (R_390_GOTPC, 14)	/* 32 bit PC relative offset to GOT.  */
     RELOC_NUMBER (R_390_GOT16, 15)	/* 16 bit GOT offset.  */
     RELOC_NUMBER (R_390_PC16, 16)	/* PC relative 16 bit.  */
@@ -63,6 +63,16 @@
     RELOC_NUMBER (R_390_GOT64, 24)	/* 64 bit GOT offset.  */
     RELOC_NUMBER (R_390_PLT64, 25)	/* 64 bit PC relative PLT address.  */
     RELOC_NUMBER (R_390_GOTENT, 26)	/* 32 bit PC rel. to GOT entry >> 1. */
+    RELOC_NUMBER (R_390_GOTOFF16, 27)   /* 16 bit offset to GOT. */
+    RELOC_NUMBER (R_390_GOTOFF64, 28)   /* 64 bit offset to GOT. */
+    RELOC_NUMBER (R_390_GOTPLT12, 29)   /* 12 bit offset to jump slot.  */
+    RELOC_NUMBER (R_390_GOTPLT16, 30)   /* 16 bit offset to jump slot.  */
+    RELOC_NUMBER (R_390_GOTPLT32, 31)   /* 32 bit offset to jump slot.  */
+    RELOC_NUMBER (R_390_GOTPLT64, 32)   /* 64 bit offset to jump slot.  */
+    RELOC_NUMBER (R_390_GOTPLTENT, 33)  /* 32 bit rel. offset to jump slot.  */
+    RELOC_NUMBER (R_390_PLTOFF16, 34)   /* 16 bit offset from GOT to PLT. */
+    RELOC_NUMBER (R_390_PLTOFF32, 35)   /* 32 bit offset from GOT to PLT. */
+    RELOC_NUMBER (R_390_PLTOFF64, 36)   /* 16 bit offset from GOT to PLT. */
     /* These are GNU extensions to enable C++ vtable garbage collection.  */
     RELOC_NUMBER (R_390_GNU_VTINHERIT, 250)
     RELOC_NUMBER (R_390_GNU_VTENTRY, 251)



More information about the Binutils mailing list