diff options
Diffstat (limited to 'gold/mips.cc')
-rw-r--r-- | gold/mips.cc | 832 |
1 files changed, 770 insertions, 62 deletions
diff --git a/gold/mips.cc b/gold/mips.cc index 59ce88e..42ec64b 100644 --- a/gold/mips.cc +++ b/gold/mips.cc @@ -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: |