/* tc-aarch64.c -- Assemble for the AArch64 ISA
- Copyright (C) 2009-2019 Free Software Foundation, Inc.
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GAS.
set_error (AARCH64_OPDE_FATAL_SYNTAX_ERROR, error);
}
\f
-/* Number of littlenums required to hold an extended precision number. */
-#define MAX_LITTLENUMS 6
-
/* Return value for certain parsers when the parsing fails; those parsers
return the information of the parsed result, e.g. register number, on
success. */
present. */
#define COND_ALWAYS 0x10
-typedef struct
-{
- const char *template;
- unsigned long value;
-} asm_barrier_opt;
-
typedef struct
{
const char *template;
/* Some well known registers that we refer to directly elsewhere. */
#define REG_SP 31
+#define REG_ZR 31
/* Instructions take 4 bytes in the object file. */
#define INSN_SIZE 4
-static struct hash_control *aarch64_ops_hsh;
-static struct hash_control *aarch64_cond_hsh;
-static struct hash_control *aarch64_shift_hsh;
-static struct hash_control *aarch64_sys_regs_hsh;
-static struct hash_control *aarch64_pstatefield_hsh;
-static struct hash_control *aarch64_sys_regs_ic_hsh;
-static struct hash_control *aarch64_sys_regs_dc_hsh;
-static struct hash_control *aarch64_sys_regs_at_hsh;
-static struct hash_control *aarch64_sys_regs_tlbi_hsh;
-static struct hash_control *aarch64_sys_regs_sr_hsh;
-static struct hash_control *aarch64_reg_hsh;
-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;
+static htab_t aarch64_ops_hsh;
+static htab_t aarch64_cond_hsh;
+static htab_t aarch64_shift_hsh;
+static htab_t aarch64_sys_regs_hsh;
+static htab_t aarch64_pstatefield_hsh;
+static htab_t aarch64_sys_regs_ic_hsh;
+static htab_t aarch64_sys_regs_dc_hsh;
+static htab_t aarch64_sys_regs_at_hsh;
+static htab_t aarch64_sys_regs_tlbi_hsh;
+static htab_t aarch64_sys_regs_sr_hsh;
+static htab_t aarch64_reg_hsh;
+static htab_t aarch64_barrier_opt_hsh;
+static htab_t aarch64_nzcv_hsh;
+static htab_t aarch64_pldop_hsh;
+static htab_t aarch64_hint_opt_hsh;
/* Stuff needed to resolve the label ambiguity
As:
/* As in 0f12.456 */
/* or 0d1.2345e12 */
-const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+const char FLT_CHARS[] = "rRsSfFdDxXeEpPhH";
/* Prefix character that indicates the start of an immediate value. */
#define is_immediate_prefix(C) ((C) == '#')
const char *
md_atof (int type, char *litP, int *sizeP)
{
+ /* If this is a bfloat16 type, then parse it slightly differently -
+ as it does not follow the IEEE standard exactly. */
+ if (type == 'b')
+ {
+ char * t;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ FLONUM_TYPE generic_float;
+
+ t = atof_ieee_detail (input_line_pointer, 1, 8, words, &generic_float);
+
+ if (t)
+ input_line_pointer = t;
+ else
+ return _("invalid floating point number");
+
+ switch (generic_float.sign)
+ {
+ /* Is +Inf. */
+ case 'P':
+ words[0] = 0x7f80;
+ break;
+
+ /* Is -Inf. */
+ case 'N':
+ words[0] = 0xff80;
+ break;
+
+ /* Is NaN. */
+ /* bfloat16 has two types of NaN - quiet and signalling.
+ Quiet NaN has bit[6] == 1 && faction != 0, whereas
+ signalling Nan's have bit[0] == 0 && fraction != 0.
+ Chose this specific encoding as it is the same form
+ as used by other IEEE 754 encodings in GAS. */
+ case 0:
+ words[0] = 0x7fff;
+ break;
+
+ default:
+ break;
+ }
+
+ *sizeP = 2;
+
+ md_number_to_chars (litP, (valueT) words[0], sizeof (LITTLENUM_TYPE));
+
+ return NULL;
+ }
+
return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
p++;
while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
- reg = (reg_entry *) hash_find_n (aarch64_reg_hsh, start, p - start);
+ reg = (reg_entry *) str_hash_find_n (aarch64_reg_hsh, start, p - start);
if (!reg)
return NULL;
return FALSE;
}
-elt_size:
+ elt_size:
switch (TOLOWER (*ptr))
{
case 'b':
reg_entry *new;
const char *name;
- if ((new = hash_find (aarch64_reg_hsh, str)) != 0)
+ if ((new = str_hash_find (aarch64_reg_hsh, str)) != 0)
{
if (new->builtin)
as_warn (_("ignoring attempt to redefine built-in register '%s'"),
new->type = type;
new->builtin = FALSE;
- if (hash_insert (aarch64_reg_hsh, name, (void *) new))
- abort ();
+ str_hash_insert (aarch64_reg_hsh, name, new, 0);
return new;
}
if (*oldname == '\0')
return FALSE;
- old = hash_find (aarch64_reg_hsh, oldname);
+ old = str_hash_find (aarch64_reg_hsh, oldname);
if (!old)
{
as_warn (_("unknown register '%s' -- .req ignored"), oldname);
as_bad (_("invalid syntax for .unreq directive"));
else
{
- reg_entry *reg = hash_find (aarch64_reg_hsh, name);
+ reg_entry *reg = str_hash_find (aarch64_reg_hsh, name);
if (!reg)
as_bad (_("unknown register alias '%s'"), name);
char *p;
char *nbuf;
- hash_delete (aarch64_reg_hsh, name, FALSE);
+ str_hash_delete (aarch64_reg_hsh, name);
free ((char *) reg->name);
free (reg);
nbuf = strdup (name);
for (p = nbuf; *p; p++)
*p = TOUPPER (*p);
- reg = hash_find (aarch64_reg_hsh, nbuf);
+ reg = str_hash_find (aarch64_reg_hsh, nbuf);
if (reg)
{
- hash_delete (aarch64_reg_hsh, nbuf, FALSE);
+ str_hash_delete (aarch64_reg_hsh, nbuf);
free ((char *) reg->name);
free (reg);
}
for (p = nbuf; *p; p++)
*p = TOLOWER (*p);
- reg = hash_find (aarch64_reg_hsh, nbuf);
+ reg = str_hash_find (aarch64_reg_hsh, nbuf);
if (reg)
{
- hash_delete (aarch64_reg_hsh, nbuf, FALSE);
+ str_hash_delete (aarch64_reg_hsh, nbuf);
free ((char *) reg->name);
free (reg);
}
abort ();
}
- symbolP = symbol_new (symname, now_seg, value, frag);
+ symbolP = symbol_new (symname, now_seg, frag, value);
symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
/* Save the mapping symbols for future reference. Also check that
if (pool->symbol == NULL)
{
pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
- (valueT) 0, &zero_address_frag);
+ &zero_address_frag, 0);
pool->id = latest_pool_num++;
}
demand_empty_rest_of_line ();
}
+/* Mark symbol that it follows a variant PCS convention. */
+
+static void
+s_variant_pcs (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ symbolS *sym;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ c = get_symbol_name (&name);
+ if (!*name)
+ as_bad (_("Missing symbol name in directive"));
+ sym = symbol_find_or_make (name);
+ restore_line_pointer (c);
+ demand_empty_rest_of_line ();
+ bfdsym = symbol_get_bfdsym (sym);
+ elfsym = elf_symbol_from (bfdsym);
+ gas_assert (elfsym);
+ elfsym->internal_elf_sym.st_other |= STO_AARCH64_VARIANT_PCS;
+}
#endif /* OBJ_ELF */
/* Output a 32-bit word, but mark as an instruction. */
{"long", s_aarch64_elf_cons, 4},
{"xword", s_aarch64_elf_cons, 8},
{"dword", s_aarch64_elf_cons, 8},
+ {"variant_pcs", s_variant_pcs, 0},
#endif
+ {"float16", float_cons, 'h'},
+ {"bfloat16", float_cons, 'b'},
{0, 0, 0}
};
\f
return FALSE;
skip_whitespace (str);
- if (*str == ',' || is_end_of_line[(unsigned int) *str])
+ if (*str == ',' || is_end_of_line[(unsigned char) *str])
return TRUE;
return FALSE;
*ccp = str;
return TRUE;
-invalid_fp:
+ invalid_fp:
set_fatal_syntax_error (_("invalid floating-point constant"));
return FALSE;
}
return FALSE;
}
- shift_op = hash_find_n (aarch64_shift_hsh, *str, p - *str);
+ shift_op = str_hash_find_n (aarch64_shift_hsh, *str, p - *str);
if (shift_op == NULL)
{
[base,Xm,SXTX {#imm}]
[base,Wm,(S|U)XTW {#imm}]
Pre-indexed
+ [base]! // in ldraa/ldrab exclusive
[base,#imm]!
Post-indexed
[base],#imm
[base,Zm.D,(S|U)XTW {#imm}] // ignores top 32 bits of Zm.D elements
[Zn.S,#imm]
[Zn.D,#imm]
+ [Zn.S{, Xm}]
[Zn.S,Zm.S{,LSL #imm}] // in ADR
[Zn.D,Zm.D{,LSL #imm}] // in ADR
[Zn.D,Zm.D,(S|U)XTW {#imm}] // in ADR
return FALSE;
}
/* We only accept:
+ [base,Xm] # For vector plus scalar SVE2 indexing.
[base,Xm{,LSL #imm}]
[base,Xm,SXTX {#imm}]
[base,Wm,(S|U)XTW {#imm}] */
return FALSE;
}
if (aarch64_get_qualifier_esize (*base_qualifier)
- != aarch64_get_qualifier_esize (*offset_qualifier))
+ != aarch64_get_qualifier_esize (*offset_qualifier)
+ && (operand->type != AARCH64_OPND_SVE_ADDR_ZX
+ || *base_qualifier != AARCH64_OPND_QLF_S_S
+ || *offset_qualifier != AARCH64_OPND_QLF_X))
{
set_syntax_error (_("offset has different size from base"));
return FALSE;
}
/* If at this point neither .preind nor .postind is set, we have a
- bare [Rn]{!}; reject [Rn]! accept [Rn] as a shorthand for [Rn,#0]. */
+ bare [Rn]{!}; only accept [Rn]! as a shorthand for [Rn,#0]! for ldraa and
+ ldrab, accept [Rn] as a shorthand for [Rn,#0].
+ For SVE2 vector plus scalar offsets, allow [Zn.<T>] as shorthand for
+ [Zn.<T>, xzr]. */
if (operand->addr.preind == 0 && operand->addr.postind == 0)
{
if (operand->addr.writeback)
{
- /* Reject [Rn]! */
- set_syntax_error (_("missing offset in the pre-indexed address"));
- return FALSE;
+ if (operand->type == AARCH64_OPND_ADDR_SIMM10)
+ {
+ /* Accept [Rn]! as a shorthand for [Rn,#0]! */
+ operand->addr.offset.is_reg = 0;
+ operand->addr.offset.imm = 0;
+ operand->addr.preind = 1;
+ }
+ else
+ {
+ /* Reject [Rn]! */
+ set_syntax_error (_("missing offset in the pre-indexed address"));
+ return FALSE;
+ }
+ }
+ else
+ {
+ operand->addr.preind = 1;
+ if (operand->type == AARCH64_OPND_SVE_ADDR_ZX)
+ {
+ operand->addr.offset.is_reg = 1;
+ operand->addr.offset.regno = REG_ZR;
+ *offset_qualifier = AARCH64_OPND_QLF_X;
+ }
+ else
+ {
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
+ }
}
-
- operand->addr.preind = 1;
- inst.reloc.exp.X_op = O_constant;
- inst.reloc.exp.X_add_number = 0;
}
*str = p;
while (ISALNUM (*q))
q++;
- o = hash_find_n (aarch64_pldop_hsh, p, q - p);
+ o = str_hash_find_n (aarch64_pldop_hsh, p, q - p);
if (!o)
return PARSE_FAIL;
parse_barrier (char **str)
{
char *p, *q;
- const asm_barrier_opt *o;
+ const struct aarch64_name_value_pair *o;
p = q = *str;
while (ISALPHA (*q))
q++;
- o = hash_find_n (aarch64_barrier_opt_hsh, p, q - p);
+ o = str_hash_find_n (aarch64_barrier_opt_hsh, p, q - p);
if (!o)
return PARSE_FAIL;
while (ISALPHA (*q))
q++;
- o = hash_find_n (aarch64_hint_opt_hsh, p, q - p);
+ o = str_hash_find_n (aarch64_hint_opt_hsh, p, q - p);
if (!o)
{
set_fatal_syntax_error
- ( _("unknown or missing option to PSB"));
+ ( _("unknown or missing option to PSB/TSB"));
return PARSE_FAIL;
}
{
/* PSB only accepts option name 'CSYNC'. */
set_syntax_error
- (_("the specified option is not accepted for PSB"));
+ (_("the specified option is not accepted for PSB/TSB"));
return PARSE_FAIL;
}
return 0;
}
+/* Parse an operand for CSR (CSRE instruction). */
+
+static int
+parse_csr_operand (char **str)
+{
+ char *p, *q;
+
+ p = q = *str;
+ while (ISALPHA (*q))
+ q++;
+
+ /* Instruction has only one operand PDEC which encodes Rt field of the
+ operation to 0b11111. */
+ if (strcasecmp(p, "pdec"))
+ {
+ set_syntax_error (_("CSR instruction accepts only PDEC"));
+ return PARSE_FAIL;
+ }
+
+ *str = q;
+ return 0;
+}
+
/* Parse an operand for BTI. Set *HINT_OPT to the hint-option record
return 0 if successful. Otherwise return PARSE_FAIL. */
while (ISALPHA (*q))
q++;
- o = hash_find_n (aarch64_hint_opt_hsh, p, q - p);
+ o = str_hash_find_n (aarch64_hint_opt_hsh, p, q - p);
if (!o)
{
set_fatal_syntax_error
*/
static int
-parse_sys_reg (char **str, struct hash_control *sys_regs,
+parse_sys_reg (char **str, htab_t sys_regs,
int imple_defined_p, int pstatefield_p,
uint32_t* flags)
{
char *p, *q;
- char buf[32];
+ char buf[AARCH64_MAX_SYSREG_NAME_LEN];
const aarch64_sys_reg *o;
int value;
p = buf;
for (q = *str; ISALNUM (*q) || *q == '_'; q++)
- if (p < buf + 31)
+ if (p < buf + (sizeof (buf) - 1))
*p++ = TOLOWER (*q);
*p = '\0';
- /* Assert that BUF be large enough. */
- gas_assert (p - buf == q - *str);
- o = hash_find (sys_regs, buf);
+ /* If the name is longer than AARCH64_MAX_SYSREG_NAME_LEN then it cannot be a
+ valid system register. This is enforced by construction of the hash
+ table. */
+ if (p - buf != q - *str)
+ return PARSE_FAIL;
+
+ o = str_hash_find (sys_regs, buf);
if (!o)
{
if (!imple_defined_p)
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))
+ if (!pstatefield_p
+ && !aarch64_sys_ins_reg_supported_p (cpu_variant, o->name,
+ o->value, o->flags, o->features))
as_bad (_("selected processor does not support system register "
"name '%s'"), buf);
- if (aarch64_sys_reg_deprecated_p (o))
+ if (aarch64_sys_reg_deprecated_p (o->flags))
as_warn (_("system register name '%s' is deprecated and may be "
"removed in a future release"), buf);
value = o->value;
for the option, or NULL. */
static const aarch64_sys_ins_reg *
-parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs)
+parse_sys_ins_reg (char **str, htab_t sys_ins_regs)
{
char *p, *q;
- char buf[32];
+ char buf[AARCH64_MAX_SYSREG_NAME_LEN];
const aarch64_sys_ins_reg *o;
p = buf;
for (q = *str; ISALNUM (*q) || *q == '_'; q++)
- if (p < buf + 31)
+ if (p < buf + (sizeof (buf) - 1))
*p++ = TOLOWER (*q);
*p = '\0';
- o = hash_find (sys_ins_regs, buf);
+ /* If the name is longer than AARCH64_MAX_SYSREG_NAME_LEN then it cannot be a
+ valid system register. This is enforced by construction of the hash
+ table. */
+ if (p - buf != q - *str)
+ return NULL;
+
+ o = str_hash_find (sys_ins_regs, buf);
if (!o)
return NULL;
- if (!aarch64_sys_ins_reg_supported_p (cpu_variant, o))
+ if (!aarch64_sys_ins_reg_supported_p (cpu_variant,
+ o->name, o->value, o->flags, 0))
as_bad (_("selected processor does not support system register "
"name '%s'"), buf);
+ if (aarch64_sys_reg_deprecated_p (o->flags))
+ as_warn (_("system register name '%s' is deprecated and may be "
+ "removed in a future release"), buf);
*str = q;
return o;
static fixS *
fix_new_aarch64 (fragS * frag,
int where,
- short int size, expressionS * exp, int pc_rel, int reloc)
+ short int size,
+ expressionS * exp,
+ int pc_rel,
+ int reloc)
{
fixS *new_fix;
/* Generate the operand string in STR. */
aarch64_print_operand (str, sizeof (str), 0, opcode, opnds, i, NULL, NULL,
- NULL);
+ NULL, cpu_variant);
/* Delimiter. */
if (str[0] != '\0')
{
unsigned char *where = (unsigned char *) buf;
uint32_t result;
- result = (where[0] | (where[1] << 8) | (where[2] << 16) | (where[3] << 24));
+ result = ((where[0] | (where[1] << 8) | (where[2] << 16)
+ | ((uint32_t) where[3] << 24)));
return result;
}
{
templates *templ = NULL;
- templ = hash_find_n (aarch64_ops_hsh, start, len);
+ templ = str_hash_find_n (aarch64_ops_hsh, start, len);
return templ;
}
/* Handle a possible condition. */
if (dot)
{
- cond = hash_find_n (aarch64_cond_hsh, dot + 1, end - dot - 1);
+ cond = str_hash_find_n (aarch64_cond_hsh, dot + 1, end - dot - 1);
if (cond)
{
inst.cond = cond->value;
if (vectype->type == NT_b && vectype->width == 4)
return AARCH64_OPND_QLF_S_4B;
+ /* Special case S_2H. */
+ if (vectype->type == NT_h && vectype->width == 2)
+ return AARCH64_OPND_QLF_S_2H;
+
/* Vector element register. */
return AARCH64_OPND_QLF_S_B + vectype->type;
}
return offset;
}
-vectype_conversion_fail:
+ vectype_conversion_fail:
first_error (_("bad vector arrangement type"));
return AARCH64_OPND_QLF_NIL;
}
case AARCH64_OPND_Rm:
case AARCH64_OPND_Rt:
case AARCH64_OPND_Rt2:
+ case AARCH64_OPND_Rt_LS64:
+ case AARCH64_OPND_Rt_SP:
case AARCH64_OPND_Rs:
case AARCH64_OPND_Ra:
case AARCH64_OPND_Rt_SYS:
case AARCH64_OPND_Rt2:
case AARCH64_OPND_Rs:
case AARCH64_OPND_Ra:
+ case AARCH64_OPND_Rt_LS64:
case AARCH64_OPND_Rt_SYS:
case AARCH64_OPND_PAIRREG:
case AARCH64_OPND_SVE_Rm:
po_int_reg_or_fail (REG_TYPE_R_Z);
+
+ /* In LS64 load/store instructions Rt register number must be even
+ and <=22. */
+ if (operands[i] == AARCH64_OPND_Rt_LS64)
+ {
+ /* We've already checked if this is valid register.
+ This will check if register number (Rt) is not undefined for LS64
+ instructions:
+ if Rt<4:3> == '11' || Rt<0> == '1' then UNDEFINED. */
+ if ((info->reg.regno & 0x18) == 0x18 || (info->reg.regno & 0x01) == 0x01)
+ {
+ set_syntax_error (_("invalid Rt register number in 64-byte load/store"));
+ goto failure;
+ }
+ }
break;
case AARCH64_OPND_Rd_SP:
case AARCH64_OPND_Rn_SP:
+ case AARCH64_OPND_Rt_SP:
case AARCH64_OPND_SVE_Rn_SP:
case AARCH64_OPND_Rm_SP:
po_int_reg_or_fail (REG_TYPE_R_SP);
case AARCH64_OPND_SVE_Zm3_INDEX:
case AARCH64_OPND_SVE_Zm3_22_INDEX:
+ case AARCH64_OPND_SVE_Zm3_11_INDEX:
+ case AARCH64_OPND_SVE_Zm4_11_INDEX:
case AARCH64_OPND_SVE_Zm4_INDEX:
case AARCH64_OPND_SVE_Zn_INDEX:
reg_type = REG_TYPE_ZN;
val = parse_vector_reg_list (&str, reg_type, &vectype);
if (val == PARSE_FAIL)
goto failure;
+
if (! reg_list_valid_p (val, /* accept_alternate */ 0))
{
set_fatal_syntax_error (_("invalid register list"));
goto failure;
}
+
+ if (vectype.width != 0 && *str != ',')
+ {
+ set_fatal_syntax_error
+ (_("expected element type rather than vector type"));
+ goto failure;
+ }
+
info->reglist.first_regno = (val >> 2) & 0x1f;
info->reglist.num_regs = (val & 0x3) + 1;
}
case AARCH64_OPND_CCMP_IMM:
case AARCH64_OPND_SIMM5:
case AARCH64_OPND_FBITS:
+ case AARCH64_OPND_TME_UIMM16:
case AARCH64_OPND_UIMM4:
case AARCH64_OPND_UIMM4_ADDG:
case AARCH64_OPND_UIMM10:
case AARCH64_OPND_SVE_LIMM_MOV:
case AARCH64_OPND_SVE_SHLIMM_PRED:
case AARCH64_OPND_SVE_SHLIMM_UNPRED:
+ case AARCH64_OPND_SVE_SHLIMM_UNPRED_22:
case AARCH64_OPND_SVE_SHRIMM_PRED:
case AARCH64_OPND_SVE_SHRIMM_UNPRED:
+ case AARCH64_OPND_SVE_SHRIMM_UNPRED_22:
case AARCH64_OPND_SVE_SIMM5:
case AARCH64_OPND_SVE_SIMM5B:
case AARCH64_OPND_SVE_SIMM6:
case AARCH64_OPND_IMM_ROT3:
case AARCH64_OPND_SVE_IMM_ROT1:
case AARCH64_OPND_SVE_IMM_ROT2:
+ case AARCH64_OPND_SVE_IMM_ROT3:
po_imm_nc_or_fail ();
info->imm.value = val;
break;
break;
case AARCH64_OPND_EXCEPTION:
+ case AARCH64_OPND_UNDEFINED:
po_misc_or_fail (parse_immediate_expression (&str, &inst.reloc.exp,
imm_reg_type));
assign_imm_if_const_or_fixup_later (&inst.reloc, info,
case AARCH64_OPND_NZCV:
{
- const asm_nzcv *nzcv = hash_find_n (aarch64_nzcv_hsh, str, 4);
+ const asm_nzcv *nzcv = str_hash_find_n (aarch64_nzcv_hsh, str, 4);
if (nzcv != NULL)
{
str += 4;
do
str++;
while (ISALPHA (*str));
- info->cond = hash_find_n (aarch64_cond_hsh, start, str - start);
+ info->cond = str_hash_find_n (aarch64_cond_hsh, start, str - start);
if (info->cond == NULL)
{
set_syntax_error (_("invalid condition"));
break;
case AARCH64_OPND_SVE_ADDR_RI_S4x16:
+ case AARCH64_OPND_SVE_ADDR_RI_S4x32:
case AARCH64_OPND_SVE_ADDR_RI_S4xVL:
case AARCH64_OPND_SVE_ADDR_RI_S4x2xVL:
case AARCH64_OPND_SVE_ADDR_RI_S4x3xVL:
info->qualifier = offset_qualifier;
goto regoff_addr;
+ case AARCH64_OPND_SVE_ADDR_ZX:
+ /* [Zn.<T>{, <Xm>}]. */
+ po_misc_or_fail (parse_sve_address (&str, info, &base_qualifier,
+ &offset_qualifier));
+ /* Things to check:
+ base_qualifier either S_S or S_D
+ offset_qualifier must be X
+ */
+ if ((base_qualifier != AARCH64_OPND_QLF_S_S
+ && base_qualifier != AARCH64_OPND_QLF_S_D)
+ || offset_qualifier != AARCH64_OPND_QLF_X)
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ info->qualifier = base_qualifier;
+ if (!info->addr.offset.is_reg || info->addr.pcrel
+ || !info->addr.preind || info->addr.writeback
+ || info->shifter.operator_present != 0)
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ info->shifter.kind = AARCH64_MOD_LSL;
+ break;
+
+
case AARCH64_OPND_SVE_ADDR_ZI_U5:
case AARCH64_OPND_SVE_ADDR_ZI_U5x2:
case AARCH64_OPND_SVE_ADDR_ZI_U5x4:
case AARCH64_OPND_SYSREG_TLBI:
inst.base.operands[i].sysins_op =
parse_sys_ins_reg (&str, aarch64_sys_regs_tlbi_hsh);
-sys_reg_ins:
+ sys_reg_ins:
if (inst.base.operands[i].sysins_op == NULL)
{
set_fatal_syntax_error ( _("unknown or missing operation name"));
backtrack_pos = 0;
goto failure;
}
+ if (val != PARSE_FAIL
+ && operands[i] == AARCH64_OPND_BARRIER)
+ {
+ /* Regular barriers accept options CRm (C0-C15).
+ DSB nXS barrier variant accepts values > 15. */
+ if (val < 0 || val > 15)
+ {
+ set_syntax_error (_("the specified option is not accepted in DSB"));
+ goto failure;
+ }
+ }
/* This is an extension to accept a 0..15 immediate. */
if (val == PARSE_FAIL)
po_imm_or_fail (0, 15);
info->barrier = aarch64_barrier_options + val;
break;
+ case AARCH64_OPND_BARRIER_DSB_NXS:
+ val = parse_barrier (&str);
+ if (val != PARSE_FAIL)
+ {
+ /* DSB nXS barrier variant accept only <option>nXS qualifiers. */
+ if (!(val == 16 || val == 20 || val == 24 || val == 28))
+ {
+ set_syntax_error (_("the specified option is not accepted in DSB"));
+ /* Turn off backtrack as this optional operand is present. */
+ backtrack_pos = 0;
+ goto failure;
+ }
+ }
+ else
+ {
+ /* DSB nXS barrier variant accept 5-bit unsigned immediate, with
+ possible values 16, 20, 24 or 28 , encoded as val<3:2>. */
+ if (! parse_constant_immediate (&str, &val, imm_reg_type))
+ goto failure;
+ if (!(val == 16 || val == 20 || val == 24 || val == 28))
+ {
+ set_syntax_error (_("immediate value must be 16, 20, 24, 28"));
+ goto failure;
+ }
+ }
+ /* Option index is encoded as 2-bit value in val<3:2>. */
+ val = (val >> 2) - 4;
+ info->barrier = aarch64_barrier_dsb_nxs_options + val;
+ break;
+
case AARCH64_OPND_PRFOP:
val = parse_pldop (&str);
/* This is an extension to accept a 0..31 immediate. */
goto failure;
break;
+ case AARCH64_OPND_CSRE_CSR:
+ val = parse_csr_operand (&str);
+ if (val == PARSE_FAIL)
+ goto failure;
+ break;
+
default:
as_fatal (_("unhandled operand code %d"), operands[i]);
}
inst.base.operands[i].present = 1;
continue;
-failure:
+ failure:
/* The parse routine should already have set the error, but in case
not, set a default one here. */
if (! error_p ())
(_("unexpected characters following instruction"));
}
-parse_operands_return:
+ parse_operands_return:
if (error_p ())
{
/* PR 21809: Do not set a mapping state for debug sections
- it just confuses other tools. */
- if (bfd_get_section_flags (NULL, now_seg) & SEC_DEBUGGING)
+ if (bfd_section_flags (now_seg) & SEC_DEBUGGING)
return;
switch (fragP->fr_type)
as_bad (_("GOT already in the symbol table"));
GOT_symbol = symbol_new (name, undefined_section,
- (valueT) 0, &zero_address_frag);
+ &zero_address_frag, 0);
}
return GOT_symbol;
switch (opnd)
{
case AARCH64_OPND_EXCEPTION:
+ case AARCH64_OPND_UNDEFINED:
if (unsigned_overflow (value, 16))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("immediate out of range"));
insn = get_aarch64_insn (buf);
- insn |= encode_svc_imm (value);
+ insn |= (opnd == AARCH64_OPND_EXCEPTION) ? encode_svc_imm (value) : value;
put_aarch64_insn (buf, insn);
break;
break;
}
-apply_fix_return:
+ apply_fix_return:
/* Free the allocated the struct aarch64_inst.
N.B. currently there are very limited number of fix-up types actually use
this field, so the impact on the performance should be minimal . */
- if (fixP->tc_fix_data.inst != NULL)
- free (fixP->tc_fix_data.inst);
+ free (fixP->tc_fix_data.inst);
return;
}
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";
- }
+#ifdef TE_CLOUDABI
+ /* FIXME: What to do for ilp32_p ? */
+ if (target_big_endian)
+ return "elf64-bigaarch64-cloudabi";
+ else
+ return "elf64-littleaarch64-cloudabi";
+#else
if (target_big_endian)
return ilp32_p ? "elf32-bigaarch64" : "elf64-bigaarch64";
else
return ilp32_p ? "elf32-littleaarch64" : "elf64-littleaarch64";
+#endif
}
void
}
static void
-checked_hash_insert (struct hash_control *table, const char *key, void *value)
+checked_hash_insert (htab_t table, const char *key, void *value)
{
- const char *hash_err;
+ str_hash_insert (table, key, value, 0);
+}
- hash_err = hash_insert (table, key, value);
- if (hash_err)
- printf ("Internal Error: Can't hash %s\n", key);
+static void
+sysreg_hash_insert (htab_t table, const char *key, void *value)
+{
+ gas_assert (strlen (key) < AARCH64_MAX_SYSREG_NAME_LEN);
+ checked_hash_insert (table, key, value);
}
static void
while (opcode->name != NULL)
{
templates *templ, *new_templ;
- templ = hash_find (aarch64_ops_hsh, opcode->name);
+ templ = str_hash_find (aarch64_ops_hsh, opcode->name);
new_templ = XNEW (templates);
new_templ->opcode = opcode;
unsigned mach;
unsigned int i;
- if ((aarch64_ops_hsh = hash_new ()) == NULL
- || (aarch64_cond_hsh = hash_new ()) == NULL
- || (aarch64_shift_hsh = hash_new ()) == NULL
- || (aarch64_sys_regs_hsh = hash_new ()) == NULL
- || (aarch64_pstatefield_hsh = hash_new ()) == NULL
- || (aarch64_sys_regs_ic_hsh = hash_new ()) == NULL
- || (aarch64_sys_regs_dc_hsh = hash_new ()) == NULL
- || (aarch64_sys_regs_at_hsh = hash_new ()) == NULL
- || (aarch64_sys_regs_tlbi_hsh = hash_new ()) == NULL
- || (aarch64_sys_regs_sr_hsh = hash_new ()) == NULL
- || (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_hint_opt_hsh = hash_new ()) == NULL)
- as_fatal (_("virtual memory exhausted"));
+ aarch64_ops_hsh = str_htab_create ();
+ aarch64_cond_hsh = str_htab_create ();
+ aarch64_shift_hsh = str_htab_create ();
+ aarch64_sys_regs_hsh = str_htab_create ();
+ aarch64_pstatefield_hsh = str_htab_create ();
+ aarch64_sys_regs_ic_hsh = str_htab_create ();
+ aarch64_sys_regs_dc_hsh = str_htab_create ();
+ aarch64_sys_regs_at_hsh = str_htab_create ();
+ aarch64_sys_regs_tlbi_hsh = str_htab_create ();
+ aarch64_sys_regs_sr_hsh = str_htab_create ();
+ aarch64_reg_hsh = str_htab_create ();
+ aarch64_barrier_opt_hsh = str_htab_create ();
+ aarch64_nzcv_hsh = str_htab_create ();
+ aarch64_pldop_hsh = str_htab_create ();
+ aarch64_hint_opt_hsh = str_htab_create ();
fill_instruction_hash_table ();
for (i = 0; aarch64_sys_regs[i].name != NULL; ++i)
- checked_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name,
+ sysreg_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name,
(void *) (aarch64_sys_regs + i));
for (i = 0; aarch64_pstatefields[i].name != NULL; ++i)
- checked_hash_insert (aarch64_pstatefield_hsh,
+ sysreg_hash_insert (aarch64_pstatefield_hsh,
aarch64_pstatefields[i].name,
(void *) (aarch64_pstatefields + i));
for (i = 0; aarch64_sys_regs_ic[i].name != NULL; i++)
- checked_hash_insert (aarch64_sys_regs_ic_hsh,
+ sysreg_hash_insert (aarch64_sys_regs_ic_hsh,
aarch64_sys_regs_ic[i].name,
(void *) (aarch64_sys_regs_ic + i));
for (i = 0; aarch64_sys_regs_dc[i].name != NULL; i++)
- checked_hash_insert (aarch64_sys_regs_dc_hsh,
+ sysreg_hash_insert (aarch64_sys_regs_dc_hsh,
aarch64_sys_regs_dc[i].name,
(void *) (aarch64_sys_regs_dc + i));
for (i = 0; aarch64_sys_regs_at[i].name != NULL; i++)
- checked_hash_insert (aarch64_sys_regs_at_hsh,
+ sysreg_hash_insert (aarch64_sys_regs_at_hsh,
aarch64_sys_regs_at[i].name,
(void *) (aarch64_sys_regs_at + i));
for (i = 0; aarch64_sys_regs_tlbi[i].name != NULL; i++)
- checked_hash_insert (aarch64_sys_regs_tlbi_hsh,
+ sysreg_hash_insert (aarch64_sys_regs_tlbi_hsh,
aarch64_sys_regs_tlbi[i].name,
(void *) (aarch64_sys_regs_tlbi + i));
for (i = 0; aarch64_sys_regs_sr[i].name != NULL; i++)
- checked_hash_insert (aarch64_sys_regs_sr_hsh,
+ sysreg_hash_insert (aarch64_sys_regs_sr_hsh,
aarch64_sys_regs_sr[i].name,
(void *) (aarch64_sys_regs_sr + i));
(void *) (aarch64_barrier_options + i));
}
+ for (i = 0; i < ARRAY_SIZE (aarch64_barrier_dsb_nxs_options); i++)
+ {
+ const char *name = aarch64_barrier_dsb_nxs_options[i].name;
+ checked_hash_insert (aarch64_barrier_opt_hsh, name,
+ (void *) (aarch64_barrier_dsb_nxs_options + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_barrier_opt_hsh, get_upper_str (name),
+ (void *) (aarch64_barrier_dsb_nxs_options + i));
+ }
+
for (i = 0; i < ARRAY_SIZE (aarch64_prfops); i++)
{
const char* name = aarch64_prfops[i].name;
for (i = 0; aarch64_hint_options[i].name != NULL; i++)
{
const char* name = aarch64_hint_options[i].name;
+ const char* upper_name = get_upper_str(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));
+
+ /* Also hash the name in the upper case if not the same. */
+ if (strcmp (name, upper_name) != 0)
+ checked_hash_insert (aarch64_hint_opt_hsh, upper_name,
+ (void *) (aarch64_hint_options + i));
}
/* Set the cpu variant based on the command-line options. */
recognized by GCC. */
static const struct aarch64_cpu_option_table aarch64_cpus[] = {
{"all", AARCH64_ANY, NULL},
+ {"cortex-a34", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A34"},
{"cortex-a35", AARCH64_FEATURE (AARCH64_ARCH_V8,
AARCH64_FEATURE_CRC), "Cortex-A35"},
{"cortex-a53", AARCH64_FEATURE (AARCH64_ARCH_V8,
{"cortex-a76", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 | AARCH64_FEATURE_DOTPROD),
"Cortex-A76"},
+ {"cortex-a76ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS),
+ "Cortex-A76AE"},
+ {"cortex-a77", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS),
+ "Cortex-A77"},
+ {"cortex-a65", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS),
+ "Cortex-A65"},
+ {"cortex-a65ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS),
+ "Cortex-A65AE"},
+ {"cortex-a78", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_PROFILE),
+ "Cortex-A78"},
+ {"cortex-a78ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_PROFILE),
+ "Cortex-A78AE"},
{"ares", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16
| AARCH64_FEATURE_DOTPROD
AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO
| AARCH64_FEATURE_RDMA),
"Qualcomm Falkor"},
+ {"neoverse-e1", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS),
+ "Neoverse E1"},
+ {"neoverse-n1", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_PROFILE),
+ "Neoverse N1"},
+ {"neoverse-n2", AARCH64_FEATURE (AARCH64_ARCH_V8_5,
+ AARCH64_FEATURE_BFLOAT16
+ | AARCH64_FEATURE_I8MM
+ | AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_SVE
+ | AARCH64_FEATURE_SVE2
+ | AARCH64_FEATURE_SVE2_BITPERM
+ | AARCH64_FEATURE_MEMTAG
+ | AARCH64_FEATURE_RNG),
+ "Neoverse N2"},
+ {"neoverse-v1", AARCH64_FEATURE (AARCH64_ARCH_V8_4,
+ AARCH64_FEATURE_PROFILE
+ | AARCH64_FEATURE_CVADP
+ | AARCH64_FEATURE_SVE
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_RNG
+ | AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_BFLOAT16
+ | AARCH64_FEATURE_I8MM), "Neoverse V1"},
{"qdf24xx", AARCH64_FEATURE (AARCH64_ARCH_V8,
AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO
| AARCH64_FEATURE_RDMA),
{"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"},
{"xgene2", AARCH64_FEATURE (AARCH64_ARCH_V8,
AARCH64_FEATURE_CRC), "APM X-Gene 2"},
+ {"cortex-r82", AARCH64_ARCH_V8_R, "Cortex-R82"},
+ {"cortex-x1", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_PROFILE),
+ "Cortex-X1"},
{"generic", AARCH64_ARCH_V8, NULL},
{NULL, AARCH64_ARCH_NONE, NULL}
{"armv8.3-a", AARCH64_ARCH_V8_3},
{"armv8.4-a", AARCH64_ARCH_V8_4},
{"armv8.5-a", AARCH64_ARCH_V8_5},
+ {"armv8.6-a", AARCH64_ARCH_V8_6},
+ {"armv8.7-a", AARCH64_ARCH_V8_7},
+ {"armv8-r", AARCH64_ARCH_V8_R},
{NULL, AARCH64_ARCH_NONE}
};
static const struct aarch64_option_cpu_value_table aarch64_features[] = {
{"crc", AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0),
AARCH64_ARCH_NONE},
- {"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO
- | AARCH64_FEATURE_AES
- | AARCH64_FEATURE_SHA2, 0),
+ {"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0),
AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)},
{"fp", AARCH64_FEATURE (AARCH64_FEATURE_FP, 0),
AARCH64_ARCH_NONE},
AARCH64_FEATURE (AARCH64_FEATURE_F16
| AARCH64_FEATURE_SIMD
| AARCH64_FEATURE_COMPNUM, 0)},
+ {"tme", AARCH64_FEATURE (AARCH64_FEATURE_TME, 0),
+ AARCH64_ARCH_NONE},
{"compnum", AARCH64_FEATURE (AARCH64_FEATURE_COMPNUM, 0),
AARCH64_FEATURE (AARCH64_FEATURE_F16
| AARCH64_FEATURE_SIMD, 0)},
AARCH64_ARCH_NONE},
{"sm4", AARCH64_FEATURE (AARCH64_FEATURE_SM4, 0),
AARCH64_ARCH_NONE},
- {"sha3", AARCH64_FEATURE (AARCH64_FEATURE_SHA2
- | AARCH64_FEATURE_SHA3, 0),
- AARCH64_ARCH_NONE},
+ {"sha3", AARCH64_FEATURE (AARCH64_FEATURE_SHA3, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SHA2, 0)},
{"rng", AARCH64_FEATURE (AARCH64_FEATURE_RNG, 0),
AARCH64_ARCH_NONE},
{"ssbs", AARCH64_FEATURE (AARCH64_FEATURE_SSBS, 0),
AARCH64_ARCH_NONE},
{"memtag", AARCH64_FEATURE (AARCH64_FEATURE_MEMTAG, 0),
AARCH64_ARCH_NONE},
+ {"sve2", AARCH64_FEATURE (AARCH64_FEATURE_SVE2, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
+ {"sve2-sm4", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_SM4, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE2
+ | AARCH64_FEATURE_SM4, 0)},
+ {"sve2-aes", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_AES, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE2
+ | AARCH64_FEATURE_AES, 0)},
+ {"sve2-sha3", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_SHA3, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE2
+ | AARCH64_FEATURE_SHA3, 0)},
+ {"sve2-bitperm", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_BITPERM, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE2, 0)},
+ {"bf16", AARCH64_FEATURE (AARCH64_FEATURE_BFLOAT16, 0),
+ AARCH64_ARCH_NONE},
+ {"i8mm", AARCH64_FEATURE (AARCH64_FEATURE_I8MM, 0),
+ AARCH64_ARCH_NONE},
+ {"f32mm", AARCH64_FEATURE (AARCH64_FEATURE_F32MM, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
+ {"f64mm", AARCH64_FEATURE (AARCH64_FEATURE_F64MM, 0),
+ AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
+ {"csre", AARCH64_FEATURE (AARCH64_FEATURE_CSRE, 0),
+ AARCH64_ARCH_NONE},
+ {"ls64", AARCH64_FEATURE (AARCH64_FEATURE_LS64, 0),
+ AARCH64_ARCH_NONE},
{NULL, AARCH64_ARCH_NONE, AARCH64_ARCH_NONE},
};
{
AARCH64_GET_FLAG (dest) = AARCH64_GET_FLAG (src);
}
+
+#ifdef OBJ_ELF
+/* Same as elf_copy_symbol_attributes, but without copying st_other.
+ This is needed so AArch64 specific st_other values can be independently
+ specified for an IFUNC resolver (that is called by the dynamic linker)
+ and the symbol it resolves (aliased to the resolver). In particular,
+ if a function symbol has special st_other value set via directives,
+ then attaching an IFUNC resolver to that symbol should not override
+ the st_other setting. Requiring the directive on the IFUNC resolver
+ symbol would be unexpected and problematic in C code, where the two
+ symbols appear as two independent function declarations. */
+
+void
+aarch64_elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ struct elf_obj_sy *srcelf = symbol_get_obj (src);
+ struct elf_obj_sy *destelf = symbol_get_obj (dest);
+ if (srcelf->size)
+ {
+ if (destelf->size == NULL)
+ destelf->size = XNEW (expressionS);
+ *destelf->size = *srcelf->size;
+ }
+ else
+ {
+ free (destelf->size);
+ destelf->size = NULL;
+ }
+ S_SET_SIZE (dest, S_GET_SIZE (src));
+}
+#endif