-/* tc-sh.c -- Assemble code for the Hitachi Super-H
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
static char *insert_loop_bounds PARAMS ((char *, sh_operand_info *));
static unsigned int build_Mytes
PARAMS ((sh_opcode_info *, sh_operand_info *));
+static bfd_boolean sh_local_pcrel PARAMS ((fixS *fix));
#ifdef OBJ_ELF
static void sh_elf_cons PARAMS ((int));
{"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 },
int sh_small;
-/* Whether -dsp was seen. */
+/* preset architecture set, if given; zero otherwise. */
-static int sh_dsp;
+static int preset_target_arch;
/* The bit mask of architectures that could
accomodate the insns seen so far. */
#ifdef HAVE_SH64
/* Update existing range to include a previous insn, if there was one. */
- sh64_update_contents_mark (true);
+ sh64_update_contents_mark (TRUE);
/* We need to make sure the contents type is set to data. */
sh64_flag_output ();
void
md_begin ()
{
- sh_opcode_info *opcode;
+ const sh_opcode_info *opcode;
char *prev_name = "";
int target_arch;
- target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
+ target_arch
+ = preset_target_arch ? preset_target_arch : arch_sh1_up & ~arch_sh_dsp_up;
valid_arch = target_arch;
#ifdef HAVE_SH64
/* Insert unique names into hash table. */
for (opcode = sh_table; opcode->name; opcode++)
{
- if (strcmp (prev_name, opcode->name))
+ if (strcmp (prev_name, opcode->name) != 0)
{
if (! (opcode->arch & target_arch))
continue;
prev_name = opcode->name;
hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
}
- else
- {
- /* Make all the opcodes with the same name point to the same
- string. */
- opcode->name = prev_name;
- }
}
}
}
else if (mode == A_PC)
{
+ /* We want @(expr, pc) to uniformly address . + expr,
+ no matter if expr is a constant, or a more complex
+ expression, e.g. sym-. or sym1-sym2.
+ However, we also used to accept @(sym,pc)
+ as adressing sym, i.e. meaning the same as plain sym.
+ Some existing code does use the @(sym,pc) syntax, so
+ we give it the old semantics for now, but warn about
+ its use, so that users have some time to fix their code.
+
+ Note that due to this backward compatibility hack,
+ we'll get unexpected results when @(offset, pc) is used,
+ and offset is a symbol that is set later to an an address
+ difference, or an external symbol that is set to an
+ address difference in another source file, so we want to
+ eventually remove it. */
if (op->immediate.X_op == O_symbol)
{
op->type = A_DISP_PC;
while (opcode->name)
{
this_try = opcode++;
- if (this_try->name != name)
+ if ((this_try->name != name) && (strcmp (this_try->name, name) != 0))
{
/* We've looked so far down the table that we've run out of
opcodes with the same name. */
sh_operand_info *user = operands + n;
sh_arg_type arg = this_try->arg[n];
- /* If this is a parallel insn check to see if both
- parts have the same destination register. */
- if ((n == 2) && (this_try->nibbles[0] == PPI))
- {
- static boolean bIsPPI = false;
- static int nLastDestReg;
-
- if (!bIsPPI)
- {
- bIsPPI = true;
- nLastDestReg = user->reg;
- }
- else /* Second insn. */
- {
- if (nLastDestReg == user->reg)
- as_warn (_("destination register is same for parallel insns"));
-
- bIsPPI = false;
- }
- }
-
switch (arg)
{
case A_DISP_PC:
int move_code;
unsigned int size;
- /* Some insn ignore one or more register fields, e.g. psts machl,a0.
- Make sure we encode a defined insn pattern. */
- reg_x = 0;
- reg_y = 0;
-
for (;;)
{
sh_operand_info operand[3];
+ /* Some insn ignore one or more register fields, e.g. psts machl,a0.
+ Make sure we encode a defined insn pattern. */
+ reg_x = 0;
+ reg_y = 0;
+ reg_n = 0;
+
if (opcode->arg[0] != A_END)
op_end = get_operands (opcode, op_end, operand);
opcode = get_specific (opcode, operand);
default:
as_bad (_("bad padd / psub pmuls output operand"));
}
+ /* Generate warning if the destination register for padd / psub
+ and pmuls is the same ( only for A0 or A1 ).
+ If the last nibble is 1010 then A0 is used in both
+ padd / psub and pmuls. If it is 1111 then A1 is used
+ as destination register in both padd / psub and pmuls. */
+
+ if ((((field_b | reg_efg) & 0x000F) == 0x000A)
+ || (((field_b | reg_efg) & 0x000F) == 0x000F))
+ as_warn (_("destination register is same for parallel insns"));
}
field_b += 0x4000 + reg_efg;
break;
{
/* If we've seen pseudo-directives, make sure any emitted data or
frags are marked as data. */
- if (seen_insn == false)
+ if (!seen_insn)
{
- sh64_update_contents_mark (true);
+ sh64_update_contents_mark (TRUE);
sh64_set_contents_type (CRT_SH5_ISA16);
}
- seen_insn = true;
+ seen_insn = TRUE;
}
#endif /* HAVE_SH64 */
if (opcode->arg[0] == A_BDISP12
|| opcode->arg[0] == A_BDISP8)
{
+ /* Since we skip get_specific here, we have to check & update
+ valid_arch now. */
+ if (valid_arch & opcode->arch)
+ valid_arch &= opcode->arch;
+ else
+ as_bad (_("Delayed branches not available on SH1"));
parse_exp (op_end + 1, &operand[0]);
build_relax (opcode, &operand[0]);
}
demand_empty_rest_of_line ();
}
\f
-CONST char *md_shortopts = "";
+const char *md_shortopts = "";
struct option md_longopts[] =
{
#define OPTION_RELAX (OPTION_MD_BASE)
#define OPTION_LITTLE (OPTION_BIG + 1)
#define OPTION_SMALL (OPTION_LITTLE + 1)
#define OPTION_DSP (OPTION_SMALL + 1)
+#define OPTION_ISA (OPTION_DSP + 1)
{"relax", no_argument, NULL, OPTION_RELAX},
{"big", no_argument, NULL, OPTION_BIG},
{"little", no_argument, NULL, OPTION_LITTLE},
{"small", no_argument, NULL, OPTION_SMALL},
{"dsp", no_argument, NULL, OPTION_DSP},
+ {"isa", required_argument, NULL, OPTION_ISA},
#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},
break;
case OPTION_DSP:
- sh_dsp = 1;
+ preset_target_arch = arch_sh1_up & ~arch_sh3e_up;
break;
-#ifdef HAVE_SH64
case OPTION_ISA:
- if (strcasecmp (arg, "shmedia") == 0)
+ if (strcasecmp (arg, "sh4") == 0)
+ preset_target_arch = arch_sh4;
+ else if (strcasecmp (arg, "any") == 0)
+ preset_target_arch = arch_sh1_up;
+#ifdef HAVE_SH64
+ else if (strcasecmp (arg, "shmedia") == 0)
{
if (sh64_isa_mode == sh64_isa_shcompact)
as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
sh64_isa_mode = sh64_isa_shcompact;
}
+#endif /* HAVE_SH64 */
else
as_bad ("Invalid argument to --isa option: %s", arg);
break;
+#ifdef HAVE_SH64
case OPTION_ABI:
if (strcmp (arg, "32") == 0)
{
break;
case OPTION_NO_MIX:
- sh64_mix = false;
+ sh64_mix = FALSE;
break;
case OPTION_SHCOMPACT_CONST_CRANGE:
- sh64_shcompact_const_crange = true;
+ sh64_shcompact_const_crange = TRUE;
break;
case OPTION_NO_EXPAND:
- sh64_expand = false;
+ sh64_expand = FALSE;
break;
case OPTION_PT32:
- sh64_pt32 = true;
+ sh64_pt32 = TRUE;
break;
#endif /* HAVE_SH64 */
-big generate big endian code\n\
-relax alter jump instructions for long displacements\n\
-small align sections to 4 byte boundaries, not 16\n\
--dsp enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
+-dsp enable sh-dsp insns, and disable sh2e/sh3e/sh4 insns.\n"));
#ifdef HAVE_SH64
fprintf (stream, _("\
-isa=[shmedia set default instruction set for SH64\n\
default:
#ifdef HAVE_SH64
- shmedia_md_convert_frag (headers, seg, fragP, true);
+ shmedia_md_convert_frag (headers, seg, fragP, TRUE);
#else
abort ();
#endif
BFD_RELOC_SH_ALIGN);
}
-/* This macro decides whether a particular reloc is an entry in a
- switch table. It is used when relaxing, because the linker needs
- to know about all such entries so that it can adjust them if
- necessary. */
-
-#ifdef BFD_ASSEMBLER
-#define SWITCH_TABLE_CONS(fix) (0)
-#else
-#define SWITCH_TABLE_CONS(fix) \
- ((fix)->fx_r_type == 0 \
- && ((fix)->fx_size == 2 \
- || (fix)->fx_size == 1 \
- || (fix)->fx_size == 4))
-#endif
+/* See whether the relocation should be resolved locally. */
-#define SWITCH_TABLE(fix) \
- ((fix)->fx_addsy != NULL \
- && (fix)->fx_subsy != NULL \
- && S_GET_SEGMENT ((fix)->fx_addsy) == text_section \
- && S_GET_SEGMENT ((fix)->fx_subsy) == text_section \
- && ((fix)->fx_r_type == BFD_RELOC_32 \
- || (fix)->fx_r_type == BFD_RELOC_16 \
- || (fix)->fx_r_type == BFD_RELOC_8 \
- || SWITCH_TABLE_CONS (fix)))
+static bfd_boolean
+sh_local_pcrel (fix)
+ fixS *fix;
+{
+ return (! sh_relax
+ && (fix->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+ || fix->fx_r_type == BFD_RELOC_8_PCREL
+ || fix->fx_r_type == BFD_RELOC_SH_SWITCH16
+ || fix->fx_r_type == BFD_RELOC_SH_SWITCH32));
+}
/* See whether we need to force a relocation into the output file.
This is used to force out switch and PC relative relocations when
sh_force_relocation (fix)
fixS *fix;
{
+ /* These relocations can't make it into a DSO, so no use forcing
+ them for global symbols. */
+ if (sh_local_pcrel (fix))
+ return 0;
- if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY
- || fix->fx_r_type == BFD_RELOC_SH_LOOP_START
- || fix->fx_r_type == BFD_RELOC_SH_LOOP_END)
+ /* Make sure some relocations get emitted. */
+ if (fix->fx_r_type == BFD_RELOC_SH_LOOP_START
+ || fix->fx_r_type == BFD_RELOC_SH_LOOP_END
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_GD_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LD_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_IE_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LDO_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LE_32
+ || generic_force_reloc (fix))
return 1;
if (! sh_relax)
}
#ifdef OBJ_ELF
-boolean
+bfd_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)
+ if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
|| fixP->fx_r_type == BFD_RELOC_RVA)
return 0;
val = EF_SH1;
else if (valid_arch & arch_sh2)
val = EF_SH2;
+ else if (valid_arch & arch_sh2e)
+ val = EF_SH2E;
else if (valid_arch & arch_sh_dsp)
val = EF_SH_DSP;
else if (valid_arch & arch_sh3)
val -= S_GET_VALUE (fixP->fx_addsy);
#endif
-#ifndef BFD_ASSEMBLER
+#ifdef BFD_ASSEMBLER
+ if (SWITCH_TABLE (fixP))
+ val -= S_GET_VALUE (fixP->fx_subsy);
+#else
if (fixP->fx_r_type == 0)
{
if (fixP->fx_size == 2)
/* 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;
+ val = fixP->fx_offset;
if (fixP->fx_subsy)
val -= S_GET_VALUE (fixP->fx_subsy);
+ fixP->fx_addnumber = val;
md_number_to_chars (buf, val, 4);
break;
md_number_to_chars (buf, val, 4);
break;
+ case BFD_RELOC_SH_TLS_GD_32:
+ case BFD_RELOC_SH_TLS_LD_32:
+ case BFD_RELOC_SH_TLS_IE_32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fallthrough */
case BFD_RELOC_32_GOT_PCREL:
case BFD_RELOC_SH_GOTPLT32:
* valP = 0; /* Fully resolved at runtime. No addend. */
md_number_to_chars (buf, 0, 4);
break;
+ case BFD_RELOC_SH_TLS_LDO_32:
+ case BFD_RELOC_SH_TLS_LE_32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fallthrough */
case BFD_RELOC_32_GOTOFF:
md_number_to_chars (buf, val, 4);
break;
fixS *fixP;
segT sec;
{
- if (fixP->fx_addsy != (symbolS *) NULL
- && (! S_IS_DEFINED (fixP->fx_addsy)
- || S_IS_EXTERN (fixP->fx_addsy)
- || S_IS_WEAK (fixP->fx_addsy)
+ if (! sh_local_pcrel (fixP)
+ && fixP->fx_addsy != (symbolS *) NULL
+ && (generic_force_reloc (fixP)
|| S_GET_SEGMENT (fixP->fx_addsy) != sec))
{
/* The symbol is undefined (or is defined but not in this section,
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
- if (fixp->fx_subsy
- && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
- {
- fixp->fx_addnumber -= S_GET_VALUE (fixp->fx_subsy);
- fixp->fx_subsy = 0;
- }
-
r_type = fixp->fx_r_type;
if (SWITCH_TABLE (fixp))
{
- rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ rel->addend = 0;
if (r_type == BFD_RELOC_16)
r_type = BFD_RELOC_SH_SWITCH16;
else if (r_type == BFD_RELOC_8)
rel->addend = 0;
rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
- if (rel->howto == NULL || fixp->fx_subsy)
+#ifdef OBJ_ELF
+ if (rel->howto->type == R_SH_IND12W)
+ rel->addend += fixp->fx_offset - 4;
+#endif
+ if (rel->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("Cannot represent relocation type %s"),
reloc_type = BFD_RELOC_32_GOT_PCREL;
else if ((next_end = sh_end_of_match (next + 1, "PLT")))
reloc_type = BFD_RELOC_32_PLT_PCREL;
+ else if ((next_end = sh_end_of_match (next + 1, "TLSGD")))
+ reloc_type = BFD_RELOC_SH_TLS_GD_32;
+ else if ((next_end = sh_end_of_match (next + 1, "TLSLDM")))
+ reloc_type = BFD_RELOC_SH_TLS_LD_32;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTTPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_IE_32;
+ else if ((next_end = sh_end_of_match (next + 1, "TPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_LE_32;
+ else if ((next_end = sh_end_of_match (next + 1, "DTPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_LDO_32;
else
goto no_suffix;