/* tc-microblaze.c -- Assemble code for Xilinx MicroBlaze
- Copyright 2009, 2010 Free Software Foundation.
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
02110-1301, USA. */
-#include <stdio.h>
#include "as.h"
+#include <stdio.h>
#include "bfd.h"
#include "subsegs.h"
#define DEFINE_TABLE
#define streq(a,b) (strcmp (a, b) == 0)
#endif
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+
void microblaze_generate_symbol (char *sym);
static bfd_boolean check_spl_reg (unsigned *);
#define GOT_OFFSET 8
#define PLT_OFFSET 9
#define GOTOFF_OFFSET 10
-
+#define TLSGD_OFFSET 11
+#define TLSLD_OFFSET 12
+#define TLSDTPMOD_OFFSET 13
+#define TLSDTPREL_OFFSET 14
+#define TLSGOTTPREL_OFFSET 15
+#define TLSTPREL_OFFSET 16
/* Initialize the relax table. */
const relax_typeS md_relax_table[] =
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 8: GOT_OFFSET. */
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 9: PLT_OFFSET. */
{ 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 10: GOTOFF_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 11: TLSGD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 12: TLSLD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*1, 0 }, /* 13: TLSDTPMOD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 14: TLSDTPREL_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 15: TLSGOTTPREL_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 } /* 16: TLSTPREL_OFFSET. */
};
static struct hash_control * opcode_hash_control; /* Opcode mnemonics. */
segT current_seg = now_seg;
subsegT current_subseg = now_subseg;
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
/* Just after name is now '\0'. */
p = input_line_pointer;
- *p = c;
+ (void) restore_line_pointer (c);
SKIP_WHITESPACE ();
if (*input_line_pointer != ',')
{
static void
microblaze_s_func (int end_p ATTRIBUTE_UNUSED)
{
- *input_line_pointer = get_symbol_end ();
+ char *name;
+ restore_line_pointer (get_symbol_name (&name));
s_func (1);
}
symbolS *symbolP;
expressionS exp;
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (&name);
symbolP = symbol_find_or_make (name);
S_SET_WEAK (symbolP);
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
SKIP_WHITESPACE ();
}
return s;
}
+ /* Stack protection registers. */
+ else if (strncasecmp (s, "rshr", 4) == 0)
+ {
+ *reg = REG_SHR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rslr", 4) == 0)
+ {
+ *reg = REG_SLR;
+ return s + 4;
+ }
else
{
if (TOLOWER (s[0]) == 'r')
}
/* Symbol modifiers (@GOT, @PLT, @GOTOFF). */
+#define IMM_NONE 0
#define IMM_GOT 1
#define IMM_PLT 2
#define IMM_GOTOFF 3
+#define IMM_TLSGD 4
+#define IMM_TLSLD 5
+#define IMM_TLSDTPMOD 6
+#define IMM_TLSDTPREL 7
+#define IMM_TLSTPREL 8
+#define IMM_MAX 9
+
+struct imm_type {
+ char *isuffix; /* Suffix String */
+ int itype; /* Suffix Type */
+ int otype; /* Offset Type */
+};
+
+/* These are NOT in assending order of type, GOTOFF is ahead to make
+ sure @GOTOFF does not get matched with @GOT */
+static struct imm_type imm_types[] = {
+ { "NONE", IMM_NONE , 0 },
+ { "GOTOFF", IMM_GOTOFF , GOTOFF_OFFSET },
+ { "GOT", IMM_GOT , GOT_OFFSET },
+ { "PLT", IMM_PLT , PLT_OFFSET },
+ { "TLSGD", IMM_TLSGD , TLSGD_OFFSET },
+ { "TLSLDM", IMM_TLSLD, TLSLD_OFFSET },
+ { "TLSDTPMOD", IMM_TLSDTPMOD, TLSDTPMOD_OFFSET },
+ { "TLSDTPREL", IMM_TLSDTPREL, TLSDTPREL_OFFSET },
+ { "TLSTPREL", IMM_TLSTPREL, TLSTPREL_OFFSET }
+};
+
+static int
+match_imm (const char *s, int *ilen)
+{
+ int i;
+ int slen;
+
+ /* Check for matching suffix */
+ for (i = 1; i < IMM_MAX; i++)
+ {
+ slen = strlen (imm_types[i].isuffix);
+
+ if (strncmp (imm_types[i].isuffix, s, slen) == 0)
+ {
+ *ilen = slen;
+ return imm_types[i].itype;
+ }
+ } /* for */
+ *ilen = 0;
+ return 0;
+}
+
+static int
+get_imm_otype (int itype)
+{
+ int i, otype;
+
+ otype = 0;
+ /* Check for matching itype */
+ for (i = 1; i < IMM_MAX; i++)
+ {
+ if (imm_types[i].itype == itype)
+ {
+ otype = imm_types[i].otype;
+ break;
+ }
+ }
+ return otype;
+}
static symbolS * GOT_symbol;
#define GOT_SYMBOL_NAME "_GLOBAL_OFFSET_TABLE_"
static char *
-parse_imm (char * s, expressionS * e, int min, int max)
+parse_imm (char * s, expressionS * e, offsetT min, offsetT max)
{
char *new_pointer;
char *atp;
+ int itype, ilen;
+
+ ilen = 0;
/* Find the start of "@GOT" or "@PLT" suffix (if any) */
for (atp = s; *atp != '@'; atp++)
if (*atp == '@')
{
- if (strncmp (atp + 1, "GOTOFF", 5) == 0)
- {
- *atp = 0;
- e->X_md = IMM_GOTOFF;
- }
- else if (strncmp (atp + 1, "GOT", 3) == 0)
- {
- *atp = 0;
- e->X_md = IMM_GOT;
- }
- else if (strncmp (atp + 1, "PLT", 3) == 0)
- {
- *atp = 0;
- e->X_md = IMM_PLT;
- }
+ itype = match_imm (atp + 1, &ilen);
+ if (itype != 0)
+ {
+ *atp = 0;
+ e->X_md = itype;
+ }
else
- {
- atp = NULL;
- e->X_md = 0;
- }
+ {
+ atp = NULL;
+ e->X_md = 0;
+ ilen = 0;
+ }
*atp = 0;
}
else
new_pointer = parse_exp (s, e);
+ if (!GOT_symbol && ! strncmp (s, GOT_SYMBOL_NAME, 20))
+ {
+ GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
+ }
+
if (e->X_op == O_absent)
; /* An error message has already been emitted. */
else if ((e->X_op != O_constant && e->X_op != O_symbol) )
as_fatal (_("operand must be a constant or a label"));
- else if ((e->X_op == O_constant) && ((int) e->X_add_number < min
- || (int) e->X_add_number > max))
+ else if (e->X_op == O_constant)
{
- as_fatal (_("operand must be absolute in range %d..%d, not %d"),
- min, max, (int) e->X_add_number);
+ /* Special case: sign extend negative 32-bit values to offsetT size. */
+ if ((e->X_add_number >> 31) == 1)
+ e->X_add_number |= -((addressT) (1U << 31));
+
+ if (e->X_add_number < min || e->X_add_number > max)
+ {
+ as_fatal (_("operand must be absolute in range %lx..%lx, not %lx"),
+ (long) min, (long) max, (long) e->X_add_number);
+ }
}
if (atp)
{
*atp = '@'; /* restore back (needed?) */
if (new_pointer >= atp)
- new_pointer += (e->X_md == IMM_GOTOFF)?7:4;
- /* sizeof("@GOTOFF", "@GOT" or "@PLT") */
-
+ new_pointer += ilen + 1; /* sizeof (imm_suffix) + 1 for '@' */
}
return new_pointer;
}
return tmpbuf;
}
-extern void
+extern bfd_reloc_code_real_type
parse_cons_expression_microblaze (expressionS *exp, int size)
{
if (size == 4)
}
else
expression (exp);
+ return BFD_RELOC_NONE;
}
/* This is the guts of the machine-dependent assembler. STR points to a
|| (*reg == REG_PID) || (*reg == REG_ZPR)
|| (*reg == REG_TLBX) || (*reg == REG_TLBLO)
|| (*reg == REG_TLBHI) || (*reg == REG_TLBSX)
+ || (*reg == REG_SHR) || (*reg == REG_SLR)
|| (*reg >= REG_PVR+MIN_PVR_REGNUM && *reg <= REG_PVR+MAX_PVR_REGNUM))
return TRUE;
if (fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_GOTOFF
|| fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOT
- || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT)
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSLD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPMOD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSDTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSTPREL)
return 0;
return 1;
opc = str_microblaze_rw_anchor;
else
opc = NULL;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
- else if (exp.X_md == IMM_GOTOFF)
- subtype = GOTOFF_OFFSET;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
else
subtype = opcode->inst_offset_type;
as_fatal (_("Error in statement syntax"));
immed = 0;
}
- /* Check for spl registers. */
- if (check_spl_reg (®1))
- as_fatal (_("Cannot use special register with this instruction"));
inst |= (immed << IMM_LOW) & RFSL_MASK;
output = frag_more (isize);
break;
immed = opcode->immval_mask | REG_TLBLO_MASK;
else if (reg2 == REG_TLBHI)
immed = opcode->immval_mask | REG_TLBHI_MASK;
+ else if (reg2 == REG_SHR)
+ immed = opcode->immval_mask | REG_SHR_MASK;
+ else if (reg2 == REG_SLR)
+ immed = opcode->immval_mask | REG_SLR_MASK;
else if (reg2 >= (REG_PVR+MIN_PVR_REGNUM) && reg2 <= (REG_PVR+MAX_PVR_REGNUM))
immed = opcode->immval_mask | REG_PVR_MASK | reg2;
else
immed = opcode->immval_mask | REG_TLBHI_MASK;
else if (reg1 == REG_TLBSX)
immed = opcode->immval_mask | REG_TLBSX_MASK;
+ else if (reg1 == REG_SHR)
+ immed = opcode->immval_mask | REG_SHR_MASK;
+ else if (reg1 == REG_SLR)
+ immed = opcode->immval_mask | REG_SLR_MASK;
else
as_fatal (_("invalid value for special purpose register"));
inst |= (reg2 << RA_LOW) & RA_MASK;
output = frag_more (isize);
break;
- case INST_TYPE_RD_R1_SPECIAL:
+ case INST_TYPE_R1_R2_SPECIAL:
if (strcmp (op_end, ""))
- op_end = parse_reg (op_end + 1, ®1); /* Get rd. */
+ op_end = parse_reg (op_end + 1, ®1); /* Get r1. */
else
{
as_fatal (_("Error in statement syntax"));
reg1 = 0;
}
if (strcmp (op_end, ""))
- op_end = parse_reg (op_end + 1, ®2); /* Get r1. */
+ op_end = parse_reg (op_end + 1, ®2); /* Get r2. */
else
{
as_fatal (_("Error in statement syntax"));
as_fatal (_("Cannot use special register with this instruction"));
/* insn wic ra, rb => wic ra, ra, rb. */
- inst |= (reg1 << RD_LOW) & RD_MASK;
inst |= (reg1 << RA_LOW) & RA_MASK;
inst |= (reg2 << RB_LOW) & RB_MASK;
char *opc = NULL;
relax_substateT subtype;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
else
subtype = opcode->inst_offset_type;
+
output = frag_var (rs_machine_dependent,
isize * 2, /* maxm of 2 words. */
isize, /* minm of 1 word. */
char *opc = NULL;
relax_substateT subtype;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
- else
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
subtype = opcode->inst_offset_type;
+
output = frag_var (rs_machine_dependent,
isize * 2, /* maxm of 2 words. */
isize, /* minm of 1 word. */
char *opc = NULL;
relax_substateT subtype;
- if (exp.X_md == IMM_GOT)
- subtype = GOT_OFFSET;
- else if (exp.X_md == IMM_PLT)
- subtype = PLT_OFFSET;
- else
- subtype = opcode->inst_offset_type;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
+ subtype = opcode->inst_offset_type;
+
output = frag_var (rs_machine_dependent,
isize * 2, /* maxm of 2 words. */
isize, /* minm of 1 word. */
output = frag_more (isize);
break;
+ case INST_TYPE_IMM5:
+ if (strcmp(op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM5, MAX_IMM5);
+ else
+ as_fatal(_("Error in statement syntax"));
+ if (exp.X_op != O_constant) {
+ as_warn(_("Symbol used as immediate for mbar instruction"));
+ } else {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+ if (immed != (immed % 32)) {
+ as_warn(_("Immediate value for mbar > 32. using <value %% 32>"));
+ immed = immed % 32;
+ }
+ inst |= (immed << IMM_MBAR);
+ break;
+
default:
as_fatal (_("unimplemented opcode \"%s\""), name);
}
struct option md_longopts[] =
{
+ {"EB", no_argument, NULL, OPTION_EB},
+ {"EL", no_argument, NULL, OPTION_EL},
{ NULL, no_argument, NULL, 0}
};
fragP->fr_fix += INST_WORD_SIZE * 2;
fragP->fr_var = 0;
break;
+ case TLSGD_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSGD);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSLD_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSLD);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSDTPREL_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSDTPREL);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
default:
abort ();
segT segment)
{
char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
- char * file = fixP->fx_file ? fixP->fx_file : _("unknown");
+ const char * file = fixP->fx_file ? fixP->fx_file : _("unknown");
const char * symname;
/* Note: use offsetT because it is signed, valueT is unsigned. */
offsetT val = (offsetT) * valp;
}
break;
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+
case BFD_RELOC_MICROBLAZE_64_GOTPC:
case BFD_RELOC_MICROBLAZE_64_GOT:
case BFD_RELOC_MICROBLAZE_64_PLT:
as_bad (_("Absolute PC-relative value in relaxation code. Assembler error....."));
abort ();
}
- else if ((S_GET_SEGMENT (fragP->fr_symbol) == segment_type))
+ else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type &&
+ !S_IS_WEAK (fragP->fr_symbol))
{
fragP->fr_subtype = DEFINED_PC_OFFSET;
/* Don't know now whether we need an imm instruction. */
else if (streq (fragP->fr_opcode, str_microblaze_ro_anchor))
{
/* It is accessed using the small data read only anchor. */
- if ((S_GET_SEGMENT (fragP->fr_symbol) == &bfd_com_section)
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == bfd_com_section_ptr)
|| (S_GET_SEGMENT (fragP->fr_symbol) == sdata2_segment)
|| (S_GET_SEGMENT (fragP->fr_symbol) == sbss2_segment)
|| (! S_IS_DEFINED (fragP->fr_symbol)))
{
/* Variable not in small data read only segment accessed
using small data read only anchor. */
- char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
+ const char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
as_bad_where (file, fragP->fr_line,
_("Variable is accessed using small data read "
}
else if (streq (fragP->fr_opcode, str_microblaze_rw_anchor))
{
- if ((S_GET_SEGMENT (fragP->fr_symbol) == &bfd_com_section)
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == bfd_com_section_ptr)
|| (S_GET_SEGMENT (fragP->fr_symbol) == sdata_segment)
|| (S_GET_SEGMENT (fragP->fr_symbol) == sbss_segment)
|| (!S_IS_DEFINED (fragP->fr_symbol)))
}
else
{
- char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
+ const char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
as_bad_where (file, fragP->fr_line,
_("Variable is accessed using small data read "
case GOT_OFFSET:
case PLT_OFFSET:
case GOTOFF_OFFSET:
+ case TLSGD_OFFSET:
+ case TLSLD_OFFSET:
+ case TLSTPREL_OFFSET:
+ case TLSDTPREL_OFFSET:
fragP->fr_var = INST_WORD_SIZE*2;
break;
case DEFINED_RO_SEGMENT:
case DEFINED_RW_SEGMENT:
case DEFINED_PC_OFFSET:
+ case TLSDTPMOD_OFFSET:
fragP->fr_var = INST_WORD_SIZE;
break;
default:
case BFD_RELOC_MICROBLAZE_64_PLT:
case BFD_RELOC_MICROBLAZE_64_GOTOFF:
case BFD_RELOC_MICROBLAZE_32_GOTOFF:
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
code = fixp->fx_r_type;
break;
{
switch (c)
{
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
default:
return 0;
}
cons_fix_new_microblaze (fragS * frag,
int where,
int size,
- expressionS *exp)
+ expressionS *exp,
+ bfd_reloc_code_real_type r)
{
-
- bfd_reloc_code_real_type r;
-
if ((exp->X_op == O_subtract) && (exp->X_add_symbol) &&
(exp->X_op_symbol) && (now_seg != absolute_section) && (size == 4)
&& (!S_IS_LOCAL (exp->X_op_symbol)))