[PATCH v12 03/25] gas: implement parsing of object attributes v2

Matthieu Longo matthieu.longo@arm.com
Tue Jan 20 15:29:13 GMT 2026


On 19/01/2026 11:32, Jan Beulich wrote:
> On 16.01.2026 19:59, Matthieu Longo wrote:
>> --- a/gas/config/obj-elf-attr.c
>> +++ b/gas/config/obj-elf-attr.c
>> @@ -22,8 +22,63 @@
>>   
>>   #ifdef TC_OBJ_ATTR
>>   
>> +#include "obstack.h"
>>   #include "safe-ctype.h"
>>   
>> +/* A variant type to store information about known OAv2 identifiers.  */
>> +typedef union {
>> +  uint8_t u8;
>> +  bool b;
>> +} oav2_identifier_variant_value_t;
>> +
>> +typedef enum {
>> +  OAv2_ASM_ID_VALUE_UNDEFINED = 0,
>> +  OAv2_ASM_ID_VALUE_U8,
>> +  OAv2_ASM_ID_VALUE_BOOL,
>> +} oav2_identifier_variant_type_info_t;
>> +
>> +typedef struct {
>> +  oav2_identifier_variant_value_t val;
>> +  oav2_identifier_variant_type_info_t vtype;
>> +} oav2_identifier_variant_t;
>> +
>> +typedef struct {
>> +  const char *const name;
>> +  const oav2_identifier_variant_t value;
>> +} oav2_identifier_t;
>> +
>> +
>> +typedef struct {
> 
> Nit: No double blank lines please.
> 

Fixed.

>> +/* Resolve the identifier if it matches the given symbol.  */
>> +static bool
>> +resolve_if_matching (const char *identifier,
>> +		     const oav2_identifier_t *known_identifier,
>> +		     arg_t *val_out)
> 
> As to the comment: What does "given symbol" refer to? I'm inclined to guess
> it's "known_identifier", but then the comment may better say "... matches
> the given one"?
> 

Fixed.

>> +#if (TC_OBJ_ATTR_v1)
>> +/* Look up attribute tags defined in the backend (object attribute v1).  */
>> +static bool
>> +obj_attr_v1_lookup_known_attr_tag_symbol
>> +  (const char *identifier  ATTRIBUTE_UNUSED,
> 
> Nit: One too many padding blanks.
> 

Fixed.

>> +/* In the context of object attributes, an identifier is defined with the
>> +   following lexical constraint: [a-zA-z_][a-zA-Z0-9_].  An identifier can
> 
> Maybe better add a * or + at the end of the pattern, to make clear identifiers
> can consist of other than exactly two characters?
> 

Yes, there was a * missing. Fixed.

>> +/* Parse an argument, and set its type accordingly depending on the input
>> +   value, and the constraints on the expected argument.  */
>> +static bool
>> +obj_attr_parse_arg (arg_token_t expected_ttype,
>> +		    bool resolve_identifier,
>> +		    bool optional,
>> +		    arg_t *arg_out)
>> +{
>> +  const arg_token_t low_ttype = (expected_ttype & LT_MASK);
>> +
>> +  if (optional && is_end_of_stmt (*input_line_pointer))
>> +    {
>> +      arg_out->vtype = VALUE_OPTIONAL_ABSENT;
>> +      return true;
>> +    }
>> +
>> +  /* Check whether this looks like a string literal
>> +     Note: symbol look-up for string literals is not available.  */
>> +  if (*input_line_pointer == '"')
>> +    {
>> +      bool status = extract_string_literal (arg_out);
>> +      if (status && (low_ttype & STRING))
>> +	return true;
>> +
>> +      if (status)
>> +	{
>> +	  char sbuffer[100];
>> +	  as_bad (_("unexpected `string' \"%s\", expected %s instead"),
>> +		  arg_out->val.string,
>> +		  expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
>> +	  free ((char *) arg_out->val.string);
>> +	  arg_out->val.string = NULL;
>> +	  arg_out->vtype = VALUE_UNDEFINED;
>> +	}
>> +      return false;
>> +    }
>> +
>> +  /* Check whether this looks like an identifier.  */
>> +  if (is_identifier_beginner (*input_line_pointer))
>> +    {
>> +      bool status = extract_identifier (is_part_of_identifier, arg_out);
>> +      /* match_identifier() confirmed that it was the beginning of an
>> +	 identifier, so we don't expect the extraction to fail.  */
> 
> What is match_identifier()? Is this stale and means to say is_identifier_beginner()?
> 

Yes, it was stale. I meant is_identifier_beginner().
Fixed.

>> +      gas_assert (status);
>> +      gas_assert (arg_out->vtype == VALUE_STRING);
>> +
>> +      if (! (low_ttype & IDENTIFIER))
>> +	{
>> +	  char sbuffer[100];
>> +	  as_bad (_("unexpected `identifier' \"%s\", expected %s instead"),
>> +		  arg_out->val.string,
>> +		  expectations_to_string (low_ttype, sbuffer, sizeof(sbuffer)));
>> +	  free ((char *) arg_out->val.string);
>> +	  arg_out->val.string = NULL;
>> +	  arg_out->vtype = VALUE_UNDEFINED;
>> +	  return false;
>> +	}
>> +
>> +      /* In some cases, we don't want to resolve the identifier because it is the
>> +	 actual value.  */
>> +      if (! resolve_identifier)
>> +	return true;
>> +
>> +      /* Move the identifier out of arg_out.  */
>> +      const char *identifier = arg_out->val.string;
>> +      arg_out->val.string = NULL;
> 
> With this, ...
> 
>> +      bool resolved = true;
>> +
>> +      /* The identifier is a symbol, let's try to resolve it by:
>> +	 1. using the provided list of known symbols.
>> +	   a) backend-independent
>> +	   b) backend-specific.  */
>> +      if (lookup_known_symbols (identifier, expected_ttype, arg_out))
>> +	goto free_identifier;
>> +
>> +      /* 2. using the symbol table for this compilation unit.
>> +	 Note: this is the last attempt before failure.  */
>> +      if (lookup_symbol_table (identifier, low_ttype, arg_out))
>> +	goto free_identifier;
>> +
>> +      as_bad (_("unknown identifier '%s' in this context"), identifier);
>> +      arg_out->val.string = NULL;
> 
> ... why this?
> 

Indeed it is not needed, it is a duplicate and can be removed.
However, on the previous occurrence, it lacks 'arg_out->vtype = VALUE_UNDEFINED;' so I will move it next the previous occurrence of 'arg_out->val.string = NULL;'.

>> +      arg_out->vtype = VALUE_UNDEFINED;
>> +      resolved = false;
>> +
>> + free_identifier:
>> +      free ((char *) identifier);
>> +      return resolved;
>> +    }
>> +
>> +  /* If it is neither a string nor an identifier, it must be an expression.  */
>> +  bool signedness_issue = false;
>> +  bool success = extract_integer_literal (arg_out,
>> +					  (low_ttype & UNSIGNED_INTEGER),
>> +					  &signedness_issue);
>> +  if (success && (low_ttype & (UNSIGNED_INTEGER | SIGNED_INTEGER)))
>> +    return true;
>> +
>> +  char sbuffer[100];
>> +  if (success)
>> +    as_bad (_("unexpected integer '%lu', expected %s instead"),
>> +	    arg_out->val.u64,
> 
> As indicated before, %lu and alike aren't suitable to print uint64_t values.
> 

Fixed.

>> +/* Can parse a list of arguments with variable length.  */
>> +static bool
>> +obj_attr_parse_args (arg_token_t expected_ttype,
>> +		     bool resolve_identifier,
>> +		     arg_t *arg_out)
>> +{
>> +  if ((expected_ttype & LIST) == 0)
>> +    return obj_attr_parse_arg (expected_ttype, resolve_identifier, false,
>> +			       arg_out);
>> +
>> +  static const size_t LIST_MAX_SIZE = 2;
>> +  arg_t *arg_list = xcalloc (LIST_MAX_SIZE, sizeof (*arg_list));
>> +
>> +  /* We don't want to support recursive lists.  */
>> +  expected_ttype &= ~LIST;
>> +
>> +  size_t n = 0;
>> +  do {
>> +    if (! trim_whitespace_before_param ())
>> +      goto bad;
>> +
>> +    if (! obj_attr_parse_arg (expected_ttype, resolve_identifier, false,
>> +			      &arg_list[n]))
>> +      goto bad;
>> +
>> +    ++n;
>> +    skip_whitespace (input_line_pointer);
>> +    if (is_end_of_stmt (*input_line_pointer))
>> +      break;
>> +
>> +    if (! skip_whitespace_past_comma ())
>> +      goto bad;
>> +
>> +    if (n >= LIST_MAX_SIZE)
>> +      {
>> +	as_bad ("too many arguments for a list (max: %lu)", LIST_MAX_SIZE);
> 
> %zu as LIST_MAX_SIZE is size_t.
> 

Fixed.

>> +/* Parse the arguments of [vendor]_attribute directive.  */
>> +static arg_t *
>> +vendor_attribute_parse_args (obj_attr_vendor_t vendor ATTRIBUTE_UNUSED,
>> +			     const obj_attr_subsection_v2_t *subsec ATTRIBUTE_UNUSED,
>> +			     unsigned int nargs, ...)
>> +{
>> +  va_list args;
>> +  va_start (args, nargs);
>> +
>> +  arg_t *args_out = xcalloc (nargs, sizeof (arg_t));
>> +
>> +  for (unsigned int n = 0; n < nargs; ++n)
>> +    {
>> +      if (! trim_whitespace_before_param ())
>> +	goto bad;
>> +
>> +      arg_t *arg_out = &args_out[n];
>> +
>> +      arg_token_t expected_ttype = va_arg (args, arg_token_t);
>> +      arg_token_t high_ttype = (expected_ttype & HT_MASK);
>> +      /* Make sure that we called the right parse_args().  */
>> +      gas_assert (high_ttype == ATTRIBUTE_KEY
>> +	       || high_ttype == ATTRIBUTE_VALUE);
>> +
>> +      if (high_ttype == ATTRIBUTE_VALUE)
>> +	{
>> +	  arg_token_t type_attr_value
>> +#if (TC_OBJ_ATTR_v1 && TC_OBJ_ATTR_v2)
>> +	    = (subsec != NULL)
>> +	      ? obj_attr_v2_get_arg_type (subsec->encoding)
>> +	      : obj_attr_v1_get_arg_type (stdoutput, vendor,
>> +					  args_out[n-1].val.u64);
> 
> Nit: [n - 1] please.
> 

Fixed.

>> +#elif (TC_OBJ_ATTR_v1)
>> +	    = obj_attr_v1_get_arg_type (stdoutput, vendor,
>> +					args_out[n-1].val.u64);
> 
> Again.
> 

Fixed.

> As I'd like to save another round trip: Okay with all remarks suitably
> addressed (either verbally or by making respective adjustments). And again
> at least an Arm64 will be needed here as well (if you haven't got one
> already).
> 
> Jan

Matthieu


More information about the Binutils mailing list