[binutils-gdb] bfd: write Object Attributes v2

Matthieu Longo mlongo@sourceware.org
Thu Jan 22 10:12:25 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=aa7eec04f8ce8be1ee004a2e32f5aa908a8c9691

commit aa7eec04f8ce8be1ee004a2e32f5aa908a8c9691
Author: Richard Ball <richard.ball@arm.com>
Date:   Thu May 1 17:34:33 2025 +0100

    bfd: write Object Attributes v2
    
    This patch adds the serialization logic to write Object Attributes v2 (OAv2)
    into the ELF section specified by the backend (.ARM.attributes for AARch64).
    
    OAv2, once processed by the parser, are stored in the order they were
    declared in the assembly, so not necessarily sorted. Sorting them
    simplifies the comparison of the data between several files. Since
    sorting OAv2 is clearly not the responsibility of readelf, they need
    to be sorted by the producers (i.e. gas or ld).
    The linker sorts the subsections and attributes as a pre-requisite of
    the merge. Since the merge preserves the ordering, no final sort is
    required before the serialization, contrarilly to the assembler.
    
    Moving the final sort for gas into set_obj_attr_contents would mean
    that a sort would be performed on an already-sorted collections. To
    avoid this redundant operation at link time, we check whether the
    output BFD is a linker output (is_linker_output = 1). If it is not
    (i.e., it is gas), we perform a finalization step that sorts the
    subsections and attributes before writing.
    
    As previously mentioned, the logic of OAv1 and OAv2 is kept separated.
    
    Co-Authored-By: Matthieu Longo <matthieu.longo@arm.com>

Diff:
---
 bfd/elf-attrs.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 183 insertions(+), 5 deletions(-)

diff --git a/bfd/elf-attrs.c b/bfd/elf-attrs.c
index 28046ec2e2e..9b1cd16bd72 100644
--- a/bfd/elf-attrs.c
+++ b/bfd/elf-attrs.c
@@ -131,13 +131,62 @@ vendor_obj_attrs_v1_size (bfd *abfd, int vendor)
 }
 
 static bfd_vma
-bfd_elf_obj_attrs_v1_size (bfd *abfd)
+oav1_section_size (bfd *abfd)
 {
   bfd_vma size = 0;
   size = vendor_obj_attrs_v1_size (abfd, OBJ_ATTR_PROC);
   size += vendor_obj_attrs_v1_size (abfd, OBJ_ATTR_GNU);
   if (size > 0)
-    size += sizeof(uint8_t); /* <format-version: ‘A’>  */
+    size += sizeof (uint8_t); /* <format-version: uint8>  */
+  return size;
+}
+
+/* Return the size of a single attribute.  */
+static bfd_vma
+oav2_attr_size (const obj_attr_v2_t *attr, obj_attr_encoding_v2_t type)
+{
+  bfd_vma size = uleb128_size (attr->tag);
+  switch (type)
+    {
+    case OA_ENC_ULEB128:
+      size += uleb128_size (attr->val.uint);
+      break;
+    case OA_ENC_NTBS:
+      size += strlen (attr->val.string) + 1; /* +1 for '\0'.  */
+      break;
+    default:
+      abort ();
+    }
+  return size;
+}
+
+/* Return the size of a subsection.  */
+static bfd_vma
+oav2_subsection_size (const obj_attr_subsection_v2_t *subsec)
+{
+  bfd_vma size = sizeof (uint32_t); /* <subsection-length: uint32>  */
+  size += strlen (subsec->name) + 1; /* <subsection-name: NTBS>  so +1 for '\0'.  */
+  size += 2 * sizeof (uint8_t); /* <optional: uint8> <encoding: uint8>  */
+  /* <attribute>*  */
+  for (const obj_attr_v2_t *attr = subsec->first;
+       attr != NULL;
+       attr = attr->next)
+    size += oav2_attr_size (attr, subsec->encoding);
+  return size;
+}
+
+/* Return the size of a object attributes section.  */
+static bfd_vma
+oav2_section_size (bfd *abfd)
+{
+  const obj_attr_subsection_v2_t *subsec
+    = elf_obj_attr_subsections (abfd).first;
+  if (subsec == NULL)
+    return 0;
+
+  bfd_vma size = sizeof (uint8_t); /* <format-version: uint8>  */
+  for (; subsec != NULL; subsec = subsec->next)
+    size += oav2_subsection_size (subsec);
   return size;
 }
 
@@ -145,7 +194,18 @@ bfd_elf_obj_attrs_v1_size (bfd *abfd)
 bfd_vma
 bfd_elf_obj_attr_size (bfd *abfd)
 {
-  return bfd_elf_obj_attrs_v1_size (abfd);
+  obj_attr_version_t version = elf_obj_attr_version (abfd);
+  switch (version)
+    {
+    case OBJ_ATTR_V1:
+      return oav1_section_size (abfd);
+    case OBJ_ATTR_V2:
+      return oav2_section_size (abfd);
+    case OBJ_ATTR_VERSION_NONE:
+      return 0;
+    default:
+      abort ();
+    }
 }
 
 /* Write VAL in uleb128 format to P, returning a pointer to the
@@ -228,7 +288,7 @@ write_vendor_obj_attrs_v1 (bfd *abfd, bfd_byte *contents, bfd_vma size,
 }
 
 static void
-write_obj_attr_section_v1 (bfd *abfd, bfd_byte *buffer, bfd_vma size)
+oav1_write_section (bfd *abfd, bfd_byte *buffer, bfd_vma size)
 {
   /* This function should only be called for object attributes version 1.  */
   BFD_ASSERT (elf_obj_attr_version (abfd) == OBJ_ATTR_V1);
@@ -251,11 +311,129 @@ write_obj_attr_section_v1 (bfd *abfd, bfd_byte *buffer, bfd_vma size)
   BFD_ASSERT (p <= buffer + size);
 }
 
+static bfd_byte *
+oav2_write_attr (bfd_byte *p,
+		 const obj_attr_v2_t *attr,
+		 obj_attr_encoding_v2_t type)
+{
+  p = write_uleb128 (p, attr->tag);
+  switch (type)
+    {
+    case OA_ENC_ULEB128:
+      p = write_uleb128 (p, attr->val.uint);
+      break;
+    case OA_ENC_NTBS:
+      /* +1 for '\0'.  */
+      p = (bfd_byte *) stpcpy ((char *) p, attr->val.string) + 1;
+      break;
+    default:
+      abort ();
+    }
+  return p;
+}
+
+static bfd_byte *
+oav2_write_subsection (bfd *abfd,
+		       const obj_attr_subsection_v2_t *subsec,
+		       bfd_byte *p)
+{
+  /* <subsection-length: uint32>  */
+  bfd_vma subsec_size = oav2_subsection_size (subsec);
+  bfd_put_32 (abfd, subsec_size, p);
+  p += sizeof (uint32_t);
+
+  /* <vendor-name: NTBS>  */
+  size_t vendor_name_size = strlen (subsec->name) + 1; /* +1 for '\0'.  */
+  memcpy (p, subsec->name, vendor_name_size);
+  p += vendor_name_size;
+
+  /* -- <vendor-data: bytes> --  */
+  /* <optional: uint8>  */
+  bfd_put_8 (abfd, subsec->optional, p);
+  ++p;
+  /* <encoding: uint8>  */
+  bfd_put_8 (abfd, obj_attr_encoding_v2_to_u8 (subsec->encoding), p);
+  ++p;
+  /* <attribute>*  */
+  for (const obj_attr_v2_t *attr = subsec->first;
+       attr != NULL;
+       attr = attr->next)
+    p = oav2_write_attr (p, attr, subsec->encoding);
+  return p;
+}
+
+static bfd_vma
+oav2_write_section (bfd *abfd, bfd_byte *buffer, bfd_vma size)
+{
+  /* This function should only be called for object attributes version 2.  */
+  BFD_ASSERT (elf_obj_attr_version (abfd) == OBJ_ATTR_V2);
+
+  bfd_vma section_size = oav2_section_size (abfd);
+  if (section_size == 0)
+    return 0;
+
+  bfd_byte *p = buffer;
+
+  const struct elf_backend_data *be = get_elf_backend_data (abfd);
+  /* <format-version: uint8>  */
+  *(p++) = be->obj_attrs_version_enc (elf_obj_attr_version (abfd));
+
+  /* [ <subsection-length: uint32> <vendor-name: NTBS> <vendor-data: bytes> ]*  */
+  for (const obj_attr_subsection_v2_t *subsec
+	 = elf_obj_attr_subsections (abfd).first;
+       subsec != NULL;
+       subsec = subsec->next)
+    p = oav2_write_subsection (abfd, subsec, p);
+
+  /* We didn't overrun the buffer.  */
+  BFD_ASSERT (p <= buffer + size);
+  /* We wrote as many data as it was computed by
+     vendor_section_obj_attr_using_subsections_size().  */
+  BFD_ASSERT (section_size == (bfd_vma) (p - buffer));
+
+  return section_size;
+}
+
+static void
+oav2_sort_subsections (obj_attr_subsection_list_t *plist)
+{
+  for (obj_attr_subsection_v2_t *subsec = plist->first;
+       subsec != NULL;
+       subsec = subsec->next)
+    LINKED_LIST_MERGE_SORT (obj_attr_v2_t) (subsec, _bfd_elf_obj_attr_v2_cmp);
+
+  LINKED_LIST_MERGE_SORT (obj_attr_subsection_v2_t)
+    (plist, _bfd_elf_obj_attr_subsection_v2_cmp);
+}
+
 /* Write the contents of the object attributes section to CONTENTS.  */
 void
 bfd_elf_set_obj_attr_contents (bfd *abfd, bfd_byte *buffer, bfd_vma size)
 {
-  write_obj_attr_section_v1 (abfd, buffer, size);
+  if (! abfd->is_linker_output
+      && elf_obj_attr_version (abfd) == OBJ_ATTR_V2)
+    /* Before dumping the data, sort subsections in alphabetical order, and
+       attributes according to their tag in numerical order.  This is useful
+       for diagnostic tools so that they dump the same output even if the
+       subsections or their attributes were not declared in the same order in
+       different files.
+       This sorting is only performed in the case of the assembler.  In the
+       case of the linker, the subsections and attributes are already sorted
+       by the merge process.  */
+    oav2_sort_subsections (&elf_obj_attr_subsections (abfd));
+
+  obj_attr_version_t version = elf_obj_attr_version (abfd);
+  switch (version)
+    {
+    case OBJ_ATTR_V1:
+      oav1_write_section (abfd, buffer, size);
+      break;
+    case OBJ_ATTR_V2:
+      oav2_write_section (abfd, buffer, size);
+      break;
+    default:
+      abort ();
+    }
 }
 
 /* The first two tags in gnu-testing namespace are known (from the perspective


More information about the Binutils-cvs mailing list