X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fwrite.c;h=f03fcf3d5b33968957477579108f68f8619b51aa;hb=9a97a5d735f8ccba73271e5098e6194551c877d7;hp=46549b834d538a6c5dc28ede02a705eb3cc1f1c4;hpb=d31f0f6d41caaba6ef79e5ebf7f135f9fa3647ac;p=binutils-gdb.git diff --git a/gas/write.c b/gas/write.c index 46549b834d5..f03fcf3d5b3 100644 --- a/gas/write.c +++ b/gas/write.c @@ -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, @@ -54,8 +54,13 @@ (! 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)