/* tc-arm.c -- Assemble for the ARM
- Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005
Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
Modified by David Taylor (dtaylor@armltd.co.uk)
#ifdef OBJ_ELF
#include "elf/arm.h"
#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
#endif
-/* XXX Set this to 1 after the next binutils release */
+/* XXX Set this to 1 after the next binutils release. */
#define WARN_DEPRECATED 0
+#ifdef OBJ_ELF
+/* Must be at least the size of the largest unwind opcode (currently two). */
+#define ARM_OPCODE_CHUNK_SIZE 8
+
+/* This structure holds the unwinding state. */
+
+static struct
+{
+ symbolS * proc_start;
+ symbolS * table_entry;
+ symbolS * personality_routine;
+ int personality_index;
+ /* The segment containing the function. */
+ segT saved_seg;
+ subsegT saved_subseg;
+ /* Opcodes generated from this function. */
+ unsigned char * opcodes;
+ int opcode_count;
+ int opcode_alloc;
+ /* The number of bytes pushed to the stack. */
+ offsetT frame_size;
+ /* We don't add stack adjustment opcodes immediately so that we can merge
+ multiple adjustments. We can also omit the final adjustment
+ when using a frame pointer. */
+ offsetT pending_offset;
+ /* These two fields are set by both unwind_movsp and unwind_setfp. They
+ hold the reg+offset to use when restoring sp from a frame pointer. */
+ offsetT fp_offset;
+ int fp_reg;
+ /* Nonzero if an unwind_setfp directive has been seen. */
+ unsigned fp_used:1;
+ /* Nonzero if the last opcode restores sp from fp_reg. */
+ unsigned sp_restored:1;
+} unwind;
+
+/* Bit N indicates that an R_ARM_NONE relocation has been output for
+ __aeabi_unwind_cpp_prN already if set. This enables dependencies to be
+ emitted only once per section, to save unnecessary bloat. */
+static unsigned int marked_pr_dependency = 0;
+
+#endif /* OBJ_ELF */
+
enum arm_float_abi
{
ARM_FLOAT_ABI_HARD,
#endif
#endif
-#ifdef TE_LINUX
-#define FPU_DEFAULT FPU_ARCH_FPA
-#endif
-
-#ifdef TE_NetBSD
-#ifdef OBJ_ELF
-#define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, but VFP order. */
-#else
-/* Legacy a.out format. */
-#define FPU_DEFAULT FPU_ARCH_FPA /* Soft-float, but FPA order. */
-#endif
-#endif
-
-/* For backwards compatibility we default to the FPA. */
#ifndef FPU_DEFAULT
-#define FPU_DEFAULT FPU_ARCH_FPA
-#endif
+# ifdef TE_LINUX
+# define FPU_DEFAULT FPU_ARCH_FPA
+# elif defined (TE_NetBSD)
+# ifdef OBJ_ELF
+# define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, but VFP order. */
+# else
+ /* Legacy a.out format. */
+# define FPU_DEFAULT FPU_ARCH_FPA /* Soft-float, but FPA order. */
+# endif
+# elif defined (TE_VXWORKS)
+# define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, VFP order. */
+# else
+ /* For backwards compatibility, default to FPA. */
+# define FPU_DEFAULT FPU_ARCH_FPA
+# endif
+#endif /* ifndef FPU_DEFAULT */
#define streq(a, b) (strcmp (a, b) == 0)
#define skip_whitespace(str) while (*(str) == ' ') ++(str)
static unsigned long cpu_variant;
-static int target_oabi = 0;
/* Flags stored in private area of BFD structure. */
static int uses_apcs_26 = FALSE;
static int mfpu_opt = -1;
static int mfloat_abi_opt = -1;
#ifdef OBJ_ELF
+# ifdef EABI_DEFAULT
+static int meabi_flags = EABI_DEFAULT;
+# else
static int meabi_flags = EF_ARM_EABI_UNKNOWN;
+# endif
#endif
/* This array holds the chars that always start a comment. If the
\f
#ifdef OBJ_ELF
/* This code is to handle mapping symbols as defined in the ARM ELF spec.
- (This text is taken from version B-02 of the spec):
-
- 4.4.7 Mapping and tagging symbols
-
- A section of an ARM ELF file can contain a mixture of ARM code,
- Thumb code, and data. There are inline transitions between code
- and data at literal pool boundaries. There can also be inline
- transitions between ARM code and Thumb code, for example in
- ARM-Thumb inter-working veneers. Linkers, machine-level
- debuggers, profiling tools, and disassembly tools need to map
- images accurately. For example, setting an ARM breakpoint on a
- Thumb location, or in a literal pool, can crash the program
- being debugged, ruining the debugging session.
-
- ARM ELF entities are mapped (see section 4.4.7.1 below) and
- tagged (see section 4.4.7.2 below) using local symbols (with
- binding STB_LOCAL). To assist consumers, mapping and tagging
- symbols should be collated first in the symbol table, before
- other symbols with binding STB_LOCAL.
-
- To allow properly collated mapping and tagging symbols to be
- skipped by consumers that have no interest in them, the first
- such symbol should have the name $m and its st_value field equal
- to the total number of mapping and tagging symbols (including
- the $m) in the symbol table.
-
- 4.4.7.1 Mapping symbols
-
- $a Labels the first byte of a sequence of ARM instructions.
- Its type is STT_FUNC.
-
- $d Labels the first byte of a sequence of data items.
- Its type is STT_OBJECT.
-
- $t Labels the first byte of a sequence of Thumb instructions.
- Its type is STT_FUNC.
-
- This list of mapping symbols may be extended in the future.
-
- Section-relative mapping symbols
-
- Mapping symbols defined in a section define a sequence of
- half-open address intervals that cover the address range of the
- section. Each interval starts at the address defined by a
- mapping symbol, and continues up to, but not including, the
- address defined by the next (in address order) mapping symbol or
- the end of the section. A corollary is that there must be a
- mapping symbol defined at the beginning of each section.
- Consumers can ignore the size of a section-relative mapping
- symbol. Producers can set it to 0.
-
- Absolute mapping symbols
-
- Because of the need to crystallize a Thumb address with the
- Thumb-bit set, absolute symbol of type STT_FUNC (symbols of type
- STT_FUNC defined in section SHN_ABS) need to be mapped with $a
- or $t.
-
- The extent of a mapping symbol defined in SHN_ABS is [st_value,
- st_value + st_size), or [st_value, st_value + 1) if st_size = 0,
- where [x, y) denotes the half-open address range from x,
- inclusive, to y, exclusive.
-
- In the absence of a mapping symbol, a consumer can interpret a
- function symbol with an odd value as the Thumb code address
- obtained by clearing the least significant bit of the
- value. This interpretation is deprecated, and it may not work in
- the future.
-
- Note - the Tagging symbols ($b, $f, $p $m) have been dropped from
- the EABI (which is still under development), so they are not
- implemented here. */
+ (See "Mapping symbols", section 4.5.5, ARM AAELF version 1.0).
+ Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
+ and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped. */
static enum mstate mapstate = MAP_UNDEFINED;
{
case MAP_DATA:
symname = "$d";
- type = BSF_OBJECT;
+ type = BSF_NO_FLAGS;
break;
case MAP_ARM:
symname = "$a";
- type = BSF_FUNCTION;
+ type = BSF_NO_FLAGS;
break;
case MAP_THUMB:
symname = "$t";
- type = BSF_FUNCTION;
+ type = BSF_NO_FLAGS;
break;
case MAP_UNDEFINED:
return;
abort ();
}
- seg_info (now_seg)->tc_segment_info_data = state;
+ seg_info (now_seg)->tc_segment_info_data.mapstate = state;
symbolP = symbol_new (symname, now_seg, (valueT) frag_now_fix (), frag_now);
symbol_table_insert (symbolP);
arm_elf_change_section (void)
{
flagword flags;
+ segment_info_type *seginfo;
+
+ /* Link an unlinked unwind index table section to the .text section. */
+ if (elf_section_type (now_seg) == SHT_ARM_EXIDX
+ && elf_linked_to_section (now_seg) == NULL)
+ elf_linked_to_section (now_seg) = text_section;
if (!SEG_NORMAL (now_seg))
return;
if ((flags & SEC_ALLOC) == 0)
return;
- mapstate = seg_info (now_seg)->tc_segment_info_data;
+ seginfo = seg_info (now_seg);
+ mapstate = seginfo->tc_segment_info_data.mapstate;
+ marked_pr_dependency = seginfo->tc_segment_info_data.marked_pr_dependency;
+}
+
+int
+arm_elf_section_type (const char * str, size_t len)
+{
+ if (len == 5 && strncmp (str, "exidx", 5) == 0)
+ return SHT_ARM_EXIDX;
+
+ return -1;
}
#else
#define mapping_state(a)
return;
}
-#if 0 /* The first edition of the ARM architecture manual stated that
- writing anything other than the flags with an immediate operation
- had UNPREDICTABLE effects. This constraint was removed in the
- second edition of the specification. */
- if ((cpu_variant & ARM_EXT_V5) != ARM_EXT_V5
- && inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
- {
- inst.error = _("immediate value cannot be used to set this field");
- return;
- }
-#endif
-
inst.instruction |= INST_IMMEDIATE;
if (inst.reloc.exp.X_add_symbol)
}
static void
-do_mla (char * str)
+do_mlas (char * str, bfd_boolean is_mls)
{
int rd, rm;
return;
}
- if (rm == rd)
+ /* This restriction does not apply to mls (nor to mla in v6, but
+ that's hard to detect at present). */
+ if (rm == rd && !is_mls)
as_tsktsk (_("rd and rm should be different in mla"));
if (skip_past_comma (&str) == FAIL
end_of_line (str);
}
+static void
+do_mla (char *str)
+{
+ do_mlas (str, FALSE);
+}
+
+static void
+do_mls (char *str)
+{
+ do_mlas (str, TRUE);
+}
+
/* Expects *str -> the characters "acc0", possibly with leading blanks.
Advances *str to the next non-alphanumeric.
Returns 0, or else FAIL (in which case sets inst.error).
end_of_line (str);
}
+/* ARM V6T2 bitfield manipulation instructions. */
+
+static int
+five_bit_unsigned_immediate (char **str)
+{
+ expressionS expr;
+
+ skip_whitespace (*str);
+ if (!is_immediate_prefix (**str))
+ {
+ inst.error = _("immediate expression expected");
+ return -1;
+ }
+ (*str)++;
+ if (my_get_expression (&expr, str))
+ {
+ inst.error = _("bad expression");
+ return -1;
+ }
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return -1;
+ }
+ if (expr.X_add_number < 0 || expr.X_add_number > 32)
+ {
+ inst.error = _("immediate value out of range");
+ return -1;
+ }
+
+ return expr.X_add_number;
+}
+
+static void
+bfci_lsb_and_width (char *str)
+{
+ int lsb, width;
+
+ if ((lsb = five_bit_unsigned_immediate (&str)) == -1)
+ return;
+
+ if (skip_past_comma (&str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if ((width = five_bit_unsigned_immediate (&str)) == -1)
+ return;
+
+ end_of_line (str);
+
+ if (width == 0 || lsb == 32)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ else if (width + lsb > 32)
+ {
+ inst.error = _("bit-field extends past end of register");
+ return;
+ }
+
+ /* Convert to LSB/MSB and write to register. */
+ inst.instruction |= lsb << 7;
+ inst.instruction |= (width + lsb - 1) << 16;
+}
+
+static void
+do_bfc (char *str)
+{
+ int rd;
+
+ /* Rd. */
+ skip_whitespace (str);
+ if (((rd = reg_required_here (&str, 12)) == FAIL)
+ || (skip_past_comma (&str) == FAIL))
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ bfci_lsb_and_width (str);
+}
+
+static void
+do_bfi (char *str)
+{
+ int rd, rm;
+
+ /* Rd. */
+ skip_whitespace (str);
+ if (((rd = reg_required_here (&str, 12)) == FAIL)
+ || (skip_past_comma (&str) == FAIL))
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Rm. Accept #0 in this position as an alternative syntax for bfc. */
+ skip_whitespace (str);
+ if (is_immediate_prefix (*str))
+ {
+ expressionS expr;
+ str++;
+ if (my_get_expression (&expr, &str))
+ {
+ inst.error = _("bad expression");
+ return;
+ }
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return;
+ }
+ if (expr.X_add_number != 0)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ inst.instruction |= 0x0000000f; /* Rm = PC -> bfc, not bfi. */
+ }
+ else
+ {
+ if ((rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+ }
+ if (skip_past_comma (&str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ bfci_lsb_and_width (str);
+}
+
+static void
+do_bfx (char *str)
+{
+ int lsb, width;
+
+ /* Rd. */
+ skip_whitespace (str);
+ if (reg_required_here (&str, 12) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ /* Rm. */
+ skip_whitespace (str);
+ if (reg_required_here (&str, 0) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if ((lsb = five_bit_unsigned_immediate (&str)) == -1)
+ return;
+
+ if (skip_past_comma (&str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if ((width = five_bit_unsigned_immediate (&str)) == -1)
+ return;
+
+ end_of_line (str);
+
+ if (width == 0 || lsb == 32)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ else if (width + lsb > 32)
+ {
+ inst.error = _("bit-field extends past end of register");
+ return;
+ }
+
+ inst.instruction |= lsb << 7;
+ inst.instruction |= (width - 1) << 16;
+}
+
+static void
+do_rbit (char *str)
+{
+ /* Rd. */
+ skip_whitespace (str);
+ if (reg_required_here (&str, 12) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ /* Rm. */
+ skip_whitespace (str);
+ if (reg_required_here (&str, 0) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ end_of_line (str);
+}
+
+/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>. */
+static void
+do_mov16 (char *str)
+{
+ int rd;
+ expressionS expr;
+
+ /* Rd. */
+ skip_whitespace (str);
+ if (((rd = reg_required_here (&str, 12)) == FAIL)
+ || (skip_past_comma (&str) == FAIL))
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Imm16. */
+ skip_whitespace (str);
+ if (!is_immediate_prefix (*str))
+ {
+ inst.error = _("immediate expression expected");
+ return;
+ }
+ str++;
+ if (my_get_expression (&expr, &str))
+ {
+ inst.error = _("bad expression");
+ return;
+ }
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return;
+ }
+ if (expr.X_add_number < 0 || expr.X_add_number > 65535)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+
+ end_of_line (str);
+
+ /* The value is in two pieces: 0:11, 16:19. */
+ inst.instruction |= (expr.X_add_number & 0x00000fff);
+ inst.instruction |= (expr.X_add_number & 0x0000f000) << 4;
+}
+
+
/* THUMB V5 breakpoint instruction (argument parse)
BKPT <immed_8>. */
end_of_line (str);
}
+#ifdef OBJ_ELF
static bfd_reloc_code_real_type
arm_parse_reloc (void)
{
MAP ("(target1)", BFD_RELOC_ARM_TARGET1),
MAP ("(sbrel)", BFD_RELOC_ARM_SBREL32),
MAP ("(target2)", BFD_RELOC_ARM_TARGET2),
+ MAP ("(tlsgd)", BFD_RELOC_ARM_TLS_GD32),
+ MAP ("(tlsldm)", BFD_RELOC_ARM_TLS_LDM32),
+ MAP ("(tlsldo)", BFD_RELOC_ARM_TLS_LDO32),
+ MAP ("(gottpoff)", BFD_RELOC_ARM_TLS_IE32),
+ MAP ("(tpoff)", BFD_RELOC_ARM_TLS_LE32),
{ NULL, 0, BFD_RELOC_UNUSED }
#undef MAP
};
return reloc_map[i].reloc;
}
+#endif
/* ARM V5 branch-link-exchange (argument parse) for BLX(1) only.
Expects inst.instruction is set for BLX(1).
end_of_line (str);
}
-static long
-reg_list (char ** strp)
+static void
+do_ldsttv4 (char * str)
+{
+ int conflict_reg;
+
+ skip_whitespace (str);
+
+ if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL)
+ {
+ inst.error = _("address expected");
+ return;
+ }
+
+ if (*str == '[')
+ {
+ int reg;
+
+ str++;
+
+ skip_whitespace (str);
+
+ if ((reg = reg_required_here (&str, 16)) == FAIL)
+ return;
+
+ /* ldrt/strt always use post-indexed addressing, so if the base is
+ the same as Rd, we warn. */
+ if (conflict_reg == reg)
+ as_warn (_("%s register same as write-back base"),
+ ((inst.instruction & LOAD_BIT)
+ ? _("destination") : _("source")));
+
+ skip_whitespace (str);
+
+ if (*str == ']')
+ {
+ str ++;
+
+ if (skip_past_comma (&str) == SUCCESS)
+ {
+ /* [Rn],... (post inc) */
+ if (ldst_extend_v4 (&str) == FAIL)
+ return;
+ }
+ else
+ {
+ /* [Rn] */
+ skip_whitespace (str);
+
+ /* Skip a write-back '!'. */
+ if (*str == '!')
+ str++;
+
+ inst.instruction |= (INDEX_UP|HWOFFSET_IMM);
+ }
+ }
+ else
+ {
+ inst.error = _("post-indexed expression expected");
+ return;
+ }
+ }
+ else
+ {
+ inst.error = _("post-indexed expression expected");
+ return;
+ }
+
+ end_of_line (str);
+}
+
+
+static long
+reg_list (char ** strp)
{
char * str = * strp;
long range = 0;
end_of_line (str);
}
+/* Encode a VFP SP register number. */
+
+static void
+vfp_sp_encode_reg (int reg, enum vfp_sp_reg_pos pos)
+{
+ switch (pos)
+ {
+ case VFP_REG_Sd:
+ inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
+ break;
+
+ case VFP_REG_Sn:
+ inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
+ break;
+
+ case VFP_REG_Sm:
+ inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
+ break;
+
+ default:
+ abort ();
+ }
+}
+
static int
vfp_sp_reg_required_here (char ** str,
enum vfp_sp_reg_pos pos)
{
int reg;
- char *start = *str;
+ char * start = *str;
if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab)) != FAIL)
{
- switch (pos)
- {
- case VFP_REG_Sd:
- inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
- break;
-
- case VFP_REG_Sn:
- inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
- break;
-
- case VFP_REG_Sm:
- inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
- break;
-
- default:
- abort ();
- }
+ vfp_sp_encode_reg (reg, pos);
return reg;
}
end_of_line (str);
}
-/* Parse and encode a VFP SP register list, storing the initial
- register in position POS and returning the range as the result. If
- the string is invalid return FAIL (an invalid range). */
+/* Parse a VFP register list. If the string is invalid return FAIL.
+ Otherwise return the number of registers, and set PBASE to the first
+ register. Double precision registers are matched if DP is nonzero. */
-static long
-vfp_sp_reg_list (char ** str, enum vfp_sp_reg_pos pos)
+static int
+vfp_parse_reg_list (char **str, int *pbase, int dp)
{
- long range = 0;
- int base_reg = 0;
+ int base_reg;
int new_base;
- long base_bits = 0;
+ int regtype;
+ int max_regs;
int count = 0;
- long tempinst;
- unsigned long mask = 0;
int warned = 0;
+ unsigned long mask = 0;
+ int i;
if (**str != '{')
return FAIL;
(*str)++;
skip_whitespace (*str);
- tempinst = inst.instruction;
-
- do
+ if (dp)
+ {
+ regtype = REG_TYPE_DN;
+ max_regs = 16;
+ }
+ else
{
- inst.instruction = 0;
+ regtype = REG_TYPE_SN;
+ max_regs = 32;
+ }
- if ((new_base = vfp_sp_reg_required_here (str, pos)) == FAIL)
- return FAIL;
+ base_reg = max_regs;
- if (count == 0 || base_reg > new_base)
+ do
+ {
+ new_base = arm_reg_parse (str, all_reg_maps[regtype].htab);
+ if (new_base == FAIL)
{
- base_reg = new_base;
- base_bits = inst.instruction;
+ inst.error = _(all_reg_maps[regtype].expected);
+ return FAIL;
}
+ if (new_base < base_reg)
+ base_reg = new_base;
+
if (mask & (1 << new_base))
{
inst.error = _("invalid register list");
(*str)++;
if ((high_range
- = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab))
+ = arm_reg_parse (str, all_reg_maps[regtype].htab))
== FAIL)
{
- inst.error = _(all_reg_maps[REG_TYPE_SN].expected);
+ inst.error = _(all_reg_maps[regtype].expected);
return FAIL;
}
}
while (skip_past_comma (str) != FAIL);
- if (**str != '}')
- {
- inst.error = _("invalid register list");
- return FAIL;
- }
-
(*str)++;
- range = count;
-
/* Sanity check -- should have raised a parse error above. */
- if (count == 0 || count > 32)
+ if (count == 0 || count > max_regs)
abort ();
+ *pbase = base_reg;
+
/* Final test -- the registers must be consecutive. */
- while (count--)
+ mask >>= base_reg;
+ for (i = 0; i < count; i++)
{
- if ((mask & (1 << base_reg++)) == 0)
+ if ((mask & (1u << i)) == 0)
{
inst.error = _("non-contiguous register range");
return FAIL;
}
}
- inst.instruction = tempinst | base_bits;
- return range;
+ return count;
}
static void
do_vfp_reg2_from_sp2 (char * str)
{
+ int reg;
+
skip_whitespace (str);
if (reg_required_here (&str, 12) == FAIL
}
/* We require exactly two consecutive SP registers. */
- if (vfp_sp_reg_list (&str, VFP_REG_Sm) != 2)
+ if (vfp_parse_reg_list (&str, ®, 0) != 2)
{
if (! inst.error)
inst.error = _("only two consecutive VFP SP registers allowed here");
}
+ vfp_sp_encode_reg (reg, VFP_REG_Sm);
end_of_line (str);
}
static void
do_vfp_sp2_from_reg2 (char * str)
{
+ int reg;
+
skip_whitespace (str);
/* We require exactly two consecutive SP registers. */
- if (vfp_sp_reg_list (&str, VFP_REG_Sm) != 2)
+ if (vfp_parse_reg_list (&str, ®, 0) != 2)
{
if (! inst.error)
inst.error = _("only two consecutive VFP SP registers allowed here");
}
+ vfp_sp_encode_reg (reg, VFP_REG_Sm);
if (skip_past_comma (&str) == FAIL
|| reg_required_here (&str, 12) == FAIL
end_of_line (str);
}
-static long
-vfp_dp_reg_list (char ** str)
-{
- long range = 0;
- int base_reg = 0;
- int new_base;
- int count = 0;
- long tempinst;
- unsigned long mask = 0;
- int warned = 0;
-
- if (**str != '{')
- return FAIL;
-
- (*str)++;
- skip_whitespace (*str);
-
- tempinst = inst.instruction;
-
- do
- {
- inst.instruction = 0;
-
- if ((new_base = vfp_dp_reg_required_here (str, VFP_REG_Dd)) == FAIL)
- return FAIL;
-
- if (count == 0 || base_reg > new_base)
- {
- base_reg = new_base;
- range = inst.instruction;
- }
-
- if (mask & (1 << new_base))
- {
- inst.error = _("invalid register list");
- return FAIL;
- }
-
- if ((mask >> new_base) != 0 && ! warned)
- {
- as_tsktsk (_("register list not in ascending order"));
- warned = 1;
- }
-
- mask |= 1 << new_base;
- count++;
-
- skip_whitespace (*str);
-
- if (**str == '-') /* We have the start of a range expression */
- {
- int high_range;
-
- (*str)++;
-
- if ((high_range
- = arm_reg_parse (str, all_reg_maps[REG_TYPE_DN].htab))
- == FAIL)
- {
- inst.error = _(all_reg_maps[REG_TYPE_DN].expected);
- return FAIL;
- }
-
- if (high_range <= new_base)
- {
- inst.error = _("register range not in ascending order");
- return FAIL;
- }
-
- for (new_base++; new_base <= high_range; new_base++)
- {
- if (mask & (1 << new_base))
- {
- inst.error = _("invalid register list");
- return FAIL;
- }
-
- mask |= 1 << new_base;
- count++;
- }
- }
- }
- while (skip_past_comma (str) != FAIL);
-
- if (**str != '}')
- {
- inst.error = _("invalid register list");
- return FAIL;
- }
-
- (*str)++;
-
- range |= 2 * count;
-
- /* Sanity check -- should have raised a parse error above. */
- if (count == 0 || count > 16)
- abort ();
-
- /* Final test -- the registers must be consecutive. */
- while (count--)
- {
- if ((mask & (1 << base_reg++)) == 0)
- {
- inst.error = _("non-contiguous register range");
- return FAIL;
- }
- }
-
- inst.instruction = tempinst;
- return range;
-}
static void
vfp_sp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
{
- long range;
+ int count;
+ int reg;
skip_whitespace (str);
}
if (skip_past_comma (&str) == FAIL
- || (range = vfp_sp_reg_list (&str, VFP_REG_Sd)) == FAIL)
+ || (count = vfp_parse_reg_list (&str, ®, 0)) == FAIL)
{
if (!inst.error)
inst.error = BAD_ARGS;
return;
}
+ vfp_sp_encode_reg (reg, VFP_REG_Sd);
- inst.instruction |= range;
+ inst.instruction |= count;
end_of_line (str);
}
static void
vfp_dp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
{
- long range;
+ int count;
+ int reg;
skip_whitespace (str);
}
if (skip_past_comma (&str) == FAIL
- || (range = vfp_dp_reg_list (&str)) == FAIL)
+ || (count = vfp_parse_reg_list (&str, ®, 1)) == FAIL)
{
if (!inst.error)
inst.error = BAD_ARGS;
return;
}
+ count <<= 1;
if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
- range += 1;
+ count += 1;
- inst.instruction |= range;
+ inst.instruction |= (reg << 12) | count;
end_of_line (str);
}
return;
}
- if (inst.reloc.type != BFD_RELOC_NONE)
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
{
/* This really doesn't seem worth it. */
- inst.reloc.type = BFD_RELOC_NONE;
+ inst.reloc.type = BFD_RELOC_UNUSED;
inst.error = _("expression too complex");
return;
}
return;
}
- if (inst.reloc.type != BFD_RELOC_NONE)
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
{
/* This really doesn't seem worth it. */
- inst.reloc.type = BFD_RELOC_NONE;
+ inst.reloc.type = BFD_RELOC_UNUSED;
inst.error = _("expression too complex");
return;
}
{ "wfe", 0xe320f002, 3, ARM_EXT_V6K, do_empty},
{ "wfi", 0xe320f003, 3, ARM_EXT_V6K, do_empty},
{ "yield", 0xe320f001, 5, ARM_EXT_V6K, do_empty},
-
+
/* ARM V6Z. */
{ "smi", 0xe1600070, 3, ARM_EXT_V6Z, do_smi},
+ /* ARM V6T2. */
+ { "bfc", 0xe7c0001f, 3, ARM_EXT_V6T2, do_bfc},
+ { "bfi", 0xe7c00010, 3, ARM_EXT_V6T2, do_bfi},
+ { "mls", 0xe0600090, 3, ARM_EXT_V6T2, do_mls},
+ { "movw", 0xe3000000, 4, ARM_EXT_V6T2, do_mov16},
+ { "movt", 0xe3400000, 4, ARM_EXT_V6T2, do_mov16},
+ { "rbit", 0xe3ff0f30, 4, ARM_EXT_V6T2, do_rbit},
+ { "sbfx", 0xe7a00050, 4, ARM_EXT_V6T2, do_bfx},
+ { "ubfx", 0xe7e00050, 4, ARM_EXT_V6T2, do_bfx},
+
+ { "ldrht", 0xe03000b0, 3, ARM_EXT_V6T2, do_ldsttv4},
+ { "ldrsht", 0xe03000f0, 3, ARM_EXT_V6T2, do_ldsttv4},
+ { "ldrsbt", 0xe03000d0, 3, ARM_EXT_V6T2, do_ldsttv4},
+ { "strht", 0xe02000b0, 3, ARM_EXT_V6T2, do_ldsttv4},
+
/* Core FPA instruction set (V1). */
{"wfs", 0xee200110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
{"rfs", 0xee300110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
}
}
-#if 0 /* Suppressed - for now. */
-#if defined OBJ_ELF || defined OBJ_COFF
-
-#ifdef OBJ_ELF
-#define arm_Note Elf_External_Note
-#else
-typedef struct
-{
- unsigned char namesz[4]; /* Size of entry's owner string. */
- unsigned char descsz[4]; /* Size of the note descriptor. */
- unsigned char type[4]; /* Interpretation of the descriptor. */
- char name[1]; /* Start of the name+desc data. */
-} arm_Note;
-#endif
-
-/* The description is kept to a fix sized in order to make updating
- it and merging it easier. */
-#define ARM_NOTE_DESCRIPTION_LENGTH 8
-
-static void
-arm_add_note (const char * name,
- const char * description,
- unsigned int type)
-{
- arm_Note note ATTRIBUTE_UNUSED;
- char * p;
- unsigned int name_len;
-
- name_len = (strlen (name) + 1 + 3) & ~3;
-
- p = frag_more (sizeof (note.namesz));
- md_number_to_chars (p, (valueT) name_len, sizeof (note.namesz));
-
- p = frag_more (sizeof (note.descsz));
- md_number_to_chars (p, (valueT) ARM_NOTE_DESCRIPTION_LENGTH, sizeof (note.descsz));
-
- p = frag_more (sizeof (note.type));
- md_number_to_chars (p, (valueT) type, sizeof (note.type));
-
- p = frag_more (name_len);
- strcpy (p, name);
-
- p = frag_more (ARM_NOTE_DESCRIPTION_LENGTH);
- strncpy (p, description, ARM_NOTE_DESCRIPTION_LENGTH);
- frag_align (2, 0, 0);
-}
-#endif
-#endif
-
-\f
-static const struct thumb_opcode tinsns[] =
+\f
+static const struct thumb_opcode tinsns[] =
{
/* Thumb v1 (ARMv4T). */
{"adc", 0x4140, 2, ARM_EXT_V4T, do_t_arit},
{"sxtb", 0xb240, 2, ARM_EXT_V6, do_t_arit},
{"uxth", 0xb280, 2, ARM_EXT_V6, do_t_arit},
{"uxtb", 0xb2c0, 2, ARM_EXT_V6, do_t_arit},
+
+ /* ARM V6K. */
+ {"sev", 0xbf40, 2, ARM_EXT_V6K, do_empty},
+ {"wfe", 0xbf20, 2, ARM_EXT_V6K, do_empty},
+ {"wfi", 0xbf30, 2, ARM_EXT_V6K, do_empty},
+ {"yield", 0xbf10, 2, ARM_EXT_V6K, do_empty},
};
void
}
else if (mfpu_opt == -1)
{
-#if !(defined (TE_LINUX) || defined (TE_NetBSD))
+#if !(defined (TE_LINUX) || defined (TE_NetBSD) || defined (TE_VXWORKS))
/* Some environments specify a default FPU. If they don't, infer it
from the processor. */
if (mcpu_fpu_opt != -1)
cpu_variant = mcpu_cpu_opt | mfpu_opt;
+#if defined OBJ_COFF || defined OBJ_ELF
{
unsigned int flags = 0;
{
case EF_ARM_EABI_UNKNOWN:
#endif
-#if defined OBJ_COFF || defined OBJ_ELF
/* Set the flags in the private structure. */
if (uses_apcs_26) flags |= F_APCS26;
if (support_interwork) flags |= F_INTERWORK;
/* Using VFP conventions (even if soft-float). */
if (cpu_variant & FPU_VFP_EXT_NONE)
flags |= F_VFP_FLOAT;
-#endif
+
#if defined OBJ_ELF
if (cpu_variant & FPU_ARCH_MAVERICK)
flags |= EF_ARM_MAVERICK_FLOAT;
break;
- case EF_ARM_EABI_VER3:
+ case EF_ARM_EABI_VER4:
/* No additional flags to set. */
break;
abort ();
}
#endif
-#if defined OBJ_COFF || defined OBJ_ELF
bfd_set_private_flags (stdoutput, flags);
/* We have run out flags in the COFF header to encode the
bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
}
}
-#endif
}
+#endif
/* Record the CPU type as well. */
switch (cpu_variant & ARM_CPU_MASK)
else if (cpu_variant & ARM_EXT_V3M)
mach = bfd_mach_arm_3M;
-#if 0 /* Suppressed - for now. */
-#if defined (OBJ_ELF) || defined (OBJ_COFF)
-
- /* Create a .note section to fully identify this arm binary. */
-
-#define NOTE_ARCH_STRING "arch: "
-
-#if defined OBJ_COFF && ! defined NT_VERSION
-#define NT_VERSION 1
-#define NT_ARCH 2
-#endif
-
- {
- segT current_seg = now_seg;
- subsegT current_subseg = now_subseg;
- asection * arm_arch;
- const char * arch_string;
-
- arm_arch = bfd_make_section_old_way (stdoutput, ARM_NOTE_SECTION);
-
-#ifdef OBJ_COFF
- bfd_set_section_flags (stdoutput, arm_arch,
- SEC_DATA | SEC_ALLOC | SEC_LOAD | SEC_LINK_ONCE \
- | SEC_HAS_CONTENTS);
-#else
- bfd_set_section_flags (stdoutput, arm_arch,
- SEC_READONLY | SEC_HAS_CONTENTS);
-#endif
- arm_arch->output_section = arm_arch;
- subseg_set (arm_arch, 0);
-
- switch (mach)
- {
- default:
- case bfd_mach_arm_unknown: arch_string = "unknown"; break;
- case bfd_mach_arm_2: arch_string = "armv2"; break;
- case bfd_mach_arm_2a: arch_string = "armv2a"; break;
- case bfd_mach_arm_3: arch_string = "armv3"; break;
- case bfd_mach_arm_3M: arch_string = "armv3M"; break;
- case bfd_mach_arm_4: arch_string = "armv4"; break;
- case bfd_mach_arm_4T: arch_string = "armv4t"; break;
- case bfd_mach_arm_5: arch_string = "armv5"; break;
- case bfd_mach_arm_5T: arch_string = "armv5t"; break;
- case bfd_mach_arm_5TE: arch_string = "armv5te"; break;
- case bfd_mach_arm_XScale: arch_string = "XScale"; break;
- case bfd_mach_arm_ep9312: arch_string = "ep9312"; break;
- case bfd_mach_arm_iWMMXt: arch_string = "iWMMXt"; break;
- }
-
- arm_add_note (NOTE_ARCH_STRING, arch_string, NT_ARCH);
-
- subseg_set (current_seg, current_subseg);
- }
-#endif
-#endif /* Suppressed code. */
-
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
}
char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data;
- assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+ assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
/* Note whether this will delete the relocation. */
-#if 0
- /* Patch from REarnshaw to JDavis (disabled for the moment, since it
- doesn't work fully.) */
- if ((fixP->fx_addsy == 0 || symbol_constant_p (fixP->fx_addsy))
- && !fixP->fx_pcrel)
-#else
if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
-#endif
fixP->fx_done = 1;
/* If this symbol is in a different section then we need to leave it for
if (fixP->fx_addsy != NULL
&& S_IS_DEFINED (fixP->fx_addsy)
&& S_GET_SEGMENT (fixP->fx_addsy) != seg)
- {
- if (target_oabi
- && (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
- || fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
- ))
- value = 0;
- else
- value += md_pcrel_from (fixP);
- }
+ value += md_pcrel_from (fixP);
}
/* Remember value for emit_reloc. */
switch (fixP->fx_r_type)
{
+ case BFD_RELOC_NONE:
+ /* This will need to go in the object file. */
+ fixP->fx_done = 0;
+ break;
+
case BFD_RELOC_ARM_IMMEDIATE:
/* We claim that this fixup has been processed here,
even if in fact we generate an error because we do
#define SEXT24(x) ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000)
#ifdef OBJ_ELF
- if (! target_oabi)
- value = fixP->fx_offset;
+ value = fixP->fx_offset;
#endif
/* We are going to store value (shifted right by two) in the
branch instruction itself, then we can compute the relocation for
ourselves and not have to bother the linker with it.
- FIXME: The tests for OBJ_ELF and ! target_oabi are only here
- because I have not worked out how to do this for OBJ_COFF or
- target_oabi. */
- if (! target_oabi
- && fixP->fx_addsy != NULL
+ FIXME: The test for OBJ_ELF is only here because I have not
+ worked out how to do this for OBJ_COFF. */
+ if (fixP->fx_addsy != NULL
&& S_IS_DEFINED (fixP->fx_addsy)
&& S_GET_SEGMENT (fixP->fx_addsy) == seg)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("out of range branch"));
- newval = (value & 0x00ffffff) | (newval & 0xff000000);
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+ /* Must unshift the value before storing it in the addend. */
+ value <<= 2;
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ newval = newval & 0xff000000;
+ }
+ else
+ newval = (value & 0x00ffffff) | (newval & 0xff000000);
md_number_to_chars (buf, newval, INSN_SIZE);
break;
newval = md_chars_to_number (buf, INSN_SIZE);
#ifdef OBJ_ELF
- if (! target_oabi)
- value = fixP->fx_offset;
+ value = fixP->fx_offset;
#endif
hbit = (value >> 1) & 1;
value = (value >> 2) & 0x00ffffff;
value = (value + (newval & 0x00ffffff)) & 0x00ffffff;
- newval = value | (newval & 0xfe000000) | (hbit << 24);
+
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+ /* Must sign-extend and unshift the value before storing
+ it in the addend. */
+ value = SEXT24 (value);
+ value = (value << 2) | hbit;
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ newval = newval & 0xfe000000;
+ }
+ else
+ newval = value | (newval & 0xfe000000) | (hbit << 24);
md_number_to_chars (buf, newval, INSN_SIZE);
}
break;
if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("branch out of range"));
- newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ newval = newval & 0xff00;
+ }
+ else
+ newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
}
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("branch out of range"));
- newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ newval = newval & 0xf800;
+ }
+ else
+ newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
}
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
as_bad_where (fixP->fx_file, fixP->fx_line,
_("branch with link out of range"));
- newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12);
- newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
/* For a BLX instruction, make sure that the relocation is rounded up
to a word boundary. This follows the semantics of the instruction
which specifies that bit 1 of the target address will come from bit
1 of the base address. */
- newval2 = (newval2 + 1) & ~ 1;
+ value = (value + 1) & ~ 1;
+
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ newval = newval & 0xf800;
+ newval2 = newval2 & 0xf800;
+ }
+ else
+ {
+ newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12);
+ newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
+ }
md_number_to_chars (buf, newval, THUMB_SIZE);
md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
}
break;
case BFD_RELOC_8:
+ if (seg->use_rela_p && !fixP->fx_done)
+ break;
if (fixP->fx_done || fixP->fx_pcrel)
md_number_to_chars (buf, value, 1);
#ifdef OBJ_ELF
- else if (!target_oabi)
+ else
{
value = fixP->fx_offset;
md_number_to_chars (buf, value, 1);
break;
case BFD_RELOC_16:
+ if (seg->use_rela_p && !fixP->fx_done)
+ break;
if (fixP->fx_done || fixP->fx_pcrel)
md_number_to_chars (buf, value, 2);
#ifdef OBJ_ELF
- else if (!target_oabi)
+ else
{
value = fixP->fx_offset;
md_number_to_chars (buf, value, 2);
break;
#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_TLS_GD32:
+ case BFD_RELOC_ARM_TLS_LE32:
+ case BFD_RELOC_ARM_TLS_IE32:
+ case BFD_RELOC_ARM_TLS_LDM32:
+ case BFD_RELOC_ARM_TLS_LDO32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* fall through */
+
case BFD_RELOC_ARM_GOT32:
case BFD_RELOC_ARM_GOTOFF:
case BFD_RELOC_ARM_TARGET2:
+ if (seg->use_rela_p && !fixP->fx_done)
+ break;
md_number_to_chars (buf, 0, 4);
break;
#endif
case BFD_RELOC_ARM_ROSEGREL32:
case BFD_RELOC_ARM_SBREL32:
case BFD_RELOC_32_PCREL:
+ if (seg->use_rela_p && !fixP->fx_done)
+ break;
if (fixP->fx_done || fixP->fx_pcrel)
md_number_to_chars (buf, value, 4);
#ifdef OBJ_ELF
- else if (!target_oabi)
+ else
{
value = fixP->fx_offset;
md_number_to_chars (buf, value, 4);
fixP->fx_done = 0;
return;
- case BFD_RELOC_NONE:
+ case BFD_RELOC_UNUSED:
default:
as_bad_where (fixP->fx_file, fixP->fx_line,
_("bad relocation fixup type (%d)"), fixP->fx_r_type);
break;
}
+ case BFD_RELOC_NONE:
case BFD_RELOC_ARM_PCREL_BRANCH:
case BFD_RELOC_ARM_PCREL_BLX:
case BFD_RELOC_RVA:
case BFD_RELOC_ARM_SBREL32:
case BFD_RELOC_ARM_PREL31:
case BFD_RELOC_ARM_TARGET2:
+ case BFD_RELOC_ARM_TLS_LE32:
+ case BFD_RELOC_ARM_TLS_LDO32:
+ code = fixp->fx_r_type;
+ break;
+
+ case BFD_RELOC_ARM_TLS_GD32:
+ case BFD_RELOC_ARM_TLS_IE32:
+ case BFD_RELOC_ARM_TLS_LDM32:
+ /* BFD will include the symbol's address in the addend.
+ But we don't want that, so subtract it out again here. */
+ if (!S_IS_COMMON (fixp->fx_addsy))
+ reloc->addend -= (*reloc->sym_ptr_ptr)->value;
code = fixp->fx_r_type;
break;
#endif
switch (fixp->fx_r_type)
{
+ case BFD_RELOC_NONE: type = "NONE"; break;
case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break;
case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break;
case BFD_RELOC_ARM_SMI: type = "SMI"; break;
else
md_number_to_chars (to, inst.instruction, inst.size);
- if (inst.reloc.type != BFD_RELOC_NONE)
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
fix_new_arm (frag_now, to - frag_now->fr_literal,
inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
inst.reloc.type);
char *p;
char *start;
- /* Align the instruction.
- This may not be the right thing to do but ... */
-#if 0
- arm_align (2, 0);
-#endif
-
/* Align the previous label if needed. */
if (last_label_seen != NULL)
{
}
memset (&inst, '\0', sizeof (inst));
- inst.reloc.type = BFD_RELOC_NONE;
+ inst.reloc.type = BFD_RELOC_UNUSED;
skip_whitespace (str);
{"mthumb", N_("assemble Thumb code"), &thumb_mode, 1, NULL},
{"mthumb-interwork", N_("support ARM/Thumb interworking"),
&support_interwork, 1, NULL},
- {"moabi", N_("use old ABI (ELF only)"), &target_oabi, 1, NULL},
{"mapcs-32", N_("code uses 32-bit program counter"), &uses_apcs_26, 0, NULL},
{"mapcs-26", N_("code uses 26-bit program counter"), &uses_apcs_26, 1, NULL},
{"mapcs-float", N_("floating point args are in fp regs"), &uses_apcs_float,
{"arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1},
{"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm1026ejs", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm1026ej-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
+ {"arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
+ {"arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
{"arm1136js", ARM_ARCH_V6, FPU_NONE},
{"arm1136j-s", ARM_ARCH_V6, FPU_NONE},
{"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
{"armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP},
{"armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP},
{"armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP},
+ {"armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP},
+ {"armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP},
+ {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP},
+ {"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP},
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP},
{"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
{NULL, 0, 0}
};
#ifdef OBJ_ELF
-/* We only know hot to output GNU and ver 3 (AAELF) formats. */
+/* We only know how to output GNU and ver 4 (AAELF) formats. */
static struct arm_eabi_option_table arm_eabis[] =
{
{"gnu", EF_ARM_EABI_UNKNOWN},
- {"3", EF_ARM_EABI_VER3},
+ {"4", EF_ARM_EABI_VER4},
{NULL, 0}
};
#endif
elf_symbol_type * elf_sym;
elf_sym = elf_symbol (symbol_get_bfdsym (sym));
- bind = ELF_ST_BIND (elf_sym);
-
- /* If it's a .thumb_func, declare it as so,
- otherwise tag label as .code 16. */
- if (THUMB_IS_FUNC (sym))
- elf_sym->internal_elf_sym.st_info =
- ELF_ST_INFO (bind, STT_ARM_TFUNC);
- else
- elf_sym->internal_elf_sym.st_info =
- ELF_ST_INFO (bind, STT_ARM_16BIT);
+ bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info);
+
+ if (! bfd_is_arm_mapping_symbol_name (elf_sym->symbol.name))
+ {
+ /* If it's a .thumb_func, declare it as so,
+ otherwise tag label as .code 16. */
+ if (THUMB_IS_FUNC (sym))
+ elf_sym->internal_elf_sym.st_info =
+ ELF_ST_INFO (bind, STT_ARM_TFUNC);
+ else
+ elf_sym->internal_elf_sym.st_info =
+ ELF_ST_INFO (bind, STT_ARM_16BIT);
+ }
}
}
#endif
if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
|| fixP->fx_r_type == BFD_RELOC_ARM_GOT32
|| fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
|| fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
return 0;
return (target_big_endian
? "elf32-bigarm-symbian"
: "elf32-littlearm-symbian");
+#elif defined (TE_VXWORKS)
+ return (target_big_endian
+ ? "elf32-bigarm-vxworks"
+ : "elf32-littlearm-vxworks");
#else
if (target_big_endian)
- {
- if (target_oabi)
- return "elf32-bigarm-oabi";
- else
- return "elf32-bigarm";
- }
+ return "elf32-bigarm";
else
- {
- if (target_oabi)
- return "elf32-littlearm-oabi";
- else
- return "elf32-littlearm";
- }
+ return "elf32-littlearm";
#endif
}
do
{
bfd_reloc_code_real_type reloc;
+ char *sym_start;
+ int sym_len;
+ sym_start = input_line_pointer;
expression (& exp);
+ sym_len = input_line_pointer - sym_start;
if (exp.X_op == O_symbol
&& * input_line_pointer == '('
howto->name, nbytes);
else
{
- char *p = frag_more ((int) nbytes);
+ char *p;
int offset = nbytes - size;
-
+ char *saved_buf = alloca (sym_len), *saved_input;
+
+ /* We've parsed an expression stopping at O_symbol. But there
+ may be more expression left now that we have parsed the
+ relocation marker. Parse it again. */
+ saved_input = input_line_pointer - sym_len;
+ memcpy (saved_buf, saved_input, sym_len);
+ memmove (saved_input, sym_start, sym_len);
+ input_line_pointer = saved_input;
+ expression (& exp);
+ memcpy (saved_input, saved_buf, sym_len);
+ assert (input_line_pointer >= saved_input + sym_len);
+
+ p = frag_more ((int) nbytes);
fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
&exp, 0, reloc);
}
demand_empty_rest_of_line ();
}
+\f
+/* Code to deal with unwinding tables. */
-#endif /* OBJ_ELF */
+static void add_unwind_adjustsp (offsetT);
-/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
- of an rs_align_code fragment. */
+/* Switch to section NAME and create section if necessary. It's
+ rather ugly that we have to manipulate input_line_pointer but I
+ don't see any other way to accomplish the same thing without
+ changing obj-elf.c (which may be the Right Thing, in the end).
+ Copied from tc-ia64.c. */
-void
-arm_handle_align (fragS * fragP)
+static void
+set_section (char *name)
{
- static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
- static char const thumb_noop[2] = { 0xc0, 0x46 };
- static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
- static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+ char *saved_input_line_pointer;
- int bytes, fix, noop_size;
- char * p;
- const char * noop;
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = name;
+ obj_elf_section (0);
+ input_line_pointer = saved_input_line_pointer;
+}
- if (fragP->fr_type != rs_align_code)
- return;
+/* Cenerate and deferred unwind frame offset. */
- bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
- p = fragP->fr_literal + fragP->fr_fix;
- fix = 0;
+static void
+flush_pending_unwind (void)
+{
+ offsetT offset;
- if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
- bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+ offset = unwind.pending_offset;
+ unwind.pending_offset = 0;
+ if (offset != 0)
+ add_unwind_adjustsp (offset);
+}
- if (fragP->tc_frag_data)
+/* Add an opcode to this list for this function. Two-byte opcodes should
+ be passed as op[0] << 8 | op[1]. The list of opcodes is built in reverse
+ order. */
+
+static void
+add_unwind_opcode (valueT op, int length)
+{
+ /* Add any deferred stack adjustment. */
+ if (unwind.pending_offset)
+ flush_pending_unwind ();
+
+ unwind.sp_restored = 0;
+
+ if (unwind.opcode_count + length > unwind.opcode_alloc)
{
- if (target_big_endian)
- noop = thumb_bigend_noop;
+ unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
+ if (unwind.opcodes)
+ unwind.opcodes = xrealloc (unwind.opcodes,
+ unwind.opcode_alloc);
else
- noop = thumb_noop;
- noop_size = sizeof (thumb_noop);
+ unwind.opcodes = xmalloc (unwind.opcode_alloc);
}
- else
+ while (length > 0)
{
- if (target_big_endian)
- noop = arm_bigend_noop;
- else
- noop = arm_noop;
- noop_size = sizeof (arm_noop);
+ length--;
+ unwind.opcodes[unwind.opcode_count] = op & 0xff;
+ op >>= 8;
+ unwind.opcode_count++;
}
+}
- if (bytes & (noop_size - 1))
+/* Add unwind opcodes to adjust the stack pointer. */
+
+static void
+add_unwind_adjustsp (offsetT offset)
+{
+ valueT op;
+
+ if (offset > 0x200)
{
- fix = bytes & (noop_size - 1);
- memset (p, 0, fix);
- p += fix;
- bytes -= fix;
- }
+ /* We need at most 5 bytes to hold a 32-bit value in a uleb128. */
+ char bytes[5];
+ int n;
+ valueT o;
- while (bytes >= noop_size)
+ /* Long form: 0xb2, uleb128. */
+ /* This might not fit in a word so add the individual bytes,
+ remembering the list is built in reverse order. */
+ o = (valueT) ((offset - 0x204) >> 2);
+ if (o == 0)
+ add_unwind_opcode (0, 1);
+
+ /* Calculate the uleb128 encoding of the offset. */
+ n = 0;
+ while (o)
+ {
+ bytes[n] = o & 0x7f;
+ o >>= 7;
+ if (o)
+ bytes[n] |= 0x80;
+ n++;
+ }
+ /* Add the insn. */
+ for (; n; n--)
+ add_unwind_opcode (bytes[n - 1], 1);
+ add_unwind_opcode (0xb2, 1);
+ }
+ else if (offset > 0x100)
{
- memcpy (p, noop, noop_size);
- p += noop_size;
- bytes -= noop_size;
- fix += noop_size;
+ /* Two short opcodes. */
+ add_unwind_opcode (0x3f, 1);
+ op = (offset - 0x104) >> 2;
+ add_unwind_opcode (op, 1);
+ }
+ else if (offset > 0)
+ {
+ /* Short opcode. */
+ op = (offset - 4) >> 2;
+ add_unwind_opcode (op, 1);
+ }
+ else if (offset < 0)
+ {
+ offset = -offset;
+ while (offset > 0x100)
+ {
+ add_unwind_opcode (0x7f, 1);
+ offset -= 0x100;
+ }
+ op = ((offset - 4) >> 2) | 0x40;
+ add_unwind_opcode (op, 1);
}
+}
- fragP->fr_fix += fix;
- fragP->fr_var = noop_size;
+/* Finish the list of unwind opcodes for this function. */
+static void
+finish_unwind_opcodes (void)
+{
+ valueT op;
+
+ if (unwind.fp_used)
+ {
+ /* Adjust sp as neccessary. */
+ unwind.pending_offset += unwind.fp_offset - unwind.frame_size;
+ flush_pending_unwind ();
+
+ /* After restoring sp from the frame pointer. */
+ op = 0x90 | unwind.fp_reg;
+ add_unwind_opcode (op, 1);
+ }
+ else
+ flush_pending_unwind ();
}
-/* Called from md_do_align. Used to create an alignment
- frag in a code section. */
-void
-arm_frag_align_code (int n, int max)
+/* Start an exception table entry. If idx is nonzero this is an index table
+ entry. */
+
+static void
+start_unwind_section (const segT text_seg, int idx)
{
- char * p;
+ const char * text_name;
+ const char * prefix;
+ const char * prefix_once;
+ size_t prefix_len;
+ size_t text_len;
+ char * sec_name;
+ size_t sec_name_len;
- /* We assume that there will never be a requirement
- to support alignments greater than 32 bytes. */
- if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
- as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+ if (idx)
+ {
+ prefix = ELF_STRING_ARM_unwind;
+ prefix_once = ELF_STRING_ARM_unwind_once;
+ }
+ else
+ {
+ prefix = ELF_STRING_ARM_unwind_info;
+ prefix_once = ELF_STRING_ARM_unwind_info_once;
+ }
- p = frag_var (rs_align_code,
- MAX_MEM_FOR_RS_ALIGN_CODE,
- 1,
- (relax_substateT) max,
- (symbolS *) NULL,
- (offsetT) n,
- (char *) NULL);
- *p = 0;
+ text_name = segment_name (text_seg);
+ if (streq (text_name, ".text"))
+ text_name = "";
+
+ if (strncmp (text_name, ".gnu.linkonce.t.",
+ strlen (".gnu.linkonce.t.")) == 0)
+ {
+ prefix = prefix_once;
+ text_name += strlen (".gnu.linkonce.t.");
+ }
+
+ prefix_len = strlen (prefix);
+ text_len = strlen (text_name);
+ sec_name_len = prefix_len + text_len;
+ sec_name = alloca (sec_name_len + 1);
+ memcpy (sec_name, prefix, prefix_len);
+ memcpy (sec_name + prefix_len, text_name, text_len);
+ sec_name[prefix_len + text_len] = '\0';
+
+ /* Handle COMDAT group. */
+ if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
+ {
+ char *section;
+ size_t len, group_name_len;
+ const char *group_name = elf_group_name (text_seg);
+
+ if (group_name == NULL)
+ {
+ as_bad ("Group section `%s' has no group signature",
+ segment_name (text_seg));
+ ignore_rest_of_line ();
+ return;
+ }
+ /* We have to construct a fake section directive. */
+ group_name_len = strlen (group_name);
+ if (idx)
+ prefix_len = 13;
+ else
+ prefix_len = 16;
+
+ len = (sec_name_len
+ + prefix_len /* ,"aG",%sectiontype, */
+ + group_name_len /* ,group_name */
+ + 7); /* ,comdat */
+
+ section = alloca (len + 1);
+ memcpy (section, sec_name, sec_name_len);
+ if (idx)
+ memcpy (section + sec_name_len, ",\"aG\",%exidx,", 13);
+ else
+ memcpy (section + sec_name_len, ",\"aG\",%progbits,", 16);
+ memcpy (section + sec_name_len + prefix_len, group_name, group_name_len);
+ memcpy (section + len - 7, ",comdat", 7);
+ section [len] = '\0';
+ set_section (section);
+ }
+ else
+ {
+ set_section (sec_name);
+ bfd_set_section_flags (stdoutput, now_seg,
+ SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+ }
+
+ /* Set the setion link for index tables. */
+ if (idx)
+ elf_linked_to_section (now_seg) = text_seg;
}
-/* Perform target specific initialisation of a frag. */
-void
-arm_init_frag (fragS * fragP)
+/* Start an unwind table entry. HAVE_DATA is nonzero if we have additional
+ personality routine data. Returns zero, or the index table value for
+ and inline entry. */
+
+static valueT
+create_unwind_entry (int have_data)
{
- /* Record whether this frag is in an ARM or a THUMB area. */
- fragP->tc_frag_data = thumb_mode;
-}
+ int size;
+ addressT where;
+ char *ptr;
+ /* The current word of data. */
+ valueT data;
+ /* The number of bytes left in this word. */
+ int n;
+
+ finish_unwind_opcodes ();
+
+ /* Remember the current text section. */
+ unwind.saved_seg = now_seg;
+ unwind.saved_subseg = now_subseg;
+
+ start_unwind_section (now_seg, 0);
+
+ if (unwind.personality_routine == NULL)
+ {
+ if (unwind.personality_index == -2)
+ {
+ if (have_data)
+ as_bad (_("handerdata in cantunwind frame"));
+ return 1; /* EXIDX_CANTUNWIND. */
+ }
+
+ /* Use a default personality routine if none is specified. */
+ if (unwind.personality_index == -1)
+ {
+ if (unwind.opcode_count > 3)
+ unwind.personality_index = 1;
+ else
+ unwind.personality_index = 0;
+ }
+
+ /* Space for the personality routine entry. */
+ if (unwind.personality_index == 0)
+ {
+ if (unwind.opcode_count > 3)
+ as_bad (_("too many unwind opcodes for personality routine 0"));
+
+ if (!have_data)
+ {
+ /* All the data is inline in the index table. */
+ data = 0x80;
+ n = 3;
+ while (unwind.opcode_count > 0)
+ {
+ unwind.opcode_count--;
+ data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+ n--;
+ }
+
+ /* Pad with "finish" opcodes. */
+ while (n--)
+ data = (data << 8) | 0xb0;
+
+ return data;
+ }
+ size = 0;
+ }
+ else
+ /* We get two opcodes "free" in the first word. */
+ size = unwind.opcode_count - 2;
+ }
+ else
+ /* An extra byte is required for the opcode count. */
+ size = unwind.opcode_count + 1;
+
+ size = (size + 3) >> 2;
+ if (size > 0xff)
+ as_bad (_("too many unwind opcodes"));
+
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+ unwind.table_entry = expr_build_dot ();
+
+ /* Allocate the table entry. */
+ ptr = frag_more ((size << 2) + 4);
+ where = frag_now_fix () - ((size << 2) + 4);
+
+ switch (unwind.personality_index)
+ {
+ case -1:
+ /* ??? Should this be a PLT generating relocation? */
+ /* Custom personality routine. */
+ fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ where += 4;
+ ptr += 4;
+
+ /* Set the first byte to the number of additional words. */
+ data = size - 1;
+ n = 3;
+ break;
+
+ /* ABI defined personality routines. */
+ case 0:
+ /* Three opcodes bytes are packed into the first word. */
+ data = 0x80;
+ n = 3;
+ break;
+
+ case 1:
+ case 2:
+ /* The size and first two opcode bytes go in the first word. */
+ data = ((0x80 + unwind.personality_index) << 8) | size;
+ n = 2;
+ break;
+
+ default:
+ /* Should never happen. */
+ abort ();
+ }
+
+ /* Pack the opcodes into words (MSB first), reversing the list at the same
+ time. */
+ while (unwind.opcode_count > 0)
+ {
+ if (n == 0)
+ {
+ md_number_to_chars (ptr, data, 4);
+ ptr += 4;
+ n = 4;
+ data = 0;
+ }
+ unwind.opcode_count--;
+ n--;
+ data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+ }
+
+ /* Finish off the last word. */
+ if (n < 4)
+ {
+ /* Pad with "finish" opcodes. */
+ while (n--)
+ data = (data << 8) | 0xb0;
+
+ md_number_to_chars (ptr, data, 4);
+ }
+
+ if (!have_data)
+ {
+ /* Add an empty descriptor if there is no user-specified data. */
+ ptr = frag_more (4);
+ md_number_to_chars (ptr, 0, 4);
+ }
+
+ return 0;
+}
+
+
+/* Parse an unwind_fnstart directive. Simply records the current location. */
+
+static void
+s_arm_unwind_fnstart (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ /* Mark the start of the function. */
+ unwind.proc_start = expr_build_dot ();
+
+ /* Reset the rest of the unwind info. */
+ unwind.opcode_count = 0;
+ unwind.table_entry = NULL;
+ unwind.personality_routine = NULL;
+ unwind.personality_index = -1;
+ unwind.frame_size = 0;
+ unwind.fp_offset = 0;
+ unwind.fp_reg = 13;
+ unwind.fp_used = 0;
+ unwind.sp_restored = 0;
+}
+
+
+/* Parse a handlerdata directive. Creates the exception handling table entry
+ for the function. */
+
+static void
+s_arm_unwind_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (unwind.table_entry)
+ as_bad (_("dupicate .handlerdata directive"));
+
+ create_unwind_entry (1);
+}
+
+/* Parse an unwind_fnend directive. Generates the index table entry. */
+
+static void
+s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED)
+{
+ long where;
+ char *ptr;
+ valueT val;
+
+ demand_empty_rest_of_line ();
+
+ /* Add eh table entry. */
+ if (unwind.table_entry == NULL)
+ val = create_unwind_entry (0);
+ else
+ val = 0;
+
+ /* Add index table entry. This is two words. */
+ start_unwind_section (unwind.saved_seg, 1);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+ ptr = frag_more (8);
+ where = frag_now_fix () - 8;
+
+ /* Self relative offset of the function start. */
+ fix_new (frag_now, where, 4, unwind.proc_start, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ /* Indicate dependency on EHABI-defined personality routines to the
+ linker, if it hasn't been done already. */
+ if (unwind.personality_index >= 0 && unwind.personality_index < 3)
+ {
+ char *name[] = { "__aeabi_unwind_cpp_pr0",
+ "__aeabi_unwind_cpp_pr1",
+ "__aeabi_unwind_cpp_pr2" };
+ if (!(marked_pr_dependency & (1 << unwind.personality_index)))
+ {
+ symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
+ fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+ marked_pr_dependency |= 1 << unwind.personality_index;
+ seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+ = marked_pr_dependency;
+ }
+ }
+
+ if (val)
+ /* Inline exception table entry. */
+ md_number_to_chars (ptr + 4, val, 4);
+ else
+ /* Self relative offset of the table entry. */
+ fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ /* Restore the original section. */
+ subseg_set (unwind.saved_seg, unwind.saved_subseg);
+}
+
+
+/* Parse an unwind_cantunwind directive. */
+
+static void
+s_arm_unwind_cantunwind (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("personality routine specified for cantunwind frame"));
+
+ unwind.personality_index = -2;
+}
+
+
+/* Parse a personalityindex directive. */
+
+static void
+s_arm_unwind_personalityindex (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("duplicate .personalityindex directive"));
+
+ SKIP_WHITESPACE ();
+
+ expression (&exp);
+
+ if (exp.X_op != O_constant
+ || exp.X_add_number < 0 || exp.X_add_number > 15)
+ {
+ as_bad (_("bad personality routine number"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ unwind.personality_index = exp.X_add_number;
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse a personality directive. */
+
+static void
+s_arm_unwind_personality (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name, *p, c;
+
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("duplicate .personality directive"));
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ unwind.personality_routine = symbol_find_or_make (name);
+ *p = c;
+ SKIP_WHITESPACE ();
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse a directive saving core registers. */
+
+static void
+s_arm_unwind_save_core (void)
+{
+ valueT op;
+ long range;
+ int n;
+
+ SKIP_WHITESPACE ();
+ range = reg_list (&input_line_pointer);
+ if (range == FAIL)
+ {
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ /* Turn .unwind_movsp ip followed by .unwind_save {..., ip, ...}
+ into .unwind_save {..., sp...}. We aren't bothered about the value of
+ ip because it is clobbered by calls. */
+ if (unwind.sp_restored && unwind.fp_reg == 12
+ && (range & 0x3000) == 0x1000)
+ {
+ unwind.opcode_count--;
+ unwind.sp_restored = 0;
+ range = (range | 0x2000) & ~0x1000;
+ unwind.pending_offset = 0;
+ }
+
+ /* See if we can use the short opcodes. These pop a block of upto 8
+ registers starting with r4, plus maybe r14. */
+ for (n = 0; n < 8; n++)
+ {
+ /* Break at the first non-saved register. */
+ if ((range & (1 << (n + 4))) == 0)
+ break;
+ }
+ /* See if there are any other bits set. */
+ if (n == 0 || (range & (0xfff0 << n) & 0xbff0) != 0)
+ {
+ /* Use the long form. */
+ op = 0x8000 | ((range >> 4) & 0xfff);
+ add_unwind_opcode (op, 2);
+ }
+ else
+ {
+ /* Use the short form. */
+ if (range & 0x4000)
+ op = 0xa8; /* Pop r14. */
+ else
+ op = 0xa0; /* Do not pop r14. */
+ op |= (n - 1);
+ add_unwind_opcode (op, 1);
+ }
+
+ /* Pop r0-r3. */
+ if (range & 0xf)
+ {
+ op = 0xb100 | (range & 0xf);
+ add_unwind_opcode (op, 2);
+ }
+
+ /* Record the number of bytes pushed. */
+ for (n = 0; n < 16; n++)
+ {
+ if (range & (1 << n))
+ unwind.frame_size += 4;
+ }
+}
+
+
+/* Parse a directive saving FPA registers. */
+
+static void
+s_arm_unwind_save_fpa (int reg)
+{
+ expressionS exp;
+ int num_regs;
+ valueT op;
+
+ /* Get Number of registers to transfer. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ expression (&exp);
+ else
+ exp.X_op = O_illegal;
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected , <constant>"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ num_regs = exp.X_add_number;
+
+ if (num_regs < 1 || num_regs > 4)
+ {
+ as_bad (_("number of registers must be in the range [1:4]"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (reg == 4)
+ {
+ /* Short form. */
+ op = 0xb4 | (num_regs - 1);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xc800 | (reg << 4) | (num_regs - 1);
+ add_unwind_opcode (op, 2);
+ }
+ unwind.frame_size += num_regs * 12;
+}
+
+
+/* Parse a directive saving VFP registers. */
+
+static void
+s_arm_unwind_save_vfp (void)
+{
+ int count;
+ int reg;
+ valueT op;
+
+ count = vfp_parse_reg_list (&input_line_pointer, ®, 1);
+ if (count == FAIL)
+ {
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (reg == 8)
+ {
+ /* Short form. */
+ op = 0xb8 | (count - 1);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xb300 | (reg << 4) | (count - 1);
+ add_unwind_opcode (op, 2);
+ }
+ unwind.frame_size += count * 8 + 4;
+}
+
+
+/* Parse a directive saving iWMMXt registers. */
+
+static void
+s_arm_unwind_save_wmmx (void)
+{
+ int reg;
+ int hi_reg;
+ int i;
+ unsigned wcg_mask;
+ unsigned wr_mask;
+ valueT op;
+
+ if (*input_line_pointer == '{')
+ input_line_pointer++;
+
+ wcg_mask = 0;
+ wr_mask = 0;
+ do
+ {
+ reg = arm_reg_parse (&input_line_pointer,
+ all_reg_maps[REG_TYPE_IWMMXT].htab);
+
+ if (wr_register (reg))
+ {
+ i = reg & ~WR_PREFIX;
+ if (wr_mask >> i)
+ as_tsktsk (_("register list not in ascending order"));
+ wr_mask |= 1 << i;
+ }
+ else if (wcg_register (reg))
+ {
+ i = (reg & ~WC_PREFIX) - 8;
+ if (wcg_mask >> i)
+ as_tsktsk (_("register list not in ascending order"));
+ wcg_mask |= 1 << i;
+ }
+ else
+ {
+ as_bad (_("expected wr or wcgr"));
+ goto error;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '-')
+ {
+ hi_reg = arm_reg_parse (&input_line_pointer,
+ all_reg_maps[REG_TYPE_IWMMXT].htab);
+ if (wr_register (reg) && wr_register (hi_reg))
+ {
+ for (; reg < hi_reg; reg++)
+ wr_mask |= 1 << (reg & ~WR_PREFIX);
+ }
+ else if (wcg_register (reg) && wcg_register (hi_reg))
+ {
+ for (; reg < hi_reg; reg++)
+ wcg_mask |= 1 << ((reg & ~WC_PREFIX) - 8);
+ }
+ else
+ {
+ as_bad (_("bad register range"));
+ goto error;
+ }
+ }
+ }
+ while (skip_past_comma (&input_line_pointer) != FAIL);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '}')
+ input_line_pointer++;
+
+ demand_empty_rest_of_line ();
+
+ if (wr_mask && wcg_mask)
+ {
+ as_bad (_("inconsistent register types"));
+ goto error;
+ }
+
+ /* Generate any deferred opcodes becuuse we're going to be looking at
+ the list. */
+ flush_pending_unwind ();
+
+ if (wcg_mask)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ if (wcg_mask & (1 << i))
+ unwind.frame_size += 4;
+ }
+ op = 0xc700 | wcg_mask;
+ add_unwind_opcode (op, 2);
+ }
+ else
+ {
+ for (i = 0; i < 16; i++)
+ {
+ if (wr_mask & (1 << i))
+ unwind.frame_size += 8;
+ }
+ /* Attempt to combine with a previous opcode. We do this because gcc
+ likes to output separate unwind directives for a single block of
+ registers. */
+ if (unwind.opcode_count > 0)
+ {
+ i = unwind.opcodes[unwind.opcode_count - 1];
+ if ((i & 0xf8) == 0xc0)
+ {
+ i &= 7;
+ /* Only merge if the blocks are contiguous. */
+ if (i < 6)
+ {
+ if ((wr_mask & 0xfe00) == (1 << 9))
+ {
+ wr_mask |= ((1 << (i + 11)) - 1) & 0xfc00;
+ unwind.opcode_count--;
+ }
+ }
+ else if (i == 6 && unwind.opcode_count >= 2)
+ {
+ i = unwind.opcodes[unwind.opcode_count - 2];
+ reg = i >> 4;
+ i &= 0xf;
+
+ op = 0xffff << (reg - 1);
+ if (reg > 0
+ || ((wr_mask & op) == (1u << (reg - 1))))
+ {
+ op = (1 << (reg + i + 1)) - 1;
+ op &= ~((1 << reg) - 1);
+ wr_mask |= op;
+ unwind.opcode_count -= 2;
+ }
+ }
+ }
+ }
+
+ hi_reg = 15;
+ /* We want to generate opcodes in the order the registers have been
+ saved, ie. descending order. */
+ for (reg = 15; reg >= -1; reg--)
+ {
+ /* Save registers in blocks. */
+ if (reg < 0
+ || !(wr_mask & (1 << reg)))
+ {
+ /* We found an unsaved reg. Generate opcodes to save the
+ preceeding block. */
+ if (reg != hi_reg)
+ {
+ if (reg == 9)
+ {
+ /* Short form. */
+ op = 0xc0 | (hi_reg - 10);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xc600 | ((reg + 1) << 4) | ((hi_reg - reg) - 1);
+ add_unwind_opcode (op, 2);
+ }
+ }
+ hi_reg = reg - 1;
+ }
+ }
+ }
+ return;
+error:
+ ignore_rest_of_line ();
+}
+
+
+/* Parse an unwind_save directive. */
+
+static void
+s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED)
+{
+ char *saved_ptr;
+ int reg;
+
+ /* Figure out what sort of save we have. */
+ SKIP_WHITESPACE ();
+ saved_ptr = input_line_pointer;
+
+ reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_FN].htab);
+ if (reg != FAIL)
+ {
+ s_arm_unwind_save_fpa (reg);
+ return;
+ }
+
+ if (*input_line_pointer == '{')
+ input_line_pointer++;
+
+ SKIP_WHITESPACE ();
+
+ reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_RN].htab);
+ if (reg != FAIL)
+ {
+ input_line_pointer = saved_ptr;
+ s_arm_unwind_save_core ();
+ return;
+ }
+
+ reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_DN].htab);
+ if (reg != FAIL)
+ {
+ input_line_pointer = saved_ptr;
+ s_arm_unwind_save_vfp ();
+ return;
+ }
+
+ reg = arm_reg_parse (&input_line_pointer,
+ all_reg_maps[REG_TYPE_IWMMXT].htab);
+ if (reg != FAIL)
+ {
+ input_line_pointer = saved_ptr;
+ s_arm_unwind_save_wmmx ();
+ return;
+ }
+
+ /* TODO: Maverick registers. */
+ as_bad (_("unrecognised register"));
+}
+
+
+/* Parse an unwind_movsp directive. */
+
+static void
+s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
+{
+ int reg;
+ valueT op;
+
+ SKIP_WHITESPACE ();
+ reg = reg_required_here (&input_line_pointer, -1);
+ if (reg == FAIL)
+ {
+ as_bad (_("ARM register expected"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (reg == 13 || reg == 15)
+ {
+ as_bad (_("r%d not permitted in .unwind_movsp directive"), reg);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (unwind.fp_reg != 13)
+ as_bad (_("unexpected .unwind_movsp directive"));
+
+ /* Generate opcode to restore the value. */
+ op = 0x90 | reg;
+ add_unwind_opcode (op, 1);
+
+ /* Record the information for later. */
+ unwind.fp_reg = reg;
+ unwind.fp_offset = unwind.frame_size;
+ unwind.sp_restored = 1;
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse #<number>. */
+
+static int
+require_hashconst (int * val)
+{
+ expressionS exp;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer++;
+ expression (&exp);
+ }
+ else
+ exp.X_op = O_illegal;
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected #constant"));
+ ignore_rest_of_line ();
+ return FAIL;
+ }
+ *val = exp.X_add_number;
+ return SUCCESS;
+}
+
+/* Parse an unwind_pad directive. */
+
+static void
+s_arm_unwind_pad (int ignored ATTRIBUTE_UNUSED)
+{
+ int offset;
+
+ if (require_hashconst (&offset) == FAIL)
+ return;
+
+ if (offset & 3)
+ {
+ as_bad (_("stack increment must be multiple of 4"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Don't generate any opcodes, just record the details for later. */
+ unwind.frame_size += offset;
+ unwind.pending_offset += offset;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse an unwind_setfp directive. */
+
+static void
+s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
+{
+ int sp_reg;
+ int fp_reg;
+ int offset;
+
+ fp_reg = reg_required_here (&input_line_pointer, -1);
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ sp_reg = FAIL;
+ else
+ sp_reg = reg_required_here (&input_line_pointer, -1);
+
+ if (fp_reg == FAIL || sp_reg == FAIL)
+ {
+ as_bad (_("expected <reg>, <reg>"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Optonal constant. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ {
+ if (require_hashconst (&offset) == FAIL)
+ return;
+ }
+ else
+ offset = 0;
+
+ demand_empty_rest_of_line ();
+
+ if (sp_reg != 13 && sp_reg != unwind.fp_reg)
+ {
+ as_bad (_("register must be either sp or set by a previous"
+ "unwind_movsp directive"));
+ return;
+ }
+
+ /* Don't generate any opcodes, just record the information for later. */
+ unwind.fp_reg = fp_reg;
+ unwind.fp_used = 1;
+ if (sp_reg == 13)
+ unwind.fp_offset = unwind.frame_size - offset;
+ else
+ unwind.fp_offset -= offset;
+}
+
+/* Parse an unwind_raw directive. */
+
+static void
+s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ /* This is an arbitary limit. */
+ unsigned char op[16];
+ int count;
+
+ SKIP_WHITESPACE ();
+ expression (&exp);
+ if (exp.X_op == O_constant
+ && skip_past_comma (&input_line_pointer) != FAIL)
+ {
+ unwind.frame_size += exp.X_add_number;
+ expression (&exp);
+ }
+ else
+ exp.X_op = O_illegal;
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected <offset>, <opcode>"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ count = 0;
+
+ /* Parse the opcode. */
+ for (;;)
+ {
+ if (count >= 16)
+ {
+ as_bad (_("unwind opcode too long"));
+ ignore_rest_of_line ();
+ }
+ if (exp.X_op != O_constant || exp.X_add_number & ~0xff)
+ {
+ as_bad (_("invalid unwind opcode"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op[count++] = exp.X_add_number;
+
+ /* Parse the next byte. */
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ break;
+
+ expression (&exp);
+ }
+
+ /* Add the opcode bytes in reverse order. */
+ while (count--)
+ add_unwind_opcode (op[count], 1);
+
+ demand_empty_rest_of_line ();
+}
+
+#endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+arm_handle_align (fragS * fragP)
+{
+ static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+ static char const thumb_noop[2] = { 0xc0, 0x46 };
+ static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+ static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+
+ 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;
+
+ if (fragP->tc_frag_data)
+ {
+ if (target_big_endian)
+ noop = thumb_bigend_noop;
+ else
+ noop = thumb_noop;
+ noop_size = sizeof (thumb_noop);
+ }
+ else
+ {
+ if (target_big_endian)
+ noop = arm_bigend_noop;
+ else
+ noop = arm_noop;
+ noop_size = sizeof (arm_noop);
+ }
+
+ if (bytes & (noop_size - 1))
+ {
+ fix = bytes & (noop_size - 1);
+ 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_var = noop_size;
+}
+
+/* Called from md_do_align. Used to create an alignment
+ frag in a code section. */
+
+void
+arm_frag_align_code (int n, int max)
+{
+ char * p;
+
+ /* We assume that there will never be a requirement
+ to support alignments greater than 32 bytes. */
+ if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+ as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+
+ p = frag_var (rs_align_code,
+ MAX_MEM_FOR_RS_ALIGN_CODE,
+ 1,
+ (relax_substateT) max,
+ (symbolS *) NULL,
+ (offsetT) n,
+ (char *) NULL);
+ *p = 0;
+}
+
+/* Perform target specific initialisation of a frag. */
+
+void
+arm_init_frag (fragS * fragP)
+{
+ /* Record whether this frag is in an ARM or a THUMB area. */
+ fragP->tc_frag_data = thumb_mode;
+}
+
+#ifdef OBJ_ELF
+
+/* Convert REGNAME to a DWARF-2 register number. */
+
+int
+tc_arm_regname_to_dw2regnum (const char *regname)
+{
+ unsigned int i;
+
+ for (i = 0; rn_table[i].name; i++)
+ if (streq (regname, rn_table[i].name))
+ return rn_table[i].number;
+
+ return -1;
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure. */
+
+void
+tc_arm_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (REG_SP, 0);
+}
+#endif
/* This table describes all the machine specific pseudo-ops the assembler
has to support. The fields are:
{ "word", s_arm_elf_cons, 4 },
{ "long", s_arm_elf_cons, 4 },
{ "rel31", s_arm_rel31, 0 },
+ { "fnstart", s_arm_unwind_fnstart, 0 },
+ { "fnend", s_arm_unwind_fnend, 0 },
+ { "cantunwind", s_arm_unwind_cantunwind, 0 },
+ { "personality", s_arm_unwind_personality, 0 },
+ { "personalityindex", s_arm_unwind_personalityindex, 0 },
+ { "handlerdata", s_arm_unwind_handlerdata, 0 },
+ { "save", s_arm_unwind_save, 0 },
+ { "movsp", s_arm_unwind_movsp, 0 },
+ { "pad", s_arm_unwind_pad, 0 },
+ { "setfp", s_arm_unwind_setfp, 0 },
+ { "unwind_raw", s_arm_unwind_raw, 0 },
#else
{ "word", cons, 4},
#endif