[PATCH v5 07/20] bfd: write Object Attributes v2
Matthieu Longo
matthieu.longo@arm.com
Mon Jul 7 16:49:24 GMT 2025
From: Richard Ball <richard.ball@arm.com>
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.
Today, two methods are used for the serialization:
- bfd_elf_obj_attr_size: compute the size of the section.
- bfd_elf_obj_set_obj_attr_contents: write the data to the section.
Moving the final sort for gas into set_obj_attr_contents would mean
that a sort would be performed on an already-sorted collections. In
order to avoid this useless operation at link time, we added a new
finalization step called only by gas: bfd_elf_obj_attr_finalize_content.
As previously mentioned, the logic of OAv1 and OAv2 is kept separated.
Co-Authored-By: Matthieu Longo <matthieu.longo@arm.com>
---
bfd/elf-attrs.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++--
bfd/elf-bfd.h | 1 +
gas/write.c | 2 +
3 files changed, 164 insertions(+), 5 deletions(-)
diff --git a/bfd/elf-attrs.c b/bfd/elf-attrs.c
index acf93a3cee5..9a9117caa14 100644
--- a/bfd/elf-attrs.c
+++ b/bfd/elf-attrs.c
@@ -131,13 +131,56 @@ 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 (obj_attr_v2 *attr, obj_attr_encoding_v2 type)
+{
+ bfd_vma size;
+
+ size = uleb128_size (attr->tag);
+ if (type == OA_ENC_ULEB128)
+ size += uleb128_size (attr->vals.uint_val);
+ if (type == OA_ENC_NTBS)
+ size += strlen (attr->vals.string_val) + 1; /* +1 for '\0'. */
+ return size;
+}
+
+/* Return the size of a subsection. */
+static bfd_vma
+oav2_subsection_size (obj_attr_subsection_v2 *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 (obj_attr_v2 *attr = subsec->first_;
+ attr != NULL;
+ attr = attr->next)
+ size += oav2_attr_size (attr, subsec->encoding);
+ return size;
+}
+
+/* Return the size of a build attributes section. */
+static bfd_vma
+oav2_section_size (bfd *abfd)
+{
+ obj_attr_subsection_v2 *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 +188,15 @@ 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);
+ if (version == OBJ_ATTR_V1)
+ return oav1_section_size (abfd);
+ else if (version == OBJ_ATTR_V2)
+ return oav2_section_size (abfd);
+ else if (version == OBJ_ATTR_VERSION_NONE)
+ return 0;
+ else
+ abort ();
}
/* Write VAL in uleb128 format to P, returning a pointer to the
@@ -228,7 +279,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)
{
bfd_byte *p = buffer;
@@ -248,11 +299,116 @@ 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, obj_attr_v2 *attr, obj_attr_encoding_v2 type)
+{
+ p = write_uleb128 (p, attr->tag);
+ switch (type)
+ {
+ case OA_ENC_ULEB128:
+ p = write_uleb128 (p, attr->vals.uint_val);
+ break;
+ case OA_ENC_NTBS:
+ {
+ size_t len = strlen (attr->vals.string_val) + 1; /* +1 for '\0'. */
+ memcpy (p, attr->vals.string_val, len);
+ p += len;
+ }
+ break;
+ default:
+ abort ();
+ }
+ return p;
+}
+
+static bfd_byte *
+oav2_write_subsection (bfd *abfd,
+ obj_attr_subsection_v2 *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> */
+ p = write_uleb128 (p, subsec->optional);
+ /* <encoding: uint8> */
+ p = write_uleb128 (p, obj_attr_encoding_v2_to_u8 (subsec->encoding));
+ /* <attribute>* */
+ for (obj_attr_v2 *attr = subsec->first_; attr != NULL; attr = attr->next)
+ p = oav2_write_attr (p, attr, subsec->encoding);
+ return p;
+}
+
+static void
+oav2_sort_subsections (obj_attr_subsection_list *plist)
+{
+ for (obj_attr_subsection_v2 *subsec = plist->first_;
+ subsec != NULL;
+ subsec = subsec->next)
+ LINKED_LIST_MERGE_SORT(obj_attr_v2) (subsec, _bfd_elf_obj_attr_v2_cmp);
+
+ LINKED_LIST_MERGE_SORT(obj_attr_subsection_v2)
+ (plist, _bfd_elf_obj_attr_subsection_v2_cmp);
+}
+
+static void
+oav2_write_section (bfd *abfd, bfd_byte *buffer, bfd_vma size)
+{
+ bfd_vma section_size = oav2_section_size (abfd);
+ if (section_size == 0)
+ return;
+
+ 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 (obj_attr_subsection_v2 *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 == (long unsigned int) (p - buffer));
+}
+
+/* Finalize the content of object attributes before writing. */
+void
+bfd_elf_obj_attr_finalize_content (bfd *abfd)
+{
+ if (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. */
+ oav2_sort_subsections (&elf_obj_attr_subsections (abfd));
+}
+
/* 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);
+ obj_attr_version_t version = elf_obj_attr_version (abfd);
+ if (version == OBJ_ATTR_V1)
+ oav1_write_section (abfd, buffer, size);
+ else if (version == OBJ_ATTR_V2)
+ oav2_write_section (abfd, buffer, size);
+ else
+ abort ();
}
/* The first two tags in gnu-testing namespace are known, and so have a name and
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index c17540c819d..4b1f2fc9c8d 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -3098,6 +3098,7 @@ extern bfd *_bfd_elf64_bfd_from_remote_memory
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_obj_attr_finalize_content (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);
diff --git a/gas/write.c b/gas/write.c
index 4d95d769d45..2ec181f091c 100644
--- a/gas/write.c
+++ b/gas/write.c
@@ -1919,6 +1919,8 @@ create_obj_attrs_section (void)
bfd_set_section_flags (s, SEC_READONLY | SEC_DATA);
frag_now_fix ();
char *p = frag_more (size);
+
+ bfd_elf_obj_attr_finalize_content (stdoutput);
bfd_elf_set_obj_attr_contents (stdoutput, (bfd_byte *)p, size);
subsegs_finish_section (s);
--
2.50.0
More information about the Binutils
mailing list