diff options
-rw-r--r-- | elfcpp/ChangeLog | 5 | ||||
-rw-r--r-- | elfcpp/powerpc.h | 26 | ||||
-rw-r--r-- | gold/ChangeLog | 15 | ||||
-rw-r--r-- | gold/powerpc.cc | 354 |
4 files changed, 393 insertions, 7 deletions
diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index d031d1b..fa33bf1 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,8 @@ +2018-07-06 Alan Modra <amodra@gmail.com> + + * powerpc.h (Tag_GNU_Power_ABI_FP): Define. + (Tag_GNU_Power_ABI_Vector, Tag_GNU_Power_ABI_Struct_Return): Define. + 2018-06-24 Nick Clifton <nickc@redhat.com> 2.31 branch created. diff --git a/elfcpp/powerpc.h b/elfcpp/powerpc.h index 71de459..5996691 100644 --- a/elfcpp/powerpc.h +++ b/elfcpp/powerpc.h @@ -230,6 +230,32 @@ enum EF_PPC64_ABI = 3 }; +// Object attribute tags. 0-3 are generic. +enum +{ + // FP ABI, low 2 bits: + // 1 for double precision hard-float, + // 2 for soft-float, + // 3 for single precision hard-float. + // 0 for not tagged or not using any ABIs affected by the differences. + // Next 2 bits: + // 1 for ibm long double + // 2 for 64-bit long double + // 3 for IEEE long double. + // 0 for not tagged or not using any ABIs affected by the differences. + Tag_GNU_Power_ABI_FP = 4, + + // Value 1 for general purpose registers only, 2 for AltiVec + // registers, 3 for SPE registers; 0 for not tagged or not using any + // ABIs affected by the differences. + Tag_GNU_Power_ABI_Vector = 8, + + // Value 1 for ABIs using r3/r4 for returning structures <= 8 bytes, + // 2 for ABIs using memory; 0 for not tagged or not using any ABIs + // affected by the differences. + Tag_GNU_Power_ABI_Struct_Return = 12 +}; + // DT_PPC_OPT bits enum { diff --git a/gold/ChangeLog b/gold/ChangeLog index 922c2ff..83df3d8 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,18 @@ +2018-07-06 Alan Modra <amodra@gmail.com> + + * powerpc.cc: Include attributes.h. + (Powerpc_relobj::attributes_section_data_): New variable, with + accessor and associated constructor and destructor support. + (Powerpc_dynobj::attributes_section_data_): Likewise. + (Powerpc_relobj::do_read_symbols): Stash SHT_GNU_ATTRIBUTES section + contents in attributes_section_data_. + (Powerpc_dynobj::do_read_symbols): Likewise. + (Target_powerpc): Add attributes_section_data_, last_fp_, last_ld_, + last_vec_, and last_struct_ vars. + (Target_powerpc::merge_object_attributes): New function. + (Target_powerpc::do_finalize_sections): Iterate over input objects + merging attributes. Create output attributes section. + 2018-06-26 Nick Clifton <nickc@redhat.com> * po/uk.po: Updated Ukranian translation. diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 314eaa7..6bc85a8 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -41,6 +41,7 @@ #include "tls.h" #include "errors.h" #include "gc.h" +#include "attributes.h" namespace { @@ -100,13 +101,14 @@ public: uniq_(object_id++), special_(0), relatoc_(0), toc_(0), has_small_toc_reloc_(false), opd_valid_(false), e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(), - access_from_map_(), has14_(), stub_table_index_(), st_other_() + access_from_map_(), has14_(), stub_table_index_(), st_other_(), + attributes_section_data_(NULL) { this->set_abiversion(0); } ~Powerpc_relobj() - { } + { delete this->attributes_section_data_; } // Read the symbols then set up st_other vector. void @@ -388,6 +390,11 @@ public: ppc64_local_entry_offset(unsigned int symndx) const { return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); } + // The contents of the .gnu.attributes section if there is one. + const Attributes_section_data* + attributes_section_data() const + { return this->attributes_section_data_; } + private: struct Opd_ent { @@ -458,6 +465,9 @@ private: // ELF st_other field for local symbols. std::vector<unsigned char> st_other_; + + // Object attributes if there is a .gnu.attributes section. + Attributes_section_data* attributes_section_data_; }; template<int size, bool big_endian> @@ -469,13 +479,14 @@ public: Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset, const typename elfcpp::Ehdr<size, big_endian>& ehdr) : Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr), - opd_shndx_(0), e_flags_(ehdr.get_e_flags()), opd_ent_() + opd_shndx_(0), e_flags_(ehdr.get_e_flags()), opd_ent_(), + attributes_section_data_(NULL) { this->set_abiversion(0); } ~Powerpc_dynobj() - { } + { delete this->attributes_section_data_; } // Call Sized_dynobj::do_read_symbols to read the symbols then // read .opd from a dynamic object, filling in opd_ent_ vector, @@ -534,6 +545,11 @@ public: void set_abiversion(int ver); + // The contents of the .gnu.attributes section if there is one. + const Attributes_section_data* + attributes_section_data() const + { return this->attributes_section_data_; } + private: // Used to specify extent of executable sections. struct Sec_info @@ -574,6 +590,9 @@ private: // corresponding to the address. Note that in dynamic objects, // offset is *not* relative to the section. std::vector<Opd_ent> opd_ent_; + + // Object attributes if there is a .gnu.attributes section. + Attributes_section_data* attributes_section_data_; }; // Powerpc_copy_relocs class. Needed to peek at dynamic relocs the @@ -618,7 +637,9 @@ class Target_powerpc : public Sized_target<size, big_endian> has_tls_get_addr_opt_(false), relax_failed_(false), relax_fail_count_(0), stub_group_size_(0), savres_section_(0), - tls_get_addr_(NULL), tls_get_addr_opt_(NULL) + tls_get_addr_(NULL), tls_get_addr_opt_(NULL), + attributes_section_data_(NULL), + last_fp_(NULL), last_ld_(NULL), last_vec_(NULL), last_struct_(NULL) { } @@ -1156,6 +1177,10 @@ class Target_powerpc : public Sized_target<size, big_endian> stk_linker() const { return this->abiversion() < 2 ? 32 : 8; } + // Merge object attributes from input object with those in the output. + void + merge_object_attributes(const char*, const Attributes_section_data*); + private: class Track_tls @@ -1647,6 +1672,15 @@ class Target_powerpc : public Sized_target<size, big_endian> Symbol* tls_get_addr_; // If optimizing __tls_get_addr calls, the "__tls_get_addr_opt" symbol. Symbol* tls_get_addr_opt_; + + // Attributes in output. + Attributes_section_data* attributes_section_data_; + + // Last input file to change various attribute tags + const char* last_fp_; + const char* last_ld_; + const char* last_vec_; + const char* last_struct_; }; template<> @@ -2312,6 +2346,8 @@ void Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) { this->base_read_symbols(sd); + if (this->input_file()->format() != Input_file::FORMAT_ELF) + return; if (size == 64) { const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; @@ -2345,6 +2381,56 @@ Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) } } } + + const size_t shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + const unsigned char* ps = sd->section_headers->data() + shdr_size; + bool merge_attributes = false; + for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size) + { + elfcpp::Shdr<size, big_endian> shdr(ps); + switch (shdr.get_sh_type()) + { + case 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); + } + break; + + case elfcpp::SHT_SYMTAB: + { + // Sometimes an object has no contents except the section + // name string table and an empty symbol table with the + // undefined symbol. We don't want to merge + // processor-specific flags from such an object. + const typename elfcpp::Elf_types<size>::Elf_WXword sym_size = + elfcpp::Elf_sizes<size>::sym_size; + if (shdr.get_sh_size() > sym_size) + merge_attributes = true; + } + break; + + case elfcpp::SHT_STRTAB: + break; + + default: + merge_attributes = true; + break; + } + } + + if (!merge_attributes) + { + // Should rarely happen. + delete this->attributes_section_data_; + this->attributes_section_data_ = NULL; + } } template<int size, bool big_endian> @@ -2376,9 +2462,26 @@ void Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) { this->base_read_symbols(sd); + const size_t shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + const unsigned char* ps = + sd->section_headers->data() + shdr_size * (this->shnum() - 1); + for (unsigned int i = this->shnum(); i > 0; --i, ps -= shdr_size) + { + elfcpp::Shdr<size, big_endian> shdr(ps); + if (shdr.get_sh_type() == elfcpp::SHT_GNU_ATTRIBUTES) + { + 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); + break; + } + } if (size == 64) { - const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; const unsigned char* const pshdrs = sd->section_headers->data(); const unsigned char* namesu = sd->section_names->data(); const char* names = reinterpret_cast<const char*>(namesu); @@ -8298,7 +8401,7 @@ template<int size, bool big_endian> void Target_powerpc<size, big_endian>::do_finalize_sections( Layout* layout, - const Input_objects*, + const Input_objects* input_objects, Symbol_table* symtab) { if (parameters->doing_static_link()) @@ -8401,6 +8504,243 @@ Target_powerpc<size, big_endian>::do_finalize_sections( // relocs. if (this->copy_relocs_.any_saved_relocs()) this->copy_relocs_.emit(this->rela_dyn_section(layout)); + + for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); + p != input_objects->relobj_end(); + ++p) + { + Powerpc_relobj<size, big_endian>* ppc_relobj + = static_cast<Powerpc_relobj<size, big_endian>*>(*p); + if (ppc_relobj->attributes_section_data()) + this->merge_object_attributes(ppc_relobj->name().c_str(), + ppc_relobj->attributes_section_data()); + } + for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin(); + p != input_objects->dynobj_end(); + ++p) + { + Powerpc_dynobj<size, big_endian>* ppc_dynobj + = static_cast<Powerpc_dynobj<size, big_endian>*>(*p); + if (ppc_dynobj->attributes_section_data()) + this->merge_object_attributes(ppc_dynobj->name().c_str(), + ppc_dynobj->attributes_section_data()); + } + + // Create a .gnu.attributes section if we have merged any attributes + // from inputs. + if (this->attributes_section_data_ != NULL + && this->attributes_section_data_->size() != 0) + { + 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); + } +} + +// Merge object attributes from input file called NAME with those of the +// output. The input object attributes are in the object pointed by PASD. + +template<int size, bool big_endian> +void +Target_powerpc<size, big_endian>::merge_object_attributes( + const char* name, + const Attributes_section_data* pasd) +{ + // Return if there is no attributes section data. + if (pasd == NULL) + return; + + // Create output object attributes. + if (this->attributes_section_data_ == NULL) + this->attributes_section_data_ = new Attributes_section_data(NULL, 0); + + const int vendor = Object_attribute::OBJ_ATTR_GNU; + const Object_attribute* in_attr = pasd->known_attributes(vendor); + Object_attribute* out_attr + = this->attributes_section_data_->known_attributes(vendor); + + const char* err; + const char* first; + const char* second; + int tag = elfcpp::Tag_GNU_Power_ABI_FP; + int in_fp = in_attr[tag].int_value() & 0xf; + int out_fp = out_attr[tag].int_value() & 0xf; + if (in_fp != out_fp) + { + err = NULL; + if ((in_fp & 3) == 0) + ; + else if ((out_fp & 3) == 0) + { + out_fp |= in_fp & 3; + out_attr[tag].set_int_value(out_fp); + out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL); + this->last_fp_ = name; + } + else if ((out_fp & 3) != 2 && (in_fp & 3) == 2) + { + err = N_("%s uses hard float, %s uses soft float"); + first = this->last_fp_; + second = name; + } + else if ((out_fp & 3) == 2 && (in_fp & 3) != 2) + { + err = N_("%s uses hard float, %s uses soft float"); + first = name; + second = this->last_fp_; + } + else if ((out_fp & 3) == 1 && (in_fp & 3) == 3) + { + err = N_("%s uses double-precision hard float, " + "%s uses single-precision hard float"); + first = this->last_fp_; + second = name; + } + else if ((out_fp & 3) == 3 && (in_fp & 3) == 1) + { + err = N_("%s uses double-precision hard float, " + "%s uses single-precision hard float"); + first = name; + second = this->last_fp_; + } + + if (err || (in_fp & 0xc) == 0) + ; + else if ((out_fp & 0xc) == 0) + { + out_fp |= in_fp & 0xc; + out_attr[tag].set_int_value(out_fp); + out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL); + this->last_ld_ = name; + } + else if ((out_fp & 0xc) != 2 * 4 && (in_fp & 0xc) == 2 * 4) + { + err = N_("%s uses 64-bit long double, %s uses 128-bit long double"); + first = name; + second = this->last_ld_; + } + else if ((in_fp & 0xc) != 2 * 4 && (out_fp & 0xc) == 2 * 4) + { + err = N_("%s uses 64-bit long double, %s uses 128-bit long double"); + first = this->last_ld_; + second = name; + } + else if ((out_fp & 0xc) == 1 * 4 && (in_fp & 0xc) == 3 * 4) + { + err = N_("%s uses IBM long double, %s uses IEEE long double"); + first = this->last_ld_; + second = name; + } + else if ((out_fp & 0xc) == 3 * 4 && (in_fp & 0xc) == 1 * 4) + { + err = N_("%s uses IBM long double, %s uses IEEE long double"); + first = name; + second = this->last_ld_; + } + + if (err) + { + if (parameters->options().warn_mismatch()) + gold_error(_(err), first, second); + // Arrange for this attribute to be deleted. It's better to + // say "don't know" about a file than to wrongly claim compliance. + out_attr[tag].set_type(0); + } + } + + if (size == 32) + { + tag = elfcpp::Tag_GNU_Power_ABI_Vector; + int in_vec = in_attr[tag].int_value() & 3; + int out_vec = out_attr[tag].int_value() & 3; + if (in_vec != out_vec) + { + err = NULL; + if (in_vec == 0) + ; + else if (out_vec == 0) + { + out_vec = in_vec; + out_attr[tag].set_int_value(out_vec); + out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL); + this->last_vec_ = name; + } + // For now, allow generic to transition to AltiVec or SPE + // without a warning. If GCC marked files with their stack + // alignment and used don't-care markings for files which are + // not affected by the vector ABI, we could warn about this + // case too. */ + else if (in_vec == 1) + ; + else if (out_vec == 1) + { + out_vec = in_vec; + out_attr[tag].set_int_value(out_vec); + out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL); + this->last_vec_ = name; + } + else if (out_vec < in_vec) + { + err = N_("%s uses AltiVec vector ABI, %s uses SPE vector ABI"); + first = this->last_vec_; + second = name; + } + else if (out_vec > in_vec) + { + err = N_("%s uses AltiVec vector ABI, %s uses SPE vector ABI"); + first = name; + second = this->last_vec_; + } + if (err) + { + if (parameters->options().warn_mismatch()) + gold_error(_(err), first, second); + out_attr[tag].set_type(0); + } + } + + tag = elfcpp::Tag_GNU_Power_ABI_Struct_Return; + int in_struct = in_attr[tag].int_value() & 3; + int out_struct = out_attr[tag].int_value() & 3; + if (in_struct != out_struct) + { + err = NULL; + if (in_struct == 0 || in_struct == 3) + ; + else if (out_struct == 0) + { + out_struct = in_struct; + out_attr[tag].set_int_value(out_struct); + out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL); + this->last_struct_ = name; + } + else if (out_struct < in_struct) + { + err = N_("%s uses r3/r4 for small structure returns, " + "%s uses memory"); + first = this->last_struct_; + second = name; + } + else if (out_struct > in_struct) + { + err = N_("%s uses r3/r4 for small structure returns, " + "%s uses memory"); + first = name; + second = this->last_struct_; + } + if (err) + { + if (parameters->options().warn_mismatch()) + gold_error(_(err), first, second); + out_attr[tag].set_type(0); + } + } + } + + // Merge Tag_compatibility attributes and any common GNU ones. + this->attributes_section_data_->merge(name, pasd); } // Emit any saved relocs, and mark toc entries using any of these |