AVR: Add support for .gnu_attribute 4 (Tag_GNU_AVR_VTABLE_AS)

Message ID 701a37e4-9249-4c5e-abf5-de05c2cc3bec@gjlay.de
State New
Headers
Series AVR: Add support for .gnu_attribute 4 (Tag_GNU_AVR_VTABLE_AS) |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply

Commit Message

Georg-Johann Lay July 2, 2026, 5:27 p.m. UTC
  The upcoming support for ISO/IEC TR 18037 "Embedded C" named
address spaces in G++ would allow to put C++ virtual tables
into such an address spaces, see https://gcc.gnu.org/PR69549.

The current address space used for vtables is the generic
one, which means that RAM is wasted on machines where
.rodata is located in RAM.

The TARGET_ADDR_SPACE_FOR_ARTIFICIAL_RODATA GCC target hook
allows to chose a custom vtable address space. However, in
contrast to current user-selectable address spaces, placing
vtables in a different address space is an ABI change, so
that a means to detect ABI violations is highly appreciated.

The feature of choice is .gnu_attribute 4 (Tag_GNU_AVR_VTABLE_AS)
which would tag an object file with one of the following values:

0 (Val_GNU_AVR_VTABLE_NONE) for files that neither define nor access 
vtables.
1 (Val_GNU_AVR_VTABLE_RAM) use the generic address space.
2 (Val_GNU_AVR_VTABLE_FLASH) use the 16-bit address space __flash.
3 (Val_GNU_AVR_VTABLE_FLASH1) use the 16-bit address space __flash1.
4 (Val_GNU_AVR_VTABLE_FLASH2) use the 16-bit address space __flash2.
5 (Val_GNU_AVR_VTABLE_FLASH3) use the 16-bit address space __flash3.
6 (Val_GNU_AVR_VTABLE_FLASH4) use the 16-bit address space __flash4.
7 (Val_GNU_AVR_VTABLE_FLASH5) use the 16-bit address space __flash5.
8 (Val_GNU_AVR_VTABLE_FLASHX) use the 24-bit address space __flashx.

A tag value of zero is compatible with all other tag values.
Two non-zero tag values are compatible iff they are the same.

Unknown tag values are silently accepted an treated just like
the values above so as to allow for compatibility with future
extensions.

Ok for trunk?

Johann

--

	PR ld/34305
include/
	* elf/avr.h (libiberty.h): Include for ARRAY_SIZE.
	(Tag_GNU_AVR_VTABLE_AS = 4): New enum.
	(Val_GNU_AVR_VTABLE_NONE, Val_GNU_AVR_VTABLE_RAM)
	(Val_GNU_AVR_VTABLE_FLASH, Val_GNU_AVR_VTABLE_FLASH1)
	(Val_GNU_AVR_VTABLE_FLASH2, Val_GNU_AVR_VTABLE_FLASH3)
	(Val_GNU_AVR_VTABLE_FLASH4, Val_GNU_AVR_VTABLE_FLASH5)
	(Val_GNU_AVR_VTABLE_FLASHX, Val_GNU_AVR_VTABLE_Sentinel): New
	enum values for Tag_GNU_AVR_VTABLE_AS.
	(avr_tag_vtable_as_name): New static function.

bfd/
	* elf32-avr.h (bfd_avr_elf_merge_private_bfd_data): New proto.
	* elf32-avr.c (libiberty.h): Include for ARRAY_SIZE.
	(avr_elf_merge_obj_attributes): New static function.
	(bfd_avr_elf_merge_private_bfd_data): New function that calls it.
	(bfd_elf32_bfd_merge_private_bfd_data): Define to
	bfd_avr_elf_merge_private_bfd_data.

gas/
	* doc/as.texi (GNU Object Attributes) [AVR Attributes]: New subsection.

ld/
	* testsuite/ld-avr/attr-gnu-4-1.s: New source.
	* testsuite/ld-avr/attr-gnu-4-2.s: New source.
	* testsuite/ld-avr/attr-gnu-4-1.d: New test.
	* testsuite/ld-avr/attr-gnu-4-2.d: New test.
	* testsuite/ld-avr/attr-gnu-4-1_2.d: New test.

binutils/
	* readelf.c (display_avr_gnu_attribute): New static function.
	(process_arch_specific) <EM_AVR>: Call process_attributes
	with display_avr_gnu_attribute as a callback.
  

Patch

diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c
index 6653e4d72f6..2a0cc339511 100644
--- a/bfd/elf32-avr.c
+++ b/bfd/elf32-avr.c
@@ -25,6 +25,7 @@ 
 #include "elf-bfd.h"
 #include "elf/avr.h"
 #include "elf32-avr.h"
+#include "libiberty.h"
 
 /* Enable debugging printout at stdout with this variable.  */
 static bool debug_relax = false;
@@ -4246,6 +4247,67 @@  avr_elf32_property_record_name (struct avr_property_record *rec)
 }
 
 
+/* Merge object attributes from IBFD into OBFD.  Error if there are
+   conflicting attributes.  The follwing attributes are supported:
+   Tag_GNU_AVR_VTABLE_AS
+      One plus avr-g++'s named address-space for VTABLEs.
+*/
+
+static bool
+avr_elf_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+  static bfd *last_fp;
+  obj_attribute *in_attr, *in_attrs;
+  obj_attribute *out_attr, *out_attrs;
+  bfd *obfd = info->output_bfd;
+
+  in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+  out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+
+  in_attr = &in_attrs[Tag_GNU_AVR_VTABLE_AS];
+  out_attr = &out_attrs[Tag_GNU_AVR_VTABLE_AS];
+
+  if (in_attr->i == Val_GNU_AVR_VTABLE_NONE
+      || out_attr->i == Val_GNU_AVR_VTABLE_NONE)
+    {
+      if (in_attr->i != Val_GNU_AVR_VTABLE_NONE)
+	{
+	  out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
+	  out_attr->i = in_attr->i;
+	  last_fp = ibfd;
+	}
+    }
+  else if (in_attr->i != out_attr->i)
+    {
+      const char *const tag = "Tag_GNU_AVR_VTABLE_AS";
+      const char *const iname = avr_tag_vtable_as_name (in_attr->i);
+      const char *const oname = avr_tag_vtable_as_name (out_attr->i);
+
+      _bfd_error_handler
+	/* xgettext:c-format */
+	(_("%pB uses %s tag %d (%s), %pB uses %s tag %d (%s)"),
+	 ibfd, tag, in_attr->i, iname,
+	 last_fp, tag, out_attr->i, oname);
+
+      out_attr->type = ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_ERROR;
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  // Merge any common GNU attributes.
+  return _bfd_elf_merge_object_attributes (ibfd, info);
+}
+
+
+/* Merge backend specific data from an object file to the output
+   object file when linking.  */
+
+bool bfd_avr_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
+{
+  return avr_elf_merge_obj_attributes (ibfd, info);
+}
+
+
 #define ELF_ARCH		bfd_arch_avr
 #define ELF_TARGET_ID		AVR_ELF_DATA
 #define ELF_MACHINE_CODE	EM_AVR
@@ -4270,6 +4332,8 @@  avr_elf32_property_record_name (struct avr_property_record *rec)
 #define bfd_elf32_bfd_get_relocated_section_contents \
 					elf32_avr_get_relocated_section_contents
 #define bfd_elf32_new_section_hook	elf_avr_new_section_hook
+#define bfd_elf32_bfd_merge_private_bfd_data \
+					bfd_avr_elf_merge_private_bfd_data
 #define elf_backend_special_sections	elf_avr_special_sections
 
 #include "elf32-target.h"
diff --git a/bfd/elf32-avr.h b/bfd/elf32-avr.h
index 46e69b8b447..1b822c6adbb 100644
--- a/bfd/elf32-avr.h
+++ b/bfd/elf32-avr.h
@@ -36,6 +36,9 @@  elf32_avr_size_stubs (bfd *, struct bfd_link_info *, bool);
 extern bool
 elf32_avr_build_stubs (struct bfd_link_info *);
 
+extern bool
+bfd_avr_elf_merge_private_bfd_data (bfd *, struct bfd_link_info *);
+
 /* The name of the section into which the property records are stored.  */
 #define AVR_PROPERTY_RECORD_SECTION_NAME ".avr.prop"
 
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 686a16c60fc..1ed1ee61e6f 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -18249,6 +18249,30 @@  display_arm_attribute (unsigned char * p,
   return display_tag_value (tag, p, end);
 }
 
+static unsigned char *
+display_avr_gnu_attribute (unsigned char * p,
+			   unsigned int tag,
+			   const unsigned char * const end)
+{
+  if (tag == Tag_GNU_AVR_VTABLE_AS)
+    {
+      unsigned int val;
+
+      printf ("  Tag_GNU_AVR_VTABLE_AS (%u): ", tag);
+      if (p == end)
+	{
+	  printf (_("<corrupt>\n"));
+	  return p;
+	}
+
+      READ_ULEB (val, p, end);
+      printf ("%d (%s)\n", val, avr_tag_vtable_as_name (val));
+      return p;
+    }
+
+  return display_tag_value (tag & 1, p, end);
+}
+
 static unsigned char *
 display_gnu_attribute (unsigned char * p,
 		       unsigned char * (* display_proc_gnu_attribute)
@@ -23449,7 +23473,9 @@  process_arch_specific (Filedata * filedata)
       return process_attributes (filedata, "aeabi", SHT_ARM_ATTRIBUTES,
 				 display_arm_attribute,
 				 display_generic_attribute);
-
+    case EM_AVR:
+	return process_attributes (filedata, NULL, SHT_GNU_ATTRIBUTES, NULL,
+				   display_avr_gnu_attribute);
     case EM_MIPS:
     case EM_MIPS_RS3_LE:
       return process_mips_specific (filedata);
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 1cb1fd879a4..8e714d9f2a1 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -8000,6 +8000,25 @@  than 1, the file can only be processed by other toolchains under some private
 arrangement indicated by the flag value and the vendor name.
 @end table
 
+@subsection AVR Attributes
+
+@table @r
+@item Tag_GNU_AVR_VTABLE_AS (4)
+The @uref{https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#AVR-Named-Address-Spaces,,named address space}
+for C++ virtual tables used by this object file:
+@itemize @bullet
+@item 0 for files that neither define nor access virtual tables.
+@item 1 for files that use the generic address space.
+@item 2 for files that use the 16-bit address space @code{__flash}.
+@item 3 for files that use the 16-bit address space @code{__flash1}.
+@item 4 for files that use the 16-bit address space @code{__flash2}.
+@item 5 for files that use the 16-bit address space @code{__flash3}.
+@item 6 for files that use the 16-bit address space @code{__flash4}.
+@item 7 for files that use the 16-bit address space @code{__flash5}.
+@item 8 for files that use the 24-bit address space @code{__flashx}.
+@end itemize
+@end table
+
 @subsection M680x0 Attributes
 
 @table @r
diff --git a/include/elf/avr.h b/include/elf/avr.h
index e60ebc7fb24..60f9e97a1dd 100644
--- a/include/elf/avr.h
+++ b/include/elf/avr.h
@@ -22,6 +22,7 @@ 
 #define _ELF_AVR_H
 
 #include "elf/reloc-macros.h"
+#include "libiberty.h" // ARRAY_SIZE
 
 /* Processor specific flags for the ELF header e_flags field.  */
 #define EF_AVR_MACH 0x7F
@@ -90,4 +91,76 @@  START_RELOC_NUMBERS (elf_avr_reloc_type)
      RELOC_NUMBER (R_AVR_32_PCREL,             36)
 END_RELOC_NUMBERS (R_AVR_max)
 
+
+// Object attribute tags.
+enum
+{
+  // 0-3 are generic.
+
+  // VTABLE is located in some named address space.
+  Tag_GNU_AVR_VTABLE_AS = 4,
+};
+
+
+// Object attribute values.
+enum
+{
+  // There's no VTABLE instance or VTABLE access in the ojbect file.
+  Val_GNU_AVR_VTABLE_NONE = 0,
+
+  // When the module hosts or reads a VTABLE, then Tag_GNU_AVR_VTABLE_AS is
+  // one plus AVR GCC's address space enum from <GCC>/gcc/config/avr/avr.h.
+
+  // VTABLE is located in AS0, the generic address space (RAM).
+  // This includes .rodata.  */
+  Val_GNU_AVR_VTABLE_RAM = 0 + 1,
+
+  // __flash: 16-bit flash in 0x0 -- 0xffff.
+  Val_GNU_AVR_VTABLE_FLASH = 1 + 1,
+
+  // __flash1: 16-bit flash in 0x10000 -- 0x1ffff.
+  Val_GNU_AVR_VTABLE_FLASH1 = 2 + 1,
+
+  // __flash2: 16-bit flash in 0x20000 -- 0x2ffff.
+  Val_GNU_AVR_VTABLE_FLASH2 = 3 + 1,
+
+  // __flash3: 16-bit flash in 0x30000 -- 0x3ffff.
+  Val_GNU_AVR_VTABLE_FLASH3 = 4 + 1,
+
+  // __flash4: 16-bit flash in 0x40000 -- 0x4ffff.
+  Val_GNU_AVR_VTABLE_FLASH4 = 5 + 1,
+
+  // __flash5: 16-bit flash in 0x50000 -- 0x5ffff.
+  Val_GNU_AVR_VTABLE_FLASH5 = 6 + 1,
+
+  // __flashx: 24-bit flash.
+  Val_GNU_AVR_VTABLE_FLASHX = 7 + 1,
+
+  Val_GNU_AVR_VTABLE_Sentinel
+};
+
+
+// Map Tag_GNU_AVR_VTABLE_AS to a static string for diagnostics.
+
+static inline const char *
+avr_tag_vtable_as_name (int tag)
+{
+  switch (tag)
+    {
+    default:
+      break;
+    case Val_GNU_AVR_VTABLE_NONE: return "no vtables";
+    case Val_GNU_AVR_VTABLE_RAM: return "generic address space";
+    case Val_GNU_AVR_VTABLE_FLASH: return "__flash";
+    case Val_GNU_AVR_VTABLE_FLASH1: return "__flash1";
+    case Val_GNU_AVR_VTABLE_FLASH2: return "__flash2";
+    case Val_GNU_AVR_VTABLE_FLASH3: return "__flash3";
+    case Val_GNU_AVR_VTABLE_FLASH4: return "__flash4";
+    case Val_GNU_AVR_VTABLE_FLASH5: return "__flash5";
+    case Val_GNU_AVR_VTABLE_FLASHX: return "__flashx";
+    }
+
+  return "<unknown>";
+}
+
 #endif /* _ELF_AVR_H */
diff --git a/ld/testsuite/ld-avr/attr-gnu-4-1.d b/ld/testsuite/ld-avr/attr-gnu-4-1.d
new file mode 100644
index 00000000000..461db4f8e92
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-4-1.d
@@ -0,0 +1,8 @@ 
+#name: AVR Tag_AVR_GNU_VTABLE_AS 1
+#source: attr-gnu-4-1.s
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_VTABLE_AS \(4\): 1 \(generic address space\)
diff --git a/ld/testsuite/ld-avr/attr-gnu-4-1.s b/ld/testsuite/ld-avr/attr-gnu-4-1.s
new file mode 100644
index 00000000000..e985a56f6b1
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-4-1.s
@@ -0,0 +1 @@ 
+.gnu_attribute 4,1
diff --git a/ld/testsuite/ld-avr/attr-gnu-4-1_2.d b/ld/testsuite/ld-avr/attr-gnu-4-1_2.d
new file mode 100644
index 00000000000..5e7ea5c667e
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-4-1_2.d
@@ -0,0 +1,7 @@ 
+#name: AVR Tag_AVR_GNU_VTABLE_AS conflict
+#source: attr-gnu-4-1.s
+#source: attr-gnu-4-2.s
+#as:
+#ld:
+#target: avr-*-*
+#error: failed to merge target specific data of file
diff --git a/ld/testsuite/ld-avr/attr-gnu-4-2.d b/ld/testsuite/ld-avr/attr-gnu-4-2.d
new file mode 100644
index 00000000000..2a823c1b280
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-4-2.d
@@ -0,0 +1,8 @@ 
+#name: AVR Tag_AVR_GNU_VTABLE_AS 2
+#source: attr-gnu-4-2.s
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_VTABLE_AS \(4\): 2 \(__flash\)
diff --git a/ld/testsuite/ld-avr/attr-gnu-4-2.s b/ld/testsuite/ld-avr/attr-gnu-4-2.s
new file mode 100644
index 00000000000..54ebf4ed8dd
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-4-2.s
@@ -0,0 +1 @@ 
+.gnu_attribute 4,2