/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1993-2015 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
accommodate the insns seen so far. */
static unsigned int valid_arch;
+#ifdef OBJ_ELF
+/* Whether --fdpic was given. */
+static int sh_fdpic;
+#endif
+
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant. */
if (exp->X_op == O_PIC_reloc)
{
-#ifdef HAVE_SH64
switch (*r_type_p)
{
case BFD_RELOC_NONE:
*r_type_p = exp->X_md;
break;
+ case BFD_RELOC_SH_DISP20:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT20;
+ break;
+
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF20;
+ break;
+
+ case BFD_RELOC_SH_GOTFUNCDESC:
+ *r_type_p = BFD_RELOC_SH_GOTFUNCDESC20;
+ break;
+
+ case BFD_RELOC_SH_GOTOFFFUNCDESC:
+ *r_type_p = BFD_RELOC_SH_GOTOFFFUNCDESC20;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+#ifdef HAVE_SH64
case BFD_RELOC_SH_IMM_LOW16:
switch (exp->X_md)
{
abort ();
}
break;
+#endif
default:
abort ();
}
-#else
- *r_type_p = exp->X_md;
-#endif
if (exp == main_exp)
exp->X_op = O_symbol;
else
/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG. */
void
-sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp)
+sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp,
+ bfd_reloc_code_real_type r_type)
{
- bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+ r_type = BFD_RELOC_UNUSED;
if (sh_check_fixup (exp, &r_type))
as_bad (_("Invalid PIC expression."));
r_type = BFD_RELOC_32;
break;
-#ifdef HAVE_SH64
case 8:
r_type = BFD_RELOC_64;
break;
-#endif
default:
goto error;
/* Clobbers input_line_pointer, checks end-of-line. */
/* NBYTES 1=.byte, 2=.word, 4=.long */
static void
-sh_elf_cons (register int nbytes)
+sh_elf_cons (int nbytes)
{
expressionS exp;
symbol_get_frag (r->X_add_symbol),
&frag_off))
{
- l->X_add_number -= r->X_add_number;
- l->X_add_number -= frag_off / OCTETS_PER_BYTE;
- l->X_add_number += (S_GET_VALUE (l->X_add_symbol)
- - S_GET_VALUE (r->X_add_symbol));
+ offsetT symval_diff = S_GET_VALUE (l->X_add_symbol)
+ - S_GET_VALUE (r->X_add_symbol);
+ subtract_from_result (l, r->X_add_number, r->X_extrabit);
+ subtract_from_result (l, frag_off / OCTETS_PER_BYTE, 0);
+ add_to_result (l, symval_diff, symval_diff < 0);
l->X_op = O_constant;
l->X_add_symbol = 0;
return 1;
parse_exp (char *s, sh_operand_info *op)
{
char *save;
- char *new;
+ char *new_pointer;
save = input_line_pointer;
input_line_pointer = s;
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;
+ new_pointer = input_line_pointer;
input_line_pointer = save;
- return new;
+ return new_pointer;
}
/* The many forms of operand:
sh_operand_info *user = operands + n;
sh_arg_type arg = this_try->arg[n];
- if (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh2a_nofpu_up)
- && ( arg == A_DISP_REG_M
- || arg == A_DISP_REG_N))
- {
- /* Check a few key IMM* fields for overflow. */
- int opf;
- long val = user->immediate.X_add_number;
-
- for (opf = 0; opf < 4; opf ++)
- switch (this_try->nibbles[opf])
- {
- case IMM0_4:
- case IMM1_4:
- if (val < 0 || val > 15)
- goto fail;
- break;
- case IMM0_4BY2:
- case IMM1_4BY2:
- if (val < 0 || val > 15 * 2)
- goto fail;
- break;
- case IMM0_4BY4:
- case IMM1_4BY4:
- if (val < 0 || val > 15 * 4)
- goto fail;
- break;
- default:
- break;
- }
- }
switch (arg)
{
case A_DISP_PC:
printf (_("unhandled %d\n"), arg);
goto fail;
}
+ if (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh2a_nofpu_up)
+ && ( arg == A_DISP_REG_M
+ || arg == A_DISP_REG_N))
+ {
+ /* Check a few key IMM* fields for overflow. */
+ int opf;
+ long val = user->immediate.X_add_number;
+
+ for (opf = 0; opf < 4; opf ++)
+ switch (this_try->nibbles[opf])
+ {
+ case IMM0_4:
+ case IMM1_4:
+ if (val < 0 || val > 15)
+ goto fail;
+ break;
+ case IMM0_4BY2:
+ case IMM1_4BY2:
+ if (val < 0 || val > 15 * 2)
+ goto fail;
+ break;
+ case IMM0_4BY4:
+ case IMM1_4BY4:
+ if (val < 0 || val > 15 * 4)
+ goto fail;
+ break;
+ default:
+ break;
+ }
+ }
}
if ( !SH_MERGE_ARCH_SET_VALID (valid_arch, this_try->arch))
goto fail;
static unsigned int
build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
{
- int index;
+ int indx;
char nbuf[8];
char *output;
unsigned int size = 2;
int low_byte = target_big_endian ? 1 : 0;
int max_index = 4;
+ bfd_reloc_code_real_type r_type;
+#ifdef OBJ_ELF
+ int unhandled_pic = 0;
+#endif
nbuf[0] = 0;
nbuf[1] = 0;
nbuf[6] = 0;
nbuf[7] = 0;
+#ifdef OBJ_ELF
+ for (indx = 0; indx < 3; indx++)
+ if (opcode->arg[indx] == A_IMM
+ && operand[indx].type == A_IMM
+ && (operand[indx].immediate.X_op == O_PIC_reloc
+ || sh_PIC_related_p (operand[indx].immediate.X_add_symbol)
+ || sh_PIC_related_p (operand[indx].immediate.X_op_symbol)))
+ unhandled_pic = 1;
+#endif
+
if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
{
output = frag_more (4);
else
output = frag_more (2);
- for (index = 0; index < max_index; index++)
+ for (indx = 0; indx < max_index; indx++)
{
- sh_nibble_type i = opcode->nibbles[index];
+ sh_nibble_type i = opcode->nibbles[indx];
if (i < 16)
{
- nbuf[index] = i;
+ nbuf[indx] = i;
}
else
{
{
case REG_N:
case REG_N_D:
- nbuf[index] = reg_n;
+ nbuf[indx] = reg_n;
break;
case REG_M:
- nbuf[index] = reg_m;
+ nbuf[indx] = 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;
+ nbuf[indx] = (reg_n & 3) | 4;
break;
case REG_NM:
- nbuf[index] = reg_n | (reg_m >> 2);
+ nbuf[indx] = reg_n | (reg_m >> 2);
break;
case REG_B:
- nbuf[index] = reg_b | 0x08;
+ nbuf[indx] = reg_b | 0x08;
break;
case REG_N_B01:
- nbuf[index] = reg_n | 0x01;
+ nbuf[indx] = reg_n | 0x01;
break;
case IMM0_3s:
- nbuf[index] |= 0x08;
+ nbuf[indx] |= 0x08;
case IMM0_3c:
insert (output + low_byte, BFD_RELOC_SH_IMM3, 0, operand);
break;
case IMM0_3Us:
- nbuf[index] |= 0x80;
+ nbuf[indx] |= 0x80;
case IMM0_3Uc:
insert (output + low_byte, BFD_RELOC_SH_IMM3U, 0, operand);
break;
case IMM0_20_4:
break;
case IMM0_20:
- insert4 (output, BFD_RELOC_SH_DISP20, 0, operand);
+ r_type = BFD_RELOC_SH_DISP20;
+#ifdef OBJ_ELF
+ if (sh_check_fixup (&operand->immediate, &r_type))
+ as_bad (_("Invalid PIC expression."));
+ unhandled_pic = 0;
+#endif
+ insert4 (output, r_type, 0, operand);
break;
case IMM0_20BY8:
insert4 (output, BFD_RELOC_SH_DISP20BY8, 0, operand);
break;
case REPEAT:
output = insert_loop_bounds (output, operand);
- nbuf[index] = opcode->nibbles[3];
+ nbuf[indx] = opcode->nibbles[3];
operand += 2;
break;
default:
}
}
}
+#ifdef OBJ_ELF
+ if (unhandled_pic)
+ as_bad (_("misplaced PIC operand"));
+#endif
if (!target_big_endian)
{
output[1] = (nbuf[0] << 4) | (nbuf[1]);
unsigned char *op_start;
unsigned char *op_end;
char name[20];
- int nlen = 0;
+ unsigned int nlen = 0;
/* Drop leading whitespace. */
while (*str == ' ')
assemble_ppi, so the opcode might be terminated by an '@'. */
for (op_start = op_end = (unsigned char *) str;
*op_end
- && nlen < 20
+ && nlen < sizeof (name) - 1
&& !is_end_of_line[*op_end] && *op_end != ' ' && *op_end != '@';
op_end++)
{
OPTION_PT32,
#endif
OPTION_H_TICK_HEX,
+#ifdef OBJ_ELF
+ OPTION_FDPIC,
+#endif
OPTION_DUMMY /* Not used. This is just here to make it easy to add and subtract options from this enum. */
};
#endif /* HAVE_SH64 */
{ "h-tick-hex", no_argument, NULL, OPTION_H_TICK_HEX },
+#ifdef OBJ_ELF
+ {"fdpic", no_argument, NULL, OPTION_FDPIC},
+#endif
+
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
}
if (!preset_target_arch)
- as_bad ("Invalid argument to --isa option: %s", arg);
+ as_bad (_("Invalid argument to --isa option: %s"), arg);
}
break;
sh64_abi = sh64_abi_64;
}
else
- as_bad ("Invalid argument to --abi option: %s", arg);
+ as_bad (_("Invalid argument to --abi option: %s"), arg);
break;
case OPTION_NO_MIX:
enable_h_tick_hex = 1;
break;
+#ifdef OBJ_ELF
+ case OPTION_FDPIC:
+ sh_fdpic = TRUE;
+ break;
+#endif /* OBJ_ELF */
+
default:
return 0;
}
--expand-pt32 with -abi=64, expand PT, PTA and PTB instructions\n\
to 32 bits only\n"));
#endif /* HAVE_SH64 */
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+--fdpic generate an FDPIC object file\n"));
+#endif /* OBJ_ELF */
}
\f
/* This struct is used to pass arguments to sh_count_relocs through
sh_cons_align (int nbytes)
{
int nalign;
- char *p;
if (sh_no_align_cons)
{
return;
}
- p = frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
- (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+ frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
record_alignment (now_seg, nalign);
}
{
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_GOT20
|| fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC20
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC20
+ || fixP->fx_r_type == BFD_RELOC_SH_FUNCDESC
|| ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
|| fixP->fx_r_type == BFD_RELOC_RVA)
return 0;
elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK;
elf_elfheader (stdoutput)->e_flags |= val;
+
+ if (sh_fdpic)
+ elf_elfheader (stdoutput)->e_flags |= EF_SH_FDPIC;
+}
+#endif
+
+#ifdef TE_UCLINUX
+/* Return the target format for uClinux. */
+
+const char *
+sh_uclinux_target_format (void)
+{
+ if (sh_fdpic)
+ return (!target_big_endian ? "elf32-sh-fdpic" : "elf32-shbig-fdpic");
+ else
+ return (!target_big_endian ? "elf32-shl" : "elf32-sh");
}
#endif
the other symbol. We have to adjust the relocation type here. */
if (fixP->fx_pcrel)
{
+#ifndef HAVE_SH64
+ /* Safeguard; this must not occur for non-sh64 configurations. */
+ gas_assert (fixP->fx_r_type != BFD_RELOC_64);
+#endif
+
switch (fixP->fx_r_type)
{
default:
buf[highbyte] |= (val >> 8) & 0xf;
break;
+#ifndef HAVE_SH64
+ case BFD_RELOC_64:
+ apply_full_field_fix (fixP, buf, *valP, 8);
+ break;
+#endif
+
case BFD_RELOC_32:
case BFD_RELOC_32_PCREL:
apply_full_field_fix (fixP, buf, val, 4);
S_SET_THREAD_LOCAL (fixP->fx_addsy);
/* Fallthrough */
case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_SH_GOT20:
case BFD_RELOC_SH_GOTPLT32:
+ case BFD_RELOC_SH_GOTFUNCDESC:
+ case BFD_RELOC_SH_GOTFUNCDESC20:
+ case BFD_RELOC_SH_GOTOFFFUNCDESC:
+ case BFD_RELOC_SH_GOTOFFFUNCDESC20:
+ case BFD_RELOC_SH_FUNCDESC:
* valP = 0; /* Fully resolved at runtime. No addend. */
apply_full_field_fix (fixP, buf, 0, 4);
break;
S_SET_THREAD_LOCAL (fixP->fx_addsy);
/* Fallthrough */
case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_SH_GOTOFF20:
apply_full_field_fix (fixP, buf, val, 4);
break;
#endif
val = ((val >> shift)
| ((long) -1 & ~ ((long) -1 >> shift)));
}
+
+ /* Extend sign for 64-bit host. */
+ val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
if (max != 0 && (val < min || val > max))
as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
else if (max != 0)
if (SWITCH_TABLE (fixp))
{
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
- rel->addend = 0;
+ rel->addend = rel->address - S_GET_VALUE(fixp->fx_subsy);
if (r_type == BFD_RELOC_16)
r_type = BFD_RELOC_SH_SWITCH16;
else if (r_type == BFD_RELOC_8)
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 if ((next_end = sh_end_of_match (next + 1, "PCREL")))
+ reloc_type = BFD_RELOC_32_PCREL;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTFUNCDESC")))
+ reloc_type = BFD_RELOC_SH_GOTFUNCDESC;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTOFFFUNCDESC")))
+ reloc_type = BFD_RELOC_SH_GOTOFFFUNCDESC;
+ else if ((next_end = sh_end_of_match (next + 1, "FUNCDESC")))
+ reloc_type = BFD_RELOC_SH_FUNCDESC;
else
goto no_suffix;