/* tc-mmix.c -- Assembler for Don Knuth's MMIX.
- Copyright (C) 2001, 2002 Free Software Foundation.
+ Copyright (C) 2001, 2002, 2003 Free Software Foundation.
This file is part of GAS, the GNU Assembler.
#include <stdio.h>
+#include <limits.h>
#include "as.h"
#include "subsegs.h"
#include "bfd.h"
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));
/* 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
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
/* 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? */
#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},
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}
};
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)
#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 C99 or gcc-specific values (assuming
+ long long is 64 bits on the host). */
+#ifdef LLONG_MIN
+#define PUSHJSTUB_MIN LLONG_MIN
+#elsif defined (LONG_LONG_MIN)
+#define PUSHJSTUB_MIN LONG_LONG_MIN
+#else
+#define PUSHJSTUB_MIN LONG_MIN
+#endif
+#ifdef LLONG_MAX
+#define PUSHJSTUB_MAX LLONG_MAX
+#elsif defined (LONG_LONG_MAX)
+#define PUSHJSTUB_MAX LONG_LONG_MAX
+#else
+#define PUSHJSTUB_MAX LONG_MAX
+#endif
+
#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
{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,
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[] =
/* 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}
};
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. */
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]);
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);
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];
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;
allocate_undefined_gregs_in_linker = 1;
break;
+ case OPTION_NOPUSHJSTUBS:
+ pushj_stubs = 0;
+ break;
+
default:
return 0;
}
mmix_discard_rest_of_line ()
{
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++;
}
/* 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 ();
}
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,
/* 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. */
break;
case mmix_operands_jmp:
- /* A JMP. Everyhing is already done. */
+ /* A JMP. Everything is already done. */
break;
case mmix_operands_roundregs:
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. */
break;
case mmix_operands_regaddr:
- /* A GETA/branch-type. */
+ /* A GETA/branch-type. */
break;
case mmix_operands_get:
break;
}
- /* "0,$Z"; UNSAVE. */
+ /* "0,$Z"; UNSAVE. */
if (n_operands != 2
|| exp[0].X_op != O_constant
|| exp[0].X_add_number != 0
{
/* 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. */
int
mmix_assemble_return_nonzero (str)
- char *str;
+ char *str;
{
int last_error_count = had_errors ();
char *s2 = str;
register. */
c = get_symbol_end ();
- if (! is_end_of_line [(unsigned char) c])
+ if (! is_end_of_line[(unsigned char) c])
input_line_pointer++;
if (*p)
#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 \
{
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. */
for (i = 0; i < prec; i++)
{
md_number_to_chars (litP, (valueT) words[i], 2);
- litP += 2;
+ litP += 2;
}
return NULL;
}
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. */
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_fix3 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): \
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;
&& 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;
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)
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
&& (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:
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);
asymbol *baddsy = addsy != NULL ? symbol_get_bfdsym (addsy) : NULL;
- bfd_vma addend = val - (baddsy == NULL ? 0 : bfd_asymbol_value (baddsy));
+ 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
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:
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 (addsec))
as_fatal (_("internal: BFD_RELOC_MMIX_BASE_PLUS_OFFSET not resolved to section"));
buf[0] = val;
return NULL;
}
- /* FALLTHROUGH. */
+ /* FALLTHROUGH. */
/* The others are supposed to be handled by md_apply_fix3.
FIXME: ... which isn't called when -linkrelax. Move over
/* 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;
}
/* Don't handle empty lines here. */
while (1)
{
- if (*s0 == 0 || is_end_of_line [(unsigned int) *s0])
+ if (*s0 == 0 || is_end_of_line[(unsigned int) *s0])
return;
if (! ISSPACE (*s0))
/* 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;
}
s0 = input_line_pointer;
- /* Skip over label. */
+ /* Skip over label. */
while (*s0 && is_part_of_name (*s0))
s0++;
pending_label[len_0 - 1] = 0;
}
- while (*s0 && ISSPACE (*s0) && ! is_end_of_line [(unsigned int) *s0])
+ while (*s0 && ISSPACE (*s0) && ! is_end_of_line[(unsigned int) *s0])
s0++;
- if (pending_label != NULL && is_end_of_line [(unsigned int) *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. */
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"));
/* 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 == '"')
int
mmix_force_relocation (fixP)
- fixS * fixP;
+ 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;
if (fixP->fx_pcrel)
return 1;
- return 0;
+ return generic_force_reloc (fixP);
}
/* The location from which a PC relative jump should be calculated,
}
/* 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 ()
{
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_EXTERN (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);
}
}
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);
{
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");
/* 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)))
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)
{
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. */
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"));
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. */