/* tc-mn10300.c -- Assembler code for the Matsushita 10300
- Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation.
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006, 2007 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,
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. */
-#include <stdio.h>
-#include <ctype.h>
#include "as.h"
+#include "safe-ctype.h"
#include "subsegs.h"
#include "opcode/mn10300.h"
#include "dwarf2dbg.h"
+#include "libiberty.h"
\f
/* Structure to hold information about predefined registers. */
struct reg_name
as in 0d1.0. */
const char FLT_CHARS[] = "dD";
\f
-const relax_typeS md_relax_table[] = {
- /* bCC relaxing */
+const relax_typeS md_relax_table[] =
+{
+ /* The plus values for the bCC and fBCC instructions in the table below
+ are because the branch instruction is translated into a jump
+ instruction that is now +2 or +3 bytes further on in memory, and the
+ correct size of jump instruction must be selected. */
+ /* bCC relaxing. */
{0x7f, -0x80, 2, 1},
- {0x7fff, -0x8000, 5, 2},
+ {0x7fff + 2, -0x8000 + 2, 5, 2},
{0x7fffffff, -0x80000000, 7, 0},
- /* bCC relaxing (uncommon cases) */
+ /* bCC relaxing (uncommon cases for 3byte length instructions) */
{0x7f, -0x80, 3, 4},
- {0x7fff, -0x8000, 6, 5},
+ {0x7fff + 3, -0x8000 + 3, 6, 5},
{0x7fffffff, -0x80000000, 8, 0},
- /* call relaxing */
+ /* call relaxing. */
{0x7fff, -0x8000, 5, 7},
{0x7fffffff, -0x80000000, 7, 0},
- /* calls relaxing */
+ /* calls relaxing. */
{0x7fff, -0x8000, 4, 9},
{0x7fffffff, -0x80000000, 6, 0},
- /* jmp relaxing */
+ /* jmp relaxing. */
{0x7f, -0x80, 2, 11},
{0x7fff, -0x8000, 3, 12},
{0x7fffffff, -0x80000000, 5, 0},
-};
+ /* fbCC relaxing. */
+ {0x7f, -0x80, 3, 14},
+ {0x7fff + 3, -0x8000 + 3, 6, 15},
+ {0x7fffffff, -0x80000000, 8, 0},
-/* Local functions. */
-static void mn10300_insert_operand PARAMS ((unsigned long *, unsigned long *,
- const struct mn10300_operand *,
- offsetT, char *, unsigned,
- unsigned));
-static unsigned long check_operand PARAMS ((unsigned long,
- const struct mn10300_operand *,
- offsetT));
-static int reg_name_search PARAMS ((const struct reg_name *, int, const char *));
-static boolean data_register_name PARAMS ((expressionS *expressionP));
-static boolean address_register_name PARAMS ((expressionS *expressionP));
-static boolean other_register_name PARAMS ((expressionS *expressionP));
-static void set_arch_mach PARAMS ((int));
+};
/* Set linkrelax here to avoid fixups in most sections. */
int linkrelax = 1;
static int current_machine;
/* Fixups. */
-#define MAX_INSN_FIXUPS (5)
+#define MAX_INSN_FIXUPS 5
+
struct mn10300_fixup
{
expressionS exp;
int mn10300_reg_operands[MN10300_MAX_OPERANDS];
\f
const char *md_shortopts = "";
-struct option md_longopts[] = {
- {NULL, no_argument, NULL, 0}
-};
-size_t md_longopts_size = sizeof (md_longopts);
-/* The target specific pseudo-ops which we support. */
-const pseudo_typeS md_pseudo_table[] =
+struct option md_longopts[] =
{
- { "file", dwarf2_directive_file, 0 },
- { "loc", dwarf2_directive_loc, 0 },
- { "am30", set_arch_mach, AM30 },
- { "am33", set_arch_mach, AM33 },
- { "mn10300", set_arch_mach, MN103 },
- {NULL, 0, 0}
+ {NULL, no_argument, NULL, 0}
};
-#define HAVE_AM33 (current_machine == AM33)
-#define HAVE_AM30 (current_machine == AM30)
+size_t md_longopts_size = sizeof (md_longopts);
+
+#define HAVE_AM33_2 (current_machine == AM33_2)
+#define HAVE_AM33 (current_machine == AM33 || HAVE_AM33_2)
+#define HAVE_AM30 (current_machine == AM30)
/* Opcode hash table. */
static struct hash_control *mn10300_hash;
{ "d2", 2 },
{ "d3", 3 },
};
-#define DATA_REG_NAME_CNT \
- (sizeof (data_registers) / sizeof (struct reg_name))
static const struct reg_name address_registers[] =
{
{ "a3", 3 },
};
-#define ADDRESS_REG_NAME_CNT \
- (sizeof (address_registers) / sizeof (struct reg_name))
-
static const struct reg_name r_registers[] =
{
{ "a0", 8 },
{ "r9", 9 },
};
-#define R_REG_NAME_CNT \
- (sizeof (r_registers) / sizeof (struct reg_name))
-
static const struct reg_name xr_registers[] =
{
{ "mcrh", 2 },
{ "xr9", 9 },
};
-#define XR_REG_NAME_CNT \
- (sizeof (xr_registers) / sizeof (struct reg_name))
+static const struct reg_name float_registers[] =
+{
+ { "fs0", 0 },
+ { "fs1", 1 },
+ { "fs10", 10 },
+ { "fs11", 11 },
+ { "fs12", 12 },
+ { "fs13", 13 },
+ { "fs14", 14 },
+ { "fs15", 15 },
+ { "fs16", 16 },
+ { "fs17", 17 },
+ { "fs18", 18 },
+ { "fs19", 19 },
+ { "fs2", 2 },
+ { "fs20", 20 },
+ { "fs21", 21 },
+ { "fs22", 22 },
+ { "fs23", 23 },
+ { "fs24", 24 },
+ { "fs25", 25 },
+ { "fs26", 26 },
+ { "fs27", 27 },
+ { "fs28", 28 },
+ { "fs29", 29 },
+ { "fs3", 3 },
+ { "fs30", 30 },
+ { "fs31", 31 },
+ { "fs4", 4 },
+ { "fs5", 5 },
+ { "fs6", 6 },
+ { "fs7", 7 },
+ { "fs8", 8 },
+ { "fs9", 9 },
+};
+
+static const struct reg_name double_registers[] =
+{
+ { "fd0", 0 },
+ { "fd10", 10 },
+ { "fd12", 12 },
+ { "fd14", 14 },
+ { "fd16", 16 },
+ { "fd18", 18 },
+ { "fd2", 2 },
+ { "fd20", 20 },
+ { "fd22", 22 },
+ { "fd24", 24 },
+ { "fd26", 26 },
+ { "fd28", 28 },
+ { "fd30", 30 },
+ { "fd4", 4 },
+ { "fd6", 6 },
+ { "fd8", 8 },
+};
+/* We abuse the `value' field, that would be otherwise unused, to
+ encode the architecture on which (access to) the register was
+ introduced. FIXME: we should probably warn when we encounter a
+ register name when assembling for an architecture that doesn't
+ support it, before parsing it as a symbol name. */
static const struct reg_name other_registers[] =
{
+ { "epsw", AM33 },
{ "mdr", 0 },
+ { "pc", AM33 },
{ "psw", 0 },
{ "sp", 0 },
};
-#define OTHER_REG_NAME_CNT \
- (sizeof (other_registers) / sizeof (struct reg_name))
+#define OTHER_REG_NAME_CNT ARRAY_SIZE (other_registers)
-/* reg_name_search does a binary search of the given register table
- to see if "name" is a valid regiter name. Returns the register
- number from the array on success, or -1 on failure. */
+/* Perform a binary search of the given register table REGS to see
+ if NAME is a valid regiter name. Returns the register number from
+ the array on success, or -1 on failure. */
static int
-reg_name_search (regs, regcount, name)
- const struct reg_name *regs;
- int regcount;
- const char *name;
+reg_name_search (const struct reg_name *regs,
+ int regcount,
+ const char *name)
{
- int middle, low, high;
- int cmp;
+ int low, high;
low = 0;
high = regcount - 1;
do
{
+ int cmp, middle;
+
middle = (low + high) / 2;
cmp = strcasecmp (name, regs[middle].name);
if (cmp < 0)
return regs[middle].value;
}
while (low <= high);
+
return -1;
}
-/* Summary of register_name().
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out: A expressionS.
- * The operand may have been a register: in this case, X_op == O_register,
- * X_add_number is set to the register number, and truth is returned.
- * Input_line_pointer->(next non-blank) char after operand, or is in
- * its original state.
- */
-
-static boolean
-r_register_name (expressionP)
- expressionS *expressionP;
+/* Looks at the current position in the input line to see if it is
+ the name of a register in TABLE. If it is, then the name is
+ converted into an expression returned in EXPRESSIONP (with X_op
+ set to O_register and X_add_number set to the register number), the
+ input pointer is left pointing at the first non-blank character after
+ the name and the function returns TRUE. Otherwise the input pointer
+ is left alone and the function returns FALSE. */
+
+static bfd_boolean
+get_register_name (expressionS * expressionP,
+ const struct reg_name * table,
+ size_t table_length)
{
int reg_number;
char *name;
start = name = input_line_pointer;
c = get_symbol_end ();
- reg_number = reg_name_search (r_registers, R_REG_NAME_CNT, name);
+ reg_number = reg_name_search (table, table_length, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
/* Look to see if it's in the register table. */
if (reg_number >= 0)
expressionP->X_add_symbol = NULL;
expressionP->X_op_symbol = NULL;
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- return true;
+ return TRUE;
}
- else
- {
- /* Reset the line as if we had not done anything. */
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- /* Reset input_line pointer. */
- input_line_pointer = start;
- return false;
- }
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
}
-/* Summary of register_name().
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out: A expressionS.
- * The operand may have been a register: in this case, X_op == O_register,
- * X_add_number is set to the register number, and truth is returned.
- * Input_line_pointer->(next non-blank) char after operand, or is in
- * its original state.
- */
-
-static boolean
-xr_register_name (expressionP)
- expressionS *expressionP;
+static bfd_boolean
+r_register_name (expressionS *expressionP)
{
- int reg_number;
- char *name;
- char *start;
- char c;
-
- /* Find the spelling of the operand. */
- start = name = input_line_pointer;
-
- c = get_symbol_end ();
- reg_number = reg_name_search (xr_registers, XR_REG_NAME_CNT, name);
-
- /* Look to see if it's in the register table. */
- if (reg_number >= 0)
- {
- expressionP->X_op = O_register;
- expressionP->X_add_number = reg_number;
-
- /* Make the rest nice. */
- expressionP->X_add_symbol = NULL;
- expressionP->X_op_symbol = NULL;
+ return get_register_name (expressionP, r_registers, ARRAY_SIZE (r_registers));
+}
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- return true;
- }
- else
- {
- /* Reset the line as if we had not done anything. */
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- /* Reset input_line pointer. */
- input_line_pointer = start;
- return false;
- }
+static bfd_boolean
+xr_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, xr_registers, ARRAY_SIZE (xr_registers));
}
-/* Summary of register_name().
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out: A expressionS.
- * The operand may have been a register: in this case, X_op == O_register,
- * X_add_number is set to the register number, and truth is returned.
- * Input_line_pointer->(next non-blank) char after operand, or is in
- * its original state.
- */
-
-static boolean
-data_register_name (expressionP)
- expressionS *expressionP;
+static bfd_boolean
+data_register_name (expressionS *expressionP)
{
- int reg_number;
- char *name;
- char *start;
- char c;
-
- /* Find the spelling of the operand. */
- start = name = input_line_pointer;
-
- c = get_symbol_end ();
- reg_number = reg_name_search (data_registers, DATA_REG_NAME_CNT, name);
-
- /* Look to see if it's in the register table. */
- if (reg_number >= 0)
- {
- expressionP->X_op = O_register;
- expressionP->X_add_number = reg_number;
-
- /* Make the rest nice. */
- expressionP->X_add_symbol = NULL;
- expressionP->X_op_symbol = NULL;
-
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- return true;
- }
- else
- {
- /* Reset the line as if we had not done anything. */
- /* Put back the delimiting char. */
- *input_line_pointer = c;
-
- /* Reset input_line pointer. */
- input_line_pointer = start;
- return false;
- }
+ return get_register_name (expressionP, data_registers, ARRAY_SIZE (data_registers));
}
-/* Summary of register_name().
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out: A expressionS.
- * The operand may have been a register: in this case, X_op == O_register,
- * X_add_number is set to the register number, and truth is returned.
- * Input_line_pointer->(next non-blank) char after operand, or is in
- * its original state.
- */
-
-static boolean
-address_register_name (expressionP)
- expressionS *expressionP;
+static bfd_boolean
+address_register_name (expressionS *expressionP)
{
- int reg_number;
- char *name;
- char *start;
- char c;
-
- /* Find the spelling of the operand. */
- start = name = input_line_pointer;
-
- c = get_symbol_end ();
- reg_number = reg_name_search (address_registers, ADDRESS_REG_NAME_CNT, name);
-
- /* Look to see if it's in the register table. */
- if (reg_number >= 0)
- {
- expressionP->X_op = O_register;
- expressionP->X_add_number = reg_number;
-
- /* Make the rest nice. */
- expressionP->X_add_symbol = NULL;
- expressionP->X_op_symbol = NULL;
-
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- return true;
- }
- else
- {
- /* Reset the line as if we had not done anything. */
- /* Put back the delimiting char. */
- *input_line_pointer = c;
+ return get_register_name (expressionP, address_registers, ARRAY_SIZE (address_registers));
+}
- /* Reset input_line pointer. */
- input_line_pointer = start;
+static bfd_boolean
+float_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, float_registers, ARRAY_SIZE (float_registers));
+}
- return false;
- }
+static bfd_boolean
+double_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, double_registers, ARRAY_SIZE (double_registers));
}
-/* Summary of register_name().
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out: A expressionS.
- * The operand may have been a register: in this case, X_op == O_register,
- * X_add_number is set to the register number, and truth is returned.
- * Input_line_pointer->(next non-blank) char after operand, or is in
- * its original state.
- */
-
-static boolean
-other_register_name (expressionP)
- expressionS *expressionP;
+static bfd_boolean
+other_register_name (expressionS *expressionP)
{
int reg_number;
char *name;
start = name = input_line_pointer;
c = get_symbol_end ();
- reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name);
+ reg_number = reg_name_search (other_registers, ARRAY_SIZE (other_registers), name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
/* Look to see if it's in the register table. */
- if (reg_number >= 0)
+ if (reg_number == 0
+ || (reg_number == AM33 && HAVE_AM33))
{
expressionP->X_op = O_register;
- expressionP->X_add_number = reg_number;
+ expressionP->X_add_number = 0;
/* Make the rest nice. */
expressionP->X_add_symbol = NULL;
expressionP->X_op_symbol = NULL;
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- return true;
+ return TRUE;
}
- else
- {
- /* Reset the line as if we had not done anything. */
- /* Put back the delimiting char. */
- *input_line_pointer = c;
- /* Reset input_line pointer. */
- input_line_pointer = start;
- return false;
- }
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
}
void
-md_show_usage (stream)
- FILE *stream;
+md_show_usage (FILE *stream)
{
- fprintf (stream, _("MN10300 options:\n\
+ fprintf (stream, _("MN10300 assembler options:\n\
none yet\n"));
}
int
-md_parse_option (c, arg)
- int c ATTRIBUTE_UNUSED;
- char *arg ATTRIBUTE_UNUSED;
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
{
return 0;
}
symbolS *
-md_undefined_symbol (name)
- char *name ATTRIBUTE_UNUSED;
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
return 0;
}
char *
-md_atof (type, litp, sizep)
- int type;
- char *litp;
- int *sizep;
+md_atof (int type, char *litp, int *sizep)
{
- int prec;
- LITTLENUM_TYPE words[4];
- char *t;
- int i;
-
- switch (type)
- {
- case 'f':
- 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 = prec - 1; i >= 0; i--)
- {
- md_number_to_chars (litp, (valueT) words[i], 2);
- litp += 2;
- }
-
- return NULL;
+ return ieee_md_atof (type, litp, sizep, FALSE);
}
void
-md_convert_frag (abfd, sec, fragP)
- bfd *abfd ATTRIBUTE_UNUSED;
- asection *sec;
- fragS *fragP;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ fragS *fragP)
{
static unsigned long label_count = 0;
char buf[40];
else if (fragP->fr_subtype == 6)
{
int offset = fragP->fr_fix;
+
fragP->fr_literal[offset] = 0xcd;
fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
else if (fragP->fr_subtype == 7)
{
int offset = fragP->fr_fix;
+
fragP->fr_literal[offset] = 0xdd;
fragP->fr_literal[offset + 5] = fragP->fr_literal[offset + 3];
fragP->fr_literal[offset + 6] = fragP->fr_literal[offset + 4];
else if (fragP->fr_subtype == 8)
{
int offset = fragP->fr_fix;
+
fragP->fr_literal[offset] = 0xfa;
fragP->fr_literal[offset + 1] = 0xff;
fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
else if (fragP->fr_subtype == 9)
{
int offset = fragP->fr_fix;
+
fragP->fr_literal[offset] = 0xfc;
fragP->fr_literal[offset + 1] = 0xff;
else if (fragP->fr_subtype == 11)
{
int offset = fragP->fr_fix;
+
fragP->fr_literal[offset] = 0xcc;
- fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
fragP->fr_var = 0;
fragP->fr_fix += 3;
else if (fragP->fr_subtype == 12)
{
int offset = fragP->fr_fix;
+
fragP->fr_literal[offset] = 0xdc;
fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
fragP->fr_var = 0;
fragP->fr_fix += 5;
}
+ else if (fragP->fr_subtype == 13)
+ {
+ fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 14)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xd0:
+ opcode = 0xd1;
+ break;
+ case 0xd1:
+ opcode = 0xd0;
+ break;
+ case 0xd2:
+ opcode = 0xdc;
+ break;
+ case 0xd3:
+ opcode = 0xdb;
+ break;
+ case 0xd4:
+ opcode = 0xda;
+ break;
+ case 0xd5:
+ opcode = 0xd9;
+ break;
+ case 0xd6:
+ opcode = 0xd8;
+ break;
+ case 0xd7:
+ opcode = 0xdd;
+ break;
+ case 0xd8:
+ opcode = 0xd6;
+ break;
+ case 0xd9:
+ opcode = 0xd5;
+ break;
+ case 0xda:
+ opcode = 0xd4;
+ break;
+ case 0xdb:
+ opcode = 0xd3;
+ break;
+ case 0xdc:
+ opcode = 0xd2;
+ break;
+ case 0xdd:
+ opcode = 0xd7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xcc;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 15)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xd0:
+ opcode = 0xd1;
+ break;
+ case 0xd1:
+ opcode = 0xd0;
+ break;
+ case 0xd2:
+ opcode = 0xdc;
+ break;
+ case 0xd3:
+ opcode = 0xdb;
+ break;
+ case 0xd4:
+ opcode = 0xda;
+ break;
+ case 0xd5:
+ opcode = 0xd9;
+ break;
+ case 0xd6:
+ opcode = 0xd8;
+ break;
+ case 0xd7:
+ opcode = 0xdd;
+ break;
+ case 0xd8:
+ opcode = 0xd6;
+ break;
+ case 0xd9:
+ opcode = 0xd5;
+ break;
+ case 0xda:
+ opcode = 0xd4;
+ break;
+ case 0xdb:
+ opcode = 0xd3;
+ break;
+ case 0xdc:
+ opcode = 0xd2;
+ break;
+ case 0xdd:
+ opcode = 0xd7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xdc;
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 8;
+ }
else
abort ();
}
valueT
-md_section_align (seg, addr)
- asection *seg;
- valueT addr;
+md_section_align (asection *seg, valueT addr)
{
int align = bfd_get_section_alignment (stdoutput, seg);
+
return ((addr + (1 << align) - 1) & (-1 << align));
}
void
-md_begin ()
+md_begin (void)
{
char *prev_name = "";
- register const struct mn10300_opcode *op;
+ const struct mn10300_opcode *op;
mn10300_hash = hash_new ();
op++;
}
- /* Set the default machine type. */
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, MN103))
- as_warn (_("could not set architecture and machine"));
-
- current_machine = MN103;
+ /* Set the default machine type. */
+#ifdef TE_LINUX
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, AM33_2))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = AM33_2;
+#else
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, MN103))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = MN103;
+#endif
+}
+
+static symbolS *GOT_symbol;
+
+static inline int
+mn10300_PIC_related_p (symbolS *sym)
+{
+ expressionS *exp;
+
+ if (! sym)
+ return 0;
+
+ if (sym == GOT_symbol)
+ return 1;
+
+ exp = symbol_get_value_expression (sym);
+
+ return (exp->X_op == O_PIC_reloc
+ || mn10300_PIC_related_p (exp->X_add_symbol)
+ || mn10300_PIC_related_p (exp->X_op_symbol));
+}
+
+static inline int
+mn10300_check_fixup (struct mn10300_fixup *fixup)
+{
+ expressionS *exp = &fixup->exp;
+
+ repeat:
+ switch (exp->X_op)
+ {
+ case O_add:
+ case O_subtract: /* If we're sufficiently unlucky that the label
+ and the expression that references it happen
+ to end up in different frags, the subtract
+ won't be simplified within expression(). */
+ /* The PIC-related operand must be the first operand of a sum. */
+ if (exp != &fixup->exp || mn10300_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+ fixup->reloc = BFD_RELOC_32_GOT_PCREL;
+
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+ goto repeat;
+
+ case O_symbol:
+ if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+ fixup->reloc = BFD_RELOC_32_GOT_PCREL;
+ break;
+
+ case O_PIC_reloc:
+ fixup->reloc = exp->X_md;
+ exp->X_op = O_symbol;
+ if (fixup->reloc == BFD_RELOC_32_PLT_PCREL
+ && fixup->opindex >= 0
+ && (mn10300_operands[fixup->opindex].flags
+ & MN10300_OPERAND_RELAX))
+ return 1;
+ break;
+
+ default:
+ return (mn10300_PIC_related_p (exp->X_add_symbol)
+ || mn10300_PIC_related_p (exp->X_op_symbol));
+ }
+
+ return 0;
+}
+
+void
+mn10300_cons_fix_new (fragS *frag, int off, int size, expressionS *exp)
+{
+ struct mn10300_fixup fixup;
+
+ fixup.opindex = -1;
+ fixup.exp = *exp;
+ fixup.reloc = BFD_RELOC_UNUSED;
+
+ mn10300_check_fixup (&fixup);
+
+ if (fixup.reloc == BFD_RELOC_MN10300_GOT32)
+ switch (size)
+ {
+ case 2:
+ fixup.reloc = BFD_RELOC_MN10300_GOT16;
+ break;
+
+ case 3:
+ fixup.reloc = BFD_RELOC_MN10300_GOT24;
+ break;
+
+ case 4:
+ break;
+
+ default:
+ goto error;
+ }
+ else if (fixup.reloc == BFD_RELOC_UNUSED)
+ switch (size)
+ {
+ case 1:
+ fixup.reloc = BFD_RELOC_8;
+ break;
+
+ case 2:
+ fixup.reloc = BFD_RELOC_16;
+ break;
+
+ case 3:
+ fixup.reloc = BFD_RELOC_24;
+ break;
+
+ case 4:
+ fixup.reloc = BFD_RELOC_32;
+ break;
+
+ default:
+ goto error;
+ }
+ else if (size != 4)
+ {
+ error:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ fixup.reloc = BFD_RELOC_UNUSED;
+ }
+
+ fix_new_exp (frag, off, size, &fixup.exp, 0, fixup.reloc);
+}
+
+static bfd_boolean
+check_operand (const struct mn10300_operand *operand,
+ offsetT val)
+{
+ /* No need to check 32bit operands for a bit. Note that
+ MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
+ if (operand->bits != 32
+ && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
+ {
+ long min, max;
+ offsetT test;
+ int bits;
+
+ bits = operand->bits;
+ if (operand->flags & MN10300_OPERAND_24BIT)
+ bits = 24;
+
+ if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+mn10300_insert_operand (unsigned long *insnp,
+ unsigned long *extensionp,
+ const struct mn10300_operand *operand,
+ offsetT val,
+ char *file,
+ unsigned int line,
+ unsigned int shift)
+{
+ /* No need to check 32bit operands for a bit. Note that
+ MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
+ if (operand->bits != 32
+ && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
+ {
+ long min, max;
+ offsetT test;
+ int bits;
+
+ bits = operand->bits;
+ if (operand->flags & MN10300_OPERAND_24BIT)
+ bits = 24;
+
+ if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
+ }
+
+ if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
+ {
+ *insnp |= (val >> (32 - operand->bits)) & ((1 << operand->bits) - 1);
+ *extensionp |= ((val & ((1 << (32 - operand->bits)) - 1))
+ << operand->shift);
+ }
+ else if ((operand->flags & MN10300_OPERAND_24BIT) != 0)
+ {
+ *insnp |= (val >> (24 - operand->bits)) & ((1 << operand->bits) - 1);
+ *extensionp |= ((val & ((1 << (24 - operand->bits)) - 1))
+ << operand->shift);
+ }
+ else if ((operand->flags & (MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG)))
+ {
+ /* See devo/opcodes/m10300-opc.c just before #define FSM0 for an
+ explanation of these variables. Note that FMT-implied shifts
+ are not taken into account for FP registers. */
+ unsigned long mask_low, mask_high;
+ int shl_low, shr_high, shl_high;
+
+ switch (operand->bits)
+ {
+ case 5:
+ /* Handle regular FP registers. */
+ if (operand->shift >= 0)
+ {
+ /* This is an `m' register. */
+ shl_low = operand->shift;
+ shl_high = 8 + (8 & shl_low) + (shl_low & 4) / 4;
+ }
+ else
+ {
+ /* This is an `n' register. */
+ shl_low = -operand->shift;
+ shl_high = shl_low / 4;
+ }
+
+ mask_low = 0x0f;
+ mask_high = 0x10;
+ shr_high = 4;
+ break;
+
+ case 3:
+ /* Handle accumulators. */
+ shl_low = -operand->shift;
+ shl_high = 0;
+ mask_low = 0x03;
+ mask_high = 0x04;
+ shr_high = 2;
+ break;
+
+ default:
+ abort ();
+ }
+ *insnp |= ((((val & mask_high) >> shr_high) << shl_high)
+ | ((val & mask_low) << shl_low));
+ }
+ else if ((operand->flags & MN10300_OPERAND_EXTENDED) == 0)
+ {
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + operand->bits));
+ }
+ else
+ {
+ *extensionp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
+ *extensionp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + operand->bits));
+ }
}
void
-md_assemble (str)
- char *str;
+md_assemble (char *str)
{
char *s;
struct mn10300_opcode *opcode;
struct mn10300_opcode *next_opcode;
const unsigned char *opindex_ptr;
int next_opindex, relaxable;
- unsigned long insn, extension, size = 0, real_size;
+ unsigned long insn, extension, size = 0;
char *f;
int i;
int match;
/* Get the opcode. */
- for (s = str; *s != '\0' && !isspace (*s); s++)
+ for (s = str; *s != '\0' && !ISSPACE (*s); s++)
;
if (*s != '\0')
*s++ = '\0';
}
str = s;
- while (isspace (*str))
+ while (ISSPACE (*str))
++str;
input_line_pointer = str;
/* If the instruction is not available on the current machine
then it can not possibly match. */
if (opcode->machine
+ && !(opcode->machine == AM33_2 && HAVE_AM33_2)
&& !(opcode->machine == AM33 && HAVE_AM33)
&& !(opcode->machine == AM30 && HAVE_AM30))
goto error;
goto error;
}
}
+ else if (operand->flags & MN10300_OPERAND_FSREG)
+ {
+ if (!float_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_FDREG)
+ {
+ if (!double_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_FPCR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "fpcr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
else if (operand->flags & MN10300_OPERAND_USP)
{
char *start = input_line_pointer;
}
}
input_line_pointer++;
- mn10300_insert_operand (&insn, &extension, operand,
- value, (char *) NULL, 0, 0);
+ mn10300_insert_operand (& insn, & extension, operand,
+ value, NULL, 0, 0);
goto keep_going;
}
str = hold;
goto error;
}
+ else if (HAVE_AM33_2 && float_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (HAVE_AM33_2 && double_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
else if (*str == ')' || *str == '(')
{
input_line_pointer = hold;
mask = MN10300_OPERAND_DREG | MN10300_OPERAND_AREG;
if (HAVE_AM33)
mask |= MN10300_OPERAND_RREG | MN10300_OPERAND_XRREG;
+ if (HAVE_AM33_2)
+ mask |= MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG;
if ((operand->flags & mask) == 0)
{
input_line_pointer = hold;
else
extra_shift = 0;
- mn10300_insert_operand (&insn, &extension, operand,
- ex.X_add_number, (char *) NULL,
+ mn10300_insert_operand (& insn, & extension, operand,
+ ex.X_add_number, NULL,
0, extra_shift);
/* And note the register number in the register array. */
then promote it (ie this opcode does not match). */
if (operand->flags
& (MN10300_OPERAND_PROMOTE | MN10300_OPERAND_RELAX)
- && !check_operand (insn, operand, ex.X_add_number))
+ && !check_operand (operand, ex.X_add_number))
{
input_line_pointer = hold;
str = hold;
goto error;
}
- mn10300_insert_operand (&insn, &extension, operand,
- ex.X_add_number, (char *) NULL,
- 0, 0);
+ mn10300_insert_operand (& insn, & extension, operand,
+ ex.X_add_number, NULL, 0, 0);
break;
default:
fixups[fc].exp = ex;
fixups[fc].opindex = *opindex_ptr;
fixups[fc].reloc = BFD_RELOC_UNUSED;
+ if (mn10300_check_fixup (& fixups[fc]))
+ goto error;
++fc;
break;
}
while (*str == ' ' || *str == ',')
++str;
-
}
/* Make sure we used all the operands! */
break;
}
- while (isspace (*str))
+ while (ISSPACE (*str))
++str;
if (*str != '\0')
if (opcode->format == FMT_D2)
size = 4;
+ if (opcode->format == FMT_D3)
+ size = 5;
+
if (opcode->format == FMT_D4)
size = 6;
- real_size = size;
-
if (relaxable && fc > 0)
{
+ /* On a 64-bit host the size of an 'int' is not the same
+ as the size of a pointer, so we need a union to convert
+ the opindex field of the fr_cgen structure into a char *
+ so that it can be stored in the frag. We do not have
+ to worry about loosing accuracy as we are not going to
+ be even close to the 32bit limit of the int. */
+ union
+ {
+ int opindex;
+ char * ptr;
+ }
+ opindex_converter;
int type;
+ /* We want to anchor the line info to the previous frag (if
+ there isn't one, create it), so that, when the insn is
+ resized, we still get the right address for the beginning of
+ the region. */
+ f = frag_more (0);
+ dwarf2_emit_insn (0);
+
/* bCC */
if (size == 2)
{
/* jmp */
else if (size == 3 && opcode->opcode == 0xcc0000)
type = 10;
+ else if (size == 3 && (opcode->opcode & 0xfff000) == 0xf8d000)
+ type = 13;
/* bCC (uncommon cases) */
else
type = 3;
+ opindex_converter.opindex = fixups[0].opindex;
f = frag_var (rs_machine_dependent, 8, 8 - size, type,
fixups[0].exp.X_add_symbol,
fixups[0].exp.X_add_number,
- (char *)fixups[0].opindex);
+ opindex_converter.ptr);
/* This is pretty hokey. We basically just care about the
opcode, so we have to write out the first word big endian.
is really two 8bit immediates. */
number_to_chars_bigendian (f, insn, 4);
}
+ else if (opcode->format == FMT_D3)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ number_to_chars_bigendian (f + 4, extension & 0xff, 1);
+ }
else if (opcode->format == FMT_D4)
{
unsigned long temp = ((insn & 0xffff) << 16) | (extension & 0xffff);
const struct mn10300_operand *operand;
operand = &mn10300_operands[fixups[i].opindex];
- if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ if (fixups[i].reloc != BFD_RELOC_UNUSED
+ && fixups[i].reloc != BFD_RELOC_32_GOT_PCREL
+ && fixups[i].reloc != BFD_RELOC_32_GOTOFF
+ && fixups[i].reloc != BFD_RELOC_32_PLT_PCREL
+ && fixups[i].reloc != BFD_RELOC_MN10300_GOT32)
{
reloc_howto_type *reloc_howto;
int size;
fixS *fixP;
reloc = BFD_RELOC_NONE;
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ reloc = fixups[i].reloc;
/* How big is the reloc? Remember SPLIT relocs are
implicitly 32bits. */
if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
/* Is the reloc pc-relative? */
pcrel = (operand->flags & MN10300_OPERAND_PCREL) != 0;
+ if (reloc != BFD_RELOC_NONE)
+ pcrel = bfd_reloc_type_lookup (stdoutput, reloc)->pc_relative;
- /* Gross. This disgusting hack is to make sure we
- get the right offset for the 16/32 bit reloc in
- "call" instructions. Basically they're a pain
- because the reloc isn't at the end of the instruction. */
- if ((size == 5 || size == 7)
- && (((insn >> 24) & 0xff) == 0xcd
- || ((insn >> 24) & 0xff) == 0xdd))
- size -= 2;
-
- /* Similarly for certain bit instructions which don't
- hav their 32bit reloc at the tail of the instruction. */
- if (size == 7
- && (((insn >> 16) & 0xffff) == 0xfe00
- || ((insn >> 16) & 0xffff) == 0xfe01
- || ((insn >> 16) & 0xffff) == 0xfe02))
- size -= 1;
-
- offset = size - reloc_size / 8;
+ offset = size - (reloc_size + operand->shift) / 8;
/* Choose a proper BFD relocation type. */
- if (pcrel)
+ if (reloc != BFD_RELOC_NONE)
+ ;
+ else if (pcrel)
{
if (reloc_size == 32)
reloc = BFD_RELOC_32_PCREL;
abort ();
}
- /* Convert the size of the reloc into what fix_new_exp wants. */
- reloc_size = reloc_size / 8;
- if (reloc_size == 8)
- reloc_size = 0;
- else if (reloc_size == 16)
- reloc_size = 1;
- else if (reloc_size == 32)
- reloc_size = 2;
-
fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
- reloc_size, &fixups[i].exp, pcrel,
+ reloc_size / 8, &fixups[i].exp, pcrel,
((bfd_reloc_code_real_type) reloc));
if (pcrel)
fixP->fx_offset += offset;
}
}
+
+ dwarf2_emit_insn (size);
}
- if (debug_type == DEBUG_DWARF2)
- dwarf2_generate_asm_lineno (real_size);
+ /* Label this frag as one that contains instructions. */
+ frag_now->tc_frag_data = TRUE;
}
/* If while processing a fixup, a reloc really needs to be created
then it is done here. */
-arelent *
-tc_gen_reloc (seg, fixp)
- asection *seg ATTRIBUTE_UNUSED;
- fixS *fixp;
+arelent **
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
{
+ static arelent * no_relocs = NULL;
+ static arelent * relocs[MAX_RELOC_EXPANSION + 1];
arelent *reloc;
- reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc = xmalloc (sizeof (arelent));
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
- if (reloc->howto == (reloc_howto_type *) NULL)
+ if (reloc->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("reloc %d not supported by object file format"),
(int) fixp->fx_r_type);
- return NULL;
+ free (reloc);
+ return & no_relocs;
}
+
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[0] = reloc;
+ relocs[1] = NULL;
+
+ if (fixp->fx_subsy
+ && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ {
+ fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_subsy = NULL;
+ }
if (fixp->fx_addsy && fixp->fx_subsy)
{
+ asection *asec, *ssec;
+
+ asec = S_GET_SEGMENT (fixp->fx_addsy);
+ ssec = S_GET_SEGMENT (fixp->fx_subsy);
- if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
- || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
+ reloc->sym_ptr_ptr = NULL;
+
+ /* If we have a difference between two (non-absolute) symbols we must
+ generate two relocs (one for each symbol) and allow the linker to
+ resolve them - relaxation may change the distances between symbols,
+ even local symbols defined in the same section. */
+ if (ssec != absolute_section || asec != absolute_section)
{
- as_bad_where (fixp->fx_file, fixp->fx_line,
- "Difference of symbols in different sections is not supported");
- return NULL;
+ arelent * reloc2 = xmalloc (sizeof * reloc);
+
+ relocs[0] = reloc2;
+ relocs[1] = reloc;
+
+ reloc2->address = reloc->address;
+ reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_MN10300_SYM_DIFF);
+ reloc2->addend = - S_GET_VALUE (fixp->fx_subsy);
+ reloc2->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+
+ reloc->addend = fixp->fx_offset;
+ if (asec == absolute_section)
+ reloc->addend += S_GET_VALUE (fixp->fx_addsy);
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ fixp->fx_pcrel = 0;
+ fixp->fx_done = 1;
+ return relocs;
}
+ else
+ {
+ char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;
+
+ reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
+ - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (fixpos, reloc->addend, 1);
+ break;
+
+ case BFD_RELOC_16:
+ md_number_to_chars (fixpos, reloc->addend, 2);
+ break;
+
+ case BFD_RELOC_24:
+ md_number_to_chars (fixpos, reloc->addend, 3);
+ break;
+
+ case BFD_RELOC_32:
+ md_number_to_chars (fixpos, reloc->addend, 4);
+ break;
- reloc->sym_ptr_ptr = (asymbol **) &bfd_abs_symbol;
- reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
- - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);
+ default:
+ reloc->sym_ptr_ptr
+ = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
+ return relocs;
+ }
+
+ if (reloc->sym_ptr_ptr)
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
+ return & no_relocs;
+ }
}
else
{
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->addend = fixp->fx_offset;
}
- return reloc;
+ return relocs;
+}
+
+/* Returns true iff the symbol attached to the frag is at a known location
+ in the given section, (and hence the relocation to it can be relaxed by
+ the assembler). */
+static inline bfd_boolean
+has_known_symbol_location (fragS * fragp, asection * sec)
+{
+ symbolS * sym = fragp->fr_symbol;
+
+ return sym != NULL
+ && S_IS_DEFINED (sym)
+ && ! S_IS_WEAK (sym)
+ && S_GET_SEGMENT (sym) == sec;
}
int
-md_estimate_size_before_relax (fragp, seg)
- fragS *fragp;
- asection *seg;
+md_estimate_size_before_relax (fragS *fragp, asection *seg)
{
- if (fragp->fr_subtype == 0)
- return 2;
- if (fragp->fr_subtype == 3)
+ if (fragp->fr_subtype == 6
+ && ! has_known_symbol_location (fragp, seg))
+ fragp->fr_subtype = 7;
+ else if (fragp->fr_subtype == 8
+ && ! has_known_symbol_location (fragp, seg))
+ fragp->fr_subtype = 9;
+ else if (fragp->fr_subtype == 10
+ && ! has_known_symbol_location (fragp, seg))
+ fragp->fr_subtype = 12;
+
+ if (fragp->fr_subtype == 13)
return 3;
- if (fragp->fr_subtype == 6)
- {
- if (!S_IS_DEFINED (fragp->fr_symbol)
- || seg != S_GET_SEGMENT (fragp->fr_symbol))
- {
- fragp->fr_subtype = 7;
- return 7;
- }
- else
- return 5;
- }
- if (fragp->fr_subtype == 8)
- {
- if (!S_IS_DEFINED (fragp->fr_symbol)
- || seg != S_GET_SEGMENT (fragp->fr_symbol))
- {
- fragp->fr_subtype = 9;
- return 6;
- }
- else
- return 4;
- }
- if (fragp->fr_subtype == 10)
- {
- if (!S_IS_DEFINED (fragp->fr_symbol)
- || seg != S_GET_SEGMENT (fragp->fr_symbol))
- {
- fragp->fr_subtype = 12;
- return 5;
- }
- else
- return 2;
- }
- abort ();
+
+ if (fragp->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
}
long
-md_pcrel_from (fixp)
- fixS *fixp;
+md_pcrel_from (fixS *fixp)
{
- if (fixp->fx_addsy != (symbolS *) NULL && !S_IS_DEFINED (fixp->fx_addsy))
- {
- /* The symbol is undefined. Let the linker figure it out. */
- return 0;
- }
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)))
+ /* The symbol is undefined or weak. Let the linker figure it out. */
+ return 0;
+
return fixp->fx_frag->fr_address + fixp->fx_where;
}
-int
-md_apply_fix3 (fixp, valuep, seg)
- fixS *fixp;
- valueT *valuep;
- segT seg;
+void
+md_apply_fix (fixS * fixP, valueT * valP, segT seg)
{
- char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;
+ char * fixpos = fixP->fx_where + fixP->fx_frag->fr_literal;
int size = 0;
- int value;
+ int value = (int) * valP;
- assert (fixp->fx_r_type < BFD_RELOC_UNUSED);
+ assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
/* This should never happen. */
if (seg->flags & SEC_ALLOC)
- abort ();
+ abort ();
/* The value we are passed in *valuep includes the symbol values.
- Since we are using BFD_ASSEMBLER, if we are doing this relocation
- the code in write.c is going to call bfd_install_relocation, which
- is also going to use the symbol value. That means that if the
- reloc is fully resolved we want to use *valuep since
- bfd_install_relocation is not being used.
+ If we are doing this relocation the code in write.c is going to
+ call bfd_install_relocation, which is also going to use the symbol
+ value. That means that if the reloc is fully resolved we want to
+ use *valuep since bfd_install_relocation is not being used.
However, if the reloc is not fully resolved we do not want to use
*valuep, and must use fx_offset instead. However, if the reloc
is PC relative, we do want to use *valuep since it includes the
result of md_pcrel_from. */
- if (fixp->fx_addsy == (symbolS *) NULL || fixp->fx_pcrel)
- value = *valuep;
- else
- value = fixp->fx_offset;
+ if (fixP->fx_addsy != NULL && ! fixP->fx_pcrel)
+ value = fixP->fx_offset;
/* If the fix is relative to a symbol which is not defined, or not
in the same segment as the fix, we cannot resolve it here. */
- if (fixp->fx_addsy != NULL
- && (! S_IS_DEFINED (fixp->fx_addsy)
- || (S_GET_SEGMENT (fixp->fx_addsy) != seg)))
+ if (fixP->fx_addsy != NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || (S_GET_SEGMENT (fixP->fx_addsy) != seg)))
{
- fixp->fx_done = 0;
- return 0;
+ fixP->fx_done = 0;
+ return;
}
- switch (fixp->fx_r_type)
+ switch (fixP->fx_r_type)
{
case BFD_RELOC_8:
+ case BFD_RELOC_8_PCREL:
size = 1;
break;
case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
size = 2;
break;
case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
size = 4;
break;
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
- fixp->fx_done = 0;
- return 1;
+ fixP->fx_done = 0;
+ return;
+ case BFD_RELOC_MN10300_ALIGN:
+ fixP->fx_done = 1;
+ return;
+
case BFD_RELOC_NONE:
default:
- as_bad_where (fixp->fx_file, fixp->fx_line,
- _("Bad relocation fixup type (%d)"), fixp->fx_r_type);
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Bad relocation fixup type (%d)"), fixP->fx_r_type);
}
md_number_to_chars (fixpos, value, size);
- fixp->fx_done = 1;
- return 0;
-
-}
-
-/* Return nonzero if the fixup in FIXP will require a relocation,
- even it if appears that the fixup could be completely handled
- within GAS. */
-
-int
-mn10300_force_relocation (fixp)
- struct fix *fixp;
-{
- if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
- return 1;
-
- return 0;
+ /* If a symbol remains, pass the fixup, as a reloc, onto the linker. */
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
}
/* Return zero if the fixup in fixp should be left alone and not
adjusted. */
-boolean
-mn10300_fix_adjustable (fixp)
- struct fix *fixp;
+bfd_boolean
+mn10300_fix_adjustable (struct fix *fixp)
{
- /* Prevent all adjustments to global symbols. */
- if (S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy))
- return 0;
+ if (fixp->fx_pcrel)
+ {
+ if (TC_FORCE_RELOCATION_LOCAL (fixp))
+ return FALSE;
+ }
+ /* Non-relative relocs can (and must) be adjusted if they do
+ not meet the criteria below, or the generic criteria. */
+ else if (TC_FORCE_RELOCATION (fixp))
+ return FALSE;
+
+ /* Do not adjust relocations involving symbols in code sections,
+ because it breaks linker relaxations. This could be fixed in the
+ linker, but this fix is simpler, and it pretty much only affects
+ object size a little bit. */
+ if (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE)
+ return FALSE;
+
+ /* Likewise, do not adjust symbols that won't be merged, or debug
+ symbols, because they too break relaxation. We do want to adjust
+ other mergable symbols, like .rodata, because code relaxations
+ need section-relative symbols to properly relax them. */
+ if (! (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE))
+ return FALSE;
+
+ if (strncmp (S_GET_SEGMENT (fixp->fx_addsy)->name, ".debug", 6) == 0)
+ return FALSE;
+
+ return TRUE;
+}
- if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
- return 0;
+static void
+set_arch_mach (int mach)
+{
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, mach))
+ as_warn (_("could not set architecture and machine"));
- return 1;
+ current_machine = mach;
}
-/* Insert an operand value into an instruction. */
+static inline char *
+mn10300_end_of_match (char *cont, char *what)
+{
+ int len = strlen (what);
-static void
-mn10300_insert_operand (insnp, extensionp, operand, val, file, line, shift)
- unsigned long *insnp;
- unsigned long *extensionp;
- const struct mn10300_operand *operand;
- offsetT val;
- char *file;
- unsigned int line;
- unsigned int shift;
+ if (strncmp (cont, what, strlen (what)) == 0
+ && ! is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+int
+mn10300_parse_name (char const *name,
+ expressionS *exprP,
+ enum expr_mode mode,
+ char *nextcharP)
{
- /* No need to check 32bit operands for a bit. Note that
- MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
- if (operand->bits != 32
- && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
- {
- long min, max;
- offsetT test;
- int bits;
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ segT segment;
- bits = operand->bits;
- if (operand->flags & MN10300_OPERAND_24BIT)
- bits = 24;
+ exprP->X_op_symbol = NULL;
- if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (! GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+ no_suffix:
+ /* If we have an absolute symbol or a reg,
+ then we know its value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (mode != expr_defer && segment == absolute_section)
{
- max = (1 << (bits - 1)) - 1;
- min = - (1 << (bits - 1));
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
}
- else
+ else if (mode != expr_defer && segment == reg_section)
{
- max = (1 << bits) - 1;
- min = 0;
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
}
-
- test = val;
-
- if (test < (offsetT) min || test > (offsetT) max)
+ else
{
- const char *err =
- _("operand out of range (%s not between %ld and %ld)");
- char buf[100];
-
- sprint_value (buf, test);
- if (file == (char *) NULL)
- as_warn (err, buf, min, max);
- else
- as_warn_where (file, line, err, buf, min, max);
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
}
- }
- if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
- {
- *insnp |= (val >> (32 - operand->bits)) & ((1 << operand->bits) - 1);
- *extensionp |= ((val & ((1 << (32 - operand->bits)) - 1))
- << operand->shift);
- }
- else if ((operand->flags & MN10300_OPERAND_24BIT) != 0)
- {
- *insnp |= (val >> (24 - operand->bits)) & ((1 << operand->bits) - 1);
- *extensionp |= ((val & ((1 << (24 - operand->bits)) - 1))
- << operand->shift);
+ return 1;
}
- else if ((operand->flags & MN10300_OPERAND_EXTENDED) == 0)
- {
- *insnp |= (((long) val & ((1 << operand->bits) - 1))
- << (operand->shift + shift));
- if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
- *insnp |= (((long) val & ((1 << operand->bits) - 1))
- << (operand->shift + shift + operand->bits));
- }
+ exprP->X_add_symbol = symbol_find_or_make (name);
+
+ if (*nextcharP != '@')
+ goto no_suffix;
+ else if ((next_end = mn10300_end_of_match (next + 1, "GOTOFF")))
+ reloc_type = BFD_RELOC_32_GOTOFF;
+ else if ((next_end = mn10300_end_of_match (next + 1, "GOT")))
+ reloc_type = BFD_RELOC_MN10300_GOT32;
+ else if ((next_end = mn10300_end_of_match (next + 1, "PLT")))
+ reloc_type = BFD_RELOC_32_PLT_PCREL;
else
- {
- *extensionp |= (((long) val & ((1 << operand->bits) - 1))
- << (operand->shift + shift));
+ goto no_suffix;
- if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
- *extensionp |= (((long) val & ((1 << operand->bits) - 1))
- << (operand->shift + shift + operand->bits));
- }
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = O_PIC_reloc;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
}
-static unsigned long
-check_operand (insn, operand, val)
- unsigned long insn ATTRIBUTE_UNUSED;
- const struct mn10300_operand *operand;
- offsetT val;
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
{
- /* No need to check 32bit operands for a bit. Note that
- MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
- if (operand->bits != 32
- && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
- {
- long min, max;
- offsetT test;
- int bits;
+ { "am30", set_arch_mach, AM30 },
+ { "am33", set_arch_mach, AM33 },
+ { "am33_2", set_arch_mach, AM33_2 },
+ { "mn10300", set_arch_mach, MN103 },
+ {NULL, 0, 0}
+};
- bits = operand->bits;
- if (operand->flags & MN10300_OPERAND_24BIT)
- bits = 24;
+/* Returns FALSE if there is some mn10300 specific reason why the
+ subtraction of two same-section symbols cannot be computed by
+ the assembler. */
- if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
- {
- max = (1 << (bits - 1)) - 1;
- min = - (1 << (bits - 1));
- }
- else
- {
- max = (1 << bits) - 1;
- min = 0;
- }
+bfd_boolean
+mn10300_allow_local_subtract (expressionS * left, expressionS * right, segT section)
+{
+ bfd_boolean result;
+ fragS * left_frag;
+ fragS * right_frag;
+ fragS * frag;
+
+ /* If we are not performing linker relaxation then we have nothing
+ to worry about. */
+ if (linkrelax == 0)
+ return TRUE;
+
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ /* Otherwise we have to scan the fragments between the two symbols.
+ If any instructions are found then we have to assume that linker
+ relaxation may change their size and so we must delay resolving
+ the subtraction until the final link. */
+ left_frag = symbol_get_frag (left->X_add_symbol);
+ right_frag = symbol_get_frag (right->X_add_symbol);
+
+ if (left_frag == right_frag)
+ return ! left_frag->tc_frag_data;
+
+ result = TRUE;
+ for (frag = left_frag; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->tc_frag_data)
+ result = FALSE;
+ if (frag == right_frag)
+ break;
+ }
- test = val;
+ if (frag == NULL)
+ for (frag = right_frag; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->tc_frag_data)
+ result = FALSE;
+ if (frag == left_frag)
+ break;
+ }
- if (test < (offsetT) min || test > (offsetT) max)
- return 0;
- else
- return 1;
- }
- return 1;
+ if (frag == NULL)
+ /* The two symbols are on disjoint fragment chains
+ - we cannot possibly compute their difference. */
+ return FALSE;
+
+ return result;
}
-static void
-set_arch_mach (mach)
- int mach;
-{
- if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, mach))
- as_warn (_("could not set architecture and machine"));
+/* When relaxing, we need to output a reloc for any .align directive
+ that requests alignment to a two byte boundary or larger. */
- current_machine = mach;
+void
+mn10300_handle_align (fragS *frag)
+{
+ if (linkrelax
+ && (frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 1
+ && now_seg != bss_section
+ /* Do not create relocs for the merging sections - such
+ relocs will prevent the contents from being merged. */
+ && (bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE) == 0)
+ /* Create a new fixup to record the alignment request. The symbol is
+ irrelevent but must be present so we use the absolute section symbol.
+ The offset from the symbol is used to record the power-of-two alignment
+ value. The size is set to 0 because the frag may already be aligned,
+ thus causing cvt_frag_to_fill to reduce the size of the frag to zero. */
+ fix_new (frag, frag->fr_fix, 0, & abs_symbol, frag->fr_offset, FALSE,
+ BFD_RELOC_MN10300_ALIGN);
}
-void
-mn10300_finalize ()
+bfd_boolean
+mn10300_force_relocation (struct fix * fixp)
{
- if (debug_type == DEBUG_DWARF2)
- dwarf2_finish ();
+ if (linkrelax
+ && (fixp->fx_pcrel
+ || fixp->fx_r_type == BFD_RELOC_MN10300_ALIGN))
+ return TRUE;
+
+ return generic_force_reloc (fixp);
}