]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gas: implement parsing of object attributes v2
authorRichard Ball <richard.ball@arm.com>
Tue, 28 Jan 2025 17:06:58 +0000 (17:06 +0000)
committerMatthieu Longo <matthieu.longo@arm.com>
Thu, 22 Jan 2026 10:11:16 +0000 (10:11 +0000)
This patch adds the parsing logic for Object Attributes v2 (OAv2), enabling
Gas to interpret and process these attributes correctly. It also updates the
AArch64 backend to utilize the new parsing capabilities, and handle the new
AArch64-specific directives.

This patch relies on the abstractions introduced in the previous patch to
store the data. Its scope is limited to parsing the new assembly directives,
checking the inputs, and storing the data into the relevant OAv2 abstractions.
Note that, for now, the new parsing capabilities are only available for AArch64.
Even if the implementation was splitted into a generic part available in
gas/config/obj-elf.c, and an AArch64-specific one in gas/config/tc-aarch64.c,
the lack of GNU generic directives to handle OAv2 prevented the capability
from being exposed to others backends.

** GNU assembler interface for aeabi subsections

OAv2 introduced two new directives for AArch64:
- .aeabi_subsection name, comprehension, encoding
  Create or switch the current subsection to 'name'.
  Comprehension values can be 'required' or 'optional'.
  Encoding values are limited for now to 'ULEB128', and 'NTBS'
  The comprehension and encoding are mandatory arguments on the first
  declaration of the subsection, but become optional for subsequent
  ones.
- .aeabi_attribute tag, value
  Set 'tag' to 'value' in the current subsection.
  Tag can either be an integer, or one of the defined symbols in the backend.

The usage of those directives will error if the following requirements
are breached:
- If the subsection X has been previously declared, the comprehension and
  encoding parameters of the current .aeabi_subsection that redeclares X
  have to match with the previous declaration. If those parameters are
  omited, no check is performed.
- The type of the value set via .aeabi_attribute has to align with
  the current subsection.
- If the tag N has already been declared for the current subsection, a
  later assignment to tag N is tolerated only if the newly set value is
  equal to the former one. This check is stricter than needed. Ideally,
  the tag N's values should be merged together, and an error should be
  raised only if an incompatibility is detected. Because the attributes
  are set in one chunk by GCC, there is no real use case for such a merge.

The new parsing code is enabled/disabled via the TC_OBJ_ATTR_v1 and
TC_OBJ_ATTR_v2 defines, and supports the following configurations:
- enable both OAv1 and OAv2 parsing. This is currently used by no
  target, but is useful for migration from OAv1 to OAv2.
- enable OAv1 parsing only. This is used by all targets supporting OAs
  except for AArch64.
- enable OAv2 parsing only. This is only used by AArch64.

** Regarding the implementation

The logic of OAv1 does not always keep separated the different data processing
steps: parsing, convertion to internal abstractions, error checking and further
processing (if any) on those abstractions, and their serialization into the
object files.
This patch takes into account the specifities of syntax for OAv1 and OAv2, but
mutualize as much as possible the common behavior so that the same methods can
be used for parsing the OAv1 and OAv2 directives.
However, the mutualization of the code is limited by a different internal model
for OAv1 and OAv2. Even if it is technically feaseable to have only one middle
-end for OAv1, OAv2 and even GNU properties, sharing the same data model to
perform the merge logic with the same code, it is a significant amount of work.
This extra work was not considered as a part of this new feature, so this patch
series will stick with the minimum of mutualization as possible.

Co-Authored-By: Matthieu Longo <matthieu.longo@arm.com>
28 files changed:
bfd/elf-attrs.c
bfd/elf-attrs.h
bfd/elf-bfd.h
bfd/elfnn-aarch64.c
bfd/elfxx-aarch64.c
bfd/elfxx-aarch64.h
bfd/elfxx-target.h
gas/config/obj-elf-attr.c
gas/config/obj-elf-attr.h
gas/config/obj-elf.c
gas/config/tc-aarch64.c
gas/config/tc-aarch64.h
gas/config/tc-arc.h
gas/config/tc-arm.h
gas/config/tc-csky.h
gas/config/tc-loongarch.h
gas/config/tc-m68k.h
gas/config/tc-mips.h
gas/config/tc-msp430.h
gas/config/tc-ppc.h
gas/config/tc-riscv.h
gas/config/tc-s390.h
gas/config/tc-sparc.h
gas/config/tc-tic6x.h
gas/configure
gas/configure.ac
gas/doc/c-aarch64.texi
include/elf/aarch64.h

index 2b6ef6705b1c1f128ac9196bcaa436984cb61ef7..28046ec2e2e0aa8386ec937b2f0f1a7b13ff045f 100644 (file)
@@ -258,6 +258,154 @@ bfd_elf_set_obj_attr_contents (bfd *abfd, bfd_byte *buffer, bfd_vma size)
   write_obj_attr_section_v1 (abfd, buffer, size);
 }
 
+/* The first two tags in gnu-testing namespace are known (from the perspective
+   of GNU ld), and so have a name and can be initialized to the default value
+   ('0' or NULL) depending on the encoding specified on the subsection.  Any
+   tags above 1 will be considered unknown, so will be default initialized in
+   the same way but its status will be set to obj_attr_subsection_v2_unknown.
+   If the tag is unknown, ld can drop it if it is inside an optional subsection,
+   whereas ld will raise an error in a required subsection.
+   Note: the array below has to be sorted by the tag's integer value.  */
+static const obj_attr_info_t known_attrs_gnu_testing[] =
+{
+  { .tag = {"GNUTestTag_0", 0} },
+  { .tag = {"GNUTestTag_1", 1} },
+};
+
+/* List of known GNU subsections.
+   Note: this array has to be sorted using the same criteria as in
+   _bfd_elf_obj_attr_subsection_v2_cmp().  */
+static const known_subsection_v2_t obj_attr_v2_known_gnu_subsections[] =
+{
+  {
+    /* Note: the currently set values for the subsection name, its optionality,
+       and encoding are irrelevant for a testing subsection.  These values are
+       unused.  This entry is only a placeholder for list of known GNU testing
+       tags.  */
+    .subsec_name = NULL,
+    .known_attrs = known_attrs_gnu_testing,
+    .optional = true,
+    .encoding = OA_ENC_ULEB128,
+    .len = ARRAY_SIZE (known_attrs_gnu_testing),
+  },
+  /* Note for the future: GNU subsections can be added here below.  */
+};
+
+/* Return True if the given subsection name is part of the reserved testing
+   namespace, i.e. SUBSEC_NAME begins with "gnu-testing".  */
+static bool
+gnu_testing_namespace (const char *subsec_name)
+{
+  return strncmp ("gnu_testing_", subsec_name, 12) == 0;
+}
+
+/* Identify the scope of a subsection from its name.
+   Note: the code below needs to be kept in sync with the code of
+   elf_parse_attrs_subsection_v2() in binutils/readelf.c.  */
+obj_attr_subsection_scope_v2_t
+bfd_elf_obj_attr_subsection_v2_scope (const bfd *abfd, const char *subsec_name)
+{
+  const char *vendor_name = get_elf_backend_data (abfd)->obj_attrs_vendor;
+  obj_attr_subsection_scope_v2_t scope = OA_SUBSEC_PRIVATE;
+  size_t vendor_name_len = strlen (vendor_name);
+  if (strncmp (subsec_name, vendor_name, vendor_name_len) == 0
+      && subsec_name[vendor_name_len] == '_')
+    scope = OA_SUBSEC_PUBLIC;
+  return scope;
+}
+
+/* Search for a subsection matching NAME in the list of subsections known from
+   bfd (generic or backend-specific).  Return the subsection information if it
+   is found, or NULL otherwise.  */
+const known_subsection_v2_t *
+bfd_obj_attr_v2_identify_subsection (const struct elf_backend_data *bed,
+                                    const char *name)
+{
+  /* Check known backend subsections.  */
+  const known_subsection_v2_t *known_subsections
+    = bed->obj_attr_v2_known_subsections;
+  const size_t known_subsections_size = bed->obj_attr_v2_known_subsections_size;
+
+  for (unsigned i = 0; i < known_subsections_size; ++i)
+    {
+      int cmp = strcmp (known_subsections[i].subsec_name, name);
+      if (cmp == 0)
+       return &known_subsections[i];
+      else if (cmp > 0)
+       break;
+    }
+
+  /* Check known GNU subsections.  */
+  /* Note for the future: search known GNU subsections here.  Don't forget to
+     skip the first entry (placeholder for GNU testing subsection).  */
+
+  /* Check whether this subsection is a GNU testing subsection.  */
+  if (gnu_testing_namespace (name))
+    return &obj_attr_v2_known_gnu_subsections[0];
+
+  return NULL;
+}
+
+/* Search for the attribute information associated to TAG in the list of known
+   tags registered in the known subsection SUBSEC.  Return the tag information
+   if it is found, NULL otherwise.  */
+static const obj_attr_info_t *
+identify_tag (const known_subsection_v2_t *subsec, obj_attr_tag_t tag)
+{
+  for (unsigned i = 0; i < subsec->len; ++i)
+    {
+      const obj_attr_info_t *known_attr = &subsec->known_attrs[i];
+      if (known_attr->tag.value == tag)
+       return known_attr;
+      else if (known_attr->tag.value > tag)
+       break;
+    }
+  return NULL;
+}
+
+/* Return the attribute information associated to the pair SUBSEC, TAG if it
+   exists, NULL otherwise.  */
+const obj_attr_info_t *
+_bfd_obj_attr_v2_find_known_by_tag (const struct elf_backend_data *bed,
+                                   const char *subsec_name,
+                                   obj_attr_tag_t tag)
+{
+  const known_subsection_v2_t *subsec_info
+    = bfd_obj_attr_v2_identify_subsection (bed, subsec_name);
+  if (subsec_info != NULL)
+    return identify_tag (subsec_info, tag);
+  return NULL;
+}
+
+/* To-string function for the pair <SUBSEC, TAG>.
+   Returns the attribute information associated to TAG if it is found,
+   or "Tag_unknown_<N>" otherwise.  */
+const char *
+_bfd_obj_attr_v2_tag_to_string (const struct elf_backend_data *bed,
+                               const char *subsec_name,
+                               obj_attr_tag_t tag)
+{
+  const obj_attr_info_t *attr_info
+    = _bfd_obj_attr_v2_find_known_by_tag (bed, subsec_name, tag);
+  if (attr_info != NULL)
+    return xstrdup (attr_info->tag.name);
+  return xasprintf ("Tag_unknown_%lu", tag);
+}
+
+/* To-string function for the subsection parameter "comprehension".  */
+const char *
+bfd_oav2_comprehension_to_string (bool comprehension)
+{
+  return comprehension ? "optional" : "required";
+}
+
+/* To-string function for the subsection parameter "encoding".  */
+const char *
+bfd_oav2_encoding_to_string (obj_attr_encoding_v2_t encoding)
+{
+  return (encoding == OA_ENC_ULEB128) ? "ULEB128" : "NTBS";
+}
+
 /* Allocate/find an object attribute.  */
 obj_attribute *
 bfd_elf_new_obj_attr (bfd *abfd, obj_attr_vendor_t vendor, obj_attr_tag_t tag)
index 1129d9763a3494daea95793f764ebd0578c8b674..2f564f1ae53a5a404b296c15abca8290da252cdb 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <stdint.h>
+#include "hidden.h"
 
 typedef enum obj_attr_version {
   OBJ_ATTR_VERSION_NONE = 0,
@@ -47,6 +48,9 @@ typedef enum obj_attr_encoding_v2
 #define obj_attr_encoding_v2_to_u8(value) \
   ((uint8_t) ((value) - 1))
 
+extern const char *
+bfd_oav2_encoding_to_string (obj_attr_encoding_v2_t);
+
 typedef union obj_attr_value_v2 {
   uint32_t uint;
 
@@ -108,6 +112,9 @@ typedef struct obj_attr_subsection_v2 {
   struct obj_attr_v2 *last;
 } obj_attr_subsection_v2_t;
 
+extern const char *
+bfd_oav2_comprehension_to_string (bool);
+
 typedef struct obj_attr_subsection_list
 {
   /* A pointer to the first node of the list.  */
@@ -119,3 +126,39 @@ typedef struct obj_attr_subsection_list
   /* The size of the list.  */
   unsigned int size;
 } obj_attr_subsection_list_t;
+
+typedef struct {
+  const char *const name;
+  obj_attr_tag_t value;
+} obj_attr_tag_info_t;
+
+/* Attribute information.  */
+typedef struct {
+  obj_attr_tag_info_t tag;
+  obj_attr_value_v2_t default_value;
+} obj_attr_info_t;
+
+typedef struct
+{
+  const char *const subsec_name;
+  const obj_attr_info_t *known_attrs;
+  const bool optional;
+  const obj_attr_encoding_v2_t encoding;
+  const size_t len;
+} known_subsection_v2_t;
+
+struct elf_backend_data;
+
+extern const known_subsection_v2_t *
+bfd_obj_attr_v2_identify_subsection (const struct elf_backend_data *,
+                                    const char *);
+
+extern const obj_attr_info_t *
+_bfd_obj_attr_v2_find_known_by_tag (const struct elf_backend_data *,
+                                   const char *,
+                                   obj_attr_tag_t) ATTRIBUTE_HIDDEN;
+
+extern const char *
+_bfd_obj_attr_v2_tag_to_string (const struct elf_backend_data *,
+                               const char *,
+                               obj_attr_tag_t) ATTRIBUTE_HIDDEN;
index c0df19ea739b937651305c53f8d1a8726520688e..e177984dd31308cef093c3a8e3b97b7936fe87cb 100644 (file)
@@ -1667,6 +1667,12 @@ struct elf_backend_data
   /* Encode the object attributes version into the output object.  */
   uint8_t (*obj_attrs_version_enc) (obj_attr_version_t);
 
+  /* The known subsections and attributes (v2 only).  */
+  const known_subsection_v2_t *obj_attr_v2_known_subsections;
+
+  /* The size of the array of known subsections.  */
+  const size_t obj_attr_v2_known_subsections_size;
+
   /* This function determines the order in which any attributes are
      written.  It must be defined for input in the range
      LEAST_KNOWN_OBJ_ATTRIBUTE..NUM_KNOWN_OBJ_ATTRIBUTES-1 (this range
@@ -3211,6 +3217,8 @@ extern int _bfd_elf_obj_attr_subsection_v2_cmp
   ATTRIBUTE_HIDDEN;
 extern obj_attr_subsection_v2_t *bfd_obj_attr_subsection_v2_find_by_name
   (obj_attr_subsection_v2_t *, const char *, bool);
+extern obj_attr_subsection_scope_v2_t bfd_elf_obj_attr_subsection_v2_scope
+  (const bfd *, const char *);
 LINKED_LIST_MUTATIVE_OPS_PROTOTYPE (obj_attr_subsection_list_t,
                                    obj_attr_subsection_v2_t,
                                    ATTRIBUTE_HIDDEN);
index 02fb7fa72ec784b57f49f7809870f145db38693b..75e16d0bc15d739b6edd40e9b6c2112de3ab3ad8 100644 (file)
@@ -10846,5 +10846,12 @@ static const struct elf_size_info elfNN_aarch64_size_info =
 #undef elf_backend_obj_attrs_version_enc
 #define elf_backend_obj_attrs_version_enc \
   _bfd_aarch64_obj_attrs_version_enc
+/* Object attributes v2 specific values.  */
+#undef elf_backend_obj_attr_v2_known_subsections
+#define elf_backend_obj_attr_v2_known_subsections \
+  aarch64_obj_attr_v2_known_subsections
+#undef elf_backend_obj_attr_v2_known_subsections_size
+#define elf_backend_obj_attr_v2_known_subsections_size \
+  ARRAY_SIZE(aarch64_obj_attr_v2_known_subsections)
 
 #include "elfNN-target.h"
index f7c27649950aeb9cc6f269a8ec8a0b92c67b9c1b..85d2e4228109918e45eb0bb09c42e012d5a8338f 100644 (file)
 #include "sysdep.h"
 #include "bfd.h"
 #include "elf-bfd.h"
+#include "elf/aarch64.h"
 #include "elfxx-aarch64.h"
 #include "libbfd.h"
+#include "libiberty.h"
 #include <stdarg.h>
 #include <string.h>
 
@@ -887,6 +889,53 @@ _bfd_aarch64_obj_attrs_version_enc (obj_attr_version_t version)
   abort ();
 }
 
+/* List of known attributes in the subsection "aeabi_feature_and_bits".
+   Note: the array below has to be sorted by the tag's integer value that can
+   be found in the document "Build Attributes for the Arm® 64-bit Architecture
+   (AArch64)".  */
+static const obj_attr_info_t known_attrs_aeabi_feature_and_bits[] =
+{
+  { .tag = {"Tag_Feature_BTI", Tag_Feature_BTI} },
+  { .tag = {"Tag_Feature_PAC", Tag_Feature_PAC} },
+  { .tag = {"Tag_Feature_GCS", Tag_Feature_GCS} },
+};
+
+/* List of known attributes in the subsection "aeabi_pauthabi".
+   Notes:
+   - "aeabi_pauthabi" is a required subsection to use PAuthABI (which is
+     today only supported by LLVM, unsupported by GCC 15 and lower. There is no
+     plan to add support for it in the future). A value of 0 for any the tags
+     below means that the user did not permit this entity to use the PAuthABI.
+   - the array below has to be sorted by the tag's integer value that can be
+     found in the document "Build Attributes for the Arm® 64-bit Architecture
+     (AArch64)".  */
+static const obj_attr_info_t known_attrs_aeabi_pauthabi[] =
+{
+  { .tag = {"Tag_PAuth_Platform", Tag_PAuth_Platform} },
+  { .tag = {"Tag_PAuth_Schema", Tag_PAuth_Schema} },
+};
+
+/* List of known subsections.
+   Note: this array is exported by the backend, and has to be sorted using
+   the same criteria as in _bfd_elf_obj_attr_subsection_v2_cmp().  */
+const known_subsection_v2_t aarch64_obj_attr_v2_known_subsections[2] =
+{
+  {
+    .subsec_name = "aeabi_feature_and_bits",
+    .known_attrs = known_attrs_aeabi_feature_and_bits,
+    .optional = true,
+    .encoding = OA_ENC_ULEB128,
+    .len = ARRAY_SIZE (known_attrs_aeabi_feature_and_bits),
+  },
+  {
+    .subsec_name = "aeabi_pauthabi",
+    .known_attrs = known_attrs_aeabi_pauthabi,
+    .optional = false,
+    .encoding = OA_ENC_ULEB128,
+    .len = ARRAY_SIZE (known_attrs_aeabi_pauthabi),
+  },
+};
+
 /* Find the first input bfd with GNU property and merge it with GPROP.  If no
    such input is found, add it to a new section at the last input.  Update
    GPROP accordingly.  */
index 1808972148ff3eac6e2f10912625103a787b161f..43d58d2a849ab952d07059b9ff06a5f476a83bcc 100644 (file)
@@ -242,6 +242,9 @@ _bfd_aarch64_obj_attrs_version_dec (uint8_t) ATTRIBUTE_HIDDEN;
 extern uint8_t
 _bfd_aarch64_obj_attrs_version_enc (obj_attr_version_t) ATTRIBUTE_HIDDEN;
 
+extern const known_subsection_v2_t aarch64_obj_attr_v2_known_subsections[2]
+  ATTRIBUTE_HIDDEN;
+
 extern bfd *
 _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *)
   ATTRIBUTE_HIDDEN;
index 239d7956d3038fa4a1343eebdff333beea7a7153..2fcf25a794c93f395959e03a617e885f883e353e 100644 (file)
 #ifndef elf_backend_obj_attrs_version_enc
 #define elf_backend_obj_attrs_version_enc      _bfd_obj_attrs_version_enc
 #endif
+#ifndef elf_backend_obj_attr_v2_known_subsections
+#define elf_backend_obj_attr_v2_known_subsections      NULL
+#endif
+#ifndef elf_backend_obj_attr_v2_known_subsections_size
+#define elf_backend_obj_attr_v2_known_subsections_size 0
+#endif
 #ifndef elf_backend_obj_attrs_order
 #define elf_backend_obj_attrs_order            NULL
 #endif
@@ -940,6 +946,8 @@ static const struct elf_backend_data elfNN_bed =
   elf_backend_default_obj_attr_version,
   elf_backend_obj_attrs_version_dec,
   elf_backend_obj_attrs_version_enc,
+  elf_backend_obj_attr_v2_known_subsections,
+  elf_backend_obj_attr_v2_known_subsections_size,
   elf_backend_obj_attrs_order,
   elf_backend_obj_attrs_handle_unknown,
   elf_backend_parse_gnu_properties,
index 005787d1bdb9c1da25dc2484acb1bde9af9abb41..505390002f0f7469fcf50c5c42fa7876ca43c0db 100644 (file)
 
 #ifdef TC_OBJ_ATTR
 
+#include "obstack.h"
 #include "safe-ctype.h"
 
+/* A variant type to store information about known OAv2 identifiers.  */
+typedef union {
+  uint8_t u8;
+  bool b;
+} oav2_identifier_variant_value_t;
+
+typedef enum {
+  OAv2_ASM_ID_VALUE_UNDEFINED = 0,
+  OAv2_ASM_ID_VALUE_U8,
+  OAv2_ASM_ID_VALUE_BOOL,
+} oav2_identifier_variant_type_info_t;
+
+typedef struct {
+  oav2_identifier_variant_value_t val;
+  oav2_identifier_variant_type_info_t vtype;
+} oav2_identifier_variant_t;
+
+typedef struct {
+  const char *const name;
+  const oav2_identifier_variant_t value;
+} oav2_identifier_t;
+
+typedef struct {
+  size_t len;
+  struct arg_variant_t *elts;
+} arg_variant_list_t;
+
+typedef union {
+  const char *string;
+  uint64_t u64;
+  int64_t i64;
+  arg_variant_list_t list;
+} arg_variant_value_t;
+
+typedef enum {
+  VALUE_UNDEFINED = 0,
+  VALUE_U64,
+  VALUE_I64,
+  VALUE_UNSIGNED_INTEGER = VALUE_U64,
+  VALUE_SIGNED_INTEGER = VALUE_I64,
+  VALUE_STRING,
+  VALUE_LIST,
+  VALUE_OPTIONAL_ABSENT,
+} arg_variant_type_info_t;
+
+/* A variant type to store the argument values of an assembly directive.  */
+typedef struct arg_variant_t {
+  arg_variant_value_t val;
+  arg_variant_type_info_t vtype;
+} arg_variant_t;
+
+typedef arg_variant_t arg_t;
+
 #define skip_whitespace(str)  do { if (is_whitespace (*(str))) ++(str); } while (0)
 
 static inline bool
@@ -38,6 +92,8 @@ skip_past_char (char **str, char c)
 }
 #define skip_past_comma(str) skip_past_char (str, ',')
 
+#if (TC_OBJ_ATTR_v1)
+
 /* A list of attributes that have been explicitly set by the assembly code.
    VENDOR is the vendor id, BASE is the tag shifted right by the number
    of bits in MASK, and bit N of MASK is set if tag BASE+N has been set.  */
@@ -121,6 +177,1073 @@ oav1_attr_seen (obj_attr_vendor_t vendor, obj_attr_tag_t tag)
   return false;
 }
 
+#endif /* TC_OBJ_ATTR_v1 */
+
+/* Expected argument tokens for object attribute directives.  */
+typedef enum {
+  /* Base types.  */
+  IDENTIFIER = 0x1,
+  UNSIGNED_INTEGER = 0x2,
+  SIGNED_INTEGER = 0x4,
+  STRING = 0x8,
+  LIST = 0x10,
+  LT_MASK = 0xFF,
+  /* Higher types.  */
+  SUBSECTION_NAME = 0x100,
+  SUBSECTION_OPTION_1 = 0x200,
+  SUBSECTION_OPTION_2 = 0x400,
+  ATTRIBUTE_KEY = 0x800,
+  ATTRIBUTE_VALUE = 0x1000,
+  HT_MASK = 0xFF00,
+} arg_token_t;
+
+/* Free an arguments list of size N.  */
+static void
+args_list_free (arg_t *args, size_t n)
+{
+  for (size_t i = 0; i < n; ++i)
+    if (args[i].vtype == VALUE_STRING)
+      free ((void *) args[i].val.string);
+    else if (args[i].vtype == VALUE_LIST)
+      args_list_free (args[i].val.list.elts, args[i].val.list.len);
+  free (args);
+}
+
+/* Extract a string literal from the input.  */
+static bool
+extract_string_literal (arg_t *arg_out)
+{
+  int len;
+  char *obstack_buf = demand_copy_C_string (&len);
+  if (obstack_buf != NULL)
+    {
+      arg_out->val.string = xstrdup (obstack_buf);
+      obstack_free (&notes, obstack_buf);
+      arg_out->vtype = VALUE_STRING;
+      return true;
+    }
+
+  arg_out->val.string = NULL;
+  return false;
+}
+
+/* Extract an integer literal from the input.
+   Anything matched by O_constant is considered an integer literal (see the
+   usage of O_constant in expr.c to see all the matches.
+   Return true on success, false otherwise. If a signedness issue is detected,
+   'signedness_issue' is also set to true.  */
+static bool
+extract_integer_literal (arg_t *arg_out,
+                        bool want_unsigned,
+                        bool *signedness_issue)
+{
+  const char *cursor_begin = input_line_pointer;
+  expressionS exp;
+  expression_and_evaluate (&exp);
+  if (exp.X_op != O_constant)
+    {
+      char backup_c = *input_line_pointer;
+      *input_line_pointer = '\0';
+      as_bad (_("expression '%s' does not resolve to an integer"),
+             cursor_begin);
+      /* Restore the character pointed by the current cursor position,
+        otherwise '\0' misleads ignore_rest_of_line().  */
+      *input_line_pointer = backup_c;
+      return false;
+    }
+
+  int64_t val = (int64_t) exp.X_add_number;
+  if (want_unsigned)
+    {
+      if (! exp.X_unsigned && val < 0)
+       {
+         as_bad (_("unexpected value '%" PRId64 "', expected `unsigned integer' instead"),
+                 val);
+         *signedness_issue = true;
+         return false;
+       }
+      arg_out->val.u64 = val;
+      arg_out->vtype = VALUE_UNSIGNED_INTEGER;
+    }
+  else
+    {
+      arg_out->val.i64 = val;
+      arg_out->vtype = VALUE_SIGNED_INTEGER;
+    }
+  return true;
+}
+
+/* Extract an identifier based on the provided character matcher.  */
+static bool
+extract_identifier (bool (*char_predicate) (char), arg_t *arg_out)
+{
+  const char *s = input_line_pointer;
+  unsigned int i = 0;
+  for (; char_predicate (*input_line_pointer); ++input_line_pointer)
+    i++;
+  if (i == 0)
+    {
+      as_bad (_("invalid character '%c' in identifier"),
+             *input_line_pointer);
+      return false;
+    }
+
+  arg_out->vtype = VALUE_STRING;
+  arg_out->val.string = xmemdup0 (s, i);
+  return true;
+}
+
+#if (TC_OBJ_ATTR_v2)
+/* Resolve the identifier if it matches a known tag.  */
+static bool
+resolve_if_matching_known_tag (const char *identifier,
+                              const obj_attr_tag_info_t *known_identifier,
+                              arg_t *val_out)
+{
+  if (strcmp (known_identifier->name, identifier) != 0)
+    return false;
+
+  val_out->val.u64 = known_identifier->value;
+  val_out->vtype = VALUE_UNSIGNED_INTEGER;
+  return true;
+}
+
+/* Resolve the identifier if it matches the known one.  */
+static bool
+resolve_if_matching (const char *identifier,
+                    const oav2_identifier_t *known_identifier,
+                    arg_t *val_out)
+{
+  /* Lowercase the identifier before comparison.  */
+  char normalized_identifier[100];
+  unsigned int i = 0;
+  for (; i < sizeof (normalized_identifier) && identifier[i] != '\0'; ++i)
+    normalized_identifier[i] = TOLOWER (identifier[i]);
+  if (i >= sizeof (normalized_identifier))
+    /* Identifier is too long.  */
+    return false;
+  gas_assert (identifier[i] == '\0');
+  normalized_identifier[i] = '\0';
+
+  /* Comparison with normalized identifier.  */
+  if (strcmp (known_identifier->name, normalized_identifier) != 0)
+    return false;
+
+  /* We only need U8 and Bool for now.  */
+  switch (known_identifier->value.vtype)
+    {
+    case OAv2_ASM_ID_VALUE_U8:
+      val_out->val.u64 = known_identifier->value.val.b;
+      val_out->vtype = VALUE_UNSIGNED_INTEGER;
+      break;
+    case OAv2_ASM_ID_VALUE_BOOL:
+      val_out->val.u64 = known_identifier->value.val.u8;
+      val_out->vtype = VALUE_UNSIGNED_INTEGER;
+      break;
+    default:
+      abort ();
+    }
+
+  return true;
+}
+#endif /* TC_OBJ_ATTR_v2 */
+
+#if (TC_OBJ_ATTR_v1)
+/* Look up attribute tags defined in the backend (object attribute v1).  */
+static bool
+obj_attr_v1_lookup_known_attr_tag_symbol
+  (const char *identifier ATTRIBUTE_UNUSED,
+   arg_token_t token_type,
+   arg_t *val_out)
+{
+  gas_assert (token_type & UNSIGNED_INTEGER);
+
+#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
+# define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
+#endif
+  int tag = CONVERT_SYMBOLIC_ATTRIBUTE (identifier);
+  if (tag < 0)
+    return false;
+  val_out->val.u64 = tag;
+  val_out->vtype = VALUE_UNSIGNED_INTEGER;
+  return true;
+}
+#endif /* TC_OBJ_ATTR_v1 */
+
+#if (TC_OBJ_ATTR_v2)
+/* Look up attribute tags defined in the backend (object attribute v2).  */
+static bool
+obj_attr_v2_lookup_known_attr_tag_symbol (const char *identifier,
+                                         arg_token_t token_type,
+                                         arg_t *val_out)
+{
+  obj_attr_subsection_v2_t *subsec = elf_obj_attr_subsections (stdoutput).last;
+  /* This function is called when setting a value for an attribute, and it
+     requires an active subsection.  Calling this function without setting
+     'subsec' is a logical error.  Invalid user code, where the subsection
+     has not been selected must be handled by the caller.  We require 'subsec'
+     to be non-NULL here.  */
+  gas_assert (subsec != NULL);
+
+  /* An attribute tag is an unsigned integer, so the expected token type should
+     always have the base type UNSIGNED_INTEGER.  Otherwise, this function was
+     called incorrectly.  */
+  gas_assert (token_type & UNSIGNED_INTEGER);
+
+  bool resolved = false;
+  const struct elf_backend_data *be = get_elf_backend_data (stdoutput);
+  const known_subsection_v2_t *known_subsec
+    = bfd_obj_attr_v2_identify_subsection (be, subsec->name);
+  if (known_subsec != NULL)
+    {
+      for (size_t i = 0; i < known_subsec->len && ! resolved; ++i)
+       resolved
+         = resolve_if_matching_known_tag (identifier,
+                                          &known_subsec->known_attrs[i].tag,
+                                          val_out);
+    }
+
+  if (resolved)
+    /* An attribute tag is an unsigned integer, so the type of the found value
+       should be VALUE_UNSIGNED_INTEGER.  Otherwise, check if you set correctly
+       the type of the value associated to the symbol.  */
+    gas_assert (val_out->vtype == VALUE_UNSIGNED_INTEGER);
+
+  return resolved;
+}
+#endif /* TC_OBJ_ATTR_v2 */
+
+/* Look up known symbols, and try to resolve the given identifier.  */
+static bool
+lookup_known_symbols (const char *identifier,
+                     arg_token_t token_type,
+                     arg_t *val_out)
+{
+  if (identifier == NULL)
+    return false;
+
+  arg_token_t high_ttype = (token_type & HT_MASK);
+
+#if (TC_OBJ_ATTR_v2)
+  static const oav2_identifier_t known_identifiers_subsection_optional[] = {
+    { "optional", {
+       .val.b = true,
+       .vtype = OAv2_ASM_ID_VALUE_BOOL
+      }
+    },
+    { "required", {
+       .val.b = false,
+       .vtype = OAv2_ASM_ID_VALUE_BOOL
+      }
+    },
+  };
+
+  static const oav2_identifier_t known_identifiers_subsection_encoding[] = {
+    { "uleb128", {
+       .val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_ULEB128),
+       .vtype = OAv2_ASM_ID_VALUE_U8
+      }
+    },
+    { "ntbs", {
+       .val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_NTBS),
+       .vtype = OAv2_ASM_ID_VALUE_U8
+      }
+    },
+  };
+#endif /* TC_OBJ_ATTR_v2 */
+
+  bool resolved = false;
+
+#if (TC_OBJ_ATTR_v2)
+  if (high_ttype == SUBSECTION_OPTION_1 || high_ttype == SUBSECTION_OPTION_2)
+    {
+      const oav2_identifier_t *known_identifiers
+       = (high_ttype == SUBSECTION_OPTION_1
+          ? known_identifiers_subsection_optional
+          : known_identifiers_subsection_encoding);
+      const size_t N_identifiers
+       = (high_ttype == SUBSECTION_OPTION_1
+          ? ARRAY_SIZE (known_identifiers_subsection_optional)
+          : ARRAY_SIZE (known_identifiers_subsection_encoding));
+
+      for (size_t i = 0; i < N_identifiers && ! resolved; ++i)
+       resolved = resolve_if_matching (identifier,
+                                       &known_identifiers[i],
+                                       val_out);
+    }
+  else
+#endif /* TC_OBJ_ATTR_v2 */
+  if (high_ttype == ATTRIBUTE_KEY)
+    {
+      obj_attr_version_t version = elf_obj_attr_version (stdoutput);
+      switch (version)
+       {
+#if (TC_OBJ_ATTR_v1)
+       case OBJ_ATTR_V1:
+         resolved = obj_attr_v1_lookup_known_attr_tag_symbol
+           (identifier, token_type, val_out);
+         break;
+#endif /* TC_OBJ_ATTR_v1 */
+#if (TC_OBJ_ATTR_v2)
+       case OBJ_ATTR_V2:
+         resolved = obj_attr_v2_lookup_known_attr_tag_symbol
+           (identifier, token_type, val_out);
+         break;
+#endif /* TC_OBJ_ATTR_v2 */
+       default:
+         abort ();
+       }
+    }
+  else
+    abort ();
+
+  return resolved;
+}
+
+/* Look up the symbol table of this compilation unit, and try to resolve the
+   given identifier.  */
+static bool
+lookup_symbol_table (const char *identifier,
+                    const arg_token_t expected_ttype,
+                    arg_t *val_out)
+{
+  if (identifier == NULL)
+    return false;
+
+  /* Note: signed integer are unsupported for now.  */
+  gas_assert (expected_ttype & UNSIGNED_INTEGER);
+
+  symbolS *symbolP = symbol_find (identifier);
+  if (symbolP == NULL)
+    return false;
+
+  if (! S_IS_DEFINED (symbolP))
+    return false;
+
+  valueT val = S_GET_VALUE (symbolP);
+  val_out->val.u64 = val;
+  val_out->vtype = VALUE_UNSIGNED_INTEGER;
+
+  return true;
+}
+
+/* Function similar to snprintf from the standard library, except that it
+   also updates the buffer pointer to point to the last written character,
+   and length to match the remaining space in the buffer.
+   Return the number of bytes printed.  The function is assumed to always be
+   successful, and a failure with vnsprintf() will trigger an assert().  */
+static size_t
+snprintf_append (char **sbuffer, size_t *length,
+                const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  int rval = vsnprintf (*sbuffer, *length, format, args);
+  va_end (args);
+
+  gas_assert (rval >= 0);
+  size_t written = rval;
+  *length -= written;
+  *sbuffer += written;
+  return written;
+}
+
+/* Return the list of comma-separated strings matching the expected types
+   (i.e. the flags set in low_ttype).  */
+static const char *
+expectations_to_string (const arg_token_t low_ttype,
+                       char *sbuffer, size_t length)
+{
+  unsigned match_n = 0;
+  char *sbuffer_start = sbuffer;
+  size_t total = 0;
+  if (low_ttype & IDENTIFIER)
+    {
+      ++match_n;
+      total += snprintf_append (&sbuffer, &length, "`%s'", "identifier");
+    }
+
+#define EXP_APPEND_TYPE_STR(type_flag, type_str)                       \
+  if (low_ttype & type_flag)                                           \
+    {                                                                  \
+      if (match_n >= 1)                                                        \
+       total += snprintf_append (&sbuffer, &length, "%s", ", or ");    \
+      ++match_n;                                                       \
+      total += snprintf_append (&sbuffer, &length, "`%s'", type_str);  \
+    }
+
+  EXP_APPEND_TYPE_STR (STRING, "string");
+  EXP_APPEND_TYPE_STR (UNSIGNED_INTEGER, "unsigned integer");
+  EXP_APPEND_TYPE_STR (SIGNED_INTEGER, "signed integer");
+#undef APPEND_TYPE_STR
+
+  gas_assert (total <= length);
+  return sbuffer_start;
+}
+
+/* In the context of object attributes, an identifier is defined with the
+   following lexical constraint: [a-zA-z_][a-zA-Z0-9_]*.  An identifier can
+   be used to define the name of a subsection, its optionality, or its
+   encoding, as well as for an attribute's tag.  */
+
+static bool
+is_identifier_beginner (char c)
+{
+  return ISALPHA (c) || c == '_';
+}
+
+static bool
+is_part_of_identifier (char c)
+{
+  return ISALNUM (c) || c == '_';
+}
+
+/* Parse an argument, and set its type accordingly depending on the input
+   value, and the constraints on the expected argument.  */
+static bool
+obj_attr_parse_arg (arg_token_t expected_ttype,
+                   bool resolve_identifier,
+                   bool optional,
+                   arg_t *arg_out)
+{
+  const arg_token_t low_ttype = (expected_ttype & LT_MASK);
+
+  if (optional && is_end_of_stmt (*input_line_pointer))
+    {
+      arg_out->vtype = VALUE_OPTIONAL_ABSENT;
+      return true;
+    }
+
+  /* Check whether this looks like a string literal
+     Note: symbol look-up for string literals is not available.  */
+  if (*input_line_pointer == '"')
+    {
+      bool status = extract_string_literal (arg_out);
+      if (status && (low_ttype & STRING))
+       return true;
+
+      if (status)
+       {
+         char sbuffer[100];
+         as_bad (_("unexpected `string' \"%s\", expected %s instead"),
+                 arg_out->val.string,
+                 expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
+         free ((char *) arg_out->val.string);
+         arg_out->val.string = NULL;
+         arg_out->vtype = VALUE_UNDEFINED;
+       }
+      return false;
+    }
+
+  /* Check whether this looks like an identifier.  */
+  if (is_identifier_beginner (*input_line_pointer))
+    {
+      bool status = extract_identifier (is_part_of_identifier, arg_out);
+      /* is_identifier_beginner() confirmed that it was the beginning of an
+        identifier, so we don't expect the extraction to fail.  */
+      gas_assert (status);
+      gas_assert (arg_out->vtype == VALUE_STRING);
+
+      if (! (low_ttype & IDENTIFIER))
+       {
+         char sbuffer[100];
+         as_bad (_("unexpected `identifier' \"%s\", expected %s instead"),
+                 arg_out->val.string,
+                 expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
+         free ((char *) arg_out->val.string);
+         arg_out->val.string = NULL;
+         arg_out->vtype = VALUE_UNDEFINED;
+         return false;
+       }
+
+      /* In some cases, we don't want to resolve the identifier because it is the
+        actual value.  */
+      if (! resolve_identifier)
+       return true;
+
+      /* Move the identifier out of arg_out.  */
+      const char *identifier = arg_out->val.string;
+      arg_out->val.string = NULL;
+      arg_out->vtype = VALUE_UNDEFINED;
+      bool resolved = true;
+
+      /* The identifier is a symbol, let's try to resolve it by:
+        1. using the provided list of known symbols.
+          a) backend-independent
+          b) backend-specific.  */
+      if (lookup_known_symbols (identifier, expected_ttype, arg_out))
+       goto free_identifier;
+
+      /* 2. using the symbol table for this compilation unit.
+        Note: this is the last attempt before failure.  */
+      if (lookup_symbol_table (identifier, low_ttype, arg_out))
+       goto free_identifier;
+
+      as_bad (_("unknown identifier '%s' in this context"), identifier);
+      resolved = false;
+
+ free_identifier:
+      free ((char *) identifier);
+      return resolved;
+    }
+
+  /* If it is neither a string nor an identifier, it must be an expression.  */
+  bool signedness_issue = false;
+  bool success = extract_integer_literal (arg_out,
+                                         (low_ttype & UNSIGNED_INTEGER),
+                                         &signedness_issue);
+  if (success && (low_ttype & (UNSIGNED_INTEGER | SIGNED_INTEGER)))
+    return true;
+
+  char sbuffer[100];
+  if (success)
+    as_bad (_("unexpected integer '%"PRIu64"', expected %s instead"),
+           arg_out->val.u64,
+           expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
+  else if ((low_ttype & UNSIGNED_INTEGER) && signedness_issue)
+    {
+      /* Already handled by extract_integer_literal(), nothing to do.  */
+    }
+  else
+    as_bad (_("fell back to integer literal extraction from expression, "
+             "but expected %s instead"),
+           expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
+
+  arg_out->vtype = VALUE_UNDEFINED;
+  return false;
+}
+
+/* Trim white space before a parameter.
+   Error if it meets a parameter separator before a parameter.  */
+static bool
+trim_whitespace_before_param (void)
+{
+  skip_whitespace (input_line_pointer);
+  if (*input_line_pointer == ',')
+    {
+      as_bad (_("syntax error, comma not expected here"));
+      return false;
+    }
+  return true;
+}
+
+/* Skip white space + parameter separator after a parameter.
+   Error if it does not meet a parameter separator after a parameter.  */
+static bool
+skip_whitespace_past_comma (void)
+{
+  skip_whitespace (input_line_pointer);
+  if (! skip_past_comma (&input_line_pointer))
+    {
+      as_bad (_("syntax error, comma missing here"));
+      return false;
+    }
+  return true;
+}
+
+/* Can parse a list of arguments with variable length.  */
+static bool
+obj_attr_parse_args (arg_token_t expected_ttype,
+                    bool resolve_identifier,
+                    arg_t *arg_out)
+{
+  if ((expected_ttype & LIST) == 0)
+    return obj_attr_parse_arg (expected_ttype, resolve_identifier, false,
+                              arg_out);
+
+  static const size_t LIST_MAX_SIZE = 2;
+  arg_t *arg_list = xcalloc (LIST_MAX_SIZE, sizeof (*arg_list));
+
+  /* We don't want to support recursive lists.  */
+  expected_ttype &= ~LIST;
+
+  size_t n = 0;
+  do {
+    if (! trim_whitespace_before_param ())
+      goto bad;
+
+    if (! obj_attr_parse_arg (expected_ttype, resolve_identifier, false,
+                             &arg_list[n]))
+      goto bad;
+
+    ++n;
+    skip_whitespace (input_line_pointer);
+    if (is_end_of_stmt (*input_line_pointer))
+      break;
+
+    if (! skip_whitespace_past_comma ())
+      goto bad;
+
+    if (n >= LIST_MAX_SIZE)
+      {
+       as_bad ("too many arguments for a list (max: %zu)", LIST_MAX_SIZE);
+       goto bad;
+      }
+  } while (n < LIST_MAX_SIZE);
+
+  arg_out->vtype = VALUE_LIST;
+  arg_out->val.list.len = n;
+  arg_out->val.list.elts = arg_list;
+  return true;
+
+ bad:
+  args_list_free (arg_list, n);
+  return false;
+}
+
+#if (TC_OBJ_ATTR_v2)
+static bool
+is_valid_boolean (uint64_t value)
+{
+  return value <= 1;
+}
+
+#define is_valid_comprehension is_valid_boolean
+
+static bool
+is_valid_encoding (uint64_t value)
+{
+  value = obj_attr_encoding_v2_from_u8 (value);
+  return OA_ENC_UNSET < value && value <= OA_ENC_MAX;
+}
+
+#endif /* TC_OBJ_ATTR_v2 */
+
+#if (TC_OBJ_ATTR_v1)
+/* Determine the expected argument type based on the tag ID.  */
+static arg_token_t
+obj_attr_v1_get_arg_type (bfd *abfd,
+                         obj_attr_vendor_t vendor,
+                         obj_attr_tag_t tag)
+{
+  int attr_type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
+  arg_token_t arg_type;
+  if (attr_type == (ATTR_TYPE_FLAG_STR_VAL | ATTR_TYPE_FLAG_INT_VAL))
+    arg_type = LIST | UNSIGNED_INTEGER | STRING;
+  else if (attr_type == ATTR_TYPE_FLAG_STR_VAL)
+    arg_type = STRING;
+  else
+    /* Covers the remaning cases:
+       - ATTR_TYPE_FLAG_INT_VAL.
+       - ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_NO_DEFAULT.  */
+    arg_type = UNSIGNED_INTEGER;
+  return arg_type;
+}
+#endif /* TC_OBJ_ATTR_v1 */
+
+#if (TC_OBJ_ATTR_v2)
+/* Determine the expected argument type based on the subsection encoding.  */
+static arg_token_t
+obj_attr_v2_get_arg_type (obj_attr_encoding_v2_t subsec_encoding)
+{
+  arg_token_t arg_type;
+  switch (subsec_encoding)
+    {
+    case OA_ENC_ULEB128:
+      arg_type = UNSIGNED_INTEGER;
+      break;
+    case OA_ENC_NTBS:
+      arg_type = STRING;
+      break;
+    case OA_ENC_UNSET:
+    default:
+      abort ();
+    }
+  return arg_type;
+}
+#endif /* TC_OBJ_ATTR_v2 */
+
+/* Parse the arguments of [vendor]_attribute directive.  */
+static arg_t *
+vendor_attribute_parse_args (obj_attr_vendor_t vendor ATTRIBUTE_UNUSED,
+                            const obj_attr_subsection_v2_t *subsec ATTRIBUTE_UNUSED,
+                            unsigned int nargs, ...)
+{
+  va_list args;
+  va_start (args, nargs);
+
+  arg_t *args_out = xcalloc (nargs, sizeof (arg_t));
+
+  for (unsigned int n = 0; n < nargs; ++n)
+    {
+      if (! trim_whitespace_before_param ())
+       goto bad;
+
+      arg_t *arg_out = &args_out[n];
+
+      arg_token_t expected_ttype = va_arg (args, arg_token_t);
+      arg_token_t high_ttype = (expected_ttype & HT_MASK);
+      /* Make sure that we called the right parse_args().  */
+      gas_assert (high_ttype == ATTRIBUTE_KEY
+              || high_ttype == ATTRIBUTE_VALUE);
+
+      if (high_ttype == ATTRIBUTE_VALUE)
+       {
+         arg_token_t type_attr_value
+#if (TC_OBJ_ATTR_v1 && TC_OBJ_ATTR_v2)
+           = (subsec != NULL)
+             ? obj_attr_v2_get_arg_type (subsec->encoding)
+             : obj_attr_v1_get_arg_type (stdoutput, vendor,
+                                         args_out[n - 1].val.u64);
+#elif (TC_OBJ_ATTR_v1)
+           = obj_attr_v1_get_arg_type (stdoutput, vendor,
+                                       args_out[n - 1].val.u64);
+#else /* TC_OBJ_ATTR_v2 */
+           = obj_attr_v2_get_arg_type (subsec->encoding);
+#endif
+         expected_ttype |= type_attr_value;
+       }
+
+      if (! obj_attr_parse_args (expected_ttype, true, arg_out))
+       {
+         if (high_ttype == ATTRIBUTE_KEY)
+           {
+             as_bad (_("could not parse attribute tag"));
+             goto bad;
+           }
+         else
+           {
+             as_bad (_("could not parse attribute value"));
+             goto bad;
+           }
+       }
+
+      if (n + 1 < nargs && !skip_whitespace_past_comma ())
+       goto bad;
+    }
+
+  demand_empty_rest_of_line ();
+  va_end (args);
+  return args_out;
+
+ bad:
+  ignore_rest_of_line ();
+  args_list_free (args_out, nargs);
+  va_end (args);
+  return NULL;
+}
+
+#if (TC_OBJ_ATTR_v1)
+/* Record an attribute (object attribute v1 only).  */
+static obj_attribute *
+obj_attr_v1_record (bfd *abfd,
+                   const obj_attr_vendor_t vendor,
+                   const obj_attr_tag_t tag,
+                   arg_t *parsed_arg)
+{
+  obj_attribute *attr = bfd_elf_new_obj_attr (abfd, vendor, tag);
+  if (attr != NULL)
+    {
+      attr->type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
+      if (parsed_arg->vtype == VALUE_LIST)
+       {
+         arg_variant_list_t *plist = &parsed_arg->val.list;
+         gas_assert (plist->len == 2
+                     && plist->elts[0].vtype == VALUE_UNSIGNED_INTEGER
+                     && plist->elts[1].vtype == VALUE_STRING);
+         attr->i = plist->elts[0].val.u64;
+         attr->s = (char *) plist->elts[1].val.string;
+         plist->elts[1].val.string = NULL;
+       }
+      else if (parsed_arg->vtype == VALUE_STRING)
+       {
+         attr->s = (char *) parsed_arg->val.string;
+         parsed_arg->val.string = NULL;
+       }
+      else
+       {
+         attr->i = parsed_arg->val.u64;
+       }
+    }
+  return attr;
+}
+#endif /* TC_OBJ_ATTR_v1 */
+
+#if (TC_OBJ_ATTR_v2)
+/* Parse the arguments of [vendor]_subsection directive (v2 only).  */
+static arg_t *
+vendor_subsection_parse_args (unsigned int nargs, ...)
+{
+  va_list args;
+  va_start (args, nargs);
+
+  arg_t *args_out = xcalloc (nargs, sizeof (arg_t));
+
+  for (unsigned int n = 0; n < nargs; ++n)
+    {
+      if (! trim_whitespace_before_param ())
+       goto bad;
+
+      arg_t *arg_out = &args_out[n];
+
+      arg_token_t expected_ttype = va_arg (args, arg_token_t);
+      arg_token_t high_ttype = (expected_ttype & HT_MASK);
+      /* Make sure that we called the right parse_args().  */
+      gas_assert (high_ttype == SUBSECTION_NAME
+                 || high_ttype == SUBSECTION_OPTION_1
+                 || high_ttype == SUBSECTION_OPTION_2);
+
+      if (high_ttype == SUBSECTION_NAME)
+       {
+         if (! obj_attr_parse_arg (expected_ttype, false, false, arg_out))
+           {
+             as_bad (_("expected <subsection_name>, <comprehension>, "
+                       "<encoding>"));
+             goto bad;
+           }
+       }
+      else if (high_ttype == SUBSECTION_OPTION_1
+           || high_ttype == SUBSECTION_OPTION_2)
+       {
+         if (! obj_attr_parse_arg (expected_ttype, true, true, arg_out))
+           goto bad;
+
+         if (arg_out->vtype == VALUE_OPTIONAL_ABSENT)
+           continue;
+
+         if (high_ttype == SUBSECTION_OPTION_1
+             && ! is_valid_comprehension (arg_out->val.u64))
+           {
+             as_bad (
+               _("invalid value '%lu', expected values for <comprehension> "
+                 "are 0 (=`required') or 1 (=`optional')"), arg_out->val.u64);
+             goto bad;
+           }
+         else if (high_ttype == SUBSECTION_OPTION_2
+               && ! is_valid_encoding (arg_out->val.u64))
+           {
+             as_bad (
+               _("invalid value '%lu', expected values for <encoding> are 0"
+                 " (=`ULEB128') or 1 (=`NTBS')"), arg_out->val.u64);
+             goto bad;
+           }
+       }
+      else
+       abort ();
+
+      if (n + 1 < nargs
+         && ! is_end_of_stmt (*input_line_pointer)
+         && ! skip_whitespace_past_comma ())
+       goto bad;
+    }
+
+  va_end (args);
+  demand_empty_rest_of_line ();
+  return args_out;
+
+ bad:
+  ignore_rest_of_line ();
+  args_list_free (args_out, nargs);
+  va_end (args);
+  return NULL;
+}
+
+/* Record an attribute (object attribute v2 only).  */
+static void
+obj_attr_v2_record (obj_attr_tag_t key, arg_t *arg_val)
+{
+  /* An OAv2 cannot be recorded unless a subsection has been recorded.  */
+  gas_assert (elf_obj_attr_subsections (stdoutput).last != NULL);
+
+  union obj_attr_value_v2 obj_attr_val;
+  if (arg_val->vtype == VALUE_UNSIGNED_INTEGER)
+    obj_attr_val.uint = arg_val->val.u64;
+  else
+    {
+      /* Move the string.  */
+      obj_attr_val.string = arg_val->val.string;
+      arg_val->val.string = NULL;
+    }
+
+  obj_attr_v2_t *obj_attr = bfd_elf_obj_attr_v2_init (key, obj_attr_val);
+  gas_assert (obj_attr != NULL);
+
+  /* Go over the list of already recorded attributes and check for
+     redefinitions (which are forbidden).  */
+  bool skip_recording = false;
+  obj_attr_v2_t *recorded_attr = _bfd_obj_attr_v2_find_by_tag
+    (elf_obj_attr_subsections (stdoutput).last, obj_attr->tag, false);
+  if (recorded_attr != NULL)
+    {
+      if ((arg_val->vtype == VALUE_UNSIGNED_INTEGER
+          && recorded_attr->val.uint != obj_attr->val.uint)
+         || (arg_val->vtype == VALUE_STRING
+             && strcmp (recorded_attr->val.string, obj_attr->val.string) != 0))
+       as_bad (_("attribute '%lu' cannot be redefined"), recorded_attr->tag);
+      skip_recording = true;
+    }
+
+  if (skip_recording)
+    {
+      if (arg_val->vtype == VALUE_STRING)
+       free ((void *) obj_attr->val.string);
+      free (obj_attr);
+      return;
+    }
+
+  LINKED_LIST_APPEND (obj_attr_v2_t)
+    (elf_obj_attr_subsections (stdoutput).last, obj_attr);
+}
+
+/* Record a subsection (object attribute v2 only).
+   Note: this function takes the ownership of 'name', so is responsible to free
+   it if an issue occurs.  */
+static void
+obj_attr_v2_subsection_record (const char *name,
+                              arg_t *arg_comprehension,
+                              arg_t *arg_encoding)
+{
+  obj_attr_subsection_v2_t *already_recorded_subsec
+    = bfd_obj_attr_subsection_v2_find_by_name
+      (elf_obj_attr_subsections (stdoutput).first, name, false);
+
+  bool comprehension_optional = arg_comprehension->val.u64;
+  obj_attr_encoding_v2_t encoding
+    = obj_attr_encoding_v2_from_u8 (arg_encoding->val.u64);
+
+  if (already_recorded_subsec != NULL)
+    {
+      bool error_redeclaration = false;
+
+      if (arg_comprehension->vtype == VALUE_OPTIONAL_ABSENT)
+       gas_assert (arg_encoding->vtype == VALUE_OPTIONAL_ABSENT);
+      else if (comprehension_optional != already_recorded_subsec->optional)
+       error_redeclaration = true;
+
+      if (arg_encoding->vtype != VALUE_OPTIONAL_ABSENT
+         && encoding != already_recorded_subsec->encoding)
+       error_redeclaration = true;
+
+      /* Check for mismatching redefinition of the subsection, i.e. the names
+        match but the properties are different.  */
+      if (error_redeclaration)
+       {
+         const char *prev_comprehension = bfd_oav2_comprehension_to_string (
+           already_recorded_subsec->optional);
+         const char *prev_encoding = bfd_oav2_encoding_to_string (
+           already_recorded_subsec->encoding);
+         as_bad (_("incompatible redeclaration of subsection %s"), name);
+         as_info (1, _("previous declaration had properties: %s=%s, %s=%s"),
+                  "comprehension", prev_comprehension,
+                  "encoding", prev_encoding);
+         goto free_name;
+       }
+
+      /* Move the existing subsection to the last position.  */
+      LINKED_LIST_REMOVE (obj_attr_subsection_v2_t)
+       (&elf_obj_attr_subsections (stdoutput), already_recorded_subsec);
+      LINKED_LIST_APPEND (obj_attr_subsection_v2_t)
+       (&elf_obj_attr_subsections (stdoutput), already_recorded_subsec);
+      /* Note: 'name' was unused, and will be freed on exit.  */
+    }
+  else
+    {
+      if (arg_comprehension->vtype == VALUE_OPTIONAL_ABSENT
+         || arg_encoding->vtype == VALUE_OPTIONAL_ABSENT)
+       {
+         as_bad (_("comprehension and encoding of a subsection cannot be "
+                   "omitted on the first declaration"));
+         goto free_name;
+       }
+
+      obj_attr_subsection_scope_v2_t scope
+       = bfd_elf_obj_attr_subsection_v2_scope (stdoutput, name);
+
+      /* Note: ownership of 'name' is transfered to the callee when initializing
+        the subsection.  That is why we skip free() at the end.  */
+      obj_attr_subsection_v2_t *new_subsection
+       = bfd_elf_obj_attr_subsection_v2_init (name, scope,
+                                              comprehension_optional,
+                                              encoding);
+      LINKED_LIST_APPEND (obj_attr_subsection_v2_t)
+       (&elf_obj_attr_subsections (stdoutput), new_subsection);
+      return;
+    }
+
+ free_name:
+  free ((void *) name);
+}
+#endif /* TC_OBJ_ATTR_v2 */
+
+/* Parse an attribute directive (supports both v1 & v2).  */
+obj_attr_tag_t
+obj_attr_process_attribute (obj_attr_vendor_t vendor)
+{
+  obj_attr_version_t version = elf_obj_attr_version (stdoutput);
+  obj_attr_subsection_v2_t *subsec = NULL;
+
+#if (TC_OBJ_ATTR_v2)
+  if (version == OBJ_ATTR_V2)
+    {
+      subsec = elf_obj_attr_subsections (stdoutput).last;
+      if (subsec == NULL)
+       {
+         as_bad (_("declaration of an attribute outside the scope of an "
+                   "attribute subsection"));
+         ignore_rest_of_line ();
+         return 0;
+       }
+    }
+#endif /* TC_OBJ_ATTR_v2 */
+
+  const size_t N_ARGS = 2;
+  arg_t *args = vendor_attribute_parse_args (
+    vendor, subsec, N_ARGS,
+    ATTRIBUTE_KEY | IDENTIFIER | UNSIGNED_INTEGER,
+    ATTRIBUTE_VALUE);
+
+  if (args == NULL)
+    return 0;
+
+  obj_attr_tag_t tag = args[0].val.u64;
+  switch (version)
+    {
+#if (TC_OBJ_ATTR_v1)
+    case OBJ_ATTR_V1:
+      oav1_attr_record_seen (vendor, tag);
+      obj_attr_v1_record (stdoutput, vendor, tag, &args[1]);
+      break;
+#endif /* TC_OBJ_ATTR_v1 */
+#if (TC_OBJ_ATTR_v2)
+    case OBJ_ATTR_V2:
+      obj_attr_v2_record (tag, &args[1]);
+      break;
+#endif /* TC_OBJ_ATTR_v2 */
+    default:
+      abort ();
+    }
+
+  args_list_free (args, N_ARGS);
+
+  return tag;
+}
+
+#if (TC_OBJ_ATTR_v2)
+/* Parse an object attribute v2's subsection directive.  */
+void
+obj_attr_process_subsection (void)
+{
+  const size_t N_ARGS = 3;
+  arg_t *args = vendor_subsection_parse_args (
+    N_ARGS,
+    SUBSECTION_NAME | IDENTIFIER,
+    SUBSECTION_OPTION_1 | IDENTIFIER | UNSIGNED_INTEGER,
+    SUBSECTION_OPTION_2 | IDENTIFIER | UNSIGNED_INTEGER);
+
+  if (args == NULL)
+    return;
+
+  /* Note: move the value to avoid double free.  */
+  const char *name = args[0].val.string;
+  args[0].val.string = NULL;
+
+  /* Note: ownership of 'name' is transferred to the callee.  */
+  obj_attr_v2_subsection_record (name, &args[1], &args[2]);
+  args_list_free (args, N_ARGS);
+}
+#endif /* TC_OBJ_ATTR_v2 */
+
+#if (TC_OBJ_ATTR_v1)
 /* Parse an attribute directive for VENDOR.
    Returns the attribute number read, or zero on error.  */
 
@@ -234,13 +1357,18 @@ obj_attr_v1_process_attribute (obj_attr_vendor_t vendor)
   ignore_rest_of_line ();
   return 0;
 }
+#endif /* TC_OBJ_ATTR_v1 */
 
 /* Parse a .gnu_attribute directive.  */
 
 void
 obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
 {
+#if (TC_OBJ_ATTR_v1 && !TC_OBJ_ATTR_v2)
   obj_attr_v1_process_attribute (OBJ_ATTR_GNU);
+#else
+  obj_attr_process_attribute (OBJ_ATTR_GNU);
+#endif
 }
 
 #endif /* TC_OBJ_ATTR */
index 6e39f5851b65aa5203a3de4c3aa303298cca286e..b91edd4276c1eafdb4e64e7125fe452e4fd0cb8d 100644 (file)
 
 #include "as.h"
 
+#ifndef TC_OBJ_ATTR_v1
+#define TC_OBJ_ATTR_v1 0
+#endif
+#ifndef TC_OBJ_ATTR_v2
+#define TC_OBJ_ATTR_v2 0
+#endif
+
+#if (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2)
+#define TC_OBJ_ATTR 1
+#endif
+
 #ifdef TC_OBJ_ATTR
 
+#if (TC_OBJ_ATTR_v1)
 extern void oav1_attr_info_init (void);
 extern void oav1_attr_info_exit (void);
 extern bool oav1_attr_seen (obj_attr_vendor_t, obj_attr_tag_t);
+#endif /* TC_OBJ_ATTR_v1 */
+
+/* Object attributes parsers.  */
+
+#if (TC_OBJ_ATTR_v1)
 extern obj_attr_tag_t obj_attr_v1_process_attribute (obj_attr_vendor_t);
+#endif /* (TC_OBJ_ATTR_v1) */
+extern obj_attr_tag_t obj_attr_process_attribute (obj_attr_vendor_t);
+#if (TC_OBJ_ATTR_v2)
+extern void obj_attr_process_subsection (void);
+#endif /* (TC_OBJ_ATTR_v2) */
 
 extern void obj_elf_gnu_attribute (int);
 
index 2ec8caf3de461167ae1707b2f9a23119486ed9af..47bf960d351bac35567692ae19fd3056ad8c8465 100644 (file)
@@ -3006,7 +3006,9 @@ elf_begin (void)
   elf_obj_attr_version (stdoutput)
     = get_elf_backend_data (stdoutput)->default_obj_attr_version;
 
+#if TC_OBJ_ATTR_v1
   oav1_attr_info_init ();
+#endif /* TC_OBJ_ATTR_v1 */
 #endif /* TC_OBJ_ATTR */
 }
 
@@ -3025,9 +3027,9 @@ elf_end (void)
       free (groups.head);
     }
 
-#ifdef TC_OBJ_ATTR
+#if TC_OBJ_ATTR_v1
   oav1_attr_info_exit ();
-#endif /* TC_OBJ_ATTR */
+#endif /* TC_OBJ_ATTR_v1 */
 }
 
 #ifdef USE_EMULATIONS
index 9bde2250eeb12cb18db46b320c4371af9536b6a2..e3accf0acb5f725929ae0bfd19e8d6042d3678f4 100644 (file)
@@ -2410,6 +2410,20 @@ s_tlsdescldr (int ignored ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 }
+
+/* Parse a .aeabi_subsection directive.  */
+static void
+s_aarch64_aeabi_subsection (int ignored ATTRIBUTE_UNUSED)
+{
+  obj_attr_process_subsection ();
+}
+
+/* Parse a .aeabi_attribute directive.  */
+static void
+s_aarch64_aeabi_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  obj_attr_process_attribute (OBJ_ATTR_PROC);
+}
 #endif /* OBJ_ELF */
 
 #ifdef TE_PE
@@ -2491,6 +2505,8 @@ const pseudo_typeS md_pseudo_table[] = {
   {"tlsdesccall", s_tlsdesccall, 0},
   {"tlsdescldr", s_tlsdescldr, 0},
   {"variant_pcs", s_variant_pcs, 0},
+  {"aeabi_subsection", s_aarch64_aeabi_subsection, 0},
+  {"aeabi_attribute", s_aarch64_aeabi_attribute, 0},
 #endif
 #if defined(OBJ_ELF) || defined(OBJ_COFF)
   {"word", s_aarch64_cons, 4},
index 133604a8e0775a0f8e9346274e1b8e843add7d0a..7126994c652bd1b1f4fa3024c34d8462db51cd65 100644 (file)
@@ -374,4 +374,9 @@ void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int);
 
 #endif /* TE_PE */
 
+#ifdef OBJ_ELF
+/* The target supports Object Attributes v2.  */
+#define TC_OBJ_ATTR_v2 1
+#endif /* OBJ_ELF */
+
 #endif /* TC_AARCH64 */
index 9b96b239a2743192431a4119c2ff57e46faac53e..0381469d4592c755b293b6330c9508a9f7436a58 100644 (file)
@@ -271,7 +271,7 @@ struct arc_relax_type
 extern void arc_md_end (void);
 #define md_end arc_md_end
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_ARC */
index 7eae415cebe6bf7fffc70013696455e1c3950058..7fb13a121f63e76ddf4396daa607ab632fa897b3 100644 (file)
@@ -380,8 +380,8 @@ extern bool arm_tc_equal_in_insn (int, char *);
 int arm_is_largest_exponent_ok (int precision);
 
 #ifdef OBJ_ELF
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
-#endif
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
+#endif /* OBJ_ELF */
 
 #endif /* TC_ARM */
index fc4c86efc6028a09ed7e1dcfc1ac73652626ecbe..c35926053f5006714475f53cee4daac857caeab3 100644 (file)
@@ -106,7 +106,7 @@ extern long csky_relax_frag (segT, fragS *, long);
 const char * elf32_csky_target_format (void);
 #endif
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_CSKY */
index 50f49c225b1c0766952376ab3bb902c106f97a89..2316534932e729763529ab136f9613cef8d84caf 100644 (file)
@@ -149,7 +149,7 @@ struct reloc_info
 #define md_finish loongarch_md_finish
 extern void loongarch_md_finish (void);
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif
index 1836091a5d2352783eedb14c2e7462b4f081ba6c..65ed76edf81dcdb7709f84f24ac8ad6560d47147 100644 (file)
@@ -155,7 +155,7 @@ struct m68k_tc_sy
 
 #define TC_SYMFIELD_TYPE struct m68k_tc_sy
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_M68K_H */
index 2e4fa4a22576a00adfe562ca4f5d64a4c7d7501f..9e6682934162593ed1e1e79b9d882fcc00e318d5 100644 (file)
@@ -217,7 +217,7 @@ extern bfd_reloc_code_real_type mips_cfi_reloc_for_encoding (int encoding);
 #define CONVERT_SYMBOLIC_ATTRIBUTE(name) mips_convert_symbolic_attribute (name)
 extern int mips_convert_symbolic_attribute (const char *);
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_MIPS */
index a5ba9d6b271ad6f4b7e3e94da5f2357182f11612..fe202abc71352cf91d0953aa767b210cf000deba 100644 (file)
@@ -174,7 +174,7 @@ extern bool msp430_allow_local_subtract (expressionS *, expressionS *, segT);
 
 #define DWARF2_ADDR_SIZE(bfd) 4
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_MSP430 */
index a1de28fb47d51a923a264d9cb4fc8a249c43cb37..fd1ea4102131894bdfd7cf526400b86a29489194 100644 (file)
@@ -370,8 +370,8 @@ extern int ppc_dwarf2_line_min_insn_length;
 #define EH_FRAME_ALIGNMENT             2
 
 #ifdef OBJ_ELF
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
-#endif
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
+#endif /* OBJ_ELF */
 
 #endif /* TC_PPC */
index 691139a4c3c2a271005922b1930e78dc1a77a70a..d695db8347759f1a9beefb0912ccd54b9039d750 100644 (file)
@@ -187,7 +187,7 @@ extern void riscv_adjust_symtab (void);
 #define OBJ_COPY_SYMBOL_ATTRIBUTES(DEST, SRC)  \
   elf_copy_symbol_size (DEST, SRC)
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_RISCV */
index 54f51237e8ac076303d595cc78ed5e6ab88bff51..a9eb618ffe6f5e059e8b2630c1e63d8d1d503a67 100644 (file)
@@ -134,7 +134,7 @@ unsigned char s390_sframe_get_abi_arch (void);
 /* Whether SFrame FDE of type SFRAME_FDE_TYPE_FLEX be generated.  */
 #define sframe_support_flex_fde_p() true
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_S390 */
index 5049cb786a92b9a3d69131967b2e15a6558c046f..2cede938b672df7204c5e050691f41c257d46091 100644 (file)
@@ -178,7 +178,7 @@ extern int sparc_cie_data_alignment;
    this, BFD_RELOC_32_PCREL will be emitted directly instead.  */
 #define CFI_DIFF_EXPR_OK 0
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_SPARC */
index c8606723bf2612fe01b2bcf15b49aafeeb78e84c..b106f42a7244f77e728da975ddb92a144341b98a 100644 (file)
@@ -228,7 +228,7 @@ void tic6x_cfi_endproc (struct fde_entry *fde);
 
 #define tc_cfi_section_name ".c6xabi.exidx"
 
-/* The target supports Object Attributes.  */
-#define TC_OBJ_ATTR 1
+/* The target supports Object Attributes v1.  */
+#define TC_OBJ_ATTR_v1 1
 
 #endif /* TC_TIC6X */
index 2bcb85bb6661d708e0540713f695203e2c807d4c..121d009c896a50d34736643df85dd25be1515b1a 100755 (executable)
@@ -12701,8 +12701,8 @@ _ACEOF
 
     # Does the target support Object Attributes ?
     case ${cpu_type} in
-      arc* | arm* | csky | loongarch | m68k | mips* | msp430 | ppc* \
-      | riscv* | s390* | sparc* | tic6x)
+      aarch64* | arc* | arm* | csky | loongarch | m68k | mips* | msp430 \
+      | ppc* | riscv* | s390* | sparc* | tic6x)
        for f in config/obj-elf-attr.o; do
          case " $extra_objects " in
            *" $f "*) ;;
index 613f85016c56106d6ebcc8bea1c1265311494f77..7fb573f8b219f62b8fb8ed8dc55e6c2bd09a4f28 100644 (file)
@@ -434,8 +434,8 @@ changequote([,])dnl
 
     # Does the target support Object Attributes ?
     case ${cpu_type} in
-      arc* | arm* | csky | loongarch | m68k | mips* | msp430 | ppc* \
-      | riscv* | s390* | sparc* | tic6x)
+      aarch64* | arc* | arm* | csky | loongarch | m68k | mips* | msp430 \
+      | ppc* | riscv* | s390* | sparc* | tic6x)
        for f in config/obj-elf-attr.o; do
          case " $extra_objects " in
            *" $f "*) ;;
index 46e0485ae299f90eacbed7f70c89cc44fc6e9b1d..fd5e6870d88fa2a66b50bbf5dc542f8a87ecc903 100644 (file)
@@ -520,6 +520,39 @@ The AArch64 architecture uses @sc{ieee} floating-point numbers.
 
 @c AAAAAAAAAAAAAAAAAAAAAAAAA
 
+@cindex @code{.aeabi_subsection} directive, AArch64
+@item .aeabi_subsection @var{name}, @var{comprehension}, @var{encoding}
+Create or switch the current object attributes subsection to @var{name}.  Valid
+values for @var{name} are following the pattern @code{[a-zA-Z0-9_]+}.
+
+The subsection property @var{comprehension} determines how a program processing
+the attributes handles attributes that it does not recognize (perhaps because
+the object file was generated by a different version of the toolchain).  A
+subsection that is marked @code{optional} can be skipped if it is not
+understood.  A subsection marked @code{required} implies that information
+conveyed by the attribute is required for correct processing of the object file;
+a fatal diagnostic must be generated if a tool does not recognize either the tag
+or the value associated with it.
+
+@var{encoding} specifies the expected encoding of the attributes recorded in the
+subsection.  Currently supported values are @code{ULEB128}/@code{uleb128} and
+@code{NTBS}/@code{ntbs} (null-terminated byte string).
+
+On the first declaration of a subsection, both @var{comprehension} and
+@var{encoding} are mandatory parameters. However, on subsequent declarations,
+none of the parameters is required. If they are still specified, their values
+will be matched against the ones from the first declaration. Any mismatch will
+be reported as an error.
+
+@cindex @code{.aeabi_attribute} @var{tag}, @var{value}
+@item .aeabi_attribute @var{tag}, @var{value}
+Create an attribute with the pair @var{tag}, @var{value} in the current
+subsection.  @var{tag} can either be an integer value, or a known named key.
+@var{value} can either be an integer or a string.
+
+The complete list of subsections and tags supported on AArch64 is documented
+in @cite{Build Attributes for the Arm 64-bit Architecture (AArch64)}.
+
 @cindex @code{.arch} directive, AArch64
 @item .arch @var{name}
 Select the target architecture.  Valid values for @var{name} are the same as
index 1ef44f944615ea1034ce400863e3ad27649fc4e5..256116cd342d046221c8d488bcc5d98b19600000 100644 (file)
 #define STO_AARCH64_VARIANT_PCS        0x80  /* Symbol may follow different call
                                         convention from the base PCS.  */
 
+/* Tags used in aeabi_feature_and_bits subsection.
+   See document 'Build Attributes for the Arm 64-bit Architecture (AArch64)
+   7.4 aeabi_feature_and_bits subsection'.  */
+typedef enum Tag_Feature_Set {
+  Tag_Feature_BTI = 0,
+  Tag_Feature_PAC = 1,
+  Tag_Feature_GCS = 2,
+} Tag_Feature_Set;
+
+/* Tags used in aeabi_pauthabi subsection.
+   See document 'Build Attributes for the Arm 64-bit Architecture (AArch64)
+   7.5 Pointer Authentication Signing Schema.  */
+typedef enum Tag_PAuth_Info {
+  Tag_PAuth_Platform = 1,
+  Tag_PAuth_Schema = 2,
+} Tag_PAuth_Info;
+
 /* Relocation types.  */
 
 START_RELOC_NUMBERS (elf_aarch64_reloc_type)