Add support for .MIPS.abiflags and .gnu.attributes sections.
authorVladimir Radosavljevic <Vladimir.Radosavljevic@imgtec.com>
Fri, 10 Jun 2016 22:50:13 +0000 (15:50 -0700)
committerCary Coutant <ccoutant@gmail.com>
Sat, 11 Jun 2016 17:09:59 +0000 (10:09 -0700)
elfcpp/
* elfcpp.h (SHT_MIPS_ABIFLAGS): New enum constant.
* mips.h (EF_MIPS_FP64, EF_MIPS_NAN2008): New enum constants for
processor-specific flags.
(E_MIPS_MACH_5900): New enum constant for machine variant.
(AFL_REG_NONE, AFL_REG_32, AFL_REG_64, AFL_REG_128): New enum
constants.
(AFL_ASE_DSP, AFL_ASE_DSPR2, AFL_ASE_EVA, AFL_ASE_MCU,
AFL_ASE_MDMX, AFL_ASE_MIPS3D, AFL_ASE_MT, AFL_ASE_SMARTMIPS,
AFL_ASE_VIRT, AFL_ASE_MSA, AFL_ASE_MIPS16, AFL_ASE_MICROMIPS,
AFL_ASE_XPA): Likewise.
(AFL_EXT_XLR, AFL_EXT_OCTEON2, AFL_EXT_OCTEONP,
AFL_EXT_LOONGSON_3A, AFL_EXT_OCTEON, AFL_EXT_5900, AFL_EXT_4650,
AFL_EXT_4010, AFL_EXT_4100, AFL_EXT_3900, AFL_EXT_10000,
AFL_EXT_SB1, AFL_EXT_4111, AFL_EXT_4120, AFL_EXT_5400,
AFL_EXT_5500, AFL_EXT_LOONGSON_2E, AFL_EXT_LOONGSON_2F,
AFL_EXT_OCTEON3): Likewise.
(Tag_GNU_MIPS_ABI_FP, Tag_GNU_MIPS_ABI_MSA): Likewise.
(Val_GNU_MIPS_ABI_FP_ANY, Val_GNU_MIPS_ABI_FP_DOUBLE,
Val_GNU_MIPS_ABI_FP_SINGLE, Val_GNU_MIPS_ABI_FP_SOFT,
Val_GNU_MIPS_ABI_FP_OLD_64,Val_GNU_MIPS_ABI_FP_XX,
Val_GNU_MIPS_ABI_FP_64, Val_GNU_MIPS_ABI_FP_64A,
Val_GNU_MIPS_ABI_FP_NAN2008, Val_GNU_MIPS_ABI_MSA_ANY,
Val_GNU_MIPS_ABI_MSA_128): Likewise.
(AFL_FLAGS1_ODDSPREG): New enum constant.
gold/
* mips.cc (struct Mips_abiflags): New struct.
(Mips_relobj::Mips_relobj): Initialize attributes_section_data_
and abiflags_.
(Mips_relobj::~Mips_relobj): Delete object pointed by
attributes_section_data_.
(Mips_relobj::abiflags): New method.
(Mips_relobj::attributes_section_data): Likewise.
(Mips_relobj::attributes_section_data_): New data member.
(Mips_relobj::abiflags_): Likewise.
(class Mips_output_section_abiflags): New class.
(Target_mips::Target_mips): Initialize attributes_section_data_,
abiflags_ and has_abiflags_section_.
(Target_mips::do_should_include_section): Don't emit input
.MIPS.abiflags sections to output .MIPS.abiflags.
(Target_mips::Mips_mach): Add new enum constants.
(Target_mips::mips_isa_ext_mach): New method.
(Target_mips::mips_isa_ext): Likewise.
(Target_mips::update_abiflags_isa): Likewise.
(Target_mips::infer_abiflags): Likewise.
(Target_mips::create_abiflags): Likewise.
(Target_mips::fp_abi_string): Likewise.
(Target_mips::select_fp_abi): Likewise.
(Target_mips::merge_obj_attributes): Likewise.
(Target_mips::merge_obj_abiflags): Likewise.
(Target_mips::level_rev): Likewise.
(Target_mips::merge_obj_e_flags): Rename from
merge_processor_specific_flags. Remove dyn_obj argument,
call update_abiflags_isa when needed, compare NaN encodings and
compare FP64 state.
(Target_mips::add_machine_extensions): Add two machine extensions
and fix one.
(Target_mips::attributes_section_data_): New data member.
(Target_mips::abiflags_): Likewise.
(Target_mips::has_abiflags_section_): Likewise.
(Mips_relobj::do_read_symbols): Read .gnu.attributes and
.MIPS.abiflags sections if they exists.
(Target_mips::elf_mips_mach): Add E_MIPS_MACH_5900 and
E_MIPS_MACH_OCTEON3 support.
(Target_mips::do_adjust_elf_header): Setup EI_ABIVERSION flag.
(Target_mips::do_finalize_sections): Merge .gnu.attributes and
.MIPS.abiflags sections from input. Create these sections if
needed.
(Target_mips::elf_mips_mach_name): Add E_MIPS_MACH_5900 and
E_MIPS_MACH_OCTEON3 support, and change strings for
E_MIPS_MACH_LS2E, E_MIPS_MACH_LS2F and E_MIPS_MACH_LS3A just
to match bfd.

elfcpp/ChangeLog
elfcpp/elfcpp.h
elfcpp/mips.h
gold/ChangeLog
gold/mips.cc

index a469f86eec7b511d4ae0f5978812819714306702..93b2189492d1ea1ddd1b5d22e57364d09340f0c7 100644 (file)
@@ -1,3 +1,30 @@
+2016-06-10  Vladimir Radosavljevic  <Vladimir.Radosavljevic@imgtec.com>
+
+       * elfcpp.h (SHT_MIPS_ABIFLAGS): New enum constant.
+       * mips.h (EF_MIPS_FP64, EF_MIPS_NAN2008): New enum constants for
+       processor-specific flags.
+       (E_MIPS_MACH_5900): New enum constant for machine variant.
+       (AFL_REG_NONE, AFL_REG_32, AFL_REG_64, AFL_REG_128): New enum
+       constants.
+       (AFL_ASE_DSP, AFL_ASE_DSPR2, AFL_ASE_EVA, AFL_ASE_MCU,
+       AFL_ASE_MDMX, AFL_ASE_MIPS3D, AFL_ASE_MT, AFL_ASE_SMARTMIPS,
+       AFL_ASE_VIRT, AFL_ASE_MSA, AFL_ASE_MIPS16, AFL_ASE_MICROMIPS,
+       AFL_ASE_XPA): Likewise.
+       (AFL_EXT_XLR, AFL_EXT_OCTEON2, AFL_EXT_OCTEONP,
+       AFL_EXT_LOONGSON_3A, AFL_EXT_OCTEON, AFL_EXT_5900, AFL_EXT_4650,
+       AFL_EXT_4010, AFL_EXT_4100, AFL_EXT_3900, AFL_EXT_10000,
+       AFL_EXT_SB1, AFL_EXT_4111, AFL_EXT_4120, AFL_EXT_5400,
+       AFL_EXT_5500, AFL_EXT_LOONGSON_2E, AFL_EXT_LOONGSON_2F,
+       AFL_EXT_OCTEON3): Likewise.
+       (Tag_GNU_MIPS_ABI_FP, Tag_GNU_MIPS_ABI_MSA): Likewise.
+       (Val_GNU_MIPS_ABI_FP_ANY, Val_GNU_MIPS_ABI_FP_DOUBLE,
+       Val_GNU_MIPS_ABI_FP_SINGLE, Val_GNU_MIPS_ABI_FP_SOFT,
+       Val_GNU_MIPS_ABI_FP_OLD_64,Val_GNU_MIPS_ABI_FP_XX,
+       Val_GNU_MIPS_ABI_FP_64, Val_GNU_MIPS_ABI_FP_64A,
+       Val_GNU_MIPS_ABI_FP_NAN2008, Val_GNU_MIPS_ABI_MSA_ANY,
+       Val_GNU_MIPS_ABI_MSA_128): Likewise.
+       (AFL_FLAGS1_ODDSPREG): New enum constant.
+
 2016-03-18  Vladimir Radosavljevic  <vladimir.radosavljevic@imgtec.com>
 
        * mips.h (abi_64): Remove.
index 3d7039a33bd674698c086169a2d8ac2268d646d7..f39a135ab3750bae5593d4c23b5050e8d438cb5d 100644 (file)
@@ -406,6 +406,8 @@ enum SHT
   SHT_MIPS_REGINFO = 0x70000006,
   // Section contains miscellaneous options.
   SHT_MIPS_OPTIONS = 0x7000000d,
+  // ABI related flags section.
+  SHT_MIPS_ABIFLAGS = 0x7000002a,
 
   // AARCH64-specific section type.
   SHT_AARCH64_ATTRIBUTES = 0x70000003,
index 10137ec22d6052e58fd2c507db1197095d4a7d14..a7a9799af11aba0ae1b94766de4d78361261057a 100644 (file)
@@ -193,6 +193,10 @@ enum
   // Indicates code compiled for a 64-bit machine in 32-bit mode.
   // (regs are 32-bits wide.)
   EF_MIPS_32BITMODE = 0x00000100,
+  // 32-bit machine but FP registers are 64 bit (-mfp64).
+  EF_MIPS_FP64 = 0x00000200,
+  /// Code in file uses the IEEE 754-2008 NaN encoding convention.
+  EF_MIPS_NAN2008 = 0x00000400,
   // MIPS dynamic
   EF_MIPS_DYNAMIC = 0x40
 };
@@ -220,6 +224,7 @@ enum
   E_MIPS_MACH_OCTEON2 = 0x008d0000,
   E_MIPS_MACH_OCTEON3 = 0x008e0000,
   E_MIPS_MACH_5400 = 0x00910000,
+  E_MIPS_MACH_5900 = 0x00920000,
   E_MIPS_MACH_5500 = 0x00980000,
   E_MIPS_MACH_9000 = 0x00990000,
   E_MIPS_MACH_LS2E = 0x00A00000,
@@ -256,6 +261,141 @@ enum
   E_MIPS_ARCH_64R6 = 0xa0000000,
 };
 
+// Values for the xxx_size bytes of an ABI flags structure.
+enum
+{
+  // No registers.
+  AFL_REG_NONE = 0x00,
+  // 32-bit registers.
+  AFL_REG_32 = 0x01,
+  // 64-bit registers.
+  AFL_REG_64 = 0x02,
+  // 128-bit registers.
+  AFL_REG_128 = 0x03
+};
+
+// Masks for the ases word of an ABI flags structure.
+enum
+{
+  // DSP ASE.
+  AFL_ASE_DSP = 0x00000001,
+  // DSP R2 ASE.
+  AFL_ASE_DSPR2 = 0x00000002,
+  // Enhanced VA Scheme.
+  AFL_ASE_EVA = 0x00000004,
+  // MCU (MicroController) ASE.
+  AFL_ASE_MCU = 0x00000008,
+  // MDMX ASE.
+  AFL_ASE_MDMX = 0x00000010,
+  // MIPS-3D ASE.
+  AFL_ASE_MIPS3D = 0x00000020,
+  // MT ASE.
+  AFL_ASE_MT  = 0x00000040,
+  // SmartMIPS ASE.
+  AFL_ASE_SMARTMIPS = 0x00000080,
+  // VZ ASE.
+  AFL_ASE_VIRT = 0x00000100,
+  // MSA ASE.
+  AFL_ASE_MSA = 0x00000200,
+  // MIPS16 ASE.
+  AFL_ASE_MIPS16 = 0x00000400,
+  // MICROMIPS ASE.
+  AFL_ASE_MICROMIPS = 0x00000800,
+  // XPA ASE.
+  AFL_ASE_XPA = 0x00001000
+};
+
+// Values for the isa_ext word of an ABI flags structure.
+enum
+{
+  // RMI Xlr instruction.
+  AFL_EXT_XLR = 1,
+  // Cavium Networks Octeon2.
+  AFL_EXT_OCTEON2 = 2,
+  // Cavium Networks OcteonP.
+  AFL_EXT_OCTEONP = 3,
+  // Loongson 3A.
+  AFL_EXT_LOONGSON_3A = 4,
+  // Cavium Networks Octeon.
+  AFL_EXT_OCTEON = 5,
+  // MIPS R5900 instruction.
+  AFL_EXT_5900 = 6,
+  // MIPS R4650 instruction.
+  AFL_EXT_4650 = 7,
+  // LSI R4010 instruction.
+  AFL_EXT_4010 = 8,
+  // NEC VR4100 instruction.
+  AFL_EXT_4100 = 9,
+  // Toshiba R3900 instruction.
+  AFL_EXT_3900 = 10,
+  // MIPS R10000 instruction.
+  AFL_EXT_10000 = 11,
+  // Broadcom SB-1 instruction.
+  AFL_EXT_SB1 = 12,
+  // NEC VR4111/VR4181 instruction.
+  AFL_EXT_4111 = 13,
+  // NEC VR4120 instruction.
+  AFL_EXT_4120 = 14,
+  // NEC VR5400 instruction.
+  AFL_EXT_5400 = 15,
+  // NEC VR5500 instruction.
+  AFL_EXT_5500 = 16,
+  // ST Microelectronics Loongson 2E.
+  AFL_EXT_LOONGSON_2E = 17,
+  // ST Microelectronics Loongson 2F.
+  AFL_EXT_LOONGSON_2F = 18,
+  // Cavium Networks Octeon3.
+  AFL_EXT_OCTEON3 = 19
+};
+
+// Masks for the flags1 word of an ABI flags structure.
+enum
+{
+  // Uses odd single-precision registers.
+  AFL_FLAGS1_ODDSPREG = 1
+};
+
+// Object attribute tags.
+enum
+{
+  // 0-3 are generic.
+  // Floating-point ABI used by this object file.
+  Tag_GNU_MIPS_ABI_FP = 4,
+  // MSA ABI used by this object file.
+  Tag_GNU_MIPS_ABI_MSA = 8
+};
+
+// Object attribute values.
+enum
+{
+  // Values defined for Tag_GNU_MIPS_ABI_FP.
+  // Not tagged or not using any ABIs affected by the differences.
+  Val_GNU_MIPS_ABI_FP_ANY = 0,
+  // Using hard-float -mdouble-float.
+  Val_GNU_MIPS_ABI_FP_DOUBLE = 1,
+  // Using hard-float -msingle-float.
+  Val_GNU_MIPS_ABI_FP_SINGLE = 2,
+  // Using soft-float.
+  Val_GNU_MIPS_ABI_FP_SOFT = 3,
+  // Using -mips32r2 -mfp64.
+  Val_GNU_MIPS_ABI_FP_OLD_64 = 4,
+  // Using -mfpxx
+  Val_GNU_MIPS_ABI_FP_XX = 5,
+  // Using -mips32r2 -mfp64.
+  Val_GNU_MIPS_ABI_FP_64 = 6,
+  // Using -mips32r2 -mfp64 -mno-odd-spreg.
+  Val_GNU_MIPS_ABI_FP_64A = 7,
+  // This is reserved for backward-compatibility with an earlier
+  // implementation of the MIPS NaN2008 functionality.
+  Val_GNU_MIPS_ABI_FP_NAN2008 = 8,
+
+  // Values defined for Tag_GNU_MIPS_ABI_MSA.
+  // Not tagged or not using any ABIs affected by the differences.
+  Val_GNU_MIPS_ABI_MSA_ANY = 0,
+  // Using 128-bit MSA.
+  Val_GNU_MIPS_ABI_MSA_128 = 1
+};
+
 enum
 {
   // Mask to extract ABI version, not really a flag value.
index 9552d5501264a4e5894f1e937bcb49d6e55eb80d..c1b833b6356044b2e0e301f4e6c3d6843b28a6d0 100644 (file)
@@ -1,3 +1,52 @@
+2016-06-10  Vladimir Radosavljevic  <Vladimir.Radosavljevic@imgtec.com>
+
+       * mips.cc (struct Mips_abiflags): New struct.
+       (Mips_relobj::Mips_relobj): Initialize attributes_section_data_
+       and abiflags_.
+       (Mips_relobj::~Mips_relobj): Delete object pointed by
+       attributes_section_data_.
+       (Mips_relobj::abiflags): New method.
+       (Mips_relobj::attributes_section_data): Likewise.
+       (Mips_relobj::attributes_section_data_): New data member.
+       (Mips_relobj::abiflags_): Likewise.
+       (class Mips_output_section_abiflags): New class.
+       (Target_mips::Target_mips): Initialize attributes_section_data_,
+       abiflags_ and has_abiflags_section_.
+       (Target_mips::do_should_include_section): Don't emit input
+       .MIPS.abiflags sections to output .MIPS.abiflags.
+       (Target_mips::Mips_mach): Add new enum constants.
+       (Target_mips::mips_isa_ext_mach): New method.
+       (Target_mips::mips_isa_ext): Likewise.
+       (Target_mips::update_abiflags_isa): Likewise.
+       (Target_mips::infer_abiflags): Likewise.
+       (Target_mips::create_abiflags): Likewise.
+       (Target_mips::fp_abi_string): Likewise.
+       (Target_mips::select_fp_abi): Likewise.
+       (Target_mips::merge_obj_attributes): Likewise.
+       (Target_mips::merge_obj_abiflags): Likewise.
+       (Target_mips::level_rev): Likewise.
+       (Target_mips::merge_obj_e_flags): Rename from
+       merge_processor_specific_flags. Remove dyn_obj argument,
+       call update_abiflags_isa when needed, compare NaN encodings and
+       compare FP64 state.
+       (Target_mips::add_machine_extensions): Add two machine extensions
+       and fix one.
+       (Target_mips::attributes_section_data_): New data member.
+       (Target_mips::abiflags_): Likewise.
+       (Target_mips::has_abiflags_section_): Likewise.
+       (Mips_relobj::do_read_symbols): Read .gnu.attributes and
+       .MIPS.abiflags sections if they exists.
+       (Target_mips::elf_mips_mach): Add E_MIPS_MACH_5900 and
+       E_MIPS_MACH_OCTEON3 support.
+       (Target_mips::do_adjust_elf_header): Setup EI_ABIVERSION flag.
+       (Target_mips::do_finalize_sections): Merge .gnu.attributes and
+       .MIPS.abiflags sections from input. Create these sections if
+       needed.
+       (Target_mips::elf_mips_mach_name): Add E_MIPS_MACH_5900 and
+       E_MIPS_MACH_OCTEON3 support, and change strings for
+       E_MIPS_MACH_LS2E, E_MIPS_MACH_LS2F and E_MIPS_MACH_LS3A just
+       to match bfd.
+
 2016-06-10  Vladimir Radosavljevic  <Vladimir.Radosavljevic@imgtec.com>
 
        * mips.cc (Mips_relobj::Mips_relobj): Initialize
index 59ce88e30988412ef91e866d66023d423f88ac3a..42ec64b7be63fad2bc2d0909721a59ceea978bbc 100644 (file)
@@ -44,6 +44,7 @@
 #include "tls.h"
 #include "errors.h"
 #include "gc.h"
+#include "attributes.h"
 #include "nacl.h"
 
 namespace
@@ -954,6 +955,43 @@ struct got16_addend
   Mips_address addend;
 };
 
+// .MIPS.abiflags section content
+
+template<bool big_endian>
+struct Mips_abiflags
+{
+  typedef typename elfcpp::Swap<8, big_endian>::Valtype Valtype8;
+  typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype16;
+  typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype32;
+
+  Mips_abiflags()
+    : version(0), isa_level(0), isa_rev(0), gpr_size(0), cpr1_size(0),
+      cpr2_size(0), fp_abi(0), isa_ext(0), ases(0), flags1(0), flags2(0)
+  { }
+
+  // Version of flags structure.
+  Valtype16 version;
+  // The level of the ISA: 1-5, 32, 64.
+  Valtype8 isa_level;
+  // The revision of ISA: 0 for MIPS V and below, 1-n otherwise.
+  Valtype8 isa_rev;
+  // The size of general purpose registers.
+  Valtype8 gpr_size;
+  // The size of co-processor 1 registers.
+  Valtype8 cpr1_size;
+  // The size of co-processor 2 registers.
+  Valtype8 cpr2_size;
+  // The floating-point ABI.
+  Valtype8 fp_abi;
+  // Processor-specific extension.
+  Valtype32 isa_ext;
+  // Mask of ASEs used.
+  Valtype32 ases;
+  // Mask of general flags.
+  Valtype32 flags1;
+  Valtype32 flags2;
+};
+
 // Mips_symbol class.  Holds additional symbol information needed for Mips.
 
 template<int size>
@@ -1547,15 +1585,15 @@ class Mips_relobj : public Sized_relobj_file<size, big_endian>
       local_mips16_call_stubs_(), gp_(0), has_reginfo_section_(false),
       got_info_(NULL), section_is_mips16_fn_stub_(),
       section_is_mips16_call_stub_(), section_is_mips16_call_fp_stub_(),
-      pdr_shndx_(-1U), gprmask_(0), cprmask1_(0), cprmask2_(0), cprmask3_(0),
-      cprmask4_(0)
+      pdr_shndx_(-1U), attributes_section_data_(NULL), abiflags_(NULL),
+      gprmask_(0), cprmask1_(0), cprmask2_(0), cprmask3_(0), cprmask4_(0)
   {
     this->is_pic_ = (ehdr.get_e_flags() & elfcpp::EF_MIPS_PIC) != 0;
     this->is_n32_ = elfcpp::abi_n32(ehdr.get_e_flags());
   }
 
   ~Mips_relobj()
-  { }
+  { delete this->attributes_section_data_; }
 
   // Downcast a base pointer to a Mips_relobj pointer.  This is
   // not type-safe but we only use Mips_relobj not the base class.
@@ -1814,6 +1852,16 @@ class Mips_relobj : public Sized_relobj_file<size, big_endian>
   cprmask4() const
   { return this->cprmask4_; }
 
+  // This is the contents of the .MIPS.abiflags section if there is one.
+  Mips_abiflags<big_endian>*
+  abiflags()
+  { return this->abiflags_; }
+
+  // This is the contents of the .gnu.attribute section if there is one.
+  const Attributes_section_data*
+  attributes_section_data() const
+  { return this->attributes_section_data_; }
+
  protected:
   // Count the local symbols.
   void
@@ -1887,6 +1935,12 @@ class Mips_relobj : public Sized_relobj_file<size, big_endian>
   // .pdr section index.
   unsigned int pdr_shndx_;
 
+  // Object attributes if there is a .gnu.attributes section or NULL.
+  Attributes_section_data* attributes_section_data_;
+
+  // Object abiflags if there is a .MIPS.abiflags section or NULL.
+  Mips_abiflags<big_endian>* abiflags_;
+
   // gprmask from the .reginfo section of this object.
   Valtype gprmask_;
   // cprmask1 from the .reginfo section of this object.
@@ -2760,6 +2814,29 @@ class Mips_output_section_reginfo : public Output_section_data
   Valtype cprmask4_;
 };
 
+// This class handles .MIPS.abiflags output section.
+
+template<int size, bool big_endian>
+class Mips_output_section_abiflags : public Output_section_data
+{
+ public:
+  Mips_output_section_abiflags(const Mips_abiflags<big_endian>& abiflags)
+    : Output_section_data(24, 8, true), abiflags_(abiflags)
+  { }
+
+ protected:
+  // Write to a map file.
+  void
+  do_print_to_mapfile(Mapfile* mapfile) const
+  { mapfile->print_output_data(this, _(".MIPS.abiflags")); }
+
+  void
+  do_write(Output_file* of);
+
+ private:
+  const Mips_abiflags<big_endian>& abiflags_;
+};
+
 // The MIPS target has relocation types which default handling of relocatable
 // relocation cannot process.  So we have to extend the default code.
 
@@ -3208,9 +3285,10 @@ class Target_mips : public Sized_target<size, big_endian>
  public:
   Target_mips(const Target::Target_info* info = &mips_info)
     : Sized_target<size, big_endian>(info), got_(NULL), gp_(NULL), plt_(NULL),
-      got_plt_(NULL), rel_dyn_(NULL), copy_relocs_(),
-      dyn_relocs_(), la25_stub_(NULL), mips_mach_extensions_(),
-      mips_stubs_(NULL), mach_(0), layout_(NULL), got16_addends_(),
+      got_plt_(NULL), rel_dyn_(NULL), copy_relocs_(), dyn_relocs_(),
+      la25_stub_(NULL), mips_mach_extensions_(), mips_stubs_(NULL),
+      attributes_section_data_(NULL), abiflags_(NULL), mach_(0), layout_(NULL),
+      got16_addends_(), has_abiflags_section_(false),
       entry_symbol_is_compressed_(false), insn32_(false)
   {
     this->add_machine_extensions();
@@ -3441,10 +3519,14 @@ class Target_mips : public Sized_target<size, big_endian>
   do_has_custom_set_dynsym_indexes() const
   { return true; }
 
-  // Don't emit input .reginfo sections to output .reginfo.
+  // Don't emit input .reginfo/.MIPS.abiflags sections to
+  // output .reginfo/.MIPS.abiflags.
   bool
   do_should_include_section(elfcpp::Elf_Word sh_type) const
-  { return sh_type != elfcpp::SHT_MIPS_REGINFO; }
+  {
+    return ((sh_type != elfcpp::SHT_MIPS_REGINFO)
+             && (sh_type != elfcpp::SHT_MIPS_ABIFLAGS));
+  }
 
   // Set the dynamic symbol indexes.  INDEX is the index of the first
   // global dynamic symbol.  Pointers to the symbols are stored into the
@@ -3476,7 +3558,7 @@ class Target_mips : public Sized_target<size, big_endian>
   }
 
   // Whether the output has microMIPS code.  This is valid only after
-  // merge_processor_specific_flags() is called.
+  // merge_obj_e_flags() is called.
   bool
   is_output_micromips() const
   {
@@ -3485,7 +3567,7 @@ class Target_mips : public Sized_target<size, big_endian>
   }
 
   // Whether the output uses N32 ABI.  This is valid only after
-  // merge_processor_specific_flags() is called.
+  // merge_obj_e_flags() is called.
   bool
   is_output_n32() const
   {
@@ -3499,7 +3581,7 @@ class Target_mips : public Sized_target<size, big_endian>
   { return size == 64; }
 
   // Whether the output uses NEWABI.  This is valid only after
-  // merge_processor_specific_flags() is called.
+  // merge_obj_e_flags() is called.
   bool
   is_output_newabi() const
   { return this->is_output_n32() || this->is_output_n64(); }
@@ -3817,6 +3899,7 @@ class Target_mips : public Sized_target<size, big_endian>
     mach_mips5000             = 5000,
     mach_mips5400             = 5400,
     mach_mips5500             = 5500,
+    mach_mips5900             = 5900,
     mach_mips6000             = 6000,
     mach_mips7000             = 7000,
     mach_mips8000             = 8000,
@@ -3834,11 +3917,16 @@ class Target_mips : public Sized_target<size, big_endian>
     mach_mips_octeon          = 6501,
     mach_mips_octeonp         = 6601,
     mach_mips_octeon2         = 6502,
+    mach_mips_octeon3         = 6503,
     mach_mips_xlr             = 887682,   // decimal 'XLR'
     mach_mipsisa32            = 32,
     mach_mipsisa32r2          = 33,
+    mach_mipsisa32r3          = 34,
+    mach_mipsisa32r5          = 36,
     mach_mipsisa64            = 64,
     mach_mipsisa64r2          = 65,
+    mach_mipsisa64r3          = 66,
+    mach_mipsisa64r5          = 68,
     mach_mips_micromips       = 96
   };
 
@@ -3846,13 +3934,55 @@ class Target_mips : public Sized_target<size, big_endian>
   unsigned int
   elf_mips_mach(elfcpp::Elf_Word);
 
+  // Return the MACH for each .MIPS.abiflags ISA Extension.
+  unsigned int
+  mips_isa_ext_mach(unsigned int);
+
+  // Return the .MIPS.abiflags value representing each ISA Extension.
+  unsigned int
+  mips_isa_ext(unsigned int);
+
+  // Update the isa_level, isa_rev, isa_ext fields of abiflags.
+  void
+  update_abiflags_isa(const std::string&, elfcpp::Elf_Word,
+                      Mips_abiflags<big_endian>*);
+
+  // Infer the content of the ABI flags based on the elf header.
+  void
+  infer_abiflags(Mips_relobj<size, big_endian>*, Mips_abiflags<big_endian>*);
+
+  // Create abiflags from elf header or from .MIPS.abiflags section.
+  void
+  create_abiflags(Mips_relobj<size, big_endian>*, Mips_abiflags<big_endian>*);
+
+  // Return the meaning of fp_abi, or "unknown" if not known.
+  const char*
+  fp_abi_string(int);
+
+  // Select fp_abi.
+  int
+  select_fp_abi(const std::string&, int, int);
+
+  // Merge attributes from input object.
+  void
+  merge_obj_attributes(const std::string&, const Attributes_section_data*);
+
+  // Merge abiflags from input object.
+  void
+  merge_obj_abiflags(const std::string&, Mips_abiflags<big_endian>*);
+
   // Check whether machine EXTENSION is an extension of machine BASE.
   bool
   mips_mach_extends(unsigned int, unsigned int);
 
-  // Merge processor specific flags.
+  // Merge file header flags from input object.
   void
-  merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word, bool);
+  merge_obj_e_flags(const std::string&, elfcpp::Elf_Word);
+
+  // Encode ISA level and revision as a single value.
+  int
+  level_rev(unsigned char isa_level, unsigned char isa_rev) const
+  { return (isa_level << 3) | isa_rev; }
 
   // True if we are linking for CPUs that are faster if JAL is converted to BAL.
   static inline bool
@@ -3942,15 +4072,16 @@ class Target_mips : public Sized_target<size, big_endian>
   add_machine_extensions()
   {
     // MIPS64r2 extensions.
+    this->add_extension(mach_mips_octeon3, mach_mips_octeon2);
     this->add_extension(mach_mips_octeon2, mach_mips_octeonp);
     this->add_extension(mach_mips_octeonp, mach_mips_octeon);
     this->add_extension(mach_mips_octeon, mach_mipsisa64r2);
+    this->add_extension(mach_mips_loongson_3a, mach_mipsisa64r2);
 
     // MIPS64 extensions.
     this->add_extension(mach_mipsisa64r2, mach_mipsisa64);
     this->add_extension(mach_mips_sb1, mach_mipsisa64);
     this->add_extension(mach_mips_xlr, mach_mipsisa64);
-    this->add_extension(mach_mips_loongson_3a, mach_mipsisa64);
 
     // MIPS V extensions.
     this->add_extension(mach_mipsisa64, mach_mips5);
@@ -3989,6 +4120,7 @@ class Target_mips : public Sized_target<size, big_endian>
     this->add_extension(mach_mips4300, mach_mips4000);
     this->add_extension(mach_mips4100, mach_mips4000);
     this->add_extension(mach_mips4010, mach_mips4000);
+    this->add_extension(mach_mips5900, mach_mips4000);
 
     // MIPS32 extensions.
     this->add_extension(mach_mipsisa32r2, mach_mipsisa32);
@@ -4044,11 +4176,19 @@ class Target_mips : public Sized_target<size, big_endian>
   // .MIPS.stubs
   Mips_output_data_mips_stubs<size, big_endian>* mips_stubs_;
 
+  // Attributes section data in output.
+  Attributes_section_data* attributes_section_data_;
+  // .MIPS.abiflags section data in output.
+  Mips_abiflags<big_endian>* abiflags_;
+
   unsigned int mach_;
   Layout* layout_;
 
   typename std::list<got16_addend<size, big_endian> > got16_addends_;
 
+  // Whether there is an input .MIPS.abiflags section.
+  bool has_abiflags_section_;
+
   // Whether the entry symbol is mips16 or micromips.
   bool entry_symbol_is_compressed_;
 
@@ -6401,6 +6541,60 @@ Mips_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
           this->cprmask4_ = elfcpp::Swap<size, big_endian>::readval(view + 16);
         }
 
+      if (shdr.get_sh_type() == elfcpp::SHT_GNU_ATTRIBUTES)
+        {
+          gold_assert(this->attributes_section_data_ == NULL);
+          section_offset_type section_offset = shdr.get_sh_offset();
+          section_size_type section_size =
+            convert_to_section_size_type(shdr.get_sh_size());
+          const unsigned char* view =
+            this->get_view(section_offset, section_size, true, false);
+          this->attributes_section_data_ =
+            new Attributes_section_data(view, section_size);
+        }
+
+      if (shdr.get_sh_type() == elfcpp::SHT_MIPS_ABIFLAGS)
+        {
+          gold_assert(this->abiflags_ == NULL);
+          section_offset_type section_offset = shdr.get_sh_offset();
+          section_size_type section_size =
+            convert_to_section_size_type(shdr.get_sh_size());
+          const unsigned char* view =
+            this->get_view(section_offset, section_size, true, false);
+          this->abiflags_ = new Mips_abiflags<big_endian>();
+
+          this->abiflags_->version =
+            elfcpp::Swap<16, big_endian>::readval(view);
+          if (this->abiflags_->version != 0)
+            {
+              gold_error(_("%s: .MIPS.abiflags section has "
+                           "unsupported version %u"),
+                         this->name().c_str(),
+                         this->abiflags_->version);
+              break;
+            }
+          this->abiflags_->isa_level =
+            elfcpp::Swap<8, big_endian>::readval(view + 2);
+          this->abiflags_->isa_rev =
+            elfcpp::Swap<8, big_endian>::readval(view + 3);
+          this->abiflags_->gpr_size =
+            elfcpp::Swap<8, big_endian>::readval(view + 4);
+          this->abiflags_->cpr1_size =
+            elfcpp::Swap<8, big_endian>::readval(view + 5);
+          this->abiflags_->cpr2_size =
+            elfcpp::Swap<8, big_endian>::readval(view + 6);
+          this->abiflags_->fp_abi =
+            elfcpp::Swap<8, big_endian>::readval(view + 7);
+          this->abiflags_->isa_ext =
+            elfcpp::Swap<32, big_endian>::readval(view + 8);
+          this->abiflags_->ases =
+            elfcpp::Swap<32, big_endian>::readval(view + 12);
+          this->abiflags_->flags1 =
+            elfcpp::Swap<32, big_endian>::readval(view + 16);
+          this->abiflags_->flags2 =
+            elfcpp::Swap<32, big_endian>::readval(view + 20);
+        }
+
       // In the 64-bit ABI, .MIPS.options section holds register information.
       // A SHT_MIPS_OPTIONS section contains a series of options, each of which
       // starts with this header:
@@ -7694,6 +7888,31 @@ Mips_output_section_reginfo<size, big_endian>::do_write(Output_file* of)
   of->write_output_view(offset, data_size, view);
 }
 
+// Mips_output_section_abiflags methods.
+
+template<int size, bool big_endian>
+void
+Mips_output_section_abiflags<size, big_endian>::do_write(Output_file* of)
+{
+  off_t offset = this->offset();
+  off_t data_size = this->data_size();
+
+  unsigned char* view = of->get_output_view(offset, data_size);
+  elfcpp::Swap<16, big_endian>::writeval(view, this->abiflags_.version);
+  elfcpp::Swap<8, big_endian>::writeval(view + 2, this->abiflags_.isa_level);
+  elfcpp::Swap<8, big_endian>::writeval(view + 3, this->abiflags_.isa_rev);
+  elfcpp::Swap<8, big_endian>::writeval(view + 4, this->abiflags_.gpr_size);
+  elfcpp::Swap<8, big_endian>::writeval(view + 5, this->abiflags_.cpr1_size);
+  elfcpp::Swap<8, big_endian>::writeval(view + 6, this->abiflags_.cpr2_size);
+  elfcpp::Swap<8, big_endian>::writeval(view + 7, this->abiflags_.fp_abi);
+  elfcpp::Swap<32, big_endian>::writeval(view + 8, this->abiflags_.isa_ext);
+  elfcpp::Swap<32, big_endian>::writeval(view + 12, this->abiflags_.ases);
+  elfcpp::Swap<32, big_endian>::writeval(view + 16, this->abiflags_.flags1);
+  elfcpp::Swap<32, big_endian>::writeval(view + 20, this->abiflags_.flags2);
+
+  of->write_output_view(offset, data_size, view);
+}
+
 // Mips_copy_relocs methods.
 
 // Emit any saved relocs.
@@ -8254,6 +8473,9 @@ Target_mips<size, big_endian>::elf_mips_mach(elfcpp::Elf_Word flags)
     case elfcpp::E_MIPS_MACH_5500:
       return mach_mips5500;
 
+    case elfcpp::E_MIPS_MACH_5900:
+      return mach_mips5900;
+
     case elfcpp::E_MIPS_MACH_9000:
       return mach_mips9000;
 
@@ -8269,6 +8491,9 @@ Target_mips<size, big_endian>::elf_mips_mach(elfcpp::Elf_Word flags)
     case elfcpp::E_MIPS_MACH_LS3A:
       return mach_mips_loongson_3a;
 
+    case elfcpp::E_MIPS_MACH_OCTEON3:
+      return mach_mips_octeon3;
+
     case elfcpp::E_MIPS_MACH_OCTEON2:
       return mach_mips_octeon2;
 
@@ -8314,6 +8539,426 @@ Target_mips<size, big_endian>::elf_mips_mach(elfcpp::Elf_Word flags)
   return 0;
 }
 
+// Return the MACH for each .MIPS.abiflags ISA Extension.
+
+template<int size, bool big_endian>
+unsigned int
+Target_mips<size, big_endian>::mips_isa_ext_mach(unsigned int isa_ext)
+{
+  switch (isa_ext)
+    {
+    case elfcpp::AFL_EXT_3900:
+      return mach_mips3900;
+
+    case elfcpp::AFL_EXT_4010:
+      return mach_mips4010;
+
+    case elfcpp::AFL_EXT_4100:
+      return mach_mips4100;
+
+    case elfcpp::AFL_EXT_4111:
+      return mach_mips4111;
+
+    case elfcpp::AFL_EXT_4120:
+      return mach_mips4120;
+
+    case elfcpp::AFL_EXT_4650:
+      return mach_mips4650;
+
+    case elfcpp::AFL_EXT_5400:
+      return mach_mips5400;
+
+    case elfcpp::AFL_EXT_5500:
+      return mach_mips5500;
+
+    case elfcpp::AFL_EXT_5900:
+      return mach_mips5900;
+
+    case elfcpp::AFL_EXT_10000:
+      return mach_mips10000;
+
+    case elfcpp::AFL_EXT_LOONGSON_2E:
+      return mach_mips_loongson_2e;
+
+    case elfcpp::AFL_EXT_LOONGSON_2F:
+      return mach_mips_loongson_2f;
+
+    case elfcpp::AFL_EXT_LOONGSON_3A:
+      return mach_mips_loongson_3a;
+
+    case elfcpp::AFL_EXT_SB1:
+      return mach_mips_sb1;
+
+    case elfcpp::AFL_EXT_OCTEON:
+      return mach_mips_octeon;
+
+    case elfcpp::AFL_EXT_OCTEONP:
+      return mach_mips_octeonp;
+
+    case elfcpp::AFL_EXT_OCTEON2:
+      return mach_mips_octeon2;
+
+    case elfcpp::AFL_EXT_XLR:
+      return mach_mips_xlr;
+
+    default:
+      return mach_mips3000;
+    }
+}
+
+// Return the .MIPS.abiflags value representing each ISA Extension.
+
+template<int size, bool big_endian>
+unsigned int
+Target_mips<size, big_endian>::mips_isa_ext(unsigned int mips_mach)
+{
+  switch (mips_mach)
+    {
+    case mach_mips3900:
+      return elfcpp::AFL_EXT_3900;
+
+    case mach_mips4010:
+      return elfcpp::AFL_EXT_4010;
+
+    case mach_mips4100:
+      return elfcpp::AFL_EXT_4100;
+
+    case mach_mips4111:
+      return elfcpp::AFL_EXT_4111;
+
+    case mach_mips4120:
+      return elfcpp::AFL_EXT_4120;
+
+    case mach_mips4650:
+      return elfcpp::AFL_EXT_4650;
+
+    case mach_mips5400:
+      return elfcpp::AFL_EXT_5400;
+
+    case mach_mips5500:
+      return elfcpp::AFL_EXT_5500;
+
+    case mach_mips5900:
+      return elfcpp::AFL_EXT_5900;
+
+    case mach_mips10000:
+      return elfcpp::AFL_EXT_10000;
+
+    case mach_mips_loongson_2e:
+      return elfcpp::AFL_EXT_LOONGSON_2E;
+
+    case mach_mips_loongson_2f:
+      return elfcpp::AFL_EXT_LOONGSON_2F;
+
+    case mach_mips_loongson_3a:
+      return elfcpp::AFL_EXT_LOONGSON_3A;
+
+    case mach_mips_sb1:
+      return elfcpp::AFL_EXT_SB1;
+
+    case mach_mips_octeon:
+      return elfcpp::AFL_EXT_OCTEON;
+
+    case mach_mips_octeonp:
+      return elfcpp::AFL_EXT_OCTEONP;
+
+    case mach_mips_octeon3:
+      return elfcpp::AFL_EXT_OCTEON3;
+
+    case mach_mips_octeon2:
+      return elfcpp::AFL_EXT_OCTEON2;
+
+    case mach_mips_xlr:
+      return elfcpp::AFL_EXT_XLR;
+
+    default:
+      return 0;
+    }
+}
+
+// Update the isa_level, isa_rev, isa_ext fields of abiflags.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::update_abiflags_isa(const std::string& name,
+    elfcpp::Elf_Word e_flags, Mips_abiflags<big_endian>* abiflags)
+{
+  int new_isa = 0;
+  switch (e_flags & elfcpp::EF_MIPS_ARCH)
+    {
+    case elfcpp::E_MIPS_ARCH_1:
+      new_isa = this->level_rev(1, 0);
+      break;
+    case elfcpp::E_MIPS_ARCH_2:
+      new_isa = this->level_rev(2, 0);
+      break;
+    case elfcpp::E_MIPS_ARCH_3:
+      new_isa = this->level_rev(3, 0);
+      break;
+    case elfcpp::E_MIPS_ARCH_4:
+      new_isa = this->level_rev(4, 0);
+      break;
+    case elfcpp::E_MIPS_ARCH_5:
+      new_isa = this->level_rev(5, 0);
+      break;
+    case elfcpp::E_MIPS_ARCH_32:
+      new_isa = this->level_rev(32, 1);
+      break;
+    case elfcpp::E_MIPS_ARCH_32R2:
+      new_isa = this->level_rev(32, 2);
+      break;
+    case elfcpp::E_MIPS_ARCH_64:
+      new_isa = this->level_rev(64, 1);
+      break;
+    case elfcpp::E_MIPS_ARCH_64R2:
+      new_isa = this->level_rev(64, 2);
+      break;
+    default:
+      gold_error(_("%s: Unknown architecture %s"), name.c_str(),
+                 this->elf_mips_mach_name(e_flags));
+    }
+
+  if (new_isa > this->level_rev(abiflags->isa_level, abiflags->isa_rev))
+    {
+      // Decode a single value into level and revision.
+      abiflags->isa_level = new_isa >> 3;
+      abiflags->isa_rev = new_isa & 0x7;
+    }
+
+  // Update the isa_ext if needed.
+  if (this->mips_mach_extends(this->mips_isa_ext_mach(abiflags->isa_ext),
+      this->elf_mips_mach(e_flags)))
+    abiflags->isa_ext = this->mips_isa_ext(this->elf_mips_mach(e_flags));
+}
+
+// Infer the content of the ABI flags based on the elf header.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::infer_abiflags(
+    Mips_relobj<size, big_endian>* relobj, Mips_abiflags<big_endian>* abiflags)
+{
+  const Attributes_section_data* pasd = relobj->attributes_section_data();
+  int attr_fp_abi = elfcpp::Val_GNU_MIPS_ABI_FP_ANY;
+  elfcpp::Elf_Word e_flags = relobj->processor_specific_flags();
+
+  this->update_abiflags_isa(relobj->name(), e_flags, abiflags);
+  if (pasd != NULL)
+    {
+      // Read fp_abi from the .gnu.attribute section.
+      const Object_attribute* attr =
+        pasd->known_attributes(Object_attribute::OBJ_ATTR_GNU);
+      attr_fp_abi = attr[elfcpp::Tag_GNU_MIPS_ABI_FP].int_value();
+    }
+
+  abiflags->fp_abi = attr_fp_abi;
+  abiflags->cpr1_size = elfcpp::AFL_REG_NONE;
+  abiflags->cpr2_size = elfcpp::AFL_REG_NONE;
+  abiflags->gpr_size = this->mips_32bit_flags(e_flags) ? elfcpp::AFL_REG_32
+                                                       : elfcpp::AFL_REG_64;
+
+  if (abiflags->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_SINGLE
+      || abiflags->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_XX
+      || (abiflags->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_DOUBLE
+      && abiflags->gpr_size == elfcpp::AFL_REG_32))
+    abiflags->cpr1_size = elfcpp::AFL_REG_32;
+  else if (abiflags->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_DOUBLE
+           || abiflags->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_64
+           || abiflags->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_64A)
+    abiflags->cpr1_size = elfcpp::AFL_REG_64;
+
+  if (e_flags & elfcpp::EF_MIPS_ARCH_ASE_MDMX)
+    abiflags->ases |= elfcpp::AFL_ASE_MDMX;
+  if (e_flags & elfcpp::EF_MIPS_ARCH_ASE_M16)
+    abiflags->ases |= elfcpp::AFL_ASE_MIPS16;
+  if (e_flags & elfcpp::EF_MIPS_ARCH_ASE_MICROMIPS)
+    abiflags->ases |= elfcpp::AFL_ASE_MICROMIPS;
+
+  if (abiflags->fp_abi != elfcpp::Val_GNU_MIPS_ABI_FP_ANY
+      && abiflags->fp_abi != elfcpp::Val_GNU_MIPS_ABI_FP_SOFT
+      && abiflags->fp_abi != elfcpp::Val_GNU_MIPS_ABI_FP_64A
+      && abiflags->isa_level >= 32
+      && abiflags->isa_ext != elfcpp::AFL_EXT_LOONGSON_3A)
+    abiflags->flags1 |= elfcpp::AFL_FLAGS1_ODDSPREG;
+}
+
+// Create abiflags from elf header or from .MIPS.abiflags section.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::create_abiflags(
+    Mips_relobj<size, big_endian>* relobj,
+    Mips_abiflags<big_endian>* abiflags)
+{
+  Mips_abiflags<big_endian>* sec_abiflags = relobj->abiflags();
+  Mips_abiflags<big_endian> header_abiflags;
+
+  this->infer_abiflags(relobj, &header_abiflags);
+
+  if (sec_abiflags == NULL)
+    {
+      // If there is no input .MIPS.abiflags section, use abiflags created
+      // from elf header.
+      *abiflags = header_abiflags;
+      return;
+    }
+
+  this->has_abiflags_section_ = true;
+
+  // It is not possible to infer the correct ISA revision for R3 or R5
+  // so drop down to R2 for the checks.
+  unsigned char isa_rev = sec_abiflags->isa_rev;
+  if (isa_rev == 3 || isa_rev == 5)
+    isa_rev = 2;
+
+  // Check compatibility between abiflags created from elf header
+  // and abiflags from .MIPS.abiflags section in this object file.
+  if (this->level_rev(sec_abiflags->isa_level, isa_rev)
+      < this->level_rev(header_abiflags.isa_level, header_abiflags.isa_rev))
+    gold_warning(_("%s: Inconsistent ISA between e_flags and .MIPS.abiflags"),
+                 relobj->name().c_str());
+  if (header_abiflags.fp_abi != elfcpp::Val_GNU_MIPS_ABI_FP_ANY
+      && sec_abiflags->fp_abi != header_abiflags.fp_abi)
+    gold_warning(_("%s: Inconsistent FP ABI between .gnu.attributes and "
+                   ".MIPS.abiflags"), relobj->name().c_str());
+  if ((sec_abiflags->ases & header_abiflags.ases) != header_abiflags.ases)
+    gold_warning(_("%s: Inconsistent ASEs between e_flags and .MIPS.abiflags"),
+                 relobj->name().c_str());
+  // The isa_ext is allowed to be an extension of what can be inferred
+  // from e_flags.
+  if (!this->mips_mach_extends(this->mips_isa_ext_mach(header_abiflags.isa_ext),
+                               this->mips_isa_ext_mach(sec_abiflags->isa_ext)))
+    gold_warning(_("%s: Inconsistent ISA extensions between e_flags and "
+                   ".MIPS.abiflags"), relobj->name().c_str());
+  if (sec_abiflags->flags2 != 0)
+    gold_warning(_("%s: Unexpected flag in the flags2 field of "
+                   ".MIPS.abiflags (0x%x)"), relobj->name().c_str(),
+                                             sec_abiflags->flags2);
+  // Use abiflags from .MIPS.abiflags section.
+  *abiflags = *sec_abiflags;
+}
+
+// Return the meaning of fp_abi, or "unknown" if not known.
+
+template<int size, bool big_endian>
+const char*
+Target_mips<size, big_endian>::fp_abi_string(int fp)
+{
+  switch (fp)
+    {
+    case elfcpp::Val_GNU_MIPS_ABI_FP_DOUBLE:
+      return "-mdouble-float";
+    case elfcpp::Val_GNU_MIPS_ABI_FP_SINGLE:
+      return "-msingle-float";
+    case elfcpp::Val_GNU_MIPS_ABI_FP_SOFT:
+      return "-msoft-float";
+    case elfcpp::Val_GNU_MIPS_ABI_FP_OLD_64:
+      return _("-mips32r2 -mfp64 (12 callee-saved)");
+    case elfcpp::Val_GNU_MIPS_ABI_FP_XX:
+      return "-mfpxx";
+    case elfcpp::Val_GNU_MIPS_ABI_FP_64:
+      return "-mgp32 -mfp64";
+    case elfcpp::Val_GNU_MIPS_ABI_FP_64A:
+      return "-mgp32 -mfp64 -mno-odd-spreg";
+    default:
+      return "unknown";
+    }
+}
+
+// Select fp_abi.
+
+template<int size, bool big_endian>
+int
+Target_mips<size, big_endian>::select_fp_abi(const std::string& name, int in_fp,
+                                             int out_fp)
+{
+  if (in_fp == out_fp)
+    return out_fp;
+
+  if (out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_ANY)
+    return in_fp;
+  else if (out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_XX
+           && (in_fp == elfcpp::Val_GNU_MIPS_ABI_FP_DOUBLE
+               || in_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64
+               || in_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64A))
+    return in_fp;
+  else if (in_fp == elfcpp::Val_GNU_MIPS_ABI_FP_XX
+           && (out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_DOUBLE
+               || out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64
+               || out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64A))
+    return out_fp; // Keep the current setting.
+  else if (out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64A
+           && in_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64)
+    return in_fp;
+  else if (in_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64A
+           && out_fp == elfcpp::Val_GNU_MIPS_ABI_FP_64)
+    return out_fp; // Keep the current setting.
+  else if (in_fp != elfcpp::Val_GNU_MIPS_ABI_FP_ANY)
+    gold_warning(_("%s: FP ABI %s is incompatible with %s"), name.c_str(),
+                 fp_abi_string(in_fp), fp_abi_string(out_fp));
+  return out_fp;
+}
+
+// Merge attributes from input object.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::merge_obj_attributes(const std::string& 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;
+    }
+
+  Object_attribute* out_attr = this->attributes_section_data_->known_attributes(
+      Object_attribute::OBJ_ATTR_GNU);
+
+  out_attr[elfcpp::Tag_GNU_MIPS_ABI_FP].set_type(1);
+  out_attr[elfcpp::Tag_GNU_MIPS_ABI_FP].set_int_value(this->abiflags_->fp_abi);
+
+  // Merge Tag_compatibility attributes and any common GNU ones.
+  this->attributes_section_data_->merge(name.c_str(), pasd);
+}
+
+// Merge abiflags from input object.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::merge_obj_abiflags(const std::string& name,
+    Mips_abiflags<big_endian>* in_abiflags)
+{
+  // If output has no abiflags, just copy.
+  if (this->abiflags_ == NULL)
+  {
+    this->abiflags_ = new Mips_abiflags<big_endian>(*in_abiflags);
+    return;
+  }
+
+  this->abiflags_->fp_abi = this->select_fp_abi(name, in_abiflags->fp_abi,
+                                                this->abiflags_->fp_abi);
+
+  // Merge abiflags.
+  this->abiflags_->isa_level = std::max(this->abiflags_->isa_level,
+                                        in_abiflags->isa_level);
+  this->abiflags_->isa_rev = std::max(this->abiflags_->isa_rev,
+                                      in_abiflags->isa_rev);
+  this->abiflags_->gpr_size = std::max(this->abiflags_->gpr_size,
+                                       in_abiflags->gpr_size);
+  this->abiflags_->cpr1_size = std::max(this->abiflags_->cpr1_size,
+                                        in_abiflags->cpr1_size);
+  this->abiflags_->cpr2_size = std::max(this->abiflags_->cpr2_size,
+                                        in_abiflags->cpr2_size);
+  this->abiflags_->ases |= in_abiflags->ases;
+  this->abiflags_->flags1 |= in_abiflags->flags1;
+}
+
 // Check whether machine EXTENSION is an extension of machine BASE.
 template<int size, bool big_endian>
 bool
@@ -8342,10 +8987,12 @@ Target_mips<size, big_endian>::mips_mach_extends(unsigned int base,
   return false;
 }
 
+// Merge file header flags from input object.
+
 template<int size, bool big_endian>
 void
-Target_mips<size, big_endian>::merge_processor_specific_flags(
-    const std::string& name, elfcpp::Elf_Word in_flags, bool dyn_obj)
+Target_mips<size, big_endian>::merge_obj_e_flags(const std::string& name,
+                                                 elfcpp::Elf_Word in_flags)
 {
   // If flags are not set yet, just copy them.
   if (!this->are_processor_specific_flags_set())
@@ -8374,10 +9021,6 @@ Target_mips<size, big_endian>::merge_processor_specific_flags(
   new_flags &= ~elfcpp::EF_MIPS_UCODE;
   old_flags &= ~elfcpp::EF_MIPS_UCODE;
 
-  // DSOs should only be linked with CPIC code.
-  if (dyn_obj)
-    new_flags |= elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC;
-
   if (new_flags == old_flags)
     {
       this->set_processor_specific_flags(merged_flags);
@@ -8413,6 +9056,9 @@ Target_mips<size, big_endian>::merge_processor_specific_flags(
           merged_flags |= (new_flags & (elfcpp::EF_MIPS_ARCH
                            | elfcpp::EF_MIPS_MACH | elfcpp::EF_MIPS_32BITMODE));
 
+          // Update the ABI flags isa_level, isa_rev, isa_ext fields.
+          this->update_abiflags_isa(name, merged_flags, this->abiflags_);
+
           // Copy across the ABI flags if output doesn't use them
           // and if that was what caused us to treat input object as 32-bit.
           if ((old_flags & elfcpp::EF_MIPS_ABI) == 0
@@ -8471,6 +9117,34 @@ Target_mips<size, big_endian>::merge_processor_specific_flags(
       old_flags &= ~ elfcpp::EF_MIPS_ARCH_ASE;
     }
 
+  // Compare NaN encodings.
+  if ((new_flags & elfcpp::EF_MIPS_NAN2008) != (old_flags & elfcpp::EF_MIPS_NAN2008))
+    {
+      gold_error(_("%s: linking %s module with previous %s modules"),
+                 name.c_str(),
+                 (new_flags & elfcpp::EF_MIPS_NAN2008
+                  ? "-mnan=2008" : "-mnan=legacy"),
+                 (old_flags & elfcpp::EF_MIPS_NAN2008
+                  ? "-mnan=2008" : "-mnan=legacy"));
+
+      new_flags &= ~elfcpp::EF_MIPS_NAN2008;
+      old_flags &= ~elfcpp::EF_MIPS_NAN2008;
+    }
+
+  // Compare FP64 state.
+  if ((new_flags & elfcpp::EF_MIPS_FP64) != (old_flags & elfcpp::EF_MIPS_FP64))
+    {
+      gold_error(_("%s: linking %s module with previous %s modules"),
+                 name.c_str(),
+                 (new_flags & elfcpp::EF_MIPS_FP64
+                  ? "-mfp64" : "-mfp32"),
+                 (old_flags & elfcpp::EF_MIPS_FP64
+                  ? "-mfp64" : "-mfp32"));
+
+      new_flags &= ~elfcpp::EF_MIPS_FP64;
+      old_flags &= ~elfcpp::EF_MIPS_FP64;
+    }
+
   // Warn about any other mismatches.
   if (new_flags != old_flags)
     gold_error(_("%s: uses different e_flags (0x%x) fields than previous "
@@ -8489,13 +9163,30 @@ Target_mips<size, big_endian>::do_adjust_elf_header(
 {
   gold_assert(len == elfcpp::Elf_sizes<size>::ehdr_size);
 
-  if (!this->entry_symbol_is_compressed_)
-    return;
-
   elfcpp::Ehdr<size, big_endian> ehdr(view);
+  unsigned char e_ident[elfcpp::EI_NIDENT];
+  elfcpp::Elf_Word flags = this->processor_specific_flags();
+  memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
+
+  unsigned char ei_abiversion = 0;
+  elfcpp::Elf_Half type = ehdr.get_e_type();
+  if (type == elfcpp::ET_EXEC
+      && parameters->options().copyreloc()
+      && (flags & (elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC))
+          == elfcpp::EF_MIPS_CPIC)
+    ei_abiversion = 1;
+
+  if (this->abiflags_ != NULL
+      && (this->abiflags_->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_64
+          || this->abiflags_->fp_abi == elfcpp::Val_GNU_MIPS_ABI_FP_64A))
+    ei_abiversion = 3;
+
+  e_ident[elfcpp::EI_ABIVERSION] = ei_abiversion;
   elfcpp::Ehdr_write<size, big_endian> oehdr(view);
+  oehdr.put_e_ident(e_ident);
 
-  oehdr.put_e_entry(ehdr.get_e_entry() + 1);
+  if (this->entry_symbol_is_compressed_)
+    oehdr.put_e_entry(ehdr.get_e_entry() + 1);
 }
 
 // do_make_elf_object to override the same function in the base class.
@@ -8598,7 +9289,6 @@ Target_mips<size, big_endian>::do_finalize_sections(Layout* layout,
   Valtype cprmask4 = 0;
   bool has_reginfo_section = false;
 
-  // Merge processor-specific flags.
   for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
        p != input_objects->relobj_end();
        ++p)
@@ -8618,49 +9308,63 @@ Target_mips<size, big_endian>::do_finalize_sections(Layout* layout,
         }
 
       Input_file::Format format = relobj->input_file()->format();
-      if (format == Input_file::FORMAT_ELF)
-        {
-          // Read processor-specific flags in ELF file header.
-          const unsigned char* pehdr = relobj->get_view(
-                                            elfcpp::file_header_offset,
-                                            elfcpp::Elf_sizes<size>::ehdr_size,
-                                            true, false);
+      if (format != Input_file::FORMAT_ELF)
+        continue;
 
-          elfcpp::Ehdr<size, big_endian> ehdr(pehdr);
-          elfcpp::Elf_Word in_flags = ehdr.get_e_flags();
-          // If all input sections will be discarded, don't use this object
-          // file for merging processor specific flags.
-          bool should_merge_processor_specific_flags = false;
+      // If all input sections will be discarded, don't use this object
+      // file for merging processor specific flags.
+      bool should_merge_processor_specific_flags = false;
 
-          for (unsigned int i = 1; i < relobj->shnum(); ++i)
-            if (relobj->output_section(i) != NULL)
-              {
-                should_merge_processor_specific_flags = true;
-                break;
-              }
+      for (unsigned int i = 1; i < relobj->shnum(); ++i)
+        if (relobj->output_section(i) != NULL)
+          {
+            should_merge_processor_specific_flags = true;
+            break;
+          }
 
-          if (should_merge_processor_specific_flags)
-            this->merge_processor_specific_flags(relobj->name(), in_flags,
-                                                 false);
-        }
+      if (!should_merge_processor_specific_flags)
+        continue;
+
+      // Merge processor specific flags.
+      Mips_abiflags<big_endian> in_abiflags;
+
+      this->create_abiflags(relobj, &in_abiflags);
+      this->merge_obj_e_flags(relobj->name(),
+                              relobj->processor_specific_flags());
+      this->merge_obj_abiflags(relobj->name(), &in_abiflags);
+      this->merge_obj_attributes(relobj->name(),
+                                 relobj->attributes_section_data());
     }
 
-  for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin();
-       p != input_objects->dynobj_end();
-       ++p)
+  // Create a .gnu.attributes section if we have merged any attributes
+  // from inputs.
+  if (this->attributes_section_data_ != NULL)
     {
-      Sized_dynobj<size, big_endian>* dynobj =
-        static_cast<Sized_dynobj<size, big_endian>*>(*p);
+      Output_attributes_section_data* attributes_section =
+        new Output_attributes_section_data(*this->attributes_section_data_);
+      layout->add_output_section_data(".gnu.attributes",
+                                      elfcpp::SHT_GNU_ATTRIBUTES, 0,
+                                      attributes_section, ORDER_INVALID, false);
+    }
 
-      // Read processor-specific flags.
-      const unsigned char* pehdr = dynobj->get_view(elfcpp::file_header_offset,
-                                           elfcpp::Elf_sizes<size>::ehdr_size,
-                                           true, false);
+  // Create .MIPS.abiflags output section if there is an input section.
+  if (this->has_abiflags_section_)
+    {
+      Mips_output_section_abiflags<size, big_endian>* abiflags_section =
+        new Mips_output_section_abiflags<size, big_endian>(*this->abiflags_);
 
-      elfcpp::Ehdr<size, big_endian> ehdr(pehdr);
-      elfcpp::Elf_Word in_flags = ehdr.get_e_flags();
+      Output_section* os =
+        layout->add_output_section_data(".MIPS.abiflags",
+                                        elfcpp::SHT_MIPS_ABIFLAGS,
+                                        elfcpp::SHF_ALLOC,
+                                        abiflags_section, ORDER_INVALID, false);
 
-      this->merge_processor_specific_flags(dynobj->name(), in_flags, true);
+      if (!parameters->options().relocatable() && os != NULL)
+        {
+          Output_segment* abiflags_segment =
+            layout->make_output_segment(elfcpp::PT_MIPS_ABIFLAGS, elfcpp::PF_R);
+          abiflags_segment->add_output_section_to_nonload(os, elfcpp::PF_R);
+        }
     }
 
   if (has_reginfo_section && !parameters->options().gc_sections())
@@ -11263,20 +11967,24 @@ Target_mips<size, big_endian>::elf_mips_mach_name(elfcpp::Elf_Word e_flags)
       return "mips:5400";
     case elfcpp::E_MIPS_MACH_5500:
       return "mips:5500";
+    case elfcpp::E_MIPS_MACH_5900:
+      return "mips:5900";
     case elfcpp::E_MIPS_MACH_SB1:
       return "mips:sb1";
     case elfcpp::E_MIPS_MACH_9000:
       return "mips:9000";
     case elfcpp::E_MIPS_MACH_LS2E:
-      return "mips:loongson-2e";
+      return "mips:loongson_2e";
     case elfcpp::E_MIPS_MACH_LS2F:
-      return "mips:loongson-2f";
+      return "mips:loongson_2f";
     case elfcpp::E_MIPS_MACH_LS3A:
-      return "mips:loongson-3a";
+      return "mips:loongson_3a";
     case elfcpp::E_MIPS_MACH_OCTEON:
       return "mips:octeon";
     case elfcpp::E_MIPS_MACH_OCTEON2:
       return "mips:octeon2";
+    case elfcpp::E_MIPS_MACH_OCTEON3:
+      return "mips:octeon3";
     case elfcpp::E_MIPS_MACH_XLR:
       return "mips:xlr";
     default: