diff options
Diffstat (limited to 'gas')
-rw-r--r-- | gas/ChangeLog | 14 | ||||
-rw-r--r-- | gas/config/tc-mips.c | 172 | ||||
-rw-r--r-- | gas/config/tc-mips.h | 4 |
3 files changed, 168 insertions, 22 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 763d0b5..d2d1a05 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,17 @@ +Thu Mar 28 15:27:47 1996 Ian Lance Taylor <ian@cygnus.com> + + * config/tc-mips.h (tc_frob_file): Define. + (mips_frob_file): Declare. + * config/tc-mips.c (struct mips_hi_fixup): Define. + (mips_hi_fixup_list): New static variable. + (imm_unmatched_hi): New static variable. + (md_assemble): Clear imm_reloc, imm_unmatched_hi, and + offset_reloc. Pass imm_unmatched_hi to append_insn. + (append_insn): Add unmatched_hi parameter. If it is set, add the + new fixup to mips_hi_fixup_list. Change all callers. + (mips_ip): Set imm_unmatched_hi when appropriate. + (mips_frob_file): New function. + Thu Mar 28 11:47:59 1996 Doug Evans <dje@canuck.cygnus.com> * configure.in (sparc-*-solaris2*): Renamed from sparc*-*-solaris2*. diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index da69803..19b271d 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -292,6 +292,31 @@ static int prev_insn_unreordered; /* Non-zero if the previous previous instruction was in a .set noreorder. */ static int prev_prev_insn_unreordered; + +/* For ECOFF and ELF, relocations against symbols are done in two + parts, with a HI relocation and a LO relocation. Each relocation + has only 16 bits of space to store an addend. This means that in + order for the linker to handle carries correctly, it must be able + to locate both the HI and the LO relocation. This means that the + relocations must appear in order in the relocation table. + + In order to implement this, we keep track of each unmatched HI + relocation. We then sort them so that they immediately precede the + corresponding LO relocation. */ + +struct mips_hi_fixup +{ + /* Next HI fixup. */ + struct mips_hi_fixup *next; + /* This fixup. */ + fixS *fixp; + /* The section this fixup is in. */ + segT seg; +}; + +/* The list of unmatched HI relocs. */ + +static struct mips_hi_fixup *mips_hi_fixup_list; /* Since the MIPS does not have multiple forms of PC relative instructions, we do not have to do relaxing as is done on other @@ -384,7 +409,8 @@ static int reg_needs_delay PARAMS ((int)); static void append_insn PARAMS ((char *place, struct mips_cl_insn * ip, expressionS * p, - bfd_reloc_code_real_type r)); + bfd_reloc_code_real_type r, + boolean)); static void mips_no_prev_insn PARAMS ((void)); static void mips_emit_delays PARAMS ((void)); #ifdef USE_STDARG @@ -519,11 +545,21 @@ mips_pop_insert () static char *expr_end; +/* Expressions which appear in instructions. These are set by + mips_ip. */ + static expressionS imm_expr; static expressionS offset_expr; + +/* Relocs associated with imm_expr and offset_expr. */ + static bfd_reloc_code_real_type imm_reloc; static bfd_reloc_code_real_type offset_reloc; +/* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc. */ + +static boolean imm_unmatched_hi; + /* * This function is called once, at assembler startup time. It should * set up all the tables, etc. that the MD part of the assembler will need. @@ -775,7 +811,10 @@ md_assemble (str) struct mips_cl_insn insn; imm_expr.X_op = O_absent; + imm_reloc = BFD_RELOC_UNUSED; + imm_unmatched_hi = false; offset_expr.X_op = O_absent; + offset_reloc = BFD_RELOC_UNUSED; mips_ip (str, &insn); if (insn_error) @@ -790,11 +829,12 @@ md_assemble (str) else { if (imm_expr.X_op != O_absent) - append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc); + append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc, + imm_unmatched_hi); else if (offset_expr.X_op != O_absent) - append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc); + append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false); else - append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED); + append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false); } } @@ -876,11 +916,12 @@ reg_needs_delay (reg) used with RELOC_TYPE. */ static void -append_insn (place, ip, address_expr, reloc_type) +append_insn (place, ip, address_expr, reloc_type, unmatched_hi) char *place; struct mips_cl_insn *ip; expressionS *address_expr; bfd_reloc_code_real_type reloc_type; + boolean unmatched_hi; { register unsigned long prev_pinfo, pinfo; char *f; @@ -1112,10 +1153,24 @@ append_insn (place, ip, address_expr, reloc_type) /* Don't generate a reloc if we are writing into a variant frag. */ if (place == NULL) - fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4, - address_expr, - reloc_type == BFD_RELOC_16_PCREL_S2, - reloc_type); + { + fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + address_expr, + reloc_type == BFD_RELOC_16_PCREL_S2, + reloc_type); + if (unmatched_hi) + { + struct mips_hi_fixup *hi_fixup; + + assert (reloc_type == BFD_RELOC_HI16_S); + hi_fixup = ((struct mips_hi_fixup *) + xmalloc (sizeof (struct mips_hi_fixup))); + hi_fixup->fixp = fixp; + hi_fixup->seg = now_seg; + hi_fixup->next = mips_hi_fixup_list; + mips_hi_fixup_list = hi_fixup; + } + } } } @@ -1633,7 +1688,7 @@ macro_build (place, counter, ep, name, fmt, va_alist) va_end (args); assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL); - append_insn (place, &insn, ep, r); + append_insn (place, &insn, ep, r, false); } /* @@ -1698,10 +1753,10 @@ macro_build_lui (place, counter, ep, regnum) if (r == BFD_RELOC_UNUSED) { insn.insn_opcode |= high_expr.X_add_number; - append_insn (place, &insn, NULL, r); + append_insn (place, &insn, NULL, r, false); } else - append_insn (place, &insn, &high_expr, r); + append_insn (place, &insn, &high_expr, r, false); } /* set_at() @@ -3648,7 +3703,8 @@ macro (ip) "d,v,t", tempreg, tempreg, GP); macro_build ((char *) NULL, &icnt, &offset_expr, mips_isa < 3 ? "lw" : "ld", - "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16); + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16, + tempreg); p = frag_var (rs_machine_dependent, 12 + gpdel, 0, RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0), offset_expr.X_add_symbol, (long) 0, (char *) NULL); @@ -3659,7 +3715,7 @@ macro (ip) } macro_build (p, &icnt, &offset_expr, mips_isa < 3 ? "lw" : "ld", - "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16); + "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP); p += 4; macro_build (p, &icnt, (expressionS *) NULL, "nop", ""); p += 4; @@ -4904,12 +4960,11 @@ macro2 (ip) as_warn ("Macro used $at after \".set noat\""); } +/* This routine assembles an instruction into its binary format. As a + side effect, it sets one of the global variables imm_reloc or + offset_reloc to the type of relocation to do if one of the operands + is an address expression. */ -/* -This routine assembles an instruction into its binary format. As a side -effect it sets one of the global variables imm_reloc or offset_reloc to the -type of relocation to do if one of the operands is an address expression. -*/ static void mips_ip (str, ip) char *str; @@ -5478,7 +5533,10 @@ mips_ip (str, ip) imm_expr.X_add_number = (imm_expr.X_add_number >> 16) & 0xffff; else if (c == 'h') - imm_reloc = BFD_RELOC_HI16_S; + { + imm_reloc = BFD_RELOC_HI16_S; + imm_unmatched_hi = true; + } else imm_reloc = BFD_RELOC_HI16; } @@ -5590,7 +5648,10 @@ mips_ip (str, ip) imm_expr.X_add_number = (imm_expr.X_add_number >> 16) & 0xffff; else if (c == 'h') - imm_reloc = BFD_RELOC_HI16_S; + { + imm_reloc = BFD_RELOC_HI16_S; + imm_unmatched_hi = true; + } else imm_reloc = BFD_RELOC_HI16; } @@ -6240,6 +6301,75 @@ cons_fix_new_mips (frag, where, nbytes, exp) nbytes == 2 ? BFD_RELOC_16 : BFD_RELOC_32); } +/* Sort any unmatched HI16_S relocs so that they immediately precede + the corresponding LO reloc. This is called before md_apply_fix and + tc_gen_reloc. Unmatched HI16_S relocs can only be generated by + explicit use of the %hi modifier. */ + +void +mips_frob_file () +{ + struct mips_hi_fixup *l; + + for (l = mips_hi_fixup_list; l != NULL; l = l->next) + { + segment_info_type *seginfo; + fixS *f, *prev; + + assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S); + + /* Check quickly whether the next fixup happens to be a matching + %lo. */ + if (l->fixp->fx_next != NULL + && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16 + && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy + && l->fixp->fx_offset == l->fixp->fx_next->fx_offset) + continue; + + /* Look through the fixups for this segment for a matching %lo. + When we find one, move the %hi just in front of it. */ + seginfo = seg_info (l->seg); + prev = NULL; + for (f = seginfo->fix_root; f != NULL; f = f->fx_next) + { + /* Check whether this is a %lo fixup which matches l->fixp; + we can't use it if the %lo is already matching a %hi. */ + if (f->fx_r_type == BFD_RELOC_LO16 + && f->fx_addsy == l->fixp->fx_addsy + && f->fx_offset == l->fixp->fx_offset + && (prev == NULL + || prev->fx_r_type != BFD_RELOC_HI16_S + || prev->fx_addsy != f->fx_addsy + || prev->fx_offset != f->fx_offset)) + { + fixS **pf; + + /* Move l->fixp before f. */ + for (pf = &seginfo->fix_root; + *pf != l->fixp; + pf = &(*pf)->fx_next) + assert (*pf != NULL); + + *pf = l->fixp->fx_next; + + l->fixp->fx_next = f; + if (prev == NULL) + seginfo->fix_root = l->fixp; + else + prev->fx_next = l->fixp; + + break; + } + + prev = f; + } + + if (f == NULL) + as_warn_where (l->fixp->fx_file, l->fixp->fx_line, + "Unmatched %%hi reloc"); + } +} + /* When generating embedded PIC code we need to use a special relocation to represent the difference of two symbols in the .text section (switch tables use a difference of this sort). See diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h index a701e76..c8672b7 100644 --- a/gas/config/tc-mips.h +++ b/gas/config/tc-mips.h @@ -27,7 +27,6 @@ #define TARGET_ARCH bfd_arch_mips #define ONLY_STANDARD_ESCAPES -#define BACKSLASH_V #define WORKING_DOT_WORD 1 #define OLD_FLOAT_READS #define REPEAT_CONS_EXPRESSIONS @@ -79,6 +78,9 @@ extern int mips_parse_long_option PARAMS ((const char *)); #define tc_frob_label(sym) mips_define_label (sym) extern void mips_define_label PARAMS ((struct symbol *)); +#define tc_frob_file() mips_frob_file () +extern void mips_frob_file PARAMS ((void)); + #define TC_CONS_FIX_NEW cons_fix_new_mips extern void cons_fix_new_mips (); |