-/* tc-sh.c -- Assemble code for the Hitachi Super-H
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
- Free Software Foundation, Inc.
+/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#endif
#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
typedef struct
{
const char line_separator_chars[] = ";";
const char line_comment_chars[] = "!#";
-static void s_uses PARAMS ((int));
-
-static void sh_count_relocs PARAMS ((bfd *, segT, PTR));
-static void sh_frob_section PARAMS ((bfd *, segT, PTR));
-
-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 *));
-static void little PARAMS ((int));
-static void big PARAMS ((int));
-static int parse_reg PARAMS ((char *, int *, int *));
-static symbolS *dot PARAMS ((void));
-static char *parse_exp PARAMS ((char *, sh_operand_info *));
-static char *parse_at PARAMS ((char *, sh_operand_info *));
-static void get_operand PARAMS ((char **, sh_operand_info *));
-static char *get_operands
- PARAMS ((sh_opcode_info *, char *, sh_operand_info *));
-static sh_opcode_info *get_specific
- PARAMS ((sh_opcode_info *, sh_operand_info *));
-static void insert PARAMS ((char *, int, int, sh_operand_info *));
-static void build_relax PARAMS ((sh_opcode_info *, sh_operand_info *));
-static char *insert_loop_bounds PARAMS ((char *, sh_operand_info *));
-static unsigned int build_Mytes
- PARAMS ((sh_opcode_info *, sh_operand_info *));
+static void s_uses (int);
+static void s_uacons (int);
#ifdef OBJ_ELF
-static void sh_elf_cons PARAMS ((int));
-
-inline static int sh_PIC_related_p PARAMS ((symbolS *));
-static int sh_check_fixup PARAMS ((expressionS *, bfd_reloc_code_real_type *));
-inline static char *sh_end_of_match PARAMS ((char *, char *));
+static void sh_elf_cons (int);
symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
#endif
static void
-big (ignore)
- int ignore ATTRIBUTE_UNUSED;
+big (int ignore ATTRIBUTE_UNUSED)
{
if (! target_big_endian)
as_bad (_("directive .big encountered when option -big required"));
}
static void
-little (ignore)
- int ignore ATTRIBUTE_UNUSED;
+little (int ignore ATTRIBUTE_UNUSED)
{
if (target_big_endian)
as_bad (_("directive .little encountered when option -little required"));
{"2byte", s_uacons, 2},
{"4byte", s_uacons, 4},
{"8byte", s_uacons, 8},
-#ifdef BFD_ASSEMBLER
- {"file", dwarf2_directive_file, 0 },
- {"loc", dwarf2_directive_loc, 0 },
-#endif
+#ifdef HAVE_SH64
+ {"mode", s_sh64_mode, 0 },
+
+ /* Have the old name too. */
+ {"isa", s_sh64_mode, 0 },
+
+ /* Assert that the right ABI is used. */
+ {"abi", s_sh64_abi, 0 },
+
+ { "vtable_inherit", sh64_vtable_inherit, 0 },
+ { "vtable_entry", sh64_vtable_entry, 0 },
+#endif /* HAVE_SH64 */
{0, 0, 0}
};
int sh_small;
-/* Whether -dsp was seen. */
+/* Flag to generate relocations against symbol values for local symbols. */
+
+static int dont_adjust_reloc_32;
+
+/* preset architecture set, if given; zero otherwise. */
-static int sh_dsp;
+static unsigned int preset_target_arch;
/* The bit mask of architectures that could
- accomodate the insns seen so far. */
-static int valid_arch;
+ accommodate the insns seen so far. */
+static unsigned int valid_arch;
const char EXP_CHARS[] = "eE";
#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 instruction. */
+/* These are the types of relaxable instructions; except for END which is
+ a marker. */
#define COND_JUMP 1
#define COND_JUMP_DELAY 2
#define UNCOND_JUMP 3
+
+#ifdef HAVE_SH64
+
+/* A 16-bit (times four) pc-relative operand, at most expanded to 32 bits. */
+#define SH64PCREL16_32 4
+/* A 16-bit (times four) pc-relative operand, at most expanded to 64 bits. */
+#define SH64PCREL16_64 5
+
+/* Variants of the above for adjusting the insn to PTA or PTB according to
+ the label. */
+#define SH64PCREL16PT_32 6
+#define SH64PCREL16PT_64 7
+
+/* A MOVI expansion, expanding to at most 32 or 64 bits. */
+#define MOVI_IMM_32 8
+#define MOVI_IMM_32_PCREL 9
+#define MOVI_IMM_64 10
+#define MOVI_IMM_64_PCREL 11
+#define END 12
+
+#else /* HAVE_SH64 */
+
#define END 4
+#endif /* HAVE_SH64 */
+
#define UNDEF_DISP 0
#define COND8 1
#define COND12 2
#define UNCOND12 1
#define UNCOND32 2
+#ifdef HAVE_SH64
+#define UNDEF_SH64PCREL 0
+#define SH64PCREL16 1
+#define SH64PCREL32 2
+#define SH64PCREL48 3
+#define SH64PCREL64 4
+#define SH64PCRELPLT 5
+
+#define UNDEF_MOVI 0
+#define MOVI_16 1
+#define MOVI_32 2
+#define MOVI_48 3
+#define MOVI_64 4
+#define MOVI_PLT 5
+#define MOVI_GOTOFF 6
+#define MOVI_GOTPC 7
+#endif /* HAVE_SH64 */
+
/* Branch displacements are from the address of the branch plus
four, thus all minimum and maximum values have 4 added to them. */
#define COND8_F 258
#define UNCOND32_M -(1<<30)
#define UNCOND32_LENGTH 14
+#ifdef HAVE_SH64
+/* The trivial expansion of a SH64PCREL16 relaxation is just a "PT label,
+ TRd" as is the current insn, so no extra length. Note that the "reach"
+ is calculated from the address *after* that insn, but the offset in the
+ insn is calculated from the beginning of the insn. We also need to
+ take into account the implicit 1 coded as the "A" in PTA when counting
+ forward. If PTB reaches an odd address, we trap that as an error
+ elsewhere, so we don't have to have different relaxation entries. We
+ don't add a one to the negative range, since PTB would then have the
+ farthest backward-reaching value skipped, not generated at relaxation. */
+#define SH64PCREL16_F (32767 * 4 - 4 + 1)
+#define SH64PCREL16_M (-32768 * 4 - 4)
+#define SH64PCREL16_LENGTH 0
+
+/* The next step is to change that PT insn into
+ MOVI ((label - datalabel Ln) >> 16) & 65535, R25
+ SHORI (label - datalabel Ln) & 65535, R25
+ Ln:
+ PTREL R25,TRd
+ which means two extra insns, 8 extra bytes. This is the limit for the
+ 32-bit ABI.
+
+ The expressions look a bit bad since we have to adjust this to avoid overflow on a
+ 32-bit host. */
+#define SH64PCREL32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define SH64PCREL32_LENGTH (2 * 4)
+
+/* Similarly, we just change the MOVI and add a SHORI for the 48-bit
+ expansion. */
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+ host. */
+#define SH64PCREL32_M (((long) -1 << 30) * 2 - 4)
+#define SH64PCREL48_F ((((long) 1 << 47) - 1) - 4)
+#define SH64PCREL48_M (((long) -1 << 47) - 4)
+#define SH64PCREL48_LENGTH (3 * 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+ in reach to the 32-bit state. Note that we have a slightly incorrect
+ reach, but the correct one above will overflow a 32-bit number. */
+#define SH64PCREL32_M (((long) -1 << 30) * 2)
+#define SH64PCREL48_F SH64PCREL32_F
+#define SH64PCREL48_M SH64PCREL32_M
+#define SH64PCREL48_LENGTH (3 * 4)
+#endif /* BFD_HOST_64BIT_LONG */
+
+/* And similarly for the 64-bit expansion; a MOVI + SHORI + SHORI + SHORI
+ + PTREL sequence. */
+#define SH64PCREL64_LENGTH (4 * 4)
+
+/* For MOVI, we make the MOVI + SHORI... expansion you can see in the
+ SH64PCREL expansions. The PCREL one is similar, but the other has no
+ pc-relative reach; it must be fully expanded in
+ shmedia_md_estimate_size_before_relax. */
+#define MOVI_16_LENGTH 0
+#define MOVI_16_F (32767 - 4)
+#define MOVI_16_M (-32768 - 4)
+#define MOVI_32_LENGTH 4
+#define MOVI_32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define MOVI_48_LENGTH 8
+
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+ host. */
+#define MOVI_32_M (((long) -1 << 30) * 2 - 4)
+#define MOVI_48_F ((((long) 1 << 47) - 1) - 4)
+#define MOVI_48_M (((long) -1 << 47) - 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+ in reach to the 32-bit state. Note that we have a slightly incorrect
+ reach, but the correct one above will overflow a 32-bit number. */
+#define MOVI_32_M (((long) -1 << 30) * 2)
+#define MOVI_48_F MOVI_32_F
+#define MOVI_48_M MOVI_32_M
+#endif /* BFD_HOST_64BIT_LONG */
+
+#define MOVI_64_LENGTH 12
+#endif /* HAVE_SH64 */
+
#define EMPTY { 0, 0, 0, 0 }
const relax_typeS md_relax_table[C (END, 0)] = {
{ 0, 0, UNCOND32_LENGTH, 0, },
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#ifdef HAVE_SH64
+ /* C (SH64PCREL16_32, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_32, SH64PCREL32) },
+ /* C (SH64PCREL16_32, SH64PCREL32) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ /* C (SH64PCREL16_32, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (SH64PCREL16_64, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_64, SH64PCREL32) },
+ /* C (SH64PCREL16_64, SH64PCREL32) */
+ { SH64PCREL32_F, SH64PCREL32_M, SH64PCREL32_LENGTH, C (SH64PCREL16_64, SH64PCREL48) },
+ /* C (SH64PCREL16_64, SH64PCREL48) */
+ { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16_64, SH64PCREL64) },
+ /* C (SH64PCREL16_64, SH64PCREL64) */
+ { 0, 0, SH64PCREL64_LENGTH, 0 },
+ /* C (SH64PCREL16_64, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL64_LENGTH, 0 },
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (SH64PCREL16PT_32, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_32, SH64PCREL32) },
+ /* C (SH64PCREL16PT_32, SH64PCREL32) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ /* C (SH64PCREL16PT_32, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (SH64PCREL16PT_64, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_64, SH64PCREL32) },
+ /* C (SH64PCREL16PT_64, SH64PCREL32) */
+ { SH64PCREL32_F,
+ SH64PCREL32_M,
+ SH64PCREL32_LENGTH,
+ C (SH64PCREL16PT_64, SH64PCREL48) },
+ /* C (SH64PCREL16PT_64, SH64PCREL48) */
+ { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16PT_64, SH64PCREL64) },
+ /* C (SH64PCREL16PT_64, SH64PCREL64) */
+ { 0, 0, SH64PCREL64_LENGTH, 0 },
+ /* C (SH64PCREL16PT_64, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL64_LENGTH, 0},
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_32, UNDEF_MOVI) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ /* C (MOVI_IMM_32, MOVI_16) */
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32, MOVI_32) },
+ /* C (MOVI_IMM_32, MOVI_32) */
+ { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY,
+ /* C (MOVI_IMM_32, MOVI_GOTOFF) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_32_PCREL, MOVI_16) */
+ EMPTY,
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32_PCREL, MOVI_32) },
+ /* C (MOVI_IMM_32_PCREL, MOVI_32) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ /* C (MOVI_IMM_32_PCREL, MOVI_PLT) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY,
+ /* C (MOVI_IMM_32_PCREL, MOVI_GOTPC) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_64, UNDEF_MOVI) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ /* C (MOVI_IMM_64, MOVI_16) */
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64, MOVI_32) },
+ /* C (MOVI_IMM_64, MOVI_32) */
+ { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64, MOVI_48) },
+ /* C (MOVI_IMM_64, MOVI_48) */
+ { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64, MOVI_64) },
+ /* C (MOVI_IMM_64, MOVI_64) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY,
+ /* C (MOVI_IMM_64, MOVI_GOTOFF) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_64_PCREL, MOVI_16) */
+ EMPTY,
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_32) },
+ /* C (MOVI_IMM_64_PCREL, MOVI_32) */
+ { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_48) },
+ /* C (MOVI_IMM_64_PCREL, MOVI_48) */
+ { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_64) },
+ /* C (MOVI_IMM_64_PCREL, MOVI_64) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ /* C (MOVI_IMM_64_PCREL, MOVI_PLT) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY,
+ /* C (MOVI_IMM_64_PCREL, MOVI_GOTPC) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#endif /* HAVE_SH64 */
+
};
#undef EMPTY
/* Determinet whether the symbol needs any kind of PIC relocation. */
inline static int
-sh_PIC_related_p (sym)
- symbolS *sym;
+sh_PIC_related_p (symbolS *sym)
{
expressionS *exp;
if (sym == GOT_symbol)
return 1;
+#ifdef HAVE_SH64
+ if (sh_PIC_related_p (*symbol_get_tc (sym)))
+ return 1;
+#endif
+
exp = symbol_get_value_expression (sym);
return (exp->X_op == O_PIC_reloc
expression, that may be rearranged. */
static int
-sh_check_fixup (main_exp, r_type_p)
- expressionS *main_exp;
- bfd_reloc_code_real_type *r_type_p;
+sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
{
expressionS *exp = main_exp;
plus/minus a symbol. However, gas' parser gives us:
O_subtract (O_add (f@PLT, .), .LPCS#+2)
-
+
so we attempt to transform this into:
O_subtract (f@PLT, O_subtract (.LPCS#+2, .))
- which we can handle simply below. */
+ which we can handle simply below. */
if (exp->X_op == O_subtract)
{
if (sh_PIC_related_p (exp->X_op_symbol))
if (exp->X_op == O_symbol || exp->X_op == O_add || exp->X_op == O_subtract)
{
+#ifdef HAVE_SH64
+ if (exp->X_add_symbol
+ && (exp->X_add_symbol == GOT_symbol
+ || (GOT_symbol
+ && *symbol_get_tc (exp->X_add_symbol) == GOT_symbol)))
+ {
+ switch (*r_type_p)
+ {
+ case BFD_RELOC_SH_IMM_LOW16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_LOW16;
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_MEDLOW16;
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_MEDHI16;
+ break;
+
+ case BFD_RELOC_SH_IMM_HI16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_HI16;
+ break;
+
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_UNUSED:
+ *r_type_p = BFD_RELOC_SH_GOTPC;
+ break;
+
+ default:
+ abort ();
+ }
+ return 0;
+ }
+#else
if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
{
*r_type_p = BFD_RELOC_SH_GOTPC;
return 0;
}
+#endif
exp = symbol_get_value_expression (exp->X_add_symbol);
if (! exp)
return 0;
if (exp->X_op == O_PIC_reloc)
{
+#ifdef HAVE_SH64
+ switch (*r_type_p)
+ {
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_UNUSED:
+ *r_type_p = exp->X_md;
+ break;
+
+ case BFD_RELOC_SH_IMM_LOW16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_LOW16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_LOW16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_LOW16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_LOW16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_MEDLOW16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_MEDLOW16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_MEDLOW16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_MEDLOW16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_MEDHI16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_MEDHI16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_MEDHI16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_MEDHI16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case BFD_RELOC_SH_IMM_HI16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_HI16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_HI16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_HI16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_HI16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+#else
*r_type_p = exp->X_md;
+#endif
if (exp == main_exp)
exp->X_op = O_symbol;
else
/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG. */
void
-sh_cons_fix_new (frag, off, size, exp)
- fragS *frag;
- int off, size;
- expressionS *exp;
+sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp)
{
bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
r_type = BFD_RELOC_32;
break;
+#ifdef HAVE_SH64
+ case 8:
+ r_type = BFD_RELOC_64;
+ break;
+#endif
+
default:
goto error;
}
as_bad (_("unsupported BFD relocation size %u"), size);
r_type = BFD_RELOC_UNUSED;
}
-
+
fix_new_exp (frag, off, size, exp, 0, r_type);
}
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. */
+/* NBYTES 1=.byte, 2=.word, 4=.long */
static void
-sh_elf_cons (nbytes)
- register int nbytes; /* 1=.byte, 2=.word, 4=.long */
+sh_elf_cons (register int nbytes)
{
expressionS exp;
+#ifdef HAVE_SH64
+
+ /* Update existing range to include a previous insn, if there was one. */
+ sh64_update_contents_mark (TRUE);
+
+ /* We need to make sure the contents type is set to data. */
+ sh64_flag_output ();
+
+#endif /* HAVE_SH64 */
+
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
do
{
expression (&exp);
set up all the tables, etc that the MD part of the assembler needs. */
void
-md_begin ()
+md_begin (void)
{
- sh_opcode_info *opcode;
+ const sh_opcode_info *opcode;
char *prev_name = "";
- int target_arch;
+ unsigned int target_arch;
- target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
+ target_arch
+ = preset_target_arch ? preset_target_arch : arch_sh_up & ~arch_sh_has_dsp;
valid_arch = target_arch;
+#ifdef HAVE_SH64
+ shmedia_md_begin ();
+#endif
+
opcode_hash_control = hash_new ();
/* Insert unique names into hash table. */
for (opcode = sh_table; opcode->name; opcode++)
{
- if (strcmp (prev_name, opcode->name))
+ if (strcmp (prev_name, opcode->name) != 0)
{
- if (! (opcode->arch & target_arch))
+ if (!SH_MERGE_ARCH_SET_VALID (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. */
- opcode->name = prev_name;
- }
}
}
/* 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;
+parse_reg (char *src, int *mode, int *reg)
{
char l0 = TOLOWER (src[0]);
char l1 = l0 ? TOLOWER (src[1]) : 0;
}
}
- if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[3]))
+ if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[2]))
{
if (l1 == 's')
{
return 3;
}
+ if (l0 == 't' && l1 == 'b' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_TBR;
+ return 3;
+ }
if (l0 == 'm' && l1 == 'a' && TOLOWER (src[2]) == 'c'
&& ! IDENT_CHAR ((unsigned char) src[4]))
{
}
}
if (l0 == 'm' && l1 == 'o' && TOLOWER (src[2]) == 'd'
- && ! IDENT_CHAR ((unsigned char) src[4]))
+ && ! IDENT_CHAR ((unsigned char) src[3]))
{
*mode = A_MOD;
return 3;
return 0;
}
-static symbolS *
-dot ()
-{
- const char *fake;
-
- /* JF: '.' is pseudo symbol with value of current location
- in current segment. */
- fake = FAKE_LABEL_NAME;
- return symbol_new (fake,
- now_seg,
- (valueT) frag_now_fix (),
- frag_now);
-}
-
static char *
-parse_exp (s, op)
- char *s;
- sh_operand_info *op;
+parse_exp (char *s, sh_operand_info *op)
{
char *save;
char *new;
*/
static char *
-parse_at (src, op)
- char *src;
- sh_operand_info *op;
+parse_at (char *src, sh_operand_info *op)
{
int len;
int mode;
src++;
- if (src[0] == '-')
+ if (src[0] == '@')
+ {
+ src = parse_at (src, op);
+ if (op->type == A_DISP_TBR)
+ op->type = A_DISP2_TBR;
+ else
+ as_bad (_("illegal double indirection"));
+ }
+ else if (src[0] == '-')
{
/* Must be predecrement. */
src++;
{
op->type = A_DISP_GBR;
}
+ else if (mode == A_TBR)
+ {
+ op->type = A_DISP_TBR;
+ }
else if (mode == A_PC)
{
- /* Turn a plain @(4,pc) into @(.+4,pc). */
- if (op->immediate.X_op == O_constant)
+ /* We want @(expr, pc) to uniformly address . + expr,
+ no matter if expr is a constant, or a more complex
+ expression, e.g. sym-. or sym1-sym2.
+ However, we also used to accept @(sym,pc)
+ as addressing sym, i.e. meaning the same as plain sym.
+ Some existing code does use the @(sym,pc) syntax, so
+ we give it the old semantics for now, but warn about
+ its use, so that users have some time to fix their code.
+
+ Note that due to this backward compatibility hack,
+ we'll get unexpected results when @(offset, pc) is used,
+ and offset is a symbol that is set later to an an address
+ difference, or an external symbol that is set to an
+ address difference in another source file, so we want to
+ eventually remove it. */
+ if (op->immediate.X_op == O_symbol)
+ {
+ op->type = A_DISP_PC;
+ as_warn (_("Deprecated syntax."));
+ }
+ else
{
- op->immediate.X_add_symbol = dot();
- op->immediate.X_op = O_symbol;
+ op->type = A_DISP_PC_ABS;
+ /* Such operands don't get corrected for PC==.+4, so
+ make the correction here. */
+ op->immediate.X_add_number -= 4;
}
- op->type = A_DISP_PC;
}
else
{
|| (l0 == 'i' && (l1 == 'x' || l1 == 's')))
{
src += 2;
- op->type = A_PMOD_N;
+ op->type = AX_PMOD_N;
}
else if ( (l0 == 'r' && l1 == '9')
|| (l0 == 'i' && l1 == 'y'))
{
src += 2;
- op->type = A_PMODY_N;
+ op->type = AY_PMOD_N;
}
else
op->type = A_INC_N;
}
static void
-get_operand (ptr, op)
- char **ptr;
- sh_operand_info *op;
+get_operand (char **ptr, sh_operand_info *op)
{
char *src = *ptr;
int mode = -1;
}
static char *
-get_operands (info, args, operand)
- sh_opcode_info *info;
- char *args;
- sh_operand_info *operand;
+get_operands (sh_opcode_info *info, char *args, sh_operand_info *operand)
{
char *ptr = args;
if (info->arg[0])
provided. */
static sh_opcode_info *
-get_specific (opcode, operands)
- sh_opcode_info *opcode;
- sh_operand_info *operands;
+get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
{
sh_opcode_info *this_try = opcode;
char *name = opcode->name;
while (opcode->name)
{
this_try = opcode++;
- if (this_try->name != name)
+ if ((this_try->name != name) && (strcmp (this_try->name, name) != 0))
{
/* We've looked so far down the table that we've run out of
opcodes with the same name. */
sh_operand_info *user = operands + n;
sh_arg_type arg = this_try->arg[n];
+ if (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh2a_nofpu_up)
+ && ( arg == A_DISP_REG_M
+ || arg == A_DISP_REG_N))
+ {
+ /* Check a few key IMM* fields for overflow. */
+ int opf;
+ long val = user->immediate.X_add_number;
+
+ for (opf = 0; opf < 4; opf ++)
+ switch (this_try->nibbles[opf])
+ {
+ case IMM0_4:
+ case IMM1_4:
+ if (val < 0 || val > 15)
+ goto fail;
+ break;
+ case IMM0_4BY2:
+ case IMM1_4BY2:
+ if (val < 0 || val > 15 * 2)
+ goto fail;
+ break;
+ case IMM0_4BY4:
+ case IMM1_4BY4:
+ if (val < 0 || val > 15 * 4)
+ goto fail;
+ break;
+ default:
+ break;
+ }
+ }
switch (arg)
{
+ case A_DISP_PC:
+ if (user->type == A_DISP_PC_ABS)
+ break;
+ /* Fall through. */
case A_IMM:
case A_BDISP12:
case A_BDISP8:
case A_DISP_GBR:
- case A_DISP_PC:
+ case A_DISP2_TBR:
case A_MACH:
case A_PR:
case A_MACL:
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)
reg_n = user->reg;
break;
case A_GBR:
+ case A_TBR:
case A_SR:
case A_VBR:
case A_DSR:
goto fail;
break;
- case A_REG_B:
- if (user->type != arg)
+ case A_REG_B:
+ if (user->type != arg)
+ goto fail;
+ reg_b = user->reg;
+ break;
+
+ case A_INC_R15:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg != 15)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case A_DEC_R15:
+ if (user->type != A_DEC_N)
+ goto fail;
+ if (user->reg != 15)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case A_REG_M:
+ case A_INC_M:
+ case A_DEC_M:
+ 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 AS_DEC_N:
+ if (user->type != A_DEC_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_PMOD_N:
+ if (user->type != AY_PMOD_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_PMOD_N:
+ if (user->type != AY_PMOD_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case DSP_REG_A_M:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ if (user->reg != A_A0_NUM
+ && user->reg != A_A1_NUM)
+ goto fail;
+ reg_m = user->reg;
+ break;
+
+ case DSP_REG_AX:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_A0_NUM:
+ reg_x = 0;
+ break;
+ case A_A1_NUM:
+ reg_x = 2;
+ break;
+ case A_X0_NUM:
+ reg_x = 1;
+ break;
+ case A_X1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_XY:
+ 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 = 2;
+ break;
+ case A_Y0_NUM:
+ reg_x = 1;
+ break;
+ case A_Y1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_AY:
+ if (user->type != DSP_REG_N)
goto fail;
- reg_b = user->reg;
+ switch (user->reg)
+ {
+ case A_A0_NUM:
+ reg_y = 0;
+ break;
+ case A_A1_NUM:
+ reg_y = 1;
+ break;
+ case A_Y0_NUM:
+ reg_y = 2;
+ break;
+ case A_Y1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
break;
- case A_REG_M:
- case A_INC_M:
- case A_DEC_M:
- 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)
+ case DSP_REG_YX:
+ if (user->type != DSP_REG_N)
goto fail;
- reg_m = user->reg;
+ switch (user->reg)
+ {
+ case A_Y0_NUM:
+ reg_y = 0;
+ break;
+ case A_Y1_NUM:
+ reg_y = 1;
+ break;
+ case A_X0_NUM:
+ reg_y = 2;
+ break;
+ case A_X1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
break;
case DSP_REG_X:
goto fail;
}
}
- if ( !(valid_arch & this_try->arch))
+ if ( !SH_MERGE_ARCH_SET_VALID (valid_arch, this_try->arch))
goto fail;
- valid_arch &= this_try->arch;
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, this_try->arch);
return this_try;
fail:
;
}
static void
-insert (where, how, pcrel, op)
- char *where;
- int how;
- int pcrel;
- sh_operand_info *op;
+insert (char *where, int how, int pcrel, sh_operand_info *op)
{
fix_new_exp (frag_now,
where - frag_now->fr_literal,
}
static void
-build_relax (opcode, op)
- sh_opcode_info *opcode;
- sh_operand_info *op;
+insert4 (char * where, int how, int pcrel, sh_operand_info * op)
+{
+ fix_new_exp (frag_now,
+ where - frag_now->fr_literal,
+ 4,
+ & op->immediate,
+ pcrel,
+ how);
+}
+static void
+build_relax (sh_opcode_info *opcode, sh_operand_info *op)
{
int high_byte = target_big_endian ? 0 : 1;
char *p;
/* Insert ldrs & ldre with fancy relocations that relaxation can recognize. */
static char *
-insert_loop_bounds (output, operand)
- char *output;
- sh_operand_info *operand;
+insert_loop_bounds (char *output, sh_operand_info *operand)
{
char *name;
symbolS *end_sym;
/* 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;
+build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
{
int index;
- char nbuf[4];
- char *output = frag_more (2);
+ char nbuf[8];
+ char *output;
unsigned int size = 2;
int low_byte = target_big_endian ? 1 : 0;
+ int max_index = 4;
+
nbuf[0] = 0;
nbuf[1] = 0;
nbuf[2] = 0;
nbuf[3] = 0;
+ nbuf[4] = 0;
+ nbuf[5] = 0;
+ nbuf[6] = 0;
+ nbuf[7] = 0;
+
+ if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
+ {
+ output = frag_more (4);
+ size = 4;
+ max_index = 8;
+ }
+ else
+ output = frag_more (2);
- for (index = 0; index < 4; index++)
+ for (index = 0; index < max_index; index++)
{
sh_nibble_type i = opcode->nibbles[index];
if (i < 16)
switch (i)
{
case REG_N:
+ case REG_N_D:
nbuf[index] = reg_n;
break;
case REG_M:
case REG_B:
nbuf[index] = reg_b | 0x08;
break;
+ case REG_N_B01:
+ nbuf[index] = reg_n | 0x01;
+ break;
+ case IMM0_3s:
+ nbuf[index] |= 0x08;
+ case IMM0_3c:
+ insert (output + low_byte, BFD_RELOC_SH_IMM3, 0, operand);
+ break;
+ case IMM0_3Us:
+ nbuf[index] |= 0x80;
+ case IMM0_3Uc:
+ insert (output + low_byte, BFD_RELOC_SH_IMM3U, 0, operand);
+ break;
+ case DISP0_12:
+ insert (output + 2, BFD_RELOC_SH_DISP12, 0, operand);
+ break;
+ case DISP0_12BY2:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY2, 0, operand);
+ break;
+ case DISP0_12BY4:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY4, 0, operand);
+ break;
+ case DISP0_12BY8:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY8, 0, operand);
+ break;
+ case DISP1_12:
+ insert (output + 2, BFD_RELOC_SH_DISP12, 0, operand+1);
+ break;
+ case DISP1_12BY2:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY2, 0, operand+1);
+ break;
+ case DISP1_12BY4:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY4, 0, operand+1);
+ break;
+ case DISP1_12BY8:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY8, 0, operand+1);
+ break;
+ case IMM0_20_4:
+ break;
+ case IMM0_20:
+ insert4 (output, BFD_RELOC_SH_DISP20, 0, operand);
+ break;
+ case IMM0_20BY8:
+ insert4 (output, BFD_RELOC_SH_DISP20BY8, 0, operand);
+ break;
case IMM0_4BY4:
insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
break;
insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1);
break;
case PCRELIMM_8BY4:
- insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1, operand);
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY4,
+ operand->type != A_DISP_PC_ABS, operand);
break;
case PCRELIMM_8BY2:
- insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1, operand);
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY2,
+ operand->type != A_DISP_PC_ABS, operand);
break;
case REPEAT:
output = insert_loop_bounds (output, operand);
output[0] = (nbuf[0] << 4) | (nbuf[1]);
output[1] = (nbuf[2] << 4) | (nbuf[3]);
}
+ if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
+ {
+ if (!target_big_endian)
+ {
+ output[3] = (nbuf[4] << 4) | (nbuf[5]);
+ output[2] = (nbuf[6] << 4) | (nbuf[7]);
+ }
+ else
+ {
+ output[2] = (nbuf[4] << 4) | (nbuf[5]);
+ output[3] = (nbuf[6] << 4) | (nbuf[7]);
+ }
+ }
return size;
}
*STR_P to the first character after the last one read. */
static sh_opcode_info *
-find_cooked_opcode (str_p)
- char **str_p;
+find_cooked_opcode (char **str_p)
{
char *str = *str_p;
unsigned char *op_start;
#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;
+assemble_ppi (char *op_end, sh_opcode_info *opcode)
{
int movx = 0;
int movy = 0;
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];
+ /* 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;
+ reg_n = 0;
+
if (opcode->arg[0] != A_END)
op_end = get_operands (opcode, op_end, operand);
+ try_another_opcode:
opcode = get_specific (opcode, operand);
if (opcode == 0)
{
movy = DDT_BASE;
break;
+ case MOVX_NOPY:
+ if (movx)
+ as_bad (_("multiple movx specifications"));
+ if ((reg_n < 4 || reg_n > 5)
+ && (reg_n < 0 || reg_n > 1))
+ as_bad (_("invalid movx address register"));
+ if (movy && movy != DDT_BASE)
+ as_bad (_("insn cannot be combined with non-nopy"));
+ movx = ((((reg_n & 1) != 0) << 9)
+ + (((reg_n & 4) == 0) << 8)
+ + (reg_x << 6)
+ + (opcode->nibbles[2] << 4)
+ + opcode->nibbles[3]
+ + DDT_BASE);
+ break;
+
+ case MOVY_NOPX:
+ if (movy)
+ as_bad (_("multiple movy specifications"));
+ if ((reg_n < 6 || reg_n > 7)
+ && (reg_n < 2 || reg_n > 3))
+ as_bad (_("invalid movy address register"));
+ if (movx && movx != DDT_BASE)
+ as_bad (_("insn cannot be combined with non-nopx"));
+ movy = ((((reg_n & 1) != 0) << 8)
+ + (((reg_n & 4) == 0) << 9)
+ + (reg_y << 6)
+ + (opcode->nibbles[2] << 4)
+ + opcode->nibbles[3]
+ + DDT_BASE);
+ break;
+
case MOVX:
if (movx)
as_bad (_("multiple movx specifications"));
+ if (movy & 0x2ac)
+ as_bad (_("previous movy requires nopx"));
if (reg_n < 4 || reg_n > 5)
as_bad (_("invalid movx address register"));
if (opcode->nibbles[2] & 8)
case MOVY:
if (movy)
as_bad (_("multiple movy specifications"));
+ if (movx & 0x153)
+ as_bad (_("previous movx requires nopy"));
if (opcode->nibbles[2] & 8)
{
/* Bit 3 in nibbles[2] is intended for bit 4 of the opcode,
| (operand[0].immediate.X_add_number & 127) << 4
| reg_n);
break;
+ case PPI3NC:
+ if (cond)
+ {
+ opcode++;
+ goto try_another_opcode;
+ }
+ /* Fall through. */
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);
+ switch (opcode->nibbles[4])
+ {
+ case HEX_0:
+ case HEX_XX00:
+ case HEX_00YY:
+ break;
+ case HEX_1:
+ case HEX_4:
+ field_b += opcode->nibbles[4] << 4;
+ break;
+ default:
+ abort ();
+ }
break;
case PDC:
if (cond)
field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
+ cond + (reg_x << 6) + (reg_y << 4) + reg_n);
cond = 0;
+ switch (opcode->nibbles[4])
+ {
+ case HEX_0:
+ case HEX_XX00:
+ case HEX_00YY:
+ break;
+ case HEX_1:
+ case HEX_4:
+ field_b += opcode->nibbles[4] << 4;
+ break;
+ default:
+ abort ();
+ }
break;
case PMUL:
if (field_b)
{
- if ((field_b & 0xef00) != 0xa100)
+ if ((field_b & 0xef00) == 0xa100)
+ field_b -= 0x8100;
+ /* pclr Dz pmuls Se,Sf,Dg */
+ else if ((field_b & 0xff00) == 0x8d00
+ && (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh4al_dsp_up)))
+ {
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, arch_sh4al_dsp_up);
+ field_b -= 0x8cf0;
+ }
+ else
as_bad (_("insn cannot be combined with pmuls"));
- field_b -= 0x8100;
switch (field_b & 0xf)
{
case A_X0_NUM:
field_b += 3 - A_A1_NUM;
break;
default:
- as_bad (_("bad padd / psub pmuls output operand"));
+ as_bad (_("bad combined pmuls output operand"));
}
+ /* Generate warning if the destination register for padd / psub
+ and pmuls is the same ( only for A0 or A1 ).
+ If the last nibble is 1010 then A0 is used in both
+ padd / psub and pmuls. If it is 1111 then A1 is used
+ as destination register in both padd / psub and pmuls. */
+
+ if ((((field_b | reg_efg) & 0x000F) == 0x000A)
+ || (((field_b | reg_efg) & 0x000F) == 0x000F))
+ as_warn (_("destination register is same for parallel insns"));
}
field_b += 0x4000 + reg_efg;
break;
the frags/bytes it assembles to. */
void
-md_assemble (str)
- char *str;
+md_assemble (char *str)
{
unsigned char *op_end;
sh_operand_info operand[3];
sh_opcode_info *opcode;
unsigned int size = 0;
+ char *initial_str = str;
+
+#ifdef HAVE_SH64
+ if (sh64_isa_mode == sh64_isa_shmedia)
+ {
+ shmedia_md_assemble (str);
+ return;
+ }
+ else
+ {
+ /* If we've seen pseudo-directives, make sure any emitted data or
+ frags are marked as data. */
+ if (!seen_insn)
+ {
+ sh64_update_contents_mark (TRUE);
+ sh64_set_contents_type (CRT_SH5_ISA16);
+ }
+
+ seen_insn = TRUE;
+ }
+#endif /* HAVE_SH64 */
opcode = find_cooked_opcode (&str);
op_end = str;
if (opcode == NULL)
{
- as_bad (_("unknown opcode"));
+ /* The opcode is not in the hash table.
+ This means we definately have an assembly failure,
+ but the instruction may be valid in another CPU variant.
+ In this case emit something better than 'unknown opcode'.
+ Search the full table in sh-opc.h to check. */
+
+ char *name = initial_str;
+ int name_length = 0;
+ const sh_opcode_info *op;
+ int found = 0;
+
+ /* identify opcode in string */
+ while (ISSPACE (*name))
+ {
+ name++;
+ }
+ while (!ISSPACE (name[name_length]))
+ {
+ name_length++;
+ }
+
+ /* search for opcode in full list */
+ for (op = sh_table; op->name; op++)
+ {
+ if (strncasecmp (op->name, name, name_length) == 0
+ && op->name[name_length] == '\0')
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if ( found )
+ {
+ as_bad (_("opcode not valid for this cpu variant"));
+ }
+ else
+ {
+ as_bad (_("unknown opcode"));
+ }
return;
}
if (opcode->arg[0] == A_BDISP12
|| opcode->arg[0] == A_BDISP8)
{
+ /* Since we skip get_specific here, we have to check & update
+ valid_arch now. */
+ if (SH_MERGE_ARCH_SET_VALID (valid_arch, opcode->arch))
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, opcode->arch);
+ else
+ as_bad (_("Delayed branches not available on SH1"));
parse_exp (op_end + 1, &operand[0]);
build_relax (opcode, &operand[0]);
}
emits a BFD_RELOC_SH_LABEL reloc if necessary. */
void
-sh_frob_label ()
+sh_frob_label (void)
{
static fragS *last_label_frag;
static int last_label_offset;
data. It emits a BFD_RELOC_SH_DATA reloc if necessary. */
void
-sh_flush_pending_output ()
+sh_flush_pending_output (void)
{
if (sh_relax
&& seg_info (now_seg)->tc_segment_info_data.in_code)
}
symbolS *
-md_undefined_symbol (name)
- char *name ATTRIBUTE_UNUSED;
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
return 0;
}
#ifndef BFD_ASSEMBLER
void
-tc_crawl_symbol_chain (headers)
- object_headers *headers;
+tc_crawl_symbol_chain (object_headers *headers ATTRIBUTE_UNUSED)
{
printf (_("call to tc_crawl_symbol_chain \n"));
}
void
-tc_headers_hook (headers)
- object_headers *headers;
+tc_headers_hook (object_headers *headers ATTRIBUTE_UNUSED)
{
printf (_("call to tc_headers_hook \n"));
}
returned, or NULL on OK. */
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];
special reloc for the linker. */
static void
-s_uses (ignore)
- int ignore ATTRIBUTE_UNUSED;
+s_uses (int ignore ATTRIBUTE_UNUSED)
{
expressionS ex;
demand_empty_rest_of_line ();
}
\f
-CONST char *md_shortopts = "";
+const char *md_shortopts = "";
struct option md_longopts[] =
{
#define OPTION_RELAX (OPTION_MD_BASE)
#define OPTION_LITTLE (OPTION_BIG + 1)
#define OPTION_SMALL (OPTION_LITTLE + 1)
#define OPTION_DSP (OPTION_SMALL + 1)
+#define OPTION_ISA (OPTION_DSP + 1)
+#define OPTION_RENESAS (OPTION_ISA + 1)
{"relax", no_argument, NULL, OPTION_RELAX},
{"big", no_argument, NULL, OPTION_BIG},
{"little", no_argument, NULL, OPTION_LITTLE},
{"small", no_argument, NULL, OPTION_SMALL},
{"dsp", no_argument, NULL, OPTION_DSP},
+ {"isa", required_argument, NULL, OPTION_ISA},
+ {"renesas", no_argument, NULL, OPTION_RENESAS},
+
+#ifdef HAVE_SH64
+#define OPTION_ABI (OPTION_RENESAS + 1)
+#define OPTION_NO_MIX (OPTION_ABI + 1)
+#define OPTION_SHCOMPACT_CONST_CRANGE (OPTION_NO_MIX + 1)
+#define OPTION_NO_EXPAND (OPTION_SHCOMPACT_CONST_CRANGE + 1)
+#define OPTION_PT32 (OPTION_NO_EXPAND + 1)
+ {"abi", required_argument, NULL, OPTION_ABI},
+ {"no-mix", no_argument, NULL, OPTION_NO_MIX},
+ {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
+ {"no-expand", no_argument, NULL, OPTION_NO_EXPAND},
+ {"expand-pt32", no_argument, NULL, OPTION_PT32},
+#endif /* HAVE_SH64 */
+
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
int
-md_parse_option (c, arg)
- int c;
- char *arg ATTRIBUTE_UNUSED;
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
{
switch (c)
{
break;
case OPTION_DSP:
- sh_dsp = 1;
+ preset_target_arch = arch_sh_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+ break;
+
+ case OPTION_RENESAS:
+ dont_adjust_reloc_32 = 1;
+ break;
+
+ case OPTION_ISA:
+ if (strcasecmp (arg, "dsp") == 0)
+ preset_target_arch = arch_sh_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+ else if (strcasecmp (arg, "fp") == 0)
+ preset_target_arch = arch_sh_up & ~arch_sh_has_dsp;
+ else if (strcasecmp (arg, "any") == 0)
+ preset_target_arch = arch_sh_up;
+#ifdef HAVE_SH64
+ else if (strcasecmp (arg, "shmedia") == 0)
+ {
+ if (sh64_isa_mode == sh64_isa_shcompact)
+ as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
+ sh64_isa_mode = sh64_isa_shmedia;
+ }
+ else if (strcasecmp (arg, "shcompact") == 0)
+ {
+ if (sh64_isa_mode == sh64_isa_shmedia)
+ as_bad (_("Invalid combination: --isa=SHmedia with --isa=SHcompact"));
+ if (sh64_abi == sh64_abi_64)
+ as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
+ sh64_isa_mode = sh64_isa_shcompact;
+ }
+#endif /* HAVE_SH64 */
+ else
+ {
+ extern const bfd_arch_info_type bfd_sh_arch;
+ bfd_arch_info_type const *bfd_arch = &bfd_sh_arch;
+ preset_target_arch = 0;
+ for (; bfd_arch; bfd_arch=bfd_arch->next)
+ {
+ int len = strlen(bfd_arch->printable_name);
+
+ if (bfd_arch->mach == bfd_mach_sh5)
+ continue;
+
+ if (strncasecmp (bfd_arch->printable_name, arg, len) != 0)
+ continue;
+
+ if (arg[len] == '\0')
+ preset_target_arch =
+ sh_get_arch_from_bfd_mach (bfd_arch->mach);
+ else if (strcasecmp(&arg[len], "-up") == 0)
+ preset_target_arch =
+ sh_get_arch_up_from_bfd_mach (bfd_arch->mach);
+ else
+ continue;
+ break;
+ }
+
+ if (!preset_target_arch)
+ as_bad ("Invalid argument to --isa option: %s", arg);
+ }
+ break;
+
+#ifdef HAVE_SH64
+ case OPTION_ABI:
+ if (strcmp (arg, "32") == 0)
+ {
+ if (sh64_abi == sh64_abi_64)
+ as_bad (_("Invalid combination: --abi=32 with --abi=64"));
+ sh64_abi = sh64_abi_32;
+ }
+ else if (strcmp (arg, "64") == 0)
+ {
+ if (sh64_abi == sh64_abi_32)
+ as_bad (_("Invalid combination: --abi=64 with --abi=32"));
+ if (sh64_isa_mode == sh64_isa_shcompact)
+ as_bad (_("Invalid combination: --isa=SHcompact with --abi=64"));
+ sh64_abi = sh64_abi_64;
+ }
+ else
+ as_bad ("Invalid argument to --abi option: %s", arg);
+ break;
+
+ case OPTION_NO_MIX:
+ sh64_mix = FALSE;
+ break;
+
+ case OPTION_SHCOMPACT_CONST_CRANGE:
+ sh64_shcompact_const_crange = TRUE;
break;
+ case OPTION_NO_EXPAND:
+ sh64_expand = FALSE;
+ break;
+
+ case OPTION_PT32:
+ sh64_pt32 = TRUE;
+ break;
+#endif /* HAVE_SH64 */
+
default:
return 0;
}
}
void
-md_show_usage (stream)
- FILE *stream;
+md_show_usage (FILE *stream)
{
fprintf (stream, _("\
SH options:\n\
-little generate little endian code\n\
-big generate big endian code\n\
-relax alter jump instructions for long displacements\n\
+-renesas disable optimization with section symbol for\n\
+ compatibility with Renesas assembler.\n\
-small align sections to 4 byte boundaries, not 16\n\
--dsp enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
+-dsp enable sh-dsp insns, and disable floating-point ISAs.\n\
+-isa=[any use most appropriate isa\n\
+ | dsp same as '-dsp'\n\
+ | fp"));
+ {
+ extern const bfd_arch_info_type bfd_sh_arch;
+ bfd_arch_info_type const *bfd_arch = &bfd_sh_arch;
+ for (; bfd_arch; bfd_arch=bfd_arch->next)
+ if (bfd_arch->mach != bfd_mach_sh5)
+ {
+ fprintf (stream, "\n | %s", bfd_arch->printable_name);
+ fprintf (stream, "\n | %s-up", bfd_arch->printable_name);
+ }
+ }
+ fprintf (stream, "]\n");
+#ifdef HAVE_SH64
+ fprintf (stream, _("\
+-isa=[shmedia set as the default instruction set for SH64\n\
+ | SHmedia\n\
+ | shcompact\n\
+ | SHcompact]\n"));
+ fprintf (stream, _("\
+-abi=[32|64] set size of expanded SHmedia operands and object\n\
+ file type\n\
+-shcompact-const-crange emit code-range descriptors for constants in\n\
+ SHcompact code sections\n\
+-no-mix disallow SHmedia code in the same section as\n\
+ constants and SHcompact code\n\
+-no-expand do not expand MOVI, PT, PTA or PTB instructions\n\
+-expand-pt32 with -abi=64, expand PT, PTA and PTB instructions\n\
+ to 32 bits only\n"));
+#endif /* HAVE_SH64 */
}
\f
/* This struct is used to pass arguments to sh_count_relocs through
bfd_map_over_sections. */
static void
-sh_count_relocs (abfd, sec, data)
- bfd *abfd ATTRIBUTE_UNUSED;
- segT sec;
- PTR data;
+sh_count_relocs (bfd *abfd ATTRIBUTE_UNUSED, segT sec, void *data)
{
struct sh_count_relocs *info = (struct sh_count_relocs *) data;
segment_info_type *seginfo;
BFD_ASSEMBLER, this is called via bfd_map_over_sections. */
static void
-sh_frob_section (abfd, sec, ignore)
- bfd *abfd ATTRIBUTE_UNUSED;
- segT sec;
- PTR ignore ATTRIBUTE_UNUSED;
+sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
+ void *ignore ATTRIBUTE_UNUSED)
{
segment_info_type *seginfo;
fixS *fix;
info.sym = sym;
info.count = 0;
#ifdef BFD_ASSEMBLER
- bfd_map_over_sections (stdoutput, sh_count_relocs, (PTR) &info);
+ bfd_map_over_sections (stdoutput, sh_count_relocs, &info);
#else
{
int iscan;
for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++)
- sh_count_relocs ((bfd *) NULL, iscan, (PTR) &info);
+ sh_count_relocs ((bfd *) NULL, iscan, &info);
}
#endif
the stored function address entirely. */
void
-sh_frob_file ()
+sh_frob_file (void)
{
+#ifdef HAVE_SH64
+ shmedia_frob_file_before_adjust ();
+#endif
+
if (! sh_relax)
return;
#ifdef BFD_ASSEMBLER
- bfd_map_over_sections (stdoutput, sh_frob_section, (PTR) NULL);
+ bfd_map_over_sections (stdoutput, sh_frob_section, NULL);
#else
{
int iseg;
for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++)
- sh_frob_section ((bfd *) NULL, iseg, (PTR) NULL);
+ sh_frob_section ((bfd *) NULL, iseg, NULL);
}
#endif
}
create relocs so that md_apply_fix3 will fill in the correct values. */
void
-md_convert_frag (headers, seg, fragP)
#ifdef BFD_ASSEMBLER
- bfd *headers ATTRIBUTE_UNUSED;
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT seg, fragS *fragP)
#else
- object_headers *headers;
+md_convert_frag (object_headers *headers ATTRIBUTE_UNUSED, segT seg,
+ fragS *fragP)
#endif
- segT seg;
- fragS *fragP;
{
int donerelax = 0;
break;
default:
+#ifdef HAVE_SH64
+ shmedia_md_convert_frag (headers, seg, fragP, TRUE);
+#else
abort ();
+#endif
}
if (donerelax && !sh_relax)
}
valueT
-md_section_align (seg, size)
- segT seg ATTRIBUTE_UNUSED;
- valueT size;
+md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
{
#ifdef BFD_ASSEMBLER
#ifdef OBJ_ELF
}
/* This static variable is set by s_uacons to tell sh_cons_align that
- the expession does not need to be aligned. */
+ the expression does not need to be aligned. */
static int sh_no_align_cons = 0;
to be aligned. */
static void
-s_uacons (bytes)
- int bytes;
+s_uacons (int bytes)
{
/* Tell sh_cons_align not to align this value. */
sh_no_align_cons = 1;
enable this warning? */
void
-sh_cons_align (nbytes)
- int nbytes;
+sh_cons_align (int nbytes)
{
int nalign;
char *p;
also where we check for misaligned data. */
void
-sh_handle_align (frag)
- fragS *frag;
+sh_handle_align (fragS *frag)
{
int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
BFD_RELOC_SH_ALIGN);
}
-/* This macro decides whether a particular reloc is an entry in a
- switch table. It is used when relaxing, because the linker needs
- to know about all such entries so that it can adjust them if
- necessary. */
-
-#ifdef BFD_ASSEMBLER
-#define SWITCH_TABLE_CONS(fix) (0)
-#else
-#define SWITCH_TABLE_CONS(fix) \
- ((fix)->fx_r_type == 0 \
- && ((fix)->fx_size == 2 \
- || (fix)->fx_size == 1 \
- || (fix)->fx_size == 4))
-#endif
+/* See whether the relocation should be resolved locally. */
-#define SWITCH_TABLE(fix) \
- ((fix)->fx_addsy != NULL \
- && (fix)->fx_subsy != NULL \
- && S_GET_SEGMENT ((fix)->fx_addsy) == text_section \
- && S_GET_SEGMENT ((fix)->fx_subsy) == text_section \
- && ((fix)->fx_r_type == BFD_RELOC_32 \
- || (fix)->fx_r_type == BFD_RELOC_16 \
- || (fix)->fx_r_type == BFD_RELOC_8 \
- || SWITCH_TABLE_CONS (fix)))
+static bfd_boolean
+sh_local_pcrel (fixS *fix)
+{
+ return (! sh_relax
+ && (fix->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+ || fix->fx_r_type == BFD_RELOC_8_PCREL
+ || fix->fx_r_type == BFD_RELOC_SH_SWITCH16
+ || fix->fx_r_type == BFD_RELOC_SH_SWITCH32));
+}
/* See whether we need to force a relocation into the output file.
This is used to force out switch and PC relative relocations when
relaxing. */
int
-sh_force_relocation (fix)
- fixS *fix;
+sh_force_relocation (fixS *fix)
{
+ /* These relocations can't make it into a DSO, so no use forcing
+ them for global symbols. */
+ if (sh_local_pcrel (fix))
+ return 0;
- if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || 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)
+ /* Make sure some relocations get emitted. */
+ if (fix->fx_r_type == BFD_RELOC_SH_LOOP_START
+ || fix->fx_r_type == BFD_RELOC_SH_LOOP_END
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_GD_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LD_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_IE_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LDO_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LE_32
+ || generic_force_reloc (fix))
return 1;
if (! sh_relax)
|| fix->fx_r_type == BFD_RELOC_SH_ALIGN
|| fix->fx_r_type == BFD_RELOC_SH_CODE
|| fix->fx_r_type == BFD_RELOC_SH_DATA
+#ifdef HAVE_SH64
+ || fix->fx_r_type == BFD_RELOC_SH_SHMEDIA_CODE
+#endif
|| fix->fx_r_type == BFD_RELOC_SH_LABEL);
}
#ifdef OBJ_ELF
-boolean
-sh_fix_adjustable (fixP)
- fixS *fixP;
+bfd_boolean
+sh_fix_adjustable (fixS *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)
+ if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+ || ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
|| fixP->fx_r_type == BFD_RELOC_RVA)
return 0;
}
void
-sh_elf_final_processing ()
+sh_elf_final_processing (void)
{
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;
+ a processor with the sh-dsp / sh2e ISA to execute. */
+#ifdef HAVE_SH64
+ /* SH5 and above don't know about the valid_arch arch_sh* bits defined
+ in sh-opc.h, so check SH64 mode before checking valid_arch. */
+ if (sh64_isa_mode != sh64_isa_unspecified)
+ val = EF_SH5;
else
- abort ();
+#elif defined TARGET_SYMBIAN
+ if (1)
+ val = sh_symbian_find_elf_flags (valid_arch);
+ else
+#endif /* HAVE_SH64 */
+ val = sh_find_elf_flags (valid_arch);
elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK;
elf_elfheader (stdoutput)->e_flags |= val;
/* Apply a fixup to the object file. */
void
-md_apply_fix3 (fixP, valP, seg)
- fixS * fixP;
- valueT * valP;
- segT seg ATTRIBUTE_UNUSED;
+md_apply_fix3 (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
int lowbyte = target_big_endian ? 1 : 0;
int highbyte = target_big_endian ? 0 : 1;
- long val = * (long *) valP;
+ long val = (long) *valP;
long max, min;
int shift;
val -= S_GET_VALUE (fixP->fx_addsy);
#endif
-#ifndef BFD_ASSEMBLER
+#ifdef BFD_ASSEMBLER
+ if (SWITCH_TABLE (fixP))
+ val -= S_GET_VALUE (fixP->fx_subsy);
+#else
if (fixP->fx_r_type == 0)
{
if (fixP->fx_size == 2)
shift = 0;
switch (fixP->fx_r_type)
{
+ case BFD_RELOC_SH_IMM3:
+ max = 0x7;
+ * buf = (* buf & 0xf8) | (val & 0x7);
+ break;
+ case BFD_RELOC_SH_IMM3U:
+ max = 0x7;
+ * buf = (* buf & 0x8f) | ((val & 0x7) << 4);
+ break;
+ case BFD_RELOC_SH_DISP12:
+ max = 0xfff;
+ buf[lowbyte] = val & 0xff;
+ buf[highbyte] |= (val >> 8) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP12BY2:
+ max = 0xfff;
+ shift = 1;
+ buf[lowbyte] = (val >> 1) & 0xff;
+ buf[highbyte] |= (val >> 9) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP12BY4:
+ max = 0xfff;
+ shift = 2;
+ buf[lowbyte] = (val >> 2) & 0xff;
+ buf[highbyte] |= (val >> 10) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP12BY8:
+ max = 0xfff;
+ shift = 3;
+ buf[lowbyte] = (val >> 3) & 0xff;
+ buf[highbyte] |= (val >> 11) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP20:
+ if (! target_big_endian)
+ abort();
+ max = 0x7ffff;
+ min = -0x80000;
+ buf[1] = (buf[1] & 0x0f) | ((val >> 12) & 0xf0);
+ buf[2] = (val >> 8) & 0xff;
+ buf[3] = val & 0xff;
+ break;
+ case BFD_RELOC_SH_DISP20BY8:
+ if (!target_big_endian)
+ abort();
+ max = 0x7ffff;
+ min = -0x80000;
+ shift = 8;
+ buf[1] = (buf[1] & 0x0f) | ((val >> 20) & 0xf0);
+ buf[2] = (val >> 16) & 0xff;
+ buf[3] = (val >> 8) & 0xff;
+ break;
+
case BFD_RELOC_SH_IMM4:
max = 0xf;
*buf = (*buf & 0xf0) | (val & 0xf);
case BFD_RELOC_SH_PCRELIMM8BY4:
/* The lower two bits of the PC are cleared before the
displacement is added in. We can assume that the destination
- is on a 4 byte bounday. If this instruction is also on a 4
+ is on a 4 byte boundary. If this instruction is also on a 4
byte boundary, then we want
(target - here) / 4
and target - here is a multiple of 4.
/* 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;
- val = fixP->fx_addnumber - S_GET_VALUE (fixP->fx_subsy);
+ val = fixP->fx_offset;
+ if (fixP->fx_subsy)
+ val -= S_GET_VALUE (fixP->fx_subsy);
+ fixP->fx_addnumber = val;
md_number_to_chars (buf, val, 4);
break;
md_number_to_chars (buf, val, 4);
break;
+ case BFD_RELOC_SH_TLS_GD_32:
+ case BFD_RELOC_SH_TLS_LD_32:
+ case BFD_RELOC_SH_TLS_IE_32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fallthrough */
case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_SH_GOTPLT32:
* valP = 0; /* Fully resolved at runtime. No addend. */
md_number_to_chars (buf, 0, 4);
break;
+ case BFD_RELOC_SH_TLS_LDO_32:
+ case BFD_RELOC_SH_TLS_LE_32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fallthrough */
case BFD_RELOC_32_GOTOFF:
md_number_to_chars (buf, val, 4);
break;
#endif
default:
+#ifdef HAVE_SH64
+ shmedia_md_apply_fix3 (fixP, valP);
+ return;
+#else
abort ();
+#endif
}
if (shift != 0)
by which a fragment must grow to reach it's destination. */
int
-md_estimate_size_before_relax (fragP, segment_type)
- register fragS *fragP;
- register segT segment_type;
+md_estimate_size_before_relax (fragS *fragP, segT segment_type)
{
int what;
switch (fragP->fr_subtype)
{
default:
+#ifdef HAVE_SH64
+ return shmedia_md_estimate_size_before_relax (fragP, segment_type);
+#else
abort ();
+#endif
+
case C (UNCOND_JUMP, UNDEF_DISP):
/* Used to be a branch to somewhere which was unknown. */
/* Put number into target byte order. */
void
-md_number_to_chars (ptr, use, nbytes)
- char *ptr;
- valueT use;
- int nbytes;
+md_number_to_chars (char *ptr, valueT use, int nbytes)
{
+#ifdef HAVE_SH64
+ /* We might need to set the contents type to data. */
+ sh64_flag_output ();
+#endif
+
if (! target_big_endian)
number_to_chars_littleendian (ptr, use, nbytes);
else
number_to_chars_bigendian (ptr, use, nbytes);
}
+/* This version is used in obj-coff.c when not using BFD_ASSEMBLER.
+ eg for the sh-hms target. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+}
+
long
-md_pcrel_from_section (fixP, sec)
- fixS *fixP;
- segT sec;
+md_pcrel_from_section (fixS *fixP, segT sec)
{
- if (fixP->fx_addsy != (symbolS *) NULL
- && (! S_IS_DEFINED (fixP->fx_addsy)
- || S_IS_EXTERN (fixP->fx_addsy)
- || S_IS_WEAK (fixP->fx_addsy)
+ if (! sh_local_pcrel (fixP)
+ && fixP->fx_addsy != (symbolS *) NULL
+ && (generic_force_reloc (fixP)
|| S_GET_SEGMENT (fixP->fx_addsy) != sec))
{
/* The symbol is undefined (or is defined but not in this section,
return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
}
- return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+ return md_pcrel_from (fixP);
}
#ifdef OBJ_COFF
int
-tc_coff_sizemachdep (frag)
- fragS *frag;
+tc_coff_sizemachdep (fragS *frag)
{
return md_relax_table[frag->fr_subtype].rlx_length;
}
but does some minor tweaking. */
void
-sh_coff_reloc_mangle (seg, fix, intr, paddr)
- segment_info_type *seg;
- fixS *fix;
- struct internal_reloc *intr;
- unsigned int paddr;
+sh_coff_reloc_mangle (segment_info_type *seg, fixS *fix,
+ struct internal_reloc *intr, unsigned int paddr)
{
symbolS *symbol_ptr = fix->fx_addsy;
symbolS *dot;
/* Create a reloc. */
arelent *
-tc_gen_reloc (section, fixp)
- asection *section ATTRIBUTE_UNUSED;
- fixS *fixp;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
arelent *rel;
bfd_reloc_code_real_type r_type;
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
- if (fixp->fx_subsy
- && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
- {
- fixp->fx_addnumber -= S_GET_VALUE (fixp->fx_subsy);
- fixp->fx_subsy = 0;
- }
-
r_type = fixp->fx_r_type;
if (SWITCH_TABLE (fixp))
{
- rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ rel->addend = 0;
if (r_type == BFD_RELOC_16)
r_type = BFD_RELOC_SH_SWITCH16;
else if (r_type == BFD_RELOC_8)
rel->addend = 0;
rel->address = rel->addend = fixp->fx_offset;
}
+#ifdef HAVE_SH64
+ else if (shmedia_init_reloc (rel, fixp))
+ ;
+#endif
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 = 0;
rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
- if (rel->howto == NULL || fixp->fx_subsy)
+
+ if (rel->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("Cannot represent relocation type %s"),
rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
assert (rel->howto != NULL);
}
+#ifdef OBJ_ELF
+ else if (rel->howto->type == R_SH_IND12W)
+ rel->addend += fixp->fx_offset - 4;
+#endif
return rel;
}
#ifdef OBJ_ELF
inline static char *
-sh_end_of_match (cont, what)
- char *cont, *what;
+sh_end_of_match (char *cont, char *what)
{
int len = strlen (what);
return cont + len;
return NULL;
-}
+}
int
-sh_parse_name (name, exprP, nextcharP)
- char const *name;
- expressionS *exprP;
- char *nextcharP;
+sh_parse_name (char const *name, expressionS *exprP, char *nextcharP)
{
char *next = input_line_pointer;
char *next_end;
}
exprP->X_add_symbol = symbol_find_or_make (name);
-
+
if (*nextcharP != '@')
goto no_suffix;
else if ((next_end = sh_end_of_match (next + 1, "GOTOFF")))
reloc_type = BFD_RELOC_32_GOTOFF;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTPLT")))
+ reloc_type = BFD_RELOC_SH_GOTPLT32;
else if ((next_end = sh_end_of_match (next + 1, "GOT")))
reloc_type = BFD_RELOC_32_GOT_PCREL;
else if ((next_end = sh_end_of_match (next + 1, "PLT")))
reloc_type = BFD_RELOC_32_PLT_PCREL;
+ else if ((next_end = sh_end_of_match (next + 1, "TLSGD")))
+ reloc_type = BFD_RELOC_SH_TLS_GD_32;
+ else if ((next_end = sh_end_of_match (next + 1, "TLSLDM")))
+ reloc_type = BFD_RELOC_SH_TLS_LD_32;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTTPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_IE_32;
+ else if ((next_end = sh_end_of_match (next + 1, "TPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_LE_32;
+ else if ((next_end = sh_end_of_match (next + 1, "DTPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_LDO_32;
else
goto no_suffix;
return 1;
}
-#endif
+
+void
+sh_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (15, 0);
+}
+
+int
+sh_regname_to_dw2regnum (const char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int i;
+ const char *p;
+ char *q;
+ static struct { char *name; int dw2regnum; } regnames[] =
+ {
+ { "pr", 17 }, { "t", 18 }, { "gbr", 19 }, { "mach", 20 },
+ { "macl", 21 }, { "fpul", 23 }
+ };
+
+ for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+ if (strcmp (regnames[i].name, regname) == 0)
+ return regnames[i].dw2regnum;
+
+ if (regname[0] == 'r')
+ {
+ p = regname + 1;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 16)
+ return -1;
+ }
+ else if (regname[0] == 'f' && regname[1] == 'r')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 16)
+ return -1;
+ regnum += 25;
+ }
+ else if (regname[0] == 'x' && regname[1] == 'd')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 8)
+ return -1;
+ regnum += 87;
+ }
+ return regnum;
+}
+#endif /* OBJ_ELF */
#endif /* BFD_ASSEMBLER */