[PATCH v5 05/20] gas: implement parsing of object attributes v2
Richard Earnshaw (lists)
Richard.Earnshaw@arm.com
Wed Jul 9 13:55:08 GMT 2025
On 07/07/2025 17:49, Matthieu Longo wrote:
> From: Richard Ball <richard.ball@arm.com>
>
> 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'
> - .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.
> - 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.
>
> 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>
> ---
> bfd/elf-attrs.c | 132 +++++-
> bfd/elf-attrs.h | 71 +++
> bfd/elf-bfd.h | 7 +
> bfd/elfnn-aarch64.c | 6 +
> bfd/elfxx-aarch64.c | 73 +++
> bfd/elfxx-aarch64.h | 2 +
> bfd/elfxx-target.h | 8 +
> gas/config/obj-elf-attr.c | 941 ++++++++++++++++++++++++++++++++++++++
> gas/config/obj-elf-attr.h | 37 +-
> gas/config/obj-elf.c | 17 +-
> gas/config/tc-aarch64.c | 16 +
> gas/doc/c-aarch64.texi | 23 +
> include/elf/aarch64.h | 13 +
> 13 files changed, 1339 insertions(+), 7 deletions(-)
>
> diff --git a/bfd/elf-attrs.c b/bfd/elf-attrs.c
> index 1ccbcc40d4d..acf93a3cee5 100644
> --- a/bfd/elf-attrs.c
> +++ b/bfd/elf-attrs.c
> @@ -255,8 +255,138 @@ 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, 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. */
> +static const obj_attr_info_t known_tags_gnu_testing [] =
> +{
> + {
> + .tag = {"GNUTestTag_0", .value = {
> + .val.u32 = 0,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u64 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> + {
> + .tag = {"GNUTestTag_1", .value = {
> + .val.u32 = 1,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u64 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> +};
> +
> +/* List of known GNU subsections.
> + Note: this list needs to be sorted. */
> +static known_subsection_v2 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_tags = known_tags_gnu_testing,
> + .optional = true,
> + .encoding = OA_ENC_ULEB128,
> + .len = sizeof (known_tags_gnu_testing) / sizeof (obj_attr_info_t),
> + },
> + /* Note for the future: GNU subsections can be added here below. */
> +};
> +
> +/* Return True if the given subsection name is part of the reserved "gnu-testing"
> + namespace. */
> +static bool
> +gnu_testing_namespace (const char *subsec_name)
> +{
> + return strncmp ("gnu-testing", subsec_name, 11) == 0;
What's the reason for using strncmp here rather than strcmp?
> +}
> +
> +/* 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 *
> +identify_subsection (const struct elf_backend_data *be,
> + const char *name)
> +{
> + /* Check known backend subsections. */
> + const known_subsection_v2 *known_subsections = be->obj_attr_v2_known_subsections;
> + const size_t known_subsections_size = be->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 *subsec, obj_attr_tag_t tag)
> +{
> + for (unsigned i = 0; i < subsec->len; ++i)
> + {
> + const obj_attr_info_t *known_tag = &subsec->known_tags[i];
> + if (known_tag->tag.value.val.u32 == tag)
> + return known_tag;
> + else if (known_tag->tag.value.val.u32 > tag)
> + break;
> + }
> + return NULL;
> +}
> +
> +/* Return the attribute information associated to the pair SUBSEC, TAG if it
> + exists, NULL otherwise. */
> +const obj_attr_info_t *
> +known_obj_attr_v2_find_by_tag (const struct elf_backend_data *be,
> + const char *subsec_name,
> + obj_attr_tag_t tag)
> +{
> + const known_subsection_v2 *subsec_info =
> + identify_subsection (be, subsec_name);
> + if (subsec_info != NULL)
> + {
> + const obj_attr_info_t *tag_info = identify_tag (subsec_info, tag);
> + return tag_info;
> + }
> + return NULL;
> +}
> +
> +/* To-string function for the pair <SUBSEC, TAG>. Returns the identifier
> + associated to TAG if it is found, NULL otherwise. */
> +const char *
> +obj_attr_v2_tag_to_string (const struct elf_backend_data *be,
> + const char *subsec_name,
> + obj_attr_tag_t tag)
> +{
> + const obj_attr_info_t *tag_info =
> + known_obj_attr_v2_find_by_tag (be, subsec_name, tag);
> + if (tag_info != NULL)
> + return tag_info->tag.identifier;
> + return NULL;
> +}
> +
> /* Allocate/find an object attribute. */
> -static obj_attribute *
> +obj_attribute *
> elf_new_obj_attr (bfd *abfd, obj_attr_vendor_t vendor, obj_attr_tag_t tag)
> {
> obj_attribute *attr;
> diff --git a/bfd/elf-attrs.h b/bfd/elf-attrs.h
> index 22be10d2bf5..9105dcaaf72 100644
> --- a/bfd/elf-attrs.h
> +++ b/bfd/elf-attrs.h
> @@ -116,3 +116,74 @@ typedef struct obj_attr_subsection_list
> /* The size of the list. */
> uint32_t size;
> } obj_attr_subsection_list;
> +
> +/* Basic implementation of a variant for the possible types associated to an
> + object attribute. */
> +struct gas_variant_t;
> +
> +typedef struct {
> + size_t len;
> + struct gas_variant_t *elts;
> +} gas_variant_list;
> +
> +typedef union {
> + const char *string;
> + uint8_t u8;
> + uint32_t u32;
> + uint64_t u64;
> + int64_t i64;
> + bool b;
> + gas_variant_list list;
> +} gas_variant_value;
> +
> +typedef enum {
> + VALUE_UNDEFINED = 0,
> + VALUE_U8,
> + VALUE_U32,
> + VALUE_U64,
> + VALUE_I64,
> + VALUE_UNSIGNED_INTEGER = VALUE_U64,
> + VALUE_SIGNED_INTEGER = VALUE_I64,
> + VALUE_BOOL,
> + VALUE_STRING,
> + VALUE_LIST,
> +} gas_variant_type_info;
> +
> +typedef struct gas_variant_t {
> + gas_variant_value val;
> + gas_variant_type_info vtype;
> +} gas_variant_t;
> +
> +typedef struct {
> + const char *const identifier;
> + const gas_variant_t value;
> +} gas_symbol_t;
> +
> +/* Attribute information. */
> +typedef struct {
> + const gas_symbol_t tag;
> + const gas_variant_t default_value;
> + const obj_attr_encoding_v2 encoding;
> +} obj_attr_info_t;
> +
> +typedef struct
> +{
> + const char *const subsec_name;
> + const obj_attr_info_t *known_tags;
> + const bool optional;
> + const obj_attr_encoding_v2 encoding;
> + const size_t len;
> +} known_subsection_v2;
> +
> +struct elf_backend_data;
> +
> +extern const known_subsection_v2 *
> +identify_subsection (const struct elf_backend_data *, const char*);
> +
> +extern const obj_attr_info_t *
> +known_obj_attr_v2_find_by_tag (const struct elf_backend_data *,
> + const char*, obj_attr_tag_t);
Indentation should be to the level of the opening parenthesis.
> +
> +extern const char *
> +obj_attr_v2_tag_to_string (const struct elf_backend_data *, const char*,
> + obj_attr_tag_t);
and here.
> diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
> index 2e617964300..c17540c819d 100644
> --- a/bfd/elf-bfd.h
> +++ b/bfd/elf-bfd.h
> @@ -1657,6 +1657,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 *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
> @@ -3093,6 +3099,7 @@ extern obj_attr_version_t _bfd_obj_attrs_version_dec (uint8_t);
> extern uint8_t _bfd_obj_attrs_version_enc (obj_attr_version_t);
> extern bfd_vma bfd_elf_obj_attr_size (bfd *);
> extern void bfd_elf_set_obj_attr_contents (bfd *, bfd_byte *, bfd_vma);
> +extern obj_attribute * elf_new_obj_attr (bfd *, obj_attr_vendor_t, obj_attr_tag_t);
> extern int bfd_elf_get_obj_attr_int (bfd *, obj_attr_vendor_t, obj_attr_tag_t);
> extern obj_attribute *bfd_elf_add_obj_attr_int
> (bfd *, obj_attr_vendor_t, obj_attr_tag_t, unsigned int);
> diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
> index 96ca6e13338..4ef18a052cd 100644
> --- a/bfd/elfnn-aarch64.c
> +++ b/bfd/elfnn-aarch64.c
> @@ -10782,6 +10782,12 @@ 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 2
>
> #include "elfNN-target.h"
>
> diff --git a/bfd/elfxx-aarch64.c b/bfd/elfxx-aarch64.c
> index b34ee13a299..2d738bbe47a 100644
> --- a/bfd/elfxx-aarch64.c
> +++ b/bfd/elfxx-aarch64.c
> @@ -21,6 +21,7 @@
> #include "sysdep.h"
> #include "bfd.h"
> #include "elf-bfd.h"
> +#include "elf/aarch64.h"
> #include "elfxx-aarch64.h"
> #include "libbfd.h"
> #include <stdarg.h>
> @@ -887,6 +888,78 @@ _bfd_aarch64_obj_attrs_version_enc (obj_attr_version_t version)
> abort ();
> }
>
> +/* Note: this array has to be sorted. */
> +static const obj_attr_info_t known_tags_aeabi_feature_and_bits [] =
> +{
> + {
> + .tag = {"Tag_Feature_BTI", .value = {
> + .val.u32 = Tag_Feature_BTI,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u32 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> + {
> + .tag = {"Tag_Feature_PAC", .value = {
> + .val.u32 = Tag_Feature_PAC,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u32 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> + {
> + .tag = {"Tag_Feature_GCS", .value = {
> + .val.u32 = Tag_Feature_GCS,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u32 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> +};
> +
> +/* This is a required subsection to use PAuthABI (which is currently
> + unsupported by GCC). A value of 0 for any the tags below means that
> + the user did not permit this entity to use the PAuthABI.
> + Note: this array has to be sorted. */
> +static const obj_attr_info_t known_tags_aeabi_pauthabi [] =
> +{
> + {
> + .tag = {"Tag_PAuth_Platform", .value = {
> + .val.u32 = Tag_PAuth_Platform,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u32 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> + {
> + .tag = {"Tag_PAuth_Schema", .value = {
> + .val.u32 = Tag_PAuth_Schema,
> + .vtype = VALUE_U32
> + }},
> + .default_value = {.val.u32 = 0, .vtype = VALUE_UNSIGNED_INTEGER},
> + .encoding = OA_ENC_ULEB128,
> + },
> +};
> +
> +/* Note: this array is exported by the backend, and needs to be sorted. */
> +const known_subsection_v2 aarch64_obj_attr_v2_known_subsections[] =
> +{
> + {
> + .subsec_name = "aeabi_feature_and_bits",
> + .known_tags = known_tags_aeabi_feature_and_bits,
> + .optional = true,
> + .encoding = OA_ENC_ULEB128,
> + .len = sizeof (known_tags_aeabi_feature_and_bits) / sizeof (obj_attr_info_t),
> + },
> + {
> + .subsec_name = "aeabi_pauthabi",
> + .known_tags = known_tags_aeabi_pauthabi,
> + .optional = false,
> + .encoding = OA_ENC_ULEB128,
> + .len = sizeof (known_tags_aeabi_pauthabi) / sizeof (obj_attr_info_t),
> + },
> +};
> +
> /* 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. */
> diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
> index a98eef1e66b..92b60439f6d 100644
> --- a/bfd/elfxx-aarch64.h
> +++ b/bfd/elfxx-aarch64.h
> @@ -215,6 +215,8 @@ _bfd_aarch64_obj_attrs_version_dec (uint8_t);
> extern uint8_t
> _bfd_aarch64_obj_attrs_version_enc (obj_attr_version_t);
>
> +extern const known_subsection_v2 aarch64_obj_attr_v2_known_subsections[];
> +
> extern bfd *
> _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *);
>
> diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
> index 738fd512169..9277edf8690 100644
> --- a/bfd/elfxx-target.h
> +++ b/bfd/elfxx-target.h
> @@ -565,6 +565,12 @@
> #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
> @@ -946,6 +952,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,
> diff --git a/gas/config/obj-elf-attr.c b/gas/config/obj-elf-attr.c
> index 4bfbf973ea5..de5fcbfc191 100644
> --- a/gas/config/obj-elf-attr.c
> +++ b/gas/config/obj-elf-attr.c
> @@ -21,6 +21,7 @@
> #include "obj-elf-attr.h"
>
> #ifdef TC_OBJ_ATTR
> +#include "obstack.h"
> #include "safe-ctype.h"
>
> #define skip_whitespace(str) do { if (is_whitespace (*(str))) ++(str); } while (0)
> @@ -38,6 +39,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. */
> @@ -122,6 +125,943 @@ 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;
> +
> +typedef gas_variant_t arg_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)
> +{
> + skip_whitespace (input_line_pointer);
> +
> + if (*input_line_pointer != '"')
> + {
> + as_bad ("missing '\"', expected a string literal");
> + return false;
> + }
> +
> + int len;
> + char *obstack_buf = demand_copy_C_string (&len);
> + if (obstack_buf != NULL)
> + {
> + arg_out->val.string = xstrdup (obstack_buf);
> + obstack_free (¬es, 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. */
> +static bool
> +extract_integer_literal (arg_t *arg_out,
> + bool signedness_unsigned)
> +{
> + expressionS exp;
> + expression (&exp);
> + if (exp.X_op != O_constant)
> + {
> + as_bad (_("invalid value, expected an integer literal"));
> + goto bad;
> + }
> +
> + int64_t val = exp.X_add_number;
> + if (val < 0 && signedness_unsigned)
> + {
> + as_bad (_("invalid negative value %ld, expected an unsigned integer"),
> + val);
> + goto bad;
> + }
> + if (signedness_unsigned)
> + {
> + 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;
> +
> +bad:
> + ignore_rest_of_line ();
> + return false;
> +}
> +
> +/* 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 value '%c', expected an identifier"),
> + *input_line_pointer);
> + ignore_rest_of_line ();
> + return false;
> + }
> +
> + char *val = xmemdup0 (s, i);
> + arg_out->vtype = VALUE_STRING;
> + arg_out->val.string = val;
> + return true;
> +}
> +
> +#if (TC_OBJ_ATTR_v2)
> +/* Resolve the identifier if it matches the given symbol. */
> +static bool
> +resolve_if_matching (const char *identifier,
> + const gas_symbol_t *symbol,
> + arg_t *val_out)
> +{
> + if (strcmp (symbol->identifier, identifier) != 0)
> + return false;
> +
> + /* Free the identifier since we found the value. */
> + free ((void *) val_out->val.string);
> +
> + switch (symbol->value.vtype)
> + {
> + case VALUE_BOOL:
> + val_out->val.u64 = symbol->value.val.b;
> + val_out->vtype = VALUE_UNSIGNED_INTEGER;
> + break;
> + case VALUE_U8:
> + val_out->val.u64 = symbol->value.val.u8;
> + val_out->vtype = VALUE_UNSIGNED_INTEGER;
> + break;
> + case VALUE_U32:
> + val_out->val.u64 = symbol->value.val.u32;
> + val_out->vtype = VALUE_UNSIGNED_INTEGER;
> + break;
> + case VALUE_U64:
> + val_out->val.u64 = symbol->value.val.u64;
> + val_out->vtype = VALUE_UNSIGNED_INTEGER;
> + break;
> + case VALUE_I64:
> + val_out->val.i64 = symbol->value.val.i64;
> + val_out->vtype = VALUE_UNSIGNED_INTEGER;
> + break;
> + case VALUE_STRING:
> + val_out->val.string = strdup (symbol->value.val.string);
> + val_out->vtype = VALUE_STRING;
> + break;
> + default:
> + abort ();
> + }
> +
> + return true;
> +}
> +#endif /* TC_OBJ_ATTR_v2 */
> +
> +#if (TC_OBJ_ATTR_v1)
> +/* Look up attribute keys defined in the backend (object attribute v1). */
> +static bool
> +obj_attr_v1_lookup_known_attr_key_symbol (const char *identifier,
> + arg_token_t token_type,
> + arg_t *val_out)
> +{
> +#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
> +#define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
> + (void) identifier;
> +#endif
> +
> + gas_assert (token_type & UNSIGNED_INTEGER);
> +
> + 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 keys defined in the backend (object attribute v2). */
> +static bool
> +obj_attr_v2_lookup_known_attr_key_symbol (const char *identifier,
> + arg_token_t token_type,
> + arg_t *val_out)
> +{
> + obj_attr_subsection_v2 *subsec = elf_obj_attr_subsections (stdoutput).last_;
> + /* If there is no current subsection, this function was called wrongly before
> + setting one (usually via the subsection directive). */
> + 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 *known_subsec =
> + identify_subsection (be, subsec->name);
> + if (known_subsec != NULL)
> + {
> + for (size_t i = 0; i < known_subsec->len && ! resolved; ++i)
> + resolved = resolve_if_matching (identifier,
> + &known_subsec->known_tags[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;
> +
> + /* The identifier should match the value in val_out. */
> + gas_assert (val_out->val.string == identifier);
> +
> + arg_token_t high_ttype = (token_type & HT_MASK);
> +
> +#if (TC_OBJ_ATTR_v2)
> + static const gas_symbol_t known_identifiers_subsection_optional[] = {
> + { "optional", .value = { .val.b = true, .vtype = VALUE_BOOL } },
> + { "required", .value = { .val.b = false, .vtype = VALUE_BOOL } },
> + };
> +
> + static const gas_symbol_t known_identifiers_subsection_encoding[] = {
> + { "ULEB128", .value = {
> + .val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_ULEB128),
> + .vtype = VALUE_U8
> + }
> + },
> + { "uleb128", .value = {
> + .val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_ULEB128),
> + .vtype = VALUE_U8
> + }
> + },
> + { "NTBS", .value = {
> + .val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_NTBS),
> + .vtype = VALUE_U8
> + }
> + },
> + { "ntbs", .value = {
> + .val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_NTBS),
> + .vtype = 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 gas_symbol_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);
> +#if (TC_OBJ_ATTR_v1)
> + if (version == OBJ_ATTR_V1)
> + resolved = obj_attr_v1_lookup_known_attr_key_symbol (identifier,
> + token_type, val_out);
> +#endif /* TC_OBJ_ATTR_v1 */
> +#if (TC_OBJ_ATTR_v2)
> + #if (TC_OBJ_ATTR_v1)
> + else
> + #endif /* TC_OBJ_ATTR_v1 */
> + if (version == OBJ_ATTR_V2)
> + resolved = obj_attr_v2_lookup_known_attr_key_symbol (identifier,
> + token_type, val_out);
> +#endif /* TC_OBJ_ATTR_v2 */
> + else
> + 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);
> + /* The identifier should match the value in val_out. */
> + gas_assert (val_out->val.string == identifier);
> +
> + symbolS *symbolP = symbol_find (identifier);
> + if (symbolP == NULL)
> + return false;
> +
> + if (! S_IS_DEFINED (symbolP))
> + return false;
> +
> + valueT val = S_GET_VALUE (symbolP);
> +
> + /* Free the identifier since we found the value. */
> + free ((void *) val_out->val.string);
> +
> + val_out->val.u64 = val;
> + val_out->vtype = VALUE_UNSIGNED_INTEGER;
> +
> + return true;
> +}
> +
> +/* Return true if the next characters are suspected to represent an integer
> + literal. */
> +static bool
> +look_like_integer_literal (char const* const cursor)
> +{
> + return ISDIGIT (*cursor)
> + || ((*cursor == '+' || *cursor == '-') && ISDIGIT (*(cursor + 1)));
> +}
> +
> +/* 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 (*match_identifier) (char c),
> + bool resolve_identifier,
> + arg_t *arg_out)
> +{
> + const arg_token_t low_ttype = (expected_ttype & LT_MASK);
> +
> + /* Note: symbol look-up for string literals is not available. */
> + if (((low_ttype & STRING) && *input_line_pointer == '"')
> + || !(low_ttype & ~STRING))
> + return extract_string_literal (arg_out);
> +
> + if (((low_ttype & (UNSIGNED_INTEGER | SIGNED_INTEGER))
> + && look_like_integer_literal (input_line_pointer))
> + || !(low_ttype & ~(UNSIGNED_INTEGER | SIGNED_INTEGER)))
> + return extract_integer_literal (arg_out, (low_ttype & UNSIGNED_INTEGER));
> +
> + /* Try to extract an identifier. */
> + if (! extract_identifier (match_identifier, arg_out))
> + return false;
> + gas_assert (arg_out->vtype == VALUE_STRING);
> +
> + /* In some cases, we don't want to resolve the identifier because it is the
> + actual value. */
> + if (! resolve_identifier)
> + return 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 (arg_out->val.string, expected_ttype, arg_out))
> + return true;
> +
> + /* 2. using the symbol table for this compilation unit.
> + Note: this is the last attempt before failure. */
> + if (lookup_symbol_table (arg_out->val.string, low_ttype, arg_out))
> + return true;
> +
> + as_bad ("unknown identifier '%s'", arg_out->val.string);
> + free ((void *) arg_out->val.string);
> + arg_out->val.string = NULL;
> + arg_out->vtype = VALUE_UNDEFINED;
> + return false;
> +}
> +
> +/* Trim white spaces before a parameter.
> + Error if it meets a parameter separator before a parameter. */
> +static bool
> +trim_whitespaces_before_param (int n)
> +{
> + bool error = false;
> + do
> + {
> + skip_whitespace (input_line_pointer);
> + if (*input_line_pointer == ',')
> + {
> + error = true;
> + if (n < 0)
> + as_bad (_("unexpected comma before value"));
> + else
> + as_bad (_("unexpected comma before parameter %d"), n + 1);
> + skip_past_comma (&input_line_pointer);
> + }
> + }
> + while (is_whitespace (*input_line_pointer));
> + return !error;
> +}
> +
> +/* Skip white spaces + parameter separator after a parameter.
> + Error if it does not meet a parameter separator after a parameter. */
> +static bool
> +skip_whitespaces_past_comma (int n)
> +{
> + skip_whitespace (input_line_pointer);
> + if (! skip_past_comma (&input_line_pointer))
> + {
> + if (n < 0)
> + as_bad (_("unexpected comma after value"));
> + else
> + as_bad (_("missing comma after parameter %d"), n + 1);
> + 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 (*match_identifier) (char c),
> + bool resolve_identifier,
> + arg_t *arg_out)
> +{
> + if ((expected_ttype & LIST) == 0)
> + return obj_attr_parse_arg (expected_ttype, match_identifier,
> + resolve_identifier, arg_out);
> +
> + static const size_t LIST_MAX_SIZE = 2;
> + gas_variant_t *arg_list = xcalloc (LIST_MAX_SIZE, sizeof (gas_variant_t));
> +
> + /* We don't want to support recursive lists. */
> + expected_ttype &= ~LIST;
> +
> + size_t n = 0;
> + do {
> + if (! trim_whitespaces_before_param (-1))
> + goto bad;
> +
> + if (! obj_attr_parse_arg (expected_ttype, match_identifier,
> + resolve_identifier, &arg_list[n]))
> + goto bad;
> +
> + ++n;
> + skip_whitespace (input_line_pointer);
> + if (is_end_of_stmt (*input_line_pointer))
> + break;
> +
> + if (! skip_whitespaces_past_comma (-1))
> + goto bad;
> +
> + if (n >= LIST_MAX_SIZE)
> + {
> + as_bad ("too many arguments for a list (max: %lu)", 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 == 0 || value == 1;
> +}
> +
> +#define is_valid_optional 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;
> +}
> +
> +static bool
> +match_subsection_identifier (char c)
> +{
> + return ISALNUM (c) || c == '_' || c == '-';
> +}
> +#endif /* TC_OBJ_ATTR_v2 */
> +
> +static bool
> +match_symbol (char c)
> +{
> + return ISALNUM (c) || c == '_';
> +}
> +
> +#define match_tag_identifier match_symbol
> +
> +#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 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 (
> +#if (TC_OBJ_ATTR_v1 && TC_OBJ_ATTR_v2)
> + obj_attr_vendor_t vendor,
> + const obj_attr_subsection_v2 *subsec,
> +#elif (TC_OBJ_ATTR_v1)
> + obj_attr_vendor_t vendor,
> + const obj_attr_subsection_v2 *subsec ATTRIBUTE_UNUSED,
> +#else /* TC_OBJ_ATTR_v2 */
> + obj_attr_vendor_t vendor ATTRIBUTE_UNUSED,
> + const obj_attr_subsection_v2 *subsec,
> +#endif
> + 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_whitespaces_before_param (n))
> + 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.u32);
> +#elif (TC_OBJ_ATTR_v1)
> + = obj_attr_v1_get_arg_type (stdoutput, vendor,
> + args_out[n-1].val.u32);
> +#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,
> + match_tag_identifier, true,
> + arg_out))
> + {
> + if (high_ttype == ATTRIBUTE_KEY)
> + as_bad (_("could not parse attribute tag"));
> + else
> + as_bad (_("could not parse attribute value"));
> + goto bad;
> + }
> +
> + if (n + 1 < nargs && !skip_whitespaces_past_comma (n))
> + goto bad;
> + }
> +
> + va_end (args);
> + demand_empty_rest_of_line ();
> +
> + return args_out;
> +
> +bad:
> + args_list_free (args_out, nargs);
> + va_end (args);
> + ignore_rest_of_line ();
> + 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 = elf_new_obj_attr (abfd, vendor, tag);
> + if (attr != NULL)
> + {
> + int tag_type = _bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
> + if (parsed_arg->vtype == VALUE_LIST)
> + {
> + gas_assert (parsed_arg->val.list.len == 2
> + && parsed_arg->val.list.elts[0].vtype == VALUE_UNSIGNED_INTEGER
> + && parsed_arg->val.list.elts[1].vtype == VALUE_STRING);
> + attr->type = tag_type;
> + attr->i = parsed_arg->val.list.elts[0].val.u64;
> + attr->s = (char *) parsed_arg->val.list.elts[1].val.string;
> + parsed_arg->val.list.elts[1].val.string = NULL;
> + }
> + else if (parsed_arg->vtype == VALUE_STRING)
> + {
> + attr->type = tag_type;
> + attr->s = (char *) parsed_arg->val.string;
> + parsed_arg->val.string = NULL;
> + }
> + else
> + {
> + attr->type = tag_type;
> + 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_whitespaces_before_param (n))
> + 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,
> + match_subsection_identifier, false,
> + arg_out))
> + {
> + as_bad (_("expected <subsection_name>, <optional>, <encoding>"));
> + goto bad;
> + }
> + }
> + else if (high_ttype == SUBSECTION_OPTION_1
> + || high_ttype == SUBSECTION_OPTION_2)
> + {
> + if (! obj_attr_parse_arg (expected_ttype,
> + match_symbol, true,
> + arg_out))
> + goto bad;
> + if (high_ttype == SUBSECTION_OPTION_1
> + && ! is_valid_optional (arg_out->val.u64))
> + {
> + as_bad (("invalid value %lu, expected values for <optional> 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 && ! skip_whitespaces_past_comma (n))
> + goto bad;
> + }
> +
> + va_end (args);
> + demand_empty_rest_of_line ();
> +
> + return args_out;
> +
> +bad:
> + args_list_free (args_out, nargs);
> + va_end (args);
> + ignore_rest_of_line ();
> + return NULL;
> +}
> +
> +/* Record an attribute (object attribute v2 only). */
> +static void
> +obj_attr_v2_record (uint64_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_vals;
> + if (arg_val->vtype == VALUE_UNSIGNED_INTEGER)
> + obj_attr_vals.uint_val = arg_val->val.u64;
> + else
> + {
> + /* Move the string. */
> + obj_attr_vals.string_val = arg_val->val.string;
> + arg_val->val.string = NULL;
> + }
> +
> + obj_attr_v2 *obj_attr = _bfd_elf_obj_attr_v2_init (key, obj_attr_vals);
> + 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 *recorded_attr = 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->vals.uint_val != obj_attr->vals.uint_val) ||
> + (arg_val->vtype == VALUE_STRING
> + && strcmp (recorded_attr->vals.string_val, obj_attr->vals.string_val) != 0))
> + as_bad (_("attribute %u cannot be redefined"), recorded_attr->tag);
> + skip_recording = true;
> + }
> +
> + if (skip_recording)
> + {
> + if (arg_val->vtype == VALUE_STRING)
> + free ((void *) obj_attr->vals.string_val);
> + free (obj_attr);
> + return;
> + }
> +
> + LINKED_LIST_APPEND(obj_attr_v2) (
> + elf_obj_attr_subsections (stdoutput).last_, obj_attr);
> +}
> +
> +/* Record a subsection (object attribute v2 only). */
> +static void
> +obj_attr_v2_subsection_record (const char *name,
> + bool optional,
> + obj_attr_encoding_v2 encoding)
> +{
> + obj_attr_subsection_v2 *already_recorded_subsec =
> + obj_attr_subsection_v2_find_by_name
> + (elf_obj_attr_subsections (stdoutput).first_, name, false);
> +
> + if (already_recorded_subsec != NULL)
> + {
> + /* Check for mismatching redefinition of the subsection, i.e. the names
> + match but the properties are different. */
> + if ((already_recorded_subsec->optional != optional)
> + || (already_recorded_subsec->encoding != encoding))
> + {
> + as_bad (_("recalled subsections must have the same parameters"));
> + return;
Even with the source code in front of me, I'm not entirely sure what 'recalled' means in this context. Perhaps a clearer error would be something along the lines of
"Incompatible redeclaration of subsection <name>. Previous declaration had parameters: <list>"
where 'name' and 'list' refer back to the subsection and properties of the subsection we need to match.
> + }
> + /* Move the existing subsection to the last position. */
> + LINKED_LIST_REMOVE(obj_attr_subsection_v2) (
> + &elf_obj_attr_subsections (stdoutput), already_recorded_subsec);
> + LINKED_LIST_APPEND(obj_attr_subsection_v2) (
> + &elf_obj_attr_subsections (stdoutput), already_recorded_subsec);
> + }
> + else
> + {
> + const char *vendor_name =
> + get_elf_backend_data (stdoutput)->obj_attrs_vendor;
> + obj_attr_subsection_scope_v2 scope =
> + (strncmp (name, vendor_name, strlen (vendor_name)) == 0)
> + ? OA_SUBSEC_PUBLIC
> + : OA_SUBSEC_PRIVATE;
> +
> + obj_attr_subsection_v2 *new_subsection =
> + _bfd_elf_obj_attr_subsection_v2_init (name, scope, optional, encoding);
> + LINKED_LIST_APPEND(obj_attr_subsection_v2) (
> + &elf_obj_attr_subsections (stdoutput), new_subsection);
> + }
> +}
> +#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 *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;
> +#if (TC_OBJ_ATTR_v1)
> + if (version == OBJ_ATTR_V1)
> + {
> + oav1_attr_record_seen (vendor, tag);
> + obj_attr_v1_record (stdoutput, vendor, tag, &args[1]);
> + }
> +#endif /* TC_OBJ_ATTR_v1 */
> +#if (TC_OBJ_ATTR_v2)
> + #if (TC_OBJ_ATTR_v1)
> + else
> + #endif /* TC_OBJ_ATTR_v1 */
> + if (version == OBJ_ATTR_V2)
> + obj_attr_v2_record (tag, &args[1]);
> +#endif /* TC_OBJ_ATTR_v2 */
> + else
> + 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 ()
> +{
> + 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;
> +
> + const char *name = NULL;
> + /* move the value to avoid double free. */
> + VALUE_SWAP (name, args[0].val.string);
> +
> + obj_attr_v2_subsection_record (name, args[1].val.u64,
> + obj_attr_encoding_v2_from_u8 (args[2].val.u64));
> +
> + 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. */
>
> @@ -235,5 +1175,6 @@ obj_attr_v1_process_attribute (obj_attr_vendor_t vendor)
> ignore_rest_of_line ();
> return 0;
> }
> +#endif /* TC_OBJ_ATTR_v1 */
>
> #endif /* TC_OBJ_ATTR */
> diff --git a/gas/config/obj-elf-attr.h b/gas/config/obj-elf-attr.h
> index e7631b8b05c..0f95fa44be4 100644
> --- a/gas/config/obj-elf-attr.h
> +++ b/gas/config/obj-elf-attr.h
> @@ -24,9 +24,10 @@
> #include "as.h"
> #include "bfd/elf-bfd.h"
>
> +#if OBJ_ELF
> +
> /* The target supports Object Attributes v1. */
> -#if OBJ_ELF \
> - && (defined (TC_ARC) \
> +#if defined (TC_ARC) \
> || defined (TC_ARM) \
> || defined (TC_CSKY) \
> || defined (TC_M68K) \
> @@ -36,16 +37,44 @@
> || defined (TC_RISCV) \
> || defined (TC_S390) \
> || defined (TC_SPARC) \
> - || defined (TC_TIC6X))
> -#define TC_OBJ_ATTR 1
> + || defined (TC_TIC6X)
> +#define TC_OBJ_ATTR_v1 1
> +#else
> +#define TC_OBJ_ATTR_v1 0
> +#endif
> +
> +/* The target supports Object Attributes v2. */
> +#if defined(TC_AARCH64)
> +#define TC_OBJ_ATTR_v2 1
> +#else
> +#define TC_OBJ_ATTR_v2 0
> +#endif
> +
> +#if (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2)
> + #define TC_OBJ_ATTR 1
> #endif
>
> +#endif /* OBJ_ELF */
> +
> #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) */
> +#if (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2)
> +extern obj_attr_tag_t obj_attr_process_attribute (obj_attr_vendor_t);
> +#endif /* (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2) */
> +#if (TC_OBJ_ATTR_v2)
> +extern void obj_attr_process_subsection (void);
> +#endif /* (TC_OBJ_ATTR_v2) */
>
> #endif /* TC_OBJ_ATTR */
>
> diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
> index be44119dd92..28e7e4efb92 100644
> --- a/gas/config/obj-elf.c
> +++ b/gas/config/obj-elf.c
> @@ -73,7 +73,9 @@ static void obj_elf_symver (int);
> static void obj_elf_subsection (int);
> static void obj_elf_popsection (int);
> #ifdef TC_OBJ_ATTR
> +#if (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2)
> static void obj_elf_gnu_attribute (int);
> +#endif /* (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2) */
> #endif /* TC_OBJ_ATTR */
> static void obj_elf_tls_common (int);
> static void obj_elf_lcomm (int);
> @@ -120,7 +122,9 @@ static const pseudo_typeS elf_pseudo_table[] =
>
> /* A GNU extension for object attributes. */
> #ifdef TC_OBJ_ATTR
> +#if (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2)
> {"gnu_attribute", obj_elf_gnu_attribute, 0},
> +#endif /* (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2) */
> #endif /* TC_OBJ_ATTR */
>
> /* These are used for dwarf2. */
> @@ -2050,13 +2054,20 @@ obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
>
> #ifdef TC_OBJ_ATTR
>
> +#if (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2)
> /* Parse a .gnu_attribute directive. */
>
> static void
> obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
> {
> +#if (TC_OBJ_ATTR_v1)
> obj_attr_v1_process_attribute (OBJ_ATTR_GNU);
> +#endif /* TC_OBJ_ATTR_v1 */
> +#if (TC_OBJ_ATTR_v2)
> + obj_attr_process_attribute (OBJ_ATTR_GNU);
> +#endif /* TC_OBJ_ATTR_v2 */
> }
> +#endif /* (TC_OBJ_ATTR_v1 || TC_OBJ_ATTR_v2) */
>
> #endif /* TC_OBJ_ATTR */
>
> @@ -2997,7 +3008,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 */
> }
>
> @@ -3016,9 +3029,9 @@ elf_end (void)
> free (groups.head);
> }
>
> -#ifdef TC_OBJ_ATTR
> +#if defined(TC_OBJ_ATTR) && TC_OBJ_ATTR_v1
> oav1_attr_info_exit ();
> -#endif /* TC_OBJ_ATTR */
> +#endif /* defined(TC_OBJ_ATTR) && TC_OBJ_ATTR_v1 */
> }
>
> #ifdef USE_EMULATIONS
> diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
> index 13649e8f3d5..5019ba20c08 100644
> --- a/gas/config/tc-aarch64.c
> +++ b/gas/config/tc-aarch64.c
> @@ -2401,6 +2401,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
> @@ -2481,6 +2495,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},
> diff --git a/gas/doc/c-aarch64.texi b/gas/doc/c-aarch64.texi
> index d7e9c95111d..d88fcdf2da7 100644
> --- a/gas/doc/c-aarch64.texi
> +++ b/gas/doc/c-aarch64.texi
> @@ -460,6 +460,29 @@ 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_-]+}.
> +
> +@var{comprehension} determines whether the subsection is @code{required} or
> +@code{optional}. An optional subsection can be skipped if it is not known by the
> +customer tool, unknown @code{required} subsection should generate an error and
> +stop the processing.
I think it would be clearer to write something like:
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} and @code{NTBS}
> +(null-terminated byte string).
> +
> +@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 exhaustive 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
> diff --git a/include/elf/aarch64.h b/include/elf/aarch64.h
> index e218e07fa73..c076174a7ac 100644
> --- a/include/elf/aarch64.h
> +++ b/include/elf/aarch64.h
> @@ -57,6 +57,19 @@
> #define STO_AARCH64_VARIANT_PCS 0x80 /* Symbol may follow different call
> convention from the base PCS. */
>
> +/* Tags used in aeabi_feature_and_bits subsection. */
> +typedef enum Tag_Feature_XXX {
> + Tag_Feature_BTI = 0,
> + Tag_Feature_PAC = 1,
> + Tag_Feature_GCS = 2,
> +} Tag_Feature_XXX;
> +
> +/* Tags used in aeabi_pauthabi subsection. */
> +typedef enum Tag_PAuth_XXX {
> + Tag_PAuth_Platform = 1,
> + Tag_PAuth_Schema = 2,
> +} Tag_PAuth_XXX;
> +
> /* Relocation types. */
>
> START_RELOC_NUMBERS (elf_aarch64_reloc_type)
R.
More information about the Binutils
mailing list