@@ -41,6 +41,10 @@
#include "elf/alpha.h"
#endif
+#ifdef TC_AARCH64
+#include "elf/aarch64.h"
+#endif
+
#ifdef TC_MIPS
#include "elf/mips.h"
#endif
@@ -2127,6 +2131,413 @@ obj_elf_seen_attribute (int vendor, unsigned int tag)
return false;
}
+static bool
+extract_string_literal (attribute_arg_value* arg_out)
+{
+ skip_whitespace (input_line_pointer);
+
+ if (*input_line_pointer != '"')
+ {
+ as_bad ("Missing token '\"' for expected string: %c", *input_line_pointer);
+ return false;
+ }
+
+ arg_out->type = STRING_VALUE;
+ int len;
+ arg_out->value.string = demand_copy_C_string (&len);
+ if (len > 0)
+ {
+ arg_out->optional_set = true;
+ return true;
+ }
+
+ arg_out->value.string = NULL;
+ return false;
+}
+
+static bool
+extract_identifier (bool (*char_predicate) (char), attribute_arg_value* arg_out)
+{
+ expressionS exp;
+
+ /* Try to extract a value from the identifier. */
+ if (ISDIGIT (*input_line_pointer))
+ {
+ expression (& exp);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("invalid value, expected an integer literal"));
+ goto bad_integer_literal;
+ }
+ int val = exp.X_add_number;
+ arg_out->optional_set = true;
+ arg_out->type = ULEB128_VALUE;
+ arg_out->value.u64 = val;
+ return true;
+ }
+
+ /* Try to extract a symbol. */
+ const char *s = input_line_pointer;
+ unsigned int i = 0;
+ for (; char_predicate (*input_line_pointer); ++input_line_pointer)
+ i++;
+ if (i == 0)
+ return false;
+ char* val = xmemdup0 (s, i);
+ arg_out->optional_set = true;
+ arg_out->type = STRING_VALUE;
+ arg_out->value.string = val;
+ return true;
+
+bad_integer_literal:
+ ignore_rest_of_line ();
+ return false;
+}
+
+typedef struct obj_attr_v2_known_identifier
+{
+ const char *name;
+ const uint32_t val;
+} obj_attr_v2_known_identifier;
+
+static bool
+obj_attr_v2_lookup_known_symbol (
+ const char *identifier,
+ const obj_attr_v2_known_identifier known_identifiers[],
+ const size_t N_identifiers,
+ uint32_t *val_out)
+{
+ if (identifier == NULL || known_identifiers == NULL || N_identifiers == 0)
+ return false;
+
+ for (size_t i = 0; i < N_identifiers; ++i)
+ if (!strcmp (known_identifiers[i].name, identifier))
+ {
+ *val_out = known_identifiers[i].val;
+ return true;
+ }
+ return false;
+}
+
+static int
+obj_attr_v2_look_symbol_table (const char *name)
+{
+ if (name == NULL)
+ return -1;
+
+ symbolS *symbolP;
+ symbolP = symbol_find (name);
+ if (symbolP == NULL)
+ return -1;
+
+ if (! S_IS_DEFINED (symbolP))
+ return -1;
+
+ valueT val = S_GET_VALUE (symbolP);
+ DEBUG_TRACE ("Resolved symbol '%s' to %lu", name, val);
+ return val;
+}
+
+static bool
+obj_attr_parse_arg (attribute_arg_type arg_type,
+ const obj_attr_v2_known_identifier known_identifiers[],
+ const size_t N_identifiers,
+ bool skip_backend_symbol_attribute_lookup,
+ attribute_arg_value *arg_out)
+{
+ bool match_symbol (char c)
+ {
+ return ISALNUM (c) || c == '_';
+ }
+
+ /* Note: for now, symbol look-up for string literals is not available. */
+ if (arg_type == STRING_VALUE)
+ return extract_string_literal (arg_out);
+
+ /* Try to extract an identifier (symbol, integer literal). */
+ if (! extract_identifier (&match_symbol, arg_out))
+ return false;
+
+ /* The identifier is fully resolved. */
+ if (arg_out->type == ULEB128_VALUE)
+ return true;
+
+ /* The identifier is a symbol, let's try to resolve it using the provided list
+ of known symbols. */
+ gas_assert (arg_out->type == STRING_VALUE);
+ uint32_t resolved_symbol_value;
+ if (obj_attr_v2_lookup_known_symbol (arg_out->value.string,
+ known_identifiers,
+ N_identifiers,
+ &resolved_symbol_value))
+ {
+ free ((void *) arg_out->value.string);
+ arg_out->value.u64 = resolved_symbol_value;
+ arg_out->type = ULEB128_VALUE;
+ return true;
+ }
+
+ int resolved_symbol;
+ if (skip_backend_symbol_attribute_lookup)
+ {
+ /* Try to resolve the name using the symbol table for this compilation
+ unit. */
+ resolved_symbol = obj_attr_v2_look_symbol_table (arg_out->value.string);
+ if (resolved_symbol == -1)
+ goto bad;
+ }
+
+#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
+ #define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
+#endif
+
+ /* Note: for now, CONVERT_SYMBOLIC_ATTRIBUTE can only handle integers. */
+ resolved_symbol = CONVERT_SYMBOLIC_ATTRIBUTE (arg_out->value.string);
+ if (resolved_symbol == -1)
+ {
+ /* Try to resolve the name using the symbol table for this compilation
+ unit. */
+ resolved_symbol = obj_attr_v2_look_symbol_table (arg_out->value.string);
+ if (resolved_symbol == -1)
+ goto bad;
+ }
+
+ free ((void *) arg_out->value.string);
+ arg_out->value.u64 = resolved_symbol;
+ arg_out->type = ULEB128_VALUE;
+ return true;
+
+bad:
+ as_bad ("unknown symbol: %s", arg_out->value.string);
+ free ((void *) arg_out->value.string);
+ return false;
+}
+
+void
+obj_elf_obj_attr_v2_free_args (attribute_arg_value *args, size_t n)
+{
+ for (size_t i = 0; i < n; ++i)
+ if (args[i].type == STRING_VALUE && args[i].value.string != NULL)
+ free ((void *) args[i].value.string);
+ free (args);
+}
+
+attribute_arg_value *
+obj_elf_subsection_obj_attr_v2_parse_args (int nargs, ...)
+{
+ bool match_subsection_char (char c)
+ {
+ return ISALNUM (c) || c == '_' || c == '-';
+ }
+
+ va_list args;
+ va_start (args, nargs);
+
+ attribute_arg_value* attrs = calloc (nargs, sizeof (attribute_arg_value));
+
+ for (int count = 0; count < nargs; ++count)
+ {
+ skip_whitespace (input_line_pointer);
+
+ attribute_arg_type type_next_arg = va_arg (args, attribute_arg_type);
+
+ if (type_next_arg == SECTION_IDENTIFIER)
+ {
+ if (!extract_identifier (&match_subsection_char, &attrs[count]))
+ {
+ as_bad (_("Invalid token for section name: %c"), *input_line_pointer);
+ goto bad;
+ }
+ gas_assert (attrs[count].type == STRING_VALUE);
+ attrs[count].type = SECTION_IDENTIFIER;
+ }
+ else if (type_next_arg == ULEB128_VALUE || type_next_arg == STRING_VALUE)
+ {
+ const obj_attr_v2_known_identifier known_identifiers[] =
+ {
+ {"ULEB128", 0},
+ {"uleb128", 0},
+ {"NTBS", 1},
+ {"ntbs", 1},
+ {"optional", 1},
+ {"required", 0},
+ };
+ if (! obj_attr_parse_arg (
+ type_next_arg, known_identifiers,
+ ARRAY_SIZE (known_identifiers),
+ /* skip_backend_symbol_attribute_lookup */ true,
+ &attrs[count]))
+ goto bad;
+ }
+
+ if (count + 1 < nargs && skip_past_comma (&input_line_pointer) == -1)
+ goto bad;
+ }
+
+ va_end (args);
+ demand_empty_rest_of_line ();
+
+ return attrs;
+
+bad:
+ obj_elf_obj_attr_v2_free_args (attrs, nargs);
+ va_end (args);
+ as_bad (_("Expected <subsection_name>, <optional>, <format>"));
+ ignore_rest_of_line ();
+ return NULL;
+}
+
+attribute_arg_value *
+obj_elf_obj_attr_v2_parse_args (int nargs, ...)
+{
+ if (elf_obj_attr_subsections (stdoutput).last_ == NULL)
+ {
+ as_bad (_("A subsection has to be declared before declaring attributes"));
+ ignore_rest_of_line ();
+ return NULL;
+ }
+
+ va_list args;
+ va_start (args, nargs);
+
+ attribute_arg_value* attrs = calloc (nargs, sizeof (attribute_arg_value));
+
+ for (int count = 0; count < nargs; ++count)
+ {
+ skip_whitespace (input_line_pointer);
+
+ attribute_arg_type type_next_arg = va_arg (args, attribute_arg_type);
+
+ gas_assert (type_next_arg == ATTRIBUTE_KEY
+ || type_next_arg == ATTRIBUTE_VALUE);
+
+ if (type_next_arg == ATTRIBUTE_KEY)
+ {
+ if (! obj_attr_parse_arg (
+ type_next_arg, NULL, 0,
+ /* skip_backend_symbol_attribute_lookup */ false,
+ &attrs[count]))
+ goto bad;
+ }
+ else if (type_next_arg == ATTRIBUTE_VALUE)
+ {
+ obj_attr_encoding_v2 expected_value_type =
+ elf_obj_attr_subsections (stdoutput).last_->encoding;
+ gas_assert (expected_value_type == ULEB128
+ || expected_value_type == NTBS);
+
+ if (! obj_attr_parse_arg (
+ expected_value_type == ULEB128 ? ULEB128_VALUE : STRING_VALUE,
+ NULL, 0, /* skip_backend_symbol_attribute_lookup */ true,
+ &attrs[count]))
+ goto bad;
+ }
+
+ if ((count + 1 < nargs) && (skip_past_comma (&input_line_pointer) == -1))
+ goto bad;
+ }
+
+ va_end (args);
+ demand_empty_rest_of_line ();
+
+ return attrs;
+
+bad:
+ obj_elf_obj_attr_v2_free_args (attrs, nargs);
+
+ va_end (args);
+ as_bad (_("expected <tag> , <value>"));
+ ignore_rest_of_line ();
+ return NULL;
+}
+
+void
+obj_elf_record_subsection_obj_attr_v2 (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;
+ }
+ /* 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)
+ ? SUBSEC_PUBLIC
+ : 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);
+ }
+}
+
+void
+obj_elf_record_obj_attr_v2 (uint64_t key, attribute_arg_value *value)
+{
+ union obj_attr_value_v2 obj_attr_vals;
+ if (value->type == ULEB128_VALUE)
+ obj_attr_vals.uint_val = value->value.u64;
+ else
+ {
+ /* Move the string. */
+ obj_attr_vals.string_val = xstrdup (value->value.string);
+ value->value.string = NULL;
+ }
+
+ obj_attr_v2* obj_attr = _bfd_elf_obj_attr_v2_init (key, obj_attr_vals);
+ if (value->type == STRING_VALUE)
+ obj_attr_vals.string_val = 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 ((value->type == ULEB128_VALUE
+ && recorded_attr->vals.uint_val != obj_attr->vals.uint_val) ||
+ (value->type == STRING_VALUE
+ && 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 (value->type == STRING_VALUE && obj_attr->vals.string_val != NULL)
+ free ((void *) obj_attr->vals.string_val);
+ free (obj_attr);
+ return;
+ }
+
+ gas_assert (elf_obj_attr_subsections (stdoutput).last_ != NULL);
+ gas_assert (obj_attr != NULL);
+ LINKED_LIST_APPEND(obj_attr_v2) (
+ elf_obj_attr_subsections (stdoutput).last_, obj_attr);
+}
+
/* Parse an attribute directive for VENDOR.
Returns the attribute number read, or zero on error. */
@@ -54,6 +54,23 @@ extern int mips_flag_mdebug;
#endif
#endif
+typedef enum {
+ SECTION_IDENTIFIER,
+ ULEB128_VALUE,
+ STRING_VALUE,
+ ATTRIBUTE_KEY,
+ ATTRIBUTE_VALUE,
+} attribute_arg_type;
+
+typedef struct {
+ union {
+ const char* string;
+ uint64_t u64;
+ } value;
+ bool optional_set;
+ attribute_arg_type type;
+} attribute_arg_value;
+
enum elf_visibility
{
visibility_unchanged = 0,
@@ -206,6 +223,16 @@ extern struct fix * obj_elf_get_vtable_inherit (void);
extern struct fix * obj_elf_get_vtable_entry (void);
extern bool obj_elf_seen_attribute
(int, unsigned int);
+
+extern attribute_arg_value *
+ obj_elf_subsection_obj_attr_v2_parse_args (int nargs, ...);
+extern attribute_arg_value *
+ obj_elf_obj_attr_v2_parse_args (int nargs, ...);
+extern void obj_elf_obj_attr_v2_free_args (attribute_arg_value *, size_t);
+extern void obj_elf_record_subsection_obj_attr_v2 (const char *, bool,
+ obj_attr_encoding_v2);
+extern void obj_elf_record_obj_attr_v2 (uint64_t, attribute_arg_value *);
+
extern int obj_elf_vendor_attribute (int);
/* BFD wants to write the udata field, which is a no-no for the
@@ -2401,6 +2401,56 @@ 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)
+{
+ const size_t N_ARGS = 3;
+ attribute_arg_value* args =
+ obj_elf_subsection_obj_attr_v2_parse_args (
+ N_ARGS, SECTION_IDENTIFIER, ULEB128_VALUE, ULEB128_VALUE);
+
+ bool is_valid_boolean (uint64_t value)
+ {
+ return value == 0 || value == 1;
+ }
+
+ bool is_valid_optional (uint64_t value)
+ {
+ return value == 0 || value == 1;
+ }
+
+ if (args == NULL)
+ return;
+
+ if (! is_valid_boolean (args[1].value.u64))
+ as_bad (("Expected subsection values for <optional> are 0 or 1"));
+ if (! is_valid_optional (args[2].value.u64))
+ as_bad (("Expected subsection values for <format> are 0 or 1"));
+
+ obj_elf_record_subsection_obj_attr_v2 (args[0].value.string,
+ args[1].value.u64,
+ args[2].value.u64);
+
+ obj_elf_obj_attr_v2_free_args (args, N_ARGS);
+}
+
+/* Parse a .aeabi_attribute directive. */
+static void
+s_aarch64_aeabi_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ const size_t N_ARGS = 2;
+ attribute_arg_value* args =
+ obj_elf_obj_attr_v2_parse_args (2, ATTRIBUTE_KEY, ATTRIBUTE_VALUE);
+
+ if (args == NULL)
+ return;
+
+ obj_elf_record_obj_attr_v2 (args[0].value.u64, &args[1]);
+
+ obj_elf_obj_attr_v2_free_args (args, N_ARGS);
+}
#endif /* OBJ_ELF */
#ifdef TE_PE
@@ -2481,6 +2531,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},
@@ -11320,4 +11372,54 @@ aarch64_elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
S_SET_SIZE (dest, S_GET_SIZE (src));
}
}
+
+static bool
+aarch64_obj_attr_v2_lookup_known_symbol (const char *identifier, uint32_t *val)
+{
+ if (identifier == NULL)
+ return false;
+
+ /* IMPORTANT: This list must be alphabetically sorted. */
+ static const struct
+ {
+ const char *name;
+ const uint32_t val;
+ } known_identifiers[] =
+ {
+ {"Tag_Feature_BTI", 0},
+ {"Tag_Feature_GCS", 2},
+ {"Tag_Feature_PAC", 1},
+ {"Tag_PAuth_Platform", 1},
+ {"Tag_PAuth_Schema", 2},
+ };
+
+ for (unsigned i = 0; i < ARRAY_SIZE (known_identifiers); ++i)
+ {
+ int cmp = strcmp (known_identifiers[i].name, identifier);
+ if (cmp == 0)
+ {
+ *val = known_identifiers[i].val;
+ return true;
+ }
+ else if (cmp > 0)
+ break;
+ }
+ return false;
+}
+
+/* Convert a symbol related to object attribute v2 to its corresponding
+ integer value. */
+int
+aarch64_convert_symbolic_attribute (const char *name)
+{
+ if (name == NULL)
+ return -1;
+
+ uint32_t value = 0;
+ /* Try to resolve the name against a known list of identifiers. */
+ if (aarch64_obj_attr_v2_lookup_known_symbol (name, &value))
+ return value;
+
+ return -1;
+}
#endif
@@ -344,4 +344,9 @@ void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int);
#endif /* TE_PE */
+#ifdef OBJ_ELF
+#define CONVERT_SYMBOLIC_ATTRIBUTE(name) aarch64_convert_symbolic_attribute (name)
+extern int aarch64_convert_symbolic_attribute (const char *);
+#endif
+
#endif /* TC_AARCH64 */