summaryrefslogtreecommitdiffstats
path: root/contrib/binutils/gas/config/tc-sh.c
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>2002-08-31 20:20:07 +0000
committerobrien <obrien@FreeBSD.org>2002-08-31 20:20:07 +0000
commit0570468a60eb57b3c504db2cc606350167341bc1 (patch)
treef3bc0ae2e746e12a1e72081504eabb88fffe2720 /contrib/binutils/gas/config/tc-sh.c
parentf3c54bd2c1850601b80aa0558b8843517125f50f (diff)
downloadFreeBSD-src-0570468a60eb57b3c504db2cc606350167341bc1.zip
FreeBSD-src-0570468a60eb57b3c504db2cc606350167341bc1.tar.gz
Import of Binutils from the FSF 2.12 branch (post-.1 release).
Diffstat (limited to 'contrib/binutils/gas/config/tc-sh.c')
-rw-r--r--contrib/binutils/gas/config/tc-sh.c2810
1 files changed, 2248 insertions, 562 deletions
diff --git a/contrib/binutils/gas/config/tc-sh.c b/contrib/binutils/gas/config/tc-sh.c
index eaa6146..ed5c9e4 100644
--- a/contrib/binutils/gas/config/tc-sh.c
+++ b/contrib/binutils/gas/config/tc-sh.c
@@ -1,5 +1,6 @@
/* tc-sh.c -- Assemble code for the Hitachi Super-H
- Copyright (C) 1993, 94, 95, 96, 1997 Free Software Foundation.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+ Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
@@ -18,10 +19,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 <sac@cygnus.com> */
#include <stdio.h>
#include "as.h"
@@ -29,7 +27,23 @@
#include "subsegs.h"
#define DEFINE_TABLE
#include "opcodes/sh-opc.h"
-#include <ctype.h>
+#include "safe-ctype.h"
+#include "struc-symbol.h"
+
+#ifdef OBJ_ELF
+#include "elf/sh.h"
+#endif
+
+#include "dwarf2dbg.h"
+
+typedef struct
+ {
+ sh_arg_type type;
+ int reg;
+ expressionS immediate;
+ }
+sh_operand_info;
+
const char comment_chars[] = "!";
const char line_separator_chars[] = ";";
const char line_comment_chars[] = "!#";
@@ -39,31 +53,75 @@ 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 *));
+static void little PARAMS ((int));
+static void big PARAMS ((int));
+static int parse_reg PARAMS ((char *, int *, int *));
+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 *));
+
+#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 *));
+
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+#endif
+
+static void
+big (ignore)
+ int ignore ATTRIBUTE_UNUSED;
+{
+ if (! target_big_endian)
+ as_bad (_("directive .big encountered when option -big required"));
-int shl = 0;
+ /* Stop further messages. */
+ target_big_endian = 1;
+}
static void
little (ignore)
- int ignore;
+ int ignore ATTRIBUTE_UNUSED;
{
- shl = 1;
+ if (target_big_endian)
+ as_bad (_("directive .little encountered when option -little required"));
+
+ /* Stop further messages. */
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 */
+ {"big", big, 0},
{"form", listing_psize, 0},
{"little", little, 0},
{"heading", listing_title, 0},
@@ -73,6 +131,26 @@ const pseudo_typeS md_pseudo_table[] =
{"uses", s_uses, 0},
{"uaword", s_uacons, 2},
{"ualong", s_uacons, 4},
+ {"uaquad", s_uacons, 8},
+ {"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}
};
@@ -84,35 +162,85 @@ 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 two types of relaxable instrction */
+/* These are the three types of relaxable instrction. */
+/* These are the types of relaxable instructions; except for END which is
+ a marker. */
#define COND_JUMP 1
-#define UNCOND_JUMP 2
+#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 COND32 3
-#define UNCOND12 1
-#define UNCOND32 2
#define UNDEF_WORD_DISP 4
-#define END 5
#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
@@ -125,6 +253,8 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
#define COND12_M -4090
#define COND12_LENGTH 6
+#define COND12_DELAY_LENGTH 4
+
/* ??? The minimum and maximum values are wrong, but this does not matter
since this relocation type is not supported yet. */
#define COND32_F (1<<30)
@@ -141,59 +271,624 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
#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 }, { 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 },
+ /* C (COND_JUMP, UNDEF_WORD_DISP) */
+ { 0, 0, COND32_LENGTH, 0, },
+ EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ 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, },
+ /* C (COND_JUMP_DELAY, UNDEF_WORD_DISP) */
+ { 0, 0, COND32_LENGTH, 0, },
+ 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,
+ /* C (UNCOND_JUMP, UNDEF_WORD_DISP) */
+ { 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
+
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
+/* Determinet whether the symbol needs any kind of PIC relocation. */
+
+inline static int
+sh_PIC_related_p (sym)
+ symbolS *sym;
+{
+ expressionS *exp;
+
+ if (! sym)
+ return 0;
+
+ 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
+ || sh_PIC_related_p (exp->X_add_symbol)
+ || sh_PIC_related_p (exp->X_op_symbol));
+}
+
+/* Determine the relocation type to be used to represent the
+ 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;
+{
+ expressionS *exp = main_exp;
+
+ /* This is here for backward-compatibility only. GCC used to generated:
+
+ f@PLT + . - (.LPCS# + 2)
+
+ but we'd rather be able to handle this as a PIC-related reference
+ 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. */
+ if (exp->X_op == O_subtract)
+ {
+ if (sh_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+
+ if (exp && sh_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp && exp->X_op == O_add
+ && sh_PIC_related_p (exp->X_add_symbol))
+ {
+ symbolS *sym = exp->X_add_symbol;
+
+ exp->X_op = O_subtract;
+ exp->X_add_symbol = main_exp->X_op_symbol;
+
+ main_exp->X_op_symbol = main_exp->X_add_symbol;
+ main_exp->X_add_symbol = sym;
+
+ main_exp->X_add_number += exp->X_add_number;
+ exp->X_add_number = 0;
+ }
+
+ exp = main_exp;
+ }
+ else if (exp->X_op == O_add && sh_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ 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
+ {
+ main_exp->X_add_symbol = exp->X_add_symbol;
+ main_exp->X_add_number += exp->X_add_number;
+ }
+ }
+ else
+ return (sh_PIC_related_p (exp->X_add_symbol)
+ || sh_PIC_related_p (exp->X_op_symbol));
+
+ return 0;
+}
+
+/* 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;
+{
+ bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+
+ if (sh_check_fixup (exp, &r_type))
+ as_bad (_("Invalid PIC expression."));
+
+ if (r_type == BFD_RELOC_UNUSED)
+ switch (size)
+ {
+ case 1:
+ r_type = BFD_RELOC_8;
+ break;
+
+ case 2:
+ r_type = BFD_RELOC_16;
+ break;
+
+ case 4:
+ r_type = BFD_RELOC_32;
+ break;
+
+#ifdef HAVE_SH64
+ case 8:
+ r_type = BFD_RELOC_64;
+ break;
+#endif
+
+ default:
+ goto error;
+ }
+ else if (size != 4)
+ {
+ error:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ r_type = BFD_RELOC_UNUSED;
+ }
+
+ fix_new_exp (frag, off, size, exp, 0, r_type);
+}
+
+/* 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;
+
+#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;
+ }
+
+ do
+ {
+ expression (&exp);
+ 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;
- if (! shl)
- target_big_endian = 1;
+ target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
+ valid_arch = target_arch;
+
+#ifdef HAVE_SH64
+ shmedia_md_begin ();
+#endif
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;
}
}
@@ -201,154 +896,290 @@ 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;
- }
+#define IDENT_CHAR(c) (ISALNUM (c) || (c) == '_')
-sh_operand_info;
+/* Try to parse a reg name. Return the number of chars consumed. */
-/* try and parse a reg name, returns 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 (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 (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 (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 (src[1] >= '0' && src[1] <= '9' && ! isalnum (src[2]))
+
+ if (l1 == 'x' && src[2] >= '0' && src[2] <= '1'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_REG_N;
+ *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[2]))
+ {
+ 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 = (src[1] - '0');
+ *reg = 9;
return 2;
}
}
- if (src[0] == 's' && src[1] == 's' && src[2] == 'r' && ! isalnum (src[3]))
+ 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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (src[4]))
+ if (l0 == 'm' && l1 == 'a' && TOLOWER (src[2]) == 'c'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
{
- if (src[3] == 'l')
+ if (TOLOWER (src[3]) == 'l')
{
*mode = A_MACL;
return 4;
}
- if (src[3] == 'h')
+ if (TOLOWER (src[3]) == 'h')
{
*mode = A_MACH;
return 4;
}
}
- if (src[0] == 'f' && src[1] == 'r')
+ if (l0 == 'm' && l1 == 'o' && TOLOWER (src[2]) == 'd'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_MOD;
+ return 3;
+ }
+ if (l0 == 'f' && l1 == 'r')
{
if (src[2] == '1')
{
- if (src[3] >= '0' && src[3] <= '5' && ! isalnum (src[4]))
+ if (src[3] >= '0' && src[3] <= '5'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
{
*mode = F_REG_N;
*reg = 10 + src[3] - '0';
return 4;
}
}
- if (src[2] >= '0' && src[2] <= '9' && ! isalnum (src[3]))
+ if (src[2] >= '0' && src[2] <= '9'
+ && ! 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 (src[4]))
+ && ! IDENT_CHAR ((unsigned char) src[4]))
{
*mode = D_REG_N;
*reg = 10 + src[3] - '0';
@@ -356,19 +1187,19 @@ parse_reg (src, mode, reg)
}
}
if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
- && ! isalnum (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 (src[4]))
+ && ! IDENT_CHAR ((unsigned char) src[4]))
{
*mode = X_REG_N;
*reg = 11 + src[3] - '0';
@@ -376,44 +1207,48 @@ parse_reg (src, mode, reg)
}
}
if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
- && ! isalnum (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 (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 (src[3]))
+ if ((src[2] == '0' || src[2] == '4' || src[2] == '8')
+ && ! 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 (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 (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 (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;
@@ -422,40 +1257,30 @@ parse_reg (src, mode, reg)
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)
+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)
- as_bad ("missing operand");
+ expression (&op->immediate);
+ if (op->immediate.X_op == O_absent)
+ as_bad (_("missing operand"));
+#ifdef OBJ_ELF
+ else if (op->immediate.X_op == O_PIC_reloc
+ || sh_PIC_related_p (op->immediate.X_add_symbol)
+ || sh_PIC_related_p (op->immediate.X_op_symbol))
+ as_bad (_("misplaced PIC operand"));
+#endif
new = input_line_pointer;
input_line_pointer = save;
return new;
}
-
/* The many forms of operand:
Rn Register direct
@@ -473,11 +1298,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;
@@ -487,12 +1310,12 @@ parse_at (src, op)
src++;
if (src[0] == '-')
{
- /* Must be predecrement */
+ /* Must be predecrement. */
src++;
len = parse_reg (src, &mode, &(op->reg));
if (mode != A_REG_N)
- as_bad ("illegal register after @-");
+ as_bad (_("illegal register after @-"));
op->type = A_DEC_N;
src += len;
@@ -500,7 +1323,7 @@ parse_at (src, op)
else if (src[0] == '(')
{
/* Could be @(disp, rn), @(disp, gbr), @(disp, pc), @(r0, gbr) or
- @(r0, rn) */
+ @(r0, rn). */
src++;
len = parse_reg (src, &mode, &(op->reg));
if (len && mode == A_REG_N)
@@ -508,32 +1331,45 @@ parse_at (src, op)
src += len;
if (op->reg != 0)
{
- as_bad ("must be @(r0,...)");
+ as_bad (_("must be @(r0,...)"));
}
if (src[0] == ',')
- src++;
- /* Now can be rn or gbr */
- len = parse_reg (src, &mode, &(op->reg));
- if (mode == A_GBR)
{
- op->type = A_R0_GBR;
+ src++;
+ /* Now can be rn or gbr. */
+ len = parse_reg (src, &mode, &(op->reg));
}
- else if (mode == A_REG_N)
+ else
{
- op->type = A_IND_R0_REG_N;
+ len = 0;
+ }
+ if (len)
+ {
+ if (mode == A_GBR)
+ {
+ op->type = A_R0_GBR;
+ }
+ else if (mode == A_REG_N)
+ {
+ op->type = A_IND_R0_REG_N;
+ }
+ else
+ {
+ as_bad (_("syntax error in @(r0,...)"));
+ }
}
else
{
- as_bad ("syntax error in @(r0,...)");
+ as_bad (_("syntax error in @(r0...)"));
}
}
else
{
- /* Must be an @(disp,.. thing) */
- src = parse_exp (src);
+ /* Must be an @(disp,.. thing). */
+ src = parse_exp (src, op);
if (src[0] == ',')
src++;
- /* Now can be rn, gbr or pc */
+ /* Now can be rn, gbr or pc. */
len = parse_reg (src, &mode, &op->reg);
if (len)
{
@@ -545,28 +1381,26 @@ 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;
- }
- op->type = A_DISP_PC;
+ 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;
}
else
{
- as_bad ("syntax error in @(disp,[Rn, gbr, pc])");
+ as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
}
}
else
{
- as_bad ("syntax error in @(disp,[Rn, gbr, pc])");
+ as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
}
}
src += len;
if (src[0] != ')')
- as_bad ("expecting )");
+ as_bad (_("expecting )"));
else
src++;
}
@@ -574,18 +1408,33 @@ parse_at (src, op)
{
src += parse_reg (src, &mode, &(op->reg));
if (mode != A_REG_N)
- {
- as_bad ("illegal register after @");
- }
+ as_bad (_("illegal register after @"));
+
if (src[0] == '+')
{
- op->type = A_INC_N;
+ char l0, l1;
+
src++;
+ l0 = TOLOWER (src[0]);
+ l1 = TOLOWER (src[1]);
+
+ if ((l0 == 'r' && l1 == '8')
+ || (l0 == 'i' && (l1 == 'x' || l1 == 's')))
+ {
+ src += 2;
+ op->type = A_PMOD_N;
+ }
+ else if ( (l0 == 'r' && l1 == '9')
+ || (l0 == 'i' && l1 == 'y'))
+ {
+ src += 2;
+ op->type = A_PMODY_N;
+ }
+ else
+ op->type = A_INC_N;
}
else
- {
- op->type = A_IND_N;
- }
+ op->type = A_IND_N;
}
return src;
}
@@ -602,7 +1451,7 @@ get_operand (ptr, op)
if (src[0] == '#')
{
src++;
- *ptr = parse_exp (src);
+ *ptr = parse_exp (src, op);
op->type = A_IMM;
return;
}
@@ -621,25 +1470,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])
@@ -649,7 +1500,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 == ',')
{
@@ -679,11 +1536,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;
@@ -691,31 +1546,36 @@ 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_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_MACH:
case A_PR:
case A_MACL:
@@ -748,16 +1608,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;
@@ -766,6 +1624,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:
@@ -774,7 +1636,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;
@@ -786,12 +1648,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:
@@ -813,64 +1807,55 @@ get_specific (opcode, operands)
goto fail;
reg_m = 4;
break;
-
+
default:
- printf ("unhandled %d\n", arg);
+ 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;
}
-int
-check (operand, low, high)
- expressionS *operand;
- int low;
- int high;
-{
- if (operand->X_op != O_constant
- || operand->X_add_number < low
- || operand->X_add_number > high)
- {
- as_bad ("operand must be absolute in range %d..%d", 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;
if (opcode->arg[0] == A_BDISP8)
{
+ int what = (opcode->nibbles[1] & 4) ? COND_JUMP_DELAY : COND_JUMP;
p = frag_var (rs_machine_dependent,
- md_relax_table[C (COND_JUMP, COND32)].rlx_length,
- md_relax_table[C (COND_JUMP, COND8)].rlx_length,
- C (COND_JUMP, 0),
- immediate.X_add_symbol,
- immediate.X_add_number,
+ md_relax_table[C (what, COND32)].rlx_length,
+ md_relax_table[C (what, COND8)].rlx_length,
+ C (what, 0),
+ op->immediate.X_add_symbol,
+ op->immediate.X_add_number,
0);
p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
}
@@ -880,25 +1865,75 @@ 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;
-
{
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;
@@ -922,95 +1957,385 @@ 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 IMM_4BY4:
- insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0);
+ case IMM0_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand);
break;
- case IMM_4BY2:
- insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0);
+ case IMM0_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand);
break;
- case IMM_4:
- insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+ case IMM1_4BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand + 1);
break;
- case IMM_8BY4:
- insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0);
+ case IMM1_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand + 1);
break;
- case IMM_8BY2:
- insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0);
+ case IMM1_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand + 1);
break;
- case IMM_8:
- insert (output + low_byte, BFD_RELOC_SH_IMM8, 0);
+ case IMM0_8BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand);
+ break;
+ case IMM0_8BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand);
+ break;
+ case IMM0_8:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand);
+ break;
+ 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,
+ operand->type != A_DISP_PC_ABS, operand);
break;
case PCRELIMM_8BY2:
- insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1);
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY2,
+ operand->type != A_DISP_PC_ABS, 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);
+ 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++)
{
- name[nlen] = op_start[nlen];
+ 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. Moreover,
+ all but the first sub-insn is a parallel processing insn won't
+ be capitalized. Instead of hacking up the machine independent
+ code, we just deal with it here. */
+ c = TOLOWER (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 (;;)
{
- as_bad ("can't find opcode ");
+ 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;
+ }
}
- opcode = (sh_opcode_info *) hash_find (opcode_hash_control, name);
+ 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
+ {
+ 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;
+
+#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 == false)
+ {
+ 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");
+ as_bad (_("unknown opcode"));
return;
}
@@ -1024,34 +2349,56 @@ 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;
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ as_bad (_("invalid operands for opcode"));
+ }
+ else
+ {
+ if (*op_end)
+ as_bad (_("excess operands: '%s'"), op_end);
- build_Mytes (opcode, operand);
+ size = build_Mytes (opcode, operand);
+ }
+ }
}
+#ifdef BFD_ASSEMBLER
+ dwarf2_emit_insn (size);
+#endif
}
/* This routine is called each time a label definition is seen. It
@@ -1071,7 +2418,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;
@@ -1095,38 +2442,41 @@ sh_flush_pending_output ()
}
symbolS *
-DEFUN (md_undefined_symbol, (name),
- char *name)
+md_undefined_symbol (name)
+ char *name ATTRIBUTE_UNUSED;
{
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 ATTRIBUTE_UNUSED;
{
- printf ("call to tc_crawl_symbol_chain \n");
+ printf (_("call to tc_crawl_symbol_chain \n"));
}
void
-DEFUN (tc_headers_hook, (headers),
- object_headers * headers)
+tc_headers_hook (headers)
+ object_headers *headers ATTRIBUTE_UNUSED;
{
- printf ("call to tc_headers_hook \n");
+ 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;
@@ -1150,7 +2500,7 @@ md_atof (type, litP, sizeP)
default:
*sizeP = 0;
- return "bad call to md_atof";
+ return _("bad call to md_atof");
}
t = atof_ieee (input_line_pointer, type, words);
@@ -1175,7 +2525,7 @@ md_atof (type, litP, sizeP)
litP += 2;
}
}
-
+
return NULL;
}
@@ -1186,18 +2536,18 @@ md_atof (type, litP, sizeP)
static void
s_uses (ignore)
- int ignore;
+ int ignore ATTRIBUTE_UNUSED;
{
expressionS ex;
if (! sh_relax)
- as_warn (".uses pseudo-op seen when not relaxing");
+ as_warn (_(".uses pseudo-op seen when not relaxing"));
expression (&ex);
if (ex.X_op != O_symbol || ex.X_add_number != 0)
{
- as_bad ("bad .uses format");
+ as_bad (_("bad .uses format"));
ignore_rest_of_line ();
return;
}
@@ -1208,23 +2558,42 @@ 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_BIG (OPTION_MD_BASE + 1)
+#define OPTION_LITTLE (OPTION_BIG + 1)
#define OPTION_SMALL (OPTION_LITTLE + 1)
+#define OPTION_DSP (OPTION_SMALL + 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},
+#ifdef HAVE_SH64
+#define OPTION_ISA (OPTION_DSP + 1)
+#define OPTION_ABI (OPTION_ISA + 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)
+ {"isa", required_argument, NULL, OPTION_ISA},
+ {"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);
+size_t md_longopts_size = sizeof (md_longopts);
int
md_parse_option (c, arg)
int c;
- char *arg;
+ char *arg ATTRIBUTE_UNUSED;
{
switch (c)
{
@@ -1232,8 +2601,11 @@ md_parse_option (c, arg)
sh_relax = 1;
break;
+ case OPTION_BIG:
+ target_big_endian = 1;
+ break;
+
case OPTION_LITTLE:
- shl = 1;
target_big_endian = 0;
break;
@@ -1241,6 +2613,66 @@ md_parse_option (c, arg)
sh_small = 1;
break;
+ case OPTION_DSP:
+ sh_dsp = 1;
+ break;
+
+#ifdef HAVE_SH64
+ case OPTION_ISA:
+ 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;
+ }
+ else
+ as_bad ("Invalid argument to --isa option: %s", arg);
+ break;
+
+ 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;
}
@@ -1252,43 +2684,31 @@ void
md_show_usage (stream)
FILE *stream;
{
- fprintf(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\
--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"));
+#ifdef HAVE_SH64
+ fprintf (stream, _("\
+-isa=[shmedia set default instruction set for SH64\n\
+ | SHmedia\n\
+ | shcompact\n\
+ | SHcompact]\n\
+-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"));
+#endif /* HAVE_SH64 */
}
-int md_short_jump_size;
-
-void
-tc_Nout_fix_to_chars ()
-{
- printf ("call to tc_Nout_fix_to_chars \n");
- abort ();
-}
-
-void
-md_create_short_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol)
- char *ptr;
- addressT from_Nddr;
- addressT to_Nddr;
- fragS *frag;
- symbolS *to_symbol;
-{
- as_fatal ("failed sanity check.");
-}
-
-void
-md_create_long_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol)
- char *ptr;
- addressT from_Nddr, to_Nddr;
- fragS *frag;
- symbolS *to_symbol;
-{
- as_fatal ("failed sanity check.");
-}
-
/* This struct is used to pass arguments to sh_count_relocs through
bfd_map_over_sections. */
@@ -1304,10 +2724,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;
{
@@ -1334,12 +2753,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;
@@ -1371,7 +2789,7 @@ sh_frob_section (abfd, sec, ignore)
|| S_IS_EXTERNAL (sym))
{
as_warn_where (fix->fx_file, fix->fx_line,
- ".uses does not refer to a local symbol in the same section");
+ _(".uses does not refer to a local symbol in the same section"));
continue;
}
@@ -1390,7 +2808,7 @@ sh_frob_section (abfd, sec, ignore)
if (fscan == NULL)
{
as_warn_where (fix->fx_file, fix->fx_line,
- "can't find fixup pointed to by .uses");
+ _("can't find fixup pointed to by .uses"));
continue;
}
@@ -1400,8 +2818,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
@@ -1413,7 +2831,7 @@ sh_frob_section (abfd, sec, ignore)
|| S_IS_EXTERNAL (sym))
{
as_warn_where (fix->fx_file, fix->fx_line,
- ".uses target does not refer to a local symbol in the same section");
+ _(".uses target does not refer to a local symbol in the same section"));
continue;
}
@@ -1439,7 +2857,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 (fscan->fx_frag,
+ S_GET_VALUE (sym) - fscan->fx_frag->fr_address,
4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT);
}
}
@@ -1457,6 +2876,10 @@ sh_frob_section (abfd, sec, ignore)
void
sh_frob_file ()
{
+#ifdef HAVE_SH64
+ shmedia_frob_file_before_adjust ();
+#endif
+
if (! sh_relax)
return;
@@ -1473,14 +2896,14 @@ sh_frob_file ()
}
/* Called after relaxing. Set the correct sizes of the fragments, and
- create relocs so that md_apply_fix will fill in the correct values. */
+ 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;
+ bfd *headers ATTRIBUTE_UNUSED;
#else
- object_headers *headers;
+ object_headers *headers ATTRIBUTE_UNUSED;
#endif
segT seg;
fragS *fragP;
@@ -1490,6 +2913,7 @@ md_convert_frag (headers, seg, fragP)
switch (fragP->fr_subtype)
{
case C (COND_JUMP, COND8):
+ case C (COND_JUMP_DELAY, COND8):
subseg_change (seg, 0);
fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
1, BFD_RELOC_SH_PCDISP8BY2);
@@ -1508,68 +2932,52 @@ 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_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 %sdefined symbol %s overflows 12-bit field",
- (unsigned long) fragP->fr_address,
- S_IS_DEFINED (fragP->fr_symbol) ? "" : "un",
- 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):
- /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */
+ case C (COND_JUMP_DELAY, COND12):
+ /* 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)
+ Therefore, branches with delay slots have to be handled
+ differently from ones without delay slots. */
{
unsigned char *buffer =
(unsigned char *) (fragP->fr_fix + fragP->fr_literal);
int highbyte = target_big_endian ? 0 : 1;
int lowbyte = target_big_endian ? 1 : 0;
+ int delay = fragP->fr_subtype == C (COND_JUMP_DELAY, COND12);
/* Toggle the true/false bit of the bcond. */
buffer[highbyte] ^= 0x2;
- /* Build a relocation to six bytes farther on. */
+ /* If this is a delayed branch, we may not put the bra in the
+ slot. So we change it to a non-delayed branch, like that:
+ b! cond slot_label; bra disp; slot_label: slot_insn
+ ??? We should try if swapping the conditional branch and
+ its delay-slot insn already makes the branch reach. */
+
+ /* Build a relocation to six / four bytes farther on. */
subseg_change (seg, 0);
fix_new (fragP, fragP->fr_fix, 2,
#ifdef BFD_ASSEMBLER
@@ -1577,7 +2985,7 @@ md_convert_frag (headers, seg, fragP)
#else
seg_info (seg)->dot,
#endif
- fragP->fr_address + fragP->fr_fix + 6,
+ fragP->fr_address + fragP->fr_fix + (delay ? 4 : 6),
1, BFD_RELOC_SH_PCDISP8BY2);
/* Set up a jump instruction. */
@@ -1586,86 +2994,64 @@ md_convert_frag (headers, seg, fragP)
fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP12BY2);
- /* Fill in a NOP instruction. */
- buffer[highbyte + 4] = 0x0;
- buffer[lowbyte + 4] = 0x9;
-
- fragP->fr_fix += 6;
+ if (delay)
+ {
+ buffer[highbyte] &= ~0x4; /* Removes delay slot from branch. */
+ fragP->fr_fix += 4;
+ }
+ else
+ {
+ /* Fill in a NOP instruction. */
+ buffer[highbyte + 4] = 0x0;
+ buffer[lowbyte + 4] = 0x9;
+
+ fragP->fr_fix += 6;
+ }
fragP->fr_var = 0;
donerelax = 1;
}
break;
case C (COND_JUMP, COND32):
+ case C (COND_JUMP_DELAY, COND32):
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);
- else
- as_bad ("at 0x%lx, displacement to %sdefined symbol %s overflows 8-bit field ",
- (unsigned long) fragP->fr_address,
- S_IS_DEFINED (fragP->fr_symbol) ? "" : "un",
- 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 overflows 8-bit field"));
+ else if (S_IS_DEFINED (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_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:
+#ifdef HAVE_SH64
+ shmedia_md_convert_frag (headers, seg, fragP, true);
+#else
abort ();
+#endif
}
if (donerelax && !sh_relax)
as_warn_where (fragP->fr_file, fragP->fr_line,
- "overflow in branch to %s; converted into longer instruction sequence",
+ _("overflow in branch to %s; converted into longer instruction sequence"),
(fragP->fr_symbol != NULL
? S_GET_NAME (fragP->fr_symbol)
: ""));
}
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
@@ -1731,11 +3117,11 @@ sh_cons_align (nbytes)
if (now_seg == absolute_section)
{
if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
- as_warn ("misaligned data");
+ as_warn (_("misaligned data"));
return;
}
- p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+ p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
(symbolS *) NULL, (offsetT) nalign, (char *) NULL);
record_alignment (now_seg, nalign);
@@ -1749,17 +3135,47 @@ void
sh_handle_align (frag)
fragS *frag;
{
+ int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
+
+ if (frag->fr_type == rs_align_code)
+ {
+ static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
+ static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
+
+ char *p = frag->fr_literal + frag->fr_fix;
+
+ if (bytes & 1)
+ {
+ *p++ = 0;
+ bytes--;
+ frag->fr_fix += 1;
+ }
+
+ if (target_big_endian)
+ {
+ memcpy (p, big_nop_pattern, sizeof big_nop_pattern);
+ frag->fr_var = sizeof big_nop_pattern;
+ }
+ else
+ {
+ memcpy (p, little_nop_pattern, sizeof little_nop_pattern);
+ frag->fr_var = sizeof little_nop_pattern;
+ }
+ }
+ else if (frag->fr_type == rs_align_test)
+ {
+ if (bytes != 0)
+ as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+ }
+
if (sh_relax
- && frag->fr_type == rs_align
+ && (frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
&& frag->fr_address + frag->fr_fix > 0
&& frag->fr_offset > 1
&& now_seg != bss_section)
fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
BFD_RELOC_SH_ALIGN);
-
- if (frag->fr_type == rs_align_code
- && frag->fr_next->fr_address - frag->fr_address - frag->fr_fix != 0)
- as_warn_where (frag->fr_file, frag->fr_line, "misaligned data");
}
/* This macro decides whether a particular reloc is an entry in a
@@ -1795,6 +3211,13 @@ int
sh_force_relocation (fix)
fixS *fix;
{
+
+ 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)
+ return 1;
+
if (! sh_relax)
return 0;
@@ -1804,32 +3227,141 @@ sh_force_relocation (fix)
|| 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);
}
-/* Apply a fixup to the object file. */
+#ifdef OBJ_ELF
+boolean
+sh_fix_adjustable (fixP)
+ 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)
+ || 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)
+ return 0;
+
+ return 1;
+}
-#ifdef BFD_ASSEMBLER
-int
-md_apply_fix (fixP, valp)
- fixS *fixP;
- valueT *valp;
-#else
void
-md_apply_fix (fixP, val)
- fixS *fixP;
- long val;
+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. */
+#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
+#endif /* HAVE_SH64 */
+ 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. */
+
+void
+md_apply_fix3 (fixP, valP, seg)
+ 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;
-#ifdef BFD_ASSEMBLER
- long val = *valp;
-#endif
+ long val = (long) *valP;
long max, min;
int shift;
+#ifdef BFD_ASSEMBLER
+ /* 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;
+
+ case BFD_RELOC_8:
+ bfd_set_error (bfd_error_bad_value);
+ return;
+ }
+ }
+
+ /* 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
+
#ifndef BFD_ASSEMBLER
if (fixP->fx_r_type == 0)
{
@@ -1884,7 +3416,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;
@@ -1904,60 +3436,39 @@ md_apply_fix (fixP, val)
variable val. */
val = (val + 2) / 4;
if (val & ~0xff)
- as_bad_where (fixP->fx_file, fixP->fx_line, "pcrel too far");
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
buf[lowbyte] = val;
break;
case BFD_RELOC_SH_PCRELIMM8BY2:
val /= 2;
if (val & ~0xff)
- as_bad_where (fixP->fx_file, fixP->fx_line, "pcrel too far");
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
buf[lowbyte] = val;
break;
case BFD_RELOC_SH_PCDISP8BY2:
val /= 2;
if (val < -0x80 || val > 0x7f)
- as_bad_where (fixP->fx_file, fixP->fx_line, "pcrel too far");
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
buf[lowbyte] = val;
break;
case BFD_RELOC_SH_PCDISP12BY2:
val /= 2;
- if (val < -0x800 || val >= 0x7ff)
- as_bad_where (fixP->fx_file, fixP->fx_line, "pcrel too far");
+ 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:
@@ -1973,14 +3484,70 @@ 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;
+ return;
+
+#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;
+ val = fixP->fx_addnumber;
+ if (fixP->fx_subsy)
+ val -= S_GET_VALUE (fixP->fx_subsy);
+ md_number_to_chars (buf, val, 4);
+ 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:
+ case BFD_RELOC_SH_GOTPLT32:
+ * valP = 0; /* Fully resolved at runtime. No addend. */
+ md_number_to_chars (buf, 0, 4);
+ break;
+
+ 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)
{
if ((val & ((1 << shift) - 1)) != 0)
- as_bad_where (fixP->fx_file, fixP->fx_line, "misaligned offset");
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("misaligned offset"));
if (val >= 0)
val >>= shift;
else
@@ -1988,15 +3555,12 @@ md_apply_fix (fixP, val)
| ((long) -1 & ~ ((long) -1 >> shift)));
}
if (max != 0 && (val < min || val > max))
- as_bad_where (fixP->fx_file, fixP->fx_line, "offset out of range");
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
-#ifdef BFD_ASSEMBLER
- return 0;
-#endif
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
}
-int md_long_jump_size;
-
/* Called just before address relaxation. Return the length
by which a fragment must grow to reach it's destination. */
@@ -2005,60 +3569,78 @@ md_estimate_size_before_relax (fragP, segment_type)
register fragS *fragP;
register 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 */
+ /* Used to be a branch to somewhere which was unknown. */
if (!fragP->fr_symbol)
{
fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
- fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
}
else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
{
fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
- fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
}
else
{
fragP->fr_subtype = C (UNCOND_JUMP, UNDEF_WORD_DISP);
- fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
- return md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
}
break;
- default:
- abort ();
case C (COND_JUMP, UNDEF_DISP):
- /* used to be a branch to somewhere which was unknown */
+ case C (COND_JUMP_DELAY, UNDEF_DISP):
+ what = GET_WHAT (fragP->fr_subtype);
+ /* Used to be a branch to somewhere which was unknown. */
if (fragP->fr_symbol
&& S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
{
/* Got a symbol and it's defined in this segment, become byte
- sized - maybe it will fix up */
- fragP->fr_subtype = C (COND_JUMP, COND8);
- fragP->fr_var = md_relax_table[C (COND_JUMP, COND8)].rlx_length;
+ sized - maybe it will fix up. */
+ fragP->fr_subtype = C (what, COND8);
}
else if (fragP->fr_symbol)
{
- /* Its got a segment, but its not ours, so it will always be long */
- fragP->fr_subtype = C (COND_JUMP, UNDEF_WORD_DISP);
- fragP->fr_var = md_relax_table[C (COND_JUMP, COND32)].rlx_length;
- return md_relax_table[C (COND_JUMP, COND32)].rlx_length;
+ /* Its got a segment, but its not ours, so it will always be long. */
+ fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
}
else
{
- /* We know the abs value */
- fragP->fr_subtype = C (COND_JUMP, COND8);
- fragP->fr_var = md_relax_table[C (COND_JUMP, COND8)].rlx_length;
+ /* We know the abs value. */
+ fragP->fr_subtype = C (what, COND8);
}
+ break;
+ case C (UNCOND_JUMP, UNCOND12):
+ case C (UNCOND_JUMP, UNCOND32):
+ case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP, COND8):
+ case C (COND_JUMP, COND12):
+ case C (COND_JUMP, COND32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP_DELAY, COND8):
+ case C (COND_JUMP_DELAY, COND12):
+ case C (COND_JUMP_DELAY, COND32):
+ case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+ /* When relaxing a section for the second time, we don't need to
+ do anything besides return the current size. */
break;
}
+
+ fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
return fragP->fr_var;
}
-/* Put number into target byte order */
+/* Put number into target byte order. */
void
md_number_to_chars (ptr, use, nbytes)
@@ -2066,12 +3648,20 @@ md_number_to_chars (ptr, use, nbytes)
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 (fixP)
fixS *fixP;
@@ -2079,6 +3669,27 @@ md_pcrel_from (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;
+{
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || S_IS_EXTERN (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ /* The symbol is undefined (or is defined but not in this section,
+ or we're not sure about it being the final definition). Let the
+ linker figure it out. We need to adjust the subtraction of a
+ symbol to the position of the relocated data, though. */
+ return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
+ }
+
+ return md_pcrel_from (fixP);
+}
+
#ifdef OBJ_COFF
int
@@ -2090,41 +3701,6 @@ tc_coff_sizemachdep (frag)
#endif /* OBJ_COFF */
-/* When we align the .text section, insert the correct NOP pattern. */
-
-int
-sh_do_align (n, fill, len, max)
- int n;
- const char *fill;
- int len;
- int max;
-{
- if (fill == NULL
-#ifdef BFD_ASSEMBLER
- && (now_seg->flags & SEC_CODE) != 0
-#else
- && now_seg != data_section
- && now_seg != bss_section
-#endif
- && n > 1)
- {
- static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
- static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
-
- /* First align to a 2 byte boundary, in case there is an odd
- .byte. */
- frag_align (1, 0, 0);
- if (target_big_endian)
- frag_align_pattern (n, big_nop_pattern, sizeof big_nop_pattern, max);
- else
- frag_align_pattern (n, little_nop_pattern, sizeof little_nop_pattern,
- max);
- return 1;
- }
-
- return 0;
-}
-
#ifndef BFD_ASSEMBLER
#ifdef OBJ_COFF
@@ -2187,7 +3763,7 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr)
break;
if (rm->bfd_reloc == BFD_RELOC_UNUSED)
as_bad_where (fix->fx_file, fix->fx_line,
- "Can not represent %s relocation in this object file format",
+ _("Can not represent %s relocation in this object file format"),
bfd_get_reloc_code_name (fix->fx_r_type));
intr->r_type = rm->sh_reloc;
intr->r_offset = 0;
@@ -2233,7 +3809,7 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr)
{
/* We can't store the offset in the object file, since this
reloc does not take up any space, so we store it in r_offset.
- The fx_addnumber field was set in md_apply_fix. */
+ The fx_addnumber field was set in md_apply_fix3. */
intr->r_offset = fix->fx_addnumber;
}
else if (fix->fx_r_type == BFD_RELOC_SH_COUNT)
@@ -2283,16 +3859,24 @@ 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;
+ 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))
@@ -2313,16 +3897,33 @@ tc_gen_reloc (section, fixp)
rel->addend = fixp->fx_offset;
else if (r_type == BFD_RELOC_SH_ALIGN)
rel->addend = fixp->fx_offset;
+ 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;
+ }
+#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 = fixp->fx_addnumber;
else
rel->addend = 0;
rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
- if (rel->howto == NULL)
+ if (rel->howto == NULL || fixp->fx_subsy)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
- "Cannot represent relocation type %s",
+ _("Cannot represent relocation type %s"),
bfd_get_reloc_code_name (r_type));
/* Set howto to a garbage value so that we can keep going. */
rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
@@ -2332,4 +3933,89 @@ tc_gen_reloc (section, fixp)
return rel;
}
+#ifdef OBJ_ELF
+inline static char *
+sh_end_of_match (cont, what)
+ char *cont, *what;
+{
+ int len = strlen (what);
+
+ if (strncasecmp (cont, what, strlen (what)) == 0
+ && ! is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+int
+sh_parse_name (name, exprP, nextcharP)
+ char const *name;
+ expressionS *exprP;
+ char *nextcharP;
+{
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ segT segment;
+
+ exprP->X_op_symbol = NULL;
+
+ if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (! GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+ no_suffix:
+ /* If we have an absolute symbol or a reg, then we know its
+ value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (segment == absolute_section)
+ {
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else if (segment == reg_section)
+ {
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else
+ {
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+ }
+
+ return 1;
+ }
+
+ 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
+ goto no_suffix;
+
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = O_PIC_reloc;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
+}
+#endif
#endif /* BFD_ASSEMBLER */
OpenPOWER on IntegriCloud