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
