along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
-#include "elf/arm.h"
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "elf-bfd.h"
+#include "elf/arm.h"
#ifndef NUM_ELEM
#define NUM_ELEM(a) (sizeof (a) / (sizeof (a)[0]))
/* The size of the thread control block. */
#define TCB_SIZE 8
+#define NUM_KNOWN_ATTRIBUTES 32
+
+typedef struct aeabi_attribute
+{
+ int type;
+ unsigned int i;
+ char *s;
+} aeabi_attribute;
+
+typedef struct aeabi_attribute_list
+{
+ struct aeabi_attribute_list *next;
+ int tag;
+ aeabi_attribute attr;
+} aeabi_attribute_list;
+
struct elf32_arm_obj_tdata
{
struct elf_obj_tdata root;
/* tls_type for each local got entry. */
char *local_got_tls_type;
+
+ aeabi_attribute known_eabi_attributes[NUM_KNOWN_ATTRIBUTES];
+ aeabi_attribute_list *other_eabi_attributes;
};
#define elf32_arm_tdata(abfd) \
}
}
+
+static int
+uleb128_size (unsigned int i)
+{
+ int size;
+ size = 1;
+ while (i >= 0x80)
+ {
+ i >>= 7;
+ size++;
+ }
+ return size;
+}
+
+/* Return TRUE if the attribute has the default value (0/""). */
+static bfd_boolean
+is_default_attr (aeabi_attribute *attr)
+{
+ if ((attr->type & 1) && attr->i != 0)
+ return FALSE;
+ if ((attr->type & 2) && attr->s && *attr->s)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Return the size of a single attribute. */
+static bfd_vma
+eabi_attr_size(int tag, aeabi_attribute *attr)
+{
+ bfd_vma size;
+
+ if (is_default_attr (attr))
+ return 0;
+
+ size = uleb128_size (tag);
+ if (attr->type & 1)
+ size += uleb128_size (attr->i);
+ if (attr->type & 2)
+ size += strlen ((char *)attr->s) + 1;
+ return size;
+}
+
+/* Returns the size of the eabi object attributess section. */
+bfd_vma
+elf32_arm_eabi_attr_size (bfd *abfd)
+{
+ bfd_vma size;
+ aeabi_attribute *attr;
+ aeabi_attribute_list *list;
+ int i;
+
+ attr = elf32_arm_tdata (abfd)->known_eabi_attributes;
+ size = 16; /* 'A' <size> "aeabi" 0x1 <size>. */
+ for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+ size += eabi_attr_size (i, &attr[i]);
+
+ for (list = elf32_arm_tdata (abfd)->other_eabi_attributes;
+ list;
+ list = list->next)
+ size += eabi_attr_size (list->tag, &list->attr);
+
+ return size;
+}
+
+static bfd_byte *
+write_uleb128 (bfd_byte *p, unsigned int val)
+{
+ bfd_byte c;
+ do
+ {
+ c = val & 0x7f;
+ val >>= 7;
+ if (val)
+ c |= 0x80;
+ *(p++) = c;
+ }
+ while (val);
+ return p;
+}
+
+/* Write attribute ATTR to butter P, and return a pointer to the following
+ byte. */
+static bfd_byte *
+write_eabi_attribute (bfd_byte *p, int tag, aeabi_attribute *attr)
+{
+ /* Suppress default entries. */
+ if (is_default_attr(attr))
+ return p;
+
+ p = write_uleb128 (p, tag);
+ if (attr->type & 1)
+ p = write_uleb128 (p, attr->i);
+ if (attr->type & 2)
+ {
+ int len;
+
+ len = strlen (attr->s) + 1;
+ memcpy (p, attr->s, len);
+ p += len;
+ }
+
+ return p;
+}
+
+/* Write the contents of the eabi attributes section to p. */
+void
+elf32_arm_set_eabi_attr_contents (bfd *abfd, bfd_byte *contents, bfd_vma size)
+{
+ bfd_byte *p;
+ aeabi_attribute *attr;
+ aeabi_attribute_list *list;
+ int i;
+
+ p = contents;
+ *(p++) = 'A';
+ bfd_put_32 (abfd, size - 1, p);
+ p += 4;
+ memcpy (p, "aeabi", 6);
+ p += 6;
+ *(p++) = Tag_File;
+ bfd_put_32 (abfd, size - 11, p);
+ p += 4;
+
+ attr = elf32_arm_tdata (abfd)->known_eabi_attributes;
+ for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+ p = write_eabi_attribute (p, i, &attr[i]);
+
+ for (list = elf32_arm_tdata (abfd)->other_eabi_attributes;
+ list;
+ list = list->next)
+ p = write_eabi_attribute (p, list->tag, &list->attr);
+}
+
+/* Override final_link to handle EABI object attribute sections. */
+
+static bfd_boolean
+elf32_arm_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+ asection *o;
+ struct bfd_link_order *p;
+ asection *attr_section = NULL;
+ bfd_byte *contents;
+ bfd_vma size = 0;
+
+ /* elf32_arm_merge_private_bfd_data will already have merged the
+ object attributes. Remove the input sections from the link, and set
+ the contents of the output secton. */
+ for (o = abfd->sections; o != NULL; o = o->next)
+ {
+ if (strcmp (o->name, ".ARM.attributes") == 0)
+ {
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
+ {
+ asection *input_section;
+
+ if (p->type != bfd_indirect_link_order)
+ continue;
+ input_section = p->u.indirect.section;
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ size = elf32_arm_eabi_attr_size (abfd);
+ bfd_set_section_size (abfd, o, size);
+ attr_section = o;
+ /* Skip this section later on. */
+ o->map_head.link_order = NULL;
+ }
+ }
+ /* Invoke the ELF linker to do all the work. */
+ if (!bfd_elf_final_link (abfd, info))
+ return FALSE;
+
+ if (attr_section)
+ {
+ contents = bfd_malloc(size);
+ if (contents == NULL)
+ return FALSE;
+ elf32_arm_set_eabi_attr_contents (abfd, contents, size);
+ bfd_set_section_contents (abfd, attr_section, contents, 0, size);
+ free (contents);
+ }
+ return TRUE;
+}
+
+
/* Add INCREMENT to the reloc (of type HOWTO) at ADDRESS. */
static void
arm_add_to_rel (bfd * abfd,
return TRUE;
}
+/* Allocate/find an object attribute. */
+static aeabi_attribute *
+elf32_arm_new_eabi_attr (bfd *abfd, int tag)
+{
+ aeabi_attribute *attr;
+ aeabi_attribute_list *list;
+ aeabi_attribute_list *p;
+ aeabi_attribute_list **lastp;
+
+
+ if (tag < NUM_KNOWN_ATTRIBUTES)
+ {
+ /* Knwon tags are preallocated. */
+ attr = &elf32_arm_tdata (abfd)->known_eabi_attributes[tag];
+ }
+ else
+ {
+ /* Create a new tag. */
+ list = (aeabi_attribute_list *)
+ bfd_alloc (abfd, sizeof (aeabi_attribute_list));
+ memset (list, 0, sizeof (aeabi_attribute_list));
+ list->tag = tag;
+ /* Keep the tag list in order. */
+ lastp = &elf32_arm_tdata (abfd)->other_eabi_attributes;
+ for (p = *lastp; p; p = p->next)
+ {
+ if (tag < p->tag)
+ break;
+ lastp = &p->next;
+ }
+ list->next = *lastp;
+ *lastp = list;
+ attr = &list->attr;
+ }
+
+ return attr;
+}
+
+void
+elf32_arm_add_eabi_attr_int (bfd *abfd, int tag, unsigned int i)
+{
+ aeabi_attribute *attr;
+
+ attr = elf32_arm_new_eabi_attr (abfd, tag);
+ attr->type = 1;
+ attr->i = i;
+}
+
+static char *
+attr_strdup (bfd *abfd, const char * s)
+{
+ char * p;
+ int len;
+
+ len = strlen (s) + 1;
+ p = (char *)bfd_alloc(abfd, len);
+ return memcpy (p, s, len);
+}
+
+void
+elf32_arm_add_eabi_attr_string (bfd *abfd, int tag, const char *s)
+{
+ aeabi_attribute *attr;
+
+ attr = elf32_arm_new_eabi_attr (abfd, tag);
+ attr->type = 2;
+ attr->s = attr_strdup (abfd, s);
+}
+
+void
+elf32_arm_add_eabi_attr_compat (bfd *abfd, unsigned int i, const char *s)
+{
+ aeabi_attribute_list *list;
+ aeabi_attribute_list *p;
+ aeabi_attribute_list **lastp;
+
+ list = (aeabi_attribute_list *)
+ bfd_alloc (abfd, sizeof (aeabi_attribute_list));
+ memset (list, 0, sizeof (aeabi_attribute_list));
+ list->tag = Tag_compatibility;
+ list->attr.type = 3;
+ list->attr.i = i;
+ list->attr.s = attr_strdup (abfd, s);
+
+ lastp = &elf32_arm_tdata (abfd)->other_eabi_attributes;
+ for (p = *lastp; p; p = p->next)
+ {
+ int cmp;
+ if (p->tag != Tag_compatibility)
+ break;
+ cmp = strcmp(s, p->attr.s);
+ if (cmp < 0 || (cmp == 0 && i < p->attr.i))
+ break;
+ lastp = &p->next;
+ }
+ list->next = *lastp;
+ *lastp = list;
+}
+
/* Set the right machine number. */
static bfd_boolean
return TRUE;
}
+/* Copy the eabi object attribute from IBFD to OBFD. */
+static void
+copy_eabi_attributes (bfd *ibfd, bfd *obfd)
+{
+ aeabi_attribute *in_attr;
+ aeabi_attribute *out_attr;
+ aeabi_attribute_list *list;
+ int i;
+
+ in_attr = elf32_arm_tdata (ibfd)->known_eabi_attributes;
+ out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+ for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+ {
+ out_attr->i = in_attr->i;
+ if (in_attr->s && *in_attr->s)
+ out_attr->s = attr_strdup (obfd, in_attr->s);
+ in_attr++;
+ out_attr++;
+ }
+
+ for (list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
+ list;
+ list = list->next)
+ {
+ in_attr = &list->attr;
+ switch (in_attr->type)
+ {
+ case 1:
+ elf32_arm_add_eabi_attr_int (obfd, list->tag, in_attr->i);
+ break;
+ case 2:
+ elf32_arm_add_eabi_attr_string (obfd, list->tag, in_attr->s);
+ break;
+ case 3:
+ elf32_arm_add_eabi_attr_compat (obfd, in_attr->i, in_attr->s);
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
+
/* Copy backend specific data from one object module to another. */
static bfd_boolean
elf_elfheader (obfd)->e_ident[EI_OSABI] =
elf_elfheader (ibfd)->e_ident[EI_OSABI];
+ /* Copy EABI object attributes. */
+ copy_eabi_attributes (ibfd, obfd);
+
+ return TRUE;
+}
+
+/* Values for Tag_ABI_PCS_R9_use. */
+enum
+{
+ AEABI_R9_V6,
+ AEABI_R9_SB,
+ AEABI_R9_TLS,
+ AEABI_R9_unused
+};
+
+/* Values for Tag_ABI_PCS_RW_data. */
+enum
+{
+ AEABI_PCS_RW_data_absolute,
+ AEABI_PCS_RW_data_PCrel,
+ AEABI_PCS_RW_data_SBrel,
+ AEABI_PCS_RW_data_unused
+};
+
+/* Values for Tag_ABI_enum_size. */
+enum
+{
+ AEABI_enum_unused,
+ AEABI_enum_short,
+ AEABI_enum_wide,
+ AEABI_enum_forced_wide
+};
+
+/* Merge EABI object attributes from IBFD into OBFD. Raise an error if there
+ are conflicting attributes. */
+static bfd_boolean
+elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
+{
+ aeabi_attribute *in_attr;
+ aeabi_attribute *out_attr;
+ aeabi_attribute_list *in_list;
+ aeabi_attribute_list *out_list;
+ /* Some tags have 0 = don't care, 1 = strong requirement,
+ 2 = weak requirement. */
+ static const int order_312[3] = {3, 1, 2};
+ int i;
+
+ if (!elf32_arm_tdata (ibfd)->known_eabi_attributes[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ copy_eabi_attributes (ibfd, obfd);
+ return TRUE;
+ }
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ elf32_arm_tdata (ibfd)->known_eabi_attributes[0].i = 1;
+
+ in_attr = elf32_arm_tdata (ibfd)->known_eabi_attributes;
+ out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+ /* This needs to happen before Tag_ABI_FP_number_model is merged. */
+ if (in_attr[Tag_ABI_VFP_args].i != out_attr[Tag_ABI_VFP_args].i)
+ {
+ /* Ignore mismatches if teh object doesn't use floating point. */
+ if (out_attr[Tag_ABI_FP_number_model].i == 0)
+ out_attr[Tag_ABI_VFP_args].i = in_attr[Tag_ABI_VFP_args].i;
+ else if (in_attr[Tag_ABI_FP_number_model].i != 0)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B uses VFP register arguments, %B does not"),
+ ibfd, obfd);
+ return FALSE;
+ }
+ }
+
+ for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+ {
+ /* Merge this attribute with existing attributes. */
+ switch (i)
+ {
+ case Tag_CPU_raw_name:
+ case Tag_CPU_name:
+ /* Use whichever has the greatest architecture requirements. */
+ if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i)
+ out_attr[i].s = attr_strdup(obfd, in_attr[i].s);
+ break;
+
+ case Tag_ABI_optimization_goals:
+ case Tag_ABI_FP_optimization_goals:
+ /* Use the first value seen. */
+ break;
+
+ case Tag_CPU_arch:
+ case Tag_ARM_ISA_use:
+ case Tag_THUMB_ISA_use:
+ case Tag_VFP_arch:
+ case Tag_WMMX_arch:
+ case Tag_NEON_arch:
+ /* ??? Do NEON and WMMX conflict? */
+ case Tag_ABI_FP_rounding:
+ case Tag_ABI_FP_denormal:
+ case Tag_ABI_FP_exceptions:
+ case Tag_ABI_FP_user_exceptions:
+ case Tag_ABI_FP_number_model:
+ case Tag_ABI_align8_preserved:
+ case Tag_ABI_HardFP_use:
+ /* Use the largest value specified. */
+ if (in_attr[i].i > out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+
+ case Tag_CPU_arch_profile:
+ /* Warn if conflicting architecture profiles used. */
+ if (out_attr[i].i && in_attr[i].i && in_attr[i].i != out_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting architecture profiles %c/%c"),
+ ibfd, in_attr[i].i, out_attr[i].i);
+ return FALSE;
+ }
+ if (in_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_PCS_config:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (in_attr[i].i != 0 && out_attr[i].i != 0)
+ {
+ /* It's sometimes ok to mix different configs, so this is only
+ a warning. */
+ _bfd_error_handler
+ (_("Warning: %B: Conflicting platform configuration"), ibfd);
+ }
+ break;
+ case Tag_ABI_PCS_R9_use:
+ if (out_attr[i].i != AEABI_R9_unused
+ && in_attr[i].i != AEABI_R9_unused)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting use of R9"), ibfd);
+ return FALSE;
+ }
+ if (out_attr[i].i == AEABI_R9_unused)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_RW_data:
+ if (in_attr[i].i == AEABI_PCS_RW_data_SBrel
+ && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_SB
+ && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_unused)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: SB relative addressing conflicts with use of R9"),
+ ibfd);
+ return FALSE;
+ }
+ /* Use the smallest value specified. */
+ if (in_attr[i].i < out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_RO_data:
+ /* Use the smallest value specified. */
+ if (in_attr[i].i < out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_GOT_use:
+ if (in_attr[i].i > 2 || out_attr[i].i > 2
+ || order_312[in_attr[i].i] < order_312[out_attr[i].i])
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_wchar_t:
+ if (out_attr[i].i && in_attr[i].i && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting definitions of wchar_t"), ibfd);
+ return FALSE;
+ }
+ if (in_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_align8_needed:
+ /* ??? Check against Tag_ABI_align8_preserved. */
+ if (in_attr[i].i > 2 || out_attr[i].i > 2
+ || order_312[in_attr[i].i] < order_312[out_attr[i].i])
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_enum_size:
+ if (in_attr[i].i != AEABI_enum_unused)
+ {
+ if (out_attr[i].i == AEABI_enum_unused
+ || out_attr[i].i == AEABI_enum_forced_wide)
+ {
+ /* The existing object is compatible with anything.
+ Use whatever requirements the new object has. */
+ out_attr[i].i = in_attr[i].i;
+ }
+ else if (in_attr[i].i != AEABI_enum_forced_wide
+ && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting enum sizes"), ibfd);
+ }
+ }
+ break;
+ case Tag_ABI_VFP_args:
+ /* Aready done. */
+ break;
+ case Tag_ABI_WMMX_args:
+ if (in_attr[i].i != out_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B uses iWMMXt register arguments, %B does not"),
+ ibfd, obfd);
+ return FALSE;
+ }
+ break;
+ default: /* All known attributes should be explicitly covered. */
+ abort ();
+ }
+ }
+
+ in_list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
+ out_list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
+ while (in_list && in_list->tag == Tag_compatibility)
+ {
+ in_attr = &in_list->attr;
+ if (in_attr->i == 0)
+ continue;
+ if (in_attr->i == 1)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Must be processed by '%s' toolchain"),
+ ibfd, in_attr->s);
+ return FALSE;
+ }
+ if (!out_list || out_list->tag != Tag_compatibility
+ || strcmp (in_attr->s, out_list->attr.s) != 0)
+ {
+ /* Add this compatibility tag to the output. */
+ elf32_arm_add_eabi_attr_compat (obfd, in_attr->i, in_attr->s);
+ continue;
+ }
+ out_attr = &out_list->attr;
+ /* Check all the input tags with the same identifier. */
+ for (;;)
+ {
+ if (out_list->tag != Tag_compatibility
+ || in_attr->i != out_attr->i
+ || strcmp (in_attr->s, out_attr->s) != 0)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Incompatible object tag '%s':%d"),
+ ibfd, in_attr->s, in_attr->i);
+ return FALSE;
+ }
+ in_list = in_list->next;
+ if (in_list->tag != Tag_compatibility
+ || strcmp (in_attr->s, in_list->attr.s) != 0)
+ break;
+ in_attr = &in_list->attr;
+ out_list = out_list->next;
+ if (out_list)
+ out_attr = &out_list->attr;
+ }
+
+ /* Check the output doesn't have extra tags with this identifier. */
+ if (out_list && out_list->tag == Tag_compatibility
+ && strcmp (in_attr->s, out_list->attr.s) == 0)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Incompatible object tag '%s':%d"),
+ ibfd, in_attr->s, out_list->attr.i);
+ return FALSE;
+ }
+ }
+
+ for (; in_list; in_list = in_list->next)
+ {
+ if ((in_list->tag & 128) < 64)
+ _bfd_error_handler
+ (_("Warning: %B: Unknown EABI object attribute %d"),
+ ibfd, in_list->tag);
+ break;
+ }
return TRUE;
}
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return TRUE;
+ if (!elf32_arm_merge_eabi_attributes (ibfd, obfd))
+ return FALSE;
+
/* The input BFD must have had its flags initialised. */
/* The following seems bogus to me -- The flags are initialized in
the assembler but I don't think an elf_flags_init field is
hdr->sh_type = SHT_ARM_EXIDX;
hdr->sh_flags |= SHF_LINK_ORDER;
}
+ else if (strcmp(name, ".ARM.attributes") == 0)
+ {
+ hdr->sh_type = SHT_ARM_ATTRIBUTES;
+ }
return TRUE;
}
+/* Parse an Arm EABI attributes section. */
+static void
+elf32_arm_parse_attributes (bfd *abfd, Elf_Internal_Shdr * hdr)
+{
+ bfd_byte *contents;
+ bfd_byte *p;
+ bfd_vma len;
+
+ contents = bfd_malloc (hdr->sh_size);
+ if (!contents)
+ return;
+ if (!bfd_get_section_contents (abfd, hdr->bfd_section, contents, 0,
+ hdr->sh_size))
+ {
+ free (contents);
+ return;
+ }
+ p = contents;
+ if (*(p++) == 'A')
+ {
+ len = hdr->sh_size - 1;
+ while (len > 0)
+ {
+ int namelen;
+ bfd_vma section_len;
+
+ section_len = bfd_get_32 (abfd, p);
+ p += 4;
+ if (section_len > len)
+ section_len = len;
+ len -= section_len;
+ namelen = strlen ((char *)p) + 1;
+ section_len -= namelen + 4;
+ if (strcmp((char *)p, "aeabi") != 0)
+ {
+ /* Vendor section. Ignore it. */
+ p += namelen + section_len;
+ }
+ else
+ {
+ p += namelen;
+ while (section_len > 0)
+ {
+ int tag;
+ unsigned int n;
+ unsigned int val;
+ bfd_vma subsection_len;
+ bfd_byte *end;
+
+ tag = read_unsigned_leb128 (abfd, p, &n);
+ p += n;
+ subsection_len = bfd_get_32 (abfd, p);
+ p += 4;
+ if (subsection_len > section_len)
+ subsection_len = section_len;
+ section_len -= subsection_len;
+ subsection_len -= n + 4;
+ end = p + subsection_len;
+ switch (tag)
+ {
+ case Tag_File:
+ while (p < end)
+ {
+ bfd_boolean is_string;
+
+ tag = read_unsigned_leb128 (abfd, p, &n);
+ p += n;
+ if (tag == 4 || tag == 5)
+ is_string = 1;
+ else if (tag < 32)
+ is_string = 0;
+ else
+ is_string = (tag & 1) != 0;
+ if (tag == Tag_compatibility)
+ {
+ val = read_unsigned_leb128 (abfd, p, &n);
+ p += n;
+ elf32_arm_add_eabi_attr_compat (abfd, val,
+ (char *)p);
+ p += strlen ((char *)p) + 1;
+ }
+ else if (is_string)
+ {
+ elf32_arm_add_eabi_attr_string (abfd, tag,
+ (char *)p);
+ p += strlen ((char *)p) + 1;
+ }
+ else
+ {
+ val = read_unsigned_leb128 (abfd, p, &n);
+ p += n;
+ elf32_arm_add_eabi_attr_int (abfd, tag, val);
+ }
+ }
+ break;
+ case Tag_Section:
+ case Tag_Symbol:
+ /* Don't have anywhere convenient to attach these.
+ Fall through for now. */
+ default:
+ /* Ignore things we don't kow about. */
+ p += subsection_len;
+ subsection_len = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+ free (contents);
+}
+
/* Handle an ARM specific section when reading an object file. This is
called when bfd_section_from_shdr finds a section with an unknown
type. */
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
return FALSE;
+ if (hdr->sh_type == SHT_ARM_ATTRIBUTES)
+ elf32_arm_parse_attributes(abfd, hdr);
return TRUE;
}
#define bfd_elf32_new_section_hook elf32_arm_new_section_hook
#define bfd_elf32_bfd_is_target_special_symbol elf32_arm_is_target_special_symbol
#define bfd_elf32_close_and_cleanup elf32_arm_close_and_cleanup
+#define bfd_elf32_bfd_final_link elf32_arm_bfd_final_link
#define elf_backend_get_symbol_type elf32_arm_get_symbol_type
#define elf_backend_gc_mark_hook elf32_arm_gc_mark_hook
#else
#if defined __thumb__
#define CPU_DEFAULT (ARM_ARCH_V5T)
-#else
-#define CPU_DEFAULT ARM_ANY
#endif
#endif
#endif
#define streq(a, b) (strcmp (a, b) == 0)
static unsigned long cpu_variant;
+static unsigned long arm_arch_used;
+static unsigned long thumb_arch_used;
/* Flags stored in private area of BFD structure. */
static int uses_apcs_26 = FALSE;
static int march_fpu_opt = -1;
static int mfpu_opt = -1;
static int mfloat_abi_opt = -1;
+/* Record user cpu selection for object attributes.
+ Zero if no default or user specified CPU. */
+static int selected_cpu = -1;
+/* Must be long enough to hold any of the names in arm_cpus. */
+static char selected_cpu_name[16];
#ifdef OBJ_ELF
# ifdef EABI_DEFAULT
static int meabi_flags = EABI_DEFAULT;
demand_empty_rest_of_line ();
}
+
+
+/* Parse a .eabi_attribute directive. */
+
+static void
+s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ bfd_boolean is_string;
+ int tag;
+ unsigned int i = 0;
+ char *s = NULL;
+ char saved_char;
+
+ expression (& exp);
+ if (exp.X_op != O_constant)
+ goto bad;
+
+ tag = exp.X_add_number;
+ if (tag == 4 || tag == 5 || tag == 32 || (tag > 32 && (tag & 1) != 0))
+ is_string = 1;
+ else
+ is_string = 0;
+
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ goto bad;
+ if (tag == 32 || !is_string)
+ {
+ expression (& exp);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected numeric constant"));
+ ignore_rest_of_line ();
+ return;
+ }
+ i = exp.X_add_number;
+ }
+ if (tag == Tag_compatibility
+ && skip_past_comma (&input_line_pointer) == FAIL)
+ {
+ as_bad (_("expected comma"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (is_string)
+ {
+ skip_whitespace(input_line_pointer);
+ if (*input_line_pointer != '"')
+ goto bad_string;
+ input_line_pointer++;
+ s = input_line_pointer;
+ while (*input_line_pointer && *input_line_pointer != '"')
+ input_line_pointer++;
+ if (*input_line_pointer != '"')
+ goto bad_string;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+ }
+ else
+ {
+ s = NULL;
+ saved_char = 0;
+ }
+
+ if (tag == Tag_compatibility)
+ elf32_arm_add_eabi_attr_compat (stdoutput, i, s);
+ else if (is_string)
+ elf32_arm_add_eabi_attr_string (stdoutput, tag, s);
+ else
+ elf32_arm_add_eabi_attr_int (stdoutput, tag, i);
+
+ if (s)
+ {
+ *input_line_pointer = saved_char;
+ input_line_pointer++;
+ }
+ demand_empty_rest_of_line ();
+ return;
+bad_string:
+ as_bad (_("bad string constant"));
+ ignore_rest_of_line ();
+ return;
+bad:
+ as_bad (_("expected <tag> , <value>"));
+ ignore_rest_of_line ();
+}
+
+static void s_arm_arch (int);
+static void s_arm_cpu (int);
+static void s_arm_fpu (int);
#endif /* OBJ_ELF */
/* This table describes all the machine specific pseudo-ops the assembler
{ "pad", s_arm_unwind_pad, 0 },
{ "setfp", s_arm_unwind_setfp, 0 },
{ "unwind_raw", s_arm_unwind_raw, 0 },
+ { "cpu", s_arm_cpu, 0 },
+ { "arch", s_arm_arch, 0 },
+ { "fpu", s_arm_fpu, 0 },
+ { "eabi_attribute", s_arm_eabi_attribute, 0 },
#else
{ "word", cons, 4},
#endif
return;
}
}
+ thumb_arch_used |= opcode->tvariant;
+ /* Many Thumb-2 instructions also have Thumb-1 variants, so explicitly
+ set those bits when Thumb-2 32-bit instuctions are seen. ie.
+ anything other than bl/blx.
+ This is overly pessimistic for relaxable instructions. */
+ if ((inst.size == 4 && (inst.instruction & 0xf800e800) != 0xf000e800)
+ || inst.relax)
+ thumb_arch_used |= ARM_EXT_V6T2;
}
else
{
inst.size = INSN_SIZE;
if (!parse_operands (p, opcode->operands))
opcode->aencode ();
+ /* Arm mode bx is marked as both v4T and v5 because it's still required
+ on a hypothetical non-thumb v5 core. */
+ if (opcode->avariant == (ARM_EXT_V4T | ARM_EXT_V5))
+ arm_arch_used |= ARM_EXT_V4T;
+ else
+ arm_arch_used |= opcode->avariant;
}
output_inst (str);
}
mfpu_opt = FPU_ARCH_FPA;
}
+#ifdef CPU_DEFAULT
+ if (mcpu_cpu_opt == -1)
+ selected_cpu = mcpu_cpu_opt = CPU_DEFAULT;
+#else
if (mcpu_cpu_opt == -1)
- mcpu_cpu_opt = CPU_DEFAULT;
+ {
+ mcpu_cpu_opt = ARM_ANY;
+ selected_cpu = 0;
+ }
+ else
+ selected_cpu = mcpu_cpu_opt;
+#endif
cpu_variant = mcpu_cpu_opt | mfpu_opt;
+ arm_arch_used = thumb_arch_used = 0;
+
#if defined OBJ_COFF || defined OBJ_ELF
{
unsigned int flags = 0;
/* For some CPUs we assume an FPU unless the user explicitly sets
-mfpu=... */
int default_fpu;
+ /* The canonical name of the CPU, or NULL to use NAME converted to upper
+ case. */
+ const char *canonical_name;
};
/* This list should, at a minimum, contain all the cpu names
recognized by GCC. */
static struct arm_cpu_option_table arm_cpus[] =
{
- {"all", ARM_ANY, FPU_ARCH_FPA},
- {"arm1", ARM_ARCH_V1, FPU_ARCH_FPA},
- {"arm2", ARM_ARCH_V2, FPU_ARCH_FPA},
- {"arm250", ARM_ARCH_V2S, FPU_ARCH_FPA},
- {"arm3", ARM_ARCH_V2S, FPU_ARCH_FPA},
- {"arm6", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm60", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm600", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm610", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm620", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA},
- {"arm7d", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA},
- {"arm7di", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA},
- {"arm70", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm700", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm700i", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm710", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm720", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm710c", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7100", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7500", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm8", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"arm810", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"strongarm", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"arm9", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm920", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA},
+ {"all", ARM_ANY, FPU_ARCH_FPA, NULL},
+ {"arm1", ARM_ARCH_V1, FPU_ARCH_FPA, NULL},
+ {"arm2", ARM_ARCH_V2, FPU_ARCH_FPA, NULL},
+ {"arm250", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL},
+ {"arm3", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL},
+ {"arm6", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm60", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm600", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm610", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm620", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL},
+ {"arm7d", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL},
+ {"arm7di", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL},
+ {"arm70", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm700", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm700i", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm710", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm720", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm710c", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7100", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7500", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
+ {"arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm8", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"arm810", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"strongarm", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ {"arm9", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm920", ARM_ARCH_V4T, FPU_ARCH_FPA, "ARM920T"},
+ {"arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
+ {"arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
/* For V5 or later processors we default to using VFP; but the user
should really set the FPU type explicitly. */
- {"arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
- {"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
- {"arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
- {"arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
- {"arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
- {"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
- {"arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1},
- {"arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1},
- {"arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1},
- {"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
- {"arm1022e", 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},
- {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
- {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2},
- {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE},
- {"arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE},
- {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2},
- {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE},
- {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2},
+ {"arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL},
+ {"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"},
+ {"arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"},
+ {"arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL},
+ {"arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL},
+ {"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM946E-S"},
+ {"arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL},
+ {"arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM966E-S"},
+ {"arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL},
+ {"arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL},
+ {"arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM1020E"},
+ {"arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL},
+ {"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
+ {"arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM1026EJ-S"},
+ {"arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL},
+ {"arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"},
+ {"arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL},
+ {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2, "ARM1136JF-S"},
+ {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL},
+ {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, NULL},
+ {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, NULL},
+ {"arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL},
+ {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL},
+ {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL},
+ {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL},
/* ??? XSCALE is really an architecture. */
- {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
+ {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL},
/* ??? iwmmxt is not a processor. */
- {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2},
- {"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
+ {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL},
+ {"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL},
/* Maverick */
- {"ep9312", ARM_ARCH_V4T | ARM_CEXT_MAVERICK, FPU_ARCH_MAVERICK},
- {NULL, 0, 0}
+ {"ep9312", ARM_ARCH_V4T | ARM_CEXT_MAVERICK, FPU_ARCH_MAVERICK, "ARM920T"},
+ {NULL, 0, 0, NULL}
};
struct arm_arch_option_table
{
mcpu_cpu_opt = opt->value;
mcpu_fpu_opt = opt->default_fpu;
+ if (opt->canonical_name)
+ strcpy(selected_cpu_name, opt->canonical_name);
+ else
+ {
+ int i;
+ for (i = 0; i < optlen; i++)
+ selected_cpu_name[i] = TOUPPER (opt->name[i]);
+ selected_cpu_name[i] = 0;
+ }
if (ext != NULL)
return arm_parse_extension (ext, &mcpu_cpu_opt);
return 0;
}
-
for (opt = arm_archs; opt->name != NULL; opt++)
if (streq (opt->name, str))
{
march_cpu_opt = opt->value;
march_fpu_opt = opt->default_fpu;
+ strcpy(selected_cpu_name, opt->name);
if (ext != NULL)
return arm_parse_extension (ext, &march_cpu_opt);
-EL assemble code for a little-endian cpu\n"));
#endif
}
+
+
+#ifdef OBJ_ELF
+/* Set the public EABI object attributes. */
+static void
+aeabi_set_public_attributes (void)
+{
+ int arch;
+ int flags;
+
+ /* Choose the architecture based on the capabilities of the requested cpu
+ (if any) and/or the instructions actually used. */
+ flags = mcpu_cpu_opt | arm_arch_used | thumb_arch_used;
+ if (flags & ARM_EXT_V6T2)
+ arch = 8;
+ else if (flags & ARM_EXT_V6Z)
+ arch = 7;
+ else if (flags & ARM_EXT_V6K)
+ arch = 9;
+ else if (flags & ARM_EXT_V6)
+ arch = 6;
+ else if (flags & ARM_EXT_V5E)
+ arch = 4;
+ else if (flags & (ARM_EXT_V5 | ARM_EXT_V5T))
+ arch = 3;
+ else if (flags & ARM_EXT_V4T)
+ arch = 2;
+ else if (flags & ARM_EXT_V4)
+ arch = 1;
+ else
+ arch = 0;
+
+ /* Tag_CPU_name. */
+ if (selected_cpu_name[0])
+ {
+ char *p;
+
+ p = selected_cpu_name;
+ if (strncmp(p, "armv", 4) == 0)
+ {
+ int i;
+
+ p += 4;
+ for (i = 0; p[i]; i++)
+ p[i] = TOUPPER (p[i]);
+ }
+ elf32_arm_add_eabi_attr_string (stdoutput, 5, p);
+ }
+ /* Tag_CPU_arch. */
+ elf32_arm_add_eabi_attr_int (stdoutput, 6, arch);
+ /* Tag_ARM_ISA_use. */
+ if (arm_arch_used)
+ elf32_arm_add_eabi_attr_int (stdoutput, 8, 1);
+ /* Tag_THUMB_ISA_use. */
+ if (thumb_arch_used)
+ elf32_arm_add_eabi_attr_int (stdoutput, 9,
+ (thumb_arch_used & ARM_EXT_V6T2) ? 2 : 1);
+ /* Tag_VFP_arch. */
+ if ((arm_arch_used | thumb_arch_used) & FPU_ARCH_VFP_V2)
+ elf32_arm_add_eabi_attr_int (stdoutput, 10, 2);
+ else if ((arm_arch_used | thumb_arch_used) & FPU_ARCH_VFP_V1)
+ elf32_arm_add_eabi_attr_int (stdoutput, 10, 1);
+ /* Tag_WMMX_arch. */
+ if ((arm_arch_used | thumb_arch_used) & ARM_CEXT_IWMMXT)
+ elf32_arm_add_eabi_attr_int (stdoutput, 11, 1);
+}
+
+/* Add the .ARM.attributes section. */
+void
+arm_md_end (void)
+{
+ segT s;
+ char *p;
+ addressT addr;
+ offsetT size;
+
+ if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
+ return;
+
+ aeabi_set_public_attributes ();
+ size = elf32_arm_eabi_attr_size (stdoutput);
+ s = subseg_new (".ARM.attributes", 0);
+ bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA);
+ addr = frag_now_fix ();
+ p = frag_more (size);
+ elf32_arm_set_eabi_attr_contents (stdoutput, (bfd_byte *)p, size);
+}
+
+
+/* Parse a .cpu directive. */
+
+static void
+s_arm_cpu (int ignored ATTRIBUTE_UNUSED)
+{
+ struct arm_cpu_option_table *opt;
+ char *name;
+ char saved_char;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE(*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ /* Skip the first "all" entry. */
+ for (opt = arm_cpus + 1; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ mcpu_cpu_opt = opt->value;
+ selected_cpu = mcpu_cpu_opt;
+ if (opt->canonical_name)
+ strcpy(selected_cpu_name, opt->canonical_name);
+ else
+ {
+ int i;
+ for (i = 0; opt->name[i]; i++)
+ selected_cpu_name[i] = TOUPPER (opt->name[i]);
+ selected_cpu_name[i] = 0;
+ }
+ cpu_variant = mcpu_cpu_opt | mfpu_opt;
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ as_bad (_("unknown cpu `%s'"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+
+/* Parse a .arch directive. */
+
+static void
+s_arm_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ struct arm_arch_option_table *opt;
+ char saved_char;
+ char *name;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE(*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ /* Skip the first "all" entry. */
+ for (opt = arm_archs + 1; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ mcpu_cpu_opt = opt->value;
+ selected_cpu = mcpu_cpu_opt;
+ strcpy(selected_cpu_name, opt->name);
+ cpu_variant = mcpu_cpu_opt | mfpu_opt;
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+
+/* Parse a .fpu directive. */
+
+static void
+s_arm_fpu (int ignored ATTRIBUTE_UNUSED)
+{
+ struct arm_option_value_table *opt;
+ char saved_char;
+ char *name;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE(*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ for (opt = arm_fpus; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ mfpu_opt = opt->value;
+ cpu_variant = mcpu_cpu_opt | mfpu_opt;
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown floating point format `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+#endif /* OBJ_ELF */
+