X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-sh.c;h=ee4058456e83dbaa59855081444a57976a4eb012;hb=bc805888417f16602aeb4bfd3f6e33d32ffcae24;hp=6d8ca8164314e3fba3c2c729722253f85d4a9f10;hpb=252b5132c753830d5fd56823373aed85f2a0db63;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c index 6d8ca81643..ee4058456e 100644 --- a/gas/config/tc-sh.c +++ b/gas/config/tc-sh.c @@ -1,5 +1,5 @@ /* tc-sh.c -- Assemble code for the Hitachi Super-H - Copyright (C) 1993, 94, 95, 96, 97, 1998 Free Software Foundation. + Copyright (C) 1993, 94, 95, 96, 97, 98, 99, 2000 Free Software Foundation. This file is part of GAS, the GNU Assembler. @@ -18,10 +18,7 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* - Written By Steve Chamberlain - sac@cygnus.com - */ +/* Written By Steve Chamberlain */ #include #include "as.h" @@ -30,6 +27,14 @@ #define DEFINE_TABLE #include "opcodes/sh-opc.h" #include +#include "struc-symbol.h" + +#ifdef OBJ_ELF +#include "elf/sh.h" +#endif + +#include "dwarf2dbg.h" + const char comment_chars[] = "!"; const char line_separator_chars[] = ";"; const char line_comment_chars[] = "!#"; @@ -39,31 +44,45 @@ static void s_uses PARAMS ((int)); static void sh_count_relocs PARAMS ((bfd *, segT, PTR)); static void sh_frob_section PARAMS ((bfd *, segT, PTR)); -/* This table describes all the machine specific pseudo-ops the assembler - has to support. The fields are: - pseudo-op name without dot - function to call to execute this pseudo-op - Integer arg to pass to the function - */ - void cons (); void s_align_bytes (); static void s_uacons PARAMS ((int)); +static sh_opcode_info *find_cooked_opcode PARAMS ((char **)); +static unsigned int assemble_ppi PARAMS ((char *, sh_opcode_info *)); + +#ifdef OBJ_ELF +static void sh_elf_cons PARAMS ((int)); + +symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ +#endif int shl = 0; static void little (ignore) - int ignore; + int ignore ATTRIBUTE_UNUSED; { shl = 1; target_big_endian = 0; } +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + pseudo-op name without dot + function to call to execute this pseudo-op + Integer arg to pass to the function. */ + const pseudo_typeS md_pseudo_table[] = { +#ifdef OBJ_ELF + {"long", sh_elf_cons, 4}, + {"int", sh_elf_cons, 4}, + {"word", sh_elf_cons, 2}, + {"short", sh_elf_cons, 2}, +#else {"int", cons, 4}, {"word", cons, 2}, +#endif /* OBJ_ELF */ {"form", listing_psize, 0}, {"little", little, 0}, {"heading", listing_title, 0}, @@ -73,6 +92,8 @@ const pseudo_typeS md_pseudo_table[] = {"uses", s_uses, 0}, {"uaword", s_uacons, 2}, {"ualong", s_uacons, 4}, + { "file", dwarf2_directive_file, 0 }, + { "loc", dwarf2_directive_loc, 0 }, {0, 0, 0} }; @@ -84,20 +105,27 @@ int sh_relax; /* set if -relax seen */ int sh_small; +/* Whether -dsp was seen. */ + +static int sh_dsp; + +/* The bit mask of architectures that could + accomodate the insns seen so far. */ +static int valid_arch; + const char EXP_CHARS[] = "eE"; -/* Chars that mean this number is a floating point constant */ +/* Chars that mean this number is a floating point constant. */ /* As in 0f12.456 */ /* or 0d1.2345e12 */ const char FLT_CHARS[] = "rRsSfFdDxXpP"; #define C(a,b) ENCODE_RELAX(a,b) -#define JREG 14 /* Register used as a temp when relaxing */ #define ENCODE_RELAX(what,length) (((what) << 4) + (length)) #define GET_WHAT(x) ((x>>4)) -/* These are the three types of relaxable instrction */ +/* These are the three types of relaxable instrction. */ #define COND_JUMP 1 #define COND_JUMP_DELAY 2 #define UNCOND_JUMP 3 @@ -144,69 +172,267 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP"; #define UNCOND32_M -(1<<30) #define UNCOND32_LENGTH 14 +#define EMPTY { 0, 0, 0, 0 } + const relax_typeS md_relax_table[C (END, 0)] = { - { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, - { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - { 0 }, + EMPTY, /* C (COND_JUMP, COND8) */ { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP, COND12) }, /* C (COND_JUMP, COND12) */ { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), }, /* C (COND_JUMP, COND32) */ { COND32_F, COND32_M, COND32_LENGTH, 0, }, - { 0 }, { 0 }, { 0 }, { 0 }, - { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - { 0 }, + EMPTY, /* C (COND_JUMP_DELAY, COND8) */ { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP_DELAY, COND12) }, /* C (COND_JUMP_DELAY, COND12) */ { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), }, /* C (COND_JUMP_DELAY, COND32) */ { COND32_F, COND32_M, COND32_LENGTH, 0, }, - { 0 }, { 0 }, { 0 }, { 0 }, - { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - { 0 }, + EMPTY, /* C (UNCOND_JUMP, UNCOND12) */ { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), }, /* C (UNCOND_JUMP, UNCOND32) */ { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, }, - { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, - { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, }; +#undef EMPTY + static struct hash_control *opcode_hash_control; /* Opcode mnemonics */ -/* - This function is called once, at assembler startup time. This should - set up all the tables, etc that the MD part of the assembler needs - */ + +#ifdef OBJ_ELF +/* Parse @got, etc. and return the desired relocation. + If we have additional arithmetic expression, then we fill in new_exp_p. */ +static bfd_reloc_code_real_type +sh_elf_suffix (str_p, exp_p, new_exp_p) + char **str_p; + expressionS *exp_p, *new_exp_p; +{ + struct map_bfd { + char *string; + int length; + bfd_reloc_code_real_type reloc; + }; + + char ident[20]; + char *str = *str_p; + char *str2; + int ch; + int len; + struct map_bfd *ptr; + +#define MAP(str,reloc) { str, sizeof (str)-1, reloc } + + static struct map_bfd mapping[] = { + MAP ("got", BFD_RELOC_32_GOT_PCREL), + MAP ("plt", BFD_RELOC_32_PLT_PCREL), + MAP ("gotoff", BFD_RELOC_32_GOTOFF), + { (char *)0, 0, BFD_RELOC_UNUSED } + }; + + if (*str++ != '@') + return BFD_RELOC_UNUSED; + + for (ch = *str, str2 = ident; + (str2 < ident + sizeof (ident) - 1 + && (isalnum (ch) || ch == '@')); + ch = *++str) + { + *str2++ = (islower (ch)) ? ch : tolower (ch); + } + + *str2 = '\0'; + len = str2 - ident; + + ch = ident[0]; + for (ptr = &mapping[0]; ptr->length > 0; ptr++) + if (ch == ptr->string[0] + && len == ptr->length + && memcmp (ident, ptr->string, ptr->length) == 0) + { + /* Now check for identifier@suffix+constant */ + if (*str == '-' || *str == '+') + { + char *orig_line = input_line_pointer; + + input_line_pointer = str; + expression (new_exp_p); + if (new_exp_p->X_op == O_constant) + { + exp_p->X_add_number += new_exp_p->X_add_number; + str = input_line_pointer; + } + if (new_exp_p->X_op == O_subtract) + str = input_line_pointer; + + if (&input_line_pointer != str_p) + input_line_pointer = orig_line; + } + + *str_p = str; + return ptr->reloc; + } + + return BFD_RELOC_UNUSED; +} + +/* The regular cons() function, that reads constants, doesn't support + suffixes such as @GOT, @GOTOFF and @PLT, that generate + machine-specific relocation types. So we must define it here. */ +/* Clobbers input_line_pointer, checks end-of-line. */ +static void +sh_elf_cons (nbytes) + register int nbytes; /* 1=.byte, 2=.word, 4=.long */ +{ + expressionS exp, new_exp; + bfd_reloc_code_real_type reloc; + const char *name; + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + + do + { + expression (&exp); + new_exp.X_op = O_absent; + new_exp.X_add_symbol = new_exp.X_op_symbol = NULL; + /* If the _GLOBAL_OFFSET_TABLE_ symbol hasn't been found yet, + use the name of the symbol to tell whether it's the + _GLOBAL_OFFSET_TABLE_. If it has, comparing the symbols is + sufficient. */ + if (! GOT_symbol && exp.X_add_symbol) + name = S_GET_NAME (exp.X_add_symbol); + else + name = NULL; + /* Check whether this expression involves the + _GLOBAL_OFFSET_TABLE_ symbol, by itself or added to a + difference of two other symbols. */ + if (((GOT_symbol && GOT_symbol == exp.X_add_symbol) + || (! GOT_symbol && name + && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)) + && (exp.X_op == O_symbol + || (exp.X_op == O_add + && ((symbol_get_value_expression (exp.X_op_symbol)->X_op) + == O_subtract)))) + { + reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, + BFD_RELOC_32); + int size = bfd_get_reloc_size (reloc_howto); + + if (GOT_symbol == NULL) + GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME); + + if (size > nbytes) + as_bad (_("%s relocations do not fit in %d bytes\n"), + reloc_howto->name, nbytes); + else + { + register char *p = frag_more ((int) nbytes); + int offset = nbytes - size; + + fix_new_exp (frag_now, p - frag_now->fr_literal + offset, + size, &exp, 0, TC_RELOC_GLOBAL_OFFSET_TABLE); + } + } + /* Check if this symbol involves one of the magic suffixes, such + as @GOT, @GOTOFF or @PLT, and determine which relocation type + to use. */ + else if ((exp.X_op == O_symbol || (exp.X_op == O_add && exp.X_op_symbol)) + && *input_line_pointer == '@' + && ((reloc = sh_elf_suffix (&input_line_pointer, &exp, &new_exp)) + != BFD_RELOC_UNUSED)) + { + reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, + reloc); + int size = bfd_get_reloc_size (reloc_howto); + + /* Force a GOT to be generated. */ + if (GOT_symbol == NULL) + GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME); + + if (size > nbytes) + as_bad (_("%s relocations do not fit in %d bytes\n"), + reloc_howto->name, nbytes); + else + { + register char *p = frag_more ((int) nbytes); + int offset = nbytes - size; + + fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, + &exp, 0, reloc); + if (new_exp.X_op != O_absent) + fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, + &new_exp, 0, BFD_RELOC_32); + } + } + else + emit_expr (&exp, (unsigned int) nbytes); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; /* Put terminator back into stream. */ + if (*input_line_pointer == '#' || *input_line_pointer == '!') + { + while (! is_end_of_line[(unsigned char) *input_line_pointer++]); + } + else + demand_empty_rest_of_line (); +} +#endif /* OBJ_ELF */ + + +/* This function is called once, at assembler startup time. This should + set up all the tables, etc that the MD part of the assembler needs. */ void md_begin () { sh_opcode_info *opcode; char *prev_name = ""; + int target_arch; +#ifdef TE_PE + /* The WinCE OS only supports little endian executables. */ + target_big_endian = 0; +#else if (! shl) target_big_endian = 1; +#endif + + target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up); + valid_arch = target_arch; opcode_hash_control = hash_new (); - /* Insert unique names into hash table */ + /* Insert unique names into hash table. */ for (opcode = sh_table; opcode->name; opcode++) { if (strcmp (prev_name, opcode->name)) { + if (! (opcode->arch & target_arch)) + continue; prev_name = opcode->name; hash_insert (opcode_hash_control, opcode->name, (char *) opcode); } else { /* Make all the opcodes with the same name point to the same - string */ + string. */ opcode->name = prev_name; } } @@ -214,128 +440,254 @@ md_begin () static int reg_m; static int reg_n; +static int reg_x, reg_y; +static int reg_efg; static int reg_b; -static expressionS immediate; /* absolute expression */ - typedef struct { sh_arg_type type; int reg; + expressionS immediate; } - sh_operand_info; -/* try and parse a reg name, returns number of chars consumed */ +#define IDENT_CHAR(c) (isalnum (c) || (c) == '_') + +/* Try to parse a reg name. Return the number of chars consumed. */ + static int parse_reg (src, mode, reg) char *src; int *mode; int *reg; { - /* We use !isalnum for the next character after the register name, to + char l0 = tolower (src[0]); + char l1 = l0 ? tolower (src[1]) : 0; + + /* We use ! IDENT_CHAR for the next character after the register name, to make sure that we won't accidentally recognize a symbol name such as - 'sram' as being a reference to the register 'sr'. */ + 'sram' or sr_ram as being a reference to the register 'sr'. */ - if (src[0] == 'r') + if (l0 == 'r') { - if (src[1] >= '0' && src[1] <= '7' && strncmp(&src[2], "_bank", 5) == 0 - && ! isalnum ((unsigned char) src[7])) + if (l1 == '1') + { + if (src[2] >= '0' && src[2] <= '5' + && ! IDENT_CHAR ((unsigned char) src[3])) + { + *mode = A_REG_N; + *reg = 10 + src[2] - '0'; + return 3; + } + } + if (l1 >= '0' && l1 <= '9' + && ! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = A_REG_N; + *reg = (l1 - '0'); + return 2; + } + if (l1 >= '0' && l1 <= '7' && strncasecmp (&src[2], "_bank", 5) == 0 + && ! IDENT_CHAR ((unsigned char) src[7])) { *mode = A_REG_B; - *reg = (src[1] - '0'); + *reg = (l1 - '0'); return 7; } + + if (l1 == 'e' && ! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = A_RE; + return 2; + } + if (l1 == 's' && ! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = A_RS; + return 2; + } } - if (src[0] == 'r') + if (l0 == 'a') { - if (src[1] == '1') + if (l1 == '0') { - if (src[2] >= '0' && src[2] <= '5' - && ! isalnum ((unsigned char) src[3])) + if (! IDENT_CHAR ((unsigned char) src[2])) { - *mode = A_REG_N; - *reg = 10 + src[2] - '0'; + *mode = DSP_REG_N; + *reg = A_A0_NUM; + return 2; + } + if (tolower (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3])) + { + *mode = DSP_REG_N; + *reg = A_A0G_NUM; return 3; } } - if (src[1] >= '0' && src[1] <= '9' - && ! isalnum ((unsigned char) src[2])) + if (l1 == '1') + { + if (! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = A_A1_NUM; + return 2; + } + if (tolower (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3])) + { + *mode = DSP_REG_N; + *reg = A_A1G_NUM; + return 3; + } + } + + if (l1 == 'x' && src[2] >= '0' && src[2] <= '1' + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_REG_N; - *reg = (src[1] - '0'); + *reg = 4 + (l1 - '0'); + return 3; + } + if (l1 == 'y' && src[2] >= '0' && src[2] <= '1' + && ! IDENT_CHAR ((unsigned char) src[3])) + { + *mode = A_REG_N; + *reg = 6 + (l1 - '0'); + return 3; + } + if (l1 == 's' && src[2] >= '0' && src[2] <= '3' + && ! IDENT_CHAR ((unsigned char) src[3])) + { + int n = l1 - '0'; + + *mode = A_REG_N; + *reg = n | ((~n & 2) << 1); + return 3; + } + } + + if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[3])) + { + if (l1 == 's') + { + *mode = A_REG_N; + *reg = 8; return 2; } + if (l1 == 'x') + { + *mode = A_REG_N; + *reg = 8; + return 2; + } + if (l1 == 'y') + { + *mode = A_REG_N; + *reg = 9; + return 2; + } + } + + if (l0 == 'x' && l1 >= '0' && l1 <= '1' + && ! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = A_X0_NUM + l1 - '0'; + return 2; + } + + if (l0 == 'y' && l1 >= '0' && l1 <= '1' + && ! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = A_Y0_NUM + l1 - '0'; + return 2; + } + + if (l0 == 'm' && l1 >= '0' && l1 <= '1' + && ! IDENT_CHAR ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = l1 == '0' ? A_M0_NUM : A_M1_NUM; + return 2; } - if (src[0] == 's' - && src[1] == 's' - && src[2] == 'r' && ! isalnum ((unsigned char) src[3])) + if (l0 == 's' + && l1 == 's' + && tolower (src[2]) == 'r' && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_SSR; return 3; } - if (src[0] == 's' && src[1] == 'p' && src[2] == 'c' - && ! isalnum ((unsigned char) src[3])) + if (l0 == 's' && l1 == 'p' && tolower (src[2]) == 'c' + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_SPC; return 3; } - if (src[0] == 's' && src[1] == 'g' && src[2] == 'r' - && ! isalnum ((unsigned char) src[3])) + if (l0 == 's' && l1 == 'g' && tolower (src[2]) == 'r' + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_SGR; return 3; } - if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r' - && ! isalnum ((unsigned char) src[3])) + if (l0 == 'd' && l1 == 's' && tolower (src[2]) == 'r' + && ! IDENT_CHAR ((unsigned char) src[3])) + { + *mode = A_DSR; + return 3; + } + + if (l0 == 'd' && l1 == 'b' && tolower (src[2]) == 'r' + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_DBR; return 3; } - if (src[0] == 's' && src[1] == 'r' && ! isalnum ((unsigned char) src[2])) + if (l0 == 's' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2])) { *mode = A_SR; return 2; } - if (src[0] == 's' && src[1] == 'p' && ! isalnum ((unsigned char) src[2])) + if (l0 == 's' && l1 == 'p' && ! IDENT_CHAR ((unsigned char) src[2])) { *mode = A_REG_N; *reg = 15; return 2; } - if (src[0] == 'p' && src[1] == 'r' && ! isalnum ((unsigned char) src[2])) + if (l0 == 'p' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2])) { *mode = A_PR; return 2; } - if (src[0] == 'p' && src[1] == 'c' && ! isalnum ((unsigned char) src[2])) + if (l0 == 'p' && l1 == 'c' && ! IDENT_CHAR ((unsigned char) src[2])) { - *mode = A_DISP_PC; + /* Don't use A_DISP_PC here - that would accept stuff like 'mova pc,r0' + and use an uninitialized immediate. */ + *mode = A_PC; return 2; } - if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r' - && ! isalnum ((unsigned char) src[3])) + if (l0 == 'g' && l1 == 'b' && tolower (src[2]) == 'r' + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_GBR; return 3; } - if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r' - && ! isalnum ((unsigned char) src[3])) + if (l0 == 'v' && l1 == 'b' && tolower (src[2]) == 'r' + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = A_VBR; return 3; } - if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c' - && ! isalnum ((unsigned char) src[4])) + if (l0 == 'm' && l1 == 'a' && tolower (src[2]) == 'c' + && ! IDENT_CHAR ((unsigned char) src[4])) { if (src[3] == 'l') { @@ -348,12 +700,18 @@ parse_reg (src, mode, reg) return 4; } } - if (src[0] == 'f' && src[1] == 'r') + if (l0 == 'm' && l1 == 'o' && tolower (src[2]) == 'd' + && ! IDENT_CHAR ((unsigned char) src[4])) + { + *mode = A_MOD; + return 3; + } + if (l0 == 'f' && l1 == 'r') { if (src[2] == '1') { if (src[3] >= '0' && src[3] <= '5' - && ! isalnum ((unsigned char) src[4])) + && ! IDENT_CHAR ((unsigned char) src[4])) { *mode = F_REG_N; *reg = 10 + src[3] - '0'; @@ -361,19 +719,19 @@ parse_reg (src, mode, reg) } } if (src[2] >= '0' && src[2] <= '9' - && ! isalnum ((unsigned char) src[3])) + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = F_REG_N; *reg = (src[2] - '0'); return 3; } } - if (src[0] == 'd' && src[1] == 'r') + if (l0 == 'd' && l1 == 'r') { if (src[2] == '1') { if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1) - && ! isalnum ((unsigned char) src[4])) + && ! IDENT_CHAR ((unsigned char) src[4])) { *mode = D_REG_N; *reg = 10 + src[3] - '0'; @@ -381,19 +739,19 @@ parse_reg (src, mode, reg) } } if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1) - && ! isalnum ((unsigned char) src[3])) + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = D_REG_N; *reg = (src[2] - '0'); return 3; } } - if (src[0] == 'x' && src[1] == 'd') + if (l0 == 'x' && l1 == 'd') { if (src[2] == '1') { if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1) - && ! isalnum ((unsigned char) src[4])) + && ! IDENT_CHAR ((unsigned char) src[4])) { *mode = X_REG_N; *reg = 11 + src[3] - '0'; @@ -401,45 +759,48 @@ parse_reg (src, mode, reg) } } if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1) - && ! isalnum ((unsigned char) src[3])) + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = X_REG_N; *reg = (src[2] - '0') + 1; return 3; } } - if (src[0] == 'f' && src[1] == 'v') + if (l0 == 'f' && l1 == 'v') { - if (src[2] == '1'&& src[3] == '2' && ! isalnum ((unsigned char) src[4])) + if (src[2] == '1'&& src[3] == '2' && ! IDENT_CHAR ((unsigned char) src[4])) { *mode = V_REG_N; *reg = 12; return 4; } if ((src[2] == '0' || src[2] == '4' || src[2] == '8') - && ! isalnum ((unsigned char) src[3])) + && ! IDENT_CHAR ((unsigned char) src[3])) { *mode = V_REG_N; *reg = (src[2] - '0'); return 3; } } - if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l' - && ! isalnum ((unsigned char) src[4])) + if (l0 == 'f' && l1 == 'p' && tolower (src[2]) == 'u' + && tolower (src[3]) == 'l' + && ! IDENT_CHAR ((unsigned char) src[4])) { *mode = FPUL_N; return 4; } - if (src[0] == 'f' && src[1] == 'p' && src[2] == 's' && src[3] == 'c' - && src[4] == 'r' && ! isalnum ((unsigned char) src[5])) + if (l0 == 'f' && l1 == 'p' && tolower (src[2]) == 's' + && tolower (src[3]) == 'c' + && tolower (src[4]) == 'r' && ! IDENT_CHAR ((unsigned char) src[5])) { *mode = FPSCR_N; return 5; } - if (src[0] == 'x' && src[1] == 'm' && src[2] == 't' && src[3] == 'r' - && src[4] == 'x' && ! isalnum ((unsigned char) src[5])) + if (l0 == 'x' && l1 == 'm' && tolower (src[2]) == 't' + && tolower (src[3]) == 'r' + && tolower (src[4]) == 'x' && ! IDENT_CHAR ((unsigned char) src[5])) { *mode = XMTRX_M4; return 5; @@ -448,7 +809,8 @@ parse_reg (src, mode, reg) return 0; } -static symbolS *dot() +static symbolS * +dot () { const char *fake; @@ -459,29 +821,26 @@ static symbolS *dot() now_seg, (valueT) frag_now_fix (), frag_now); - } - -static -char * -parse_exp (s) +static char * +parse_exp (s, op) char *s; + sh_operand_info *op; { char *save; char *new; save = input_line_pointer; input_line_pointer = s; - expression (&immediate); - if (immediate.X_op == O_absent) + expression (&op->immediate); + if (op->immediate.X_op == O_absent) as_bad (_("missing operand")); new = input_line_pointer; input_line_pointer = save; return new; } - /* The many forms of operand: Rn Register direct @@ -499,11 +858,9 @@ parse_exp (s) disp:12 #imm8 pr, gbr, vbr, macl, mach - */ -static -char * +static char * parse_at (src, op) char *src; sh_operand_info *op; @@ -513,7 +870,7 @@ parse_at (src, op) src++; if (src[0] == '-') { - /* Must be predecrement */ + /* Must be predecrement. */ src++; len = parse_reg (src, &mode, &(op->reg)); @@ -556,7 +913,7 @@ parse_at (src, op) else { /* Must be an @(disp,.. thing) */ - src = parse_exp (src); + src = parse_exp (src, op); if (src[0] == ',') src++; /* Now can be rn, gbr or pc */ @@ -571,13 +928,14 @@ parse_at (src, op) { op->type = A_DISP_GBR; } - else if (mode == A_DISP_PC) + else if (mode == A_PC) { - /* Turn a plain @(4,pc) into @(.+4,pc) */ - if (immediate.X_op == O_constant) { - immediate.X_add_symbol = dot(); - immediate.X_op = O_symbol; - } + /* Turn a plain @(4,pc) into @(.+4,pc). */ + if (op->immediate.X_op == O_constant) + { + op->immediate.X_add_symbol = dot(); + op->immediate.X_op = O_symbol; + } op->type = A_DISP_PC; } else @@ -605,8 +963,21 @@ parse_at (src, op) } if (src[0] == '+') { - op->type = A_INC_N; src++; + if ((src[0] == 'r' && src[1] == '8') + || (src[0] == 'i' && (src[1] == 'x' || src[1] == 's'))) + { + src += 2; + op->type = A_PMOD_N; + } + if ((src[0] == 'r' && src[1] == '9') + || (src[0] == 'i' && src[1] == 'y')) + { + src += 2; + op->type = A_PMODY_N; + } + else + op->type = A_INC_N; } else { @@ -628,7 +999,7 @@ get_operand (ptr, op) if (src[0] == '#') { src++; - *ptr = parse_exp (src); + *ptr = parse_exp (src, op); op->type = A_IMM; return; } @@ -647,25 +1018,27 @@ get_operand (ptr, op) } else { - /* Not a reg, the only thing left is a displacement */ - *ptr = parse_exp (src); + /* Not a reg, the only thing left is a displacement. */ + *ptr = parse_exp (src, op); op->type = A_DISP_PC; return; } } -static -char * +static char * get_operands (info, args, operand) sh_opcode_info *info; char *args; sh_operand_info *operand; - { char *ptr = args; if (info->arg[0]) { - ptr++; + /* The pre-processor will eliminate whitespace in front of '@' + after the first argument; we may be called multiple times + from assemble_ppi, so don't insist on finding whitespace here. */ + if (*ptr == ' ') + ptr++; get_operand (&ptr, operand + 0); if (info->arg[1]) @@ -675,7 +1048,13 @@ get_operands (info, args, operand) ptr++; } get_operand (&ptr, operand + 1); - if (info->arg[2]) + /* ??? Hack: psha/pshl have a varying operand number depending on + the type of the first operand. We handle this by having the + three-operand version first and reducing the number of operands + parsed to two if we see that the first operand is an immediate. + This works because no insn with three operands has an immediate + as first operand. */ + if (info->arg[2] && operand[0].type != A_IMM) { if (*ptr == ',') { @@ -705,11 +1084,9 @@ get_operands (info, args, operand) /* Passed a pointer to a list of opcodes which use different addressing modes, return the opcode which matches the opcodes - provided - */ + provided. */ -static -sh_opcode_info * +static sh_opcode_info * get_specific (opcode, operands) sh_opcode_info *opcode; sh_operand_info *operands; @@ -717,24 +1094,26 @@ get_specific (opcode, operands) sh_opcode_info *this_try = opcode; char *name = opcode->name; int n = 0; + while (opcode->name) { this_try = opcode++; if (this_try->name != name) { /* We've looked so far down the table that we've run out of - opcodes with the same name */ + opcodes with the same name. */ return 0; } - /* look at both operands needed by the opcodes and provided by + + /* Look at both operands needed by the opcodes and provided by the user - since an arg test will often fail on the same arg again and again, we'll try and test the last failing arg the - first on each opcode try */ - + first on each opcode try. */ for (n = 0; this_try->arg[n]; n++) { sh_operand_info *user = operands + n; sh_arg_type arg = this_try->arg[n]; + switch (arg) { case A_IMM: @@ -774,16 +1153,14 @@ get_specific (opcode, operands) case V_REG_N: case FPUL_N: case FPSCR_N: + case A_PMOD_N: + case A_PMODY_N: + case DSP_REG_N: /* Opcode needs rn */ if (user->type != arg) goto fail; reg_n = user->reg; break; - case FD_REG_N: - if (user->type != F_REG_N && user->type != D_REG_N) - goto fail; - reg_n = user->reg; - break; case DX_REG_N: if (user->type != D_REG_N && user->type != X_REG_N) goto fail; @@ -792,6 +1169,10 @@ get_specific (opcode, operands) case A_GBR: case A_SR: case A_VBR: + case A_DSR: + case A_MOD: + case A_RE: + case A_RS: case A_SSR: case A_SPC: case A_SGR: @@ -800,7 +1181,7 @@ get_specific (opcode, operands) goto fail; break; - case A_REG_B: + case A_REG_B: if (user->type != arg) goto fail; reg_b = user->reg; @@ -812,12 +1193,144 @@ get_specific (opcode, operands) case A_IND_M: case A_IND_R0_REG_M: case A_DISP_REG_M: + case DSP_REG_M: /* Opcode needs rn */ if (user->type != arg - A_REG_M + A_REG_N) goto fail; reg_m = user->reg; break; + case DSP_REG_X: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_X0_NUM: + reg_x = 0; + break; + case A_X1_NUM: + reg_x = 1; + break; + case A_A0_NUM: + reg_x = 2; + break; + case A_A1_NUM: + reg_x = 3; + break; + default: + goto fail; + } + break; + + case DSP_REG_Y: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_Y0_NUM: + reg_y = 0; + break; + case A_Y1_NUM: + reg_y = 1; + break; + case A_M0_NUM: + reg_y = 2; + break; + case A_M1_NUM: + reg_y = 3; + break; + default: + goto fail; + } + break; + + case DSP_REG_E: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_X0_NUM: + reg_efg = 0 << 10; + break; + case A_X1_NUM: + reg_efg = 1 << 10; + break; + case A_Y0_NUM: + reg_efg = 2 << 10; + break; + case A_A1_NUM: + reg_efg = 3 << 10; + break; + default: + goto fail; + } + break; + + case DSP_REG_F: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_Y0_NUM: + reg_efg |= 0 << 8; + break; + case A_Y1_NUM: + reg_efg |= 1 << 8; + break; + case A_X0_NUM: + reg_efg |= 2 << 8; + break; + case A_A1_NUM: + reg_efg |= 3 << 8; + break; + default: + goto fail; + } + break; + + case DSP_REG_G: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_M0_NUM: + reg_efg |= 0 << 2; + break; + case A_M1_NUM: + reg_efg |= 1 << 2; + break; + case A_A0_NUM: + reg_efg |= 2 << 2; + break; + case A_A1_NUM: + reg_efg |= 3 << 2; + break; + default: + goto fail; + } + break; + + case A_A0: + if (user->type != DSP_REG_N || user->reg != A_A0_NUM) + goto fail; + break; + case A_X0: + if (user->type != DSP_REG_N || user->reg != A_X0_NUM) + goto fail; + break; + case A_X1: + if (user->type != DSP_REG_N || user->reg != A_X1_NUM) + goto fail; + break; + case A_Y0: + if (user->type != DSP_REG_N || user->reg != A_Y0_NUM) + goto fail; + break; + case A_Y1: + if (user->type != DSP_REG_N || user->reg != A_Y1_NUM) + goto fail; + break; + case F_REG_M: case D_REG_M: case X_REG_M: @@ -839,14 +1352,18 @@ get_specific (opcode, operands) goto fail; reg_m = 4; break; - + default: printf (_("unhandled %d\n"), arg); goto fail; } } + if ( !(valid_arch & this_try->arch)) + goto fail; + valid_arch &= this_try->arch; return this_try; - fail:; + fail: + ; } return 0; @@ -867,24 +1384,25 @@ check (operand, low, high) return operand->X_add_number; } - static void -insert (where, how, pcrel) +insert (where, how, pcrel, op) char *where; int how; int pcrel; + sh_operand_info *op; { fix_new_exp (frag_now, where - frag_now->fr_literal, 2, - &immediate, + &op->immediate, pcrel, how); } static void -build_relax (opcode) +build_relax (opcode, op) sh_opcode_info *opcode; + sh_operand_info *op; { int high_byte = target_big_endian ? 0 : 1; char *p; @@ -896,8 +1414,8 @@ build_relax (opcode) md_relax_table[C (what, COND32)].rlx_length, md_relax_table[C (what, COND8)].rlx_length, C (what, 0), - immediate.X_add_symbol, - immediate.X_add_number, + op->immediate.X_add_symbol, + op->immediate.X_add_number, 0); p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]); } @@ -907,17 +1425,67 @@ build_relax (opcode) md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length, md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length, C (UNCOND_JUMP, 0), - immediate.X_add_symbol, - immediate.X_add_number, + op->immediate.X_add_symbol, + op->immediate.X_add_number, 0); p[high_byte] = (opcode->nibbles[0] << 4); } } -/* Now we know what sort of opcodes it is, lets build the bytes - - */ -static void +/* Insert ldrs & ldre with fancy relocations that relaxation can recognize. */ + +static char * +insert_loop_bounds (output, operand) + char *output; + sh_operand_info *operand; +{ + char *name; + symbolS *end_sym; + + /* Since the low byte of the opcode will be overwritten by the reloc, we + can just stash the high byte into both bytes and ignore endianness. */ + output[0] = 0x8c; + output[1] = 0x8c; + insert (output, BFD_RELOC_SH_LOOP_START, 1, operand); + insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1); + + if (sh_relax) + { + static int count = 0; + + /* If the last loop insn is a two-byte-insn, it is in danger of being + swapped with the insn after it. To prevent this, create a new + symbol - complete with SH_LABEL reloc - after the last loop insn. + If the last loop insn is four bytes long, the symbol will be + right in the middle, but four byte insns are not swapped anyways. */ + /* A REPEAT takes 6 bytes. The SH has a 32 bit address space. + Hence a 9 digit number should be enough to count all REPEATs. */ + name = alloca (11); + sprintf (name, "_R%x", count++ & 0x3fffffff); + end_sym = symbol_new (name, undefined_section, 0, &zero_address_frag); + /* Make this a local symbol. */ +#ifdef OBJ_COFF + SF_SET_LOCAL (end_sym); +#endif /* OBJ_COFF */ + symbol_table_insert (end_sym); + end_sym->sy_value = operand[1].immediate; + end_sym->sy_value.X_add_number += 2; + fix_new (frag_now, frag_now_fix (), 2, end_sym, 0, 1, BFD_RELOC_SH_LABEL); + } + + output = frag_more (2); + output[0] = 0x8e; + output[1] = 0x8e; + insert (output, BFD_RELOC_SH_LOOP_START, 1, operand); + insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1); + + return frag_more (2); +} + +/* Now we know what sort of opcodes it is, let's build the bytes. */ + +static unsigned int build_Mytes (opcode, operand) sh_opcode_info *opcode; sh_operand_info *operand; @@ -926,6 +1494,7 @@ build_Mytes (opcode, operand) int index; char nbuf[4]; char *output = frag_more (2); + unsigned int size = 2; int low_byte = target_big_endian ? 1 : 0; nbuf[0] = 0; nbuf[1] = 0; @@ -949,98 +1518,359 @@ build_Mytes (opcode, operand) case REG_M: nbuf[index] = reg_m; break; + case SDT_REG_N: + if (reg_n < 2 || reg_n > 5) + as_bad (_("Invalid register: 'r%d'"), reg_n); + nbuf[index] = (reg_n & 3) | 4; + break; case REG_NM: nbuf[index] = reg_n | (reg_m >> 2); break; - case REG_B: + case REG_B: nbuf[index] = reg_b | 0x08; break; - case DISP_4: - insert (output + low_byte, BFD_RELOC_SH_IMM4, 0); + case IMM0_4BY4: + insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand); + break; + case IMM0_4BY2: + insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand); + break; + case IMM0_4: + insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand); + break; + case IMM1_4BY4: + insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand + 1); break; - case IMM_4BY4: - insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0); + case IMM1_4BY2: + insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand + 1); break; - case IMM_4BY2: - insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0); + case IMM1_4: + insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand + 1); break; - case IMM_4: - insert (output + low_byte, BFD_RELOC_SH_IMM4, 0); + case IMM0_8BY4: + insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand); break; - case IMM_8BY4: - insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0); + case IMM0_8BY2: + insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand); break; - case IMM_8BY2: - insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0); + case IMM0_8: + insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand); break; - case IMM_8: - insert (output + low_byte, BFD_RELOC_SH_IMM8, 0); + case IMM1_8BY4: + insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand + 1); + break; + case IMM1_8BY2: + insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand + 1); + break; + case IMM1_8: + insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1); break; case PCRELIMM_8BY4: - insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1); + insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1, operand); break; case PCRELIMM_8BY2: - insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1); + insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1, operand); + break; + case REPEAT: + output = insert_loop_bounds (output, operand); + nbuf[index] = opcode->nibbles[3]; + operand += 2; break; default: printf (_("failed for %d\n"), i); } } } - if (! target_big_endian) { - output[1] = (nbuf[0] << 4) | (nbuf[1]); - output[0] = (nbuf[2] << 4) | (nbuf[3]); - } - else { - output[0] = (nbuf[0] << 4) | (nbuf[1]); - output[1] = (nbuf[2] << 4) | (nbuf[3]); - } + if (!target_big_endian) + { + output[1] = (nbuf[0] << 4) | (nbuf[1]); + output[0] = (nbuf[2] << 4) | (nbuf[3]); + } + else + { + output[0] = (nbuf[0] << 4) | (nbuf[1]); + output[1] = (nbuf[2] << 4) | (nbuf[3]); + } + return size; } -/* This is the guts of the machine-dependent assembler. STR points to a - machine dependent instruction. This function is supposed to emit - the frags/bytes it assembles to. - */ +/* Find an opcode at the start of *STR_P in the hash table, and set + *STR_P to the first character after the last one read. */ -void -md_assemble (str) - char *str; +static sh_opcode_info * +find_cooked_opcode (str_p) + char **str_p; { + char *str = *str_p; unsigned char *op_start; unsigned char *op_end; - sh_operand_info operand[3]; - sh_opcode_info *opcode; char name[20]; int nlen = 0; - /* Drop leading whitespace */ + + /* Drop leading whitespace. */ while (*str == ' ') str++; - /* find the op code end */ + /* Find the op code end. + The pre-processor will eliminate whitespace in front of + any '@' after the first argument; we may be called from + assemble_ppi, so the opcode might be terminated by an '@'. */ for (op_start = op_end = (unsigned char *) (str); *op_end && nlen < 20 - && !is_end_of_line[*op_end] && *op_end != ' '; + && !is_end_of_line[*op_end] && *op_end != ' ' && *op_end != '@'; op_end++) { unsigned char c = op_start[nlen]; /* The machine independent code will convert CMP/EQ into cmp/EQ - because it thinks the '/' is the end of the symbol. Instead of - hacking up the machine independent code, we just deal with it - here. */ + because it thinks the '/' is the end of the symbol. Moreover, + all but the first sub-insn is a parallel processing insn won't + be capitailzed. Instead of hacking up the machine independent + code, we just deal with it here. */ c = isupper (c) ? tolower (c) : c; name[nlen] = c; nlen++; } + name[nlen] = 0; + *str_p = op_end; if (nlen == 0) + as_bad (_("can't find opcode ")); + + return (sh_opcode_info *) hash_find (opcode_hash_control, name); +} + +/* Assemble a parallel processing insn. */ +#define DDT_BASE 0xf000 /* Base value for double data transfer insns */ + +static unsigned int +assemble_ppi (op_end, opcode) + char *op_end; + sh_opcode_info *opcode; +{ + int movx = 0; + int movy = 0; + int cond = 0; + int field_b = 0; + char *output; + int move_code; + unsigned int size; + + /* Some insn ignore one or more register fields, e.g. psts machl,a0. + Make sure we encode a defined insn pattern. */ + reg_x = 0; + reg_y = 0; + + for (;;) + { + sh_operand_info operand[3]; + + if (opcode->arg[0] != A_END) + op_end = get_operands (opcode, op_end, operand); + opcode = get_specific (opcode, operand); + if (opcode == 0) + { + /* Couldn't find an opcode which matched the operands. */ + char *where = frag_more (2); + size = 2; + + where[0] = 0x0; + where[1] = 0x0; + as_bad (_("invalid operands for opcode")); + return size; + } + + if (opcode->nibbles[0] != PPI) + as_bad (_("insn can't be combined with parallel processing insn")); + + switch (opcode->nibbles[1]) + { + + case NOPX: + if (movx) + as_bad (_("multiple movx specifications")); + movx = DDT_BASE; + break; + case NOPY: + if (movy) + as_bad (_("multiple movy specifications")); + movy = DDT_BASE; + break; + + case MOVX: + if (movx) + as_bad (_("multiple movx specifications")); + if (reg_n < 4 || reg_n > 5) + as_bad (_("invalid movx address register")); + if (opcode->nibbles[2] & 8) + { + if (reg_m == A_A1_NUM) + movx = 1 << 7; + else if (reg_m != A_A0_NUM) + as_bad (_("invalid movx dsp register")); + } + else + { + if (reg_x > 1) + as_bad (_("invalid movx dsp register")); + movx = reg_x << 7; + } + movx += ((reg_n - 4) << 9) + (opcode->nibbles[2] << 2) + DDT_BASE; + break; + + case MOVY: + if (movy) + as_bad (_("multiple movy specifications")); + if (opcode->nibbles[2] & 8) + { + /* Bit 3 in nibbles[2] is intended for bit 4 of the opcode, + so add 8 more. */ + movy = 8; + if (reg_m == A_A1_NUM) + movy += 1 << 6; + else if (reg_m != A_A0_NUM) + as_bad (_("invalid movy dsp register")); + } + else + { + if (reg_y > 1) + as_bad (_("invalid movy dsp register")); + movy = reg_y << 6; + } + if (reg_n < 6 || reg_n > 7) + as_bad (_("invalid movy address register")); + movy += ((reg_n - 6) << 8) + opcode->nibbles[2] + DDT_BASE; + break; + + case PSH: + if (operand[0].immediate.X_op != O_constant) + as_bad (_("dsp immediate shift value not constant")); + field_b = ((opcode->nibbles[2] << 12) + | (operand[0].immediate.X_add_number & 127) << 4 + | reg_n); + break; + case PPI3: + if (field_b) + as_bad (_("multiple parallel processing specifications")); + field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8) + + (reg_x << 6) + (reg_y << 4) + reg_n); + break; + case PDC: + if (cond) + as_bad (_("multiple condition specifications")); + cond = opcode->nibbles[2] << 8; + if (*op_end) + goto skip_cond_check; + break; + case PPIC: + if (field_b) + as_bad (_("multiple parallel processing specifications")); + field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8) + + cond + (reg_x << 6) + (reg_y << 4) + reg_n); + cond = 0; + break; + case PMUL: + if (field_b) + { + if ((field_b & 0xef00) != 0xa100) + as_bad (_("insn cannot be combined with pmuls")); + field_b -= 0x8100; + switch (field_b & 0xf) + { + case A_X0_NUM: + field_b += 0 - A_X0_NUM; + break; + case A_Y0_NUM: + field_b += 1 - A_Y0_NUM; + break; + case A_A0_NUM: + field_b += 2 - A_A0_NUM; + break; + case A_A1_NUM: + field_b += 3 - A_A1_NUM; + break; + default: + as_bad (_("bad padd / psub pmuls output operand")); + } + } + field_b += 0x4000 + reg_efg; + break; + default: + abort (); + } + if (cond) + { + as_bad (_("condition not followed by conditionalizable insn")); + cond = 0; + } + if (! *op_end) + break; + skip_cond_check: + opcode = find_cooked_opcode (&op_end); + if (opcode == NULL) + { + (as_bad + (_("unrecognized characters at end of parallel processing insn"))); + break; + } + } + + move_code = movx | movy; + if (field_b) + { + /* Parallel processing insn. */ + unsigned long ppi_code = (movx | movy | 0xf800) << 16 | field_b; + + output = frag_more (4); + size = 4; + if (! target_big_endian) + { + output[3] = ppi_code >> 8; + output[2] = ppi_code; + } + else + { + output[2] = ppi_code >> 8; + output[3] = ppi_code; + } + move_code |= 0xf800; + } + else + { + /* Just a double data transfer. */ + output = frag_more (2); + size = 2; + } + if (! target_big_endian) + { + output[1] = move_code >> 8; + output[0] = move_code; + } + else { - as_bad (_("can't find opcode ")); + output[0] = move_code >> 8; + output[1] = move_code; } + return size; +} + +/* This is the guts of the machine-dependent assembler. STR points to a + machine dependent instruction. This function is supposed to emit + the frags/bytes it assembles to. */ + +void +md_assemble (str) + char *str; +{ + unsigned char *op_end; + sh_operand_info operand[3]; + sh_opcode_info *opcode; + unsigned int size = 0; - opcode = (sh_opcode_info *) hash_find (opcode_hash_control, name); + opcode = find_cooked_opcode (&str); + op_end = str; if (opcode == NULL) { @@ -1058,34 +1888,54 @@ md_assemble (str) seg_info (now_seg)->tc_segment_info_data.in_code = 1; } - if (opcode->arg[0] == A_BDISP12 - || opcode->arg[0] == A_BDISP8) + if (opcode->nibbles[0] == PPI) { - parse_exp (op_end + 1); - build_relax (opcode); + size = assemble_ppi (op_end, opcode); } else { - if (opcode->arg[0] != A_END) + if (opcode->arg[0] == A_BDISP12 + || opcode->arg[0] == A_BDISP8) { - get_operands (opcode, op_end, operand); + parse_exp (op_end + 1, &operand[0]); + build_relax (opcode, &operand[0]); } - opcode = get_specific (opcode, operand); - - if (opcode == 0) + else { - /* Couldn't find an opcode which matched the operands */ - char *where = frag_more (2); + if (opcode->arg[0] == A_END) + { + /* Ignore trailing whitespace. If there is any, it has already + been compressed to a single space. */ + if (*op_end == ' ') + op_end++; + } + else + { + op_end = get_operands (opcode, op_end, operand); + } + opcode = get_specific (opcode, operand); - where[0] = 0x0; - where[1] = 0x0; - as_bad (_("invalid operands for opcode")); - return; - } + if (opcode == 0) + { + /* Couldn't find an opcode which matched the operands. */ + char *where = frag_more (2); + size = 2; - build_Mytes (opcode, operand); + where[0] = 0x0; + where[1] = 0x0; + as_bad (_("invalid operands for opcode")); + } + else + { + if (*op_end) + as_bad (_("excess operands: '%s'"), op_end); + + size = build_Mytes (opcode, operand); + } + } } + dwarf2_emit_insn (size); } /* This routine is called each time a label definition is seen. It @@ -1105,7 +1955,7 @@ sh_frob_label () offset = frag_now_fix (); if (frag_now != last_label_frag || offset != last_label_offset) - { + { fix_new (frag_now, offset, 2, &abs_symbol, 0, 0, BFD_RELOC_SH_LABEL); last_label_frag = frag_now; last_label_offset = offset; @@ -1129,38 +1979,59 @@ sh_flush_pending_output () } symbolS * -DEFUN (md_undefined_symbol, (name), - char *name) +md_undefined_symbol (name) + char *name; { +#ifdef OBJ_ELF + /* Under ELF we need to default _GLOBAL_OFFSET_TABLE. Otherwise we + have no need to default values of symbols. */ + if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0) + { + if (!GOT_symbol) + { + if (symbol_find (name)) + as_bad ("GOT already in the symbol table"); + + GOT_symbol = symbol_new (name, undefined_section, + (valueT)0, & zero_address_frag); + } + + return GOT_symbol; + } +#endif /* OBJ_ELF */ + return 0; } #ifdef OBJ_COFF +#ifndef BFD_ASSEMBLER void -DEFUN (tc_crawl_symbol_chain, (headers), - object_headers * headers) +tc_crawl_symbol_chain (headers) + object_headers *headers; { printf (_("call to tc_crawl_symbol_chain \n")); } void -DEFUN (tc_headers_hook, (headers), - object_headers * headers) +tc_headers_hook (headers) + object_headers *headers; { printf (_("call to tc_headers_hook \n")); } +#endif #endif -/* Various routines to kill one day */ -/* Equal to MAX_PRECISION in atof-ieee.c */ +/* Various routines to kill one day. */ +/* Equal to MAX_PRECISION in atof-ieee.c. */ #define MAX_LITTLENUMS 6 -/* Turn a string in input_line_pointer into a floating point constant of type - type, and store the appropriate bytes in *litP. The number of LITTLENUMS - emitted is stored in *sizeP . An error message is returned, or NULL on OK. - */ +/* Turn a string in input_line_pointer into a floating point constant + of type TYPE, and store the appropriate bytes in *LITP. The number + of LITTLENUMS emitted is stored in *SIZEP . An error message is + returned, or NULL on OK. */ + char * md_atof (type, litP, sizeP) int type; @@ -1209,7 +2080,7 @@ md_atof (type, litP, sizeP) litP += 2; } } - + return NULL; } @@ -1220,7 +2091,7 @@ md_atof (type, litP, sizeP) static void s_uses (ignore) - int ignore; + int ignore ATTRIBUTE_UNUSED; { expressionS ex; @@ -1242,23 +2113,25 @@ s_uses (ignore) } CONST char *md_shortopts = ""; -struct option md_longopts[] = { - +struct option md_longopts[] = +{ #define OPTION_RELAX (OPTION_MD_BASE) #define OPTION_LITTLE (OPTION_MD_BASE + 1) #define OPTION_SMALL (OPTION_LITTLE + 1) +#define OPTION_DSP (OPTION_SMALL + 1) {"relax", no_argument, NULL, OPTION_RELAX}, {"little", no_argument, NULL, OPTION_LITTLE}, {"small", no_argument, NULL, OPTION_SMALL}, + {"dsp", no_argument, NULL, OPTION_DSP}, {NULL, no_argument, NULL, 0} }; -size_t md_longopts_size = sizeof(md_longopts); +size_t md_longopts_size = sizeof (md_longopts); int md_parse_option (c, arg) int c; - char *arg; + char *arg ATTRIBUTE_UNUSED; { switch (c) { @@ -1275,6 +2148,10 @@ md_parse_option (c, arg) sh_small = 1; break; + case OPTION_DSP: + sh_dsp = 1; + break; + default: return 0; } @@ -1286,11 +2163,12 @@ void md_show_usage (stream) FILE *stream; { - fprintf(stream, _("\ + fprintf (stream, _("\ SH options:\n\ -little generate little endian code\n\ -relax alter jump instructions for long displacements\n\ --small align sections to 4 byte boundaries, not 16\n")); +-small align sections to 4 byte boundaries, not 16\n\ +-dsp enable sh-dsp insns, and disable sh3e / sh4 insns.\n")); } void @@ -1315,10 +2193,9 @@ struct sh_count_relocs symbol. When using BFD_ASSEMBLER, this is called via bfd_map_over_sections. */ -/*ARGSUSED*/ static void sh_count_relocs (abfd, sec, data) - bfd *abfd; + bfd *abfd ATTRIBUTE_UNUSED; segT sec; PTR data; { @@ -1345,12 +2222,11 @@ sh_count_relocs (abfd, sec, data) /* Handle the count relocs for a particular section. When using BFD_ASSEMBLER, this is called via bfd_map_over_sections. */ -/*ARGSUSED*/ static void sh_frob_section (abfd, sec, ignore) - bfd *abfd; + bfd *abfd ATTRIBUTE_UNUSED; segT sec; - PTR ignore; + PTR ignore ATTRIBUTE_UNUSED; { segment_info_type *seginfo; fixS *fix; @@ -1411,8 +2287,8 @@ sh_frob_section (abfd, sec, ignore) continue; } - /* fscan should also be a fixup to a local symbol in the same - section. */ + /* The variable fscan should also be a fixup to a local symbol + in the same section. */ sym = fscan->fx_addsy; if (sym == NULL || fscan->fx_subsy != NULL @@ -1450,7 +2326,8 @@ sh_frob_section (abfd, sec, ignore) We have already adjusted the value of sym to include the fragment address, so we undo that adjustment here. */ subseg_change (sec, 0); - fix_new (sym->sy_frag, S_GET_VALUE (sym) - sym->sy_frag->fr_address, + fix_new (symbol_get_frag (sym), + S_GET_VALUE (sym) - symbol_get_frag (sym)->fr_address, 4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT); } } @@ -1489,7 +2366,7 @@ sh_frob_file () void md_convert_frag (headers, seg, fragP) #ifdef BFD_ASSEMBLER - bfd *headers; + bfd *headers ATTRIBUTE_UNUSED; #else object_headers *headers; #endif @@ -1520,68 +2397,30 @@ md_convert_frag (headers, seg, fragP) case C (UNCOND_JUMP, UNCOND32): case C (UNCOND_JUMP, UNDEF_WORD_DISP): if (fragP->fr_symbol == NULL) - as_bad (_("at 0x%lx, displacement overflows 12-bit field"), - (unsigned long) fragP->fr_address); + as_bad_where (fragP->fr_file, fragP->fr_line, + _("displacement overflows 12-bit field")); else if (S_IS_DEFINED (fragP->fr_symbol)) - as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 12-bit field"), - (unsigned long) fragP->fr_address, - S_GET_NAME (fragP->fr_symbol)); + as_bad_where (fragP->fr_file, fragP->fr_line, + _("displacement to defined symbol %s overflows 12-bit field"), + S_GET_NAME (fragP->fr_symbol)); else - as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 12-bit field"), - (unsigned long) fragP->fr_address, - S_GET_NAME (fragP->fr_symbol)); - -#if 0 /* This code works, but generates poor code and the compiler - should never produce a sequence that requires it to be used. */ - - /* A jump wont fit in 12 bits, make code which looks like - bra foo - mov.w @(0, PC), r14 - .long disp - foo: bra @r14 - */ - int t = buffer[0] & 0x10; - - buffer[highbyte] = 0xa0; /* branch over move and disp */ - buffer[lowbyte] = 3; - buffer[highbyte+2] = 0xd0 | JREG; /* Build mov insn */ - buffer[lowbyte+2] = 0x00; - - buffer[highbyte+4] = 0; /* space for 32 bit jump disp */ - buffer[lowbyte+4] = 0; - buffer[highbyte+6] = 0; - buffer[lowbyte+6] = 0; - - buffer[highbyte+8] = 0x40 | JREG; /* Build jmp @JREG */ - buffer[lowbyte+8] = t ? 0xb : 0x2b; - - buffer[highbyte+10] = 0x20; /* build nop */ - buffer[lowbyte+10] = 0x0b; - - /* Make reloc for the long disp */ - fix_new (fragP, - fragP->fr_fix + 4, - 4, - fragP->fr_symbol, - fragP->fr_offset, - 0, - BFD_RELOC_32); - fragP->fr_fix += UNCOND32_LENGTH; + as_bad_where (fragP->fr_file, fragP->fr_line, + _("displacement to undefined symbol %s overflows 12-bit field"), + S_GET_NAME (fragP->fr_symbol)); + /* Stabilize this frag, so we don't trip an assert. */ + fragP->fr_fix += fragP->fr_var; fragP->fr_var = 0; - donerelax = 1; -#endif - break; case C (COND_JUMP, COND12): case C (COND_JUMP_DELAY, COND12): - /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */ + /* A bcond won't fit, so turn it into a b!cond; bra disp; nop. */ /* I found that a relax failure for gcc.c-torture/execute/930628-1.c was due to gas incorrectly relaxing an out-of-range conditional branch with delay slot. It turned: bf.s L6 (slot mov.l r12,@(44,r0)) into: - + 2c: 8f 01 a0 8b bf.s 32 <_main+32> (slot bra L6) 30: 00 09 nop 32: 10 cb mov.l r12,@(44,r0) @@ -1643,58 +2482,19 @@ md_convert_frag (headers, seg, fragP) case C (COND_JUMP, UNDEF_WORD_DISP): case C (COND_JUMP_DELAY, UNDEF_WORD_DISP): if (fragP->fr_symbol == NULL) - as_bad (_("at 0x%lx, displacement overflows 8-bit field"), - (unsigned long) fragP->fr_address); + as_bad_where (fragP->fr_file, fragP->fr_line, + _("displacement overflows 8-bit field")); else if (S_IS_DEFINED (fragP->fr_symbol)) - as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 8-bit field "), - (unsigned long) fragP->fr_address, - S_GET_NAME (fragP->fr_symbol)); + as_bad_where (fragP->fr_file, fragP->fr_line, + _("displacement to defined symbol %s overflows 8-bit field"), + S_GET_NAME (fragP->fr_symbol)); else - as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 8-bit field "), - (unsigned long) fragP->fr_address, - S_GET_NAME (fragP->fr_symbol)); - -#if 0 /* This code works, but generates poor code, and the compiler - should never produce a sequence that requires it to be used. */ - - /* A bcond won't fit and it won't go into a 12 bit - displacement either, the code sequence looks like: - b!cond foop - mov.w @(n, PC), r14 - jmp @r14 - nop - .long where - foop: - */ - - buffer[0] ^= 0x2; /* Toggle T/F bit */ -#define JREG 14 - buffer[1] = 5; /* branch over mov, jump, nop and ptr */ - buffer[2] = 0xd0 | JREG; /* Build mov insn */ - buffer[3] = 0x2; - buffer[4] = 0x40 | JREG; /* Build jmp @JREG */ - buffer[5] = 0x0b; - buffer[6] = 0x20; /* build nop */ - buffer[7] = 0x0b; - buffer[8] = 0; /* space for 32 bit jump disp */ - buffer[9] = 0; - buffer[10] = 0; - buffer[11] = 0; - buffer[12] = 0; - buffer[13] = 0; - /* Make reloc for the long disp */ - fix_new (fragP, - fragP->fr_fix + 8, - 4, - fragP->fr_symbol, - fragP->fr_offset, - 0, - BFD_RELOC_32); - fragP->fr_fix += COND32_LENGTH; + as_bad_where (fragP->fr_file, fragP->fr_line, + _("displacement to undefined symbol %s overflows 8-bit field "), + S_GET_NAME (fragP->fr_symbol)); + /* Stabilize this frag, so we don't trip an assert. */ + fragP->fr_fix += fragP->fr_var; fragP->fr_var = 0; - donerelax = 1; -#endif - break; default: @@ -1710,9 +2510,9 @@ md_convert_frag (headers, seg, fragP) } valueT -DEFUN (md_section_align, (seg, size), - segT seg AND - valueT size) +md_section_align (seg, size) + segT seg ATTRIBUTE_UNUSED; + valueT size; { #ifdef BFD_ASSEMBLER #ifdef OBJ_ELF @@ -1844,7 +2644,9 @@ sh_force_relocation (fix) { if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT - || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fix->fx_r_type == BFD_RELOC_SH_LOOP_START + || fix->fx_r_type == BFD_RELOC_SH_LOOP_END) return 1; if (! sh_relax) @@ -1867,7 +2669,21 @@ sh_fix_adjustable (fixP) if (fixP->fx_addsy == NULL) return 1; - + + if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2 + || fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2 + || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2 + || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4 + || fixP->fx_r_type == BFD_RELOC_8_PCREL + || fixP->fx_r_type == BFD_RELOC_SH_SWITCH16 + || fixP->fx_r_type == BFD_RELOC_SH_SWITCH32) + return 1; + + if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP) + || fixP->fx_r_type == BFD_RELOC_32_GOTOFF + || fixP->fx_r_type == BFD_RELOC_RVA) + return 0; + /* We need the symbol name for the VTABLE entries */ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) @@ -1875,6 +2691,34 @@ sh_fix_adjustable (fixP) return 1; } + +void +sh_elf_final_processing () +{ + int val; + + /* Set file-specific flags to indicate if this code needs + a processor with the sh-dsp / sh3e ISA to execute. */ + if (valid_arch & arch_sh1) + val = EF_SH1; + else if (valid_arch & arch_sh2) + val = EF_SH2; + else if (valid_arch & arch_sh_dsp) + val = EF_SH_DSP; + else if (valid_arch & arch_sh3) + val = EF_SH3; + else if (valid_arch & arch_sh3_dsp) + val = EF_SH_DSP; + else if (valid_arch & arch_sh3e) + val = EF_SH3E; + else if (valid_arch & arch_sh4) + val = EF_SH4; + else + abort (); + + elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK; + elf_elfheader (stdoutput)->e_flags |= val; +} #endif /* Apply a fixup to the object file. */ @@ -1901,11 +2745,49 @@ md_apply_fix (fixP, val) int shift; #ifdef BFD_ASSEMBLER - /* adjust_reloc_syms won't convert a reloc against a weak symbol - into a reloc against a section, but bfd_install_relocation will - screw up if the symbol is defined, so we have to adjust val here - to avoid the screw up later. */ - if (fixP->fx_addsy != NULL + /* A difference between two symbols, the second of which is in the + current section, is transformed in a PC-relative relocation to + the other symbol. We have to adjust the relocation type here. */ + if (fixP->fx_pcrel) + { + switch (fixP->fx_r_type) + { + default: + break; + + case BFD_RELOC_32: + fixP->fx_r_type = BFD_RELOC_32_PCREL; + break; + + /* Currently, we only support 32-bit PCREL relocations. + We'd need a new reloc type to handle 16_PCREL, and + 8_PCREL is already taken for R_SH_SWITCH8, which + apparently does something completely different than what + we need. FIXME. */ + case BFD_RELOC_16: + bfd_set_error (bfd_error_bad_value); + return false; + + case BFD_RELOC_8: + bfd_set_error (bfd_error_bad_value); + return false; + } + } + + /* The function adjust_reloc_syms won't convert a reloc against a weak + symbol into a reloc against a section, but bfd_install_relocation + will screw up if the symbol is defined, so we have to adjust val here + to avoid the screw up later. + + For ordinary relocs, this does not happen for ELF, since for ELF, + bfd_install_relocation uses the "special function" field of the + howto, and does not execute the code that needs to be undone, as long + as the special function does not return bfd_reloc_continue. + It can happen for GOT- and PLT-type relocs the way they are + described in elf32-sh.c as they use bfd_elf_generic_reloc, but it + doesn't matter here since those relocs don't use VAL; see below. */ + if (OUTPUT_FLAVOR != bfd_target_elf_flavour + && fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy)) val -= S_GET_VALUE (fixP->fx_addsy); #endif @@ -1964,7 +2846,7 @@ md_apply_fix (fixP, val) Note that adding further restrictions may invalidate reasonable looking assembly code, such as ``and -0x1,r0''. */ max = 0xff; - min = - 0xff; + min = -0xff; *buf++ = val; break; @@ -2004,40 +2886,19 @@ md_apply_fix (fixP, val) case BFD_RELOC_SH_PCDISP12BY2: val /= 2; - if (val < -0x800 || val >= 0x7ff) + if (val < -0x800 || val > 0x7ff) as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far")); buf[lowbyte] = val & 0xff; buf[highbyte] |= (val >> 8) & 0xf; break; case BFD_RELOC_32: - if (! target_big_endian) - { - *buf++ = val >> 0; - *buf++ = val >> 8; - *buf++ = val >> 16; - *buf++ = val >> 24; - } - else - { - *buf++ = val >> 24; - *buf++ = val >> 16; - *buf++ = val >> 8; - *buf++ = val >> 0; - } + case BFD_RELOC_32_PCREL: + md_number_to_chars (buf, val, 4); break; case BFD_RELOC_16: - if (! target_big_endian) - { - *buf++ = val >> 0; - *buf++ = val >> 8; - } - else - { - *buf++ = val >> 8; - *buf++ = val >> 0; - } + md_number_to_chars (buf, val, 2); break; case BFD_RELOC_SH_USES: @@ -2053,10 +2914,54 @@ md_apply_fix (fixP, val) /* Nothing to do here. */ break; + case BFD_RELOC_SH_LOOP_START: + case BFD_RELOC_SH_LOOP_END: + case BFD_RELOC_VTABLE_INHERIT: case BFD_RELOC_VTABLE_ENTRY: fixP->fx_done = 0; +#ifdef BFD_ASSEMBLER + return 0; +#else return; +#endif + +#ifdef OBJ_ELF + case BFD_RELOC_32_PLT_PCREL: + /* Make the jump instruction point to the address of the operand. At + runtime we merely add the offset to the actual PLT entry. */ + *valp = 0xfffffffc; + break; + + case BFD_RELOC_SH_GOTPC: + /* This is tough to explain. We end up with this one if we have + operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]". + The goal here is to obtain the absolute address of the GOT, + and it is strongly preferable from a performance point of + view to avoid using a runtime relocation for this. There are + cases where you have something like: + + .long _GLOBAL_OFFSET_TABLE_+[.-.L66] + + and here no correction would be required. Internally in the + assembler we treat operands of this form as not being pcrel + since the '.' is explicitly mentioned, and I wonder whether + it would simplify matters to do it this way. Who knows. In + earlier versions of the PIC patches, the pcrel_adjust field + was used to store the correction, but since the expression is + not pcrel, I felt it would be confusing to do it this way. */ + *valp -= 1; + md_number_to_chars (buf, val, 4); + break; + + case BFD_RELOC_32_GOT_PCREL: + *valp = 0; /* Fully resolved at runtime. No addend. */ + md_number_to_chars (buf, 0, 4); + break; + + case BFD_RELOC_32_GOTOFF: + break; +#endif default: abort (); @@ -2091,7 +2996,7 @@ md_estimate_size_before_relax (fragP, segment_type) switch (fragP->fr_subtype) { case C (UNCOND_JUMP, UNDEF_DISP): - /* used to be a branch to somewhere which was unknown */ + /* Used to be a branch to somewhere which was unknown. */ if (!fragP->fr_symbol) { fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12); @@ -2114,20 +3019,20 @@ md_estimate_size_before_relax (fragP, segment_type) abort (); case C (COND_JUMP, UNDEF_DISP): case C (COND_JUMP_DELAY, UNDEF_DISP): - /* used to be a branch to somewhere which was unknown */ + /* Used to be a branch to somewhere which was unknown. */ if (fragP->fr_symbol && S_GET_SEGMENT (fragP->fr_symbol) == segment_type) { int what = GET_WHAT (fragP->fr_subtype); /* Got a symbol and it's defined in this segment, become byte - sized - maybe it will fix up */ + sized - maybe it will fix up. */ fragP->fr_subtype = C (what, COND8); fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length; } else if (fragP->fr_symbol) { int what = GET_WHAT (fragP->fr_subtype); - /* Its got a segment, but its not ours, so it will always be long */ + /* Its got a segment, but its not ours, so it will always be long. */ fragP->fr_subtype = C (what, UNDEF_WORD_DISP); fragP->fr_var = md_relax_table[C (what, COND32)].rlx_length; return md_relax_table[C (what, COND32)].rlx_length; @@ -2135,7 +3040,7 @@ md_estimate_size_before_relax (fragP, segment_type) else { int what = GET_WHAT (fragP->fr_subtype); - /* We know the abs value */ + /* We know the abs value. */ fragP->fr_subtype = C (what, COND8); fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length; } @@ -2145,7 +3050,7 @@ md_estimate_size_before_relax (fragP, segment_type) return fragP->fr_var; } -/* Put number into target byte order */ +/* Put number into target byte order. */ void md_number_to_chars (ptr, use, nbytes) @@ -2183,16 +3088,11 @@ int sh_do_align (n, fill, len, max) int n; const char *fill; - int len; + int len ATTRIBUTE_UNUSED; int max; { if (fill == NULL -#ifdef BFD_ASSEMBLER - && (now_seg->flags & SEC_CODE) != 0 -#else - && now_seg != data_section - && now_seg != bss_section -#endif + && subseg_text_p (now_seg) && n > 1) { static const unsigned char big_nop_pattern[] = { 0x00, 0x09 }; @@ -2370,14 +3270,15 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr) arelent * tc_gen_reloc (section, fixp) - asection *section; + asection *section ATTRIBUTE_UNUSED; fixS *fixp; { arelent *rel; bfd_reloc_code_real_type r_type; rel = (arelent *) xmalloc (sizeof (arelent)); - rel->sym_ptr_ptr = &fixp->fx_addsy->bsym; + rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); rel->address = fixp->fx_frag->fr_address + fixp->fx_where; r_type = fixp->fx_r_type; @@ -2403,8 +3304,18 @@ tc_gen_reloc (section, fixp) else if (r_type == BFD_RELOC_VTABLE_INHERIT || r_type == BFD_RELOC_VTABLE_ENTRY) rel->addend = fixp->fx_offset; + else if (r_type == BFD_RELOC_SH_LOOP_START + || r_type == BFD_RELOC_SH_LOOP_END) + rel->addend = fixp->fx_offset; + else if (r_type == BFD_RELOC_SH_LABEL && fixp->fx_pcrel) + { + rel->addend = 0; + rel->address = rel->addend = fixp->fx_offset; + } else if (fixp->fx_pcrel) rel->addend = fixp->fx_addnumber; + else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF) + rel->addend = fixp->fx_addnumber; else rel->addend = 0;