X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-mn10300.c;h=4f76677af5e8c0239249340b3292ae741e548cc1;hb=17b9d67d4e6bcf2b2b4eecc91b0ff6af35e0fef5;hp=d84fb4a15c15b51e90e93b6d7ea3537ea9e50737;hpb=b4c1ea07cd2f3029054bac5abb51739556fc4277;p=binutils-gdb.git diff --git a/gas/config/tc-mn10300.c b/gas/config/tc-mn10300.c index d84fb4a15c1..4f76677af5e 100644 --- a/gas/config/tc-mn10300.c +++ b/gas/config/tc-mn10300.c @@ -1,11 +1,11 @@ /* tc-mn10300.c -- Assembler code for the Matsushita 10300 - Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation. + Copyright (C) 1996-2016 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,15 +15,15 @@ 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 -#include #include "as.h" +#include "safe-ctype.h" #include "subsegs.h" #include "opcode/mn10300.h" #include "dwarf2dbg.h" +#include "libiberty.h" /* Structure to hold information about predefined registers. */ struct reg_name @@ -32,8 +32,6 @@ struct reg_name int value; }; -struct dwarf2_line_info debug_line; - /* Generic assembler global variables which must be defined by all targets. */ @@ -55,50 +53,50 @@ const char EXP_CHARS[] = "eE"; as in 0d1.0. */ const char FLT_CHARS[] = "dD"; -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; @@ -113,24 +111,17 @@ static int fc; int mn10300_reg_operands[MN10300_MAX_OPERANDS]; 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; @@ -143,8 +134,6 @@ static const struct reg_name data_registers[] = { "d2", 2 }, { "d3", 3 }, }; -#define DATA_REG_NAME_CNT \ - (sizeof (data_registers) / sizeof (struct reg_name)) static const struct reg_name address_registers[] = { @@ -154,9 +143,6 @@ 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 }, @@ -201,9 +187,6 @@ static const struct reg_name r_registers[] = { "r9", 9 }, }; -#define R_REG_NAME_CNT \ - (sizeof (r_registers) / sizeof (struct reg_name)) - static const struct reg_name xr_registers[] = { { "mcrh", 2 }, @@ -229,37 +212,98 @@ static const struct reg_name xr_registers[] = { "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 }, + { "ssp", 0 }, + { "usp", 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) @@ -270,23 +314,22 @@ reg_name_search (regs, regcount, name) 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; @@ -294,10 +337,13 @@ r_register_name (expressionP) char c; /* Find the spelling of the operand. */ - start = name = input_line_pointer; + start = input_line_pointer; + + c = get_symbol_name (&name); + reg_number = reg_name_search (table, table_length, name); - c = get_symbol_end (); - reg_number = reg_name_search (r_registers, R_REG_NAME_CNT, name); + /* Put back the delimiting char. */ + (void) restore_line_pointer (c); /* Look to see if it's in the register table. */ if (reg_number >= 0) @@ -309,193 +355,53 @@ r_register_name (expressionP) 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; @@ -503,106 +409,62 @@ other_register_name (expressionP) char c; /* Find the spelling of the operand. */ - start = name = input_line_pointer; + start = input_line_pointer; + + c = get_symbol_name (&name); + reg_number = reg_name_search (other_registers, ARRAY_SIZE (other_registers), name); - c = get_symbol_end (); - reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name); + /* Put back the delimiting char. */ + (void) restore_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, const 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]; @@ -814,6 +676,7 @@ md_convert_frag (abfd, sec, fragP) 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); @@ -823,9 +686,12 @@ md_convert_frag (abfd, sec, fragP) 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]; + fragP->fr_literal[offset + 3] = 0; + fragP->fr_literal[offset + 4] = 0; fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol, fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL); @@ -835,6 +701,7 @@ md_convert_frag (abfd, sec, fragP) 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, @@ -845,6 +712,7 @@ md_convert_frag (abfd, sec, fragP) else if (fragP->fr_subtype == 9) { int offset = fragP->fr_fix; + fragP->fr_literal[offset] = 0xfc; fragP->fr_literal[offset + 1] = 0xff; @@ -864,9 +732,10 @@ md_convert_frag (abfd, sec, fragP) 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; @@ -874,6 +743,7 @@ md_convert_frag (abfd, sec, fragP) 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, @@ -881,24 +751,168 @@ md_convert_frag (abfd, sec, fragP) 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)); + + return ((addr + (1 << align) - 1) & -(1 << align)); } void -md_begin () +md_begin (void) { - char *prev_name = ""; - register const struct mn10300_opcode *op; + const char *prev_name = ""; + const struct mn10300_opcode *op; mn10300_hash = hash_new (); @@ -918,34 +932,316 @@ md_begin () op++; } - /* This is both a simplification (we don't have to write md_apply_fix) - and support for future optimizations (branch shortening and similar - stuff in the linker). */ - linkrelax = 1; - /* 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; + 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, + bfd_reloc_code_real_type r ATTRIBUTE_UNUSED) +{ + 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'; @@ -959,7 +1255,7 @@ md_assemble (str) } str = s; - while (isspace (*str)) + while (ISSPACE (*str)) ++str; input_line_pointer = str; @@ -986,6 +1282,7 @@ md_assemble (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; @@ -1049,17 +1346,17 @@ md_assemble (str) } else if (operand->flags & MN10300_OPERAND_SP) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "sp") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_RREG) @@ -1080,79 +1377,112 @@ md_assemble (str) 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; + char c = get_symbol_name (&start); + + if (strcasecmp (start, "fpcr") != 0) + { + (void) restore_line_pointer (c); + input_line_pointer = hold; + str = hold; + goto error; + } + (void) restore_line_pointer (c); + goto keep_going; + } else if (operand->flags & MN10300_OPERAND_USP) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "usp") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_SSP) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "ssp") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_MSP) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "msp") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_PC) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "pc") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_EPSW) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "epsw") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_PLUS) @@ -1168,32 +1498,32 @@ md_assemble (str) } else if (operand->flags & MN10300_OPERAND_PSW) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "psw") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_MDR) { - char *start = input_line_pointer; - char c = get_symbol_end (); + char *start; + char c = get_symbol_name (&start); if (strcasecmp (start, "mdr") != 0) { - *input_line_pointer = c; + (void) restore_line_pointer (c); input_line_pointer = hold; str = hold; goto error; } - *input_line_pointer = c; + (void) restore_line_pointer (c); goto keep_going; } else if (operand->flags & MN10300_OPERAND_REG_LIST) @@ -1224,57 +1554,56 @@ md_assemble (str) if (*input_line_pointer == ',') input_line_pointer++; - start = input_line_pointer; - c = get_symbol_end (); + c = get_symbol_name (&start); if (strcasecmp (start, "d2") == 0) { value |= 0x80; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (strcasecmp (start, "d3") == 0) { value |= 0x40; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (strcasecmp (start, "a2") == 0) { value |= 0x20; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (strcasecmp (start, "a3") == 0) { value |= 0x10; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (strcasecmp (start, "other") == 0) { value |= 0x08; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (HAVE_AM33 && strcasecmp (start, "exreg0") == 0) { value |= 0x04; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (HAVE_AM33 && strcasecmp (start, "exreg1") == 0) { value |= 0x02; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (HAVE_AM33 && strcasecmp (start, "exother") == 0) { value |= 0x01; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else if (HAVE_AM33 && strcasecmp (start, "all") == 0) { value |= 0xff; - *input_line_pointer = c; + (void) restore_line_pointer (c); } else { @@ -1284,8 +1613,8 @@ md_assemble (str) } } 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; } @@ -1319,6 +1648,18 @@ md_assemble (str) 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; @@ -1345,6 +1686,8 @@ md_assemble (str) 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; @@ -1368,8 +1711,8 @@ md_assemble (str) 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. */ @@ -1383,16 +1726,15 @@ md_assemble (str) 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: @@ -1411,6 +1753,8 @@ md_assemble (str) 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; } @@ -1421,7 +1765,6 @@ keep_going: while (*str == ' ' || *str == ',') ++str; - } /* Make sure we used all the operands! */ @@ -1432,8 +1775,6 @@ keep_going: that they do indeed not match. */ if (opcode->no_match_operands) { - int i; - /* Look at each operand to see if it's marked. */ for (i = 0; i < MN10300_MAX_OPERANDS; i++) { @@ -1473,7 +1814,7 @@ keep_going: break; } - while (isspace (*str)) + while (ISSPACE (*str)) ++str; if (*str != '\0') @@ -1512,15 +1853,35 @@ keep_going: 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) { @@ -1545,14 +1906,17 @@ keep_going: /* 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. @@ -1651,6 +2015,12 @@ keep_going: 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); @@ -1687,14 +2057,23 @@ keep_going: for (i = 0; i < fc; i++) { const struct mn10300_operand *operand; + int reloc_size; 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_TLS_GD + && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LD + && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LDO + && fixups[i].reloc != BFD_RELOC_MN10300_TLS_GOTIE + && fixups[i].reloc != BFD_RELOC_MN10300_TLS_IE + && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LE + && fixups[i].reloc != BFD_RELOC_MN10300_GOT32) { reloc_howto_type *reloc_howto; - int size; int offset; - fixS *fixP; reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc); @@ -1702,23 +2081,25 @@ keep_going: if (!reloc_howto) abort (); - size = bfd_get_reloc_size (reloc_howto); + reloc_size = bfd_get_reloc_size (reloc_howto); - if (size < 1 || size > 4) + if (reloc_size < 1 || reloc_size > 4) abort (); offset = 4 - size; - fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset, - size, &fixups[i].exp, - reloc_howto->pc_relative, - fixups[i].reloc); + fix_new_exp (frag_now, f - frag_now->fr_literal + offset, + reloc_size, &fixups[i].exp, + reloc_howto->pc_relative, + fixups[i].reloc); } else { - int reloc, pcrel, reloc_size, offset; + int reloc, pcrel, offset; 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) @@ -1730,28 +2111,15 @@ keep_going: /* 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; @@ -1774,373 +2142,497 @@ keep_going: 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; } } - } - - if (debug_type == DEBUG_DWARF2) - { - bfd_vma addr; - /* First update the notion of the current source line. */ - dwarf2_where (&debug_line); - - /* We want the offset of the start of this instruction within the - the current frag. */ - addr = frag_now->fr_address + frag_now_fix () - real_size; - - /* And record the information. */ - dwarf2_gen_line_info (addr, &debug_line); + dwarf2_emit_insn (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; - if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy)) - || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section) + asec = S_GET_SEGMENT (fixp->fx_addsy); + ssec = S_GET_SEGMENT (fixp->fx_subsy); + + /* 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 = bfd_abs_section_ptr->symbol_ptr_ptr; + } + else + { + 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; - 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); + 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; + + default: + reloc->sym_ptr_ptr + = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr; + return relocs; + } + + 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) { - return fixp->fx_frag->fr_address; -#if 0 - 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; -#endif } -int -md_apply_fix3 (fixp, valuep, seg) - fixS *fixp; - valueT *valuep ATTRIBUTE_UNUSED; - 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) * valP; - assert (fixp->fx_r_type < BFD_RELOC_UNUSED); + gas_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. + 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 != 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, fixp->fx_offset, size); - - fixp->fx_done = 1; - return 0; + md_number_to_chars (fixpos, value, size); + /* If a symbol remains, pass the fixup, as a reloc, onto the linker. */ + if (fixP->fx_addsy == NULL) + fixP->fx_done = 1; } -/* Return nonzero if the fixup in FIXP will require a relocation, - even it if appears that the fixup could be completely handled - within GAS. */ +/* Return zero if the fixup in fixp should be left alone and not + adjusted. */ -int -mn10300_force_relocation (fixp) - struct fix *fixp; +bfd_boolean +mn10300_fix_adjustable (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 (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; } -/* Return zero if the fixup in fixp should be left alone and not - adjusted. */ +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")); -boolean -mn10300_fix_adjustable (fixp) - struct fix *fixp; + current_machine = mach; +} + +static inline char * +mn10300_end_of_match (char *cont, const char *what) { - /* Prevent all adjustments to global symbols. */ - if (S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)) - return 0; + int len = strlen (what); - if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT - || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) - return 0; + if (strncmp (cont, what, strlen (what)) == 0 + && ! is_part_of_name (cont[len])) + return cont + len; - return 1; + return NULL; } -/* Insert an operand value into an instruction. */ - -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; +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 if ((next_end = mn10300_end_of_match (next + 1, "tlsgd"))) + reloc_type = BFD_RELOC_MN10300_TLS_GD; + else if ((next_end = mn10300_end_of_match (next + 1, "tlsldm"))) + reloc_type = BFD_RELOC_MN10300_TLS_LD; + else if ((next_end = mn10300_end_of_match (next + 1, "dtpoff"))) + reloc_type = BFD_RELOC_MN10300_TLS_LDO; + else if ((next_end = mn10300_end_of_match (next + 1, "gotntpoff"))) + reloc_type = BFD_RELOC_MN10300_TLS_GOTIE; + else if ((next_end = mn10300_end_of_match (next + 1, "indntpoff"))) + reloc_type = BFD_RELOC_MN10300_TLS_IE; + else if ((next_end = mn10300_end_of_match (next + 1, "tpoff"))) + reloc_type = BFD_RELOC_MN10300_TLS_LE; 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); }