X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-mmix.c;h=f3d1d56529566181444c84f2937ad1b6532d46c7;hb=219d1afa89d0d53ca93a684cac341f16470f3ca0;hp=46d5decf9e120e64788153e8561f90f5a586fc95;hpb=94f592af1baedbbe3957436c713fd58c02d55ef9;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-mmix.c b/gas/config/tc-mmix.c index 46d5decf9e..f3d1d56529 100644 --- a/gas/config/tc-mmix.c +++ b/gas/config/tc-mmix.c @@ -1,11 +1,11 @@ /* tc-mmix.c -- Assembler for Don Knuth's MMIX. - Copyright (C) 2001 Free Software Foundation. + Copyright (C) 2001-2018 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, @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to - the Free Software Foundation, 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ /* Knuth's assembler mmixal does not provide a relocatable format; mmo is to be considered a final link-format. In the final link, we make mmo, @@ -26,10 +26,9 @@ compatible syntax, but the main purpose is to serve GCC. */ -#include #include "as.h" +#include #include "subsegs.h" -#include "bfd.h" #include "elf/mmix.h" #include "opcode/mmix.h" #include "safe-ctype.h" @@ -40,34 +39,31 @@ for example assert something of what it became or make a relocation. */ enum mmix_fixup_action - { - mmix_fixup_byte, - mmix_fixup_register, - mmix_fixup_register_or_adjust_for_byte - }; +{ + mmix_fixup_byte, + mmix_fixup_register, + mmix_fixup_register_or_adjust_for_byte +}; -static int get_spec_regno PARAMS ((char *)); -static int get_operands PARAMS ((int, char *, expressionS[])); -static int get_putget_operands - PARAMS ((struct mmix_opcode *, char *, expressionS[])); -static void s_prefix PARAMS ((int)); -static void s_greg PARAMS ((int)); -static void s_loc PARAMS ((int)); -static void s_bspec PARAMS ((int)); -static void s_espec PARAMS ((int)); -static void mmix_s_local PARAMS ((int)); -static void mmix_greg_internal PARAMS ((char *)); -static void mmix_set_geta_branch_offset PARAMS ((char *, offsetT value)); -static void mmix_set_jmp_offset PARAMS ((char *, offsetT)); -static void mmix_fill_nops PARAMS ((char *, int)); -static int cmp_greg_symbol_fixes PARAMS ((const PTR, const PTR)); -static int cmp_greg_val_greg_symbol_fixes - PARAMS ((const PTR p1, const PTR p2)); -static void mmix_handle_rest_of_empty_line PARAMS ((void)); -static void mmix_discard_rest_of_line PARAMS ((void)); -static void mmix_byte PARAMS ((void)); -static void mmix_cons PARAMS ((int)); -static void mmix_frob_local_reloc PARAMS ((bfd *, asection *, PTR)); +static int get_spec_regno (char *); +static int get_operands (int, char *, expressionS *); +static int get_putget_operands (struct mmix_opcode *, char *, expressionS *); +static void s_prefix (int); +static void s_greg (int); +static void s_loc (int); +static void s_bspec (int); +static void s_espec (int); +static void mmix_s_local (int); +static void mmix_greg_internal (char *); +static void mmix_set_geta_branch_offset (char *, offsetT); +static void mmix_set_jmp_offset (char *, offsetT); +static void mmix_fill_nops (char *, int); +static int cmp_greg_symbol_fixes (const void *, const void *); +static int cmp_greg_val_greg_symbol_fixes (const void *, const void *); +static void mmix_handle_rest_of_empty_line (void); +static void mmix_discard_rest_of_line (void); +static void mmix_byte (void); +static void mmix_cons (int); /* Continue the tradition of symbols.c; use control characters to enforce magic. These are used when replacing e.g. 8F and 8B so we can handle @@ -112,6 +108,14 @@ static struct expressionS exp; } mmix_raw_gregs[MAX_GREGS]; +static struct loc_assert_s + { + segT old_seg; + symbolS *loc_sym; + fragS *frag; + struct loc_assert_s *next; + } *loc_asserts = NULL; + /* Fixups for all unique GREG registers. We store the fixups here in md_convert_frag, then we use the array to convert BFD_RELOC_MMIX_BASE_PLUS_OFFSET fixups in tc_gen_reloc. The index is @@ -144,7 +148,8 @@ struct mmix_symbol_gregs this line? */ static int label_without_colon_this_line = 1; -/* Should we expand operands for external symbols? */ +/* Should we automatically expand instructions into multiple insns in + order to generate working code? */ static int expand_op = 1; /* Should we warn when expanding operands? FIXME: test-cases for when -x @@ -154,11 +159,15 @@ static int warn_on_expansion = 1; /* Should we merge non-zero GREG register definitions? */ static int merge_gregs = 1; +/* Should we pass on undefined BFD_RELOC_MMIX_BASE_PLUS_OFFSET relocs + (missing suitable GREG definitions) to the linker? */ +static int allocate_undefined_gregs_in_linker = 0; + /* Should we emit built-in symbols? */ static int predefined_syms = 1; -/* Should we anything but the listed special register name (e.g. equated - symbols)? */ +/* Should we allow anything but the listed special register name + (e.g. equated symbols)? */ static int equated_spec_regs = 1; /* Do we require standard GNU syntax? */ @@ -167,13 +176,17 @@ int mmix_gnu_syntax = 0; /* Do we globalize all symbols? */ int mmix_globalize_symbols = 0; +/* When expanding insns, do we want to expand PUSHJ as a call to a stub + (or else as a series of insns)? */ +int pushj_stubs = 1; + /* Do we know that the next semicolon is at the end of the operands field - (in mmixal mode; constant 1 in GNU mode)? */ + (in mmixal mode; constant 1 in GNU mode)? */ int mmix_next_semicolon_is_eoln = 1; /* Do we have a BSPEC in progress? */ static int doing_bspec = 0; -static char *bspec_file; +static const char *bspec_file; static unsigned int bspec_line; struct option md_longopts[] = @@ -185,6 +198,8 @@ struct option md_longopts[] = #define OPTION_GNU_SYNTAX (OPTION_NOSYMS + 1) #define OPTION_GLOBALIZE_SYMBOLS (OPTION_GNU_SYNTAX + 1) #define OPTION_FIXED_SPEC_REGS (OPTION_GLOBALIZE_SYMBOLS + 1) +#define OPTION_LINKER_ALLOCATED_GREGS (OPTION_FIXED_SPEC_REGS + 1) +#define OPTION_NOPUSHJSTUBS (OPTION_LINKER_ALLOCATED_GREGS + 1) {"linkrelax", no_argument, NULL, OPTION_RELAX}, {"no-expand", no_argument, NULL, OPTION_NOEXPAND}, {"no-merge-gregs", no_argument, NULL, OPTION_NOMERGEGREG}, @@ -193,6 +208,10 @@ struct option md_longopts[] = {"globalize-symbols", no_argument, NULL, OPTION_GLOBALIZE_SYMBOLS}, {"fixed-special-register-names", no_argument, NULL, OPTION_FIXED_SPEC_REGS}, + {"linker-allocated-gregs", no_argument, NULL, + OPTION_LINKER_ALLOCATED_GREGS}, + {"no-pushj-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS}, + {"no-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS}, {NULL, no_argument, NULL, 0} }; @@ -221,15 +240,27 @@ struct obstack mmix_sym_obstack; 3. PUSHJ extra length: zero or four insns. + Special handling to deal with transition to PUSHJSTUB. 4. JMP - extra length: zero or four insns. */ + extra length: zero or four insns. + + 5. GREG + special handling, allocates a named global register unless another + is within reach for all uses. + + 6. PUSHJSTUB + special handling (mostly) for external references; assumes the + linker will generate a stub if target is no longer than 256k from + the end of the section plus max size of previous stubs. Zero or + four insns. */ #define STATE_GETA (1) #define STATE_BCC (2) #define STATE_PUSHJ (3) #define STATE_JMP (4) #define STATE_GREG (5) +#define STATE_PUSHJSTUB (6) /* No fine-grainedness here. */ #define STATE_LENGTH_MASK (1) @@ -248,35 +279,40 @@ struct obstack mmix_sym_obstack; #define STATE_GREG_UNDF ENCODE_RELAX (STATE_GREG, STATE_ZERO) #define STATE_GREG_DEF ENCODE_RELAX (STATE_GREG, STATE_MAX) -/* These displacements are relative to the adress following the opcode +/* These displacements are relative to the address following the opcode word of the instruction. The catch-all states have zero for "reach" and "next" entries. */ #define GETA_0F (65536 * 4 - 8) #define GETA_0B (-65536 * 4 - 4) -#define GETA_MAX_LEN 4*4 +#define GETA_MAX_LEN 4 * 4 #define GETA_3F 0 #define GETA_3B 0 #define BCC_0F GETA_0F #define BCC_0B GETA_0B -#define BCC_MAX_LEN 6*4 +#define BCC_MAX_LEN 6 * 4 #define BCC_5F GETA_3F #define BCC_5B GETA_3B #define PUSHJ_0F GETA_0F #define PUSHJ_0B GETA_0B -#define PUSHJ_MAX_LEN 5*4 +#define PUSHJ_MAX_LEN 5 * 4 #define PUSHJ_4F GETA_3F #define PUSHJ_4B GETA_3B +/* We'll very rarely have sections longer than LONG_MAX, but we'll make a + feeble attempt at getting 64-bit values. */ +#define PUSHJSTUB_MAX ((offsetT) (((addressT) -1) >> 1)) +#define PUSHJSTUB_MIN (-PUSHJSTUB_MAX - 1) + #define JMP_0F (65536 * 256 * 4 - 8) #define JMP_0B (-65536 * 256 * 4 - 4) -#define JMP_MAX_LEN 5*4 +#define JMP_MAX_LEN 5 * 4 #define JMP_4F 0 #define JMP_4B 0 @@ -305,8 +341,8 @@ const relax_typeS mmix_relax_table[] = {BCC_5F, BCC_5B, BCC_MAX_LEN - 4, 0}, - /* PUSHJ (3, 0). */ - {PUSHJ_0F, PUSHJ_0B, 0, ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)}, + /* PUSHJ (3, 0). Next state is actually PUSHJSTUB (6, 0). */ + {PUSHJ_0F, PUSHJ_0B, 0, ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO)}, /* PUSHJ (3, 1). */ {PUSHJ_4F, PUSHJ_4B, @@ -320,7 +356,13 @@ const relax_typeS mmix_relax_table[] = JMP_MAX_LEN - 4, 0}, /* GREG (5, 0), (5, 1), though the table entry isn't used. */ - {0, 0, 0, 0}, {0, 0, 0, 0} + {0, 0, 0, 0}, {0, 0, 0, 0}, + + /* PUSHJSTUB (6, 0). PUSHJ (3, 0) uses the range, so we set it to infinite. */ + {PUSHJSTUB_MAX, PUSHJSTUB_MIN, + 0, ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)}, + /* PUSHJSTUB (6, 1) isn't used. */ + {0, 0, PUSHJ_MAX_LEN, 0} }; const pseudo_typeS md_pseudo_table[] = @@ -337,10 +379,6 @@ const pseudo_typeS md_pseudo_table[] = /* Support " .local $45" syntax. */ {"local", mmix_s_local, 1}, - /* Support DWARF2 debugging info. */ - {"file", dwarf2_directive_file, 0}, - {"loc", dwarf2_directive_loc, 0}, - {NULL, 0, 0} }; @@ -357,17 +395,15 @@ const char line_comment_chars[] = "*#"; const char line_separator_chars[] = ";"; -const char mmix_exp_chars[] = "eE"; +const char EXP_CHARS[] = "eE"; -const char mmix_flt_chars[] = "rf"; +const char FLT_CHARS[] = "rf"; /* Fill in the offset-related part of GETA or Bcc. */ static void -mmix_set_geta_branch_offset (opcodep, value) - char *opcodep; - offsetT value; +mmix_set_geta_branch_offset (char *opcodep, offsetT value) { if (value < 0) { @@ -382,9 +418,7 @@ mmix_set_geta_branch_offset (opcodep, value) /* Fill in the offset-related part of JMP. */ static void -mmix_set_jmp_offset (opcodep, value) - char *opcodep; - offsetT value; +mmix_set_jmp_offset (char *opcodep, offsetT value) { if (value < 0) { @@ -399,22 +433,18 @@ mmix_set_jmp_offset (opcodep, value) /* Fill in NOP:s for the expanded part of GETA/JMP/Bcc/PUSHJ. */ static void -mmix_fill_nops (opcodep, n) - char *opcodep; - int n; +mmix_fill_nops (char *opcodep, int n) { int i; for (i = 0; i < n; i++) - md_number_to_chars (opcodep + i*4, SWYM_INSN_BYTE << 24, 4); + md_number_to_chars (opcodep + i * 4, SWYM_INSN_BYTE << 24, 4); } /* See macro md_parse_name in tc-mmix.h. */ int -mmix_current_location (fn, exp) - void (*fn) PARAMS ((expressionS *)); - expressionS *exp; +mmix_current_location (void (*fn) (expressionS *), expressionS *exp) { (*fn) (exp); @@ -425,10 +455,7 @@ mmix_current_location (fn, exp) General idea and code stolen from the tic80 port. */ static int -get_operands (max_operands, s, exp) - int max_operands; - char *s; - expressionS exp[]; +get_operands (int max_operands, char *s, expressionS *exp) { char *p = s; int numexp = 0; @@ -454,7 +481,7 @@ get_operands (max_operands, s, exp) return 0; } - /* Begin operand parsing at the current scan point. */ + /* Begin operand parsing at the current scan point. */ input_line_pointer = p; expression (&exp[numexp]); @@ -484,7 +511,7 @@ get_operands (max_operands, s, exp) input_line_pointer--; } - /* Mark the end of the valid operands with an illegal expression. */ + /* Mark the end of the valid operands with an illegal expression. */ exp[numexp].X_op = O_illegal; return (numexp); @@ -494,8 +521,7 @@ get_operands (max_operands, s, exp) one. NAME is a null-terminated string. */ static int -get_spec_regno (name) - char *name; +get_spec_regno (char *name) { int i; @@ -517,10 +543,8 @@ get_spec_regno (name) /* For GET and PUT, parse the register names "manually", so we don't use user labels. */ static int -get_putget_operands (insn, operands, exp) - struct mmix_opcode *insn; - char *operands; - expressionS exp[]; +get_putget_operands (struct mmix_opcode *insn, char *operands, + expressionS *exp) { expressionS *expp_reg; expressionS *expp_sreg; @@ -536,6 +560,11 @@ get_putget_operands (insn, operands, exp) input_line_pointer = p; + /* Initialize both possible operands to error state, in case we never + get further. */ + exp[0].X_op = O_illegal; + exp[1].X_op = O_illegal; + if (insn->operands == mmix_operands_get) { expp_reg = &exp[0]; @@ -558,8 +587,10 @@ get_putget_operands (insn, operands, exp) p++; sregp = p; input_line_pointer = sregp; - c = get_symbol_end (); + c = get_symbol_name (&sregp); sregend = input_line_pointer; + if (c == '"') + ++ input_line_pointer; } } else @@ -567,14 +598,10 @@ get_putget_operands (insn, operands, exp) expp_sreg = &exp[0]; expp_reg = &exp[1]; - /* Initialize to error state in case we'll never call expression on - this operand. */ - expp_reg->X_op = O_illegal; - - sregp = p; - c = get_symbol_end (); - sregend = p = input_line_pointer; - *p = c; + c = get_symbol_name (&sregp); + sregend = input_line_pointer; + restore_line_pointer (c); + p = input_line_pointer; /* Skip whitespace */ while (*p == ' ' || *p == '\t') @@ -613,14 +640,13 @@ get_putget_operands (insn, operands, exp) /* Handle MMIX-specific option. */ int -md_parse_option (c, arg) - int c; - char *arg ATTRIBUTE_UNUSED; +md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED) { switch (c) { case 'x': warn_on_expansion = 0; + allocate_undefined_gregs_in_linker = 1; break; case OPTION_RELAX: @@ -653,6 +679,14 @@ md_parse_option (c, arg) equated_spec_regs = 0; break; + case OPTION_LINKER_ALLOCATED_GREGS: + allocate_undefined_gregs_in_linker = 1; + break; + + case OPTION_NOPUSHJSTUBS: + pushj_stubs = 0; + break; + default: return 0; } @@ -663,8 +697,7 @@ md_parse_option (c, arg) /* Display MMIX-specific help text. */ void -md_show_usage (stream) - FILE * stream; +md_show_usage (FILE * stream) { fprintf (stream, _(" MMIX-specific command line options:\n")); fprintf (stream, _("\ @@ -685,18 +718,22 @@ md_show_usage (stream) fprintf (stream, _("\ -no-merge-gregs Do not merge GREG definitions with nearby values.\n")); fprintf (stream, _("\ + -linker-allocated-gregs If there's no suitable GREG definition for the\ + operands of an instruction, let the linker resolve.\n")); + fprintf (stream, _("\ -x Do not warn when an operand to GETA, a branch,\n\ PUSHJ or JUMP is not known to be within range.\n\ - The linker will catch any errors.\n")); + The linker will catch any errors. Implies\n\ + -linker-allocated-gregs.")); } /* Step to end of line, but don't step over the end of the line. */ static void -mmix_discard_rest_of_line () +mmix_discard_rest_of_line (void) { while (*input_line_pointer - && (! is_end_of_line [(unsigned char) *input_line_pointer] + && (! is_end_of_line[(unsigned char) *input_line_pointer] || TC_EOL_IN_INSN (input_line_pointer))) input_line_pointer++; } @@ -706,7 +743,7 @@ mmix_discard_rest_of_line () delimiter). */ static void -mmix_handle_rest_of_empty_line () +mmix_handle_rest_of_empty_line (void) { if (mmix_gnu_syntax) demand_empty_rest_of_line (); @@ -720,7 +757,7 @@ mmix_handle_rest_of_empty_line () /* Initialize GAS MMIX specifics. */ void -mmix_md_begin () +mmix_md_begin (void) { int i; const struct mmix_opcode *opcode; @@ -730,7 +767,7 @@ mmix_md_begin () /* This will break the day the "lex" thingy changes. For now, it's the only way to make ':' part of a name, and a name beginner. */ - lex_type [':'] = (LEX_NAME | LEX_BEGIN_NAME); + lex_type[':'] = (LEX_NAME | LEX_BEGIN_NAME); mmix_opcode_hash = hash_new (); @@ -779,8 +816,7 @@ mmix_md_begin () /* Assemble one insn in STR. */ void -md_assemble (str) - char *str; +md_assemble (char *str) { char *operands = str; char modified_char = 0; @@ -1031,10 +1067,10 @@ md_assemble (str) current_fb_label = -1; } - /* We also assume that the length of the instruction is determinable - from the first format character. Currently *all* the information is - in the first character. We need a self-contained frag since we want - the relocation to point to the instruction, not the variant part. */ + /* We also assume that the length of the instruction is at least 4, the + size of an unexpanded instruction. We need a self-contained frag + since we want the relocation to point to the instruction, not the + variant part. */ opcodep = frag_more (4); mmix_opcode_frag = opc_fragP = frag_now; @@ -1065,7 +1101,7 @@ md_assemble (str) } if (expand_op) - frag_var (rs_machine_dependent, 4*4, 0, + frag_var (rs_machine_dependent, 4 * 4, 0, ENCODE_RELAX (STATE_JMP, STATE_UNDF), exp[0].X_add_symbol, exp[0].X_add_number, @@ -1265,7 +1301,7 @@ md_assemble (str) /* SYNCD: "X,$Y,$Z|Z". */ /* FALLTHROUGH. */ case mmix_operands_regs: - /* Three registers, $X,$Y,$Z. */ + /* Three registers, $X,$Y,$Z. */ /* FALLTHROUGH. */ case mmix_operands_regs_z: /* Operands "$X,$Y,$Z|Z", number of arguments checked above. */ @@ -1338,6 +1374,9 @@ md_assemble (str) pass expressions as symbols and use fix_new, not fix_new_exp. */ sym = make_expr_symbol (exp + 1); + /* Mark the symbol as being OK for a reloc. */ + symbol_get_bfdsym (sym)->flags |= BSF_KEEP; + /* Now we know it can be a "base address plus offset". Add proper fixup types so we can handle this later, when we've parsed everything. */ @@ -1403,7 +1442,7 @@ md_assemble (str) break; case mmix_operands_jmp: - /* A JMP. Everyhing is already done. */ + /* A JMP. Everything is already done. */ break; case mmix_operands_roundregs: @@ -1438,8 +1477,8 @@ md_assemble (str) && ((exp[1].X_op == O_register && exp[1].X_add_number < 512) || (exp[1].X_op == O_constant - && exp[1].X_add_number < 0 - && exp[1].X_add_number > 4) + && (exp[1].X_add_number < 0 + || exp[1].X_add_number > 4)) || (exp[1].X_op != O_register && exp[1].X_op != O_constant)))) { @@ -1470,27 +1509,27 @@ md_assemble (str) case mmix_operands_sync: a_single_24_bit_number_operand: - if (n_operands != 1 - || exp[0].X_op == O_register - || (exp[0].X_op == O_constant - && (exp[0].X_add_number > 0xffffff || exp[0].X_add_number < 0))) - { - as_bad (_("invalid operands to opcode %s: `%s'"), - instruction->name, operands); - return; - } + if (n_operands != 1 + || exp[0].X_op == O_register + || (exp[0].X_op == O_constant + && (exp[0].X_add_number > 0xffffff || exp[0].X_add_number < 0))) + { + as_bad (_("invalid operands to opcode %s: `%s'"), + instruction->name, operands); + return; + } - if (exp[0].X_op == O_constant) - { - opcodep[1] = (exp[0].X_add_number >> 16) & 255; - opcodep[2] = (exp[0].X_add_number >> 8) & 255; - opcodep[3] = exp[0].X_add_number & 255; - } - else - /* FIXME: This doesn't bring us unsignedness checking. */ - fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1, - 3, exp + 0, 0, BFD_RELOC_24); - break; + if (exp[0].X_op == O_constant) + { + opcodep[1] = (exp[0].X_add_number >> 16) & 255; + opcodep[2] = (exp[0].X_add_number >> 8) & 255; + opcodep[3] = exp[0].X_add_number & 255; + } + else + /* FIXME: This doesn't bring us unsignedness checking. */ + fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1, + 3, exp + 0, 0, BFD_RELOC_24); + break; case mmix_operands_neg: /* Operands "$X,Y,$Z|Z"; NEG or NEGU. Y is optional, 0 is default. */ @@ -1544,7 +1583,7 @@ md_assemble (str) break; case mmix_operands_regaddr: - /* A GETA/branch-type. */ + /* A GETA/branch-type. */ break; case mmix_operands_get: @@ -1622,7 +1661,7 @@ md_assemble (str) break; } - /* "0,$Z"; UNSAVE. */ + /* "0,$Z"; UNSAVE. */ if (n_operands != 2 || exp[0].X_op != O_constant || exp[0].X_add_number != 0 @@ -1643,7 +1682,10 @@ md_assemble (str) break; case mmix_operands_xyz_opt: - /* SWYM, TRIP, TRAP: zero, one, two or three operands. */ + /* SWYM, TRIP, TRAP: zero, one, two or three operands. It's + unspecified whether operands are registers or constants, but + when we find register syntax, we require operands to be literal and + within 0..255. */ if (n_operands == 0 && ! mmix_gnu_syntax) /* Zeros are in place - nothing needs to be done for zero operands. We don't allow this in GNU syntax mode, because it @@ -1654,7 +1696,7 @@ md_assemble (str) { if (exp[0].X_op == O_constant) { - if (exp[0].X_add_number > 255*255*255 + if (exp[0].X_add_number > 255*256*256 || exp[0].X_add_number < 0) { as_bad (_("invalid operands to opcode %s: `%s'"), @@ -1696,7 +1738,7 @@ md_assemble (str) if (exp[1].X_op == O_constant) { - if (exp[1].X_add_number > 255*255 + if (exp[1].X_add_number > 255*256 || exp[1].X_add_number < 0) { as_bad (_("invalid operands to opcode %s: `%s'"), @@ -1768,62 +1810,64 @@ md_assemble (str) fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3, 1, exp + 2, 0, BFD_RELOC_8); } - else if (n_operands <= 3 - && (strcmp (instruction->name, "trip") == 0 - || strcmp (instruction->name, "trap") == 0)) + else { - /* The meaning of operands to TRIP and TRAP are not defined, so - we add combinations not handled above here as we find them. */ + /* We can't get here for other cases. */ + gas_assert (n_operands <= 3); + + /* The meaning of operands to TRIP and TRAP is not defined (and + SWYM operands aren't enforced in mmixal, so let's avoid + that). We add combinations not handled above here as we find + them and as they're reported. */ if (n_operands == 3) { /* Don't require non-register operands. Always generate fixups, so we don't have to copy lots of code and create - maintanance problems. TRIP is supposed to be a rare + maintenance problems. TRIP is supposed to be a rare instruction, so the overhead should not matter. We aren't allowed to fix_new_exp for an expression which is - an O_register at this point, however. */ + an O_register at this point, however. + + Don't use BFD_RELOC_MMIX_REG_OR_BYTE as that modifies + the insn for a register in the Z field and we want + consistency. */ if (exp[0].X_op == O_register) opcodep[1] = exp[0].X_add_number; else fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1, - 1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE); + 1, exp, 0, BFD_RELOC_8); if (exp[1].X_op == O_register) opcodep[2] = exp[1].X_add_number; else fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2, - 1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE); + 1, exp + 1, 0, BFD_RELOC_8); if (exp[2].X_op == O_register) opcodep[3] = exp[2].X_add_number; else fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3, - 1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE); + 1, exp + 2, 0, BFD_RELOC_8); } else if (n_operands == 2) { if (exp[0].X_op == O_register) - opcodep[2] = exp[0].X_add_number; + opcodep[1] = exp[0].X_add_number; else - fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2, - 1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE); + fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1, + 1, exp, 0, BFD_RELOC_8); if (exp[1].X_op == O_register) opcodep[3] = exp[1].X_add_number; else - fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3, - 1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE); + fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2, + 2, exp + 1, 0, BFD_RELOC_16); } else { - as_bad (_("unsupported operands to %s: `%s'"), - instruction->name, operands); - return; + /* We can't get here for other cases. */ + gas_assert (n_operands == 1 && exp[0].X_op == O_register); + + opcodep[3] = exp[0].X_add_number; } } - else - { - as_bad (_("invalid operands to opcode %s: `%s'"), - instruction->name, operands); - return; - } break; case mmix_operands_resume: @@ -1861,8 +1905,7 @@ md_assemble (str) tc_unrecognized_line too, through this function. */ int -mmix_assemble_return_nonzero (str) - char *str; +mmix_assemble_return_nonzero (char *str) { int last_error_count = had_errors (); char *s2 = str; @@ -1891,19 +1934,16 @@ mmix_assemble_return_nonzero (str) /* The PREFIX pseudo. */ static void -s_prefix (unused) - int unused ATTRIBUTE_UNUSED; +s_prefix (int unused ATTRIBUTE_UNUSED) { char *p; int c; SKIP_WHITESPACE (); - p = input_line_pointer; - - c = get_symbol_end (); - - /* Reseting prefix? */ + c = get_symbol_name (&p); + + /* Resetting prefix? */ if (*p == ':' && p[1] == 0) mmix_current_prefix = NULL; else @@ -1921,7 +1961,7 @@ s_prefix (unused) mmix_current_prefix = p; } - *input_line_pointer = c; + (void) restore_line_pointer (c); mmix_handle_rest_of_empty_line (); } @@ -1934,8 +1974,7 @@ s_prefix (unused) that. (It might be worth a rewrite for other reasons, though). */ char * -mmix_prefix_name (shortname) - char *shortname; +mmix_prefix_name (char *shortname) { if (*shortname == ':') return shortname + 1; @@ -1959,14 +1998,14 @@ mmix_prefix_name (shortname) be persistent, perhaps allocated on an obstack. */ static void -mmix_greg_internal (label) - char *label; +mmix_greg_internal (char *label) { expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp; + segT section; /* Don't set the section to register contents section before the expression has been parsed; it may refer to the current position. */ - expression (expP); + section = expression (expP); /* FIXME: Check that no expression refers to the register contents section. May need to be done in elf64-mmix.c. */ @@ -1980,6 +2019,24 @@ mmix_greg_internal (label) expP->X_op_symbol = NULL; } + if (section == undefined_section) + { + /* This is an error or a LOC with an expression involving + forward references. For the expression to be correctly + evaluated, we need to force a proper symbol; gas loses track + of the segment for "local symbols". */ + if (expP->X_op == O_add) + { + symbol_get_value_expression (expP->X_op_symbol); + symbol_get_value_expression (expP->X_add_symbol); + } + else + { + gas_assert (expP->X_op == O_symbol); + symbol_get_value_expression (expP->X_add_symbol); + } + } + /* We must handle prefixes here, as we save the labels and expressions to be output later. */ mmix_raw_gregs[n_of_raw_gregs].label @@ -1996,20 +2053,21 @@ mmix_greg_internal (label) /* The ".greg label,expr" worker. */ static void -s_greg (unused) - int unused ATTRIBUTE_UNUSED; +s_greg (int unused ATTRIBUTE_UNUSED) { char *p; char c; - p = input_line_pointer; /* This will skip over what can be a symbol and zero out the next character, which we assume is a ',' or other meaningful delimiter. What comes after that is the initializer expression for the register. */ - c = get_symbol_end (); + c = get_symbol_name (&p); - if (! is_end_of_line [(unsigned char) c]) + if (c == '"') + c = * ++ input_line_pointer; + + if (! is_end_of_line[(unsigned char) c]) input_line_pointer++; if (*p) @@ -2026,8 +2084,7 @@ s_greg (unused) /* The "BSPEC expr" worker. */ static void -s_bspec (unused) - int unused ATTRIBUTE_UNUSED; +s_bspec (int unused ATTRIBUTE_UNUSED) { asection *expsec; asection *sec; @@ -2084,7 +2141,7 @@ s_bspec (unused) subseg_set (sec, 0); /* Save position for missing ESPEC. */ - as_where (&bspec_file, &bspec_line); + bspec_file = as_where (&bspec_line); doing_bspec = 1; } @@ -2092,8 +2149,7 @@ s_bspec (unused) /* The "ESPEC" worker. */ static void -s_espec (unused) - int unused ATTRIBUTE_UNUSED; +s_espec (int unused ATTRIBUTE_UNUSED) { /* First, check that we *do* have a BSPEC in progress. */ if (! doing_bspec) @@ -2116,8 +2172,7 @@ s_espec (unused) Implementing this by means of contents in a section lost. */ static void -mmix_s_local (unused) - int unused ATTRIBUTE_UNUSED; +mmix_s_local (int unused ATTRIBUTE_UNUSED) { expressionS exp; @@ -2147,16 +2202,15 @@ mmix_s_local (unused) function may be called multiple times. */ int -md_estimate_size_before_relax (fragP, segment) - fragS *fragP; - segT segment; +md_estimate_size_before_relax (fragS *fragP, segT segment) { int length; #define HANDLE_RELAXABLE(state) \ case ENCODE_RELAX (state, STATE_UNDF): \ if (fragP->fr_symbol != NULL \ - && S_GET_SEGMENT (fragP->fr_symbol) == segment) \ + && S_GET_SEGMENT (fragP->fr_symbol) == segment \ + && !S_IS_WEAK (fragP->fr_symbol)) \ { \ /* The symbol lies in the same segment - a relaxable case. */ \ fragP->fr_subtype \ @@ -2168,12 +2222,27 @@ md_estimate_size_before_relax (fragP, segment) { HANDLE_RELAXABLE (STATE_GETA); HANDLE_RELAXABLE (STATE_BCC); - HANDLE_RELAXABLE (STATE_PUSHJ); HANDLE_RELAXABLE (STATE_JMP); + case ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF): + if (fragP->fr_symbol != NULL + && S_GET_SEGMENT (fragP->fr_symbol) == segment + && !S_IS_WEAK (fragP->fr_symbol)) + /* The symbol lies in the same segment - a relaxable case. */ + fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO); + else if (pushj_stubs) + /* If we're to generate stubs, assume we can reach a stub after + the section. */ + fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO); + /* FALLTHROUGH. */ + case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO): + case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO): + /* We need to distinguish different relaxation rounds. */ + seg_info (segment)->tc_segment_info_data.last_stubfrag = fragP; + break; + case ENCODE_RELAX (STATE_GETA, STATE_ZERO): case ENCODE_RELAX (STATE_BCC, STATE_ZERO): - case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO): case ENCODE_RELAX (STATE_JMP, STATE_ZERO): /* When relaxing a section for the second time, we don't need to do anything except making sure that fr_var is set right. */ @@ -2203,59 +2272,26 @@ md_estimate_size_before_relax (fragP, segment) emitted is stored in *sizeP . An error message is returned, or NULL on OK. */ -char * -md_atof (type, litP, sizeP) - int type; - char *litP; - int *sizeP; +const char * +md_atof (int type, char *litP, int *sizeP) { - int prec; - LITTLENUM_TYPE words[4]; - char *t; - int i; - - switch (type) - { - /* FIXME: Having 'f' in mmix_flt_chars (and here) makes it - problematic to also have a forward reference in an expression. - The testsuite wants it, and it's customary. - We'll deal with the real problems when they come; we share the - problem with most other ports. */ - case 'f': - case 'r': - prec = 2; - break; - case 'd': - prec = 4; - break; - default: - *sizeP = 0; - return _("bad call to md_atof"); - } - - t = atof_ieee (input_line_pointer, type, words); - if (t) - input_line_pointer = t; - - *sizeP = prec * 2; - - for (i = 0; i < prec; i++) - { - md_number_to_chars (litP, (valueT) words[i], 2); - litP += 2; - } - return NULL; + if (type == 'r') + type = 'f'; + /* FIXME: Having 'f' in FLT_CHARS (and here) makes it + problematic to also have a forward reference in an expression. + The testsuite wants it, and it's customary. + We'll deal with the real problems when they come; we share the + problem with most other ports. */ + return ieee_md_atof (type, litP, sizeP, TRUE); } /* Convert variable-sized frags into one or more fixups. */ void -md_convert_frag (abfd, sec, fragP) - bfd *abfd ATTRIBUTE_UNUSED; - segT sec ATTRIBUTE_UNUSED; - fragS *fragP; +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, + fragS *fragP) { - /* Pointer to first byte in variable-sized part of the frag. */ + /* Pointer to first byte in variable-sized part of the frag. */ char *var_partp; /* Pointer to first opcode byte in frag. */ @@ -2293,48 +2329,62 @@ md_convert_frag (abfd, sec, fragP) opcode_address = fragP->fr_address + fragP->fr_fix - 4; switch (fragP->fr_subtype) - { - case ENCODE_RELAX (STATE_GETA, STATE_ZERO): - case ENCODE_RELAX (STATE_BCC, STATE_ZERO): - case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO): - mmix_set_geta_branch_offset (opcodep, target_address - opcode_address); - if (linkrelax) - { - tmpfixP - = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4, - fragP->fr_symbol, fragP->fr_offset, 1, - BFD_RELOC_MMIX_ADDR19); - COPY_FR_WHERE_TO_FX (fragP, tmpfixP); - } - var_part_size = 0; - break; + { + case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO): + /* Setting the unknown bits to 0 seems the most appropriate. */ + mmix_set_geta_branch_offset (opcodep, 0); + tmpfixP = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 8, + fragP->fr_symbol, fragP->fr_offset, 1, + BFD_RELOC_MMIX_PUSHJ_STUBBABLE); + COPY_FR_WHERE_TO_FX (fragP, tmpfixP); + var_part_size = 0; + break; - case ENCODE_RELAX (STATE_JMP, STATE_ZERO): - mmix_set_jmp_offset (opcodep, target_address - opcode_address); - if (linkrelax) - { - tmpfixP - = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4, - fragP->fr_symbol, fragP->fr_offset, 1, - BFD_RELOC_MMIX_ADDR27); - COPY_FR_WHERE_TO_FX (fragP, tmpfixP); - } - var_part_size = 0; - break; + case ENCODE_RELAX (STATE_GETA, STATE_ZERO): + case ENCODE_RELAX (STATE_BCC, STATE_ZERO): + case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO): + mmix_set_geta_branch_offset (opcodep, target_address - opcode_address); + if (linkrelax) + { + tmpfixP + = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4, + fragP->fr_symbol, fragP->fr_offset, 1, + BFD_RELOC_MMIX_ADDR19); + COPY_FR_WHERE_TO_FX (fragP, tmpfixP); + } + var_part_size = 0; + break; - case STATE_GREG_DEF: - if (fragP->tc_frag_data == NULL) - { - tmpfixP - = fix_new (fragP, var_partp - fragP->fr_literal, 8, - fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_64); - COPY_FR_WHERE_TO_FX (fragP, tmpfixP); - mmix_gregs[n_of_cooked_gregs++] = tmpfixP; - var_part_size = 8; - } - else + case ENCODE_RELAX (STATE_JMP, STATE_ZERO): + mmix_set_jmp_offset (opcodep, target_address - opcode_address); + if (linkrelax) + { + tmpfixP + = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4, + fragP->fr_symbol, fragP->fr_offset, 1, + BFD_RELOC_MMIX_ADDR27); + COPY_FR_WHERE_TO_FX (fragP, tmpfixP); + } var_part_size = 0; - break; + break; + + case STATE_GREG_DEF: + if (fragP->tc_frag_data == NULL) + { + /* We must initialize data that's supposed to be "fixed up" to + avoid emitting garbage, because md_apply_fix won't do + anything for undefined symbols. */ + md_number_to_chars (var_partp, 0, 8); + tmpfixP + = fix_new (fragP, var_partp - fragP->fr_literal, 8, + fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_64); + COPY_FR_WHERE_TO_FX (fragP, tmpfixP); + mmix_gregs[n_of_cooked_gregs++] = tmpfixP; + var_part_size = 8; + } + else + var_part_size = 0; + break; #define HANDLE_MAX_RELOC(state, reloc) \ case ENCODE_RELAX (state, STATE_MAX): \ @@ -2349,15 +2399,15 @@ md_convert_frag (abfd, sec, fragP) COPY_FR_WHERE_TO_FX (fragP, tmpfixP); \ break - HANDLE_MAX_RELOC (STATE_GETA, BFD_RELOC_MMIX_GETA); - HANDLE_MAX_RELOC (STATE_BCC, BFD_RELOC_MMIX_CBRANCH); - HANDLE_MAX_RELOC (STATE_PUSHJ, BFD_RELOC_MMIX_PUSHJ); - HANDLE_MAX_RELOC (STATE_JMP, BFD_RELOC_MMIX_JMP); + HANDLE_MAX_RELOC (STATE_GETA, BFD_RELOC_MMIX_GETA); + HANDLE_MAX_RELOC (STATE_BCC, BFD_RELOC_MMIX_CBRANCH); + HANDLE_MAX_RELOC (STATE_PUSHJ, BFD_RELOC_MMIX_PUSHJ); + HANDLE_MAX_RELOC (STATE_JMP, BFD_RELOC_MMIX_JMP); - default: - BAD_CASE (fragP->fr_subtype); - break; - } + default: + BAD_CASE (fragP->fr_subtype); + break; + } fragP->fr_fix += var_part_size; fragP->fr_var = 0; @@ -2370,10 +2420,7 @@ md_convert_frag (abfd, sec, fragP) Note that this function isn't called when linkrelax != 0. */ void -md_apply_fix3 (fixP, valP, segment) - fixS * fixP; - valueT * valP; - segT segment; +md_apply_fix (fixS *fixP, valueT *valP, segT segment) { char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; /* Note: use offsetT because it is signed, valueT is unsigned. */ @@ -2393,8 +2440,7 @@ md_apply_fix3 (fixP, valP, segment) && symsec != absolute_section && ((fixP->fx_r_type != BFD_RELOC_MMIX_REG && fixP->fx_r_type != BFD_RELOC_MMIX_REG_OR_BYTE) - || (symsec != reg_section - && symsec != real_reg_section))))) + || symsec != reg_section)))) { fixP->fx_done = 0; return; @@ -2437,6 +2483,7 @@ md_apply_fix3 (fixP, valP, segment) case BFD_RELOC_MMIX_GETA: case BFD_RELOC_MMIX_CBRANCH: case BFD_RELOC_MMIX_PUSHJ: + case BFD_RELOC_MMIX_PUSHJ_STUBBABLE: /* If this fixup is out of range, punt to the linker to emit an error. This should only happen with -no-expand. */ if (val < -(((offsetT) 1 << 19)/2) @@ -2478,11 +2525,17 @@ md_apply_fix3 (fixP, valP, segment) case BFD_RELOC_MMIX_REG_OR_BYTE: if (fixP->fx_addsy != NULL - && (S_GET_SEGMENT (fixP->fx_addsy) != real_reg_section + && (S_GET_SEGMENT (fixP->fx_addsy) != reg_section || S_GET_VALUE (fixP->fx_addsy) > 255) && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("invalid operands")); + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid operands")); + /* We don't want this "symbol" appearing in output, because + that will fail. */ + fixP->fx_done = 1; + } + buf[0] = val; /* If this reloc is for a Z field, we need to adjust @@ -2493,25 +2546,19 @@ md_apply_fix3 (fixP, valP, segment) && (fixP->fx_addsy == NULL || S_GET_SEGMENT (fixP->fx_addsy) == absolute_section)) buf[-3] |= IMM_OFFSET_BIT; - - /* We don't want this "symbol" appearing in output, because that - will fail. */ - if (fixP->fx_addsy - && S_GET_SEGMENT (fixP->fx_addsy) == real_reg_section) - symbol_clear_used_in_reloc (fixP->fx_addsy); break; case BFD_RELOC_MMIX_REG: if (fixP->fx_addsy == NULL - || S_GET_SEGMENT (fixP->fx_addsy) != real_reg_section + || S_GET_SEGMENT (fixP->fx_addsy) != reg_section || S_GET_VALUE (fixP->fx_addsy) > 255) - as_bad_where (fixP->fx_file, fixP->fx_line, - _("invalid operands")); - *buf = val; + { + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid operands")); + fixP->fx_done = 1; + } - if (fixP->fx_addsy - && S_GET_SEGMENT (fixP->fx_addsy) == real_reg_section) - symbol_clear_used_in_reloc (fixP->fx_addsy); + *buf = val; break; case BFD_RELOC_MMIX_BASE_PLUS_OFFSET: @@ -2547,9 +2594,7 @@ md_apply_fix3 (fixP, valP, segment) definitions. */ static int -cmp_greg_val_greg_symbol_fixes (p1, p2) - const PTR p1; - const PTR p2; +cmp_greg_val_greg_symbol_fixes (const void *p1, const void *p2) { offsetT val1 = *(offsetT *) p1; offsetT val2 = ((struct mmix_symbol_greg_fixes *) p2)->offs; @@ -2566,19 +2611,23 @@ cmp_greg_val_greg_symbol_fixes (p1, p2) /* Generate a machine-dependent relocation. */ arelent * -tc_gen_reloc (section, fixP) - asection *section ATTRIBUTE_UNUSED; - fixS *fixP; +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP) { bfd_signed_vma val - = fixP->fx_offset + (fixP->fx_addsy ? S_GET_VALUE (fixP->fx_addsy) : 0); + = fixP->fx_offset + + (fixP->fx_addsy != NULL + && !S_IS_WEAK (fixP->fx_addsy) + && !S_IS_COMMON (fixP->fx_addsy) + ? S_GET_VALUE (fixP->fx_addsy) : 0); arelent *relP; bfd_reloc_code_real_type code = BFD_RELOC_NONE; char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; symbolS *addsy = fixP->fx_addsy; asection *addsec = addsy == NULL ? NULL : S_GET_SEGMENT (addsy); - bfd_vma addend = fixP->fx_offset; asymbol *baddsy = addsy != NULL ? symbol_get_bfdsym (addsy) : NULL; + bfd_vma addend + = val - (baddsy == NULL || S_IS_COMMON (addsy) || S_IS_WEAK (addsy) + ? 0 : bfd_asymbol_value (baddsy)); /* A single " LOCAL expression" in the wrong section will not work when linking to MMO; relocations for zero-content sections are then @@ -2608,10 +2657,9 @@ tc_gen_reloc (section, fixP) case BFD_RELOC_8: code = fixP->fx_r_type; - if (addsy == NULL - || bfd_is_abs_section (S_GET_SEGMENT (addsy))) + if (addsy == NULL || bfd_is_abs_section (addsec)) { - /* Resolve this reloc now, as md_apply_fix3 would have done (not + /* Resolve this reloc now, as md_apply_fix would have done (not called if -linkrelax). There is no point in keeping a reloc to an absolute symbol. No reloc that is subject to relaxation must be to an absolute symbol; difference @@ -2644,6 +2692,7 @@ tc_gen_reloc (section, fixP) case BFD_RELOC_MMIX_PUSHJ_1: case BFD_RELOC_MMIX_PUSHJ_2: case BFD_RELOC_MMIX_PUSHJ_3: + case BFD_RELOC_MMIX_PUSHJ_STUBBABLE: case BFD_RELOC_MMIX_JMP: case BFD_RELOC_MMIX_JMP_1: case BFD_RELOC_MMIX_JMP_2: @@ -2658,7 +2707,7 @@ tc_gen_reloc (section, fixP) register contents section (that is, to a register), then we can't resolve the relocation here. */ if (addsy != NULL - && (bfd_is_und_section (S_GET_SEGMENT (addsy)) + && (bfd_is_und_section (addsec) || strcmp (bfd_get_section_name (addsec->owner, addsec), MMIX_REG_CONTENTS_SECTION_NAME) == 0)) { @@ -2672,14 +2721,13 @@ tc_gen_reloc (section, fixP) && (S_GET_SEGMENT (addsy) != real_reg_section || val > 255 || val < 0) - && ! bfd_is_abs_section (S_GET_SEGMENT (addsy))) + && ! bfd_is_abs_section (addsec)) goto badop; /* Set the "immediate" bit of the insn if this relocation is to Z field when the value is a numeric value, i.e. not a register. */ if ((fixP->fx_where & 3) == 3 - && (addsy == NULL - || S_GET_SEGMENT (addsy) == absolute_section)) + && (addsy == NULL || bfd_is_abs_section (addsec))) buf[-3] |= IMM_OFFSET_BIT; buf[0] = val; @@ -2687,8 +2735,8 @@ tc_gen_reloc (section, fixP) case BFD_RELOC_MMIX_BASE_PLUS_OFFSET: if (addsy != NULL - && strcmp (bfd_get_section_name (addsec->owner, addsec), - MMIX_REG_CONTENTS_SECTION_NAME) == 0) + && strcmp (bfd_get_section_name (addsec->owner, addsec), + MMIX_REG_CONTENTS_SECTION_NAME) == 0) { /* This changed into a register; the relocation is for the register-contents section. The constant part remains zero. */ @@ -2701,16 +2749,16 @@ tc_gen_reloc (section, fixP) If we encounter any other defined symbol, then we must find a suitable register and emit a reloc. */ - if (addsy == NULL - || S_GET_SEGMENT (addsy) != real_reg_section) + if (addsy == NULL || addsec != real_reg_section) { struct mmix_symbol_gregs *gregs; struct mmix_symbol_greg_fixes *fix; - if (S_IS_DEFINED (addsy)) + if (S_IS_DEFINED (addsy) + && !bfd_is_com_section (addsec) + && !S_IS_WEAK (addsy)) { - if (! symbol_section_p (addsy) - && ! bfd_is_abs_section (S_GET_SEGMENT (addsy))) + if (! symbol_section_p (addsy) && ! bfd_is_abs_section (addsec)) as_fatal (_("internal: BFD_RELOC_MMIX_BASE_PLUS_OFFSET not resolved to section")); /* If this is an absolute symbol sufficiently near @@ -2720,7 +2768,7 @@ tc_gen_reloc (section, fixP) comparisons. */ if (lowest_data_loc != (bfd_vma) -1 && (bfd_vma) val + 256 > lowest_data_loc - && bfd_is_abs_section (S_GET_SEGMENT (addsy))) + && bfd_is_abs_section (addsec)) { val -= (offsetT) lowest_data_loc; addsy = section_symbol (data_section); @@ -2728,7 +2776,7 @@ tc_gen_reloc (section, fixP) /* Likewise text section. */ else if (lowest_text_loc != (bfd_vma) -1 && (bfd_vma) val + 256 > lowest_text_loc - && bfd_is_abs_section (S_GET_SEGMENT (addsy))) + && bfd_is_abs_section (addsec)) { val -= (offsetT) lowest_text_loc; addsy = section_symbol (text_section); @@ -2738,8 +2786,7 @@ tc_gen_reloc (section, fixP) gregs = *symbol_get_tc (addsy); /* If that symbol does not have any associated GREG definitions, - we can't do anything. FIXME: implement allocate-on-demand in - the linker. */ + we can't do anything. */ if (gregs == NULL || (fix = bsearch (&val, gregs->greg_fixes, gregs->n_gregs, sizeof (gregs->greg_fixes[0]), @@ -2750,8 +2797,17 @@ tc_gen_reloc (section, fixP) before the address we want. */ || fix->offs + 255 < val) { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("no suitable GREG definition for operands")); + /* We can either let the linker allocate GREGs + automatically, or emit an error. */ + if (allocate_undefined_gregs_in_linker) + { + /* The values in baddsy and addend are right. */ + code = fixP->fx_r_type; + break; + } + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("no suitable GREG definition for operands")); return NULL; } else @@ -2781,7 +2837,7 @@ tc_gen_reloc (section, fixP) case BFD_RELOC_MMIX_REG: if (addsy != NULL - && (bfd_is_und_section (S_GET_SEGMENT (addsy)) + && (bfd_is_und_section (addsec) || strcmp (bfd_get_section_name (addsec->owner, addsec), MMIX_REG_CONTENTS_SECTION_NAME) == 0)) { @@ -2790,10 +2846,10 @@ tc_gen_reloc (section, fixP) } if (addsy != NULL - && (S_GET_SEGMENT (addsy) != real_reg_section + && (addsec != real_reg_section || val > 255 || val < 0) - && ! bfd_is_und_section (S_GET_SEGMENT (addsy))) + && ! bfd_is_und_section (addsec)) /* Drop through to error message. */ ; else @@ -2801,11 +2857,11 @@ tc_gen_reloc (section, fixP) buf[0] = val; return NULL; } - /* FALLTHROUGH. */ + /* FALLTHROUGH. */ - /* The others are supposed to be handled by md_apply_fix3. + /* The others are supposed to be handled by md_apply_fix. FIXME: ... which isn't called when -linkrelax. Move over - md_apply_fix3 code here for everything reasonable. */ + md_apply_fix code here for everything reasonable. */ badop: default: as_bad_where @@ -2814,14 +2870,13 @@ tc_gen_reloc (section, fixP) /* Unmark this symbol as used in a reloc, so we don't bump into a BFD assert when trying to output reg_section. FIXME: A gas bug. */ - if (addsy) - symbol_clear_used_in_reloc (addsy); + fixP->fx_addsy = NULL; return NULL; } - relP = (arelent *) xmalloc (sizeof (arelent)); - assert (relP != 0); - relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + relP = XNEW (arelent); + gas_assert (relP != 0); + relP->sym_ptr_ptr = XNEW (asymbol *); *relP->sym_ptr_ptr = baddsy; relP->address = fixP->fx_frag->fr_address + fixP->fx_where; @@ -2850,10 +2905,10 @@ tc_gen_reloc (section, fixP) ugly labels_without_colons etc. */ void -mmix_handle_mmixal () +mmix_handle_mmixal (void) { - char *s0 = input_line_pointer; - char *s; + char *insn; + char *s = input_line_pointer; char *label = NULL; char c; @@ -2863,93 +2918,96 @@ mmix_handle_mmixal () if (mmix_gnu_syntax) return; - /* If the first character is a '.', then it's a pseudodirective, not a - label. Make GAS not handle label-without-colon on this line. We - also don't do mmixal-specific stuff on this line. */ - if (input_line_pointer[0] == '.') - { - label_without_colon_this_line = 0; - return; - } - - /* Don't handle empty lines here. */ - while (1) - { - if (*s0 == 0 || is_end_of_line [(unsigned int) *s0]) - return; - - if (! ISSPACE (*s0)) - break; - - s0++; - } - /* If we're on a line with a label, check if it's a mmixal fb-label. Save an indicator and skip the label; it must be set only after all fb-labels of expressions are evaluated. */ - if (ISDIGIT (input_line_pointer[0]) - && input_line_pointer[1] == 'H' - && ISSPACE (input_line_pointer[2])) + if (ISDIGIT (s[0]) && s[1] == 'H' && ISSPACE (s[2])) { - char *s; - current_fb_label = input_line_pointer[0] - '0'; + current_fb_label = s[0] - '0'; /* We have to skip the label, but also preserve the newlineness of the previous character, since the caller checks that. It's a mess we blame on the caller. */ - input_line_pointer[1] = input_line_pointer[-1]; - input_line_pointer += 2; + s[1] = s[-1]; + s += 2; + input_line_pointer = s; - s = input_line_pointer; while (*s && ISSPACE (*s) && ! is_end_of_line[(unsigned int) *s]) s++; /* For errors emitted here, the book-keeping is off by one; the caller is about to bump the counters. Adjust the error messages. */ - if (is_end_of_line [(unsigned int) *s]) + if (is_end_of_line[(unsigned int) *s]) { - char *name; unsigned int line; - as_where (&name, &line); + const char * name = as_where (&line); as_bad_where (name, line + 1, _("[0-9]H labels may not appear alone on a line")); current_fb_label = -1; } if (*s == '.') { - char *name; unsigned int line; - as_where (&name, &line); + const char * name = as_where (&line); as_bad_where (name, line + 1, _("[0-9]H labels do not mix with dot-pseudos")); current_fb_label = -1; } + + /* Back off to the last space before the opcode so we don't handle + the opcode as a label. */ + s--; } else + current_fb_label = -1; + + if (*s == '.') { - current_fb_label = -1; - if (is_name_beginner (input_line_pointer[0])) - label = input_line_pointer; + /* If the first character is a '.', then it's a pseudodirective, not a + label. Make GAS not handle label-without-colon on this line. We + also don't do mmixal-specific stuff on this line. */ + label_without_colon_this_line = 0; + return; } - s0 = input_line_pointer; - /* Skip over label. */ - while (*s0 && is_part_of_name (*s0)) - s0++; - - /* Remove trailing ":" off labels, as they'd otherwise be considered - part of the name. But don't do it for local labels. */ - if (s0 != input_line_pointer && s0[-1] == ':' - && (s0 - 2 != input_line_pointer - || ! ISDIGIT (s0[-2]))) - s0[-1] = ' '; - else if (label != NULL) + if (*s == 0 || is_end_of_line[(unsigned int) *s]) + /* We avoid handling empty lines here. */ + return; + + if (is_name_beginner (*s)) + label = s; + + /* If there is a label, skip over it. */ + while (*s && is_part_of_name (*s)) + s++; + + /* Find the start of the instruction or pseudo following the label, + if there is one. */ + for (insn = s; + *insn && ISSPACE (*insn) && ! is_end_of_line[(unsigned int) *insn]; + insn++) + /* Empty */ + ; + + /* Remove a trailing ":" off labels, as they'd otherwise be considered + part of the name. But don't do this for local labels. */ + if (s != input_line_pointer && s[-1] == ':' + && (s - 2 != input_line_pointer + || ! ISDIGIT (s[-2]))) + s[-1] = ' '; + else if (label != NULL + /* For a lone label on a line, we don't attach it to the next + instruction or MMIXAL-pseudo (getting its alignment). Thus + is acts like a "normal" :-ended label. Ditto if it's + followed by a non-MMIXAL pseudo. */ + && !is_end_of_line[(unsigned int) *insn] + && *insn != '.') { /* For labels that don't end in ":", we save it so we can later give it the same alignment and address as the associated instruction. */ /* Make room for the label including the ending nul. */ - int len_0 = s0 - label + 1; + size_t len_0 = s - label + 1; /* Save this label on the MMIX symbol obstack. Saving it on an obstack is needless for "IS"-pseudos, but it's harmless and we @@ -2959,14 +3017,10 @@ mmix_handle_mmixal () pending_label[len_0 - 1] = 0; } - while (*s0 && ISSPACE (*s0) && ! is_end_of_line [(unsigned int) *s0]) - s0++; - - if (pending_label != NULL && is_end_of_line [(unsigned int) *s0]) - /* Whoops, this was actually a lone label on a line. Like :-ended - labels, we don't attach such labels to the next instruction or - pseudo. */ - pending_label = NULL; + /* If we have a non-MMIXAL pseudo, we have not business with the rest of + the line. */ + if (*insn == '.') + return; /* Find local labels of operands. Look for "[0-9][FB]" where the characters before and after are not part of words. Break if a single @@ -2978,18 +3032,17 @@ mmix_handle_mmixal () /* First make sure we don't have any of the magic characters on the line appearing as input. */ - s = s0; while (*s) { c = *s++; - if (is_end_of_line [(unsigned int) c]) + if (is_end_of_line[(unsigned int) c]) break; if (c == MAGIC_FB_BACKWARD_CHAR || c == MAGIC_FB_FORWARD_CHAR) as_bad (_("invalid characters in input")); } /* Scan again, this time looking for ';' after operands. */ - s = s0; + s = insn; /* Skip the insn. */ while (*s @@ -3019,7 +3072,7 @@ mmix_handle_mmixal () /* FIXME: Test-case for semi-colon in string. */ while (*s && *s != '"' - && (! is_end_of_line [(unsigned int) *s] || *s == ';')) + && (! is_end_of_line[(unsigned int) *s] || *s == ';')) s++; if (*s == '"') @@ -3029,7 +3082,9 @@ mmix_handle_mmixal () { if ((s[1] != 'B' && s[1] != 'F') || is_part_of_name (s[-1]) - || is_part_of_name (s[2])) + || is_part_of_name (s[2]) + /* Don't treat e.g. #1F as a local-label reference. */ + || (s != input_line_pointer && s[-1] == '#')) s++; else { @@ -3055,7 +3110,7 @@ mmix_handle_mmixal () /* Make IS into an EQU by replacing it with "= ". Only match upper-case though; let lower-case be a syntax error. */ - s = s0; + s = insn; if (s[0] == 'I' && s[1] == 'S' && ISSPACE (s[2])) { *s = '='; @@ -3161,8 +3216,7 @@ mmix_handle_mmixal () We fill in the label as an expression. */ void -mmix_fb_label (expP) - expressionS *expP; +mmix_fb_label (expressionS *expP) { symbolS *sym; char *fb_internal_name; @@ -3211,40 +3265,30 @@ mmix_fb_label (expP) relaxing. */ int -mmix_force_relocation (fixP) - fixS * fixP; +mmix_force_relocation (fixS *fixP) { if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL - || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT - || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY || fixP->fx_r_type == BFD_RELOC_MMIX_BASE_PLUS_OFFSET) return 1; - /* FIXME: This is dubious. Handling of weak symbols should have been - caught before we get here. */ - if ((fixP->fx_addsy && S_IS_WEAK (fixP->fx_addsy))) - return 1; - if (linkrelax) return 1; - /* All our pcrel relocations are must-keep. Note that md_apply_fix3 is + /* All our pcrel relocations are must-keep. Note that md_apply_fix is called *after* this, and will handle getting rid of the presumed reloc; a relocation isn't *forced* other than to be handled by - md_apply_fix3 (or tc_gen_reloc if linkrelax). */ + md_apply_fix (or tc_gen_reloc if linkrelax). */ if (fixP->fx_pcrel) return 1; - return 0; + return generic_force_reloc (fixP); } /* The location from which a PC relative jump should be calculated, given a PC relative reloc. */ long -md_pcrel_from_section (fixP, sec) - fixS * fixP; - segT sec; +md_pcrel_from_section (fixS *fixP, segT sec) { if (fixP->fx_addsy != (symbolS *) NULL && (! S_IS_DEFINED (fixP->fx_addsy) @@ -3259,47 +3303,29 @@ md_pcrel_from_section (fixP, sec) } /* Adjust the symbol table. We make reg_section relative to the real - register section. - - FIXME: There's a gas bug; should be fixed when the reg_section symbol - is "accidentally" saved for relocs which are really fixups that will be - fixed up. */ + register section. */ void -mmix_adjust_symtab () +mmix_adjust_symtab (void) { symbolS *sym; - symbolS *prevsym; symbolS *regsec = section_symbol (reg_section); - segT realregsec = NULL; - for (prevsym = sym = symbol_rootP; - sym != NULL; - prevsym = sym, sym = symbol_next (sym)) + for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) if (S_GET_SEGMENT (sym) == reg_section) { - if (sym == regsec - || (!S_IS_EXTERN (sym) && !symbol_used_in_reloc_p (sym))) + if (sym == regsec) { + if (S_IS_EXTERNAL (sym) || symbol_used_in_reloc_p (sym)) + abort (); symbol_remove (sym, &symbol_rootP, &symbol_lastP); - - /* We make one extra turn, or we'll lose the next symbol. We - assume that the symbol we remove is not the symbol root - (.text normally is). */ - sym = prevsym; } else - { - /* Change section to the *real* register section, so it gets - proper treatment when writing it out. Only do this for - global symbols. This also means we don't have to check for - $0..$255. */ - if (realregsec == NULL) - realregsec - = bfd_make_section_old_way (stdoutput, MMIX_REG_SECTION_NAME); - - S_SET_SEGMENT (sym, realregsec); - } + /* Change section to the *real* register section, so it gets + proper treatment when writing it out. Only do this for + global symbols. This also means we don't have to check for + $0..$255. */ + S_SET_SEGMENT (sym, real_reg_section); } } @@ -3310,7 +3336,7 @@ mmix_adjust_symtab () thought at the time I first wrote this. */ int -mmix_label_without_colon_this_line () +mmix_label_without_colon_this_line (void) { int retval = label_without_colon_this_line; @@ -3326,18 +3352,123 @@ mmix_label_without_colon_this_line () join with. */ long -mmix_md_relax_frag (seg, fragP, stretch) - segT seg; - fragS *fragP; - long stretch; +mmix_md_relax_frag (segT seg, fragS *fragP, long stretch) { - if (fragP->fr_subtype != STATE_GREG_DEF - && fragP->fr_subtype != STATE_GREG_UNDF) - return relax_frag (seg, fragP, stretch); + switch (fragP->fr_subtype) + { + /* Growth for this type has been handled by mmix_md_end and + correctly estimated, so there's nothing more to do here. */ + case STATE_GREG_DEF: + return 0; - /* If we're defined, we don't grow. */ - if (fragP->fr_subtype == STATE_GREG_DEF) - return 0; + case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO): + { + /* We need to handle relaxation type ourselves, since relax_frag + doesn't update fr_subtype if there's no size increase in the + current section; when going from plain PUSHJ to a stub. This + is otherwise functionally the same as relax_frag in write.c, + simplified for this case. */ + offsetT aim; + addressT target; + addressT address; + symbolS *symbolP; + target = fragP->fr_offset; + address = fragP->fr_address; + symbolP = fragP->fr_symbol; + + if (symbolP) + { + fragS *sym_frag; + + sym_frag = symbol_get_frag (symbolP); + know (S_GET_SEGMENT (symbolP) != absolute_section + || sym_frag == &zero_address_frag); + target += S_GET_VALUE (symbolP); + + /* If frag has yet to be reached on this pass, assume it will + move by STRETCH just as we did. If this is not so, it will + be because some frag between grows, and that will force + another pass. */ + + if (stretch != 0 + && sym_frag->relax_marker != fragP->relax_marker + && S_GET_SEGMENT (symbolP) == seg) + target += stretch; + } + + aim = target - address - fragP->fr_fix; + if (aim >= PUSHJ_0B && aim <= PUSHJ_0F) + { + /* Target is reachable with a PUSHJ. */ + segment_info_type *seginfo = seg_info (seg); + + /* If we're at the end of a relaxation round, clear the stub + counter as initialization for the next round. */ + if (fragP == seginfo->tc_segment_info_data.last_stubfrag) + seginfo->tc_segment_info_data.nstubs = 0; + return 0; + } + + /* Not reachable. Try a stub. */ + fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO); + } + /* FALLTHROUGH. */ + + /* See if this PUSHJ is redirectable to a stub. */ + case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO): + { + segment_info_type *seginfo = seg_info (seg); + fragS *lastfrag = seginfo->frchainP->frch_last; + relax_substateT prev_type = fragP->fr_subtype; + + /* The last frag is always an empty frag, so it suffices to look + at its address to know the ending address of this section. */ + know (lastfrag->fr_type == rs_fill + && lastfrag->fr_fix == 0 + && lastfrag->fr_var == 0); + + /* For this PUSHJ to be relaxable into a call to a stub, the + distance must be no longer than 256k bytes from the PUSHJ to + the end of the section plus the maximum size of stubs so far. */ + if ((lastfrag->fr_address + + stretch + + PUSHJ_MAX_LEN * seginfo->tc_segment_info_data.nstubs) + - (fragP->fr_address + fragP->fr_fix) + > GETA_0F + || !pushj_stubs) + fragP->fr_subtype = mmix_relax_table[prev_type].rlx_more; + else + seginfo->tc_segment_info_data.nstubs++; + + /* If we're at the end of a relaxation round, clear the stub + counter as initialization for the next round. */ + if (fragP == seginfo->tc_segment_info_data.last_stubfrag) + seginfo->tc_segment_info_data.nstubs = 0; + + return + (mmix_relax_table[fragP->fr_subtype].rlx_length + - mmix_relax_table[prev_type].rlx_length); + } + + case ENCODE_RELAX (STATE_PUSHJ, STATE_MAX): + { + segment_info_type *seginfo = seg_info (seg); + + /* Need to cover all STATE_PUSHJ states to act on the last stub + frag (the end of this relax round; initialization for the + next). */ + if (fragP == seginfo->tc_segment_info_data.last_stubfrag) + seginfo->tc_segment_info_data.nstubs = 0; + + return 0; + } + + default: + return relax_frag (seg, fragP, stretch); + + case STATE_GREG_UNDF: + BAD_CASE (fragP->fr_subtype); + } as_fatal (_("internal: unexpected relax type %d:%d"), fragP->fr_type, fragP->fr_subtype); @@ -3347,10 +3478,12 @@ mmix_md_relax_frag (seg, fragP, stretch) /* Various things we punt until all input is seen. */ void -mmix_md_end () +mmix_md_end (void) { fragS *fragP; symbolS *mainsym; + asection *regsec; + struct loc_assert_s *loc_assert; int i; /* The first frag of GREG:s going into the register contents section. */ @@ -3385,8 +3518,8 @@ mmix_md_end () { symbolS *symbolP; char locsymbol[sizeof (":") - 1 - + sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1 - + sizeof (".data")]; + + sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1 + + sizeof (".data")]; sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX, ".data"); @@ -3408,6 +3541,38 @@ mmix_md_end () S_SET_EXTERNAL (mainsym); } + /* Check that we didn't LOC into the unknown, or rather that when it + was unknown, we actually change sections. */ + for (loc_assert = loc_asserts; + loc_assert != NULL; + loc_assert = loc_assert->next) + { + segT actual_seg; + + resolve_symbol_value (loc_assert->loc_sym); + actual_seg = S_GET_SEGMENT (loc_assert->loc_sym); + if (actual_seg != loc_assert->old_seg) + { + const char *fnam; + unsigned int line; + int e_valid = expr_symbol_where (loc_assert->loc_sym, &fnam, &line); + + gas_assert (e_valid == 1); + as_bad_where (fnam, line, + _("LOC to section unknown or indeterminable " + "at first pass")); + + /* Patch up the generic location data to avoid cascading + error messages from later passes. (See original in + write.c:relax_segment.) */ + fragP = loc_assert->frag; + fragP->fr_type = rs_align; + fragP->fr_subtype = 0; + fragP->fr_offset = 0; + fragP->fr_fix = 0; + } + } + if (n_of_raw_gregs != 0) { /* Emit GREGs. They are collected in order of appearance, but must @@ -3415,9 +3580,9 @@ mmix_md_end () and the same allocation order (within a file) as mmixal. */ segT this_segment = now_seg; subsegT this_subsegment = now_subseg; - asection *regsec - = bfd_make_section_old_way (stdoutput, - MMIX_REG_CONTENTS_SECTION_NAME); + + regsec = bfd_make_section_old_way (stdoutput, + MMIX_REG_CONTENTS_SECTION_NAME); subseg_set (regsec, 0); /* Finally emit the initialization-value. Emit a variable frag, which @@ -3444,6 +3609,11 @@ mmix_md_end () subseg_set (this_segment, this_subsegment); } + regsec = bfd_get_section_by_name (stdoutput, MMIX_REG_CONTENTS_SECTION_NAME); + /* Mark the section symbol as being OK for a reloc. */ + if (regsec != NULL) + regsec->symbol->flags |= BSF_KEEP; + /* Iterate over frags resulting from GREGs and move those that evidently have the same value together and point one to another. @@ -3522,9 +3692,7 @@ mmix_md_end () /* qsort function for mmix_symbol_gregs. */ static int -cmp_greg_symbol_fixes (parg, qarg) - const PTR parg; - const PTR qarg; +cmp_greg_symbol_fixes (const void *parg, const void *qarg) { const struct mmix_symbol_greg_fixes *p = (const struct mmix_symbol_greg_fixes *) parg; @@ -3541,7 +3709,7 @@ cmp_greg_symbol_fixes (parg, qarg) as an ELF section. */ void -mmix_frob_file () +mmix_frob_file (void) { int i; struct mmix_symbol_gregs *all_greg_symbols[MAX_GREGS]; @@ -3572,7 +3740,9 @@ mmix_frob_file () /* If the symbol is defined, then it must be resolved to a section symbol at this time, or else we don't know how to handle it. */ - if (S_IS_DEFINED (sym)) + if (S_IS_DEFINED (sym) + && !bfd_is_com_section (S_GET_SEGMENT (sym)) + && !S_IS_WEAK (sym)) { if (! symbol_section_p (sym) && ! bfd_is_abs_section (S_GET_SEGMENT (sym))) @@ -3605,7 +3775,7 @@ mmix_frob_file () if (gregs == NULL) { - gregs = xmalloc (sizeof (*gregs)); + gregs = XNEW (struct mmix_symbol_gregs); gregs->n_gregs = 0; symbol_set_tc (sym, &gregs); all_greg_symbols[n_greg_symbols++] = gregs; @@ -3623,20 +3793,11 @@ mmix_frob_file () if (real_reg_section != NULL) { - asection **secpp; - /* FIXME: Pass error state gracefully. */ if (bfd_get_section_flags (stdoutput, real_reg_section) & SEC_HAS_CONTENTS) as_fatal (_("register section has contents\n")); - /* FIXME: This does not seem like the proper way to kill a section, - but it's the way it's done elsewhere, like elf64-alpha.c. */ - /* Really remove the section. */ - for (secpp = &stdoutput->sections; - *secpp != real_reg_section; - secpp = &(*secpp)->next) - ; - *secpp = (*secpp)->next; + bfd_section_list_remove (stdoutput, real_reg_section); --stdoutput->section_count; } @@ -3649,12 +3810,10 @@ mmix_frob_file () If the name isn't a built-in name and parsed into *EXPP, return zero. */ int -mmix_parse_predefined_name (name, expP) - char *name; - expressionS *expP; +mmix_parse_predefined_name (char *name, expressionS *expP) { char *canon_name; - char *handler_charp; + const char *handler_charp; const char handler_chars[] = "DVWIOUZX"; symbolS *symp; @@ -3729,7 +3888,7 @@ mmix_parse_predefined_name (name, expP) return 0; for (i = 0; - i < sizeof (predefined_abs_syms)/sizeof (predefined_abs_syms[0]); + i < sizeof (predefined_abs_syms) / sizeof (predefined_abs_syms[0]); i++) if (strcmp (canon_name, predefined_abs_syms[i].name) == 0) { @@ -3755,56 +3914,12 @@ mmix_parse_predefined_name (name, expP) return 1; } -/* Worker for mmix_frob_file_before_adjust. */ - -static void -mmix_frob_local_reloc (abfd, sec, xxx) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - PTR xxx ATTRIBUTE_UNUSED; -{ - segment_info_type *seginfo = seg_info (sec); - fixS *fixp; - - if (seginfo == NULL) - return; - - for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) - if (! fixp->fx_done && fixp->fx_addsy != NULL) - { - symbolS *sym = fixp->fx_addsy; - asection *section = S_GET_SEGMENT (sym); - - if (section == reg_section - && fixp->fx_r_type == BFD_RELOC_MMIX_LOCAL) - { - /* If the register is marked global, we don't need to replace - with the *real* register section since that will be done - when the symbol is changed. */ - if (! S_IS_EXTERNAL (sym)) - /* If it's a local symbol, we replace it with an anonymous - one with the same constant value. */ - fixp->fx_addsy = expr_build_uconstant (S_GET_VALUE (sym)); - } - } -} - -/* Change fixups for register symbols for BFD_MMIX_LOCAL to be for an - absolute symbol. */ - -void -mmix_frob_file_before_adjust () -{ - return; - bfd_map_over_sections (stdoutput, mmix_frob_local_reloc, (char *) 0); -} - /* Just check that we don't have a BSPEC/ESPEC pair active when changing sections "normally", and get knowledge about alignment from the new section. */ void -mmix_md_elf_section_change_hook () +mmix_md_elf_section_change_hook (void) { if (doing_bspec) as_bad (_("section change from within a BSPEC/ESPEC pair is not supported")); @@ -3817,8 +3932,7 @@ mmix_md_elf_section_change_hook () section too. */ static void -s_loc (ignore) - int ignore ATTRIBUTE_UNUSED; +s_loc (int ignore ATTRIBUTE_UNUSED) { segT section; expressionS exp; @@ -3826,7 +3940,7 @@ s_loc (ignore) symbolS *sym; offsetT off; - /* Must not have a BSPEC in progress. */ + /* Must not have a BSPEC in progress. */ if (doing_bspec) { as_bad (_("directive LOC from within a BSPEC/ESPEC pair is not supported")); @@ -3837,20 +3951,39 @@ s_loc (ignore) if (exp.X_op == O_illegal || exp.X_op == O_absent - || exp.X_op == O_big - || section == undefined_section) + || exp.X_op == O_big) { as_bad (_("invalid LOC expression")); return; } + if (section == undefined_section) + { + /* This is an error or a LOC with an expression involving + forward references. For the expression to be correctly + evaluated, we need to force a proper symbol; gas loses track + of the segment for "local symbols". */ + if (exp.X_op == O_add) + { + symbol_get_value_expression (exp.X_op_symbol); + symbol_get_value_expression (exp.X_add_symbol); + } + else + { + gas_assert (exp.X_op == O_symbol); + symbol_get_value_expression (exp.X_add_symbol); + } + } + if (section == absolute_section) { /* Translate a constant into a suitable section. */ if (exp.X_add_number < ((offsetT) 0x20 << 56)) { - /* Lower than Data_Segment - assume it's .text. */ + /* Lower than Data_Segment or in the reserved area (the + segment number is >= 0x80, appearing negative) - assume + it's .text. */ section = text_section; /* Save the lowest seen location, so we can pass on this @@ -3862,8 +3995,8 @@ s_loc (ignore) this one), we org at (this - lower). There's an implicit "LOC 0" before any entered code. FIXME: handled by spurious settings of text_has_contents. */ - if (exp.X_add_number < 0 - || exp.X_add_number < (offsetT) lowest_text_loc) + if (lowest_text_loc != (bfd_vma) -1 + && (bfd_vma) exp.X_add_number < lowest_text_loc) { as_bad (_("LOC expression stepping backwards is not supported")); exp.X_op = O_absent; @@ -3886,7 +4019,8 @@ s_loc (ignore) } else { - /* Do the same for the .data section. */ + /* Do the same for the .data section, except we don't have + to worry about exp.X_add_number carrying a sign. */ section = data_section; if (exp.X_add_number < (offsetT) lowest_data_loc) @@ -3912,7 +4046,9 @@ s_loc (ignore) } } - if (section != now_seg) + /* If we can't deduce the section, it must be the current one. + Below, we arrange to assert this. */ + if (section != now_seg && section != undefined_section) { obj_elf_section_change_hook (); subseg_set (section, 0); @@ -3923,16 +4059,41 @@ s_loc (ignore) if (exp.X_op != O_absent) { + symbolS *esym = NULL; + if (exp.X_op != O_constant && exp.X_op != O_symbol) { /* Handle complex expressions. */ - sym = make_expr_symbol (&exp); + esym = sym = make_expr_symbol (&exp); off = 0; } else { sym = exp.X_add_symbol; off = exp.X_add_number; + + if (section == undefined_section) + { + /* We need an expr_symbol when tracking sections. In + order to make this an expr_symbol with file and line + tracked, we have to make the exp non-trivial; not an + O_symbol with .X_add_number == 0. The constant part + is unused. */ + exp.X_add_number = 1; + esym = make_expr_symbol (&exp); + } + } + + /* Track the LOC's where we couldn't deduce the section: assert + that we weren't supposed to change section. */ + if (section == undefined_section) + { + struct loc_assert_s *next = loc_asserts; + loc_asserts = XNEW (struct loc_assert_s); + loc_asserts->next = next; + loc_asserts->old_seg = now_seg; + loc_asserts->loc_sym = esym; + loc_asserts->frag = frag_now; } p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0); @@ -3947,10 +4108,9 @@ s_loc (ignore) by comma. */ static void -mmix_byte () +mmix_byte (void) { unsigned int c; - char *start; if (now_seg == text_section) text_has_contents = 1; @@ -3964,7 +4124,6 @@ mmix_byte () { case '\"': ++input_line_pointer; - start = input_line_pointer; while (is_a_char (c = next_char_of_string ())) { FRAG_APPEND_1_CHAR (c); @@ -4037,11 +4196,9 @@ mmix_byte () lenient than mmix_byte but FIXME: they should eventually merge. */ static void -mmix_cons (nbytes) - int nbytes; +mmix_cons (int nbytes) { expressionS exp; - char *start; /* If we don't have any contents, then it's ok to have a specified start address that is not a multiple of the max data size. We will then @@ -4100,7 +4257,7 @@ mmix_cons (nbytes) SKIP_WHITESPACE (); - if (is_end_of_line [(unsigned int) *input_line_pointer]) + if (is_end_of_line[(unsigned int) *input_line_pointer]) { /* Default to zero if the expression was absent. */ @@ -4122,7 +4279,6 @@ mmix_cons (nbytes) bytes. */ case '\"': ++input_line_pointer; - start = input_line_pointer; while (is_a_char (c = next_char_of_string ())) { exp.X_op = O_constant; @@ -4167,11 +4323,8 @@ mmix_cons (nbytes) Arguably this is a GCC bug. */ void -mmix_md_do_align (n, fill, len, max) - int n; - char *fill ATTRIBUTE_UNUSED; - int len ATTRIBUTE_UNUSED; - int max ATTRIBUTE_UNUSED; +mmix_md_do_align (int n, char *fill ATTRIBUTE_UNUSED, + int len ATTRIBUTE_UNUSED, int max ATTRIBUTE_UNUSED) { last_alignment = n; want_unaligned = n == 0;