/* tc-aarch64.c -- Assemble for the AArch64 ISA
- Copyright 2009, 2010, 2011, 2012, 2013
- Free Software Foundation, Inc.
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GAS.
#define streq(a, b) (strcmp (a, b) == 0)
+#define END_OF_INSN '\0'
+
static aarch64_feature_set cpu_variant;
/* Variables that we set while parsing command-line options. Once all
/* Constants for known architecture features. */
static const aarch64_feature_set cpu_default = CPU_DEFAULT;
-static const aarch64_feature_set aarch64_arch_any = AARCH64_ANY;
-static const aarch64_feature_set aarch64_arch_none = AARCH64_ARCH_NONE;
-
#ifdef OBJ_ELF
/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
static symbolS *GOT_symbol;
+
+/* Which ABI to use. */
+enum aarch64_abi_type
+{
+ AARCH64_ABI_LP64 = 0,
+ AARCH64_ABI_ILP32 = 1
+};
+
+/* AArch64 ABI for the output file. */
+static enum aarch64_abi_type aarch64_abi = AARCH64_ABI_LP64;
+
+/* When non-zero, program to a 32-bit model, in which the C data types
+ int, long and all pointer types are 32-bit objects (ILP32); or to a
+ 64-bit model, in which the C int type is 32-bits but the C long type
+ and all pointer types are 64-bit objects (LP64). */
+#define ilp32_p (aarch64_abi == AARCH64_ABI_ILP32)
#endif
enum neon_el_type
return inst.parsing_error.error;
}
-static inline void
-set_error_message (const char *error)
-{
- inst.parsing_error.error = error;
-}
-
static inline enum aarch64_operand_error_kind
get_error_kind (void)
{
return inst.parsing_error.kind;
}
-static inline void
-set_error_kind (enum aarch64_operand_error_kind kind)
-{
- inst.parsing_error.kind = kind;
-}
-
static inline void
set_error (enum aarch64_operand_error_kind kind, const char *error)
{
static struct hash_control *aarch64_barrier_opt_hsh;
static struct hash_control *aarch64_nzcv_hsh;
static struct hash_control *aarch64_pldop_hsh;
+static struct hash_control *aarch64_hint_opt_hsh;
/* Stuff needed to resolve the label ambiguity
As:
and per-sub-section basis. */
#define MAX_LITERAL_POOL_SIZE 1024
+typedef struct literal_expression
+{
+ expressionS exp;
+ /* If exp.op == O_big then this bignum holds a copy of the global bignum value. */
+ LITTLENUM_TYPE * bignum;
+} literal_expression;
+
typedef struct literal_pool
{
- expressionS literals[MAX_LITERAL_POOL_SIZE];
+ literal_expression literals[MAX_LITERAL_POOL_SIZE];
unsigned int next_free_entry;
unsigned int id;
symbolS *symbol;
otherwise return FALSE.
Accept only one occurrence of:
- 8b 16b 4h 8h 2s 4s 1d 2d
+ 8b 16b 2h 4h 8h 2s 4s 1d 2d
b h s d q */
static bfd_boolean
parse_neon_type_for_operand (struct neon_type_el *parsed_type, char **str)
first_error (_("missing element size"));
return FALSE;
}
- if (width != 0 && width * element_size != 64 && width * element_size != 128)
+ if (width != 0 && width * element_size != 64 && width * element_size != 128
+ && !(width == 2 && element_size == 16))
{
first_error_fmt (_
("invalid element size %d and vector size combination %c"),
{
enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
-#define TRANSITION(from, to) (mapstate == (from) && state == (to))
+ if (state == MAP_INSN)
+ /* AArch64 instructions require 4-byte alignment. When emitting
+ instructions into any section, record the appropriate section
+ alignment. */
+ record_alignment (now_seg, 2);
if (mapstate == state)
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
- else if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
- /* This case will be evaluated later in the next else. */
+
+#define TRANSITION(from, to) (mapstate == (from) && state == (to))
+ if (TRANSITION (MAP_UNDEFINED, MAP_DATA) && !subseg_text_p (now_seg))
+ /* Emit MAP_DATA within executable section in order. Otherwise, it will be
+ evaluated later in the next else. */
return;
else if (TRANSITION (MAP_UNDEFINED, MAP_INSN))
{
/* Only add the symbol if the offset is > 0:
- if we're at the first frag, check it's size > 0;
- if we're not at the first frag, then for sure
- the offset is > 0. */
+ if we're at the first frag, check it's size > 0;
+ if we're not at the first frag, then for sure
+ the offset is > 0. */
struct frag *const frag_first = seg_info (now_seg)->frchainP->frch_root;
const int add_symbol = (frag_now != frag_first)
|| (frag_now_fix () > 0);
if (add_symbol)
make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
}
+#undef TRANSITION
mapping_state_2 (state, 0);
-#undef TRANSITION
}
/* Same as mapping_state, but MAX_CHARS bytes have already been
/* Check if this literal value is already in the pool. */
for (entry = 0; entry < pool->next_free_entry; entry++)
{
- if ((pool->literals[entry].X_op == exp->X_op)
+ expressionS * litexp = & pool->literals[entry].exp;
+
+ if ((litexp->X_op == exp->X_op)
&& (exp->X_op == O_constant)
- && (pool->literals[entry].X_add_number == exp->X_add_number)
- && (pool->literals[entry].X_unsigned == exp->X_unsigned))
+ && (litexp->X_add_number == exp->X_add_number)
+ && (litexp->X_unsigned == exp->X_unsigned))
break;
- if ((pool->literals[entry].X_op == exp->X_op)
+ if ((litexp->X_op == exp->X_op)
&& (exp->X_op == O_symbol)
- && (pool->literals[entry].X_add_number == exp->X_add_number)
- && (pool->literals[entry].X_add_symbol == exp->X_add_symbol)
- && (pool->literals[entry].X_op_symbol == exp->X_op_symbol))
+ && (litexp->X_add_number == exp->X_add_number)
+ && (litexp->X_add_symbol == exp->X_add_symbol)
+ && (litexp->X_op_symbol == exp->X_op_symbol))
break;
}
return FALSE;
}
- pool->literals[entry] = *exp;
+ pool->literals[entry].exp = *exp;
pool->next_free_entry += 1;
+ if (exp->X_op == O_big)
+ {
+ /* PR 16688: Bignums are held in a single global array. We must
+ copy and preserve that value now, before it is overwritten. */
+ pool->literals[entry].bignum = xmalloc (CHARS_PER_LITTLENUM * exp->X_add_number);
+ memcpy (pool->literals[entry].bignum, generic_bignum,
+ CHARS_PER_LITTLENUM * exp->X_add_number);
+ }
+ else
+ pool->literals[entry].bignum = NULL;
}
exp->X_op = O_symbol;
valueT valu, /* Symbol value. */
fragS * frag) /* Associated fragment. */
{
- unsigned int name_length;
+ size_t name_length;
char *preserved_copy_of_name;
name_length = strlen (name) + 1; /* +1 for \0. */
symbol_table_insert (pool->symbol);
for (entry = 0; entry < pool->next_free_entry; entry++)
- /* First output the expression in the instruction to the pool. */
- emit_expr (&(pool->literals[entry]), size); /* .word|.xword */
+ {
+ expressionS * exp = & pool->literals[entry].exp;
+
+ if (exp->X_op == O_big)
+ {
+ /* PR 16688: Restore the global bignum value. */
+ gas_assert (pool->literals[entry].bignum != NULL);
+ memcpy (generic_bignum, pool->literals[entry].bignum,
+ CHARS_PER_LITTLENUM * exp->X_add_number);
+ }
+
+ /* First output the expression in the instruction to the pool. */
+ emit_expr (exp, size); /* .word|.xword */
+
+ if (exp->X_op == O_big)
+ {
+ free (pool->literals[entry].bignum);
+ pool->literals[entry].bignum = NULL;
+ }
+ }
/* Mark the pool as empty. */
pool->next_free_entry = 0;
return;
}
- if (!need_pass_2)
+ /* Sections are assumed to start aligned. In executable section, there is no
+ MAP_DATA symbol pending. So we only align the address during
+ MAP_DATA --> MAP_INSN transition.
+ For other sections, this is not guaranteed. */
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+ if (!need_pass_2 && subseg_text_p (now_seg) && mapstate == MAP_DATA)
frag_align_code (2, 0);
+
#ifdef OBJ_ELF
mapping_state (MAP_INSN);
#endif
}
#ifdef OBJ_ELF
+/* Emit BFD_RELOC_AARCH64_TLSDESC_ADD on the next ADD instruction. */
+
+static void
+s_tlsdescadd (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ expression (&exp);
+ frag_grow (4);
+ fix_new_aarch64 (frag_now, frag_more (0) - frag_now->fr_literal, 4, &exp, 0,
+ BFD_RELOC_AARCH64_TLSDESC_ADD);
+
+ demand_empty_rest_of_line ();
+}
+
/* Emit BFD_RELOC_AARCH64_TLSDESC_CALL on the next BLR instruction. */
static void
demand_empty_rest_of_line ();
}
+
+/* Emit BFD_RELOC_AARCH64_TLSDESC_LDR on the next LDR instruction. */
+
+static void
+s_tlsdescldr (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ expression (&exp);
+ frag_grow (4);
+ fix_new_aarch64 (frag_now, frag_more (0) - frag_now->fr_literal, 4, &exp, 0,
+ BFD_RELOC_AARCH64_TLSDESC_LDR);
+
+ demand_empty_rest_of_line ();
+}
#endif /* OBJ_ELF */
static void s_aarch64_arch (int);
static void s_aarch64_cpu (int);
+static void s_aarch64_arch_extension (int);
/* This table describes all the machine specific pseudo-ops the assembler
has to support. The fields are:
{"pool", s_ltorg, 0},
{"cpu", s_aarch64_cpu, 0},
{"arch", s_aarch64_arch, 0},
+ {"arch_extension", s_aarch64_arch_extension, 0},
{"inst", s_aarch64_inst, 0},
#ifdef OBJ_ELF
+ {"tlsdescadd", s_tlsdescadd, 0},
{"tlsdesccall", s_tlsdesccall, 0},
+ {"tlsdescldr", s_tlsdescldr, 0},
{"word", s_aarch64_elf_cons, 4},
{"long", s_aarch64_elf_cons, 4},
{"xword", s_aarch64_elf_cons, 8},
| ((imm >> (31 - 7)) & 0x80); /* b[31] -> b[7] */
}
-/* Return TRUE if IMM is a valid floating-point immediate; return FALSE
- otherwise. */
+/* Return TRUE if the single-precision floating-point value encoded in IMM
+ can be expressed in the AArch64 8-bit signed floating-point format with
+ 3-bit exponent and normalized 4 bits of precision; in other words, the
+ floating-point value must be expressable as
+ (+/-) n / 16 * power (2, r)
+ where n and r are integers such that 16 <= n <=31 and -3 <= r <= 4. */
+
static bfd_boolean
aarch64_imm_float_p (uint32_t imm)
{
- /* 3 32222222 2221111111111
+ /* If a single-precision floating-point value has the following bit
+ pattern, it can be expressed in the AArch64 8-bit floating-point
+ format:
+
+ 3 32222222 2221111111111
1 09876543 21098765432109876543210
- n Eeeeeexx xxxx0000000000000000000 */
- uint32_t e;
+ n Eeeeeexx xxxx0000000000000000000
+
+ where n, e and each x are either 0 or 1 independently, with
+ E == ~ e. */
+
+ uint32_t pattern;
+
+ /* Prepare the pattern for 'Eeeeee'. */
+ if (((imm >> 30) & 0x1) == 0)
+ pattern = 0x3e000000;
+ else
+ pattern = 0x40000000;
+
+ return (imm & 0x7ffff) == 0 /* lower 19 bits are 0. */
+ && ((imm & 0x7e000000) == pattern); /* bits 25 - 29 == ~ bit 30. */
+}
+
+/* Like aarch64_imm_float_p but for a double-precision floating-point value.
+
+ Return TRUE if the value encoded in IMM can be expressed in the AArch64
+ 8-bit signed floating-point format with 3-bit exponent and normalized 4
+ bits of precision (i.e. can be used in an FMOV instruction); return the
+ equivalent single-precision encoding in *FPWORD.
+
+ Otherwise return FALSE. */
+
+static bfd_boolean
+aarch64_double_precision_fmovable (uint64_t imm, uint32_t *fpword)
+{
+ /* If a double-precision floating-point value has the following bit
+ pattern, it can be expressed in the AArch64 8-bit floating-point
+ format:
+
+ 6 66655555555 554444444...21111111111
+ 3 21098765432 109876543...098765432109876543210
+ n Eeeeeeeeexx xxxx00000...000000000000000000000
- e = (imm >> 30) & 0x1;
- if (e == 0)
- e = 0x3e000000;
+ where n, e and each x are either 0 or 1 independently, with
+ E == ~ e. */
+
+ uint32_t pattern;
+ uint32_t high32 = imm >> 32;
+
+ /* Lower 32 bits need to be 0s. */
+ if ((imm & 0xffffffff) != 0)
+ return FALSE;
+
+ /* Prepare the pattern for 'Eeeeeeeee'. */
+ if (((high32 >> 30) & 0x1) == 0)
+ pattern = 0x3fc00000;
else
- e = 0x40000000;
- return (imm & 0x7ffff) == 0 /* lower 19 bits are 0 */
- && ((imm & 0x7e000000) == e); /* bits 25-29 = ~ bit 30 */
+ pattern = 0x40000000;
+
+ if ((high32 & 0xffff) == 0 /* bits 32 - 47 are 0. */
+ && (high32 & 0x7fc00000) == pattern) /* bits 54 - 61 == ~ bit 62. */
+ {
+ /* Convert to the single-precision encoding.
+ i.e. convert
+ n Eeeeeeeeexx xxxx00000...000000000000000000000
+ to
+ n Eeeeeexx xxxx0000000000000000000. */
+ *fpword = ((high32 & 0xfe000000) /* nEeeeee. */
+ | (((high32 >> 16) & 0x3f) << 19)); /* xxxxxx. */
+ return TRUE;
+ }
+ else
+ return FALSE;
}
-/* Note: this accepts the floating-point 0 constant. */
+/* Parse a floating-point immediate. Return TRUE on success and return the
+ value in *IMMED in the format of IEEE754 single-precision encoding.
+ *CCP points to the start of the string; DP_P is TRUE when the immediate
+ is expected to be in double-precision (N.B. this only matters when
+ hexadecimal representation is involved).
+
+ N.B. 0.0 is accepted by this function. */
+
static bfd_boolean
-parse_aarch64_imm_float (char **ccp, int *immed)
+parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p)
{
char *str = *ccp;
char *fpnum;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
int found_fpchar = 0;
+ int64_t val = 0;
+ unsigned fpword = 0;
+ bfd_boolean hex_p = FALSE;
skip_past_char (&str, '#');
- /* We must not accidentally parse an integer as a floating-point number. Make
- sure that the value we parse is not an integer by checking for special
- characters '.' or 'e'.
- FIXME: This is a hack that is not very efficient, but doing better is
- tricky because type information isn't in a very usable state at parse
- time. */
fpnum = str;
skip_whitespace (fpnum);
if (strncmp (fpnum, "0x", 2) == 0)
- return FALSE;
+ {
+ /* Support the hexadecimal representation of the IEEE754 encoding.
+ Double-precision is expected when DP_P is TRUE, otherwise the
+ representation should be in single-precision. */
+ if (! parse_constant_immediate (&str, &val))
+ goto invalid_fp;
+
+ if (dp_p)
+ {
+ if (! aarch64_double_precision_fmovable (val, &fpword))
+ goto invalid_fp;
+ }
+ else if ((uint64_t) val > 0xffffffff)
+ goto invalid_fp;
+ else
+ fpword = val;
+
+ hex_p = TRUE;
+ }
else
{
+ /* We must not accidentally parse an integer as a floating-point number.
+ Make sure that the value we parse is not an integer by checking for
+ special characters '.' or 'e'. */
for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++)
if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
{
return FALSE;
}
- if ((str = atof_ieee (str, 's', words)) != NULL)
+ if (! hex_p)
{
- unsigned fpword = 0;
int i;
+ if ((str = atof_ieee (str, 's', words)) == NULL)
+ goto invalid_fp;
+
/* Our FP word must be 32 bits (single-precision FP). */
for (i = 0; i < 32 / LITTLENUM_NUMBER_OF_BITS; i++)
{
fpword <<= LITTLENUM_NUMBER_OF_BITS;
fpword |= words[i];
}
+ }
- if (aarch64_imm_float_p (fpword) || (fpword & 0x7fffffff) == 0)
- *immed = fpword;
- else
- goto invalid_fp;
-
+ if (aarch64_imm_float_p (fpword) || (fpword & 0x7fffffff) == 0)
+ {
+ *immed = fpword;
*ccp = str;
-
return TRUE;
}
{
const char *name;
int pc_rel;
+ bfd_reloc_code_real_type adr_type;
bfd_reloc_code_real_type adrp_type;
bfd_reloc_code_real_type movw_type;
bfd_reloc_code_real_type add_type;
bfd_reloc_code_real_type ldst_type;
+ bfd_reloc_code_real_type ld_literal_type;
};
static struct reloc_table_entry reloc_table[] = {
/* Low 12 bits of absolute address: ADD/i and LDR/STR */
{"lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_ADD_LO12,
- BFD_RELOC_AARCH64_LDST_LO12},
+ BFD_RELOC_AARCH64_LDST_LO12,
+ 0},
/* Higher 21 bits of pc-relative page offset: ADRP */
{"pg_hi21", 1,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_ADR_HI21_PCREL,
0,
0,
+ 0,
0},
/* Higher 21 bits of pc-relative page offset: ADRP, no check */
{"pg_hi21_nc", 1,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL,
0,
0,
+ 0,
0},
/* Most significant bits 0-15 of unsigned address/value: MOVZ */
{"abs_g0", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G0,
0,
+ 0,
0},
/* Most significant bits 0-15 of signed address/value: MOVN/Z */
{"abs_g0_s", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G0_S,
0,
+ 0,
0},
/* Less significant bits 0-15 of address/value: MOVK, no check */
{"abs_g0_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G0_NC,
0,
+ 0,
0},
/* Most significant bits 16-31 of unsigned address/value: MOVZ */
{"abs_g1", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G1,
0,
+ 0,
0},
/* Most significant bits 16-31 of signed address/value: MOVN/Z */
{"abs_g1_s", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G1_S,
0,
+ 0,
0},
/* Less significant bits 16-31 of address/value: MOVK, no check */
{"abs_g1_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G1_NC,
0,
+ 0,
0},
/* Most significant bits 32-47 of unsigned address/value: MOVZ */
{"abs_g2", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G2,
0,
+ 0,
0},
/* Most significant bits 32-47 of signed address/value: MOVN/Z */
{"abs_g2_s", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G2_S,
0,
+ 0,
0},
/* Less significant bits 32-47 of address/value: MOVK, no check */
{"abs_g2_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G2_NC,
0,
+ 0,
0},
/* Most significant bits 48-63 of signed/unsigned address/value: MOVZ */
{"abs_g3", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G3,
- 0,
- 0},
- /* Get to the GOT entry for a symbol. */
- {"got_prel19", 0,
0,
0,
- 0,
- BFD_RELOC_AARCH64_GOT_LD_PREL19},
+ 0},
+
/* Get to the page containing GOT entry for a symbol. */
{"got", 1,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_ADR_GOT_PAGE,
0,
0,
- 0},
+ 0,
+ BFD_RELOC_AARCH64_GOT_LD_PREL19},
+
/* 12 bit offset into the page containing GOT entry for that symbol. */
{"got_lo12", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_LD_GOT_LO12_NC,
+ 0},
+
+ /* 0-15 bits of address/value: MOVk, no check. */
+ {"gotoff_g0_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC,
+ 0,
+ 0,
+ 0},
+
+ /* Most significant bits 16-31 of address/value: MOVZ. */
+ {"gotoff_g1", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_MOVW_GOTOFF_G1,
+ 0,
+ 0,
+ 0},
+
+ /* 15 bit offset into the page containing GOT entry for that symbol. */
+ {"gotoff_lo15", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_LD64_GOTOFF_LO15,
+ 0},
+
+ /* Get to the page containing GOT TLS entry for a symbol */
+ {"gottprel_g0_nc", 0,
+ 0, /* adr_type */
0,
+ BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC,
0,
0,
- BFD_RELOC_AARCH64_LD64_GOT_LO12_NC},
+ 0},
+
+ /* Get to the page containing GOT TLS entry for a symbol */
+ {"gottprel_g1", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1,
+ 0,
+ 0,
+ 0},
/* Get to the page containing GOT TLS entry for a symbol */
{"tlsgd", 0,
+ BFD_RELOC_AARCH64_TLSGD_ADR_PREL21, /* adr_type */
BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21,
0,
0,
+ 0,
0},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"tlsgd_lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC,
+ 0,
+ 0},
+
+ /* Lower 16 bits address/value: MOVk. */
+ {"tlsgd_g0_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC,
+ 0,
+ 0,
+ 0},
+
+ /* Most significant bits 16-31 of address/value: MOVZ. */
+ {"tlsgd_g1", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSGD_MOVW_G1,
+ 0,
+ 0,
0},
/* Get to the page containing GOT TLS entry for a symbol */
{"tlsdesc", 0,
- BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE,
+ BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21, /* adr_type */
+ BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21,
0,
0,
- 0},
+ 0,
+ BFD_RELOC_AARCH64_TLSDESC_LD_PREL19},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"tlsdesc_lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC,
- BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC},
+ BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC,
+ 0},
+
+ /* Get to the page containing GOT TLS entry for a symbol.
+ The same as GD, we allocate two consecutive GOT slots
+ for module index and module offset, the only difference
+ with GD is the module offset should be intialized to
+ zero without any outstanding runtime relocation. */
+ {"tlsldm", 0,
+ BFD_RELOC_AARCH64_TLSLD_ADR_PREL21, /* adr_type */
+ BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21,
+ 0,
+ 0,
+ 0,
+ 0},
+
+ /* 12 bit offset into the page containing GOT TLS entry for a symbol */
+ {"tlsldm_lo12_nc", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC,
+ 0,
+ 0},
+
+ /* 12 bit offset into the module TLS base address. */
+ {"dtprel_lo12", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12,
+ BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12,
+ 0},
+
+ /* Same as dtprel_lo12, no overflow check. */
+ {"dtprel_lo12_nc", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12_NC,
+ BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC,
+ 0},
+
+ /* bits[23:12] of offset to the module TLS base address. */
+ {"dtprel_hi12", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_HI12,
+ 0,
+ 0},
+
+ /* bits[15:0] of offset to the module TLS base address. */
+ {"dtprel_g0", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0,
+ 0,
+ 0,
+ 0},
+
+ /* No overflow check version of BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0. */
+ {"dtprel_g0_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC,
+ 0,
+ 0,
+ 0},
+
+ /* bits[31:16] of offset to the module TLS base address. */
+ {"dtprel_g1", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1,
+ 0,
+ 0,
+ 0},
+
+ /* No overflow check version of BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1. */
+ {"dtprel_g1_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC,
+ 0,
+ 0,
+ 0},
+
+ /* bits[47:32] of offset to the module TLS base address. */
+ {"dtprel_g2", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2,
+ 0,
+ 0,
+ 0},
+
+ /* Lower 16 bit offset into GOT entry for a symbol */
+ {"tlsdesc_off_g0_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC,
+ 0,
+ 0,
+ 0},
+
+ /* Higher 16 bit offset into GOT entry for a symbol */
+ {"tlsdesc_off_g1", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSDESC_OFF_G1,
+ 0,
+ 0,
+ 0},
/* Get to the page containing GOT TLS entry for a symbol */
{"gottprel", 0,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21,
0,
0,
- 0},
+ 0,
+ BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"gottprel_lo12", 0,
+ 0, /* adr_type */
0,
0,
0,
- BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC},
+ BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC,
+ 0},
/* Get tp offset for a symbol. */
{"tprel", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+ 0,
0},
/* Get tp offset for a symbol. */
{"tprel_lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+ 0,
0},
/* Get tp offset for a symbol. */
{"tprel_hi12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12,
+ 0,
0},
/* Get tp offset for a symbol. */
{"tprel_lo12_nc", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
+ 0,
0},
/* Most significant bits 32-47 of address/value: MOVZ. */
{"tprel_g2", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2,
0,
+ 0,
0},
/* Most significant bits 16-31 of address/value: MOVZ. */
{"tprel_g1", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1,
0,
+ 0,
0},
/* Most significant bits 16-31 of address/value: MOVZ, no check. */
{"tprel_g1_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC,
0,
+ 0,
0},
/* Most significant bits 0-15 of address/value: MOVZ. */
{"tprel_g0", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0,
0,
+ 0,
0},
/* Most significant bits 0-15 of address/value: MOVZ, no check. */
{"tprel_g0_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC,
0,
+ 0,
0},
-};
-
-/* Given the address of a pointer pointing to the textual name of a
- relocation as may appear in assembler source, attempt to find its
- details in reloc_table. The pointer will be updated to the character
- after the trailing colon. On failure, NULL will be returned;
- otherwise return the reloc_table_entry. */
-static struct reloc_table_entry *
-find_reloc_table_entry (char **str)
-{
+ /* 15bit offset from got entry to base address of GOT table. */
+ {"gotpage_lo15", 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15,
+ 0},
+
+ /* 14bit offset from got entry to base address of GOT table. */
+ {"gotpage_lo14", 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14,
+ 0},
+};
+
+/* Given the address of a pointer pointing to the textual name of a
+ relocation as may appear in assembler source, attempt to find its
+ details in reloc_table. The pointer will be updated to the character
+ after the trailing colon. On failure, NULL will be returned;
+ otherwise return the reloc_table_entry. */
+
+static struct reloc_table_entry *
+find_reloc_table_entry (char **str)
+{
unsigned int i;
for (i = 0; i < ARRAY_SIZE (reloc_table); i++)
{
if (**str == '\0')
return TRUE;
- /* Otherwise, we have a shifted reloc modifier, so rewind to
+ /* Otherwise, we have a shifted reloc modifier, so rewind to
recover the variable name and continue parsing for the shifter. */
*str = p;
return parse_shifter_operand_imm (str, operand, mode);
skip_past_char (&p, '#');
if (reloc && skip_past_char (&p, ':'))
{
+ bfd_reloc_code_real_type ty;
struct reloc_table_entry *entry;
/* Try to parse a relocation modifier. Anything else is
return FALSE;
}
- if (entry->ldst_type == 0)
+ switch (operand->type)
+ {
+ case AARCH64_OPND_ADDR_PCREL21:
+ /* adr */
+ ty = entry->adr_type;
+ break;
+
+ default:
+ ty = entry->ld_literal_type;
+ break;
+ }
+
+ if (ty == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this "
}
/* #:<reloc_op>:<expr> */
- /* Record the load/store relocation type. */
- inst.reloc.type = entry->ldst_type;
+ /* Record the relocation type. */
+ inst.reloc.type = ty;
inst.reloc.pc_rel = entry->pc_rel;
}
else
static bfd_boolean
parse_half (char **str, int *internal_fixup_p)
{
- char *p, *saved;
- int dummy;
+ char *p = *str;
- p = *str;
skip_past_char (&p, '#');
gas_assert (internal_fixup_p);
else
*internal_fixup_p = 1;
- /* Avoid parsing a register as a general symbol. */
- saved = p;
- if (aarch64_reg_parse_32_64 (&p, 0, 0, &dummy, &dummy) != PARSE_FAIL)
- return FALSE;
- p = saved;
-
if (! my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, 1))
return FALSE;
return o->value;
}
+/* Parse an operand for a PSB barrier. Set *HINT_OPT to the hint-option record
+ return 0 if successful. Otherwise return PARSE_FAIL. */
+
+static int
+parse_barrier_psb (char **str,
+ const struct aarch64_name_value_pair ** hint_opt)
+{
+ char *p, *q;
+ const struct aarch64_name_value_pair *o;
+
+ p = q = *str;
+ while (ISALPHA (*q))
+ q++;
+
+ o = hash_find_n (aarch64_hint_opt_hsh, p, q - p);
+ if (!o)
+ {
+ set_fatal_syntax_error
+ ( _("unknown or missing option to PSB"));
+ return PARSE_FAIL;
+ }
+
+ if (o->value != 0x11)
+ {
+ /* PSB only accepts option name 'CSYNC'. */
+ set_syntax_error
+ (_("the specified option is not accepted for PSB"));
+ return PARSE_FAIL;
+ }
+
+ *str = q;
+ *hint_opt = o;
+ return 0;
+}
+
/* Parse a system register or a PSTATE field name for an MSR/MRS instruction.
Returns the encoding for the option, or PARSE_FAIL.
If IMPLE_DEFINED_P is non-zero, the function will also try to parse the
- implementation defined system register name S3_<op1>_<Cn>_<Cm>_<op2>. */
+ implementation defined system register name S<op0>_<op1>_<Cn>_<Cm>_<op2>.
+
+ If PSTATEFIELD_P is non-zero, the function will parse the name as a PSTATE
+ field, otherwise as a system register.
+*/
static int
-parse_sys_reg (char **str, struct hash_control *sys_regs, int imple_defined_p)
+parse_sys_reg (char **str, struct hash_control *sys_regs,
+ int imple_defined_p, int pstatefield_p)
{
char *p, *q;
char buf[32];
- const struct aarch64_name_value_pair *o;
+ const aarch64_sys_reg *o;
int value;
p = buf;
return PARSE_FAIL;
else
{
- /* Parse S3_<op1>_<Cn>_<Cm>_<op2>, the implementation defined
- registers. */
+ /* Parse S<op0>_<op1>_<Cn>_<Cm>_<op2>. */
unsigned int op0, op1, cn, cm, op2;
- if (sscanf (buf, "s%u_%u_c%u_c%u_%u", &op0, &op1, &cn, &cm, &op2) != 5)
+
+ if (sscanf (buf, "s%u_%u_c%u_c%u_%u", &op0, &op1, &cn, &cm, &op2)
+ != 5)
return PARSE_FAIL;
- /* Register access is encoded as follows:
- op0 op1 CRn CRm op2
- 11 xxx 1x11 xxxx xxx. */
- if (op0 != 3 || op1 > 7 || (cn | 0x4) != 0xf || cm > 15 || op2 > 7)
+ if (op0 > 3 || op1 > 7 || cn > 15 || cm > 15 || op2 > 7)
return PARSE_FAIL;
value = (op0 << 14) | (op1 << 11) | (cn << 7) | (cm << 3) | op2;
}
}
else
- value = o->value;
+ {
+ if (pstatefield_p && !aarch64_pstatefield_supported_p (cpu_variant, o))
+ as_bad (_("selected processor does not support PSTATE field "
+ "name '%s'"), buf);
+ if (!pstatefield_p && !aarch64_sys_reg_supported_p (cpu_variant, o))
+ as_bad (_("selected processor does not support system register "
+ "name '%s'"), buf);
+ if (aarch64_sys_reg_deprecated_p (o))
+ as_warn (_("system register name '%s' is deprecated and may be "
+ "removed in a future release"), buf);
+ value = o->value;
+ }
*str = q;
return value;
if (!o)
return NULL;
+ if (!aarch64_sys_ins_reg_supported_p (cpu_variant, o))
+ as_bad (_("selected processor does not support system register "
+ "name '%s'"), buf);
+
*str = q;
return o;
}
\f
/* Diagnostics on operands errors. */
-/* By default, output one-line error message only.
- Enable the verbose error message by -merror-verbose. */
-static int verbose_error_p = 0;
+/* By default, output verbose error message.
+ Disable the verbose error message by -mno-verbose-error. */
+static int verbose_error_p = 1;
#ifdef DEBUG_AARCH64
/* N.B. this is only for the purpose of debugging. */
static void
output_info (const char *format, ...)
{
- char *file;
+ const char *file;
unsigned int line;
va_list args;
- as_where (&file, &line);
+ file = as_where (&line);
if (file)
{
if (line != 0)
static void
output_operand_error_record (const operand_error_record *record, char *str)
{
- int idx = record->detail.index;
+ const aarch64_operand_error *detail = &record->detail;
+ int idx = detail->index;
const aarch64_opcode *opcode = record->opcode;
- enum aarch64_opnd opd_code = (idx != -1 ? opcode->operands[idx]
+ enum aarch64_opnd opd_code = (idx >= 0 ? opcode->operands[idx]
: AARCH64_OPND_NIL);
- const aarch64_operand_error *detail = &record->detail;
switch (detail->kind)
{
case AARCH64_OPDE_RECOVERABLE:
case AARCH64_OPDE_FATAL_SYNTAX_ERROR:
case AARCH64_OPDE_OTHER_ERROR:
- gas_assert (idx >= 0);
/* Use the prepared error message if there is, otherwise use the
operand description string to describe the error. */
if (detail->error != NULL)
{
- if (detail->index == -1)
+ if (idx < 0)
as_bad (_("%s -- `%s'"), detail->error, str);
else
as_bad (_("%s at operand %d -- `%s'"),
- detail->error, detail->index + 1, str);
+ detail->error, idx + 1, str);
}
else
- as_bad (_("operand %d should be %s -- `%s'"), idx + 1,
+ {
+ gas_assert (idx >= 0);
+ as_bad (_("operand %d should be %s -- `%s'"), idx + 1,
aarch64_get_operand_desc (opd_code), str);
+ }
break;
case AARCH64_OPDE_INVALID_VARIANT:
break;
case AARCH64_OPDE_OUT_OF_RANGE:
- as_bad (_("%s out of range %d to %d at operand %d -- `%s'"),
- detail->error ? detail->error : _("immediate value"),
- detail->data[0], detail->data[1], detail->index + 1, str);
+ if (detail->data[0] != detail->data[1])
+ as_bad (_("%s out of range %d to %d at operand %d -- `%s'"),
+ detail->error ? detail->error : _("immediate value"),
+ detail->data[0], detail->data[1], idx + 1, str);
+ else
+ as_bad (_("%s expected to be %d at operand %d -- `%s'"),
+ detail->error ? detail->error : _("immediate value"),
+ detail->data[0], idx + 1, str);
break;
case AARCH64_OPDE_REG_LIST:
if (detail->data[0] == 1)
as_bad (_("invalid number of registers in the list; "
"only 1 register is expected at operand %d -- `%s'"),
- detail->index + 1, str);
+ idx + 1, str);
else
as_bad (_("invalid number of registers in the list; "
"%d registers are expected at operand %d -- `%s'"),
- detail->data[0], detail->index + 1, str);
+ detail->data[0], idx + 1, str);
break;
case AARCH64_OPDE_UNALIGNED:
as_bad (_("immediate value should be a multiple of "
"%d at operand %d -- `%s'"),
- detail->data[0], detail->index + 1, str);
+ detail->data[0], idx + 1, str);
break;
default:
/* Element size in bytes indexed by neon_el_type. */
const unsigned char ele_size[5]
= {1, 2, 4, 8, 16};
+ const unsigned int ele_base [5] =
+ {
+ AARCH64_OPND_QLF_V_8B,
+ AARCH64_OPND_QLF_V_2H,
+ AARCH64_OPND_QLF_V_2S,
+ AARCH64_OPND_QLF_V_1D,
+ AARCH64_OPND_QLF_V_1Q
+ };
if (!vectype->defined || vectype->type == NT_invtype)
goto vectype_conversion_fail;
/* Vector register. */
int reg_size = ele_size[vectype->type] * vectype->width;
unsigned offset;
- if (reg_size != 16 && reg_size != 8)
+ unsigned shift;
+ if (reg_size != 16 && reg_size != 8 && reg_size != 4)
goto vectype_conversion_fail;
- /* The conversion is calculated based on the relation of the order of
- qualifiers to the vector element size and vector register size. */
- offset = (vectype->type == NT_q)
- ? 8 : (vectype->type << 1) + (reg_size >> 4);
- gas_assert (offset <= 8);
- return AARCH64_OPND_QLF_V_8B + offset;
+
+ /* The conversion is by calculating the offset from the base operand
+ qualifier for the vector type. The operand qualifiers are regular
+ enough that the offset can established by shifting the vector width by
+ a vector-type dependent amount. */
+ shift = 0;
+ if (vectype->type == NT_b)
+ shift = 4;
+ else if (vectype->type == NT_h || vectype->type == NT_s)
+ shift = 2;
+ else if (vectype->type >= NT_d)
+ shift = 1;
+ else
+ gas_assert (0);
+
+ offset = ele_base [vectype->type] + (vectype->width >> shift);
+ gas_assert (AARCH64_OPND_QLF_V_8B <= offset
+ && offset <= AARCH64_OPND_QLF_V_1Q);
+ return offset;
}
vectype_conversion_fail:
case BFD_RELOC_AARCH64_MOVW_G0_S:
case BFD_RELOC_AARCH64_MOVW_G1_S:
case BFD_RELOC_AARCH64_MOVW_G2_S:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G1:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
set_syntax_error
(_("the specified relocation type is not allowed for MOVK"));
switch (inst.reloc.type)
{
case BFD_RELOC_AARCH64_MOVW_G0:
- case BFD_RELOC_AARCH64_MOVW_G0_S:
case BFD_RELOC_AARCH64_MOVW_G0_NC:
+ case BFD_RELOC_AARCH64_MOVW_G0_S:
+ case BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC:
+ case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
shift = 0;
break;
case BFD_RELOC_AARCH64_MOVW_G1:
- case BFD_RELOC_AARCH64_MOVW_G1_S:
case BFD_RELOC_AARCH64_MOVW_G1_NC:
+ case BFD_RELOC_AARCH64_MOVW_G1_S:
+ case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G1:
+ case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
shift = 16;
break;
case BFD_RELOC_AARCH64_MOVW_G2:
- case BFD_RELOC_AARCH64_MOVW_G2_S:
case BFD_RELOC_AARCH64_MOVW_G2_NC:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
if (is32)
{
static inline bfd_reloc_code_real_type
ldst_lo12_determine_real_reloc_type (void)
{
- int logsz;
+ unsigned logsz;
enum aarch64_opnd_qualifier opd0_qlf = inst.base.operands[0].qualifier;
enum aarch64_opnd_qualifier opd1_qlf = inst.base.operands[1].qualifier;
- const bfd_reloc_code_real_type reloc_ldst_lo12[5] = {
- BFD_RELOC_AARCH64_LDST8_LO12, BFD_RELOC_AARCH64_LDST16_LO12,
- BFD_RELOC_AARCH64_LDST32_LO12, BFD_RELOC_AARCH64_LDST64_LO12,
+ const bfd_reloc_code_real_type reloc_ldst_lo12[3][5] = {
+ {
+ BFD_RELOC_AARCH64_LDST8_LO12,
+ BFD_RELOC_AARCH64_LDST16_LO12,
+ BFD_RELOC_AARCH64_LDST32_LO12,
+ BFD_RELOC_AARCH64_LDST64_LO12,
BFD_RELOC_AARCH64_LDST128_LO12
+ },
+ {
+ BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12,
+ BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12,
+ BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12,
+ BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12,
+ BFD_RELOC_AARCH64_NONE
+ },
+ {
+ BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC,
+ BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC,
+ BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC,
+ BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC,
+ BFD_RELOC_AARCH64_NONE
+ }
};
- gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12);
+ gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12
+ || inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12
+ || (inst.reloc.type
+ == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC));
gas_assert (inst.base.opcode->operands[1] == AARCH64_OPND_ADDR_UIMM12);
if (opd1_qlf == AARCH64_OPND_QLF_NIL)
gas_assert (opd1_qlf != AARCH64_OPND_QLF_NIL);
logsz = get_logsz (aarch64_get_qualifier_esize (opd1_qlf));
- gas_assert (logsz >= 0 && logsz <= 4);
+ if (inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12
+ || inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC)
+ gas_assert (logsz <= 3);
+ else
+ gas_assert (logsz <= 4);
- return reloc_ldst_lo12[logsz];
+ /* In reloc.c, these pseudo relocation types should be defined in similar
+ order as above reloc_ldst_lo12 array. Because the array index calcuation
+ below relies on this. */
+ return reloc_ldst_lo12[inst.reloc.type - BFD_RELOC_AARCH64_LDST_LO12][logsz];
}
/* Check whether a register list REGINFO is valid. The registers must be
case AARCH64_OPND_Rs:
case AARCH64_OPND_Ra:
case AARCH64_OPND_Rt_SYS:
+ case AARCH64_OPND_PAIRREG:
po_int_reg_or_fail (1, 0);
break;
bfd_boolean res1 = FALSE, res2 = FALSE;
/* N.B. -0.0 will be rejected; although -0.0 shouldn't be rejected,
it is probably not worth the effort to support it. */
- if (!(res1 = parse_aarch64_imm_float (&str, &qfloat))
+ if (!(res1 = parse_aarch64_imm_float (&str, &qfloat, FALSE))
&& !(res2 = parse_constant_immediate (&str, &val)))
goto failure;
if ((res1 && qfloat == 0) || (res2 && val == 0))
case AARCH64_OPND_IMM_MOV:
{
char *saved = str;
- if (reg_name_p (str, REG_TYPE_R_Z_SP))
+ if (reg_name_p (str, REG_TYPE_R_Z_SP) ||
+ reg_name_p (str, REG_TYPE_VN))
goto failure;
str = saved;
po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
case AARCH64_OPND_SIMD_FPIMM:
{
int qfloat;
- if (! parse_aarch64_imm_float (&str, &qfloat))
+ bfd_boolean dp_p
+ = (aarch64_get_qualifier_esize (inst.base.operands[0].qualifier)
+ == 8);
+ if (! parse_aarch64_imm_float (&str, &qfloat, dp_p))
goto failure;
if (qfloat == 0)
{
break;
case AARCH64_OPND_COND:
+ case AARCH64_OPND_COND1:
info->cond = hash_find_n (aarch64_cond_hsh, str, 2);
str += 2;
if (info->cond == NULL)
set_syntax_error (_("invalid condition"));
goto failure;
}
+ else if (operands[i] == AARCH64_OPND_COND1
+ && (info->cond->value & 0xe) == 0xe)
+ {
+ /* Not allow AL or NV. */
+ set_default_error ();
+ goto failure;
+ }
break;
case AARCH64_OPND_ADDR_ADRP:
}
if (inst.reloc.type == BFD_RELOC_UNUSED)
aarch64_set_gas_internal_fixup (&inst.reloc, info, 1);
- else if (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12)
+ else if (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12
+ || (inst.reloc.type
+ == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12)
+ || (inst.reloc.type
+ == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC))
inst.reloc.type = ldst_lo12_determine_real_reloc_type ();
/* Leave qualifier to be determined by libopcodes. */
break;
break;
case AARCH64_OPND_SYSREG:
- if ((val = parse_sys_reg (&str, aarch64_sys_regs_hsh, 1))
+ if ((val = parse_sys_reg (&str, aarch64_sys_regs_hsh, 1, 0))
== PARSE_FAIL)
{
set_syntax_error (_("unknown or missing system register name"));
break;
case AARCH64_OPND_PSTATEFIELD:
- if ((val = parse_sys_reg (&str, aarch64_pstatefield_hsh, 0))
+ if ((val = parse_sys_reg (&str, aarch64_pstatefield_hsh, 0, 1))
== PARSE_FAIL)
{
set_syntax_error (_("unknown or missing PSTATE field name"));
inst.base.operands[i].prfop = aarch64_prfops + val;
break;
+ case AARCH64_OPND_BARRIER_PSB:
+ val = parse_barrier_psb (&str, &(info->hint_option));
+ if (val == PARSE_FAIL)
+ goto failure;
+ break;
+
default:
as_fatal (_("unhandled operand code %d"), operands[i]);
}
if (! backtrack_pos)
goto parse_operands_return;
+ {
+ /* We reach here because this operand is marked as optional, and
+ either no operand was supplied or the operand was supplied but it
+ was syntactically incorrect. In the latter case we report an
+ error. In the former case we perform a few more checks before
+ dropping through to the code to insert the default operand. */
+
+ char *tmp = backtrack_pos;
+ char endchar = END_OF_INSN;
+
+ if (i != (aarch64_num_of_operands (opcode) - 1))
+ endchar = ',';
+ skip_past_char (&tmp, ',');
+
+ if (*tmp != endchar)
+ /* The user has supplied an operand in the wrong format. */
+ goto parse_operands_return;
+
+ /* Make sure there is not a comma before the optional operand.
+ For example the fifth operand of 'sys' is optional:
+
+ sys #0,c0,c0,#0, <--- wrong
+ sys #0,c0,c0,#0 <--- correct. */
+ if (comma_skipped_p && i && endchar == END_OF_INSN)
+ {
+ set_fatal_syntax_error
+ (_("unexpected comma before the omitted optional operand"));
+ goto parse_operands_return;
+ }
+ }
+
/* Reaching here means we are dealing with an optional operand that is
omitted from the assembly line. */
gas_assert (optional_operand_p (opcode, i));
str = backtrack_pos;
backtrack_pos = 0;
- /* If this is the last operand that is optional and omitted, but without
- the presence of a comma. */
- if (i && comma_skipped_p && i == aarch64_num_of_operands (opcode) - 1)
- {
- set_fatal_syntax_error
- (_("unexpected comma before the omitted optional operand"));
- goto parse_operands_return;
- }
-
/* Clear any error record after the omitted optional operand has been
successfully handled. */
clear_error ();
}
}
break;
- case asimdimm:
- /* Allow MOVI V0.16B, 97, LSL 0, although the preferred architectural
- syntax requires that the LSL shifter can only be used when the
- destination register has the shape of 4H, 8H, 2S or 4S. */
- if (op == OP_V_MOVI_B && operands[1].shifter.kind == AARCH64_MOD_LSL
- && (operands[0].qualifier == AARCH64_OPND_QLF_V_8B
- || operands[0].qualifier == AARCH64_OPND_QLF_V_16B))
- {
- if (operands[1].shifter.amount != 0)
- {
- record_operand_error (opcode, 1,
- AARCH64_OPDE_OTHER_ERROR,
- _("shift amount non-zero"));
- return FALSE;
- }
- operands[1].shifter.kind = AARCH64_MOD_NONE;
- operands[1].qualifier = AARCH64_OPND_QLF_NIL;
- }
- break;
case log_shift:
case bitfield:
/* UXT[BHW] Wd, Wn
return TRUE;
}
+/* Check for loads and stores that will cause unpredictable behavior. */
+
+static void
+warn_unpredictable_ldst (aarch64_instruction *instr, char *str)
+{
+ aarch64_inst *base = &instr->base;
+ const aarch64_opcode *opcode = base->opcode;
+ const aarch64_opnd_info *opnds = base->operands;
+ switch (opcode->iclass)
+ {
+ case ldst_pos:
+ case ldst_imm9:
+ case ldst_unscaled:
+ case ldst_unpriv:
+ /* Loading/storing the base register is unpredictable if writeback. */
+ if ((aarch64_get_operand_class (opnds[0].type)
+ == AARCH64_OPND_CLASS_INT_REG)
+ && opnds[0].reg.regno == opnds[1].addr.base_regno
+ && opnds[1].addr.base_regno != REG_SP
+ && opnds[1].addr.writeback)
+ as_warn (_("unpredictable transfer with writeback -- `%s'"), str);
+ break;
+ case ldstpair_off:
+ case ldstnapair_offs:
+ case ldstpair_indexed:
+ /* Loading/storing the base register is unpredictable if writeback. */
+ if ((aarch64_get_operand_class (opnds[0].type)
+ == AARCH64_OPND_CLASS_INT_REG)
+ && (opnds[0].reg.regno == opnds[2].addr.base_regno
+ || opnds[1].reg.regno == opnds[2].addr.base_regno)
+ && opnds[2].addr.base_regno != REG_SP
+ && opnds[2].addr.writeback)
+ as_warn (_("unpredictable transfer with writeback -- `%s'"), str);
+ /* Load operations must load different registers. */
+ if ((opcode->opcode & (1 << 22))
+ && opnds[0].reg.regno == opnds[1].reg.regno)
+ as_warn (_("unpredictable load of register pair -- `%s'"), str);
+ break;
+ default:
+ break;
+ }
+}
+
/* A wrapper function to interface with libopcodes on encoding and
record the error message if there is any.
init_operand_error_report ();
+ /* Sections are assumed to start aligned. In executable section, there is no
+ MAP_DATA symbol pending. So we only align the address during
+ MAP_DATA --> MAP_INSN transition.
+ For other sections, this is not guaranteed. */
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+ if (!need_pass_2 && subseg_text_p (now_seg) && mapstate == MAP_DATA)
+ frag_align_code (2, 0);
+
saved_cond = inst.cond;
reset_aarch64_instruction (&inst);
inst.cond = saved_cond;
dump_opcode_operands (opcode);
#endif /* DEBUG_AARCH64 */
- /* Check that this instruction is supported for this CPU. */
- if (!opcode->avariant
- || !AARCH64_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant))
- {
- as_bad (_("selected processor does not support `%s'"), str);
- return;
- }
-
mapping_state (MAP_INSN);
inst_base = &inst.base;
&& programmer_friendly_fixup (&inst)
&& do_encode (inst_base->opcode, &inst.base, &inst_base->value))
{
+ /* Check that this instruction is supported for this CPU. */
+ if (!opcode->avariant
+ || !AARCH64_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant))
+ {
+ as_bad (_("selected processor does not support `%s'"), str);
+ return;
+ }
+
+ warn_unpredictable_ldst (&inst, str);
+
if (inst.reloc.type == BFD_RELOC_UNUSED
|| !inst.reloc.need_libopcodes_p)
output_inst (NULL);
}
/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
- of an rs_align_code fragment. */
+ of an rs_align_code fragment.
+
+ Here we fill the frag with the appropriate info for padding the
+ output stream. The resulting frag will consist of a fixed (fr_fix)
+ and of a repeating (fr_var) part.
+
+ The fixed content is always emitted before the repeating content and
+ these two parts are used as follows in constructing the output:
+ - the fixed part will be used to align to a valid instruction word
+ boundary, in case that we start at a misaligned address; as no
+ executable instruction can live at the misaligned location, we
+ simply fill with zeros;
+ - the variable part will be used to cover the remaining padding and
+ we fill using the AArch64 NOP instruction.
+
+ Note that the size of a RS_ALIGN_CODE fragment is always 7 to provide
+ enough storage space for up to 3 bytes for padding the back to a valid
+ instruction alignment and exactly 4 bytes to store the NOP pattern. */
void
aarch64_handle_align (fragS * fragP)
int bytes, fix, noop_size;
char *p;
- const char *noop;
if (fragP->fr_type != rs_align_code)
return;
bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
p = fragP->fr_literal + fragP->fr_fix;
- fix = 0;
-
- if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
- bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
#ifdef OBJ_ELF
gas_assert (fragP->tc_frag_data.recorded);
#endif
- noop = aarch64_noop;
noop_size = sizeof (aarch64_noop);
- fragP->fr_var = noop_size;
- if (bytes & (noop_size - 1))
+ fix = bytes & (noop_size - 1);
+ if (fix)
{
- fix = bytes & (noop_size - 1);
#ifdef OBJ_ELF
insert_data_mapping_symbol (MAP_INSN, fragP->fr_fix, fragP, fix);
#endif
memset (p, 0, fix);
p += fix;
- bytes -= fix;
- }
-
- while (bytes >= noop_size)
- {
- memcpy (p, noop, noop_size);
- p += noop_size;
- bytes -= noop_size;
- fix += noop_size;
+ fragP->fr_fix += fix;
}
- fragP->fr_fix += fix;
-}
-
-/* Called from md_do_align. Used to create an alignment
- frag in a code section. */
-
-void
-aarch64_frag_align_code (int n, int max)
-{
- char *p;
-
- /* We assume that there will never be a requirement
- to support alignments greater than x bytes. */
- if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
- as_fatal (_
- ("alignments greater than %d bytes not supported in .text sections"),
- MAX_MEM_FOR_RS_ALIGN_CODE + 1);
-
- p = frag_var (rs_align_code,
- MAX_MEM_FOR_RS_ALIGN_CODE,
- 1,
- (relax_substateT) max,
- (symbolS *) NULL, (offsetT) n, (char *) NULL);
- *p = 0;
+ if (noop_size)
+ memcpy (p, aarch64_noop, noop_size);
+ fragP->fr_var = noop_size;
}
/* Perform target specific initialisation of a frag.
/* Record a mapping symbol for alignment frags. We will delete this
later if the alignment ends up empty. */
if (!fragP->tc_frag_data.recorded)
+ fragP->tc_frag_data.recorded = 1;
+
+ switch (fragP->fr_type)
{
- fragP->tc_frag_data.recorded = 1;
- switch (fragP->fr_type)
- {
- case rs_align:
- case rs_align_test:
- case rs_fill:
- mapping_state_2 (MAP_DATA, max_chars);
- break;
- case rs_align_code:
- mapping_state_2 (MAP_INSN, max_chars);
- break;
- default:
- break;
- }
+ case rs_align:
+ case rs_align_test:
+ case rs_fill:
+ mapping_state_2 (MAP_DATA, max_chars);
+ break;
+ case rs_align_code:
+ mapping_state_2 (MAP_INSN, max_chars);
+ break;
+ default:
+ break;
}
}
\f
case REG_TYPE_SP_64:
case REG_TYPE_R_32:
case REG_TYPE_R_64:
+ return reg->number;
+
case REG_TYPE_FP_B:
case REG_TYPE_FP_H:
case REG_TYPE_FP_S:
case REG_TYPE_FP_D:
case REG_TYPE_FP_Q:
- return reg->number;
+ return reg->number + 64;
+
default:
break;
}
return -1;
}
+/* Implement DWARF2_ADDR_SIZE. */
+
+int
+aarch64_dwarf2_addr_size (void)
+{
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+ if (ilp32_p)
+ return 4;
+#endif
+ return bfd_arch_bits_per_address (stdoutput) / 8;
+}
+
/* MD interface: Symbol and relocation handling. */
/* Return the address within the segment that a PC-relative fixup is
break;
case BFD_RELOC_AARCH64_LD_LO19_PCREL:
- if (value & 3)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("pc-relative load offset not word aligned"));
- if (signed_overflow (value, 21))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("pc-relative load offset out of range"));
if (fixP->fx_done || !seg->use_rela_p)
{
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pc-relative load offset not word aligned"));
+ if (signed_overflow (value, 21))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pc-relative load offset out of range"));
insn = get_aarch64_insn (buf);
insn |= encode_ld_lit_ofs_19 (value >> 2);
put_aarch64_insn (buf, insn);
break;
case BFD_RELOC_AARCH64_ADR_LO21_PCREL:
- if (signed_overflow (value, 21))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("pc-relative address offset out of range"));
if (fixP->fx_done || !seg->use_rela_p)
{
+ if (signed_overflow (value, 21))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pc-relative address offset out of range"));
insn = get_aarch64_insn (buf);
insn |= encode_adr_imm (value);
put_aarch64_insn (buf, insn);
break;
case BFD_RELOC_AARCH64_BRANCH19:
- if (value & 3)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("conditional branch target not word aligned"));
- if (signed_overflow (value, 21))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("conditional branch out of range"));
if (fixP->fx_done || !seg->use_rela_p)
{
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch target not word aligned"));
+ if (signed_overflow (value, 21))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch out of range"));
insn = get_aarch64_insn (buf);
insn |= encode_cond_branch_ofs_19 (value >> 2);
put_aarch64_insn (buf, insn);
break;
case BFD_RELOC_AARCH64_TSTBR14:
- if (value & 3)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("conditional branch target not word aligned"));
- if (signed_overflow (value, 16))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("conditional branch out of range"));
if (fixP->fx_done || !seg->use_rela_p)
{
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch target not word aligned"));
+ if (signed_overflow (value, 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch out of range"));
insn = get_aarch64_insn (buf);
insn |= encode_tst_branch_ofs_14 (value >> 2);
put_aarch64_insn (buf, insn);
}
break;
- case BFD_RELOC_AARCH64_JUMP26:
case BFD_RELOC_AARCH64_CALL26:
- if (value & 3)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch target not word aligned"));
- if (signed_overflow (value, 28))
- as_bad_where (fixP->fx_file, fixP->fx_line, _("branch out of range"));
+ case BFD_RELOC_AARCH64_JUMP26:
if (fixP->fx_done || !seg->use_rela_p)
{
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch target not word aligned"));
+ if (signed_overflow (value, 28))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
insn = get_aarch64_insn (buf);
insn |= encode_branch_ofs_26 (value >> 2);
put_aarch64_insn (buf, insn);
break;
case BFD_RELOC_AARCH64_MOVW_G0:
- case BFD_RELOC_AARCH64_MOVW_G0_S:
case BFD_RELOC_AARCH64_MOVW_G0_NC:
+ case BFD_RELOC_AARCH64_MOVW_G0_S:
+ case BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC:
scale = 0;
goto movw_common;
case BFD_RELOC_AARCH64_MOVW_G1:
- case BFD_RELOC_AARCH64_MOVW_G1_S:
case BFD_RELOC_AARCH64_MOVW_G1_NC:
+ case BFD_RELOC_AARCH64_MOVW_G1_S:
+ case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
scale = 16;
goto movw_common;
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC:
+ scale = 0;
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ goto movw_common;
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
+ scale = 16;
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ goto movw_common;
case BFD_RELOC_AARCH64_MOVW_G2:
- case BFD_RELOC_AARCH64_MOVW_G2_S:
case BFD_RELOC_AARCH64_MOVW_G2_NC:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
scale = 32;
goto movw_common;
case BFD_RELOC_AARCH64_MOVW_G3:
case BFD_RELOC_AARCH64_MOVW_G1:
case BFD_RELOC_AARCH64_MOVW_G2:
case BFD_RELOC_AARCH64_MOVW_G3:
+ case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
if (unsigned_overflow (value, scale + 16))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("unsigned value out of range"));
}
break;
- case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC:
+ fixP->fx_r_type = (ilp32_p
+ ? BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC
+ : BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC);
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC:
+ fixP->fx_r_type = (ilp32_p
+ ? BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC
+ : BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC);
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
+ case BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD_PREL19:
case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G1:
case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC:
case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19:
+ case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21:
+ case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2:
case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
- case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE:
- case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
- case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
S_SET_THREAD_LOCAL (fixP->fx_addsy);
/* Should always be exported to object file, see
aarch64_force_relocation(). */
gas_assert (seg->use_rela_p);
break;
- case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
- case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+ case BFD_RELOC_AARCH64_LD_GOT_LO12_NC:
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ fixP->fx_r_type = (ilp32_p
+ ? BFD_RELOC_AARCH64_LD32_GOT_LO12_NC
+ : BFD_RELOC_AARCH64_LD64_GOT_LO12_NC);
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
case BFD_RELOC_AARCH64_ADD_LO12:
- case BFD_RELOC_AARCH64_LDST8_LO12:
+ case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+ case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+ case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+ case BFD_RELOC_AARCH64_GOT_LD_PREL19:
+ case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
+ case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LDST128_LO12:
case BFD_RELOC_AARCH64_LDST16_LO12:
case BFD_RELOC_AARCH64_LDST32_LO12:
case BFD_RELOC_AARCH64_LDST64_LO12:
- case BFD_RELOC_AARCH64_LDST128_LO12:
- case BFD_RELOC_AARCH64_GOT_LD_PREL19:
- case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
- case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LDST8_LO12:
/* Should always be exported to object file, see
aarch64_force_relocation(). */
gas_assert (!fixP->fx_done);
break;
case BFD_RELOC_AARCH64_TLSDESC_ADD:
- case BFD_RELOC_AARCH64_TLSDESC_LDR:
case BFD_RELOC_AARCH64_TLSDESC_CALL:
+ case BFD_RELOC_AARCH64_TLSDESC_LDR:
+ break;
+
+ case BFD_RELOC_UNUSED:
+ /* An error will already have been reported. */
break;
default:
even if the symbol is extern or weak. */
return 0;
- case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_LD_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC:
+ /* Pseudo relocs that need to be fixed up according to
+ ilp32_p. */
+ return 0;
+
+ case BFD_RELOC_AARCH64_ADD_LO12:
+ case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+ case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+ case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+ case BFD_RELOC_AARCH64_GOT_LD_PREL19:
+ case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
+ case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LDST128_LO12:
+ case BFD_RELOC_AARCH64_LDST16_LO12:
+ case BFD_RELOC_AARCH64_LDST32_LO12:
+ case BFD_RELOC_AARCH64_LDST64_LO12:
+ case BFD_RELOC_AARCH64_LDST8_LO12:
+ case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
+ case BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD_PREL19:
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_OFF_G1:
case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC:
+ case BFD_RELOC_AARCH64_TLSGD_MOVW_G1:
case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC:
case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19:
+ case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_HI12:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21:
+ case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2:
case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
- case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE:
- case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
- case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
- case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
- case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
- case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
- case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
- case BFD_RELOC_AARCH64_ADD_LO12:
- case BFD_RELOC_AARCH64_LDST8_LO12:
- case BFD_RELOC_AARCH64_LDST16_LO12:
- case BFD_RELOC_AARCH64_LDST32_LO12:
- case BFD_RELOC_AARCH64_LDST64_LO12:
- case BFD_RELOC_AARCH64_LDST128_LO12:
- case BFD_RELOC_AARCH64_GOT_LD_PREL19:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
/* Always leave these relocations for the linker. */
return 1;
const char *
elf64_aarch64_target_format (void)
{
+ if (strcmp (TARGET_OS, "cloudabi") == 0)
+ {
+ /* FIXME: What to do for ilp32_p ? */
+ return target_big_endian ? "elf64-bigaarch64-cloudabi" : "elf64-littleaarch64-cloudabi";
+ }
if (target_big_endian)
- return "elf64-bigaarch64";
+ return ilp32_p ? "elf32-bigaarch64" : "elf64-bigaarch64";
else
- return "elf64-littleaarch64";
+ return ilp32_p ? "elf32-littleaarch64" : "elf64-littleaarch64";
}
void
|| (aarch64_reg_hsh = hash_new ()) == NULL
|| (aarch64_barrier_opt_hsh = hash_new ()) == NULL
|| (aarch64_nzcv_hsh = hash_new ()) == NULL
- || (aarch64_pldop_hsh = hash_new ()) == NULL)
+ || (aarch64_pldop_hsh = hash_new ()) == NULL
+ || (aarch64_hint_opt_hsh = hash_new ()) == NULL)
as_fatal (_("virtual memory exhausted"));
fill_instruction_hash_table ();
aarch64_pstatefields[i].name,
(void *) (aarch64_pstatefields + i));
- for (i = 0; aarch64_sys_regs_ic[i].template != NULL; i++)
+ for (i = 0; aarch64_sys_regs_ic[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_ic_hsh,
- aarch64_sys_regs_ic[i].template,
+ aarch64_sys_regs_ic[i].name,
(void *) (aarch64_sys_regs_ic + i));
- for (i = 0; aarch64_sys_regs_dc[i].template != NULL; i++)
+ for (i = 0; aarch64_sys_regs_dc[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_dc_hsh,
- aarch64_sys_regs_dc[i].template,
+ aarch64_sys_regs_dc[i].name,
(void *) (aarch64_sys_regs_dc + i));
- for (i = 0; aarch64_sys_regs_at[i].template != NULL; i++)
+ for (i = 0; aarch64_sys_regs_at[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_at_hsh,
- aarch64_sys_regs_at[i].template,
+ aarch64_sys_regs_at[i].name,
(void *) (aarch64_sys_regs_at + i));
- for (i = 0; aarch64_sys_regs_tlbi[i].template != NULL; i++)
+ for (i = 0; aarch64_sys_regs_tlbi[i].name != NULL; i++)
checked_hash_insert (aarch64_sys_regs_tlbi_hsh,
- aarch64_sys_regs_tlbi[i].template,
+ aarch64_sys_regs_tlbi[i].name,
(void *) (aarch64_sys_regs_tlbi + i));
for (i = 0; i < ARRAY_SIZE (reg_names); i++)
for (i = 0; i < ARRAY_SIZE (aarch64_prfops); i++)
{
const char* name = aarch64_prfops[i].name;
- /* Skip 0011x, 01xxx, 1011x and 11xxx - the unallocated hint encodings
- as a 5-bit immediate #uimm5. */
- if ((i & 0xf) >= 6)
+ /* Skip the unallocated hint encodings. */
+ if (name == NULL)
continue;
checked_hash_insert (aarch64_pldop_hsh, name,
(void *) (aarch64_prfops + i));
(void *) (aarch64_prfops + i));
}
+ for (i = 0; aarch64_hint_options[i].name != NULL; i++)
+ {
+ const char* name = aarch64_hint_options[i].name;
+
+ checked_hash_insert (aarch64_hint_opt_hsh, name,
+ (void *) (aarch64_hint_options + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_pldop_hsh, get_upper_str (name),
+ (void *) (aarch64_hint_options + i));
+ }
+
/* Set the cpu variant based on the command-line options. */
if (!mcpu_cpu_opt)
mcpu_cpu_opt = march_cpu_opt;
cpu_variant = *mcpu_cpu_opt;
/* Record the CPU type. */
- mach = bfd_mach_aarch64;
+ mach = ilp32_p ? bfd_mach_aarch64_ilp32 : bfd_mach_aarch64;
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
}
#endif /* DEBUG_AARCH64 */
{"mverbose-error", N_("output verbose error messages"), &verbose_error_p, 1,
NULL},
+ {"mno-verbose-error", N_("do not output verbose error messages"),
+ &verbose_error_p, 0, NULL},
{NULL, NULL, NULL, 0, NULL}
};
recognized by GCC. */
static const struct aarch64_cpu_option_table aarch64_cpus[] = {
{"all", AARCH64_ANY, NULL},
- {"cortex-a53", AARCH64_ARCH_V8, "Cortex-A53"},
- {"cortex-a57", AARCH64_ARCH_V8, "Cortex-A57"},
+ {"cortex-a35", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A35"},
+ {"cortex-a53", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A53"},
+ {"cortex-a57", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A57"},
+ {"cortex-a72", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A72"},
+ {"exynos-m1", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO),
+ "Samsung Exynos M1"},
+ {"qdf24xx", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO),
+ "Qualcomm QDF24XX"},
+ {"thunderx", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO),
+ "Cavium ThunderX"},
+ /* The 'xgene-1' name is an older name for 'xgene1', which was used
+ in earlier releases and is superseded by 'xgene1' in all
+ tools. */
+ {"xgene-1", AARCH64_ARCH_V8, "APM X-Gene 1"},
+ {"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"},
+ {"xgene2", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "APM X-Gene 2"},
{"generic", AARCH64_ARCH_V8, NULL},
- /* These two are example CPUs supported in GCC, once we have real
- CPUs they will be removed. */
- {"example-1", AARCH64_ARCH_V8, NULL},
- {"example-2", AARCH64_ARCH_V8, NULL},
-
{NULL, AARCH64_ARCH_NONE, NULL}
};
static const struct aarch64_arch_option_table aarch64_archs[] = {
{"all", AARCH64_ANY},
{"armv8-a", AARCH64_ARCH_V8},
+ {"armv8.1-a", AARCH64_ARCH_V8_1},
+ {"armv8.2-a", AARCH64_ARCH_V8_2},
{NULL, AARCH64_ARCH_NONE}
};
};
static const struct aarch64_option_cpu_value_table aarch64_features[] = {
+ {"crc", AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0)},
{"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0)},
{"fp", AARCH64_FEATURE (AARCH64_FEATURE_FP, 0)},
+ {"lse", AARCH64_FEATURE (AARCH64_FEATURE_LSE, 0)},
{"simd", AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)},
+ {"pan", AARCH64_FEATURE (AARCH64_FEATURE_PAN, 0)},
+ {"lor", AARCH64_FEATURE (AARCH64_FEATURE_LOR, 0)},
+ {"rdma", AARCH64_FEATURE (AARCH64_FEATURE_SIMD
+ | AARCH64_FEATURE_RDMA, 0)},
+ {"fp16", AARCH64_FEATURE (AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_FP, 0)},
+ {"profile", AARCH64_FEATURE (AARCH64_FEATURE_PROFILE, 0)},
{NULL, AARCH64_ARCH_NONE}
};
};
static int
-aarch64_parse_features (char *str, const aarch64_feature_set **opt_p)
+aarch64_parse_features (char *str, const aarch64_feature_set **opt_p,
+ bfd_boolean ext_only)
{
/* We insist on extensions being added before being removed. We achieve
this by using the ADDING_VALUE variable to indicate whether we are
while (str != NULL && *str != 0)
{
const struct aarch64_option_cpu_value_table *opt;
- char *ext;
+ char *ext = NULL;
int optlen;
- if (*str != '+')
+ if (!ext_only)
{
- as_bad (_("invalid architectural extension"));
- return 0;
- }
+ if (*str != '+')
+ {
+ as_bad (_("invalid architectural extension"));
+ return 0;
+ }
- str++;
- ext = strchr (str, '+');
+ ext = strchr (++str, '+');
+ }
if (ext != NULL)
optlen = ext - str;
{
mcpu_cpu_opt = &opt->value;
if (ext != NULL)
- return aarch64_parse_features (ext, &mcpu_cpu_opt);
+ return aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE);
return 1;
}
{
march_cpu_opt = &opt->value;
if (ext != NULL)
- return aarch64_parse_features (ext, &march_cpu_opt);
+ return aarch64_parse_features (ext, &march_cpu_opt, FALSE);
return 1;
}
return 0;
}
+/* ABIs. */
+struct aarch64_option_abi_value_table
+{
+ char *name;
+ enum aarch64_abi_type value;
+};
+
+static const struct aarch64_option_abi_value_table aarch64_abis[] = {
+ {"ilp32", AARCH64_ABI_ILP32},
+ {"lp64", AARCH64_ABI_LP64},
+ {NULL, 0}
+};
+
+static int
+aarch64_parse_abi (char *str)
+{
+ const struct aarch64_option_abi_value_table *opt;
+ size_t optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing abi name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = aarch64_abis; opt->name != NULL; opt++)
+ if (strlen (opt->name) == optlen && strncmp (str, opt->name, optlen) == 0)
+ {
+ aarch64_abi = opt->value;
+ return 1;
+ }
+
+ as_bad (_("unknown abi `%s'\n"), str);
+ return 0;
+}
+
static struct aarch64_long_option_table aarch64_long_opts[] = {
+#ifdef OBJ_ELF
+ {"mabi=", N_("<abi name>\t specify for ABI <abi name>"),
+ aarch64_parse_abi, NULL},
+#endif /* OBJ_ELF */
{"mcpu=", N_("<cpu name>\t assemble for CPU <cpu name>"),
aarch64_parse_cpu, NULL},
{"march=", N_("<arch name>\t assemble for architecture <arch name>"),
{
mcpu_cpu_opt = &opt->value;
if (ext != NULL)
- if (!aarch64_parse_features (ext, &mcpu_cpu_opt))
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE))
return;
cpu_variant = *mcpu_cpu_opt;
{
mcpu_cpu_opt = &opt->value;
if (ext != NULL)
- if (!aarch64_parse_features (ext, &mcpu_cpu_opt))
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE))
return;
cpu_variant = *mcpu_cpu_opt;
ignore_rest_of_line ();
}
+/* Parse a .arch_extension directive. */
+
+static void
+s_aarch64_arch_extension (int ignored ATTRIBUTE_UNUSED)
+{
+ char saved_char;
+ char *ext = input_line_pointer;;
+
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt, TRUE))
+ return;
+
+ cpu_variant = *mcpu_cpu_opt;
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+}
+
/* Copy symbol information. */
void