From a3a33af390c3a99e00b20847a102e307cfb538c7 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor <ian@airs.com>
Date: Fri, 25 Mar 1994 22:37:55 +0000
Subject: [PATCH] 	Changes to support linker relaxing of embedded MIPS
 PIC code to 	use a five instruction sequence for funtion calls which are
 out of 	range of the bal instruction. 	* libecoff.h (struct
 ecoff_section_tdata): Define. 	(ecoff_section_data): Define. 
 (ecoff_bfd_relax_section): Don't define. 	* ecoff.c
 (ecoff_final_link_debug_accumulate): Don't read or free 	the debugging
 information if it has already been read. 	(ecoff_indirect_link_order):
 Handle _cooked_size being different 	from _raw_size.  Don't reread the
 contents or the relocs if they 	have already been read in. 	*
 coff-mips.c (mips_howto_table): Change bitsize of PCREL16 from 	18 to
 16. 	(PCREL16_EXPANSION_ADJUSTMENT): Define. 	(mips_relocate_refhi):
 Take adjust argument. 	(mips_relocate_section): Handle reloc offsets stored
 in section 	used_by_bfd field.  Call mips_relax_pcrel16 to handle details
 of 	expanding an out of range PCREL16.  Keep trace of adjustments 
 required by expansions.  Set s and unset h when converting a reloc 	from
 undefined to section.  Change handling of PC relative relocs: 	if against a
 section, they are correct in the object file, if 	against an external
 symbol they are pcrel_offset. 	(mips_relax_section): New function. 
 (mips_relax_pcrel16): New function. 	(ecoff_bfd_relax_section): Define. 
 * coff-alpha.c (ecoff_bfd_relax_section): Define. 	* ecofflink.c
 (bfd_ecoff_debug_accumulate): Handle adjustments 	built by
 mips_relax_section when writing out addresses. 	* elf32-mips.c
 (mips_elf_read_ecoff_info): Clear adjust field.

---
 bfd/ChangeLog    |  30 +++
 bfd/coff-alpha.c |  44 +++-
 bfd/coff-mips.c  | 558 +++++++++++++++++++++++++++++++++++++++++++++--
 bfd/ecoff.c      | 418 ++++++++++++++++++++++++++++-------
 bfd/ecofflink.c  | 128 +++++++++--
 bfd/elf32-mips.c |  76 +++++--
 bfd/libecoff.h   | 213 +++++++++++-------
 7 files changed, 1247 insertions(+), 220 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 528f826a79f..7e657fd1fab 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,35 @@
 Fri Mar 25 17:10:45 1994  Ian Lance Taylor  (ian@tweedledumb.cygnus.com)
 
+	Changes to support linker relaxing of embedded MIPS PIC code to
+	use a five instruction sequence for funtion calls which are out of
+	range of the bal instruction.
+	* libecoff.h (struct ecoff_section_tdata): Define.
+	(ecoff_section_data): Define.
+	(ecoff_bfd_relax_section): Don't define.
+	* ecoff.c (ecoff_final_link_debug_accumulate): Don't read or free
+	the debugging information if it has already been read.
+	(ecoff_indirect_link_order): Handle _cooked_size being different
+	from _raw_size.  Don't reread the contents or the relocs if they
+	have already been read in.
+	* coff-mips.c (mips_howto_table): Change bitsize of PCREL16 from
+	18 to 16.
+	(PCREL16_EXPANSION_ADJUSTMENT): Define.
+	(mips_relocate_refhi): Take adjust argument.
+	(mips_relocate_section): Handle reloc offsets stored in section
+	used_by_bfd field.  Call mips_relax_pcrel16 to handle details of
+	expanding an out of range PCREL16.  Keep trace of adjustments
+	required by expansions.  Set s and unset h when converting a reloc
+	from undefined to section.  Change handling of PC relative relocs:
+	if against a section, they are correct in the object file, if
+	against an external symbol they are pcrel_offset.
+	(mips_relax_section): New function.
+	(mips_relax_pcrel16): New function.
+	(ecoff_bfd_relax_section): Define.
+	* coff-alpha.c (ecoff_bfd_relax_section): Define.
+	* ecofflink.c (bfd_ecoff_debug_accumulate): Handle adjustments
+	built by mips_relax_section when writing out addresses.
+	* elf32-mips.c (mips_elf_read_ecoff_info): Clear adjust field.
+
 	* aoutx.h (NAME(aout,find_nearest_line)): The caller expects
 	functionname_ptr to be set to a symbol name, so prepend
 	symbol_leading_char.
diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 64ab8406867..43530781e5a 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -715,16 +715,23 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order,
   bfd *input_bfd = link_order->u.indirect.section->owner;
   asection *input_section = link_order->u.indirect.section;
   size_t reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
-  arelent **reloc_vector = (arelent **) alloca (reloc_size);
+  arelent **reloc_vector = NULL;
   bfd *output_bfd = relocateable ? abfd : (bfd *) NULL;
   bfd_vma gp;
   boolean gp_undefined;
   bfd_vma stack[RELOC_STACKSIZE];
   int tos = 0;
 
+  reloc_vector = (arelent **) malloc (reloc_size);
+  if (reloc_vector == NULL && reloc_size != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+
   if (! bfd_get_section_contents (input_bfd, input_section, data,
 				  (file_ptr) 0, input_section->_raw_size))
-    return NULL;
+    goto error_return;
 
   /* The section size is not going to change.  */
   input_section->_cooked_size = input_section->_raw_size;
@@ -733,7 +740,7 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order,
   if (bfd_canonicalize_reloc (input_bfd, input_section, reloc_vector,
 			      symbols)
       == 0)
-    return data;
+    goto successful_return;
 
   /* Get the GP value for the output BFD.  */
   gp_undefined = false;
@@ -1080,20 +1087,20 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order,
 	      if (! ((*link_info->callbacks->undefined_symbol)
 		     (link_info, bfd_asymbol_name (*rel->sym_ptr_ptr),
 		      input_bfd, input_section, rel->address)))
-		return NULL;
+		goto error_return;
 	      break;
 	    case bfd_reloc_dangerous: 
 	      if (! ((*link_info->callbacks->reloc_dangerous)
 		     (link_info, err, input_bfd, input_section,
 		      rel->address)))
-		return NULL;
+		goto error_return;
 	      break;
 	    case bfd_reloc_overflow:
 	      if (! ((*link_info->callbacks->reloc_overflow)
 		     (link_info, bfd_asymbol_name (*rel->sym_ptr_ptr),
 		      rel->howto->name, rel->addend, input_bfd,
 		      input_section, rel->address)))
-		return NULL;
+		goto error_return;
 	      break;
 	    case bfd_reloc_outofrange:
 	    default:
@@ -1106,7 +1113,15 @@ alpha_ecoff_get_relocated_section_contents (abfd, link_info, link_order,
   if (tos != 0)
     abort ();
 
+ successful_return:
+  if (reloc_vector != NULL)
+    free (reloc_vector);
   return data;
+
+ error_return:
+  if (reloc_vector != NULL)
+    free (reloc_vector);
+  return NULL;
 }
 
 /* Get the howto structure for a generic reloc type.  */
@@ -1124,6 +1139,7 @@ alpha_bfd_reloc_type_lookup (abfd, code)
       alpha_type = ALPHA_R_REFLONG;
       break;
     case BFD_RELOC_64:
+    case BFD_RELOC_CTOR:
       alpha_type = ALPHA_R_REFQUAD;
       break;
     case BFD_RELOC_GPREL32:
@@ -1658,17 +1674,26 @@ alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 	     adjust the address of the reloc.  */
 	  if (! info->relocateable)
 	    {
+	      bfd_vma mask;
 	      bfd_vma val;
 
 	      if (tos == 0)
 		abort ();
 
+	      /* Get the relocation mask.  The separate steps and the
+		 casts to bfd_vma are attempts to avoid a bug in the
+		 Alpha OSF 1.3 C compiler.  See reloc.c for more
+		 details.  */
+	      mask = 1;
+	      mask <<= (bfd_vma) r_size;
+	      mask -= 1;
+
 	      /* FIXME: I don't know what kind of overflow checking,
 		 if any, should be done here.  */
 	      val = bfd_get_64 (input_bfd,
 				contents + r_vaddr - input_section->vma);
-	      val &=~ (((1 << r_size) - 1) << r_offset);
-	      val |= (stack[--tos] & ((1 << r_size) - 1)) << r_offset;
+	      val &=~ mask << (bfd_vma) r_offset;
+	      val |= (stack[--tos] & mask) << (bfd_vma) r_offset;
 	      bfd_put_64 (input_bfd, val,
 			  contents + r_vaddr - input_section->vma);
 	    }
@@ -1961,6 +1986,9 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data =
 #define ecoff_bfd_get_relocated_section_contents \
   alpha_ecoff_get_relocated_section_contents
 
+/* Relaxing sections is generic.  */
+#define ecoff_bfd_relax_section bfd_generic_relax_section
+
 bfd_target ecoffalpha_little_vec =
 {
   "ecoff-littlealpha",		/* name */
diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c
index a9a95e5d6a4..71a0cd5e5d5 100644
--- a/bfd/coff-mips.c
+++ b/bfd/coff-mips.c
@@ -77,10 +77,18 @@ static void mips_relocate_refhi PARAMS ((struct internal_reloc *refhi,
 					 bfd *input_bfd,
 					 asection *input_section,
 					 bfd_byte *contents,
+					 size_t adjust,
 					 bfd_vma relocation));
 static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *,
 					      bfd *, asection *,
 					      bfd_byte *, PTR));
+static boolean mips_relax_section PARAMS ((bfd *, asection *,
+					   struct bfd_link_info *,
+					   boolean *));
+static boolean mips_relax_pcrel16 PARAMS ((struct bfd_link_info *, bfd *,
+					   asection *,
+					   struct ecoff_link_hash_entry *,
+					   bfd_byte *, bfd_vma));
 
 /* ECOFF has COFF sections, but the debugging information is stored in
    a completely different format.  ECOFF targets use some of the
@@ -236,11 +244,45 @@ static reloc_howto_type mips_howto_table[] =
 	 true,			/* partial_inplace */
 	 0xffff,		/* src_mask */
 	 0xffff,		/* dst_mask */
-	 false)			/* pcrel_offset */
+	 false),		/* pcrel_offset */
+
+  /* This reloc is a Cygnus extension used when generating position
+     independent code for embedded systems.  It represents a 16 bit PC
+     relative reloc rightshifted twice as used in the MIPS branch
+     instructions.  */
+  HOWTO (MIPS_R_PCREL16,	/* type */
+	 2,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 mips_generic_reloc,	/* special_function */
+	 "PCREL16",		/* name */
+	 true,			/* partial_inplace */
+	 0xffff,		/* src_mask */
+	 0xffff,		/* dst_mask */
+	 true)			/* pcrel_offset */
 };
 
 #define MIPS_HOWTO_COUNT \
   (sizeof mips_howto_table / sizeof mips_howto_table[0])
+
+/* When the linker is doing relaxing, it may change a external PCREL16
+   reloc.  This typically represents an instruction like
+       bal foo
+   We change it to
+       .set  noreorder
+       bal   $L1
+       lui   $at,%hi(foo - $L1)
+     $L1:
+       addiu $at,%lo(foo - $L1)
+       addu  $at,$at,$31
+       jalr  $at
+   PCREL16_EXPANSION_ADJUSTMENT is the number of bytes this changes the
+   instruction by.  */
+
+#define PCREL16_EXPANSION_ADJUSTMENT (4 * 4)
 
 /* See whether the magic number matches.  */
 
@@ -357,7 +399,7 @@ mips_adjust_reloc_in (abfd, intern, rptr)
      const struct internal_reloc *intern;
      arelent *rptr;
 {
-  if (intern->r_type > MIPS_R_LITERAL)
+  if (intern->r_type > MIPS_R_PCREL16)
     abort ();
 
   if (! intern->r_extern
@@ -715,6 +757,9 @@ mips_bfd_reloc_type_lookup (abfd, code)
     case BFD_RELOC_MIPS_LITERAL:
       mips_type = MIPS_R_LITERAL;
       break;
+    case BFD_RELOC_16_PCREL_S2:
+      mips_type = MIPS_R_PCREL16;
+      break;
     default:
       return (CONST struct reloc_howto_struct *) NULL;
     }
@@ -729,12 +774,13 @@ mips_bfd_reloc_type_lookup (abfd, code)
 
 static void
 mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
-		     relocation)
+		     adjust, relocation)
      struct internal_reloc *refhi;
      struct internal_reloc *reflo;
      bfd *input_bfd;
      asection *input_section;
      bfd_byte *contents;
+     size_t adjust;
      bfd_vma relocation;
 {
   unsigned long insn;
@@ -742,9 +788,9 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
   unsigned long vallo;
 
   insn = bfd_get_32 (input_bfd,
-		     contents + refhi->r_vaddr - input_section->vma);
+		     contents + adjust + refhi->r_vaddr - input_section->vma);
   vallo = (bfd_get_32 (input_bfd,
-		       contents + reflo->r_vaddr - input_section->vma)
+		       contents + adjust + reflo->r_vaddr - input_section->vma)
 	   & 0xffff);
   val = ((insn & 0xffff) << 16) + vallo;
   val += relocation;
@@ -761,7 +807,7 @@ mips_relocate_refhi (refhi, reflo, input_bfd, input_section, contents,
 
   insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
   bfd_put_32 (input_bfd, (bfd_vma) insn,
-	      contents + refhi->r_vaddr - input_section->vma);
+	      contents + adjust + refhi->r_vaddr - input_section->vma);
 }
 
 /* Relocate a section while linking a MIPS ECOFF file.  */
@@ -780,9 +826,13 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
   struct ecoff_link_hash_entry **sym_hashes;
   bfd_vma gp;
   boolean gp_undefined;
+  size_t adjust;
+  long *offsets;
   struct external_reloc *ext_rel;
   struct external_reloc *ext_rel_end;
+  unsigned int i;
   boolean got_reflo;
+  struct internal_reloc reflo_int_rel;
 
   BFD_ASSERT (input_bfd->xvec->header_byteorder_big_p
 	      == output_bfd->xvec->header_byteorder_big_p);
@@ -842,12 +892,18 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 
   got_reflo = false;
 
+  adjust = 0;
+
+  if (ecoff_section_data (input_bfd, input_section) == NULL)
+    offsets = NULL;
+  else
+    offsets = ecoff_section_data (input_bfd, input_section)->offsets;
+
   ext_rel = (struct external_reloc *) external_relocs;
   ext_rel_end = ext_rel + input_section->reloc_count;
-  for (; ext_rel < ext_rel_end; ext_rel++)
+  for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
     {
       struct internal_reloc int_rel;
-      struct internal_reloc reflo_int_rel;
       bfd_vma addend;
       reloc_howto_type *howto;
       struct ecoff_link_hash_entry *h = NULL;
@@ -955,6 +1011,54 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 	    }
 	}
 
+      /* If we are relaxing, mips_relax_section may have set
+	 offsets[i] to some value.  A value of 1 means we must expand
+	 a PC relative branch into a multi-instruction of sequence,
+	 and any other value is an addend.  */
+      if (offsets != NULL
+	  && offsets[i] != 0)
+	{
+	  BFD_ASSERT (! info->relocateable);
+	  BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16);
+	  if (offsets[i] != 1)
+	    {
+	      BFD_ASSERT (! int_rel.r_extern);
+	      addend += offsets[i];
+	    }
+	  else
+	    {
+	      bfd_byte *here;
+
+	      BFD_ASSERT (int_rel.r_extern);
+
+	      /* Move the rest of the instructions up.  */
+	      here = (contents
+		      + adjust
+		      + int_rel.r_vaddr
+		      - input_section->vma);
+	      memmove (here + PCREL16_EXPANSION_ADJUSTMENT, here,
+		       (input_section->_raw_size
+			- (int_rel.r_vaddr - input_section->vma)));
+		       
+	      /* Generate the new instructions.  */
+	      if (! mips_relax_pcrel16 (info, input_bfd, input_section,
+					h, here,
+					(input_section->output_section->vma
+					 + input_section->output_offset
+					 + (int_rel.r_vaddr
+					    - input_section->vma)
+					 + adjust)))
+		return false;
+
+	      /* We must adjust everything else up a notch.  */
+	      adjust += PCREL16_EXPANSION_ADJUSTMENT;
+
+	      /* mips_relax_pcrel16 handles all the details of this
+		 relocation.  */
+	      continue;
+	    }
+	}
+
       if (info->relocateable)
 	{
 	  /* We are generating relocateable output, and must convert
@@ -963,7 +1067,6 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 	    {
 	      if (h->root.type == bfd_link_hash_defined)
 		{
-		  asection *hsec;
 		  const char *name;
 
 		  /* This symbol is defined in the output.  Convert
@@ -974,9 +1077,9 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 		  int_rel.r_extern = 0;
 
 		  /* Compute a new r_symndx value.  */
-		  hsec = h->root.u.def.section;
+		  s = h->root.u.def.section;
 		  name = bfd_get_section_name (output_bfd,
-					       hsec->output_section);
+					       s->output_section);
 
 		  int_rel.r_symndx = -1;
 		  switch (name[1])
@@ -1024,8 +1127,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 
 		  /* Add the section VMA and the symbol value.  */
 		  relocation = (h->root.u.def.value
-				+ hsec->output_section->vma
-				+ hsec->output_offset);
+				+ s->output_section->vma
+				+ s->output_offset);
+
+		  /* For a PC relative relocation, the object file
+		     currently holds just the addend.  We must adjust
+		     by the address to get the right value.  */
+		  if (howto->pc_relative)
+		    relocation -= int_rel.r_vaddr - input_section->vma;
+
+		  h = NULL;
 		}
 	      else
 		{
@@ -1056,6 +1167,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 
 	  relocation += addend;
 
+	  /* Adjust a PC relative relocation by removing the reference
+	     to the original address in the section and including the
+	     reference to the new address.  */
+	  if (howto->pc_relative)
+	    relocation -= (input_section->output_section->vma
+			   + input_section->output_offset
+			   - input_section->vma);
+
 	  /* Adjust the contents.  */
 	  if (relocation == 0)
 	    r = bfd_reloc_ok;
@@ -1064,13 +1183,14 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 	      if (int_rel.r_type != MIPS_R_REFHI)
 		r = _bfd_relocate_contents (howto, input_bfd, relocation,
 					    (contents
+					     + adjust
 					     + int_rel.r_vaddr
 					     - input_section->vma));
 	      else
 		{
 		  mips_relocate_refhi (&int_rel, &reflo_int_rel,
 				       input_bfd, input_section, contents,
-				       relocation);
+				       adjust, relocation);
 		  r = bfd_reloc_ok;
 		}
 	    }
@@ -1115,10 +1235,11 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 			    + s->output_offset
 			    - s->vma);
 
-	      /* Adjust a PC relative relocation by removing the
-		 reference to the original source section.  */
+	      /* A PC relative reloc is already correct in the object
+		 file.  Make it look like a pcrel_offset relocation by
+		 adding in the start address.  */
 	      if (howto->pc_relative)
-		relocation += input_section->vma;
+		relocation += int_rel.r_vaddr + adjust;
 	    }
 
 	  if (int_rel.r_type != MIPS_R_REFHI)
@@ -1126,13 +1247,16 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
 					  input_bfd,
 					  input_section,
 					  contents,
-					  int_rel.r_vaddr - input_section->vma,
+					  (int_rel.r_vaddr
+					   - input_section->vma
+					   + adjust),
 					  relocation,
 					  addend);
 	  else
 	    {
 	      mips_relocate_refhi (&int_rel, &reflo_int_rel, input_bfd,
-				   input_section, contents, relocation);
+				   input_section, contents, adjust,
+				   relocation);
 	      r = bfd_reloc_ok;
 	    }
 	}
@@ -1166,6 +1290,399 @@ mips_relocate_section (output_bfd, info, input_bfd, input_section,
   return true;
 }
 
+/* Relax a section when linking a MIPS ECOFF file.  This is used for
+   embedded PIC code, which always uses PC relative branches which
+   only have an 18 bit range on MIPS.  If a branch is not in range, we
+   generate a long instruction sequence to compensate.  Each time we
+   find a branch to expand, we have to check all the others again to
+   make sure they are still in range.  This is slow, but it only has
+   to be done when -relax is passed to the linker.
+
+   This routine figures out which branches need to expand; the actual
+   expansion is done in mips_relocate_section when the section
+   contents are relocated.  The information is stored in the offsets
+   field of the ecoff_section_tdata structure.  An offset of 1 means
+   that the branch must be expanded into a multi-instruction PC
+   relative branch (such an offset will only occur for a PC relative
+   branch to an external symbol).  Any other offset must be a multiple
+   of four, and is the amount to change the branch by (such an offset
+   will only occur for a PC relative branch within the same section).
+
+   We do not modify the section relocs or contents themselves so that
+   if memory usage becomes an issue we can discard them and read them
+   again.  The only information we must save in memory between this
+   routine and the mips_relocate_section routine is the table of
+   offsets.  */
+
+static boolean
+mips_relax_section (abfd, sec, info, again)
+     bfd *abfd;
+     asection *sec;
+     struct bfd_link_info *info;
+     boolean *again;
+{
+  struct ecoff_section_tdata *section_tdata;
+  bfd_byte *contents = NULL;
+  long *offsets;
+  struct external_reloc *ext_rel;
+  struct external_reloc *ext_rel_end;
+  unsigned int i;
+
+  /* Assume we are not going to need another pass.  */
+  *again = false;
+
+  /* If we are not generating an ECOFF file, this is much too
+     confusing to deal with.  */
+  if (info->hash->creator->flavour != bfd_get_flavour (abfd))
+    return true;
+
+  /* If there are no relocs, there is nothing to do.  */
+  if (sec->reloc_count == 0)
+    return true;
+
+  /* We are only interested in PC relative relocs, and why would there
+     ever be one from anything but the .text section?  */
+  if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0)
+    return true;
+
+  /* Read in the relocs, if we haven't already got them.  */
+  section_tdata = ecoff_section_data (abfd, sec);
+  if (section_tdata == (struct ecoff_section_tdata *) NULL)
+    {
+      bfd_size_type external_reloc_size;
+      bfd_size_type external_relocs_size;
+
+      sec->used_by_bfd =
+	(PTR) bfd_alloc_by_size_t (abfd, sizeof (struct ecoff_section_tdata));
+      if (sec->used_by_bfd == NULL)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  goto error_return;
+	}
+
+      section_tdata = ecoff_section_data (abfd, sec);
+      section_tdata->contents = NULL;
+      section_tdata->offsets = NULL;
+
+      external_reloc_size = ecoff_backend (abfd)->external_reloc_size;
+      external_relocs_size = external_reloc_size * sec->reloc_count;
+
+      section_tdata->external_relocs =
+	(PTR) bfd_alloc (abfd, external_relocs_size);
+      if (section_tdata->external_relocs == NULL && external_relocs_size != 0)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  goto error_return;
+	}
+
+      if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0
+	  || (bfd_read (section_tdata->external_relocs, 1,
+			external_relocs_size, abfd)
+	      != external_relocs_size))
+	goto error_return;
+
+      /* We must initialize _cooked_size only the first time we are
+	 called.  */
+      sec->_cooked_size = sec->_raw_size;
+    }
+
+  contents = section_tdata->contents;
+  offsets = section_tdata->offsets;
+
+  /* Look for any external PC relative relocs.  Internal PC relative
+     relocs are already correct in the object file, so they certainly
+     can not overflow.  */
+  ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+  ext_rel_end = ext_rel + sec->reloc_count;
+  for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
+    {
+      struct internal_reloc int_rel;
+      struct ecoff_link_hash_entry *h;
+      asection *hsec;
+      bfd_signed_vma relocation;
+      struct external_reloc *adj_ext_rel;
+      unsigned int adj_i;
+      unsigned long ext_count;
+      struct ecoff_link_hash_entry **adj_h_ptr;
+      struct ecoff_link_hash_entry **adj_h_ptr_end;
+      struct ecoff_value_adjust *adjust;
+
+      /* If we have already expanded this reloc, we certainly don't
+	 need to do it again.  */
+      if (offsets != (long *) NULL && offsets[i] == 1)
+	continue;
+
+      /* Quickly check that this reloc is external PCREL16.  */
+      if (abfd->xvec->header_byteorder_big_p)
+	{
+	  if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0
+	      || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
+		   >> RELOC_BITS3_TYPE_SH_BIG)
+		  != MIPS_R_PCREL16))
+	    continue;
+	}
+      else
+	{
+	  if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0
+	      || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
+		   >> RELOC_BITS3_TYPE_SH_LITTLE)
+		  != MIPS_R_PCREL16))
+	    continue;
+	}
+
+      mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel);
+
+      h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx];
+      if (h == (struct ecoff_link_hash_entry *) NULL)
+	abort ();
+
+      if (h->root.type != bfd_link_hash_defined)
+	{
+	  /* Just ignore undefined symbols.  These will presumably
+	     generate an error later in the link.  */
+	  continue;
+	}
+
+      /* Get the value of the symbol.  */
+      hsec = h->root.u.def.section;
+      relocation = (h->root.u.def.value
+		    + hsec->output_section->vma
+		    + hsec->output_offset);
+
+      /* Subtract out the current address.  */
+      relocation -= (sec->output_section->vma
+		     + sec->output_offset
+		     + (int_rel.r_vaddr - sec->vma));
+
+      /* The addend is stored in the object file.  In the normal case
+	 of ``bal symbol'', the addend will be -4.  It will only be
+	 different in the case of ``bal symbol+constant''.  To avoid
+	 always reading in the section contents, we don't check the
+	 addend in the object file (we could easily check the contents
+	 if we happen to have already read them in, but I fear that
+	 this could be confusing).  This means we will screw up if
+	 there is a branch to a symbol that is in range, but added to
+	 a constant which puts it out of range; in such a case the
+	 link will fail with a reloc overflow error.  Since the
+	 compiler will never generate such code, it should be easy
+	 enough to work around it by changing the assembly code in the
+	 source file.  */
+      relocation -= 4;
+
+      /* Now RELOCATION is the number we want to put in the object
+	 file.  See whether it fits.  */
+      if (relocation >= -0x20000 && relocation < 0x20000)
+	continue;
+
+      /* Now that we know this reloc needs work, which will rarely
+	 happen, go ahead and grab the section contents.  */
+      if (contents == (bfd_byte *) NULL)
+	{
+	  if (info->keep_memory)
+	    contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size);
+	  else
+	    contents = (bfd_byte *) malloc (sec->_raw_size);
+	  if (contents == (bfd_byte *) NULL)
+	    {
+	      bfd_set_error (bfd_error_no_memory);
+	      goto error_return;
+	    }
+	  if (! bfd_get_section_contents (abfd, sec, (PTR) contents,
+					  (file_ptr) 0, sec->_raw_size))
+	    goto error_return;
+	  if (info->keep_memory)
+	    section_tdata->contents = contents;
+	}
+
+      /* We only support changing the bal instruction.  It would be
+	 possible to handle other PC relative branches, but some of
+	 them (the conditional branches) would require a different
+	 length instruction sequence which would complicate both this
+	 routine and mips_relax_pcrel16.  It could be written if
+	 somebody felt it were important.  Ignoring this reloc will
+	 presumably cause a reloc overflow error later on.  */
+      if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma)
+	  != 0x0411ffff) /* bgezal $0,. == bal . */
+	continue;
+
+      /* Bother.  We need to expand this reloc, and we will need to
+	 make another relaxation pass since this change may put other
+	 relocs out of range.  We need to examine the local branches
+	 and we need to allocate memory to hold the offsets we must
+	 add to them.  We also need to adjust the values of all
+	 symbols in the object file following this location.  */
+
+      sec->_cooked_size += PCREL16_EXPANSION_ADJUSTMENT;
+      *again = true;
+
+      if (offsets == (long *) NULL)
+	{
+	  size_t size;
+
+	  size = sec->reloc_count * sizeof (long);
+	  offsets = (long *) bfd_alloc_by_size_t (abfd, size);
+	  if (offsets == (long *) NULL)
+	    {
+	      bfd_set_error (bfd_error_no_memory);
+	      goto error_return;
+	    }
+	  memset (offsets, 0, size);
+	  section_tdata->offsets = offsets;
+	}
+
+      offsets[i] = 1;
+
+      /* Now look for all PC relative branches that cross this reloc
+	 and adjust their offsets.  We will turn the single branch
+	 instruction into a four instruction sequence.  In this loop
+	 we are only interested in local PC relative branches.  */
+      adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+      for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++)
+	{
+	  struct internal_reloc adj_int_rel;
+	  unsigned long insn;
+	  bfd_vma dst;
+
+	  /* Quickly check that this reloc is internal PCREL16.  */
+	  if (abfd->xvec->header_byteorder_big_p)
+	    {
+	      if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) != 0
+		  || (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
+		       >> RELOC_BITS3_TYPE_SH_BIG)
+		      != MIPS_R_PCREL16))
+		continue;
+	    }
+	  else
+	    {
+	      if ((adj_ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0
+		  || (((adj_ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
+		       >> RELOC_BITS3_TYPE_SH_LITTLE)
+		      != MIPS_R_PCREL16))
+		continue;
+	    }
+
+	  mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel);
+
+	  /* We are only interested in a PC relative reloc within this
+	     section.  FIXME: Cross section PC relative relocs may not
+	     be handled correctly; does anybody care?  */
+	  if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
+	    continue;
+
+	  /* Fetch the branch instruction.  */
+	  insn = bfd_get_32 (abfd, contents + adj_int_rel.r_vaddr - sec->vma);
+
+	  /* Work out the destination address.  */
+	  dst = (insn & 0xffff) << 2;
+	  if ((dst & 0x20000) != 0)
+	    dst -= 0x40000;
+	  dst += adj_int_rel.r_vaddr + 4;
+
+	  /* If this branch crosses the branch we just decided to
+	     expand, adjust the offset appropriately.  */
+	  if (adj_int_rel.r_vaddr < int_rel.r_vaddr
+	      && dst > int_rel.r_vaddr)
+	    offsets[adj_i] += PCREL16_EXPANSION_ADJUSTMENT;
+	  else if (adj_int_rel.r_vaddr > int_rel.r_vaddr
+		   && dst <= int_rel.r_vaddr)
+	    offsets[adj_i] -= PCREL16_EXPANSION_ADJUSTMENT;
+	}
+
+      /* Find all symbols in this section defined by this object file
+	 and adjust their values.  Note that we decide whether to
+	 adjust the value based on the value stored in the ECOFF EXTR
+	 structure, because the value stored in the hash table may
+	 have been changed by an earlier expanded reloc and thus may
+	 no longer correctly indicate whether the symbol is before or
+	 after the expanded reloc.  */
+      ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax;
+      adj_h_ptr = ecoff_data (abfd)->sym_hashes;
+      adj_h_ptr_end = adj_h_ptr + ext_count;
+      for (; adj_h_ptr < adj_h_ptr_end; adj_h_ptr++)
+	{
+	  struct ecoff_link_hash_entry *adj_h;
+
+	  adj_h = *adj_h_ptr;
+	  if (adj_h != (struct ecoff_link_hash_entry *) NULL
+	      && adj_h->root.type == bfd_link_hash_defined
+	      && adj_h->root.u.def.section == sec
+	      && adj_h->esym.asym.value > int_rel.r_vaddr)
+	    adj_h->root.u.def.value += PCREL16_EXPANSION_ADJUSTMENT;
+	}
+
+      /* Add an entry to the symbol value adjust list.  This is used
+	 by bfd_ecoff_debug_accumulate to adjust the values of
+	 internal symbols and FDR's.  */
+      adjust = ((struct ecoff_value_adjust *)
+		bfd_alloc (abfd, sizeof (struct ecoff_value_adjust)));
+      if (adjust == (struct ecoff_value_adjust *) NULL)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  goto error_return;
+	}
+
+      adjust->start = int_rel.r_vaddr;
+      adjust->end = sec->vma + sec->_raw_size;
+      adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT;
+
+      adjust->next = ecoff_data (abfd)->debug_info.adjust;
+      ecoff_data (abfd)->debug_info.adjust = adjust;
+    }
+
+  if (contents != (bfd_byte *) NULL && ! info->keep_memory)
+    free (contents);
+
+  return true;
+
+ error_return:
+  if (contents != (bfd_byte *) NULL && ! info->keep_memory)
+    free (contents);
+  return false;
+}
+
+/* This routine is called from mips_relocate_section when a PC
+   relative reloc must be expanded into the five instruction sequence.
+   It handles all the details of the expansion, including resolving
+   the reloc.  */
+
+static boolean
+mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address)
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     struct ecoff_link_hash_entry *h;
+     bfd_byte *location;
+     bfd_vma address;
+{
+  bfd_vma relocation;
+
+  /* 0x0411ffff is bgezal $0,. == bal .  */
+  BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff);
+
+  /* We need to compute the distance between the symbol and the
+     current address plus eight.  */
+  relocation = (h->root.u.def.value
+		+ h->root.u.def.section->output_section->vma
+		+ h->root.u.def.section->output_offset);
+  relocation -= address + 8;
+
+  /* If the lower half is negative, increment the upper 16 half.  */
+  if ((relocation & 0x8000) != 0)
+    relocation += 0x10000;
+
+  bfd_put_32 (input_bfd, 0x04110001, location); /* bal .+8 */
+  bfd_put_32 (input_bfd,
+	      0x3c010000 | ((relocation >> 16) & 0xffff), /* lui $at,XX */
+	      location + 4);
+  bfd_put_32 (input_bfd,
+	      0x24210000 | (relocation & 0xffff), /* addiu $at,$at,XX */
+	      location + 8);
+  bfd_put_32 (input_bfd, 0x003f0821, location + 12); /* addu $at,$at,$ra */
+  bfd_put_32 (input_bfd, 0x0020f809, location + 16); /* jalr $at */
+
+  return true;
+}
+
 /* This is the ECOFF backend structure.  The backend field of the
    target vector points to this.  */
 
@@ -1256,6 +1773,9 @@ static const struct ecoff_backend_data mips_ecoff_backend_data =
 #define ecoff_bfd_get_relocated_section_contents \
   bfd_generic_get_relocated_section_contents
 
+/* Relaxing sections is MIPS specific.  */
+#define ecoff_bfd_relax_section mips_relax_section
+
 /* Core file support is usually traditional (but note that Irix uses
    irix-core.c).  */
 #define ecoff_core_file_p _bfd_dummy_target
diff --git a/bfd/ecoff.c b/bfd/ecoff.c
index 745d46a33e9..5665a993d56 100644
--- a/bfd/ecoff.c
+++ b/bfd/ecoff.c
@@ -371,13 +371,16 @@ ecoff_styp_to_sec_flags (abfd, hdr)
     }
   else if ((styp_flags & STYP_DATA)
 	   || (styp_flags & STYP_RDATA)
-	   || (styp_flags & STYP_SDATA))
+	   || (styp_flags & STYP_SDATA)
+	   || styp_flags == STYP_PDATA
+	   || styp_flags == STYP_XDATA)
     {
       if (sec_flags & SEC_NEVER_LOAD)
 	sec_flags |= SEC_DATA | SEC_SHARED_LIBRARY;
       else
 	sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC;
-      if (styp_flags & STYP_RDATA)
+      if ((styp_flags & STYP_RDATA)
+	  || styp_flags == STYP_PDATA)
 	sec_flags |= SEC_READONLY;
     }
   else if ((styp_flags & STYP_BSS)
@@ -385,7 +388,7 @@ ecoff_styp_to_sec_flags (abfd, hdr)
     {
       sec_flags |= SEC_ALLOC;
     }
-  else if (styp_flags & STYP_INFO) 
+  else if ((styp_flags & STYP_INFO) || styp_flags == STYP_COMMENT)
     {
       sec_flags |= SEC_NEVER_LOAD;
     }
@@ -3650,7 +3653,7 @@ ecoff_link_check_archive_element (abfd, info, pneeded)
   external_ext_size = backend->debug_swap.external_ext_size;
   esize = symhdr->iextMax * external_ext_size;
   external_ext = (PTR) malloc (esize);
-  if (external_ext == NULL)
+  if (external_ext == NULL && esize != 0)
     {
       bfd_set_error (bfd_error_no_memory);
       goto error_return;
@@ -3661,7 +3664,7 @@ ecoff_link_check_archive_element (abfd, info, pneeded)
     goto error_return;
 
   ssext = (char *) malloc (symhdr->issExtMax);
-  if (ssext == NULL)
+  if (ssext == NULL && symhdr->issExtMax != 0)
     {
       bfd_set_error (bfd_error_no_memory);
       goto error_return;
@@ -3774,7 +3777,7 @@ ecoff_link_add_object_symbols (abfd, info)
   external_ext_size = ecoff_backend (abfd)->debug_swap.external_ext_size;
   esize = symhdr->iextMax * external_ext_size;
   external_ext = (PTR) malloc (esize);
-  if (external_ext == NULL)
+  if (external_ext == NULL && esize != 0)
     {
       bfd_set_error (bfd_error_no_memory);
       goto error_return;
@@ -3785,7 +3788,7 @@ ecoff_link_add_object_symbols (abfd, info)
     goto error_return;
 
   ssext = (char *) malloc (symhdr->issExtMax);
-  if (ssext == NULL)
+  if (ssext == NULL && symhdr->issExtMax != 0)
     {
       bfd_set_error (bfd_error_no_memory);
       goto error_return;
@@ -3972,7 +3975,7 @@ ecoff_link_add_externals (abfd, info, external_ext, ssext)
 
       if (! (_bfd_generic_link_add_one_symbol
 	     (info, abfd, name, BSF_GLOBAL, section, value,
-	      (const char *) NULL, true, true, backend->constructor_bitsize,
+	      (const char *) NULL, true, true,
 	      (struct bfd_link_hash_entry **) &h)))
 	return false;
 
@@ -4006,6 +4009,9 @@ static boolean ecoff_link_write_external
 static boolean ecoff_indirect_link_order
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
 	   struct bfd_link_order *));
+static boolean ecoff_reloc_link_order
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+	   struct bfd_link_order *));
 
 /* ECOFF final link routine.  This looks through all the input BFDs
    and gathers together all the debugging information, and then
@@ -4111,6 +4117,9 @@ ecoff_bfd_final_link (abfd, info)
 	       p = p->next)
 	    if (p->type == bfd_indirect_link_order)
 	      o->reloc_count += p->u.indirect.section->reloc_count;
+	    else if (p->type == bfd_section_reloc_link_order
+		     || p->type == bfd_symbol_reloc_link_order)
+	      ++o->reloc_count;
 	}
     }
 
@@ -4189,6 +4198,12 @@ ecoff_bfd_final_link (abfd, info)
 	      if (! ecoff_indirect_link_order (abfd, info, o, p))
 		return false;
 	    }
+	  else if (p->type == bfd_section_reloc_link_order
+		   || p->type == bfd_symbol_reloc_link_order)
+	    {
+	      if (! ecoff_reloc_link_order (abfd, info, o, p))
+		return false;
+	    }
 	  else
 	    {
 	      if (! _bfd_default_link_order (abfd, info, o, p))
@@ -4241,16 +4256,22 @@ ecoff_final_link_debug_accumulate (output_bfd, input_bfd, info, handle)
 	}								\
     }
 
-  READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
-  READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR);
-  READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR);
-  READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR);
-  READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR);
-  READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
-	union aux_ext *);
-  READ (ss, cbSsOffset, issMax, sizeof (char), char *);
-  READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR);
-  READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR);
+  /* If raw_syments is not NULL, then the data was already by read by
+     ecoff_slurp_symbolic_info.  */
+  if (ecoff_data (input_bfd)->raw_syments == NULL)
+    {
+      READ (line, cbLineOffset, cbLine, sizeof (unsigned char),
+	    unsigned char *);
+      READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR);
+      READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR);
+      READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR);
+      READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR);
+      READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
+	    union aux_ext *);
+      READ (ss, cbSsOffset, issMax, sizeof (char), char *);
+      READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR);
+      READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR);
+    }
 #undef READ
 
   /* We do not read the external strings or the external symbols.  */
@@ -4261,36 +4282,39 @@ ecoff_final_link_debug_accumulate (output_bfd, input_bfd, info, handle)
 	  input_bfd, debug, swap, info));
 
  return_something:
-  if (debug->line != NULL)
-    free (debug->line);
-  if (debug->external_dnr != NULL)
-    free (debug->external_dnr);
-  if (debug->external_pdr != NULL)
-    free (debug->external_pdr);
-  if (debug->external_sym != NULL)
-    free (debug->external_sym);
-  if (debug->external_opt != NULL)
-    free (debug->external_opt);
-  if (debug->external_aux != NULL)
-    free (debug->external_aux);
-  if (debug->ss != NULL)
-    free (debug->ss);
-  if (debug->external_fdr != NULL)
-    free (debug->external_fdr);
-  if (debug->external_rfd != NULL)
-    free (debug->external_rfd);
+  if (ecoff_data (input_bfd)->raw_syments == NULL)
+    {
+      if (debug->line != NULL)
+	free (debug->line);
+      if (debug->external_dnr != NULL)
+	free (debug->external_dnr);
+      if (debug->external_pdr != NULL)
+	free (debug->external_pdr);
+      if (debug->external_sym != NULL)
+	free (debug->external_sym);
+      if (debug->external_opt != NULL)
+	free (debug->external_opt);
+      if (debug->external_aux != NULL)
+	free (debug->external_aux);
+      if (debug->ss != NULL)
+	free (debug->ss);
+      if (debug->external_fdr != NULL)
+	free (debug->external_fdr);
+      if (debug->external_rfd != NULL)
+	free (debug->external_rfd);
 
-  /* Make sure we don't accidentally follow one of these pointers on
-     to the stack.  */
-  debug->line = NULL;
-  debug->external_dnr = NULL;
-  debug->external_pdr = NULL;
-  debug->external_sym = NULL;
-  debug->external_opt = NULL;
-  debug->external_aux = NULL;
-  debug->ss = NULL;
-  debug->external_fdr = NULL;
-  debug->external_rfd = NULL;
+      /* Make sure we don't accidentally follow one of these pointers
+	 into freed memory.  */
+      debug->line = NULL;
+      debug->external_dnr = NULL;
+      debug->external_pdr = NULL;
+      debug->external_sym = NULL;
+      debug->external_opt = NULL;
+      debug->external_aux = NULL;
+      debug->ss = NULL;
+      debug->external_fdr = NULL;
+      debug->external_rfd = NULL;
+    }
 
   return ret;
 }
@@ -4318,9 +4342,42 @@ ecoff_link_write_external (h, data)
       h->esym.reserved = 0;
       h->esym.ifd = ifdNil;
       h->esym.asym.value = 0;
-      /* FIXME: we can do better than this for st and sc.  */
       h->esym.asym.st = stGlobal;
-      h->esym.asym.sc = scAbs;
+
+      if (h->root.type != bfd_link_hash_defined)
+	h->esym.asym.sc = scAbs;
+      else
+	{
+	  asection *output_section;
+	  const char *name;
+
+	  output_section = h->root.u.def.section->output_section;
+	  name = bfd_section_name (output_section->owner, output_section);
+	
+	  if (strcmp (name, _TEXT) == 0)
+	    h->esym.asym.sc = scText;
+	  else if (strcmp (name, _DATA) == 0)
+	    h->esym.asym.sc = scData;
+	  else if (strcmp (name, _SDATA) == 0)
+	    h->esym.asym.sc = scSData;
+	  else if (strcmp (name, _RDATA) == 0)
+	    h->esym.asym.sc = scRData;
+	  else if (strcmp (name, _BSS) == 0)
+	    h->esym.asym.sc = scBss;
+	  else if (strcmp (name, _SBSS) == 0)
+	    h->esym.asym.sc = scSBss;
+	  else if (strcmp (name, _INIT) == 0)
+	    h->esym.asym.sc = scInit;
+	  else if (strcmp (name, _FINI) == 0)
+	    h->esym.asym.sc = scFini;
+	  else if (strcmp (name, _PDATA) == 0)
+	    h->esym.asym.sc = scPData;
+	  else if (strcmp (name, _XDATA) == 0)
+	    h->esym.asym.sc = scXData;
+	  else
+	    h->esym.asym.sc = scAbs;
+	}
+
       h->esym.asym.reserved = 0;
       h->esym.asym.index = indexNil;
     }
@@ -4343,9 +4400,9 @@ ecoff_link_write_external (h, data)
       abort ();
     case bfd_link_hash_undefined:
     case bfd_link_hash_weak:
-      if (h->esym.asym.st != scUndefined
-	  && h->esym.asym.st != scSUndefined)
-	h->esym.asym.st = scUndefined;
+      if (h->esym.asym.sc != scUndefined
+	  && h->esym.asym.sc != scSUndefined)
+	h->esym.asym.sc = scUndefined;
       break;
     case bfd_link_hash_defined:
       if (h->esym.asym.sc == scUndefined
@@ -4394,7 +4451,9 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order)
 {
   asection *input_section;
   bfd *input_bfd;
-  bfd_size_type input_size;
+  struct ecoff_section_tdata *section_tdata;
+  bfd_size_type raw_size;
+  bfd_size_type cooked_size;
   bfd_byte *contents = NULL;
   bfd_size_type external_reloc_size;
   bfd_size_type external_relocs_size;
@@ -4407,37 +4466,63 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order)
 
   input_section = link_order->u.indirect.section;
   input_bfd = input_section->owner;
+  section_tdata = ecoff_section_data (input_bfd, input_section);
+
+  raw_size = input_section->_raw_size;
+  cooked_size = input_section->_cooked_size;
+  if (cooked_size == 0)
+    cooked_size = raw_size;
 
   BFD_ASSERT (input_section->output_section == output_section);
   BFD_ASSERT (input_section->output_offset == link_order->offset);
-  BFD_ASSERT (bfd_section_size (input_bfd, input_section) == link_order->size);
+  BFD_ASSERT (cooked_size == link_order->size);
 
-  /* Get the section contents.  */
-  input_size = bfd_section_size (input_bfd, input_section);
-  contents = (bfd_byte *) malloc (input_size);
-  if (contents == NULL)
+  /* Get the section contents.  We allocate memory for the larger of
+     the size before relocating and the size after relocating.  */
+  contents = (bfd_byte *) malloc (raw_size >= cooked_size
+				  ? raw_size
+				  : cooked_size);
+  if (contents == NULL && raw_size != 0)
     {
       bfd_set_error (bfd_error_no_memory);
       goto error_return;
     }
-  if (! bfd_get_section_contents (input_bfd, input_section, (PTR) contents,
-				  (file_ptr) 0, input_size))
-    goto error_return;
 
-  /* Get the relocs.  */
+  /* If we are relaxing, the contents may have already been read into
+     memory, in which case we copy them into our new buffer.  We don't
+     simply reuse the old buffer in case cooked_size > raw_size.  */
+  if (section_tdata != (struct ecoff_section_tdata *) NULL
+      && section_tdata->contents != (bfd_byte *) NULL)
+    memcpy (contents, section_tdata->contents, raw_size);
+  else
+    {
+      if (! bfd_get_section_contents (input_bfd, input_section,
+				      (PTR) contents,
+				      (file_ptr) 0, raw_size))
+	goto error_return;
+    }
+
+  /* Get the relocs.  If we are relaxing MIPS code, they will already
+     have been read in.  Otherwise, we read them in now.  */
   external_reloc_size = ecoff_backend (input_bfd)->external_reloc_size;
   external_relocs_size = external_reloc_size * input_section->reloc_count;
-  external_relocs = (PTR) malloc (external_relocs_size);
-  if (external_relocs == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
-    }
 
-  if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
-      || (bfd_read (external_relocs, 1, external_relocs_size, input_bfd)
-	  != external_relocs_size))
-    goto error_return;
+  if (section_tdata != (struct ecoff_section_tdata *) NULL)
+    external_relocs = section_tdata->external_relocs;
+  else
+    {
+      external_relocs = (PTR) malloc (external_relocs_size);
+      if (external_relocs == NULL && external_relocs_size != 0)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  goto error_return;
+	}
+
+      if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
+	  || (bfd_read (external_relocs, 1, external_relocs_size, input_bfd)
+	      != external_relocs_size))
+	goto error_return;
+    }
 
   /* Relocate the section contents.  */
   if (! ((*ecoff_backend (input_bfd)->relocate_section)
@@ -4450,7 +4535,7 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order)
 				  output_section,
 				  (PTR) contents,
 				  input_section->output_offset,
-				  input_size))
+				  cooked_size))
     goto error_return;
 
   /* If we are producing relocateable output, the relocs were
@@ -4471,14 +4556,197 @@ ecoff_indirect_link_order (output_bfd, info, output_section, link_order)
 
   if (contents != NULL)
     free (contents);
-  if (external_relocs != NULL)
+  if (external_relocs != NULL && section_tdata == NULL)
     free (external_relocs);
   return true;
 
  error_return:
   if (contents != NULL)
     free (contents);
-  if (external_relocs != NULL)
+  if (external_relocs != NULL && section_tdata == NULL)
     free (external_relocs);
   return false;
 }
+
+/* Generate a reloc when linking an ECOFF file.  This is a reloc
+   requested by the linker, and does come from any input file.  This
+   is used to build constructor and destructor tables when linking
+   with -Ur.  */
+
+static boolean
+ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     asection *output_section;
+     struct bfd_link_order *link_order;
+{
+  arelent rel;
+  struct internal_reloc in;
+  bfd_size_type external_reloc_size;
+  bfd_byte *rbuf;
+  boolean ok;
+
+  /* We set up an arelent to pass to the backend adjust_reloc_out
+     routine.  */
+  rel.address = link_order->offset;
+
+  rel.howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc);
+  if (rel.howto == (const reloc_howto_type *) NULL)
+    {
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  if (link_order->type == bfd_section_reloc_link_order)
+    rel.sym_ptr_ptr = link_order->u.reloc.p->u.section->symbol_ptr_ptr;
+  else
+    {
+      /* We can't set up a reloc against a symbol correctly, because
+	 we have no asymbol structure.  Currently no adjust_reloc_out
+	 routine cases.  */
+      rel.sym_ptr_ptr = (asymbol **) NULL;
+    }
+
+  /* All ECOFF relocs are in-place.  Put the addend into the object
+     file.  */
+
+  BFD_ASSERT (rel.howto->partial_inplace);
+  if (link_order->u.reloc.p->addend != 0)
+    {
+      bfd_size_type size;
+      bfd_reloc_status_type rstat;
+      bfd_byte *buf;
+      boolean ok;
+
+      size = bfd_get_reloc_size (rel.howto);
+      buf = (bfd_byte *) bfd_zmalloc (size);
+      if (buf == (bfd_byte *) NULL)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  return false;
+	}
+      rstat = _bfd_relocate_contents (rel.howto, output_bfd,
+				      link_order->u.reloc.p->addend, buf);
+      switch (rstat)
+	{
+	case bfd_reloc_ok:
+	  break;
+	default:
+	case bfd_reloc_outofrange:
+	  abort ();
+	case bfd_reloc_overflow:
+	  if (! ((*info->callbacks->reloc_overflow)
+		 (info,
+		  (link_order->type == bfd_section_reloc_link_order
+		   ? bfd_section_name (output_bfd,
+				       link_order->u.reloc.p->u.section)
+		   : link_order->u.reloc.p->u.name),
+		  rel.howto->name, link_order->u.reloc.p->addend,
+		  (bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
+	    {
+	      free (buf);
+	      return false;
+	    }
+	  break;
+	}
+      ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf,
+				     (file_ptr) link_order->offset, size);
+      free (buf);
+      if (! ok)
+	return false;
+    }
+
+  rel.addend = 0;
+
+  /* Move the information into a internal_reloc structure.  */
+  in.r_vaddr = (rel.address
+		+ bfd_get_section_vma (output_bfd, output_section));
+  in.r_type = rel.howto->type;
+
+  if (link_order->type == bfd_symbol_reloc_link_order)
+    {
+      struct ecoff_link_hash_entry *h;
+
+      h = ecoff_link_hash_lookup (ecoff_hash_table (info),
+				  link_order->u.reloc.p->u.name,
+				  false, false, true);
+      if (h != (struct ecoff_link_hash_entry *) NULL
+	  && h->indx != -1)
+	in.r_symndx = h->indx;
+      else
+	{
+	  if (! ((*info->callbacks->unattached_reloc)
+		 (info, link_order->u.reloc.p->u.name, (bfd *) NULL,
+		  (asection *) NULL, (bfd_vma) 0)))
+	    return false;
+	  in.r_symndx = 0;
+	}
+      in.r_extern = 1;
+    }
+  else
+    {
+      CONST char *name;
+
+      name = bfd_get_section_name (output_bfd,
+				   link_order->u.reloc.p->u.section);
+      if (strcmp (name, ".text") == 0)
+	in.r_symndx = RELOC_SECTION_TEXT;
+      else if (strcmp (name, ".rdata") == 0)
+	in.r_symndx = RELOC_SECTION_RDATA;
+      else if (strcmp (name, ".data") == 0)
+	in.r_symndx = RELOC_SECTION_DATA;
+      else if (strcmp (name, ".sdata") == 0)
+	in.r_symndx = RELOC_SECTION_SDATA;
+      else if (strcmp (name, ".sbss") == 0)
+	in.r_symndx = RELOC_SECTION_SBSS;
+      else if (strcmp (name, ".bss") == 0)
+	in.r_symndx = RELOC_SECTION_BSS;
+      else if (strcmp (name, ".init") == 0)
+	in.r_symndx = RELOC_SECTION_INIT;
+      else if (strcmp (name, ".lit8") == 0)
+	in.r_symndx = RELOC_SECTION_LIT8;
+      else if (strcmp (name, ".lit4") == 0)
+	in.r_symndx = RELOC_SECTION_LIT4;
+      else if (strcmp (name, ".xdata") == 0)
+	in.r_symndx = RELOC_SECTION_XDATA;
+      else if (strcmp (name, ".pdata") == 0)
+	in.r_symndx = RELOC_SECTION_PDATA;
+      else if (strcmp (name, ".fini") == 0)
+	in.r_symndx = RELOC_SECTION_FINI;
+      else if (strcmp (name, ".lita") == 0)
+	in.r_symndx = RELOC_SECTION_LITA;
+      else if (strcmp (name, "*ABS*") == 0)
+	in.r_symndx = RELOC_SECTION_ABS;
+      else
+	abort ();
+      in.r_extern = 0;
+    }
+
+  /* Let the BFD backend adjust the reloc.  */
+  (*ecoff_backend (output_bfd)->adjust_reloc_out) (output_bfd, &rel, &in);
+
+  /* Get some memory and swap out the reloc.  */
+  external_reloc_size = ecoff_backend (output_bfd)->external_reloc_size;
+  rbuf = (bfd_byte *) malloc (external_reloc_size);
+  if (rbuf == (bfd_byte *) NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+
+  (*ecoff_backend (output_bfd)->swap_reloc_out) (output_bfd, &in, (PTR) rbuf);
+
+  ok = (bfd_seek (output_bfd,
+		  (output_section->rel_filepos +
+		   output_section->reloc_count * external_reloc_size),
+		  SEEK_SET) == 0
+	&& (bfd_write ((PTR) rbuf, 1, external_reloc_size, output_bfd)
+	    == external_reloc_size));
+
+  if (ok)
+    ++output_section->reloc_count;
+
+  free (rbuf);
+
+  return ok;
+}
diff --git a/bfd/ecofflink.c b/bfd/ecofflink.c
index a4507eaba25..4b14dcd5437 100644
--- a/bfd/ecofflink.c
+++ b/bfd/ecofflink.c
@@ -519,10 +519,17 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap,
 	     hash reduces the chance that we will merge symbol
 	     information that should not be merged.  */
 	  name = input_debug->ss + fdr.issBase + fdr.rss;
-	  lookup = (char *) alloca (strlen (name) + 20);
+
+	  lookup = (char *) malloc (strlen (name) + 20);
+	  if (lookup == NULL)
+	    {
+	      bfd_set_error (bfd_error_no_memory);
+	      return false;
+	    }
 	  sprintf (lookup, "%s %lx", name, fdr.csym);
 
 	  fh = string_hash_lookup (&ainfo->fdr_hash, lookup, true, true);
+	  free (lookup);
 	  if (fh == (struct string_hash_entry *) NULL)
 	    return false;
 
@@ -600,6 +607,22 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap,
       else
 	(*input_swap->swap_fdr_in) (input_bfd, (PTR) fdr_ptr, &fdr);
 
+      /* Adjust the FDR address for any changes that may have been
+	 made by relaxing.  */
+      if (input_debug->adjust != (struct ecoff_value_adjust *) NULL)
+	{
+	  bfd_vma adr;
+	  struct ecoff_value_adjust *adjust;
+
+	  adr = fdr.adr;
+	  for (adjust = input_debug->adjust;
+	       adjust != (struct ecoff_value_adjust *) NULL;
+	       adjust = adjust->next)
+	    if (adr >= adjust->start
+		&& adr < adjust->end)
+	      fdr.adr += adjust->adjust;
+	}
+
       /* FIXME: It is conceivable that this FDR points to the .init or
 	 .fini section, in which case this will not do the right
 	 thing.  */
@@ -641,6 +664,19 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap,
 	    case stLabel:
 	    case stProc:
 	    case stStaticProc:
+	      if (input_debug->adjust != (struct ecoff_value_adjust *) NULL)
+		{
+		  bfd_vma value;
+		  struct ecoff_value_adjust *adjust;
+
+		  value = internal_sym.value;
+		  for (adjust = input_debug->adjust;
+		       adjust != (struct ecoff_value_adjust *) NULL;
+		       adjust = adjust->next)
+		    if (value >= adjust->start
+			&& value < adjust->end)
+		      internal_sym.value += adjust->adjust;
+		}
 	      internal_sym.value += section_adjust[internal_sym.sc];
 	      break;
 
@@ -1253,7 +1289,7 @@ ecoff_write_symhdr (abfd, debug, swap, where)
      file_ptr where;
 {
   HDRR * const symhdr = &debug->symbolic_header;
-  char *buff;
+  char *buff = NULL;
 
   ecoff_align_debug (abfd, debug, swap);
 
@@ -1288,13 +1324,25 @@ ecoff_write_symhdr (abfd, debug, swap, where)
   SET (cbExtOffset, iextMax, swap->external_ext_size);
 #undef SET
 
-  buff = (PTR) alloca (swap->external_hdr_size);
+  buff = (PTR) malloc (swap->external_hdr_size);
+  if (buff == NULL && swap->external_hdr_size != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+
   (*swap->swap_hdr_out) (abfd, symhdr, buff);
   if (bfd_write (buff, 1, swap->external_hdr_size, abfd)
       != swap->external_hdr_size)
-    return false;
+    goto error_return;
 
+  if (buff != NULL)
+    free (buff);
   return true;
+ error_return:
+  if (buff != NULL)
+    free (buff);
+  return false;
 }
 
 /* Write out the ECOFF debugging information.  This function assumes
@@ -1377,10 +1425,20 @@ ecoff_write_shuffle (abfd, swap, shuffle, space)
       bfd_byte *s;
 
       i = swap->debug_align - (total & (swap->debug_align - 1));
-      s = (bfd_byte *) alloca (i);
+      s = (bfd_byte *) malloc (i);
+      if (s == NULL && i != 0)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  return false;
+	}
+
       memset ((PTR) s, 0, i);
       if (bfd_write ((PTR) s, 1, i, abfd) != i)
-	return false;
+	{
+	  free (s);
+	  return false;
+	}
+      free (s);
     }
 
   return true;
@@ -1399,19 +1457,24 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
      file_ptr where;
 {
   struct accumulate *ainfo = (struct accumulate *) handle;
-  PTR space;
+  PTR space = NULL;
 
   if (! ecoff_write_symhdr (abfd, debug, swap, where))
-    return false;
+    goto error_return;
 
-  space = (PTR) alloca (ainfo->largest_file_shuffle);
+  space = (PTR) malloc (ainfo->largest_file_shuffle);
+  if (space == NULL && ainfo->largest_file_shuffle != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
 
   if (! ecoff_write_shuffle (abfd, swap, ainfo->line, space)
       || ! ecoff_write_shuffle (abfd, swap, ainfo->pdr, space)
       || ! ecoff_write_shuffle (abfd, swap, ainfo->sym, space)
       || ! ecoff_write_shuffle (abfd, swap, ainfo->opt, space)
       || ! ecoff_write_shuffle (abfd, swap, ainfo->aux, space))
-    return false;
+    goto error_return;
 
   /* The string table is written out from the hash table if this is a
      final link.  */
@@ -1419,7 +1482,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
     {
       BFD_ASSERT (ainfo->ss_hash == (struct string_hash_entry *) NULL);
       if (! ecoff_write_shuffle (abfd, swap, ainfo->ss, space))
-	return false;
+	goto error_return;
     }
   else
     {
@@ -1430,7 +1493,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
       BFD_ASSERT (ainfo->ss == (struct shuffle *) NULL);
       null = 0;
       if (bfd_write ((PTR) &null, 1, 1, abfd) != 1)
-	return false;
+	goto error_return;
       total = 1;
       BFD_ASSERT (ainfo->ss_hash == NULL || ainfo->ss_hash->val == 1);
       for (sh = ainfo->ss_hash;
@@ -1441,7 +1504,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
 
 	  len = strlen (sh->root.string);
 	  if (bfd_write ((PTR) sh->root.string, 1, len + 1, abfd) != len + 1)
-	    return false;
+	    goto error_return;
 	  total += len + 1;
 	}
 
@@ -1451,10 +1514,19 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
 	  bfd_byte *s;
 
 	  i = swap->debug_align - (total & (swap->debug_align - 1));
-	  s = (bfd_byte *) alloca (i);
+	  s = (bfd_byte *) malloc (i);
+	  if (s == NULL && i != 0)
+	    {
+	      bfd_set_error (bfd_error_no_memory);
+	      goto error_return;
+	    }
 	  memset ((PTR) s, 0, i);
 	  if (bfd_write ((PTR) s, 1, i, abfd) != i)
-	    return false;
+	    {
+	      free (s);
+	      goto error_return;
+	    }
+	  free (s);
 	}
     }
 
@@ -1462,7 +1534,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
      shuffles.  FIXME: They probably should be.  */
   if (bfd_write (debug->ssext, 1, debug->symbolic_header.issExtMax, abfd)
       != debug->symbolic_header.issExtMax)
-    return false;
+    goto error_return;
   if ((debug->symbolic_header.issExtMax & (swap->debug_align - 1)) != 0)
     {
       int i;
@@ -1470,15 +1542,24 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
 
       i = (swap->debug_align
 	   - (debug->symbolic_header.issExtMax & (swap->debug_align - 1)));
-      s = (bfd_byte *) alloca (i);
+      s = (bfd_byte *) malloc (i);
+      if (s == NULL && i != 0)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  goto error_return;
+	}
       memset ((PTR) s, 0, i);
       if (bfd_write ((PTR) s, 1, i, abfd) != i)
-	return false;
+	{
+	  free (s);
+	  goto error_return;
+	}
+      free (s);
     }
 
   if (! ecoff_write_shuffle (abfd, swap, ainfo->fdr, space)
       || ! ecoff_write_shuffle (abfd, swap, ainfo->rfd, space))
-    return false;
+    goto error_return;
 
   BFD_ASSERT (debug->symbolic_header.cbExtOffset == 0
 	      || debug->symbolic_header.cbExtOffset == bfd_tell (abfd));
@@ -1486,7 +1567,14 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
   if (bfd_write (debug->external_ext, swap->external_ext_size,
 		 debug->symbolic_header.iextMax, abfd)
       != debug->symbolic_header.iextMax * swap->external_ext_size)
-    return false;
+    goto error_return;
 
+  if (space != NULL)
+    free (space);
   return true;
+
+ error_return:
+  if (space != NULL)
+    free (space);
+  return false;
 }
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index 57734bc5e88..15debf8a3a8 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -1028,16 +1028,21 @@ mips_elf_read_ecoff_info (abfd, section, debug)
 {
   HDRR *symhdr;
   const struct ecoff_debug_swap *swap;
-  char *ext_hdr;
+  char *ext_hdr = NULL;
 
   swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
 
-  ext_hdr = (char *) alloca (swap->external_hdr_size);
+  ext_hdr = (char *) malloc (swap->external_hdr_size);
+  if (ext_hdr == NULL && swap->external_hdr_size != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
 
   if (bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0,
 				swap->external_hdr_size)
       == false)
-    return false;
+    goto error_return;
 
   symhdr = &debug->symbolic_header;
   (*swap->swap_hdr_in) (abfd, ext_hdr, symhdr);
@@ -1052,13 +1057,13 @@ mips_elf_read_ecoff_info (abfd, section, debug)
       debug->ptr = (type) malloc (size * symhdr->count);		\
       if (debug->ptr == NULL)						\
 	{								\
-	  bfd_error = no_memory;					\
-	  return false;							\
+	  bfd_set_error (bfd_error_no_memory);				\
+	  goto error_return;						\
 	}								\
       if (bfd_seek (abfd, (file_ptr) symhdr->offset, SEEK_SET) != 0	\
 	  || (bfd_read (debug->ptr, size, symhdr->count,		\
 			abfd) != size * symhdr->count))			\
-	return false;							\
+	goto error_return;						\
     }
 
   READ (external_ext, cbExtOffset, iextMax, swap->external_ext_size, PTR);
@@ -1075,8 +1080,36 @@ mips_elf_read_ecoff_info (abfd, section, debug)
   READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR);
 
   debug->fdr = NULL;
+  debug->adjust = NULL;
 
   return true;
+
+ error_return:
+  if (ext_hdr != NULL)
+    free (ext_hdr);
+  if (debug->external_ext != NULL)
+    free (debug->external_ext);
+  if (debug->line != NULL)
+    free (debug->line);
+  if (debug->external_dnr != NULL)
+    free (debug->external_dnr);
+  if (debug->external_pdr != NULL)
+    free (debug->external_pdr);
+  if (debug->external_sym != NULL)
+    free (debug->external_sym);
+  if (debug->external_opt != NULL)
+    free (debug->external_opt);
+  if (debug->external_aux != NULL)
+    free (debug->external_aux);
+  if (debug->ss != NULL)
+    free (debug->ss);
+  if (debug->ssext != NULL)
+    free (debug->ssext);
+  if (debug->external_fdr != NULL)
+    free (debug->external_fdr);
+  if (debug->external_rfd != NULL)
+    free (debug->external_rfd);
+  return false;
 }
 
 /* Get EXTR information for a symbol.  */
@@ -1301,10 +1334,6 @@ mips_elf_final_link (abfd, info)
 	      if (p->type != bfd_indirect_link_order)
 		continue;
 
-#ifndef alloca
-	      alloca (0);
-#endif
-
 	      input_section = p->u.indirect.section;
 	      input_bfd = input_section->owner;
 
@@ -1433,7 +1462,10 @@ mips_elf_final_link (abfd, info)
 	       p != (struct bfd_link_order *) NULL;
 	       p = p->next)
 	    {
-	      if (p->type == bfd_indirect_link_order)
+	      if (p->type == bfd_section_reloc_link_order
+		  || p->type == bfd_symbol_reloc_link_order)
+		++o->reloc_count;
+	      else if (p->type == bfd_indirect_link_order)
 		{
 		  asection *input_section;
 		  bfd *input_bfd;
@@ -1446,9 +1478,9 @@ mips_elf_final_link (abfd, info)
 		  relsize = bfd_get_reloc_upper_bound (input_bfd,
 						       input_section);
 		  relocs = (arelent **) malloc (relsize);
-		  if (!relocs)
+		  if (!relocs && relsize != 0)
 		    {
-		      bfd_error = no_memory;
+		      bfd_set_error (bfd_error_no_memory);
 		      return false;
 		    }
 		  reloc_count =
@@ -1468,9 +1500,10 @@ mips_elf_final_link (abfd, info)
 					    * sizeof (arelent *))));
 	      if (!o->orelocation)
 		{
-		  bfd_error = no_memory;
+		  bfd_set_error (bfd_error_no_memory);
 		  return false;
 		}
+	      o->flags |= SEC_RELOC;
 	      /* Reset the count so that it can be used as an index
 		 when putting in the output relocs.  */
 	      o->reloc_count = 0;
@@ -1515,8 +1548,18 @@ mips_elf_final_link (abfd, info)
 	   p != (struct bfd_link_order *) NULL;
 	   p = p->next)
 	{
-	  if (! _bfd_default_link_order (abfd, info, o, p))
-	    return false;
+	  switch (p->type)
+	    {
+	    case bfd_section_reloc_link_order:
+	    case bfd_symbol_reloc_link_order:
+	      if (! _bfd_generic_reloc_link_order (abfd, info, o, p))
+		return false;
+	      break;
+	    default:
+	      if (! _bfd_default_link_order (abfd, info, o, p))
+		return false;
+	      break;
+	    }
 	}
     }
 
@@ -1661,6 +1704,7 @@ static const struct ecoff_debug_swap mips_elf_ecoff_debug_swap =
 					mips_elf_final_write_processing
 #define elf_backend_ecoff_debug_swap	&mips_elf_ecoff_debug_swap
 
+#define bfd_elf32_bfd_link_add_symbols	_bfd_generic_link_add_symbols_collect
 #define bfd_elf32_bfd_final_link	mips_elf_final_link
 
 #include "elf32-target.h"
diff --git a/bfd/libecoff.h b/bfd/libecoff.h
index 74361c50757..f0f3a111be3 100644
--- a/bfd/libecoff.h
+++ b/bfd/libecoff.h
@@ -18,65 +18,56 @@ You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
-/* This is the backend information kept for ECOFF files.  This
-   structure is constant for a particular backend.  ECOFF already
-   keeps a bfd_coff_backend_data structure in the bfd_target
-   backend_data field, so a pointer to this backend structure is kept
-   in the tdata field.  */
+#include "bfdlink.h"
 
-#define ecoff_backend(abfd) (ecoff_data (abfd)->backend_data)
+#ifndef ECOFF_H
+#include "coff/ecoff.h"
+#endif
+
+/* This is the backend information kept for ECOFF files.  This
+   structure is constant for a particular backend.  The first element
+   is the COFF backend data structure, so that ECOFF targets can use
+   the generic COFF code.  */
+
+#define ecoff_backend(abfd) \
+  ((struct ecoff_backend_data *) (abfd)->xvec->backend_data)
 
 struct ecoff_backend_data
 {
+  /* COFF backend information.  This must be the first field.  */
+  bfd_coff_backend_data coff;
   /* Supported architecture.  */
   enum bfd_architecture arch;
-  /* Big endian magic number.  */
-  int big_magic;
-  /* Little endian magic number.  */
-  int little_magic;
-  /* Alignment of debugging information.  E.g., 4.  */
-  bfd_size_type debug_align;
+  /* Initial portion of armap string.  */
+  const char *armap_start;
   /* The page boundary used to align sections in a demand-paged
      executable file.  E.g., 0x1000.  */
   bfd_vma round;
+  /* True if the .rdata section is part of the text segment, as on the
+     Alpha.  False if .rdata is part of the data segment, as on the
+     MIPS.  */
+  boolean rdata_in_text;
   /* Bitsize of constructor entries.  */
   unsigned int constructor_bitsize;
-  /* Sizes of external symbolic information.  */
-  bfd_size_type external_hdr_size;
-  bfd_size_type external_dnr_size;
-  bfd_size_type external_pdr_size;
-  bfd_size_type external_sym_size;
-  bfd_size_type external_opt_size;
-  bfd_size_type external_fdr_size;
-  bfd_size_type external_rfd_size;
-  bfd_size_type external_ext_size;
-  /* Functions to swap in external symbolic data.  */
-  void (*swap_hdr_in) PARAMS ((bfd *, PTR, HDRR *));
-  void (*swap_dnr_in) PARAMS ((bfd *, PTR, DNR *));
-  void (*swap_pdr_in) PARAMS ((bfd *, PTR, PDR *));
-  void (*swap_sym_in) PARAMS ((bfd *, PTR, SYMR *));
-  void (*swap_opt_in) PARAMS ((bfd *, PTR, OPTR *));
-  void (*swap_fdr_in) PARAMS ((bfd *, PTR, FDR *));
-  void (*swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *));
-  void (*swap_ext_in) PARAMS ((bfd *, PTR, EXTR *));
-  /* Functions to swap out external symbolic data.  */
-  void (*swap_hdr_out) PARAMS ((bfd *, const HDRR *, PTR));
-  void (*swap_dnr_out) PARAMS ((bfd *, const DNR *, PTR));
-  void (*swap_pdr_out) PARAMS ((bfd *, const PDR *, PTR));
-  void (*swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR));
-  void (*swap_opt_out) PARAMS ((bfd *, const OPTR *, PTR));
-  void (*swap_fdr_out) PARAMS ((bfd *, const FDR *, PTR));
-  void (*swap_rfd_out) PARAMS ((bfd *, const RFDT *, PTR));
-  void (*swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR));
-  /* It so happens that the auxiliary type information has the same
-     type and format for all known ECOFF targets.  I don't see any
-     reason that that should change, so at least for now the auxiliary
-     swapping information is not in this table.  */
+  /* Reloc to use for constructor entries.  */
+  CONST struct reloc_howto_struct *constructor_reloc;
+  /* How to swap debugging information.  */
+  struct ecoff_debug_swap debug_swap;
   /* External reloc size.  */
   bfd_size_type external_reloc_size;
   /* Reloc swapping functions.  */
   void (*swap_reloc_in) PARAMS ((bfd *, PTR, struct internal_reloc *));
   void (*swap_reloc_out) PARAMS ((bfd *, const struct internal_reloc *, PTR));
+  /* Backend reloc tweaking.  */
+  void (*adjust_reloc_in) PARAMS ((bfd *, const struct internal_reloc *,
+				   arelent *));
+  void (*adjust_reloc_out) PARAMS ((bfd *, const arelent *,
+				    struct internal_reloc *));
+  /* Relocate section contents while linking.  */
+  boolean (*relocate_section) PARAMS ((bfd *output_bfd, struct bfd_link_info *,
+				       bfd *input_bfd, asection *input_section,
+				       bfd_byte *contents,
+				       PTR external_relocs));
 };
 
 /* This is the target specific information kept for ECOFF files.  */
@@ -85,9 +76,6 @@ struct ecoff_backend_data
 
 typedef struct ecoff_tdata
 {
-  /* Constant backend information.  */
-  const struct ecoff_backend_data *backend_data;
-
   /* The reloc file position, set by
      ecoff_compute_section_file_positions.  */
   file_ptr reloc_filepos;
@@ -109,42 +97,30 @@ typedef struct ecoff_tdata
   int gp_size;
 
   /* The register masks.  When linking, all the masks found in the
-     input files are combined into the masks of the output file.  */
+     input files are combined into the masks of the output file.
+     These are not all used for all targets, but that's OK, because
+     the relevant ones are the only ones swapped in and out.  */
   unsigned long gprmask;
+  unsigned long fprmask;
   unsigned long cprmask[4];
 
-  /* The size of the unswapped ECOFF symbolic information.  */
-  bfd_size_type raw_size;
+  /* The ECOFF symbolic debugging information.  */
+  struct ecoff_debug_info debug_info;
 
   /* The unswapped ECOFF symbolic information.  */
   PTR raw_syments;
 
-  /* The swapped ECOFF symbolic header.  */
-  HDRR symbolic_header;
-
-  /* Pointers to the unswapped symbolic information.  */
-  unsigned char *line;
-  PTR external_dnr;	/* struct dnr_ext */
-  PTR external_pdr;	/* struct pdr_ext */
-  PTR external_sym;	/* struct sym_ext */
-  PTR external_opt;	/* struct opt_ext */
-  union aux_ext *external_aux;
-  char *ss;
-  char *ssext;
-  PTR external_fdr;	/* struct fdr_ext */
-  PTR external_rfd;	/* struct rfd_ext */
-  PTR external_ext;	/* struct ext_ext */
-
-  /* The swapped FDR information.  */
-  FDR *fdr;
-
-  /* The FDR index.  This is set for an input BFD to a link so that
-     the external symbols can set their FDR index correctly.  */
-  unsigned int ifdbase;
-
   /* The canonical BFD symbols.  */
   struct ecoff_symbol_struct *canonical_symbols;
 
+  /* A mapping from external symbol numbers to entries in the linker
+     hash table, used when linking.  */
+  struct ecoff_link_hash_entry **sym_hashes;
+
+  /* A mapping from reloc symbol indices to sections, used when
+     linking.  */
+  asection **symndx_to_section;
+
 } ecoff_data_type;
 
 /* Each canonical asymbol really looks like this.  */
@@ -176,13 +152,71 @@ typedef struct ecoff_symbol_struct
 #define ecoff_get_sym_index(symbol) ((unsigned long) (symbol)->udata)
 #define ecoff_set_sym_index(symbol, idx) ((symbol)->udata = (PTR) (idx))
 
-/* Make an empty ECOFF symbol.  */
-extern asymbol *ecoff_make_empty_symbol PARAMS ((bfd *abfd));
+/* When generating MIPS embedded PIC code, the linker relaxes the code
+   to turn PC relative branches into longer code sequences when the PC
+   relative branch is out of range.  This involves reading the relocs
+   in bfd_relax_section as well as in bfd_final_link, and requires the
+   code to keep track of which relocs have been expanded.  A pointer
+   to this structure is put in the used_by_bfd pointer of a section to
+   keep track of this information.  The user_by_bfd pointer will be
+   NULL if the information was not needed.  */
+
+struct ecoff_section_tdata
+{
+  /* The unswapped relocs for this section.  These are stored in
+     memory so the input file does not have to be read twice.  */
+  PTR external_relocs;
+
+  /* The contents of the section.  These bytes may or may not be saved
+     in memory, but if it is this is a pointer to them.  */
+  bfd_byte *contents;
+
+  /* Offset adjustments for PC relative branches.  A number other than
+   1 is an addend for a PC relative branch; this addend arises because
+   it crosses one or more branches which were expanded into a larger
+   code sequence.  A 1 means that this branch was itself expanded into
+   a larger code sequence.  1 is not a possible offset, since all
+   offsets must be multiples of the instruction size, which is 4;
+   also, the only relocs with non-zero offsets will be PC relative
+   branches within the same object file.  If this field is NULL, no
+   branches were expanded and no offsets are required.  Otherwise
+   there are as many entries as there are relocs in the section, and
+   the entry for any reloc that is not PC relative is zero.  */
+  long *offsets;
+};
+
+/* An accessor macro for the ecoff_section_tdata structure.  */
+#define ecoff_section_data(abfd, sec) \
+  ((struct ecoff_section_tdata *) (sec)->used_by_bfd)
+
+/* ECOFF linker hash table entries.  */
+
+struct ecoff_link_hash_entry
+{
+  struct bfd_link_hash_entry root;
+  /* Symbol index in output file.  */
+  long indx;
+  /* BFD that ext field value came from.  */
+  bfd *abfd;
+  /* ECOFF external symbol information.  */
+  EXTR esym;
+};
+
+/* ECOFF linker hash table.  */
+
+struct ecoff_link_hash_table
+{
+  struct bfd_link_hash_table root;
+};
+
+/* Make an ECOFF object.  */
+extern boolean ecoff_mkobject PARAMS ((bfd *));
 
 /* Read in the ECOFF symbolic debugging information.  */
 extern boolean ecoff_slurp_symbolic_info PARAMS ((bfd *));
 
 /* Generic ECOFF BFD backend vectors.  */
+extern asymbol *ecoff_make_empty_symbol PARAMS ((bfd *abfd));
 extern unsigned int ecoff_get_symtab_upper_bound PARAMS ((bfd *abfd));
 extern unsigned int ecoff_get_symtab PARAMS ((bfd *abfd,
 					      asymbol **alocation));
@@ -196,8 +230,6 @@ extern unsigned int ecoff_canonicalize_reloc PARAMS ((bfd *abfd,
 						      asection *section,
 						      arelent **relptr,
 						      asymbol **symbols));
-extern CONST struct reloc_howto_struct *ecoff_bfd_reloc_type_lookup
-  PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
 extern boolean ecoff_find_nearest_line PARAMS ((bfd *abfd,
 						asection *section,
 						asymbol **symbols,
@@ -205,8 +237,6 @@ extern boolean ecoff_find_nearest_line PARAMS ((bfd *abfd,
 						CONST char **filename_ptr,
 						CONST char **fnname_ptr,
 						unsigned int *retline_ptr));
-extern boolean ecoff_bfd_seclet_link PARAMS ((bfd *abfd, PTR data,
-					      boolean relocateable));
 extern boolean ecoff_set_arch_mach PARAMS ((bfd *abfd,
 					    enum bfd_architecture arch,
 					    unsigned long machine));
@@ -216,6 +246,11 @@ extern boolean ecoff_set_section_contents PARAMS ((bfd *abfd,
 						   PTR location,
 						   file_ptr offset,
 						   bfd_size_type count));
+extern boolean ecoff_get_section_contents PARAMS ((bfd *abfd,
+						   asection *section,
+						   PTR location,
+						   file_ptr offset,
+						   bfd_size_type count));
 extern boolean ecoff_write_object_contents PARAMS ((bfd *abfd));
 extern boolean ecoff_slurp_armap PARAMS ((bfd *abfd));
 extern boolean ecoff_write_armap PARAMS ((bfd *abfd, unsigned int elength,
@@ -229,20 +264,34 @@ extern bfd_target *ecoff_archive_p PARAMS ((bfd *abfd));
 #define ecoff_truncate_arname		bfd_dont_truncate_arname
 #define ecoff_openr_next_archived_file	bfd_generic_openr_next_archived_file
 #define ecoff_generic_stat_arch_elt	bfd_generic_stat_arch_elt
-#define	ecoff_get_section_contents	bfd_generic_get_section_contents
 #define ecoff_get_reloc_upper_bound	coff_get_reloc_upper_bound
 #define	ecoff_close_and_cleanup		bfd_generic_close_and_cleanup
 #define ecoff_bfd_debug_info_start	bfd_void
 #define ecoff_bfd_debug_info_end	bfd_void
 #define ecoff_bfd_debug_info_accumulate	\
   ((void (*) PARAMS ((bfd *, struct sec *))) bfd_void)
-#define ecoff_bfd_get_relocated_section_contents \
-  bfd_generic_get_relocated_section_contents
-#define ecoff_bfd_relax_section		bfd_generic_relax_section
 #define ecoff_bfd_make_debug_symbol \
   ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr)
+extern struct bfd_link_hash_table *ecoff_bfd_link_hash_table_create
+  PARAMS ((bfd *));
+extern boolean ecoff_bfd_link_add_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+extern boolean ecoff_bfd_final_link PARAMS ((bfd *, struct bfd_link_info *));
+
+#ifndef ecoff_bfd_copy_private_section_data
+#define ecoff_bfd_copy_private_section_data \
+  ((boolean (*) PARAMS ((bfd *, asection *, bfd *, asection *))) bfd_true)
+#endif
+#ifndef ecoff_bfd_copy_private_bfd_data
+#define ecoff_bfd_copy_private_bfd_data \
+  ((boolean (*) PARAMS ((bfd *, bfd *))) bfd_true)
+#endif
+#ifndef ecoff_bfd_is_local_label
+#define ecoff_bfd_is_local_label bfd_generic_is_local_label
+#endif
 
 /* Hook functions for the generic COFF section reading code.  */
+extern PTR ecoff_mkobject_hook PARAMS ((bfd *, PTR filehdr, PTR aouthdr));
 extern asection *ecoff_make_section_hook PARAMS ((bfd *abfd, char *name));
 extern boolean ecoff_new_section_hook PARAMS ((bfd *abfd,
 					       asection *section));