Changes to support linker relaxing of embedded MIPS PIC code to
authorIan Lance Taylor <ian@airs.com>
Fri, 25 Mar 1994 22:37:55 +0000 (22:37 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 25 Mar 1994 22:37:55 +0000 (22:37 +0000)
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
bfd/coff-alpha.c
bfd/coff-mips.c
bfd/ecoff.c
bfd/ecofflink.c
bfd/elf32-mips.c
bfd/libecoff.h

index 528f826..7e657fd 100644 (file)
@@ -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.
index 64ab840..4353078 100644 (file)
@@ -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 */
index a9a95e5..71a0cd5 100644 (file)
@@ -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));
 \f
 /* 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)
 \f
 /* 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;
 }
 \f
+/* 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;
+}
+\f
 /* 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
index 745d46a..5665a99 100644 (file)
@@ -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);
-
-  /* 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;
+  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
+        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);
-
-  /* Get the section contents.  */
-  input_size = bfd_section_size (input_bfd, input_section);
-  contents = (bfd_byte *) malloc (input_size);
-  if (contents == NULL)
+  BFD_ASSERT (cooked_size == link_order->size);
+
+  /* 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)
+
+  if (section_tdata != (struct ecoff_section_tdata *) NULL)
+    external_relocs = section_tdata->external_relocs;
+  else
     {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
-    }
+      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;
+      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;
+}
index a4507ea..4b14dcd 100644 (file)
@@ -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;
 }
index 57734bc..15debf8 100644 (file)
@@ -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"
index 74361c5..f0f3a11 100644 (file)
@@ -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.  */
 
+#include "bfdlink.h"
+
+#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.  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.  */
+   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) (ecoff_data (abfd)->backend_data)
+#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));