* write.c (md_register_arithmetic): Define.
[binutils-gdb.git] / gas / write.c
index 46549b834d538a6c5dc28ede02a705eb3cc1f1c4..f03fcf3d5b33968957477579108f68f8619b51aa 100644 (file)
@@ -1,13 +1,13 @@
 /* write.c - emit .o file
    Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
   (! SEG_NORMAL (SEG))
 #endif
 
+#ifndef md_register_arithmetic
+# define md_register_arithmetic 1
+#endif
+
 #ifndef TC_FORCE_RELOCATION_SUB_ABS
-#define TC_FORCE_RELOCATION_SUB_ABS(FIX)       0
+#define TC_FORCE_RELOCATION_SUB_ABS(FIX, SEG)  \
+  (!md_register_arithmetic && (SEG) == reg_section)
 #endif
 
 #ifndef TC_FORCE_RELOCATION_SUB_LOCAL
@@ -117,6 +122,9 @@ symbolS *abs_section_sym;
 /* Remember the value of dot when parsing expressions.  */
 addressT dot_value;
 
+/* Relocs generated by ".reloc" pseudo.  */
+struct reloc_list* reloc_list;
+
 void print_fixup (fixS *);
 
 /* We generally attach relocs to frag chains.  However, after we have
@@ -553,11 +561,7 @@ size_seg (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   if (size > 0 && ! seginfo->bss)
     flags |= SEC_HAS_CONTENTS;
 
-  /* @@ This is just an approximation.  */
-  if (seginfo && seginfo->fix_root)
-    flags |= SEC_RELOC;
-  else
-    flags &= ~SEC_RELOC;
+  flags &= ~SEC_RELOC;
   x = bfd_set_section_flags (abfd, sec, flags);
   assert (x);
 
@@ -628,6 +632,86 @@ dump_section_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, FILE *stream)
 #define EMIT_SECTION_SYMBOLS 1
 #endif
 
+/* Resolve U.A.OFFSET_SYM and U.A.SYM fields of RELOC_LIST entries,
+   and check for validity.  Convert RELOC_LIST from using U.A fields
+   to U.B fields.  */
+static void
+resolve_reloc_expr_symbols (void)
+{
+  struct reloc_list *r;
+
+  for (r = reloc_list; r; r = r->next)
+    {
+      expressionS *symval;
+      symbolS *sym;
+      bfd_vma offset, addend;
+      asection *sec;
+      reloc_howto_type *howto;
+
+      resolve_symbol_value (r->u.a.offset_sym);
+      symval = symbol_get_value_expression (r->u.a.offset_sym);
+
+      offset = 0;
+      sym = NULL;
+      if (symval->X_op == O_constant)
+       sym = r->u.a.offset_sym;
+      else if (symval->X_op == O_symbol)
+       {
+         sym = symval->X_add_symbol;
+         offset = symval->X_add_number;
+         symval = symbol_get_value_expression (symval->X_add_symbol);
+       }
+      if (sym == NULL
+         || symval->X_op != O_constant
+         || (sec = S_GET_SEGMENT (sym)) == NULL
+         || !SEG_NORMAL (sec))
+       {
+         as_bad_where (r->file, r->line, _("invalid offset expression"));
+         sec = NULL;
+       }
+      else
+       offset += S_GET_VALUE (sym);
+
+      sym = NULL;
+      addend = r->u.a.addend;
+      if (r->u.a.sym != NULL)
+       {
+         resolve_symbol_value (r->u.a.sym);
+         symval = symbol_get_value_expression (r->u.a.sym);
+         if (symval->X_op == O_constant)
+           sym = r->u.a.sym;
+         else if (symval->X_op == O_symbol)
+           {
+             sym = symval->X_add_symbol;
+             addend += symval->X_add_number;
+             symval = symbol_get_value_expression (symval->X_add_symbol);
+           }
+         if (symval->X_op != O_constant)
+           {
+             as_bad_where (r->file, r->line, _("invalid reloc expression"));
+             sec = NULL;
+           }
+         else if (sym != NULL)
+           symbol_mark_used_in_reloc (sym);
+       }
+      if (sym == NULL)
+       {
+         if (abs_section_sym == NULL)
+           abs_section_sym = section_symbol (absolute_section);
+         sym = abs_section_sym;
+       }
+
+      howto = r->u.a.howto;
+
+      r->u.b.sec = sec;
+      r->u.b.s = symbol_get_bfdsym (sym);
+      r->u.b.r.sym_ptr_ptr = &r->u.b.s;
+      r->u.b.r.address = offset;
+      r->u.b.r.addend = addend;
+      r->u.b.r.howto = howto;
+    }
+}
+
 /* This pass over fixups decides whether symbols can be replaced with
    section symbols.  */
 
@@ -845,7 +929,7 @@ fixup_segment (fixS *fixP, segT this_segment)
 #endif
            }
          else if (sub_symbol_segment == absolute_section
-                  && !TC_FORCE_RELOCATION_SUB_ABS (fixP))
+                  && !TC_FORCE_RELOCATION_SUB_ABS (fixP, add_symbol_segment))
            {
              add_number -= S_GET_VALUE (fixP->fx_subsy);
              fixP->fx_offset = add_number;
@@ -876,12 +960,18 @@ fixup_segment (fixS *fixP, segT this_segment)
            }
          else if (!TC_VALIDATE_FIX_SUB (fixP))
            {
-             as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("can't resolve `%s' {%s section} - `%s' {%s section}"),
-                           fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
-                           segment_name (add_symbol_segment),
-                           S_GET_NAME (fixP->fx_subsy),
-                           segment_name (sub_symbol_segment));
+             if (!md_register_arithmetic
+                 && (add_symbol_segment == reg_section
+                     || sub_symbol_segment == reg_section))
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("register value used as expression"));
+             else
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("can't resolve `%s' {%s section} - `%s' {%s section}"),
+                             fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
+                             segment_name (add_symbol_segment),
+                             S_GET_NAME (fixP->fx_subsy),
+                             segment_name (sub_symbol_segment));
            }
        }
 
@@ -999,15 +1089,50 @@ fix_segment (bfd *abfd ATTRIBUTE_UNUSED,
   fixup_segment (seginfo->fix_root, sec);
 }
 
+static void
+install_reloc (asection *sec, arelent *reloc, fragS *fragp,
+              char *file, unsigned int line)
+{
+  char *err;
+  bfd_reloc_status_type s;
+  asymbol *sym;
+
+  if (reloc->sym_ptr_ptr != NULL
+      && (sym = *reloc->sym_ptr_ptr) != NULL
+      && (sym->flags & BSF_KEEP) == 0
+      && ((sym->flags & BSF_SECTION_SYM) == 0
+         || (EMIT_SECTION_SYMBOLS
+             && !bfd_is_abs_section (sym->section))))
+    as_bad_where (file, line, _("redefined symbol cannot be used on reloc"));
+
+  s = bfd_install_relocation (stdoutput, reloc,
+                             fragp->fr_literal, fragp->fr_address,
+                             sec, &err);
+  switch (s)
+    {
+    case bfd_reloc_ok:
+      break;
+    case bfd_reloc_overflow:
+      as_bad_where (file, line, _("relocation overflow"));
+      break;
+    case bfd_reloc_outofrange:
+      as_bad_where (file, line, _("relocation out of range"));
+      break;
+    default:
+      as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
+               file, line, s);
+    }
+}
+
 static void
 write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
 {
   segment_info_type *seginfo = seg_info (sec);
   unsigned int i;
   unsigned int n;
+  struct reloc_list *my_reloc_list, **rp, *r;
   arelent **relocs;
   fixS *fixp;
-  char *err;
 
   /* If seginfo is NULL, we did not create this section; don't do
      anything with it.  */
@@ -1016,129 +1141,73 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
 
   n = 0;
   for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
-    n++;
+    if (!fixp->fx_done)
+      n++;
 
-#ifndef RELOC_EXPANSION_POSSIBLE
-  /* Set up reloc information as well.  */
-  relocs = xcalloc (n, sizeof (arelent *));
+#ifdef RELOC_EXPANSION_POSSIBLE
+  n *= MAX_RELOC_EXPANSION;
+#endif
 
-  i = 0;
-  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+  /* Extract relocs for this section from reloc_list.  */
+  rp = &reloc_list;
+  my_reloc_list = NULL;
+  while ((r = *rp) != NULL)
     {
-      arelent *reloc;
-      bfd_reloc_status_type s;
-      int fx_size, slack;
-      offsetT loc;
-
-      if (fixp->fx_done)
-       {
-         n--;
-         continue;
-       }
-
-      reloc = tc_gen_reloc (sec, fixp);
-      if (!reloc)
+      if (r->u.b.sec == sec)
        {
-         n--;
-         continue;
+         *rp = r->next;
+         r->next = my_reloc_list;
+         my_reloc_list = r;
+         n++;
        }
-
-      fx_size = fixp->fx_size;
-      slack = TC_FX_SIZE_SLACK (fixp);
-      if (slack > 0)
-       fx_size = fx_size > slack ? fx_size - slack : 0;
-      loc = fixp->fx_where + fx_size;
-      if (slack >= 0
-         && loc > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
-       as_bad_where (fixp->fx_file, fixp->fx_line,
-                     _("internal error: fixup not contained within frag"));
-
-      s = bfd_install_relocation (stdoutput, reloc,
-                                 fixp->fx_frag->fr_literal,
-                                 fixp->fx_frag->fr_address,
-                                 sec, &err);
-      switch (s)
-       {
-       case bfd_reloc_ok:
-         break;
-       case bfd_reloc_overflow:
-         as_bad_where (fixp->fx_file, fixp->fx_line,
-                       _("relocation overflow"));
-         break;
-       case bfd_reloc_outofrange:
-         as_bad_where (fixp->fx_file, fixp->fx_line,
-                       _("relocation out of range"));
-         break;
-       default:
-         as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
-                   fixp->fx_file, fixp->fx_line, s);
-       }
-      relocs[i++] = reloc;
+      else
+       rp = &r->next;
     }
-#else
-  n = n * MAX_RELOC_EXPANSION;
-  /* Set up reloc information as well.  */
+
   relocs = xcalloc (n, sizeof (arelent *));
 
   i = 0;
   for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
     {
-      arelent **reloc;
-      bfd_reloc_status_type s;
       int j;
       int fx_size, slack;
       offsetT loc;
 
       if (fixp->fx_done)
-       {
-         n--;
-         continue;
-       }
-
-      reloc = tc_gen_reloc (sec, fixp);
-
-      for (j = 0; reloc[j]; j++)
-       {
-         relocs[i++] = reloc[j];
-         assert (i <= n);
-       }
+       continue;
 
       fx_size = fixp->fx_size;
       slack = TC_FX_SIZE_SLACK (fixp);
       if (slack > 0)
        fx_size = fx_size > slack ? fx_size - slack : 0;
       loc = fixp->fx_where + fx_size;
-      if (slack >= 0
-         && loc > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
+      if (slack >= 0 && loc > fixp->fx_frag->fr_fix)
        as_bad_where (fixp->fx_file, fixp->fx_line,
                      _("internal error: fixup not contained within frag"));
 
-      for (j = 0; reloc[j]; j++)
-       {
-         s = bfd_install_relocation (stdoutput, reloc[j],
-                                     fixp->fx_frag->fr_literal,
-                                     fixp->fx_frag->fr_address,
-                                     sec, &err);
-         switch (s)
-           {
-           case bfd_reloc_ok:
-             break;
-           case bfd_reloc_overflow:
-             as_bad_where (fixp->fx_file, fixp->fx_line,
-                           _("relocation overflow"));
-             break;
-           case bfd_reloc_outofrange:
-             as_bad_where (fixp->fx_file, fixp->fx_line,
-                           _("relocation out of range"));
-             break;
-           default:
-             as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
-                       fixp->fx_file, fixp->fx_line, s);
-           }
-       }
+#ifndef RELOC_EXPANSION_POSSIBLE
+      {
+       arelent *reloc = tc_gen_reloc (sec, fixp);
+
+       if (!reloc)
+         continue;
+       relocs[i++] = reloc;
+       j = 1;
+      }
+#else
+      {
+       arelent **reloc = tc_gen_reloc (sec, fixp);
+
+       for (j = 0; reloc[j]; j++)
+         relocs[i++] = reloc[j];
+      }
+#endif
+
+      for ( ; j != 0; --j)
+       install_reloc (sec, relocs[i - j], fixp->fx_frag,
+                      fixp->fx_file, fixp->fx_line);
     }
   n = i;
-#endif
 
 #ifdef DEBUG4
   {
@@ -1158,12 +1227,30 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   }
 #endif
 
+  for (r = my_reloc_list; r != NULL; r = r->next)
+    {
+      fragS *f;
+      for (f = seginfo->frchainP->frch_root; f; f = f->fr_next)
+       if (f->fr_address <= r->u.b.r.address
+           && r->u.b.r.address < f->fr_address + f->fr_fix)
+         break;
+      if (f == NULL)
+       as_bad_where (r->file, r->line,
+                     _("reloc not within (fixed part of) section"));
+      else
+       {
+         relocs[n++] = &r->u.b.r;
+         install_reloc (sec, &r->u.b.r, f, r->file, r->line);
+       }
+    }
+
   if (n)
-    bfd_set_reloc (stdoutput, sec, relocs, n);
-  else
-    bfd_set_section_flags (abfd, sec,
-                          (bfd_get_section_flags (abfd, sec)
-                           & (flagword) ~SEC_RELOC));
+    {
+      flagword flags = bfd_get_section_flags (abfd, sec);
+      flags |= SEC_RELOC;
+      bfd_set_section_flags (abfd, sec, flags);
+      bfd_set_reloc (stdoutput, sec, relocs, n);
+    }
 
 #ifdef SET_SECTION_RELOCS
   SET_SECTION_RELOCS (sec, relocs, n);
@@ -1310,6 +1397,10 @@ set_symtab (void)
       for (i = 0; i < nsyms; i++, symp = symbol_next (symp))
        {
          asympp[i] = symbol_get_bfdsym (symp);
+         if (asympp[i]->flags != BSF_SECTION_SYM
+             || !(bfd_is_const_section (asympp[i]->section)
+                  && asympp[i]->section->symbol == asympp[i]))
+           asympp[i]->flags |= BSF_KEEP;
          symbol_mark_written (symp);
        }
     }
@@ -1614,6 +1705,7 @@ write_object_file (void)
        resolve_symbol_value (symp);
     }
   resolve_local_symbol_values ();
+  resolve_reloc_expr_symbols ();
 
   PROGRESS (1);
 
@@ -1695,6 +1787,13 @@ write_object_file (void)
                  as_bad (_("Local symbol `%s' can't be equated to common symbol `%s'"),
                          name, S_GET_NAME (e->X_add_symbol));
                }
+             if (S_GET_SEGMENT (symp) == reg_section)
+               {
+                 /* Report error only if we know the symbol name.  */
+                 if (S_GET_NAME (symp) != reg_section->name)
+                   as_bad (_("can't make global register symbol `%s'"),
+                           name);
+               }
              symbol_remove (symp, &symbol_rootP, &symbol_lastP);
              continue;
            }
@@ -1762,6 +1861,10 @@ write_object_file (void)
   obj_adjust_symtab ();
 #endif
 
+  /* Stop if there is an error.  */
+  if (had_errors ())
+    return;
+
   /* Now that all the sizes are known, and contents correct, we can
      start writing to the file.  */
   set_symtab ();
@@ -2010,13 +2113,38 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
   /* Do relax().  */
   {
     unsigned long max_iterations;
-    offsetT stretch;   /* May be any size, 0 or negative.  */
-    /* Cumulative number of addresses we have relaxed this pass.
-       We may have relaxed more than one address.  */
-    int stretched;     /* Have we stretched on this pass?  */
-    /* This is 'cuz stretch may be zero, when, in fact some piece of code
-       grew, and another shrank.  If a branch instruction doesn't fit anymore,
-       we could be scrod.  */
+
+    /* Cumulative address adjustment.  */
+    offsetT stretch;
+
+    /* Have we made any adjustment this pass?  We can't just test
+       stretch because one piece of code may have grown and another
+       shrank.  */
+    int stretched;
+
+    /* Most horrible, but gcc may give us some exception data that
+       is impossible to assemble, of the form
+
+       .align 4
+       .byte 0, 0
+       .uleb128 end - start
+       start:
+       .space 128*128 - 1
+       .align 4
+       end:
+
+       If the leb128 is two bytes in size, then end-start is 128*128,
+       which requires a three byte leb128.  If the leb128 is three
+       bytes in size, then end-start is 128*128-1, which requires a
+       two byte leb128.  We work around this dilemma by inserting
+       an extra 4 bytes of alignment just after the .align.  This
+       works because the data after the align is accessed relative to
+       the end label.
+
+       This counter is used in a tiny state machine to detect
+       whether a leb128 followed by an align is impossible to
+       relax.  */
+    int rs_leb128_fudge = 0;
 
     /* We want to prevent going into an infinite loop where one frag grows
        depending upon the location of a symbol which is in turn moved by
@@ -2139,6 +2267,49 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                    }
 
                  growth = newoff - oldoff;
+
+                 /* If this align happens to follow a leb128 and
+                    we have determined that the leb128 is bouncing
+                    in size, then break the cycle by inserting an
+                    extra alignment.  */
+                 if (growth < 0
+                     && (rs_leb128_fudge & 16) != 0
+                     && (rs_leb128_fudge & 15) >= 2)
+                   {
+                     segment_info_type *seginfo = seg_info (segment);
+                     struct obstack *ob = &seginfo->frchainP->frch_obstack;
+                     struct frag *newf;
+
+                     newf = frag_alloc (ob);
+                     obstack_blank_fast (ob, fragP->fr_var);
+                     obstack_finish (ob);
+                     memcpy (newf, fragP, SIZEOF_STRUCT_FRAG);
+                     memcpy (newf->fr_literal,
+                             fragP->fr_literal + fragP->fr_fix,
+                             fragP->fr_var);
+                     newf->fr_type = rs_fill;
+                     newf->fr_fix = 0;
+                     newf->fr_offset = (((offsetT) 1 << fragP->fr_offset)
+                                        / fragP->fr_var);
+                     if (newf->fr_offset * newf->fr_var
+                         != (offsetT) 1 << fragP->fr_offset)
+                       {
+                         newf->fr_offset = (offsetT) 1 << fragP->fr_offset;
+                         newf->fr_var = 1;
+                       }
+                     /* Include growth of new frag, because rs_fill
+                        frags don't normally grow.  */
+                     growth += newf->fr_offset * newf->fr_var;
+                     /* The new frag address is newoff.  Adjust this
+                        for the amount we'll add when we process the
+                        new frag.  */
+                     newf->fr_address = newoff - stretch - growth;
+                     newf->relax_marker ^= 1;
+                     fragP->fr_next = newf;
+#ifdef DEBUG
+                     as_warn (_("padding added"));
+#endif
+                   }
                }
                break;
 
@@ -2158,7 +2329,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                    }
 
                  know (fragP->fr_next);
-                 after = fragP->fr_next->fr_address;
+                 after = fragP->fr_next->fr_address + stretch;
                  growth = target - after;
                  if (growth < 0)
                    {
@@ -2193,14 +2364,10 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                      fragP->fr_type = rs_align;
                      fragP->fr_subtype = 0;
                      fragP->fr_offset = 0;
-                     fragP->fr_fix = after - was_address;
-                     break;
+                     fragP->fr_fix = after - address;
                    }
-
-                 /* This is an absolute growth factor  */
-                 growth -= stretch;
-                 break;
                }
+               break;
 
              case rs_space:
                growth = 0;
@@ -2278,8 +2445,23 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
              {
                stretch += growth;
                stretched = 1;
+               if (fragP->fr_type == rs_leb128)
+                 rs_leb128_fudge += 16;
+               else if (fragP->fr_type == rs_align
+                        && (rs_leb128_fudge & 16) != 0
+                        && stretch == 0)
+                 rs_leb128_fudge += 16;
+               else
+                 rs_leb128_fudge = 0;
              }
          }
+
+       if (stretch == 0
+           && (rs_leb128_fudge & 16) == 0
+           && (rs_leb128_fudge & -16) != 0)
+         rs_leb128_fudge += 1;
+       else
+         rs_leb128_fudge = 0;
       }
     /* Until nothing further to relax.  */
     while (stretched && -- max_iterations);
@@ -2335,7 +2517,9 @@ void
 print_fixup (fixS *fixp)
 {
   indent_level = 1;
-  fprintf (stderr, "fix %lx %s:%d", (long) fixp, fixp->fx_file, fixp->fx_line);
+  fprintf (stderr, "fix ");
+  fprintf_vma (stderr, (bfd_vma)((bfd_hostptr_t) fixp));
+  fprintf (stderr, " %s:%d",fixp->fx_file, fixp->fx_line);
   if (fixp->fx_pcrel)
     fprintf (stderr, " pcrel");
   if (fixp->fx_pcrel_adjust)
@@ -2352,9 +2536,12 @@ print_fixup (fixS *fixp)
     fprintf (stderr, " tcbit");
   if (fixp->fx_done)
     fprintf (stderr, " done");
-  fprintf (stderr, "\n    size=%d frag=%lx where=%ld offset=%lx addnumber=%lx",
-          fixp->fx_size, (long) fixp->fx_frag, (long) fixp->fx_where,
-          (long) fixp->fx_offset, (long) fixp->fx_addnumber);
+  fprintf (stderr, "\n    size=%d frag=", fixp->fx_size);
+  fprintf_vma (stderr, (bfd_vma) ((bfd_hostptr_t) fixp->fx_frag));
+  fprintf (stderr, " where=%ld offset=%lx addnumber=%lx",
+          (long) fixp->fx_where,
+          (unsigned long) fixp->fx_offset,
+          (unsigned long) fixp->fx_addnumber);
   fprintf (stderr, "\n    %s (%d)", bfd_get_reloc_code_name (fixp->fx_r_type),
           fixp->fx_r_type);
   if (fixp->fx_addsy)