#include "tls.h"
#include "errors.h"
#include "gc.h"
+#include "attributes.h"
#include "nacl.h"
namespace
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>
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.
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
// .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.
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.
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();
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
}
// 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
{
}
// 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
{
{ 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(); }
mach_mips5000 = 5000,
mach_mips5400 = 5400,
mach_mips5500 = 5500,
+ mach_mips5900 = 5900,
mach_mips6000 = 6000,
mach_mips7000 = 7000,
mach_mips8000 = 8000,
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
};
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
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);
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);
// .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_;
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:
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.
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;
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;
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
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())
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);
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
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 "
{
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.
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)
}
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())
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: