+ 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_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_32R6:
+ new_isa = this->level_rev(32, 6);
+ 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;
+ case elfcpp::E_MIPS_ARCH_64R6:
+ new_isa = this->level_rev(64, 6);
+ 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->ases != elfcpp::AFL_ASE_LOONGSON_EXT)
+ 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
+Target_mips<size, big_endian>::mips_mach_extends(unsigned int base,
+ unsigned int extension)
+{
+ if (extension == base)
+ return true;
+
+ if ((base == mach_mipsisa32)
+ && this->mips_mach_extends(mach_mipsisa64, extension))
+ return true;
+
+ if ((base == mach_mipsisa32r2)
+ && this->mips_mach_extends(mach_mipsisa64r2, extension))
+ return true;
+
+ for (unsigned int i = 0; i < this->mips_mach_extensions_.size(); ++i)
+ if (extension == this->mips_mach_extensions_[i].first)
+ {
+ extension = this->mips_mach_extensions_[i].second;
+ if (extension == base)
+ return true;
+ }
+
+ return false;
+}
+
+// Merge file header flags from input object.
+
+template<int size, bool big_endian>
+void
+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())
+ {
+ this->set_processor_specific_flags(in_flags);
+ this->mach_ = this->elf_mips_mach(in_flags);
+ return;
+ }
+
+ elfcpp::Elf_Word new_flags = in_flags;
+ elfcpp::Elf_Word old_flags = this->processor_specific_flags();
+ elfcpp::Elf_Word merged_flags = this->processor_specific_flags();
+ merged_flags |= new_flags & elfcpp::EF_MIPS_NOREORDER;
+
+ // Check flag compatibility.
+ new_flags &= ~elfcpp::EF_MIPS_NOREORDER;
+ old_flags &= ~elfcpp::EF_MIPS_NOREORDER;
+
+ // Some IRIX 6 BSD-compatibility objects have this bit set. It
+ // doesn't seem to matter.
+ new_flags &= ~elfcpp::EF_MIPS_XGOT;
+ old_flags &= ~elfcpp::EF_MIPS_XGOT;
+
+ // MIPSpro generates ucode info in n64 objects. Again, we should
+ // just be able to ignore this.
+ new_flags &= ~elfcpp::EF_MIPS_UCODE;
+ old_flags &= ~elfcpp::EF_MIPS_UCODE;
+
+ if (new_flags == old_flags)
+ {
+ this->set_processor_specific_flags(merged_flags);
+ return;
+ }
+
+ if (((new_flags & (elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC)) != 0)
+ != ((old_flags & (elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC)) != 0))