Home | History | Annotate | Line # | Download | only in config
      1 /* Object attributes parsing.
      2    Copyright (C) 2025-2026 Free Software Foundation, Inc.
      3 
      4    This file is part of GAS, the GNU Assembler.
      5 
      6    GAS is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3, or (at your option)
      9    any later version.
     10 
     11    GAS is distributed in the hope that it will be useful,
     12    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14    GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with GAS; see the file COPYING.  If not, write to the Free
     18    Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
     19    02110-1301, USA.  */
     20 
     21 #include "obj-elf-attr.h"
     22 
     23 #ifdef TC_OBJ_ATTR
     24 
     25 #include "obstack.h"
     26 #include "safe-ctype.h"
     27 
     28 /* A variant type to store information about known OAv2 identifiers.  */
     29 typedef union {
     30   uint8_t u8;
     31   bool b;
     32 } oav2_identifier_variant_value_t;
     33 
     34 typedef enum {
     35   OAv2_ASM_ID_VALUE_UNDEFINED = 0,
     36   OAv2_ASM_ID_VALUE_U8,
     37   OAv2_ASM_ID_VALUE_BOOL,
     38 } oav2_identifier_variant_type_info_t;
     39 
     40 typedef struct {
     41   oav2_identifier_variant_value_t val;
     42   oav2_identifier_variant_type_info_t vtype;
     43 } oav2_identifier_variant_t;
     44 
     45 typedef struct {
     46   const char *const name;
     47   const oav2_identifier_variant_t value;
     48 } oav2_identifier_t;
     49 
     50 typedef struct {
     51   size_t len;
     52   struct arg_variant_t *elts;
     53 } arg_variant_list_t;
     54 
     55 typedef union {
     56   const char *string;
     57   uint64_t u64;
     58   int64_t i64;
     59   arg_variant_list_t list;
     60 } arg_variant_value_t;
     61 
     62 typedef enum {
     63   VALUE_UNDEFINED = 0,
     64   VALUE_U64,
     65   VALUE_I64,
     66   VALUE_UNSIGNED_INTEGER = VALUE_U64,
     67   VALUE_SIGNED_INTEGER = VALUE_I64,
     68   VALUE_STRING,
     69   VALUE_LIST,
     70   VALUE_OPTIONAL_ABSENT,
     71 } arg_variant_type_info_t;
     72 
     73 /* A variant type to store the argument values of an assembly directive.  */
     74 typedef struct arg_variant_t {
     75   arg_variant_value_t val;
     76   arg_variant_type_info_t vtype;
     77 } arg_variant_t;
     78 
     79 typedef arg_variant_t arg_t;
     80 
     81 #define skip_whitespace(str)  do { if (is_whitespace (*(str))) ++(str); } while (0)
     82 
     83 static inline bool
     84 skip_past_char (char **str, char c)
     85 {
     86   if (**str == c)
     87     {
     88       (*str)++;
     89       return true;
     90     }
     91   return false;
     92 }
     93 #define skip_past_comma(str) skip_past_char (str, ',')
     94 
     95 #if (TC_OBJ_ATTR_v1)
     96 
     97 /* A list of attributes that have been explicitly set by the assembly code.
     98    VENDOR is the vendor id, BASE is the tag shifted right by the number
     99    of bits in MASK, and bit N of MASK is set if tag BASE+N has been set.  */
    100 typedef struct recorded_attribute_info {
    101   struct recorded_attribute_info *next;
    102   obj_attr_vendor_t vendor;
    103   unsigned int base;
    104   unsigned long mask;
    105 } recorded_attribute_info_t;
    106 static recorded_attribute_info_t *recorded_attributes;
    107 
    108 static void
    109 oav1_attr_info_free (recorded_attribute_info_t *node)
    110 {
    111   while (node != NULL)
    112     {
    113       recorded_attribute_info_t *next = node->next;
    114       free (node);
    115       node = next;
    116     }
    117 }
    118 
    119 void
    120 oav1_attr_info_init (void)
    121 {
    122   /* Note: this "constructor" was added for symmetry with oav1_attr_info_exit.
    123      recorded_attributes is a static variable which is automatically initialized
    124      to NULL.  There is no need to initialize it another time except for a
    125      cosmetic reason and to possibly help fuzzing.  */
    126   recorded_attributes = NULL;
    127 }
    128 
    129 void
    130 oav1_attr_info_exit (void)
    131 {
    132   oav1_attr_info_free (recorded_attributes);
    133 }
    134 
    135 /* Record that we have seen an explicit specification of attribute TAG
    136    for vendor VENDOR.  */
    137 
    138 static void
    139 oav1_attr_record_seen (obj_attr_vendor_t vendor, obj_attr_tag_t tag)
    140 {
    141   unsigned int base;
    142   unsigned long mask;
    143   recorded_attribute_info_t *rai;
    144 
    145   base = tag / (8 * sizeof (rai->mask));
    146   mask = 1UL << (tag % (8 * sizeof (rai->mask)));
    147   for (rai = recorded_attributes; rai; rai = rai->next)
    148     if (rai->vendor == vendor && rai->base == base)
    149       {
    150 	rai->mask |= mask;
    151 	return;
    152       }
    153 
    154   rai = XNEW (recorded_attribute_info_t);
    155   rai->next = recorded_attributes;
    156   rai->vendor = vendor;
    157   rai->base = base;
    158   rai->mask = mask;
    159   recorded_attributes = rai;
    160 }
    161 
    162 /* Return true if we have seen an explicit specification of attribute TAG
    163    for vendor VENDOR.  */
    164 
    165 bool
    166 oav1_attr_seen (obj_attr_vendor_t vendor, obj_attr_tag_t tag)
    167 {
    168   unsigned int base;
    169   unsigned long mask;
    170   recorded_attribute_info_t *rai;
    171 
    172   base = tag / (8 * sizeof (rai->mask));
    173   mask = 1UL << (tag % (8 * sizeof (rai->mask)));
    174   for (rai = recorded_attributes; rai; rai = rai->next)
    175     if (rai->vendor == vendor && rai->base == base)
    176       return (rai->mask & mask) != 0;
    177   return false;
    178 }
    179 
    180 #endif /* TC_OBJ_ATTR_v1 */
    181 
    182 /* Expected argument tokens for object attribute directives.  */
    183 typedef enum {
    184   /* Base types.  */
    185   IDENTIFIER = 0x1,
    186   UNSIGNED_INTEGER = 0x2,
    187   SIGNED_INTEGER = 0x4,
    188   STRING = 0x8,
    189   LIST = 0x10,
    190   LT_MASK = 0xFF,
    191   /* Higher types.  */
    192   SUBSECTION_NAME = 0x100,
    193   SUBSECTION_OPTION_1 = 0x200,
    194   SUBSECTION_OPTION_2 = 0x400,
    195   ATTRIBUTE_KEY = 0x800,
    196   ATTRIBUTE_VALUE = 0x1000,
    197   HT_MASK = 0xFF00,
    198 } arg_token_t;
    199 
    200 /* Free an arguments list of size N.  */
    201 static void
    202 args_list_free (arg_t *args, size_t n)
    203 {
    204   for (size_t i = 0; i < n; ++i)
    205     if (args[i].vtype == VALUE_STRING)
    206       free ((void *) args[i].val.string);
    207     else if (args[i].vtype == VALUE_LIST)
    208       args_list_free (args[i].val.list.elts, args[i].val.list.len);
    209   free (args);
    210 }
    211 
    212 /* Extract a string literal from the input.  */
    213 static bool
    214 extract_string_literal (arg_t *arg_out)
    215 {
    216   int len;
    217   char *obstack_buf = demand_copy_C_string (&len);
    218   if (obstack_buf != NULL)
    219     {
    220       arg_out->val.string = xstrdup (obstack_buf);
    221       obstack_free (&notes, obstack_buf);
    222       arg_out->vtype = VALUE_STRING;
    223       return true;
    224     }
    225 
    226   arg_out->val.string = NULL;
    227   return false;
    228 }
    229 
    230 /* Extract an integer literal from the input.
    231    Anything matched by O_constant is considered an integer literal (see the
    232    usage of O_constant in expr.c to see all the matches.
    233    Return true on success, false otherwise. If a signedness issue is detected,
    234    'signedness_issue' is also set to true.  */
    235 static bool
    236 extract_integer_literal (arg_t *arg_out,
    237 			 bool want_unsigned,
    238 			 bool *signedness_issue)
    239 {
    240   const char *cursor_begin = input_line_pointer;
    241   expressionS exp;
    242   expression_and_evaluate (&exp);
    243   if (exp.X_op != O_constant)
    244     {
    245       char backup_c = *input_line_pointer;
    246       *input_line_pointer = '\0';
    247       as_bad (_("expression '%s' does not resolve to an integer"),
    248 	      cursor_begin);
    249       /* Restore the character pointed by the current cursor position,
    250 	 otherwise '\0' misleads ignore_rest_of_line().  */
    251       *input_line_pointer = backup_c;
    252       return false;
    253     }
    254 
    255   int64_t val = (int64_t) exp.X_add_number;
    256   if (want_unsigned)
    257     {
    258       if (! exp.X_unsigned && val < 0)
    259 	{
    260 	  as_bad (_("unexpected value '%" PRId64 "', expected `unsigned integer' instead"),
    261 		  val);
    262 	  *signedness_issue = true;
    263 	  return false;
    264 	}
    265       arg_out->val.u64 = val;
    266       arg_out->vtype = VALUE_UNSIGNED_INTEGER;
    267     }
    268   else
    269     {
    270       arg_out->val.i64 = val;
    271       arg_out->vtype = VALUE_SIGNED_INTEGER;
    272     }
    273   return true;
    274 }
    275 
    276 /* Extract an identifier based on the provided character matcher.  */
    277 static bool
    278 extract_identifier (bool (*char_predicate) (char), arg_t *arg_out)
    279 {
    280   const char *s = input_line_pointer;
    281   unsigned int i = 0;
    282   for (; char_predicate (*input_line_pointer); ++input_line_pointer)
    283     i++;
    284   if (i == 0)
    285     {
    286       as_bad (_("invalid character '%c' in identifier"),
    287 	      *input_line_pointer);
    288       return false;
    289     }
    290 
    291   arg_out->vtype = VALUE_STRING;
    292   arg_out->val.string = xmemdup0 (s, i);
    293   return true;
    294 }
    295 
    296 #if (TC_OBJ_ATTR_v2)
    297 /* Resolve the identifier if it matches a known tag.  */
    298 static bool
    299 resolve_if_matching_known_tag (const char *identifier,
    300 			       const obj_attr_tag_info_t *known_identifier,
    301 			       arg_t *val_out)
    302 {
    303   if (strcmp (known_identifier->name, identifier) != 0)
    304     return false;
    305 
    306   val_out->val.u64 = known_identifier->value;
    307   val_out->vtype = VALUE_UNSIGNED_INTEGER;
    308   return true;
    309 }
    310 
    311 /* Resolve the identifier if it matches the known one.  */
    312 static bool
    313 resolve_if_matching (const char *identifier,
    314 		     const oav2_identifier_t *known_identifier,
    315 		     arg_t *val_out)
    316 {
    317   /* Lowercase the identifier before comparison.  */
    318   char normalized_identifier[100];
    319   unsigned int i = 0;
    320   for (; i < sizeof (normalized_identifier) && identifier[i] != '\0'; ++i)
    321     normalized_identifier[i] = TOLOWER (identifier[i]);
    322   if (i >= sizeof (normalized_identifier))
    323     /* Identifier is too long.  */
    324     return false;
    325   gas_assert (identifier[i] == '\0');
    326   normalized_identifier[i] = '\0';
    327 
    328   /* Comparison with normalized identifier.  */
    329   if (strcmp (known_identifier->name, normalized_identifier) != 0)
    330     return false;
    331 
    332   /* We only need U8 and Bool for now.  */
    333   switch (known_identifier->value.vtype)
    334     {
    335     case OAv2_ASM_ID_VALUE_U8:
    336       val_out->val.u64 = known_identifier->value.val.b;
    337       val_out->vtype = VALUE_UNSIGNED_INTEGER;
    338       break;
    339     case OAv2_ASM_ID_VALUE_BOOL:
    340       val_out->val.u64 = known_identifier->value.val.u8;
    341       val_out->vtype = VALUE_UNSIGNED_INTEGER;
    342       break;
    343     default:
    344       abort ();
    345     }
    346 
    347   return true;
    348 }
    349 #endif /* TC_OBJ_ATTR_v2 */
    350 
    351 #if (TC_OBJ_ATTR_v1)
    352 /* Look up attribute tags defined in the backend (object attribute v1).  */
    353 static bool
    354 obj_attr_v1_lookup_known_attr_tag_symbol
    355   (const char *identifier ATTRIBUTE_UNUSED,
    356    arg_token_t token_type,
    357    arg_t *val_out)
    358 {
    359   gas_assert (token_type & UNSIGNED_INTEGER);
    360 
    361 #ifndef CONVERT_SYMBOLIC_ATTRIBUTE
    362 # define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
    363 #endif
    364   int tag = CONVERT_SYMBOLIC_ATTRIBUTE (identifier);
    365   if (tag < 0)
    366     return false;
    367   val_out->val.u64 = tag;
    368   val_out->vtype = VALUE_UNSIGNED_INTEGER;
    369   return true;
    370 }
    371 #endif /* TC_OBJ_ATTR_v1 */
    372 
    373 #if (TC_OBJ_ATTR_v2)
    374 /* Look up attribute tags defined in the backend (object attribute v2).  */
    375 static bool
    376 obj_attr_v2_lookup_known_attr_tag_symbol (const char *identifier,
    377 					  arg_token_t token_type,
    378 					  arg_t *val_out)
    379 {
    380   obj_attr_subsection_v2_t *subsec = elf_obj_attr_subsections (stdoutput).last;
    381   /* This function is called when setting a value for an attribute, and it
    382      requires an active subsection.  Calling this function without setting
    383      'subsec' is a logical error.  Invalid user code, where the subsection
    384      has not been selected must be handled by the caller.  We require 'subsec'
    385      to be non-NULL here.  */
    386   gas_assert (subsec != NULL);
    387 
    388   /* An attribute tag is an unsigned integer, so the expected token type should
    389      always have the base type UNSIGNED_INTEGER.  Otherwise, this function was
    390      called incorrectly.  */
    391   gas_assert (token_type & UNSIGNED_INTEGER);
    392 
    393   bool resolved = false;
    394   const struct elf_backend_data *be = get_elf_backend_data (stdoutput);
    395   const known_subsection_v2_t *known_subsec
    396     = bfd_obj_attr_v2_identify_subsection (be, subsec->name);
    397   if (known_subsec != NULL)
    398     {
    399       for (size_t i = 0; i < known_subsec->len && ! resolved; ++i)
    400 	resolved
    401 	  = resolve_if_matching_known_tag (identifier,
    402 					   &known_subsec->known_attrs[i].tag,
    403 					   val_out);
    404     }
    405 
    406   if (resolved)
    407     /* An attribute tag is an unsigned integer, so the type of the found value
    408        should be VALUE_UNSIGNED_INTEGER.  Otherwise, check if you set correctly
    409        the type of the value associated to the symbol.  */
    410     gas_assert (val_out->vtype == VALUE_UNSIGNED_INTEGER);
    411 
    412   return resolved;
    413 }
    414 #endif /* TC_OBJ_ATTR_v2 */
    415 
    416 /* Look up known symbols, and try to resolve the given identifier.  */
    417 static bool
    418 lookup_known_symbols (const char *identifier,
    419 		      arg_token_t token_type,
    420 		      arg_t *val_out)
    421 {
    422   if (identifier == NULL)
    423     return false;
    424 
    425   arg_token_t high_ttype = (token_type & HT_MASK);
    426 
    427 #if (TC_OBJ_ATTR_v2)
    428   static const oav2_identifier_t known_identifiers_subsection_optional[] = {
    429     { "optional", {
    430 	.val.b = true,
    431 	.vtype = OAv2_ASM_ID_VALUE_BOOL
    432       }
    433     },
    434     { "required", {
    435 	.val.b = false,
    436 	.vtype = OAv2_ASM_ID_VALUE_BOOL
    437       }
    438     },
    439   };
    440 
    441   static const oav2_identifier_t known_identifiers_subsection_encoding[] = {
    442     { "uleb128", {
    443 	.val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_ULEB128),
    444 	.vtype = OAv2_ASM_ID_VALUE_U8
    445       }
    446     },
    447     { "ntbs", {
    448 	.val.u8 = obj_attr_encoding_v2_to_u8 (OA_ENC_NTBS),
    449 	.vtype = OAv2_ASM_ID_VALUE_U8
    450       }
    451     },
    452   };
    453 #endif /* TC_OBJ_ATTR_v2 */
    454 
    455   bool resolved = false;
    456 
    457 #if (TC_OBJ_ATTR_v2)
    458   if (high_ttype == SUBSECTION_OPTION_1 || high_ttype == SUBSECTION_OPTION_2)
    459     {
    460       const oav2_identifier_t *known_identifiers
    461 	= (high_ttype == SUBSECTION_OPTION_1
    462 	   ? known_identifiers_subsection_optional
    463 	   : known_identifiers_subsection_encoding);
    464       const size_t N_identifiers
    465 	= (high_ttype == SUBSECTION_OPTION_1
    466 	   ? ARRAY_SIZE (known_identifiers_subsection_optional)
    467 	   : ARRAY_SIZE (known_identifiers_subsection_encoding));
    468 
    469       for (size_t i = 0; i < N_identifiers && ! resolved; ++i)
    470 	resolved = resolve_if_matching (identifier,
    471 					&known_identifiers[i],
    472 					val_out);
    473     }
    474   else
    475 #endif /* TC_OBJ_ATTR_v2 */
    476   if (high_ttype == ATTRIBUTE_KEY)
    477     {
    478       obj_attr_version_t version = elf_obj_attr_version (stdoutput);
    479       switch (version)
    480 	{
    481 #if (TC_OBJ_ATTR_v1)
    482 	case OBJ_ATTR_V1:
    483 	  resolved = obj_attr_v1_lookup_known_attr_tag_symbol
    484 	    (identifier, token_type, val_out);
    485 	  break;
    486 #endif /* TC_OBJ_ATTR_v1 */
    487 #if (TC_OBJ_ATTR_v2)
    488 	case OBJ_ATTR_V2:
    489 	  resolved = obj_attr_v2_lookup_known_attr_tag_symbol
    490 	    (identifier, token_type, val_out);
    491 	  break;
    492 #endif /* TC_OBJ_ATTR_v2 */
    493 	default:
    494 	  abort ();
    495 	}
    496     }
    497   else
    498     abort ();
    499 
    500   return resolved;
    501 }
    502 
    503 /* Look up the symbol table of this compilation unit, and try to resolve the
    504    given identifier.  */
    505 static bool
    506 lookup_symbol_table (const char *identifier,
    507 		     const arg_token_t expected_ttype,
    508 		     arg_t *val_out)
    509 {
    510   if (identifier == NULL)
    511     return false;
    512 
    513   /* Note: signed integer are unsupported for now.  */
    514   gas_assert (expected_ttype & UNSIGNED_INTEGER);
    515 
    516   symbolS *symbolP = symbol_find (identifier);
    517   if (symbolP == NULL)
    518     return false;
    519 
    520   if (! S_IS_DEFINED (symbolP))
    521     return false;
    522 
    523   valueT val = S_GET_VALUE (symbolP);
    524   val_out->val.u64 = val;
    525   val_out->vtype = VALUE_UNSIGNED_INTEGER;
    526 
    527   return true;
    528 }
    529 
    530 /* Function similar to snprintf from the standard library, except that it
    531    also updates the buffer pointer to point to the last written character,
    532    and length to match the remaining space in the buffer.
    533    Return the number of bytes printed.  The function is assumed to always be
    534    successful, and a failure with vnsprintf() will trigger an assert().  */
    535 static size_t
    536 snprintf_append (char **sbuffer, size_t *length,
    537 		 const char *format, ...)
    538 {
    539   va_list args;
    540   va_start (args, format);
    541   int rval = vsnprintf (*sbuffer, *length, format, args);
    542   va_end (args);
    543 
    544   gas_assert (rval >= 0);
    545   size_t written = rval;
    546   *length -= written;
    547   *sbuffer += written;
    548   return written;
    549 }
    550 
    551 /* Return the list of comma-separated strings matching the expected types
    552    (i.e. the flags set in low_ttype).  */
    553 static const char *
    554 expectations_to_string (const arg_token_t low_ttype,
    555 			char *sbuffer, size_t length)
    556 {
    557   unsigned match_n = 0;
    558   char *sbuffer_start = sbuffer;
    559   size_t total = 0;
    560   if (low_ttype & IDENTIFIER)
    561     {
    562       ++match_n;
    563       total += snprintf_append (&sbuffer, &length, "`%s'", "identifier");
    564     }
    565 
    566 #define EXP_APPEND_TYPE_STR(type_flag, type_str)			\
    567   if (low_ttype & type_flag)						\
    568     {									\
    569       if (match_n >= 1)							\
    570 	total += snprintf_append (&sbuffer, &length, "%s", ", or ");	\
    571       ++match_n;							\
    572       total += snprintf_append (&sbuffer, &length, "`%s'", type_str);	\
    573     }
    574 
    575   EXP_APPEND_TYPE_STR (STRING, "string");
    576   EXP_APPEND_TYPE_STR (UNSIGNED_INTEGER, "unsigned integer");
    577   EXP_APPEND_TYPE_STR (SIGNED_INTEGER, "signed integer");
    578 #undef APPEND_TYPE_STR
    579 
    580   gas_assert (total <= length);
    581   return sbuffer_start;
    582 }
    583 
    584 /* In the context of object attributes, an identifier is defined with the
    585    following lexical constraint: [a-zA-z_][a-zA-Z0-9_]*.  An identifier can
    586    be used to define the name of a subsection, its optionality, or its
    587    encoding, as well as for an attribute's tag.  */
    588 
    589 static bool
    590 is_identifier_beginner (char c)
    591 {
    592   return ISALPHA (c) || c == '_';
    593 }
    594 
    595 static bool
    596 is_part_of_identifier (char c)
    597 {
    598   return ISALNUM (c) || c == '_';
    599 }
    600 
    601 /* Parse an argument, and set its type accordingly depending on the input
    602    value, and the constraints on the expected argument.  */
    603 static bool
    604 obj_attr_parse_arg (arg_token_t expected_ttype,
    605 		    bool resolve_identifier,
    606 		    bool optional,
    607 		    arg_t *arg_out)
    608 {
    609   const arg_token_t low_ttype = (expected_ttype & LT_MASK);
    610 
    611   if (optional && is_end_of_stmt (*input_line_pointer))
    612     {
    613       arg_out->vtype = VALUE_OPTIONAL_ABSENT;
    614       return true;
    615     }
    616 
    617   /* Check whether this looks like a string literal
    618      Note: symbol look-up for string literals is not available.  */
    619   if (*input_line_pointer == '"')
    620     {
    621       bool status = extract_string_literal (arg_out);
    622       if (status && (low_ttype & STRING))
    623 	return true;
    624 
    625       if (status)
    626 	{
    627 	  char sbuffer[100];
    628 	  as_bad (_("unexpected `string' \"%s\", expected %s instead"),
    629 		  arg_out->val.string,
    630 		  expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
    631 	  free ((char *) arg_out->val.string);
    632 	  arg_out->val.string = NULL;
    633 	  arg_out->vtype = VALUE_UNDEFINED;
    634 	}
    635       return false;
    636     }
    637 
    638   /* Check whether this looks like an identifier.  */
    639   if (is_identifier_beginner (*input_line_pointer))
    640     {
    641       bool status = extract_identifier (is_part_of_identifier, arg_out);
    642       /* is_identifier_beginner() confirmed that it was the beginning of an
    643 	 identifier, so we don't expect the extraction to fail.  */
    644       gas_assert (status);
    645       gas_assert (arg_out->vtype == VALUE_STRING);
    646 
    647       if (! (low_ttype & IDENTIFIER))
    648 	{
    649 	  char sbuffer[100];
    650 	  as_bad (_("unexpected `identifier' \"%s\", expected %s instead"),
    651 		  arg_out->val.string,
    652 		  expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
    653 	  free ((char *) arg_out->val.string);
    654 	  arg_out->val.string = NULL;
    655 	  arg_out->vtype = VALUE_UNDEFINED;
    656 	  return false;
    657 	}
    658 
    659       /* In some cases, we don't want to resolve the identifier because it is the
    660 	 actual value.  */
    661       if (! resolve_identifier)
    662 	return true;
    663 
    664       /* Move the identifier out of arg_out.  */
    665       const char *identifier = arg_out->val.string;
    666       arg_out->val.string = NULL;
    667       arg_out->vtype = VALUE_UNDEFINED;
    668       bool resolved = true;
    669 
    670       /* The identifier is a symbol, let's try to resolve it by:
    671 	 1. using the provided list of known symbols.
    672 	   a) backend-independent
    673 	   b) backend-specific.  */
    674       if (lookup_known_symbols (identifier, expected_ttype, arg_out))
    675 	goto free_identifier;
    676 
    677       /* 2. using the symbol table for this compilation unit.
    678 	 Note: this is the last attempt before failure.  */
    679       if (lookup_symbol_table (identifier, low_ttype, arg_out))
    680 	goto free_identifier;
    681 
    682       as_bad (_("unknown identifier '%s' in this context"), identifier);
    683       resolved = false;
    684 
    685  free_identifier:
    686       free ((char *) identifier);
    687       return resolved;
    688     }
    689 
    690   /* If it is neither a string nor an identifier, it must be an expression.  */
    691   bool signedness_issue = false;
    692   bool success = extract_integer_literal (arg_out,
    693 					  (low_ttype & UNSIGNED_INTEGER),
    694 					  &signedness_issue);
    695   if (success && (low_ttype & (UNSIGNED_INTEGER | SIGNED_INTEGER)))
    696     return true;
    697 
    698   char sbuffer[100];
    699   if (success)
    700     as_bad (_("unexpected integer '%"PRIu64"', expected %s instead"),
    701 	    arg_out->val.u64,
    702 	    expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
    703   else if ((low_ttype & UNSIGNED_INTEGER) && signedness_issue)
    704     {
    705       /* Already handled by extract_integer_literal(), nothing to do.  */
    706     }
    707   else
    708     as_bad (_("fell back to integer literal extraction from expression, "
    709 	      "but expected %s instead"),
    710 	    expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
    711 
    712   arg_out->vtype = VALUE_UNDEFINED;
    713   return false;
    714 }
    715 
    716 /* Trim white space before a parameter.
    717    Error if it meets a parameter separator before a parameter.  */
    718 static bool
    719 trim_whitespace_before_param (void)
    720 {
    721   skip_whitespace (input_line_pointer);
    722   if (*input_line_pointer == ',')
    723     {
    724       as_bad (_("syntax error, comma not expected here"));
    725       return false;
    726     }
    727   return true;
    728 }
    729 
    730 /* Skip white space + parameter separator after a parameter.
    731    Error if it does not meet a parameter separator after a parameter.  */
    732 static bool
    733 skip_whitespace_past_comma (void)
    734 {
    735   skip_whitespace (input_line_pointer);
    736   if (! skip_past_comma (&input_line_pointer))
    737     {
    738       as_bad (_("syntax error, comma missing here"));
    739       return false;
    740     }
    741   return true;
    742 }
    743 
    744 /* Can parse a list of arguments with variable length.  */
    745 static bool
    746 obj_attr_parse_args (arg_token_t expected_ttype,
    747 		     bool resolve_identifier,
    748 		     arg_t *arg_out)
    749 {
    750   if ((expected_ttype & LIST) == 0)
    751     return obj_attr_parse_arg (expected_ttype, resolve_identifier, false,
    752 			       arg_out);
    753 
    754   static const size_t LIST_MAX_SIZE = 2;
    755   arg_t *arg_list = xcalloc (LIST_MAX_SIZE, sizeof (*arg_list));
    756 
    757   /* We don't want to support recursive lists.  */
    758   expected_ttype &= ~LIST;
    759 
    760   size_t n = 0;
    761   do {
    762     if (! trim_whitespace_before_param ())
    763       goto bad;
    764 
    765     if (! obj_attr_parse_arg (expected_ttype, resolve_identifier, false,
    766 			      &arg_list[n]))
    767       goto bad;
    768 
    769     ++n;
    770     skip_whitespace (input_line_pointer);
    771     if (is_end_of_stmt (*input_line_pointer))
    772       break;
    773 
    774     if (! skip_whitespace_past_comma ())
    775       goto bad;
    776 
    777     if (n >= LIST_MAX_SIZE)
    778       {
    779 	as_bad ("too many arguments for a list (max: %zu)", LIST_MAX_SIZE);
    780 	goto bad;
    781       }
    782   } while (n < LIST_MAX_SIZE);
    783 
    784   arg_out->vtype = VALUE_LIST;
    785   arg_out->val.list.len = n;
    786   arg_out->val.list.elts = arg_list;
    787   return true;
    788 
    789  bad:
    790   args_list_free (arg_list, n);
    791   return false;
    792 }
    793 
    794 #if (TC_OBJ_ATTR_v2)
    795 static bool
    796 is_valid_boolean (uint64_t value)
    797 {
    798   return value <= 1;
    799 }
    800 
    801 #define is_valid_comprehension is_valid_boolean
    802 
    803 static bool
    804 is_valid_encoding (uint64_t value)
    805 {
    806   value = obj_attr_encoding_v2_from_u8 (value);
    807   return OA_ENC_UNSET < value && value <= OA_ENC_MAX;
    808 }
    809 
    810 #endif /* TC_OBJ_ATTR_v2 */
    811 
    812 #if (TC_OBJ_ATTR_v1)
    813 /* Determine the expected argument type based on the tag ID.  */
    814 static arg_token_t
    815 obj_attr_v1_get_arg_type (bfd *abfd,
    816 			  obj_attr_vendor_t vendor,
    817 			  obj_attr_tag_t tag)
    818 {
    819   int attr_type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
    820   arg_token_t arg_type;
    821   if (attr_type == (ATTR_TYPE_FLAG_STR_VAL | ATTR_TYPE_FLAG_INT_VAL))
    822     arg_type = LIST | UNSIGNED_INTEGER | STRING;
    823   else if (attr_type == ATTR_TYPE_FLAG_STR_VAL)
    824     arg_type = STRING;
    825   else
    826     /* Covers the remaning cases:
    827        - ATTR_TYPE_FLAG_INT_VAL.
    828        - ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_NO_DEFAULT.  */
    829     arg_type = UNSIGNED_INTEGER;
    830   return arg_type;
    831 }
    832 #endif /* TC_OBJ_ATTR_v1 */
    833 
    834 #if (TC_OBJ_ATTR_v2)
    835 /* Determine the expected argument type based on the subsection encoding.  */
    836 static arg_token_t
    837 obj_attr_v2_get_arg_type (obj_attr_encoding_v2_t subsec_encoding)
    838 {
    839   arg_token_t arg_type;
    840   switch (subsec_encoding)
    841     {
    842     case OA_ENC_ULEB128:
    843       arg_type = UNSIGNED_INTEGER;
    844       break;
    845     case OA_ENC_NTBS:
    846       arg_type = STRING;
    847       break;
    848     case OA_ENC_UNSET:
    849     default:
    850       abort ();
    851     }
    852   return arg_type;
    853 }
    854 #endif /* TC_OBJ_ATTR_v2 */
    855 
    856 /* Parse the arguments of [vendor]_attribute directive.  */
    857 static arg_t *
    858 vendor_attribute_parse_args (obj_attr_vendor_t vendor ATTRIBUTE_UNUSED,
    859 			     const obj_attr_subsection_v2_t *subsec ATTRIBUTE_UNUSED,
    860 			     unsigned int nargs, ...)
    861 {
    862   va_list args;
    863   va_start (args, nargs);
    864 
    865   arg_t *args_out = xcalloc (nargs, sizeof (arg_t));
    866 
    867   for (unsigned int n = 0; n < nargs; ++n)
    868     {
    869       if (! trim_whitespace_before_param ())
    870 	goto bad;
    871 
    872       arg_t *arg_out = &args_out[n];
    873 
    874       arg_token_t expected_ttype = va_arg (args, arg_token_t);
    875       arg_token_t high_ttype = (expected_ttype & HT_MASK);
    876       /* Make sure that we called the right parse_args().  */
    877       gas_assert (high_ttype == ATTRIBUTE_KEY
    878 	       || high_ttype == ATTRIBUTE_VALUE);
    879 
    880       if (high_ttype == ATTRIBUTE_VALUE)
    881 	{
    882 	  arg_token_t type_attr_value
    883 #if (TC_OBJ_ATTR_v1 && TC_OBJ_ATTR_v2)
    884 	    = (subsec != NULL)
    885 	      ? obj_attr_v2_get_arg_type (subsec->encoding)
    886 	      : obj_attr_v1_get_arg_type (stdoutput, vendor,
    887 					  args_out[n - 1].val.u64);
    888 #elif (TC_OBJ_ATTR_v1)
    889 	    = obj_attr_v1_get_arg_type (stdoutput, vendor,
    890 					args_out[n - 1].val.u64);
    891 #else /* TC_OBJ_ATTR_v2 */
    892 	    = obj_attr_v2_get_arg_type (subsec->encoding);
    893 #endif
    894 	  expected_ttype |= type_attr_value;
    895 	}
    896 
    897       if (! obj_attr_parse_args (expected_ttype, true, arg_out))
    898 	{
    899 	  if (high_ttype == ATTRIBUTE_KEY)
    900 	    {
    901 	      as_bad (_("could not parse attribute tag"));
    902 	      goto bad;
    903 	    }
    904 	  else
    905 	    {
    906 	      as_bad (_("could not parse attribute value"));
    907 	      goto bad;
    908 	    }
    909 	}
    910 
    911       if (n + 1 < nargs && !skip_whitespace_past_comma ())
    912 	goto bad;
    913     }
    914 
    915   demand_empty_rest_of_line ();
    916   va_end (args);
    917   return args_out;
    918 
    919  bad:
    920   ignore_rest_of_line ();
    921   args_list_free (args_out, nargs);
    922   va_end (args);
    923   return NULL;
    924 }
    925 
    926 #if (TC_OBJ_ATTR_v1)
    927 /* Record an attribute (object attribute v1 only).  */
    928 static obj_attribute *
    929 obj_attr_v1_record (bfd *abfd,
    930 		    const obj_attr_vendor_t vendor,
    931 		    const obj_attr_tag_t tag,
    932 		    arg_t *parsed_arg)
    933 {
    934   obj_attribute *attr = bfd_elf_new_obj_attr (abfd, vendor, tag);
    935   if (attr != NULL)
    936     {
    937       attr->type = bfd_elf_obj_attrs_arg_type (abfd, vendor, tag);
    938       if (parsed_arg->vtype == VALUE_LIST)
    939 	{
    940 	  arg_variant_list_t *plist = &parsed_arg->val.list;
    941 	  gas_assert (plist->len == 2
    942 		      && plist->elts[0].vtype == VALUE_UNSIGNED_INTEGER
    943 		      && plist->elts[1].vtype == VALUE_STRING);
    944 	  attr->i = plist->elts[0].val.u64;
    945 	  attr->s = (char *) plist->elts[1].val.string;
    946 	  plist->elts[1].val.string = NULL;
    947 	}
    948       else if (parsed_arg->vtype == VALUE_STRING)
    949 	{
    950 	  attr->s = (char *) parsed_arg->val.string;
    951 	  parsed_arg->val.string = NULL;
    952 	}
    953       else
    954 	{
    955 	  attr->i = parsed_arg->val.u64;
    956 	}
    957     }
    958   return attr;
    959 }
    960 #endif /* TC_OBJ_ATTR_v1 */
    961 
    962 #if (TC_OBJ_ATTR_v2)
    963 /* Parse the arguments of [vendor]_subsection directive (v2 only).  */
    964 static arg_t *
    965 vendor_subsection_parse_args (unsigned int nargs, ...)
    966 {
    967   va_list args;
    968   va_start (args, nargs);
    969 
    970   arg_t *args_out = xcalloc (nargs, sizeof (arg_t));
    971 
    972   for (unsigned int n = 0; n < nargs; ++n)
    973     {
    974       if (! trim_whitespace_before_param ())
    975 	goto bad;
    976 
    977       arg_t *arg_out = &args_out[n];
    978 
    979       arg_token_t expected_ttype = va_arg (args, arg_token_t);
    980       arg_token_t high_ttype = (expected_ttype & HT_MASK);
    981       /* Make sure that we called the right parse_args().  */
    982       gas_assert (high_ttype == SUBSECTION_NAME
    983 		  || high_ttype == SUBSECTION_OPTION_1
    984 		  || high_ttype == SUBSECTION_OPTION_2);
    985 
    986       if (high_ttype == SUBSECTION_NAME)
    987 	{
    988 	  if (! obj_attr_parse_arg (expected_ttype, false, false, arg_out))
    989 	    {
    990 	      as_bad (_("expected <subsection_name>, <comprehension>, "
    991 			"<encoding>"));
    992 	      goto bad;
    993 	    }
    994 	}
    995       else if (high_ttype == SUBSECTION_OPTION_1
    996 	    || high_ttype == SUBSECTION_OPTION_2)
    997 	{
    998 	  if (! obj_attr_parse_arg (expected_ttype, true, true, arg_out))
    999 	    goto bad;
   1000 
   1001 	  if (arg_out->vtype == VALUE_OPTIONAL_ABSENT)
   1002 	    continue;
   1003 
   1004 	  if (high_ttype == SUBSECTION_OPTION_1
   1005 	      && ! is_valid_comprehension (arg_out->val.u64))
   1006 	    {
   1007 	      as_bad
   1008 		(_("invalid value '%" PRIu64 "', expected values for "
   1009 		   "<comprehension> are 0 (=`required') or 1 (=`optional')"),
   1010 		 arg_out->val.u64);
   1011 	      goto bad;
   1012 	    }
   1013 	  else if (high_ttype == SUBSECTION_OPTION_2
   1014 		&& ! is_valid_encoding (arg_out->val.u64))
   1015 	    {
   1016 	      as_bad
   1017 		(_("invalid value '%" PRIu64 "', expected values for <encoding>"
   1018 		   " are 0 (=`ULEB128') or 1 (=`NTBS')"),
   1019 		 arg_out->val.u64);
   1020 	      goto bad;
   1021 	    }
   1022 	}
   1023       else
   1024 	abort ();
   1025 
   1026       if (n + 1 < nargs
   1027 	  && ! is_end_of_stmt (*input_line_pointer)
   1028 	  && ! skip_whitespace_past_comma ())
   1029 	goto bad;
   1030     }
   1031 
   1032   va_end (args);
   1033   demand_empty_rest_of_line ();
   1034   return args_out;
   1035 
   1036  bad:
   1037   ignore_rest_of_line ();
   1038   args_list_free (args_out, nargs);
   1039   va_end (args);
   1040   return NULL;
   1041 }
   1042 
   1043 /* Record an attribute (object attribute v2 only).  */
   1044 static void
   1045 obj_attr_v2_record (obj_attr_tag_t key, arg_t *arg_val)
   1046 {
   1047   /* An OAv2 cannot be recorded unless a subsection has been recorded.  */
   1048   gas_assert (elf_obj_attr_subsections (stdoutput).last != NULL);
   1049 
   1050   union obj_attr_value_v2 obj_attr_val;
   1051   if (arg_val->vtype == VALUE_UNSIGNED_INTEGER)
   1052     obj_attr_val.uint = arg_val->val.u64;
   1053   else
   1054     {
   1055       /* Move the string.  */
   1056       obj_attr_val.string = arg_val->val.string;
   1057       arg_val->val.string = NULL;
   1058     }
   1059 
   1060   obj_attr_v2_t *obj_attr = bfd_elf_obj_attr_v2_init (key, obj_attr_val);
   1061   gas_assert (obj_attr != NULL);
   1062 
   1063   /* Go over the list of already recorded attributes and check for
   1064      redefinitions (which are forbidden).  */
   1065   bool skip_recording = false;
   1066   obj_attr_v2_t *recorded_attr = bfd_obj_attr_v2_find_by_tag
   1067     (elf_obj_attr_subsections (stdoutput).last, obj_attr->tag, false);
   1068   if (recorded_attr != NULL)
   1069     {
   1070       if ((arg_val->vtype == VALUE_UNSIGNED_INTEGER
   1071 	   && recorded_attr->val.uint != obj_attr->val.uint)
   1072 	  || (arg_val->vtype == VALUE_STRING
   1073 	      && strcmp (recorded_attr->val.string, obj_attr->val.string) != 0))
   1074 	as_bad (_("attribute '%" PRIu64 "' cannot be redefined"),
   1075 		recorded_attr->tag);
   1076       skip_recording = true;
   1077     }
   1078 
   1079   if (skip_recording)
   1080     {
   1081       if (arg_val->vtype == VALUE_STRING)
   1082 	free ((void *) obj_attr->val.string);
   1083       free (obj_attr);
   1084       return;
   1085     }
   1086 
   1087   bfd_obj_attr_subsection_v2_append
   1088     (elf_obj_attr_subsections (stdoutput).last, obj_attr);
   1089 }
   1090 
   1091 /* Record a subsection (object attribute v2 only).
   1092    Note: this function takes the ownership of 'name', so is responsible to free
   1093    it if an issue occurs.  */
   1094 static void
   1095 obj_attr_v2_subsection_record (const char *name,
   1096 			       arg_t *arg_comprehension,
   1097 			       arg_t *arg_encoding)
   1098 {
   1099   obj_attr_subsection_v2_t *already_recorded_subsec
   1100     = bfd_obj_attr_subsection_v2_find_by_name
   1101       (elf_obj_attr_subsections (stdoutput).first, name, false);
   1102 
   1103   bool comprehension_optional = arg_comprehension->val.u64;
   1104   obj_attr_encoding_v2_t encoding
   1105     = obj_attr_encoding_v2_from_u8 (arg_encoding->val.u64);
   1106 
   1107   if (already_recorded_subsec != NULL)
   1108     {
   1109       bool error_redeclaration = false;
   1110 
   1111       if (arg_comprehension->vtype == VALUE_OPTIONAL_ABSENT)
   1112 	gas_assert (arg_encoding->vtype == VALUE_OPTIONAL_ABSENT);
   1113       else if (comprehension_optional != already_recorded_subsec->optional)
   1114 	error_redeclaration = true;
   1115 
   1116       if (arg_encoding->vtype != VALUE_OPTIONAL_ABSENT
   1117 	  && encoding != already_recorded_subsec->encoding)
   1118 	error_redeclaration = true;
   1119 
   1120       /* Check for mismatching redefinition of the subsection, i.e. the names
   1121 	 match but the properties are different.  */
   1122       if (error_redeclaration)
   1123 	{
   1124 	  const char *prev_comprehension = bfd_oav2_comprehension_to_string (
   1125 	    already_recorded_subsec->optional);
   1126 	  const char *prev_encoding = bfd_oav2_encoding_to_string (
   1127 	    already_recorded_subsec->encoding);
   1128 	  as_bad (_("incompatible redeclaration of subsection %s"), name);
   1129 	  as_info (1, _("previous declaration had properties: %s=%s, %s=%s"),
   1130 		   "comprehension", prev_comprehension,
   1131 		   "encoding", prev_encoding);
   1132 	  goto free_name;
   1133 	}
   1134 
   1135       /* Move the existing subsection to the last position.  */
   1136       bfd_obj_attr_subsection_v2_list_remove
   1137 	(&elf_obj_attr_subsections (stdoutput), already_recorded_subsec);
   1138       bfd_obj_attr_subsection_v2_list_append
   1139 	(&elf_obj_attr_subsections (stdoutput), already_recorded_subsec);
   1140       /* Note: 'name' was unused, and will be freed on exit.  */
   1141     }
   1142   else
   1143     {
   1144       if (arg_comprehension->vtype == VALUE_OPTIONAL_ABSENT
   1145 	  || arg_encoding->vtype == VALUE_OPTIONAL_ABSENT)
   1146 	{
   1147 	  as_bad (_("comprehension and encoding of a subsection cannot be "
   1148 		    "omitted on the first declaration"));
   1149 	  goto free_name;
   1150 	}
   1151 
   1152       obj_attr_subsection_scope_v2_t scope
   1153 	= bfd_elf_obj_attr_subsection_v2_scope (stdoutput, name);
   1154 
   1155       /* Note: ownership of 'name' is transfered to the callee when initializing
   1156 	 the subsection.  That is why we skip free() at the end.  */
   1157       obj_attr_subsection_v2_t *new_subsection
   1158 	= bfd_elf_obj_attr_subsection_v2_init (name, scope,
   1159 					       comprehension_optional,
   1160 					       encoding);
   1161       bfd_obj_attr_subsection_v2_list_append
   1162 	(&elf_obj_attr_subsections (stdoutput), new_subsection);
   1163       return;
   1164     }
   1165 
   1166  free_name:
   1167   free ((void *) name);
   1168 }
   1169 #endif /* TC_OBJ_ATTR_v2 */
   1170 
   1171 /* Parse an attribute directive (supports both v1 & v2).  */
   1172 obj_attr_tag_t
   1173 obj_attr_process_attribute (obj_attr_vendor_t vendor)
   1174 {
   1175   obj_attr_version_t version = elf_obj_attr_version (stdoutput);
   1176   obj_attr_subsection_v2_t *subsec = NULL;
   1177 
   1178 #if (TC_OBJ_ATTR_v2)
   1179   if (version == OBJ_ATTR_V2)
   1180     {
   1181       subsec = elf_obj_attr_subsections (stdoutput).last;
   1182       if (subsec == NULL)
   1183 	{
   1184 	  as_bad (_("declaration of an attribute outside the scope of an "
   1185 		    "attribute subsection"));
   1186 	  ignore_rest_of_line ();
   1187 	  return 0;
   1188 	}
   1189     }
   1190 #endif /* TC_OBJ_ATTR_v2 */
   1191 
   1192   const size_t N_ARGS = 2;
   1193   arg_t *args = vendor_attribute_parse_args (
   1194     vendor, subsec, N_ARGS,
   1195     ATTRIBUTE_KEY | IDENTIFIER | UNSIGNED_INTEGER,
   1196     ATTRIBUTE_VALUE);
   1197 
   1198   if (args == NULL)
   1199     return 0;
   1200 
   1201   obj_attr_tag_t tag = args[0].val.u64;
   1202   switch (version)
   1203     {
   1204 #if (TC_OBJ_ATTR_v1)
   1205     case OBJ_ATTR_V1:
   1206       oav1_attr_record_seen (vendor, tag);
   1207       obj_attr_v1_record (stdoutput, vendor, tag, &args[1]);
   1208       break;
   1209 #endif /* TC_OBJ_ATTR_v1 */
   1210 #if (TC_OBJ_ATTR_v2)
   1211     case OBJ_ATTR_V2:
   1212       obj_attr_v2_record (tag, &args[1]);
   1213       break;
   1214 #endif /* TC_OBJ_ATTR_v2 */
   1215     default:
   1216       abort ();
   1217     }
   1218 
   1219   args_list_free (args, N_ARGS);
   1220 
   1221   return tag;
   1222 }
   1223 
   1224 #if (TC_OBJ_ATTR_v2)
   1225 /* Parse an object attribute v2's subsection directive.  */
   1226 void
   1227 obj_attr_process_subsection (void)
   1228 {
   1229   const size_t N_ARGS = 3;
   1230   arg_t *args = vendor_subsection_parse_args (
   1231     N_ARGS,
   1232     SUBSECTION_NAME | IDENTIFIER,
   1233     SUBSECTION_OPTION_1 | IDENTIFIER | UNSIGNED_INTEGER,
   1234     SUBSECTION_OPTION_2 | IDENTIFIER | UNSIGNED_INTEGER);
   1235 
   1236   if (args == NULL)
   1237     return;
   1238 
   1239   /* Note: move the value to avoid double free.  */
   1240   const char *name = args[0].val.string;
   1241   args[0].val.string = NULL;
   1242 
   1243   /* Note: ownership of 'name' is transferred to the callee.  */
   1244   obj_attr_v2_subsection_record (name, &args[1], &args[2]);
   1245   args_list_free (args, N_ARGS);
   1246 }
   1247 #endif /* TC_OBJ_ATTR_v2 */
   1248 
   1249 /* Parse a .gnu_attribute directive.  */
   1250 
   1251 void
   1252 obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
   1253 {
   1254   obj_attr_process_attribute (OBJ_ATTR_GNU);
   1255 }
   1256 
   1257 #if (TC_OBJ_ATTR_v2)
   1258 /* Return True if the VERSION of object attributes supports subsections, False
   1259    otherwise.  */
   1260 
   1261 static inline bool
   1262 attr_fmt_has_subsections (obj_attr_version_t version)
   1263 {
   1264   switch (version)
   1265   {
   1266   case OBJ_ATTR_V1:
   1267     return false;
   1268   case OBJ_ATTR_V2:
   1269     return true;
   1270   default:
   1271     abort ();  /* Unsupported format.  */
   1272   }
   1273 }
   1274 
   1275 /* Parse a .gnu_subsection directive.  */
   1276 
   1277 void
   1278 obj_elf_gnu_subsection (int ignored ATTRIBUTE_UNUSED)
   1279 {
   1280   obj_attr_version_t version = elf_obj_attr_version (stdoutput);
   1281   if (! attr_fmt_has_subsections (version))
   1282     {
   1283       as_bad (_(".gnu_subsection is only available with object attributes v2"));
   1284       ignore_rest_of_line ();
   1285       return;
   1286     }
   1287   obj_attr_process_subsection ();
   1288 }
   1289 #endif /* TC_OBJ_ATTR_v2 */
   1290 
   1291 #endif /* TC_OBJ_ATTR */
   1292