Update support for .note.gnu.property sections.
authorCary Coutant <ccoutant@gmail.com>
Sat, 23 Jun 2018 06:36:50 +0000 (23:36 -0700)
committerCary Coutant <ccoutant@gmail.com>
Sat, 23 Jun 2018 06:36:50 +0000 (23:36 -0700)
The original patch did not give the target enough hooks to discover that
an input object file does not have a particular property. For the
GNU_PROPERTY_X86_FEATURE_1_AND property, for example, where a missing
property should be assumed to be all zeroes, and ANDed with other
object modules, this is essential. We now store the target-specific
properties locally in the Target structure as native uint32_t fields,
then AND the per-object feature bits with the program's feature bits
when we're finished processing each input object file. The target-specific
properties are then added back to the output note section during
finalization.

gold/
PR gold/22914
* layout.cc (read_sized_value): Fix spelling of section name.
(Layout::layout_gnu_property): Call Sized_target::record_gnu_property
for target-specific properties;
don't store them with target-independent properties yet.
(Layout::merge_gnu_properties): New method.
(Layout::add_gnu_property): New method.
(Layout::create_gnu_properties_note): Call target to finalize
target-specific properties. Fix spelling of output section name.
* layout.h (Layout::merge_gnu_properties): New method.
(Layout::add_gnu_property): New method.
* object.cc (Sized_relobj_file::do_layout): Call
Layout::merge_gnu_properties.
* target.h (Target::merge_gnu_property): Remove.
(Target::finalize_gnu_properties): New method.
(Target::do_merge_gnu_property): Move to Sized_target and rename.
(Target::do_finalize_gnu_properties): New virtual method.
(Sized_target::record_gnu_property): Moved and renamed from
Target::do_merge_gnu_property.
(Sized_target::merge_gnu_properties): New virtual method.
* x86_64.cc (Target_x86_64::isa_1_used_, isa_1_needed_)
(feature_1_, object_feature_1_, seen_first_object_): New data members.
(Target_x86_64::do_merge_gnu_property): Rename to ...
(Target_x86_64::record_gnu_property): ... this.  Save target-specific
properties in Target class object.
(Target_x86_64::merge_gnu_properties): New method.
(add_property): New static inline function.
(Target_x86_64::do_finalize_gnu_properties): New method.
* testsuite/Makefile.am (gnu_property_test): Remove C source file;
link directly without compiler driver.
* testsuite/Makefile.in: Regenerate.
* testsuite/gnu_property_a.S: Add _start.

gold/ChangeLog
gold/layout.cc
gold/layout.h
gold/object.cc
gold/target.h
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/gnu_property_a.S
gold/x86_64.cc

index 1010513..f4c6f0a 100644 (file)
@@ -1,5 +1,40 @@
 2018-06-22  Cary Coutant  <ccoutant@gmail.com>
 
+       PR gold/22914
+       * layout.cc (read_sized_value): Fix spelling of section name.
+       (Layout::layout_gnu_property): Call Sized_target::record_gnu_property
+       for target-specific properties;
+       don't store them with target-independent properties yet.
+       (Layout::merge_gnu_properties): New method.
+       (Layout::add_gnu_property): New method.
+       (Layout::create_gnu_properties_note): Call target to finalize
+       target-specific properties. Fix spelling of output section name.
+       * layout.h (Layout::merge_gnu_properties): New method.
+       (Layout::add_gnu_property): New method.
+       * object.cc (Sized_relobj_file::do_layout): Call
+       Layout::merge_gnu_properties.
+       * target.h (Target::merge_gnu_property): Remove.
+       (Target::finalize_gnu_properties): New method.
+       (Target::do_merge_gnu_property): Move to Sized_target and rename.
+       (Target::do_finalize_gnu_properties): New virtual method.
+       (Sized_target::record_gnu_property): Moved and renamed from
+       Target::do_merge_gnu_property.
+       (Sized_target::merge_gnu_properties): New virtual method.
+       * x86_64.cc (Target_x86_64::isa_1_used_, isa_1_needed_)
+       (feature_1_, object_feature_1_, seen_first_object_): New data members.
+       (Target_x86_64::do_merge_gnu_property): Rename to ...
+       (Target_x86_64::record_gnu_property): ... this.  Save target-specific
+       properties in Target class object.
+       (Target_x86_64::merge_gnu_properties): New method.
+       (add_property): New static inline function.
+       (Target_x86_64::do_finalize_gnu_properties): New method.
+       * testsuite/Makefile.am (gnu_property_test): Remove C source file;
+       link directly without compiler driver.
+       * testsuite/Makefile.in: Regenerate.
+       * testsuite/gnu_property_a.S: Add _start.
+
+2018-06-22  Cary Coutant  <ccoutant@gmail.com>
+
        * incremental.cc (Sized_incremental_binary::setup_readers): Use
        emplace_back for GCC 5 and later.
        * incremental.h (Incremental_binary::Input_reader): Provide copy
index 9ce38c3..0df7ed3 100644 (file)
@@ -2217,7 +2217,7 @@ read_sized_value(size_t size, const unsigned char* buf, bool is_big_endian,
     }
   else
     {
-      gold_warning(_("%s: in .note.gnu.properties section, "
+      gold_warning(_("%s: in .note.gnu.property section, "
                     "pr_datasz must be 4 or 8"),
                   object->name().c_str());
     }
@@ -2262,6 +2262,63 @@ Layout::layout_gnu_property(unsigned int note_type,
   // We currently support only the one note type.
   gold_assert(note_type == elfcpp::NT_GNU_PROPERTY_TYPE_0);
 
+  if (pr_type >= elfcpp::GNU_PROPERTY_LOPROC
+      && pr_type < elfcpp::GNU_PROPERTY_HIPROC)
+    {
+      // Target-dependent property value; call the target to record.
+      const int size = parameters->target().get_size();
+      const bool is_big_endian = parameters->target().is_big_endian();
+      if (size == 32)
+        {
+          if (is_big_endian)
+            {
+#ifdef HAVE_TARGET_32_BIG
+             parameters->sized_target<32, true>()->
+                 record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
+                                     object);
+#else
+             gold_unreachable();
+#endif
+            }
+          else
+            {
+#ifdef HAVE_TARGET_32_LITTLE
+             parameters->sized_target<32, false>()->
+                 record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
+                                     object);
+#else
+             gold_unreachable();
+#endif
+            }
+        }
+      else if (size == 64)
+        {
+          if (is_big_endian)
+            {
+#ifdef HAVE_TARGET_64_BIG
+             parameters->sized_target<64, true>()->
+                 record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
+                                     object);
+#else
+             gold_unreachable();
+#endif
+            }
+          else
+            {
+#ifdef HAVE_TARGET_64_LITTLE
+             parameters->sized_target<64, false>()->
+                 record_gnu_property(note_type, pr_type, pr_datasz, pr_data,
+                                     object);
+#else
+             gold_unreachable();
+#endif
+            }
+        }
+      else
+        gold_unreachable();
+      return;
+    }
+
   Gnu_properties::iterator pprop = this->gnu_properties_.find(pr_type);
   if (pprop == this->gnu_properties_.end())
     {
@@ -2273,46 +2330,99 @@ Layout::layout_gnu_property(unsigned int note_type,
     }
   else
     {
-      if (pr_type >= elfcpp::GNU_PROPERTY_LOPROC
-         && pr_type < elfcpp::GNU_PROPERTY_HIPROC)
+      const bool is_big_endian = parameters->target().is_big_endian();
+      switch (pr_type)
+       {
+       case elfcpp::GNU_PROPERTY_STACK_SIZE:
+         // Record the maximum value seen.
+         {
+           uint64_t val1 = read_sized_value(pprop->second.pr_datasz,
+                                            pprop->second.pr_data,
+                                            is_big_endian, object);
+           uint64_t val2 = read_sized_value(pr_datasz, pr_data,
+                                            is_big_endian, object);
+           if (val2 > val1)
+             write_sized_value(val2, pprop->second.pr_datasz,
+                               pprop->second.pr_data, is_big_endian);
+         }
+         break;
+       case elfcpp::GNU_PROPERTY_NO_COPY_ON_PROTECTED:
+         // No data to merge.
+         break;
+       default:
+         gold_warning(_("%s: unknown program property type %d "
+                        "in .note.gnu.property section"),
+                      object->name().c_str(), pr_type);
+       }
+    }
+}
+
+// Merge per-object properties with program properties.
+// This lets the target identify objects that are missing certain
+// properties, in cases where properties must be ANDed together.
+
+void
+Layout::merge_gnu_properties(const Object* object)
+{
+  const int size = parameters->target().get_size();
+  const bool is_big_endian = parameters->target().is_big_endian();
+  if (size == 32)
+    {
+      if (is_big_endian)
+       {
+#ifdef HAVE_TARGET_32_BIG
+         parameters->sized_target<32, true>()->merge_gnu_properties(object);
+#else
+         gold_unreachable();
+#endif
+       }
+      else
+       {
+#ifdef HAVE_TARGET_32_LITTLE
+         parameters->sized_target<32, false>()->merge_gnu_properties(object);
+#else
+         gold_unreachable();
+#endif
+       }
+    }
+  else if (size == 64)
+    {
+      if (is_big_endian)
        {
-         // Target-dependent property value; call the target to merge.
-         parameters->target().merge_gnu_property(note_type,
-                                                 pr_type,
-                                                 pr_datasz,
-                                                 pr_data,
-                                                 pprop->second.pr_datasz,
-                                                 pprop->second.pr_data,
-                                                 object);
+#ifdef HAVE_TARGET_64_BIG
+         parameters->sized_target<64, true>()->merge_gnu_properties(object);
+#else
+         gold_unreachable();
+#endif
        }
       else
        {
-         const bool is_big_endian = parameters->target().is_big_endian();
-         switch (pr_type)
-           {
-           case elfcpp::GNU_PROPERTY_STACK_SIZE:
-             // Record the maximum value seen.
-             {
-               uint64_t val1 = read_sized_value(pprop->second.pr_datasz,
-                                                pprop->second.pr_data,
-                                                is_big_endian, object);
-               uint64_t val2 = read_sized_value(pr_datasz, pr_data,
-                                                is_big_endian, object);
-               if (val2 > val1)
-                 write_sized_value(val2, pprop->second.pr_datasz,
-                                   pprop->second.pr_data, is_big_endian);
-             }
-             break;
-           case elfcpp::GNU_PROPERTY_NO_COPY_ON_PROTECTED:
-             // No data to merge.
-             break;
-           default:
-             gold_warning(_("%s: unknown program property type %d "
-                            "in .note.gnu.properties section"),
-                          object->name().c_str(), pr_type);
-           }
+#ifdef HAVE_TARGET_64_LITTLE
+         parameters->sized_target<64, false>()->merge_gnu_properties(object);
+#else
+         gold_unreachable();
+#endif
        }
     }
+  else
+    gold_unreachable();
+}
+
+// Add a target-specific property for the output .note.gnu.property section.
+
+void
+Layout::add_gnu_property(unsigned int note_type,
+                        unsigned int pr_type,
+                        size_t pr_datasz,
+                        const unsigned char* pr_data)
+{
+  gold_assert(note_type == elfcpp::NT_GNU_PROPERTY_TYPE_0);
+
+  Gnu_property prop;
+  prop.pr_datasz = pr_datasz;
+  prop.pr_data = new unsigned char[pr_datasz];
+  memcpy(prop.pr_data, pr_data, pr_datasz);
+  this->gnu_properties_[pr_type] = prop;
 }
 
 // Create automatic note sections.
@@ -3144,12 +3254,14 @@ Layout::create_note(const char* name, int note_type,
   return os;
 }
 
-// Create a .note.gnu.properties section to record program properties
+// Create a .note.gnu.property section to record program properties
 // accumulated from the input files.
 
 void
 Layout::create_gnu_properties_note()
 {
+  parameters->target().finalize_gnu_properties(this);
+
   if (this->gnu_properties_.empty())
     return;
 
@@ -3168,7 +3280,7 @@ Layout::create_gnu_properties_note()
   // Create the note section.
   size_t trailing_padding;
   Output_section* os = this->create_note("GNU", elfcpp::NT_GNU_PROPERTY_TYPE_0,
-                                        ".note.gnu.properties", descsz,
+                                        ".note.gnu.property", descsz,
                                         true, &trailing_padding);
   if (os == NULL)
     return;
index d71d291..8305b2a 100644 (file)
@@ -701,6 +701,17 @@ class Layout
                      const unsigned char* pr_data,
                      const Object* object);
 
+  // Merge per-object properties with program properties.
+  void
+  merge_gnu_properties(const Object* object);
+
+  // Add a target-specific property for the output .note.gnu.property section.
+  void
+  add_gnu_property(unsigned int note_type,
+                  unsigned int pr_type,
+                  size_t pr_datasz,
+                  const unsigned char* pr_data);
+
   // Add an Output_section_data to the layout.  This is used for
   // special sections like the GOT section.  ORDER is where the
   // section should wind up in the output segment.  IS_RELRO is true
@@ -1359,7 +1370,7 @@ class Layout
     std::vector<Section_info> section_infos_;
   };
 
-  // Program properties from .note.gnu.properties sections.
+  // Program properties from .note.gnu.property sections.
   struct Gnu_property
   {
     size_t pr_datasz;
index 8c874fe..374340f 100644 (file)
@@ -1862,7 +1862,10 @@ Sized_relobj_file<size, big_endian>::do_layout(Symbol_table* symtab,
     }
 
   if (!is_pass_two)
-    layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags, this);
+    {
+      layout->merge_gnu_properties(this);
+      layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags, this);
+    }
 
   // Handle the .eh_frame sections after the other sections.
   gold_assert(!is_pass_one || eh_frame_sections.empty());
index 1f8f757..bb31206 100644 (file)
@@ -509,22 +509,10 @@ class Target
   should_include_section(elfcpp::Elf_Word sh_type) const
   { return this->do_should_include_section(sh_type); }
 
-  // Merge a target-specific program property in the .note.gnu.properties
-  // section.
+  // Finalize the target-specific properties in the .note.gnu.property section.
   void
-  merge_gnu_property(int note_type,
-                    int pr_type,
-                    size_t new_pr_datasz,
-                    const unsigned char* new_pr_data,
-                    size_t old_pr_datasz,
-                    unsigned char* old_pr_data,
-                    const Object* object) const
-  {
-    return this->do_merge_gnu_property(note_type, pr_type,
-                                      new_pr_datasz, new_pr_data,
-                                      old_pr_datasz, old_pr_data,
-                                      object);
-  }
+  finalize_gnu_properties(Layout* layout) const
+  { this->do_finalize_gnu_properties(layout); }
 
  protected:
   // This struct holds the constant information for a child class.  We
@@ -832,11 +820,9 @@ class Target
   do_should_include_section(elfcpp::Elf_Word) const
   { return true; }
 
-  // Merge a target-specific program property in the .note.gnu.properties
-  // section.
+  // Finalize the target-specific properties in the .note.gnu.property section.
   virtual void
-  do_merge_gnu_property(int, int, size_t, const unsigned char*,
-                       size_t, unsigned char*, const Object*) const
+  do_finalize_gnu_properties(Layout*) const
   { }
 
  private:
@@ -1158,6 +1144,17 @@ class Sized_target : public Target
     return elfcpp::elf_r_sym<size>(rel.get_r_info());
   }
 
+  // Record a target-specific program property in the .note.gnu.property
+  // section.
+  virtual void
+  record_gnu_property(int, int, size_t, const unsigned char*, const Object*)
+  { }
+
+  // Merge the target-specific program properties from the current object.
+  virtual void
+  merge_gnu_properties(const Object*)
+  { }
+
  protected:
   Sized_target(const Target::Target_info* pti)
     : Target(pti)
index f439011..5f7d981 100644 (file)
@@ -3260,8 +3260,8 @@ check_SCRIPTS += gnu_property_test.sh
 check_DATA += gnu_property_test.stdout
 gnu_property_test.stdout: gnu_property_test
        $(TEST_READELF) -n $< >$@
-gnu_property_test: gcctestdir/ld gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o
-       $(LINK) -Bgcctestdir/ -o $@ gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o
+gnu_property_test: gcctestdir/ld gnu_property_a.o gnu_property_b.o gnu_property_c.o
+       gcctestdir/ld -o $@ gnu_property_a.o gnu_property_b.o gnu_property_c.o
 gnu_property_main.o: gnu_property_main.c
        $(COMPILE) -c -o $@ $<
 gnu_property_a.o: gnu_property_a.S
index ee44c95..b4729bc 100644 (file)
@@ -9465,8 +9465,8 @@ uninstall-am:
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@     $(CXXCOMPILE) -c -Bgcctestdir/ -Wa,-madd-bnd-prefix -o $@ $<
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_test.stdout: gnu_property_test
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@     $(TEST_READELF) -n $< >$@
-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_test: gcctestdir/ld gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o
-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@     $(LINK) -Bgcctestdir/ -o $@ gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_test: gcctestdir/ld gnu_property_a.o gnu_property_b.o gnu_property_c.o
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@     gcctestdir/ld -o $@ gnu_property_a.o gnu_property_b.o gnu_property_c.o
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_main.o: gnu_property_main.c
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@     $(COMPILE) -c -o $@ $<
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_a.o: gnu_property_a.S
index 2fa073d..463bc8e 100644 (file)
 # define ALIGN 2
 #endif
 
+       .text
+       .globl _start
+_start:
+       ret
+
         .section ".note.gnu.property", "a"
         .p2align ALIGN
 
index e6fa47e..a27f84d 100644 (file)
@@ -590,7 +590,8 @@ class Target_x86_64 : public Sized_target<size, false>
       got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
       rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY),
       got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
-      tls_base_symbol_defined_(false)
+      tls_base_symbol_defined_(false), isa_1_used_(0), isa_1_needed_(0),
+      feature_1_(0), object_feature_1_(0), seen_first_object_(false)
   { }
 
   // Hook for a new output section.
@@ -1188,11 +1189,19 @@ class Target_x86_64 : public Sized_target<size, false>
                                  this->rela_dyn_section(layout));
   }
 
-  // Merge a target-specific program property in the .note.gnu.properties
+  // Record a target-specific program property in the .note.gnu.property
   // section.
   void
-  do_merge_gnu_property(int, int, size_t, const unsigned char*,
-                       size_t, unsigned char*, const Object*) const;
+  record_gnu_property(int, int, size_t, const unsigned char*, const Object*);
+
+  // Merge the target-specific program properties from the current object.
+  void
+  merge_gnu_properties(const Object*);
+
+  // Finalize the target-specific program properties and add them back to
+  // the layout.
+  void
+  do_finalize_gnu_properties(Layout*) const;
 
   // Information about this specific target which we pass to the
   // general Target structure.
@@ -1251,6 +1260,17 @@ class Target_x86_64 : public Sized_target<size, false>
   std::vector<Tlsdesc_info> tlsdesc_reloc_info_;
   // True if the _TLS_MODULE_BASE_ symbol has been defined.
   bool tls_base_symbol_defined_;
+  // Target-specific program properties, from .note.gnu.property section.
+  // Each bit represents a specific feature.
+  uint32_t isa_1_used_;
+  uint32_t isa_1_needed_;
+  uint32_t feature_1_;
+  // Target-specific properties from the current object.
+  // These bits get ANDed into FEATURE_1_ after all properties for the object
+  // have been processed.
+  uint32_t object_feature_1_;
+  // Whether we have seen our first object, for use in initializing FEATURE_1_.
+  bool seen_first_object_;
 };
 
 template<>
@@ -1439,37 +1459,91 @@ Target_x86_64<size>::rela_irelative_section(Layout* layout)
   return this->rela_irelative_;
 }
 
-// Merge a target-specific program property in the .note.gnu.properties
+// Record a target-specific program property from the .note.gnu.property
 // section.
 template<int size>
 void
-Target_x86_64<size>::do_merge_gnu_property(
+Target_x86_64<size>::record_gnu_property(
     int, int pr_type,
-    size_t new_pr_datasz, const unsigned char* new_pr_data,
-    size_t old_pr_datasz, unsigned char* old_pr_data,
-    const Object*) const
+    size_t pr_datasz, const unsigned char* pr_data,
+    const Object* object)
 {
-  size_t min_datasz = (new_pr_datasz > old_pr_datasz
-                      ? old_pr_datasz
-                      : new_pr_datasz);
+  uint32_t val;
+
   switch (pr_type)
     {
     case elfcpp::GNU_PROPERTY_X86_ISA_1_USED:
     case elfcpp::GNU_PROPERTY_X86_ISA_1_NEEDED:
-      {
-        for (size_t i = 0; i < min_datasz; ++i)
-          old_pr_data[i] |= new_pr_data[i];
-      }
-      break;
     case elfcpp::GNU_PROPERTY_X86_FEATURE_1_AND:
-      {
-        for (size_t i = 0; i < min_datasz; ++i)
-          old_pr_data[i] &= new_pr_data[i];
-      }
+      if (pr_datasz != 4)
+       {
+         gold_warning(_("%s: corrupt .note.gnu.property section "
+                        "(pr_datasz for property %d is not 4)"),
+                      object->name().c_str(), pr_type);
+         return;
+       }
+      val = elfcpp::Swap<32, false>::readval(pr_data);
       break;
     default:
+      gold_warning(_("%s: unknown program property type 0x%x "
+                    "in .note.gnu.property section"),
+                  object->name().c_str(), pr_type);
       break;
     }
+
+  switch (pr_type)
+    {
+    case elfcpp::GNU_PROPERTY_X86_ISA_1_USED:
+      this->isa_1_used_ |= val;
+      break;
+    case elfcpp::GNU_PROPERTY_X86_ISA_1_NEEDED:
+      this->isa_1_needed_ |= val;
+      break;
+    case elfcpp::GNU_PROPERTY_X86_FEATURE_1_AND:
+      // If we see multiple feature props in one object, OR them together.
+      this->object_feature_1_ |= val;
+      break;
+    }
+}
+
+// Merge the target-specific program properties from the current object.
+template<int size>
+void
+Target_x86_64<size>::merge_gnu_properties(const Object*)
+{
+  if (this->seen_first_object_)
+    this->feature_1_ &= this->object_feature_1_;
+  else
+    {
+      this->feature_1_ = this->object_feature_1_;
+      this->seen_first_object_ = true;
+    }
+  this->object_feature_1_ = 0;
+}
+
+static inline void
+add_property(Layout* layout, unsigned int pr_type, uint32_t val)
+{
+  unsigned char buf[4];
+  elfcpp::Swap<32, false>::writeval(buf, val);
+  layout->add_gnu_property(elfcpp::NT_GNU_PROPERTY_TYPE_0, pr_type, 4, buf);
+}
+
+// Finalize the target-specific program properties and add them back to
+// the layout.
+template<int size>
+void
+Target_x86_64<size>::do_finalize_gnu_properties(Layout* layout) const
+{
+  if (this->isa_1_used_ != 0)
+    add_property(layout, elfcpp::GNU_PROPERTY_X86_ISA_1_USED,
+                this->isa_1_used_);
+  if (this->isa_1_needed_ != 0)
+    add_property(layout, elfcpp::GNU_PROPERTY_X86_ISA_1_NEEDED,
+                this->isa_1_needed_);
+  if (this->feature_1_ != 0)
+    add_property(layout, elfcpp::GNU_PROPERTY_X86_FEATURE_1_AND,
+                this->feature_1_);
 }
 
 // Write the first three reserved words of the .got.plt section.