AVR: Support .gnu_attribute to encode the size of [long] double

Message ID c05208aa-d2e0-46b1-8601-cc7102e0f49d@gjlay.de
State New
Headers
Series AVR: Support .gnu_attribute to encode the size of [long] double |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Georg-Johann Lay Aug. 21, 2026, 1:55 p.m. UTC
  AVR GCC allows to chose the size of double and long double at
compile time by means of -m[long-]double={32|64}.  As these
options select a different ABI, Binutils should complain when
an attempt is being made to link incompatible object files.

This patch adds
    .gnu_attribute 8 (Tag_GNU_AVR_BITS_DOUBLE)
    .gnu_attribute 12 (Tag_GNU_AVR_BITS_LONG_DOUBLE)
and some simple test cases.

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

Ok for trunk?

Johann

--

	PR ld/34557
include/
	* elf/avr.h (Tag_GNU_AVR_BITS_DOUBLE = 8)
	(Tag_GNU_AVR_BITS_LONG_DOUBLE = 12): Define new enum values.
bfd/
	* elf32-avr.c (avr_elf_merge_obj_attributes): Support
	Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
binutils/
	* readelf.c (display_avr_gnu_attribute): Support
	Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
gas/
	* doc/as.texi (AVR Attributes): Document
	Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
ld/
	* testsuite/ld-avr/attr-gnu-8-0.s: New file.
	* testsuite/ld-avr/attr-gnu-8-32.s: New file.
	* testsuite/ld-avr/attr-gnu-8-64.s: New file.
	* testsuite/ld-avr/attr-gnu-8-32.d: New test.
	* testsuite/ld-avr/attr-gnu-8-32_0.d: New test.
	* testsuite/ld-avr/attr-gnu-8-32_64.d: New test.
	* testsuite/ld-avr/attr-gnu-8_12.d: New test.
	* testsuite/ld-avr/attr-gnu-12-0.s: New file.
	* testsuite/ld-avr/attr-gnu-12-64.s: New file.
	* testsuite/ld-avr/attr-gnu-12-32.s: New file.
	* testsuite/ld-avr/attr-gnu-12-32.d: New test.
	* testsuite/ld-avr/attr-gnu-12-32_0.d: New test.
	* testsuite/ld-avr/attr-gnu-12-32_64.d: New test.
  

Comments

Georg-Johann Lay Sept. 2, 2026, 5:19 p.m. UTC | #1
Approved here: 
https://gcc.gnu.org/pipermail/gcc-patches/2026-September/729765.html

Am 21.08.26 um 15:55 schrieb Georg-Johann Lay via Binutils:
> AVR GCC allows to chose the size of double and long double at
> compile time by means of -m[long-]double={32|64}.  As these
> options select a different ABI, Binutils should complain when
> an attempt is being made to link incompatible object files.
> 
> This patch adds
>     .gnu_attribute 8 (Tag_GNU_AVR_BITS_DOUBLE)
>     .gnu_attribute 12 (Tag_GNU_AVR_BITS_LONG_DOUBLE)
> and some simple test cases.
> 
> A tag value of 0 is compatible with all other tag values.
> Two non-zero tag values are compatible iff they are the same.
> 
> Ok for trunk?
> 
> Johann
> 
> -- 
> 
>      PR ld/34557
> include/
>      * elf/avr.h (Tag_GNU_AVR_BITS_DOUBLE = 8)
>      (Tag_GNU_AVR_BITS_LONG_DOUBLE = 12): Define new enum values.
> bfd/
>      * elf32-avr.c (avr_elf_merge_obj_attributes): Support
>      Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
> binutils/
>      * readelf.c (display_avr_gnu_attribute): Support
>      Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
> gas/
>      * doc/as.texi (AVR Attributes): Document
>      Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
> ld/
>      * testsuite/ld-avr/attr-gnu-8-0.s: New file.
>      * testsuite/ld-avr/attr-gnu-8-32.s: New file.
>      * testsuite/ld-avr/attr-gnu-8-64.s: New file.
>      * testsuite/ld-avr/attr-gnu-8-32.d: New test.
>      * testsuite/ld-avr/attr-gnu-8-32_0.d: New test.
>      * testsuite/ld-avr/attr-gnu-8-32_64.d: New test.
>      * testsuite/ld-avr/attr-gnu-8_12.d: New test.
>      * testsuite/ld-avr/attr-gnu-12-0.s: New file.
>      * testsuite/ld-avr/attr-gnu-12-64.s: New file.
>      * testsuite/ld-avr/attr-gnu-12-32.s: New file.
>      * testsuite/ld-avr/attr-gnu-12-32.d: New test.
>      * testsuite/ld-avr/attr-gnu-12-32_0.d: New test.
>      * testsuite/ld-avr/attr-gnu-12-32_64.d: New test.
  

Patch

    AVR: ld/34557 - Support .gnu_attribute to encode the size of [long] double.
    
    AVR GCC allows to chose the size of double and long double at
    compile time by means of -m[long-]double={32|64}.  As these
    options select a different ABI, Binutils should complain when
    an attempt is being made to link incompatible object files.
    
    This patch adds
       .gnu_attribute 8 (Tag_GNU_AVR_BITS_DOUBLE)
       .gnu_attribute 12 (Tag_GNU_AVR_BITS_LONG_DOUBLE)
    and some simple test cases.
    
    A tag value of 0 is compatible with all other tag values.
    Two non-zero tag values are compatible iff they are the same.
    
            PR ld/34557
    include/
            * elf/avr.h (Tag_GNU_AVR_BITS_DOUBLE = 8)
            (Tag_GNU_AVR_BITS_LONG_DOUBLE = 12): Define new enum values.
    bfd/
            * elf32-avr.c (avr_elf_merge_obj_attributes): Support
            Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
    binutils/
            * readelf.c (display_avr_gnu_attribute): Support
            Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
    gas/
            * doc/as.texi (AVR Attributes): Document
            Tag_GNU_AVR_BITS_DOUBLE, Tag_GNU_AVR_BITS_LONG_DOUBLE.
    ld/
            * testsuite/ld-avr/attr-gnu-8-0.s: New file.
            * testsuite/ld-avr/attr-gnu-8-32.s: New file.
            * testsuite/ld-avr/attr-gnu-8-64.s: New file.
            * testsuite/ld-avr/attr-gnu-8-32.d: New test.
            * testsuite/ld-avr/attr-gnu-8-32_0.d: New test.
            * testsuite/ld-avr/attr-gnu-8-32_64.d: New test.
            * testsuite/ld-avr/attr-gnu-8_12.d: New test.
            * testsuite/ld-avr/attr-gnu-12-0.s: New file.
            * testsuite/ld-avr/attr-gnu-12-64.s: New file.
            * testsuite/ld-avr/attr-gnu-12-32.s: New file.
            * testsuite/ld-avr/attr-gnu-12-32.d: New test.
            * testsuite/ld-avr/attr-gnu-12-32_0.d: New test.
            * testsuite/ld-avr/attr-gnu-12-32_64.d: New test.

diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c
index 929a2fe2519..3cc79adec68 100644
--- a/bfd/elf32-avr.c
+++ b/bfd/elf32-avr.c
@@ -4200,7 +4200,6 @@  avr_elf32_property_record_name (struct avr_property_record *rec)
 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;
@@ -4208,6 +4207,9 @@  avr_elf_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
   in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
   out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
 
+  // Merge Tag_GNU_AVR_VTABLE_AS (4).
+
+  static bfd *last_fp_vtab;
   in_attr = &in_attrs[Tag_GNU_AVR_VTABLE_AS];
   out_attr = &out_attrs[Tag_GNU_AVR_VTABLE_AS];
 
@@ -4218,7 +4220,7 @@  avr_elf_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
 	{
 	  out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
 	  out_attr->i = in_attr->i;
-	  last_fp = ibfd;
+	  last_fp_vtab = ibfd;
 	}
     }
   else if (in_attr->i != out_attr->i)
@@ -4227,11 +4229,70 @@  avr_elf_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
       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);
+      // xgettext:c-format
+      _bfd_error_handler (_("%pB uses %s tag %d (%s), %pB uses %s tag %d (%s)"),
+			  ibfd, tag, in_attr->i, iname,
+			  last_fp_vtab, 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 Tag_GNU_AVR_BITS_DOUBLE (8).
+
+  static bfd *last_fp_dbl;
+  in_attr = &in_attrs[Tag_GNU_AVR_BITS_DOUBLE];
+  out_attr = &out_attrs[Tag_GNU_AVR_BITS_DOUBLE];
+
+  if (in_attr->i == 0
+      || out_attr->i == 0)
+    {
+      if (in_attr->i != 0)
+	{
+	  out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
+	  out_attr->i = in_attr->i;
+	  last_fp_dbl = ibfd;
+	}
+    }
+  else if (in_attr->i != out_attr->i)
+    {
+      const char *const tag = "Tag_GNU_AVR_BITS_DOUBLE";
+
+      // xgettext:c-format
+      _bfd_error_handler (_("%pB uses %s tag %d, %pB uses %s tag %d"),
+			  ibfd, tag, in_attr->i,
+			  last_fp_dbl, tag, out_attr->i);
+
+      out_attr->type = ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_ERROR;
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  // Merge Tag_GNU_AVR_BITS_LONG_DOUBLE (12).
+
+  static bfd *last_fp_ldbl;
+  in_attr = &in_attrs[Tag_GNU_AVR_BITS_LONG_DOUBLE];
+  out_attr = &out_attrs[Tag_GNU_AVR_BITS_LONG_DOUBLE];
+
+  if (in_attr->i == 0
+      || out_attr->i == 0)
+    {
+      if (in_attr->i != 0)
+	{
+	  out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
+	  out_attr->i = in_attr->i;
+	  last_fp_ldbl = ibfd;
+	}
+    }
+  else if (in_attr->i != out_attr->i)
+    {
+      const char *const tag = "Tag_GNU_AVR_BITS_LONG_DOUBLE";
+
+      // xgettext:c-format
+      _bfd_error_handler (_("%pB uses %s tag %d, %pB uses %s tag %d"),
+			  ibfd, tag, in_attr->i,
+			  last_fp_ldbl, tag, out_attr->i);
 
       out_attr->type = ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_ERROR;
       bfd_set_error (bfd_error_bad_value);
diff --git a/binutils/readelf.c b/binutils/readelf.c
index b5ccc675af6..2b594d632fa 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -18370,10 +18370,14 @@  display_avr_gnu_attribute (const unsigned char * p,
 			   unsigned int tag,
 			   const unsigned char * const end)
 {
-  if (tag == Tag_GNU_AVR_VTABLE_AS)
+  unsigned int val;
+
+  switch (tag)
     {
-      unsigned int val;
+    default:
+      break;
 
+    case Tag_GNU_AVR_VTABLE_AS:
       printf ("  Tag_GNU_AVR_VTABLE_AS (%u): ", tag);
       if (p == end)
 	{
@@ -18384,6 +18388,28 @@  display_avr_gnu_attribute (const unsigned char * p,
       READ_ULEB (val, p, end);
       printf ("%d (%s)\n", val, avr_tag_vtable_as_name (val));
       return p;
+
+    case Tag_GNU_AVR_BITS_DOUBLE:
+      printf ("  Tag_GNU_AVR_BITS_DOUBLE (%u): ", tag);
+      if (p == end)
+	printf (_("<corrupt>\n"));
+      else
+	{
+	  READ_ULEB (val, p, end);
+	  printf ("%d\n", val);
+	}
+      return p;
+
+    case Tag_GNU_AVR_BITS_LONG_DOUBLE:
+      printf ("  Tag_GNU_AVR_BITS_LONG_DOUBLE (%u): ", tag);
+      if (p == end)
+	printf (_("<corrupt>\n"));
+      else
+	{
+	  READ_ULEB (val, p, end);
+	  printf ("%d\n", val);
+	}
+      return p;
     }
 
   return display_tag_value (tag & 1, p, end);
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index ebe23da340a..2cf4bdfe610 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -8117,6 +8117,12 @@  for C++ virtual tables used by this object file:
 @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
+
+@item Tag_GNU_AVR_BITS_DOUBLE (8)
+Bit size of the @code{double} type, or 0 if no @code{double} is used.
+
+@item Tag_GNU_AVR_BITS_LONG_DOUBLE (12)
+Bit size of the @code{long double} type, or 0 if no @code{long double} is used.
 @end table
 
 @subsection M680x0 Attributes
diff --git a/include/elf/avr.h b/include/elf/avr.h
index 23f21ecec15..1b7cdb648bc 100644
--- a/include/elf/avr.h
+++ b/include/elf/avr.h
@@ -99,6 +99,12 @@  enum
 
   // VTABLE is located in some named address space.
   Tag_GNU_AVR_VTABLE_AS = 4,
+
+  // Bits used by the double type, or 0 if no see.
+  Tag_GNU_AVR_BITS_DOUBLE = 8,
+
+  // Bits used by the long double type, or 0 if no see.
+  Tag_GNU_AVR_BITS_LONG_DOUBLE = 12,
 };
 
 
diff --git a/ld/testsuite/ld-avr/attr-gnu-12-0.s b/ld/testsuite/ld-avr/attr-gnu-12-0.s
new file mode 100644
index 00000000000..9486de20476
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-12-0.s
@@ -0,0 +1 @@ 
+.gnu_attribute 12,0
diff --git a/ld/testsuite/ld-avr/attr-gnu-12-32.d b/ld/testsuite/ld-avr/attr-gnu-12-32.d
new file mode 100644
index 00000000000..5843e18bdee
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-12-32.d
@@ -0,0 +1,8 @@ 
+#name: AVR Tag_AVR_GNU_BITS_LONG_DOUBLE 32
+#source: attr-gnu-12-32.s
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_BITS_LONG_DOUBLE \(12\): 32
diff --git a/ld/testsuite/ld-avr/attr-gnu-12-32.s b/ld/testsuite/ld-avr/attr-gnu-12-32.s
new file mode 100644
index 00000000000..ce87ca76660
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-12-32.s
@@ -0,0 +1 @@ 
+.gnu_attribute 12,32
diff --git a/ld/testsuite/ld-avr/attr-gnu-12-32_0.d b/ld/testsuite/ld-avr/attr-gnu-12-32_0.d
new file mode 100644
index 00000000000..114823957ba
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-12-32_0.d
@@ -0,0 +1,11 @@ 
+#name: AVR Tag_AVR_GNU_BITS_LONG_DOUBLE 32 + 0
+#source: attr-gnu-12-32.s
+#source: attr-gnu-12-0.s
+#as:
+#ld:
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_BITS_LONG_DOUBLE \(12\): 32
diff --git a/ld/testsuite/ld-avr/attr-gnu-12-32_64.d b/ld/testsuite/ld-avr/attr-gnu-12-32_64.d
new file mode 100644
index 00000000000..689d8036fbe
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-12-32_64.d
@@ -0,0 +1,7 @@ 
+#name: AVR Tag_AVR_GNU_BITS_LONG_DOUBLE conflict
+#source: attr-gnu-12-32.s
+#source: attr-gnu-12-64.s
+#as:
+#ld:
+#target: avr-*-*
+#error: failed to merge target specific data of file
diff --git a/ld/testsuite/ld-avr/attr-gnu-12-64.s b/ld/testsuite/ld-avr/attr-gnu-12-64.s
new file mode 100644
index 00000000000..883d0c791a2
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-12-64.s
@@ -0,0 +1 @@ 
+.gnu_attribute 12,64
diff --git a/ld/testsuite/ld-avr/attr-gnu-8-0.s b/ld/testsuite/ld-avr/attr-gnu-8-0.s
new file mode 100644
index 00000000000..b28c578da07
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8-0.s
@@ -0,0 +1 @@ 
+.gnu_attribute 8,0
diff --git a/ld/testsuite/ld-avr/attr-gnu-8-32.d b/ld/testsuite/ld-avr/attr-gnu-8-32.d
new file mode 100644
index 00000000000..47918d379db
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8-32.d
@@ -0,0 +1,8 @@ 
+#name: AVR Tag_AVR_GNU_BITS_DOUBLE 32
+#source: attr-gnu-8-32.s
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_BITS_DOUBLE \(8\): 32
diff --git a/ld/testsuite/ld-avr/attr-gnu-8-32.s b/ld/testsuite/ld-avr/attr-gnu-8-32.s
new file mode 100644
index 00000000000..6bfa47d0bda
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8-32.s
@@ -0,0 +1 @@ 
+.gnu_attribute 8,32
diff --git a/ld/testsuite/ld-avr/attr-gnu-8-32_0.d b/ld/testsuite/ld-avr/attr-gnu-8-32_0.d
new file mode 100644
index 00000000000..345b1add438
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8-32_0.d
@@ -0,0 +1,11 @@ 
+#name: AVR Tag_AVR_GNU_BITS_DOUBLE 32 + 0
+#source: attr-gnu-8-32.s
+#source: attr-gnu-8-0.s
+#as:
+#ld:
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_BITS_DOUBLE \(8\): 32
diff --git a/ld/testsuite/ld-avr/attr-gnu-8-32_64.d b/ld/testsuite/ld-avr/attr-gnu-8-32_64.d
new file mode 100644
index 00000000000..3e780858661
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8-32_64.d
@@ -0,0 +1,7 @@ 
+#name: AVR Tag_AVR_GNU_BITS_DOUBLE conflict
+#source: attr-gnu-8-32.s
+#source: attr-gnu-8-64.s
+#as:
+#ld:
+#target: avr-*-*
+#error: failed to merge target specific data of file
diff --git a/ld/testsuite/ld-avr/attr-gnu-8-64.s b/ld/testsuite/ld-avr/attr-gnu-8-64.s
new file mode 100644
index 00000000000..34141651524
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8-64.s
@@ -0,0 +1 @@ 
+.gnu_attribute 8,64
diff --git a/ld/testsuite/ld-avr/attr-gnu-8_12.d b/ld/testsuite/ld-avr/attr-gnu-8_12.d
new file mode 100644
index 00000000000..f35f9e48d61
--- /dev/null
+++ b/ld/testsuite/ld-avr/attr-gnu-8_12.d
@@ -0,0 +1,12 @@ 
+#name: AVR Tag_AVR_GNU_BITS DOUBLE + LONG_DOUBLE
+#source: attr-gnu-8-32.s
+#source: attr-gnu-12-64.s
+#as:
+#ld:
+#readelf: -A
+#target: avr-*-*
+
+Attribute Section: gnu
+File Attributes
+  Tag_GNU_AVR_BITS_DOUBLE \(8\): 32
+  Tag_GNU_AVR_BITS_LONG_DOUBLE \(12\): 64