PR 10173
[deliverable/binutils-gdb.git] / gas / write.c
index df858a3c5bd53baeeb7ff3111fc0efb9835f58b8..9d681f2687473a8d3c6230314518fa1446929605 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
 #ifdef DIFF_EXPR_OK
-#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX)     0
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG)        \
+  (!md_register_arithmetic && (SEG) == reg_section)
 #else
-#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX)     1
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG)        1
 #endif
 #endif
 
 #ifndef TC_VALIDATE_FIX_SUB
 #ifdef UNDEFINED_DIFFERENCE_OK
 /* The PA needs this for PIC code generation.  */
-#define TC_VALIDATE_FIX_SUB(FIX) 1
+#define TC_VALIDATE_FIX_SUB(FIX, SEG)                  \
+  (md_register_arithmetic || (SEG) != reg_section)
 #else
-#define TC_VALIDATE_FIX_SUB(FIX)               \
-  ((FIX)->fx_r_type == BFD_RELOC_GPREL32       \
-   || (FIX)->fx_r_type == BFD_RELOC_GPREL16)
+#define TC_VALIDATE_FIX_SUB(FIX, SEG)                  \
+  ((md_register_arithmetic || (SEG) != reg_section)    \
+   && ((FIX)->fx_r_type == BFD_RELOC_GPREL32           \
+       || (FIX)->fx_r_type == BFD_RELOC_GPREL16))
 #endif
 #endif
 
@@ -117,6 +125,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
@@ -624,6 +635,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.  */
 
@@ -841,14 +932,14 @@ 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;
              fixP->fx_subsy = NULL;
            }
          else if (sub_symbol_segment == this_segment
-                  && !TC_FORCE_RELOCATION_SUB_LOCAL (fixP))
+                  && !TC_FORCE_RELOCATION_SUB_LOCAL (fixP, add_symbol_segment))
            {
              add_number -= S_GET_VALUE (fixP->fx_subsy);
              fixP->fx_offset = (add_number + fixP->fx_dot_value
@@ -870,14 +961,20 @@ fixup_segment (fixS *fixP, segT this_segment)
              fixP->fx_subsy = NULL;
              fixP->fx_pcrel = 1;
            }
-         else if (!TC_VALIDATE_FIX_SUB (fixP))
+         else if (!TC_VALIDATE_FIX_SUB (fixP, add_symbol_segment))
            {
-             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));
            }
        }
 
@@ -1001,6 +1098,15 @@ install_reloc (asection *sec, arelent *reloc, fragS *fragp,
 {
   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,
@@ -1027,6 +1133,7 @@ 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;
 
@@ -1044,6 +1151,22 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   n *= MAX_RELOC_EXPANSION;
 #endif
 
+  /* Extract relocs for this section from reloc_list.  */
+  rp = &reloc_list;
+  my_reloc_list = NULL;
+  while ((r = *rp) != NULL)
+    {
+      if (r->u.b.sec == sec)
+       {
+         *rp = r->next;
+         r->next = my_reloc_list;
+         my_reloc_list = r;
+         n++;
+       }
+      else
+       rp = &r->next;
+    }
+
   relocs = xcalloc (n, sizeof (arelent *));
 
   i = 0;
@@ -1107,6 +1230,23 @@ 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)
     {
       flagword flags = bfd_get_section_flags (abfd, sec);
@@ -1260,6 +1400,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);
        }
     }
@@ -1527,18 +1671,22 @@ write_object_file (void)
            if (lie->added == 2)
              continue;
            /* Patch the jump table.  */
-           /* This is the offset from ??? to table_ptr+0.  */
-           to_addr = table_addr - S_GET_VALUE (lie->sub);
-#ifdef TC_CHECK_ADJUSTED_BROKEN_DOT_WORD
-           TC_CHECK_ADJUSTED_BROKEN_DOT_WORD (to_addr, lie);
-#endif
-           md_number_to_chars (lie->word_goes_here, to_addr, 2);
-           for (untruth = lie->next_broken_word;
+           for (untruth = (struct broken_word *) (fragP->fr_symbol);
                 untruth && untruth->dispfrag == fragP;
                 untruth = untruth->next_broken_word)
              {
                if (untruth->use_jump == lie)
-                 md_number_to_chars (untruth->word_goes_here, to_addr, 2);
+                 {
+                   /* This is the offset from ??? to table_ptr+0.
+                      The target is the same for all users of this
+                      md_long_jump, but the "sub" bases (and hence the
+                      offsets) may be different.  */
+                   addressT to_word = table_addr - S_GET_VALUE (untruth->sub);
+#ifdef TC_CHECK_ADJUSTED_BROKEN_DOT_WORD
+                   TC_CHECK_ADJUSTED_BROKEN_DOT_WORD (to_word, untruth);
+#endif
+                   md_number_to_chars (untruth->word_goes_here, to_word, 2);
+                 }
              }
 
            /* Install the long jump.  */
@@ -1564,6 +1712,7 @@ write_object_file (void)
        resolve_symbol_value (symp);
     }
   resolve_local_symbol_values ();
+  resolve_reloc_expr_symbols ();
 
   PROGRESS (1);
 
@@ -1645,6 +1794,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;
            }
@@ -1712,6 +1868,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 ();
@@ -2070,13 +2230,17 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                                             S_GET_NAME (lie->sub),
                                             buf);
                            }
-                         lie->added = 1;
                          if (fragP->fr_subtype == 0)
                            {
                              fragP->fr_subtype++;
                              growth += md_short_jump_size;
                            }
-                         for (untruth = lie->next_broken_word;
+
+                         /* Redirect *all* words of this table with the same
+                            target, lest we have to handle the case where the
+                            same target but with a offset that fits on this
+                            round overflows at the next relaxation round.  */
+                         for (untruth = (struct broken_word *) (fragP->fr_symbol);
                               untruth && untruth->dispfrag == lie->dispfrag;
                               untruth = untruth->next_broken_word)
                            if ((symbol_get_frag (untruth->add)
@@ -2087,6 +2251,8 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                                untruth->added = 2;
                                untruth->use_jump = lie;
                              }
+
+                         lie->added = 1;
                          growth += md_long_jump_size;
                        }
                    }
@@ -2176,7 +2342,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)
                    {
@@ -2211,14 +2377,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;
@@ -2368,7 +2530,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)
@@ -2385,9 +2549,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)
This page took 0.029008 seconds and 4 git commands to generate.