2009-12-10 Doug Kwan <dougkwan@google.com>
authorDoug Kwan <dougkwan@google.com>
Thu, 10 Dec 2009 20:55:33 +0000 (20:55 +0000)
committerDoug Kwan <dougkwan@google.com>
Thu, 10 Dec 2009 20:55:33 +0000 (20:55 +0000)
elfcpp/ChangeLog
* arm.h: New enums for EABI object attribute tags and values.

gold/ChangeLog
* arm.cc (attributes.h): New include.
(Arm_relobj::Arm_relobj): Initialize attributes_section_data_.
(Arm_relobj::~Arm_relobj): Delete object pointed by
attributes_section_data_.
(Arm_relobj::attributes_section_data): New method definition.
(Arm_relobj::attributes_section_data_): New data member declaration.
(Arm_dynobj::Arm_dynobj): Initialize attributes_section_data_.
(Arm_dynobj::~Arm_dynobj): Delete object pointed by
attributes_section_data_.
(Arm_dynobj::attributes_section_data): New method definition.
(Arm_dynobj::attributes_section_data_): New data member declaration.
(Target_arm::Target_arm): Initialize attributes_section_data_.  Change
initialization value of may_use_blx_ to false.
    (Target_arm::using_thumb2, Target_arm::using_thumb_only,
Target_arm::may_use_arm_nop, Target_arm::may_use_thumb2_nop): Use
object attributes to compute results instead of hard-coding.
(Target_arm::do_attribute_arg_type, Target_arm::do_attributes_order,
Target_arm::get_secondary_compatible_arch,
Target_arm::set_secondary_compatible_arch
Target_arm::tag_cpu_arch_combine, Target_arm::aeabi_enum_name,
Target_arm::tag_cpu_name_value, Target_arm::merge_object_attributes):
New method declarations.
(Target_arm::get_aeabi_object_attribute): New method definition.
(Target_arm::attributes_section_data_): New data member declaration.
(read_arm_attributes_section): New template definition.
(Arm_relobj::do_read_symbols): Read attributes section if it exists.
(Arm_dynobj::do_read_symbols): Ditto.
(Target_arm::do_finalize_sections): Merge attributes sections from
input.  Check for BLX use after attributes section merging.
Fix __exidx_start and __exidx_end visibility.  Create an
.ARM.attributes section if necessary.
(Target_arm::get_secondary_compatible_arch,
Target_arm::set_secondary_compatible_arch,
Target_arm::tag_cpu_arch_combine, Target_arm::aeabi_enum_name,
Target_arm::tag_cpu_name_value, Target_arm::merge_object_attributes,
Target_arm::do_attribute_arg_type, Target_arm::do_attributes_order):
New method definitions.

elfcpp/ChangeLog
elfcpp/arm.h
gold/ChangeLog
gold/arm.cc

index 653a863c5cbbbb7fb71a71919f1a827112f567f2..ca6b78b9080f76f917ff1c221fddcd42aa095c63 100644 (file)
@@ -1,3 +1,7 @@
+2009-12-10  Doug Kwan  <dougkwan@google.com>
+
+       * arm.h: New enums for EABI object attribute tags and values.
+
 2009-12-05  Doug Kwan  <dougkwan@google.com>
 
        * arm.h: Define enums for Tag_CPU_arch EABI attribute.
index 52b80c5a7cd918cfc8601170c26a9f88ebbfbe55..08a17ad8871f005af10d484f79bd6be5a774c9d2 100644 (file)
@@ -245,6 +245,82 @@ enum
   TAG_CPU_ARCH_V4T_PLUS_V6_M = (MAX_TAG_CPU_ARCH + 1)
 };
 
+// EABI object attributes.
+enum
+{
+  // 0-3 are generic.
+  Tag_CPU_raw_name = 4,
+  Tag_CPU_name = 5,
+  Tag_CPU_arch = 6,
+  Tag_CPU_arch_profile = 7,
+  Tag_ARM_ISA_use = 8,
+  Tag_THUMB_ISA_use = 9,
+  Tag_VFP_arch = 10,
+  Tag_WMMX_arch = 11,
+  Tag_Advanced_SIMD_arch = 12,
+  Tag_PCS_config = 13,
+  Tag_ABI_PCS_R9_use = 14,
+  Tag_ABI_PCS_RW_data = 15,
+  Tag_ABI_PCS_RO_data = 16,
+  Tag_ABI_PCS_GOT_use = 17,
+  Tag_ABI_PCS_wchar_t = 18,
+  Tag_ABI_FP_rounding = 19,
+  Tag_ABI_FP_denormal = 20,
+  Tag_ABI_FP_exceptions = 21,
+  Tag_ABI_FP_user_exceptions = 22,
+  Tag_ABI_FP_number_model = 23,
+  Tag_ABI_align8_needed = 24,
+  Tag_ABI_align8_preserved = 25,
+  Tag_ABI_enum_size = 26,
+  Tag_ABI_HardFP_use = 27,
+  Tag_ABI_VFP_args = 28,
+  Tag_ABI_WMMX_args = 29,
+  Tag_ABI_optimization_goals = 30,
+  Tag_ABI_FP_optimization_goals = 31,
+  // 32 is generic (Tag_compatibility).
+  Tag_undefined33 = 33,
+  Tag_CPU_unaligned_access = 34,
+  Tag_undefined35 = 35,
+  Tag_VFP_HP_extension = 36,
+  Tag_undefined37 = 37,
+  Tag_ABI_FP_16bit_format = 38,
+  Tag_undefined39 = 39,
+  Tag_nodefaults = 64,
+  Tag_also_compatible_with = 65,
+  Tag_T2EE_use = 66,
+  Tag_conformance = 67,
+  Tag_Virtualization_use = 68,
+  Tag_undefined69 = 69,
+  Tag_MPextension_use = 70
+};
+
+// Values for Tag_ABI_PCS_R9_use.
+enum
+{
+  AEABI_R9_V6 = 0,
+  AEABI_R9_SB = 1,
+  AEABI_R9_TLS = 2,
+  AEABI_R9_unused = 3
+};
+
+// Values for Tag_ABI_PCS_RW_data.
+enum
+{
+  AEABI_PCS_RW_data_absolute = 0,
+  AEABI_PCS_RW_data_PCrel = 1,
+  AEABI_PCS_RW_data_SBrel = 2,
+  AEABI_PCS_RW_data_unused = 3
+};
+
+// Values for Tag_ABI_enum_size.
+enum
+{
+  AEABI_enum_unused = 0,
+  AEABI_enum_short = 1,
+  AEABI_enum_wide = 2,
+  AEABI_enum_forced_wide = 3
+};
+
 } // End namespace elfcpp.
 
 #endif // !defined(ELFCPP_ARM_H)
index 339d6e1736352a847f4d3ffd9a3c096ef5e412e8..5801a5c64c7899a663f82c0587f944d94062b123 100644 (file)
@@ -1,3 +1,43 @@
+2009-12-10  Doug Kwan  <dougkwan@google.com>
+
+       * arm.cc (attributes.h): New include.
+       (Arm_relobj::Arm_relobj): Initialize attributes_section_data_.
+       (Arm_relobj::~Arm_relobj): Delete object pointed by
+       attributes_section_data_.
+       (Arm_relobj::attributes_section_data): New method definition.
+       (Arm_relobj::attributes_section_data_): New data member declaration.
+       (Arm_dynobj::Arm_dynobj): Initialize attributes_section_data_.
+       (Arm_dynobj::~Arm_dynobj): Delete object pointed by
+       attributes_section_data_.
+       (Arm_dynobj::attributes_section_data): New method definition.
+       (Arm_dynobj::attributes_section_data_): New data member declaration.
+       (Target_arm::Target_arm): Initialize attributes_section_data_.  Change
+       initialization value of may_use_blx_ to false.
+       (Target_arm::using_thumb2, Target_arm::using_thumb_only,
+       Target_arm::may_use_arm_nop, Target_arm::may_use_thumb2_nop): Use
+       object attributes to compute results instead of hard-coding.
+       (Target_arm::do_attribute_arg_type, Target_arm::do_attributes_order,
+       Target_arm::get_secondary_compatible_arch,
+       Target_arm::set_secondary_compatible_arch
+       Target_arm::tag_cpu_arch_combine, Target_arm::aeabi_enum_name,
+       Target_arm::tag_cpu_name_value, Target_arm::merge_object_attributes):
+       New method declarations.
+       (Target_arm::get_aeabi_object_attribute): New method definition.
+       (Target_arm::attributes_section_data_): New data member declaration.
+       (read_arm_attributes_section): New template definition.
+       (Arm_relobj::do_read_symbols): Read attributes section if it exists.
+       (Arm_dynobj::do_read_symbols): Ditto.
+       (Target_arm::do_finalize_sections): Merge attributes sections from
+       input.  Check for BLX use after attributes section merging.
+       Fix __exidx_start and __exidx_end visibility.  Create an
+       .ARM.attributes section if necessary.
+       (Target_arm::get_secondary_compatible_arch,
+       Target_arm::set_secondary_compatible_arch,
+       Target_arm::tag_cpu_arch_combine, Target_arm::aeabi_enum_name,
+       Target_arm::tag_cpu_name_value, Target_arm::merge_object_attributes,
+       Target_arm::do_attribute_arg_type, Target_arm::do_attributes_order): 
+       New method definitions.
+
 2009-12-09  Ian Lance Taylor  <iant@google.com>
 
        * plugin.cc (Plugin::load): Don't cast from void* to a function
index 85c4cafa0e71f23a7bf53fa2aed96e3bc74c050f..03cc69798f6f47cc531caea31676d4c6bc27889e 100644 (file)
@@ -46,6 +46,7 @@
 #include "tls.h"
 #include "defstd.h"
 #include "gc.h"
+#include "attributes.h"
 
 namespace
 {
@@ -892,11 +893,12 @@ class Arm_relobj : public Sized_relobj<32, big_endian>
   Arm_relobj(const std::string& name, Input_file* input_file, off_t offset,
              const typename elfcpp::Ehdr<32, big_endian>& ehdr)
     : Sized_relobj<32, big_endian>(name, input_file, offset, ehdr),
-      stub_tables_(), local_symbol_is_thumb_function_()
+      stub_tables_(), local_symbol_is_thumb_function_(),
+      attributes_section_data_(NULL)
   { }
 
   ~Arm_relobj()
-  { }
+  { delete this->attributes_section_data_; }
  
   // Return the stub table of the SHNDX-th section if there is one.
   Stub_table<big_endian>*
@@ -950,6 +952,12 @@ class Arm_relobj : public Sized_relobj<32, big_endian>
   processor_specific_flags() const
   { return this->processor_specific_flags_; }
 
+  // Attribute section data  This is the contents of the .ARM.attribute section
+  // if there is one.
+  const Attributes_section_data*
+  attributes_section_data() const
+  { return this->attributes_section_data_; }
+
  protected:
   // Post constructor setup.
   void
@@ -986,6 +994,8 @@ class Arm_relobj : public Sized_relobj<32, big_endian>
   std::vector<bool> local_symbol_is_thumb_function_;
   // processor-specific flags in ELF file header.
   elfcpp::Elf_Word processor_specific_flags_;
+  // Object attributes if there is an .ARM.attributes section or NULL.
+  Attributes_section_data* attributes_section_data_;
 };
 
 // Arm_dynobj class.
@@ -997,11 +1007,11 @@ class Arm_dynobj : public Sized_dynobj<32, big_endian>
   Arm_dynobj(const std::string& name, Input_file* input_file, off_t offset,
             const elfcpp::Ehdr<32, big_endian>& ehdr)
     : Sized_dynobj<32, big_endian>(name, input_file, offset, ehdr),
-      processor_specific_flags_(0)
+      processor_specific_flags_(0), attributes_section_data_(NULL)
   { }
  
   ~Arm_dynobj()
-  { }
+  { delete this->attributes_section_data_; }
 
   // Downcast a base pointer to an Arm_relobj pointer.  This is
   // not type-safe but we only use Arm_relobj not the base class.
@@ -1015,6 +1025,11 @@ class Arm_dynobj : public Sized_dynobj<32, big_endian>
   processor_specific_flags() const
   { return this->processor_specific_flags_; }
 
+  // Attributes section data.
+  const Attributes_section_data*
+  attributes_section_data() const
+  { return this->attributes_section_data_; }
+
  protected:
   // Read the symbol information.
   void
@@ -1023,6 +1038,8 @@ class Arm_dynobj : public Sized_dynobj<32, big_endian>
  private:
   // processor-specific flags in ELF file header.
   elfcpp::Elf_Word processor_specific_flags_;
+  // Object attributes if there is an .ARM.attributes section or NULL.
+  Attributes_section_data* attributes_section_data_;
 };
 
 // Functor to read reloc addends during stub generation.
@@ -1143,9 +1160,9 @@ class Target_arm : public Sized_target<32, big_endian>
     : Sized_target<32, big_endian>(&arm_info),
       got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
       copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL), stub_tables_(),
-      stub_factory_(Stub_factory::get_instance()),
-      may_use_blx_(true), should_force_pic_veneer_(false),
-      arm_input_section_map_()
+      stub_factory_(Stub_factory::get_instance()), may_use_blx_(false),
+      should_force_pic_veneer_(false), arm_input_section_map_(),
+      attributes_section_data_(NULL)
   { }
 
   // Whether we can use BLX.
@@ -1172,32 +1189,48 @@ class Target_arm : public Sized_target<32, big_endian>
   bool
   using_thumb2() const
   {
-    // FIXME:  This should not hard-coded.
-    return false;
+    Object_attribute* attr =
+      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
+    int arch = attr->int_value();
+    return arch == elfcpp::TAG_CPU_ARCH_V6T2 || arch >= elfcpp::TAG_CPU_ARCH_V7;
   }
 
   // Whether we use THUMB/THUMB-2 instructions only.
   bool
   using_thumb_only() const
   {
-    // FIXME:  This should not hard-coded.
-    return false;
+    Object_attribute* attr =
+      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
+    if (attr->int_value() != elfcpp::TAG_CPU_ARCH_V7
+       && attr->int_value() != elfcpp::TAG_CPU_ARCH_V7E_M)
+      return false;
+    attr = this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile);
+    return attr->int_value() == 'M';
   }
 
   // Whether we have an NOP instruction.  If not, use mov r0, r0 instead.
   bool
   may_use_arm_nop() const
   {
-    // FIXME:  This should not hard-coded.
-    return false;
+    Object_attribute* attr =
+      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
+    int arch = attr->int_value();
+    return (arch == elfcpp::TAG_CPU_ARCH_V6T2
+           || arch == elfcpp::TAG_CPU_ARCH_V6K
+           || arch == elfcpp::TAG_CPU_ARCH_V7
+           || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
   }
 
   // Whether we have THUMB-2 NOP.W instruction.
   bool
   may_use_thumb2_nop() const
   {
-    // FIXME:  This should not hard-coded.
-    return false;
+    Object_attribute* attr =
+      this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
+    int arch = attr->int_value();
+    return (arch == elfcpp::TAG_CPU_ARCH_V6T2
+           || arch == elfcpp::TAG_CPU_ARCH_V7
+           || arch == elfcpp::TAG_CPU_ARCH_V7E_M);
   }
   
   // Process the relocations to determine unreferenced sections for 
@@ -1386,6 +1419,15 @@ class Target_arm : public Sized_target<32, big_endian>
   bool
   do_relax(int, const Input_objects*, Symbol_table*, Layout*);
 
+  // Determine whether an object attribute tag takes an integer, a
+  // string or both.
+  int
+  do_attribute_arg_type(int tag) const;
+
+  // Reorder tags during output.
+  int
+  do_attributes_order(int num) const;
+
  private:
   // The class which scans relocations.
   class Scan
@@ -1576,6 +1618,41 @@ class Target_arm : public Sized_target<32, big_endian>
   void
   merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word);
 
+  // Get the secondary compatible architecture.
+  static int
+  get_secondary_compatible_arch(const Attributes_section_data*);
+
+  // Set the secondary compatible architecture.
+  static void
+  set_secondary_compatible_arch(Attributes_section_data*, int);
+
+  static int
+  tag_cpu_arch_combine(const char*, int, int*, int, int);
+
+  // Helper to print AEABI enum tag value.
+  static std::string
+  aeabi_enum_name(unsigned int);
+
+  // Return string value for TAG_CPU_name.
+  static std::string
+  tag_cpu_name_value(unsigned int);
+
+  // Merge object attributes from input object and those in the output.
+  void
+  merge_object_attributes(const char*, const Attributes_section_data*);
+
+  // Helper to get an AEABI object attribute
+  Object_attribute*
+  get_aeabi_object_attribute(int tag) const
+  {
+    Attributes_section_data* pasd = this->attributes_section_data_;
+    gold_assert(pasd != NULL);
+    Object_attribute* attr =
+      pasd->get_attribute(Object_attribute::OBJ_ATTR_PROC, tag);
+    gold_assert(attr != NULL);
+    return attr;
+  }
+
   //
   // Methods to support stub-generations.
   //
@@ -1645,6 +1722,8 @@ class Target_arm : public Sized_target<32, big_endian>
   bool should_force_pic_veneer_;
   // Map for locating Arm_input_sections.
   Arm_input_section_map arm_input_section_map_;
+  // Attributes section data in output.
+  Attributes_section_data* attributes_section_data_;
 };
 
 template<bool big_endian>
@@ -3763,6 +3842,37 @@ Arm_relobj<big_endian>::do_relocate_sections(
     }
 }
 
+// Helper functions for both Arm_relobj and Arm_dynobj to read ARM
+// ABI information.
+
+template<bool big_endian>
+Attributes_section_data*
+read_arm_attributes_section(
+    Object* object,
+    Read_symbols_data *sd)
+{
+  // Read the attributes section if there is one.
+  // We read from the end because gas seems to put it near the end of
+  // the section headers.
+  const size_t shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
+  const unsigned char *ps =
+    sd->section_headers->data() + shdr_size * (object->shnum() - 1);
+  for (unsigned int i = object->shnum(); i > 0; --i, ps -= shdr_size)
+    {
+      elfcpp::Shdr<32, big_endian> shdr(ps);
+      if (shdr.get_sh_type() == elfcpp::SHT_ARM_ATTRIBUTES)
+       {
+         section_offset_type section_offset = shdr.get_sh_offset();
+         section_size_type section_size =
+           convert_to_section_size_type(shdr.get_sh_size());
+         File_view* view = object->get_lasting_view(section_offset,
+                                                    section_size, true, false);
+         return new Attributes_section_data(view->data(), section_size);
+       }
+    }
+  return NULL;
+}
+
 // Read the symbol information.
 
 template<bool big_endian>
@@ -3778,6 +3888,8 @@ Arm_relobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
                                              true, false);
   elfcpp::Ehdr<32, big_endian> ehdr(pehdr);
   this->processor_specific_flags_ = ehdr.get_e_flags();
+  this->attributes_section_data_ =
+    read_arm_attributes_section<big_endian>(this, sd); 
 }
 
 // Arm_dynobj methods.
@@ -3797,6 +3909,8 @@ Arm_dynobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
                                              true, false);
   elfcpp::Ehdr<32, big_endian> ehdr(pehdr);
   this->processor_specific_flags_ = ehdr.get_e_flags();
+  this->attributes_section_data_ =
+    read_arm_attributes_section<big_endian>(this, sd); 
 }
 
 // Stub_addend_reader methods.
@@ -4576,6 +4690,9 @@ Target_arm<big_endian>::do_finalize_sections(
       this->merge_processor_specific_flags(
          arm_relobj->name(),
          arm_relobj->processor_specific_flags());
+      this->merge_object_attributes(arm_relobj->name().c_str(),
+                                   arm_relobj->attributes_section_data());
+
     } 
 
   for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin();
@@ -4587,8 +4704,16 @@ Target_arm<big_endian>::do_finalize_sections(
       this->merge_processor_specific_flags(
          arm_dynobj->name(),
          arm_dynobj->processor_specific_flags());
+      this->merge_object_attributes(arm_dynobj->name().c_str(),
+                                   arm_dynobj->attributes_section_data());
     }
 
+  // Check BLX use.
+  Object_attribute* attr =
+    this->get_aeabi_object_attribute(elfcpp::Tag_CPU_arch);
+  if (attr->int_value() > elfcpp::TAG_CPU_ARCH_V4)
+    this->set_may_use_blx(true);
   // Fill in some more dynamic tags.
   Output_data_dynamic* const odyn = layout->dynamic_data();
   if (odyn != NULL)
@@ -4638,11 +4763,11 @@ Target_arm<big_endian>::do_finalize_sections(
       // Create __exidx_start and __exdix_end symbols.
       symtab->define_in_output_data("__exidx_start", NULL, exidx_section,
                                    0, 0, elfcpp::STT_OBJECT,
-                                   elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN, 0,
+                                   elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0,
                                    false, false);
       symtab->define_in_output_data("__exidx_end", NULL, exidx_section,
                                    0, 0, elfcpp::STT_OBJECT,
-                                   elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN, 0,
+                                   elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, 0,
                                    true, false);
 
       // For the ARM target, we need to add a PT_ARM_EXIDX segment for
@@ -4657,6 +4782,13 @@ Target_arm<big_endian>::do_finalize_sections(
                                            false);
        }
     }
+
+  // Create an .ARM.attributes section if there is not one already.
+  Output_attributes_section_data* attributes_section =
+    new Output_attributes_section_data(*this->attributes_section_data_);
+  layout->add_output_section_data(".ARM.attributes",
+                                 elfcpp::SHT_ARM_ATTRIBUTES, 0,
+                                 attributes_section, false);
 }
 
 // Return whether a direct absolute static relocation needs to be applied.
@@ -5469,6 +5601,774 @@ Target_arm<big_endian>::do_make_elf_object(
     }
 }
 
+// Read the architecture from the Tag_also_compatible_with attribute, if any.
+// Returns -1 if no architecture could be read.
+// This is adapted from get_secondary_compatible_arch() in bfd/elf32-arm.c.
+
+template<bool big_endian>
+int
+Target_arm<big_endian>::get_secondary_compatible_arch(
+    const Attributes_section_data* pasd)
+{
+  const Object_attribute *known_attributes =
+    pasd->known_attributes(Object_attribute::OBJ_ATTR_PROC);
+
+  // Note: the tag and its argument below are uleb128 values, though
+  // currently-defined values fit in one byte for each.
+  const std::string& sv =
+    known_attributes[elfcpp::Tag_also_compatible_with].string_value();
+  if (sv.size() == 2
+      && sv.data()[0] == elfcpp::Tag_CPU_arch
+      && (sv.data()[1] & 128) != 128)
+   return sv.data()[1];
+
+  // This tag is "safely ignorable", so don't complain if it looks funny.
+  return -1;
+}
+
+// Set, or unset, the architecture of the Tag_also_compatible_with attribute.
+// The tag is removed if ARCH is -1.
+// This is adapted from set_secondary_compatible_arch() in bfd/elf32-arm.c.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::set_secondary_compatible_arch(
+    Attributes_section_data* pasd,
+    int arch)
+{
+  Object_attribute *known_attributes =
+    pasd->known_attributes(Object_attribute::OBJ_ATTR_PROC);
+
+  if (arch == -1)
+    {
+      known_attributes[elfcpp::Tag_also_compatible_with].set_string_value("");
+      return;
+    }
+
+  // Note: the tag and its argument below are uleb128 values, though
+  // currently-defined values fit in one byte for each.
+  char sv[3];
+  sv[0] = elfcpp::Tag_CPU_arch;
+  gold_assert(arch != 0);
+  sv[1] = arch;
+  sv[2] = '\0';
+
+  known_attributes[elfcpp::Tag_also_compatible_with].set_string_value(sv);
+}
+
+// Combine two values for Tag_CPU_arch, taking secondary compatibility tags
+// into account.
+// This is adapted from tag_cpu_arch_combine() in bfd/elf32-arm.c.
+
+template<bool big_endian>
+int
+Target_arm<big_endian>::tag_cpu_arch_combine(
+    const char* name,
+    int oldtag,
+    int* secondary_compat_out,
+    int newtag,
+    int secondary_compat)
+{
+#define T(X) elfcpp::TAG_CPU_ARCH_##X
+  static const int v6t2[] =
+    {
+      T(V6T2),   // PRE_V4.
+      T(V6T2),   // V4.
+      T(V6T2),   // V4T.
+      T(V6T2),   // V5T.
+      T(V6T2),   // V5TE.
+      T(V6T2),   // V5TEJ.
+      T(V6T2),   // V6.
+      T(V7),     // V6KZ.
+      T(V6T2)    // V6T2.
+    };
+  static const int v6k[] =
+    {
+      T(V6K),    // PRE_V4.
+      T(V6K),    // V4.
+      T(V6K),    // V4T.
+      T(V6K),    // V5T.
+      T(V6K),    // V5TE.
+      T(V6K),    // V5TEJ.
+      T(V6K),    // V6.
+      T(V6KZ),   // V6KZ.
+      T(V7),     // V6T2.
+      T(V6K)     // V6K.
+    };
+  static const int v7[] =
+    {
+      T(V7),     // PRE_V4.
+      T(V7),     // V4.
+      T(V7),     // V4T.
+      T(V7),     // V5T.
+      T(V7),     // V5TE.
+      T(V7),     // V5TEJ.
+      T(V7),     // V6.
+      T(V7),     // V6KZ.
+      T(V7),     // V6T2.
+      T(V7),     // V6K.
+      T(V7)      // V7.
+    };
+  static const int v6_m[] =
+    {
+      -1,        // PRE_V4.
+      -1,        // V4.
+      T(V6K),    // V4T.
+      T(V6K),    // V5T.
+      T(V6K),    // V5TE.
+      T(V6K),    // V5TEJ.
+      T(V6K),    // V6.
+      T(V6KZ),   // V6KZ.
+      T(V7),     // V6T2.
+      T(V6K),    // V6K.
+      T(V7),     // V7.
+      T(V6_M)    // V6_M.
+    };
+  static const int v6s_m[] =
+    {
+      -1,        // PRE_V4.
+      -1,        // V4.
+      T(V6K),    // V4T.
+      T(V6K),    // V5T.
+      T(V6K),    // V5TE.
+      T(V6K),    // V5TEJ.
+      T(V6K),    // V6.
+      T(V6KZ),   // V6KZ.
+      T(V7),     // V6T2.
+      T(V6K),    // V6K.
+      T(V7),     // V7.
+      T(V6S_M),  // V6_M.
+      T(V6S_M)   // V6S_M.
+    };
+  static const int v7e_m[] =
+    {
+      -1,      // PRE_V4.
+      -1,      // V4.
+      T(V7E_M),        // V4T.
+      T(V7E_M),        // V5T.
+      T(V7E_M),        // V5TE.
+      T(V7E_M),        // V5TEJ.
+      T(V7E_M),        // V6.
+      T(V7E_M),        // V6KZ.
+      T(V7E_M),        // V6T2.
+      T(V7E_M),        // V6K.
+      T(V7E_M),        // V7.
+      T(V7E_M),        // V6_M.
+      T(V7E_M),        // V6S_M.
+      T(V7E_M) // V7E_M.
+    };
+  static const int v4t_plus_v6_m[] =
+    {
+      -1,              // PRE_V4.
+      -1,              // V4.
+      T(V4T),          // V4T.
+      T(V5T),          // V5T.
+      T(V5TE),         // V5TE.
+      T(V5TEJ),                // V5TEJ.
+      T(V6),           // V6.
+      T(V6KZ),         // V6KZ.
+      T(V6T2),         // V6T2.
+      T(V6K),          // V6K.
+      T(V7),           // V7.
+      T(V6_M),         // V6_M.
+      T(V6S_M),                // V6S_M.
+      T(V7E_M),                // V7E_M.
+      T(V4T_PLUS_V6_M) // V4T plus V6_M.
+    };
+  static const int *comb[] =
+    {
+      v6t2,
+      v6k,
+      v7,
+      v6_m,
+      v6s_m,
+      v7e_m,
+      // Pseudo-architecture.
+      v4t_plus_v6_m
+    };
+
+  // Check we've not got a higher architecture than we know about.
+
+  if (oldtag >= elfcpp::MAX_TAG_CPU_ARCH || newtag >= elfcpp::MAX_TAG_CPU_ARCH)
+    {
+      gold_error(_("%s: unknown CPU architecture"), name);
+      return -1;
+    }
+
+  // Override old tag if we have a Tag_also_compatible_with on the output.
+
+  if ((oldtag == T(V6_M) && *secondary_compat_out == T(V4T))
+      || (oldtag == T(V4T) && *secondary_compat_out == T(V6_M)))
+    oldtag = T(V4T_PLUS_V6_M);
+
+  // And override the new tag if we have a Tag_also_compatible_with on the
+  // input.
+
+  if ((newtag == T(V6_M) && secondary_compat == T(V4T))
+      || (newtag == T(V4T) && secondary_compat == T(V6_M)))
+    newtag = T(V4T_PLUS_V6_M);
+
+  // Architectures before V6KZ add features monotonically.
+  int tagh = std::max(oldtag, newtag);
+  if (tagh <= elfcpp::TAG_CPU_ARCH_V6KZ)
+    return tagh;
+
+  int tagl = std::min(oldtag, newtag);
+  int result = comb[tagh - T(V6T2)][tagl];
+
+  // Use Tag_CPU_arch == V4T and Tag_also_compatible_with (Tag_CPU_arch V6_M)
+  // as the canonical version.
+  if (result == T(V4T_PLUS_V6_M))
+    {
+      result = T(V4T);
+      *secondary_compat_out = T(V6_M);
+    }
+  else
+    *secondary_compat_out = -1;
+
+  if (result == -1)
+    {
+      gold_error(_("%s: conflicting CPU architectures %d/%d"),
+                name, oldtag, newtag);
+      return -1;
+    }
+
+  return result;
+#undef T
+}
+
+// Helper to print AEABI enum tag value.
+
+template<bool big_endian>
+std::string
+Target_arm<big_endian>::aeabi_enum_name(unsigned int value)
+{
+  static const char *aeabi_enum_names[] =
+    { "", "variable-size", "32-bit", "" };
+  const size_t aeabi_enum_names_size =
+    sizeof(aeabi_enum_names) / sizeof(aeabi_enum_names[0]);
+
+  if (value < aeabi_enum_names_size)
+    return std::string(aeabi_enum_names[value]);
+  else
+    {
+      char buffer[100];
+      sprintf(buffer, "<unknown value %u>", value);
+      return std::string(buffer);
+    }
+}
+
+// Return the string value to store in TAG_CPU_name.
+
+template<bool big_endian>
+std::string
+Target_arm<big_endian>::tag_cpu_name_value(unsigned int value)
+{
+  static const char *name_table[] = {
+    // These aren't real CPU names, but we can't guess
+    // that from the architecture version alone.
+   "Pre v4",
+   "ARM v4",
+   "ARM v4T",
+   "ARM v5T",
+   "ARM v5TE",
+   "ARM v5TEJ",
+   "ARM v6",
+   "ARM v6KZ",
+   "ARM v6T2",
+   "ARM v6K",
+   "ARM v7",
+   "ARM v6-M",
+   "ARM v6S-M",
+   "ARM v7E-M"
+ };
+ const size_t name_table_size = sizeof(name_table) / sizeof(name_table[0]);
+
+  if (value < name_table_size)
+    return std::string(name_table[value]);
+  else
+    {
+      char buffer[100];
+      sprintf(buffer, "<unknown CPU value %u>", value);
+      return std::string(buffer);
+    } 
+}
+
+// Merge object attributes from input file called NAME with those of the
+// output.  The input object attributes are in the object pointed by PASD.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::merge_object_attributes(
+    const char* name,
+    const Attributes_section_data* pasd)
+{
+  // Return if there is no attributes section data.
+  if (pasd == NULL)
+    return;
+
+  // If output has no object attributes, just copy.
+  if (this->attributes_section_data_ == NULL)
+    {
+      this->attributes_section_data_ = new Attributes_section_data(*pasd);
+      return;
+    }
+
+  const int vendor = Object_attribute::OBJ_ATTR_PROC;
+  const Object_attribute* in_attr = pasd->known_attributes(vendor);
+  Object_attribute* out_attr =
+    this->attributes_section_data_->known_attributes(vendor);
+
+  // This needs to happen before Tag_ABI_FP_number_model is merged.  */
+  if (in_attr[elfcpp::Tag_ABI_VFP_args].int_value()
+      != out_attr[elfcpp::Tag_ABI_VFP_args].int_value())
+    {
+      // Ignore mismatches if the object doesn't use floating point.  */
+      if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value() == 0)
+       out_attr[elfcpp::Tag_ABI_VFP_args].set_int_value(
+           in_attr[elfcpp::Tag_ABI_VFP_args].int_value());
+      else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() != 0)
+        gold_error(_("%s uses VFP register arguments, output does not"),
+                  name);
+    }
+
+  for (int i = 4; i < Vendor_object_attributes::NUM_KNOWN_ATTRIBUTES; ++i)
+    {
+      // Merge this attribute with existing attributes.
+      switch (i)
+       {
+       case elfcpp::Tag_CPU_raw_name:
+       case elfcpp::Tag_CPU_name:
+         // These are merged after Tag_CPU_arch.
+         break;
+
+       case elfcpp::Tag_ABI_optimization_goals:
+       case elfcpp::Tag_ABI_FP_optimization_goals:
+         // Use the first value seen.
+         break;
+
+       case elfcpp::Tag_CPU_arch:
+         {
+           unsigned int saved_out_attr = out_attr->int_value();
+           // Merge Tag_CPU_arch and Tag_also_compatible_with.
+           int secondary_compat =
+             this->get_secondary_compatible_arch(pasd);
+           int secondary_compat_out =
+             this->get_secondary_compatible_arch(
+                 this->attributes_section_data_);
+           out_attr[i].set_int_value(
+               tag_cpu_arch_combine(name, out_attr[i].int_value(),
+                                    &secondary_compat_out,
+                                    in_attr[i].int_value(),
+                                    secondary_compat));
+           this->set_secondary_compatible_arch(this->attributes_section_data_,
+                                               secondary_compat_out);
+
+           // Merge Tag_CPU_name and Tag_CPU_raw_name.
+           if (out_attr[i].int_value() == saved_out_attr)
+             ; // Leave the names alone.
+           else if (out_attr[i].int_value() == in_attr[i].int_value())
+             {
+               // The output architecture has been changed to match the
+               // input architecture.  Use the input names.
+               out_attr[elfcpp::Tag_CPU_name].set_string_value(
+                   in_attr[elfcpp::Tag_CPU_name].string_value());
+               out_attr[elfcpp::Tag_CPU_raw_name].set_string_value(
+                   in_attr[elfcpp::Tag_CPU_raw_name].string_value());
+             }
+           else
+             {
+               out_attr[elfcpp::Tag_CPU_name].set_string_value("");
+               out_attr[elfcpp::Tag_CPU_raw_name].set_string_value("");
+             }
+
+           // If we still don't have a value for Tag_CPU_name,
+           // make one up now.  Tag_CPU_raw_name remains blank.
+           if (out_attr[elfcpp::Tag_CPU_name].string_value() == "")
+             {
+               const std::string cpu_name =
+                 this->tag_cpu_name_value(out_attr[i].int_value());
+               // FIXME:  If we see an unknown CPU, this will be set
+               // to "<unknown CPU n>", where n is the attribute value.
+               // This is different from BFD, which leaves the name alone.
+               out_attr[elfcpp::Tag_CPU_name].set_string_value(cpu_name);
+             }
+         }
+         break;
+
+       case elfcpp::Tag_ARM_ISA_use:
+       case elfcpp::Tag_THUMB_ISA_use:
+       case elfcpp::Tag_WMMX_arch:
+       case elfcpp::Tag_Advanced_SIMD_arch:
+         // ??? Do Advanced_SIMD (NEON) and WMMX conflict?
+       case elfcpp::Tag_ABI_FP_rounding:
+       case elfcpp::Tag_ABI_FP_exceptions:
+       case elfcpp::Tag_ABI_FP_user_exceptions:
+       case elfcpp::Tag_ABI_FP_number_model:
+       case elfcpp::Tag_VFP_HP_extension:
+       case elfcpp::Tag_CPU_unaligned_access:
+       case elfcpp::Tag_T2EE_use:
+       case elfcpp::Tag_Virtualization_use:
+       case elfcpp::Tag_MPextension_use:
+         // Use the largest value specified.
+         if (in_attr[i].int_value() > out_attr[i].int_value())
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+
+       case elfcpp::Tag_ABI_align8_preserved:
+       case elfcpp::Tag_ABI_PCS_RO_data:
+         // Use the smallest value specified.
+         if (in_attr[i].int_value() < out_attr[i].int_value())
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+
+       case elfcpp::Tag_ABI_align8_needed:
+         if ((in_attr[i].int_value() > 0 || out_attr[i].int_value() > 0)
+             && (in_attr[elfcpp::Tag_ABI_align8_preserved].int_value() == 0
+                 || (out_attr[elfcpp::Tag_ABI_align8_preserved].int_value()
+                     == 0)))
+           {
+             // This error message should be enabled once all non-conformant
+             // binaries in the toolchain have had the attributes set
+             // properly.
+             // gold_error(_("output 8-byte data alignment conflicts with %s"),
+             //            name);
+           }
+         // Fall through.
+       case elfcpp::Tag_ABI_FP_denormal:
+       case elfcpp::Tag_ABI_PCS_GOT_use:
+         {
+           // These tags have 0 = don't care, 1 = strong requirement,
+           // 2 = weak requirement.
+           static const int order_021[3] = {0, 2, 1};
+
+           // Use the "greatest" from the sequence 0, 2, 1, or the largest
+           // value if greater than 2 (for future-proofing).
+           if ((in_attr[i].int_value() > 2
+                && in_attr[i].int_value() > out_attr[i].int_value())
+               || (in_attr[i].int_value() <= 2
+                   && out_attr[i].int_value() <= 2
+                   && (order_021[in_attr[i].int_value()]
+                       > order_021[out_attr[i].int_value()])))
+             out_attr[i].set_int_value(in_attr[i].int_value());
+         }
+         break;
+
+       case elfcpp::Tag_CPU_arch_profile:
+         if (out_attr[i].int_value() != in_attr[i].int_value())
+           {
+             // 0 will merge with anything.
+             // 'A' and 'S' merge to 'A'.
+             // 'R' and 'S' merge to 'R'.
+             // 'M' and 'A|R|S' is an error.
+             if (out_attr[i].int_value() == 0
+                 || (out_attr[i].int_value() == 'S'
+                     && (in_attr[i].int_value() == 'A'
+                         || in_attr[i].int_value() == 'R')))
+               out_attr[i].set_int_value(in_attr[i].int_value());
+             else if (in_attr[i].int_value() == 0
+                      || (in_attr[i].int_value() == 'S'
+                          && (out_attr[i].int_value() == 'A'
+                              || out_attr[i].int_value() == 'R')))
+               ; // Do nothing.
+             else
+               {
+                 gold_error
+                   (_("conflicting architecture profiles %c/%c"),
+                    in_attr[i].int_value() ? in_attr[i].int_value() : '0',
+                    out_attr[i].int_value() ? out_attr[i].int_value() : '0');
+               }
+           }
+         break;
+       case elfcpp::Tag_VFP_arch:
+           {
+             static const struct
+             {
+                 int ver;
+                 int regs;
+             } vfp_versions[7] =
+               {
+                 {0, 0},
+                 {1, 16},
+                 {2, 16},
+                 {3, 32},
+                 {3, 16},
+                 {4, 32},
+                 {4, 16}
+               };
+
+             // Values greater than 6 aren't defined, so just pick the
+             // biggest.
+             if (in_attr[i].int_value() > 6
+                 && in_attr[i].int_value() > out_attr[i].int_value())
+               {
+                 *out_attr = *in_attr;
+                 break;
+               }
+             // The output uses the superset of input features
+             // (ISA version) and registers.
+             int ver = std::max(vfp_versions[in_attr[i].int_value()].ver,
+                                vfp_versions[out_attr[i].int_value()].ver);
+             int regs = std::max(vfp_versions[in_attr[i].int_value()].regs,
+                                 vfp_versions[out_attr[i].int_value()].regs);
+             // This assumes all possible supersets are also a valid
+             // options.
+             int newval;
+             for (newval = 6; newval > 0; newval--)
+               {
+                 if (regs == vfp_versions[newval].regs
+                     && ver == vfp_versions[newval].ver)
+                   break;
+               }
+             out_attr[i].set_int_value(newval);
+           }
+         break;
+       case elfcpp::Tag_PCS_config:
+         if (out_attr[i].int_value() == 0)
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         else if (in_attr[i].int_value() != 0 && out_attr[i].int_value() != 0)
+           {
+             // It's sometimes ok to mix different configs, so this is only
+             // a warning.
+             gold_warning(_("%s: conflicting platform configuration"), name);
+           }
+         break;
+       case elfcpp::Tag_ABI_PCS_R9_use:
+         if (in_attr[i].int_value() != out_attr[i].int_value()
+             && out_attr[i].int_value() != elfcpp::AEABI_R9_unused
+             && in_attr[i].int_value() != elfcpp::AEABI_R9_unused)
+           {
+             gold_error(_("%s: conflicting use of R9"), name);
+           }
+         if (out_attr[i].int_value() == elfcpp::AEABI_R9_unused)
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+       case elfcpp::Tag_ABI_PCS_RW_data:
+         if (in_attr[i].int_value() == elfcpp::AEABI_PCS_RW_data_SBrel
+             && (in_attr[elfcpp::Tag_ABI_PCS_R9_use].int_value()
+                 != elfcpp::AEABI_R9_SB)
+             && (out_attr[elfcpp::Tag_ABI_PCS_R9_use].int_value()
+                 != elfcpp::AEABI_R9_unused))
+           {
+             gold_error(_("%s: SB relative addressing conflicts with use "
+                          "of R9"),
+                        name);
+           }
+         // Use the smallest value specified.
+         if (in_attr[i].int_value() < out_attr[i].int_value())
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+       case elfcpp::Tag_ABI_PCS_wchar_t:
+         // FIXME: Make it possible to turn off this warning.
+         if (out_attr[i].int_value()
+             && in_attr[i].int_value()
+             && out_attr[i].int_value() != in_attr[i].int_value())
+           {
+             gold_warning(_("%s uses %u-byte wchar_t yet the output is to "
+                            "use %u-byte wchar_t; use of wchar_t values "
+                            "across objects may fail"),
+                          name, in_attr[i].int_value(),
+                          out_attr[i].int_value());
+           }
+         else if (in_attr[i].int_value() && !out_attr[i].int_value())
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+       case elfcpp::Tag_ABI_enum_size:
+         if (in_attr[i].int_value() != elfcpp::AEABI_enum_unused)
+           {
+             if (out_attr[i].int_value() == elfcpp::AEABI_enum_unused
+                 || out_attr[i].int_value() == elfcpp::AEABI_enum_forced_wide)
+               {
+                 // The existing object is compatible with anything.
+                 // Use whatever requirements the new object has.
+                 out_attr[i].set_int_value(in_attr[i].int_value());
+               }
+             // FIXME: Make it possible to turn off this warning.
+             else if (in_attr[i].int_value() != elfcpp::AEABI_enum_forced_wide
+                      && out_attr[i].int_value() != in_attr[i].int_value())
+               {
+                 unsigned int in_value = in_attr[i].int_value();
+                 unsigned int out_value = out_attr[i].int_value();
+                 gold_warning(_("%s uses %s enums yet the output is to use "
+                                "%s enums; use of enum values across objects "
+                                "may fail"),
+                              name,
+                              this->aeabi_enum_name(in_value).c_str(),
+                              this->aeabi_enum_name(out_value).c_str());
+               }
+           }
+         break;
+       case elfcpp::Tag_ABI_VFP_args:
+         // Aready done.
+         break;
+       case elfcpp::Tag_ABI_WMMX_args:
+         if (in_attr[i].int_value() != out_attr[i].int_value())
+           {
+             gold_error(_("%s uses iWMMXt register arguments, output does "
+                          "not"),
+                        name);
+           }
+         break;
+       case Object_attribute::Tag_compatibility:
+         // Merged in target-independent code.
+         break;
+       case elfcpp::Tag_ABI_HardFP_use:
+         // 1 (SP) and 2 (DP) conflict, so combine to 3 (SP & DP).
+         if ((in_attr[i].int_value() == 1 && out_attr[i].int_value() == 2)
+             || (in_attr[i].int_value() == 2 && out_attr[i].int_value() == 1))
+           out_attr[i].set_int_value(3);
+         else if (in_attr[i].int_value() > out_attr[i].int_value())
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+       case elfcpp::Tag_ABI_FP_16bit_format:
+         if (in_attr[i].int_value() != 0 && out_attr[i].int_value() != 0)
+           {
+             if (in_attr[i].int_value() != out_attr[i].int_value())
+               gold_error(_("fp16 format mismatch between %s and output"),
+                          name);
+           }
+         if (in_attr[i].int_value() != 0)
+           out_attr[i].set_int_value(in_attr[i].int_value());
+         break;
+
+       case elfcpp::Tag_nodefaults:
+         // This tag is set if it exists, but the value is unused (and is
+         // typically zero).  We don't actually need to do anything here -
+         // the merge happens automatically when the type flags are merged
+         // below.
+         break;
+       case elfcpp::Tag_also_compatible_with:
+         // Already done in Tag_CPU_arch.
+         break;
+       case elfcpp::Tag_conformance:
+         // Keep the attribute if it matches.  Throw it away otherwise.
+         // No attribute means no claim to conform.
+         if (in_attr[i].string_value() != out_attr[i].string_value())
+           out_attr[i].set_string_value("");
+         break;
+
+       default:
+         {
+           const char* err_object = NULL;
+
+           // The "known_obj_attributes" table does contain some undefined
+           // attributes.  Ensure that there are unused.
+           if (out_attr[i].int_value() != 0
+               || out_attr[i].string_value() != "")
+             err_object = "output";
+           else if (in_attr[i].int_value() != 0
+                    || in_attr[i].string_value() != "")
+             err_object = name;
+
+           if (err_object != NULL)
+             {
+               // Attribute numbers >=64 (mod 128) can be safely ignored.
+               if ((i & 127) < 64)
+                 gold_error(_("%s: unknown mandatory EABI object attribute "
+                              "%d"),
+                            err_object, i);
+               else
+                 gold_warning(_("%s: unknown EABI object attribute %d"),
+                              err_object, i);
+             }
+
+           // Only pass on attributes that match in both inputs.
+           if (!in_attr[i].matches(out_attr[i]))
+             {
+               out_attr[i].set_int_value(0);
+               out_attr[i].set_string_value("");
+             }
+         }
+       }
+
+      // If out_attr was copied from in_attr then it won't have a type yet.
+      if (in_attr[i].type() && !out_attr[i].type())
+       out_attr[i].set_type(in_attr[i].type());
+    }
+
+  // Merge Tag_compatibility attributes and any common GNU ones.
+  this->attributes_section_data_->merge(name, pasd);
+
+  // Check for any attributes not known on ARM.
+  typedef Vendor_object_attributes::Other_attributes Other_attributes;
+  const Other_attributes* in_other_attributes = pasd->other_attributes(vendor);
+  Other_attributes::const_iterator in_iter = in_other_attributes->begin();
+  Other_attributes* out_other_attributes =
+    this->attributes_section_data_->other_attributes(vendor);
+  Other_attributes::iterator out_iter = out_other_attributes->begin();
+
+  while (in_iter != in_other_attributes->end()
+        || out_iter != out_other_attributes->end())
+    {
+      const char* err_object = NULL;
+      int err_tag = 0;
+
+      // The tags for each list are in numerical order.
+      // If the tags are equal, then merge.
+      if (out_iter != out_other_attributes->end()
+         && (in_iter == in_other_attributes->end()
+             || in_iter->first > out_iter->first))
+       {
+         // This attribute only exists in output.  We can't merge, and we
+         // don't know what the tag means, so delete it.
+         err_object = "output";
+         err_tag = out_iter->first;
+         int saved_tag = out_iter->first;
+         delete out_iter->second;
+         out_other_attributes->erase(out_iter); 
+         out_iter = out_other_attributes->upper_bound(saved_tag);
+       }
+      else if (in_iter != in_other_attributes->end()
+              && (out_iter != out_other_attributes->end()
+                  || in_iter->first < out_iter->first))
+       {
+         // This attribute only exists in input. We can't merge, and we
+         // don't know what the tag means, so ignore it.
+         err_object = name;
+         err_tag = in_iter->first;
+         ++in_iter;
+       }
+      else // The tags are equal.
+       {
+         // As present, all attributes in the list are unknown, and
+         // therefore can't be merged meaningfully.
+         err_object = "output";
+         err_tag = out_iter->first;
+
+         //  Only pass on attributes that match in both inputs.
+         if (!in_iter->second->matches(*(out_iter->second)))
+           {
+             // No match.  Delete the attribute.
+             int saved_tag = out_iter->first;
+             delete out_iter->second;
+             out_other_attributes->erase(out_iter);
+             out_iter = out_other_attributes->upper_bound(saved_tag);
+           }
+         else
+           {
+             // Matched.  Keep the attribute and move to the next.
+             ++out_iter;
+             ++in_iter;
+           }
+       }
+
+      if (err_object)
+       {
+         // Attribute numbers >=64 (mod 128) can be safely ignored.  */
+         if ((err_tag & 127) < 64)
+           {
+             gold_error(_("%s: unknown mandatory EABI object attribute %d"),
+                        err_object, err_tag);
+           }
+         else
+           {
+             gold_warning(_("%s: unknown EABI object attribute %d"),
+                          err_object, err_tag);
+           }
+       }
+    }
+}
+
 // Return whether a relocation type used the LSB to distinguish THUMB
 // addresses.
 template<bool big_endian>
@@ -6044,7 +6944,53 @@ Target_arm<big_endian>::relocate_stub(
     }
 }
 
-// The selector for arm object files.
+// Determine whether an object attribute tag takes an integer, a
+// string or both.
+
+template<bool big_endian>
+int
+Target_arm<big_endian>::do_attribute_arg_type(int tag) const
+{
+  if (tag == Object_attribute::Tag_compatibility)
+    return (Object_attribute::ATTR_TYPE_FLAG_INT_VAL
+           | Object_attribute::ATTR_TYPE_FLAG_STR_VAL);
+  else if (tag == elfcpp::Tag_nodefaults)
+    return (Object_attribute::ATTR_TYPE_FLAG_INT_VAL
+           | Object_attribute::ATTR_TYPE_FLAG_NO_DEFAULT);
+  else if (tag == elfcpp::Tag_CPU_raw_name || tag == elfcpp::Tag_CPU_name)
+    return Object_attribute::ATTR_TYPE_FLAG_STR_VAL;
+  else if (tag < 32)
+    return Object_attribute::ATTR_TYPE_FLAG_INT_VAL;
+  else
+    return ((tag & 1) != 0
+           ? Object_attribute::ATTR_TYPE_FLAG_STR_VAL
+           : Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
+}
+
+// Reorder attributes.
+//
+// The ABI defines that Tag_conformance should be emitted first, and that
+// Tag_nodefaults should be second (if either is defined).  This sets those
+// two positions, and bumps up the position of all the remaining tags to
+// compensate.
+
+template<bool big_endian>
+int
+Target_arm<big_endian>::do_attributes_order(int num) const
+{
+  // Reorder the known object attributes in output.  We want to move
+  // Tag_conformance to position 4 and Tag_conformance to position 5
+  // and shift eveything between 4 .. Tag_conformance - 1 to make room.
+  if (num == 4)
+    return elfcpp::Tag_conformance;
+  if (num == 5)
+    return elfcpp::Tag_nodefaults;
+  if ((num - 2) < elfcpp::Tag_nodefaults)
+    return num - 2;
+  if ((num - 1) < elfcpp::Tag_conformance)
+    return num - 1;
+  return num;
+}
 
 template<bool big_endian>
 class Target_selector_arm : public Target_selector