[GOLD] PowerPC .gnu.attributes support
elfcpp/ * powerpc.h (Tag_GNU_Power_ABI_FP): Define. (Tag_GNU_Power_ABI_Vector, Tag_GNU_Power_ABI_Struct_Return): Define. gold/ * 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.
This commit is contained in:
parent
6821842f15
commit
724436fccb
4 changed files with 393 additions and 7 deletions
|
@ -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>
|
2018-06-24 Nick Clifton <nickc@redhat.com>
|
||||||
|
|
||||||
2.31 branch created.
|
2.31 branch created.
|
||||||
|
|
|
@ -230,6 +230,32 @@ enum
|
||||||
EF_PPC64_ABI = 3
|
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
|
// DT_PPC_OPT bits
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
2018-06-26 Nick Clifton <nickc@redhat.com>
|
||||||
|
|
||||||
* po/uk.po: Updated Ukranian translation.
|
* po/uk.po: Updated Ukranian translation.
|
||||||
|
|
354
gold/powerpc.cc
354
gold/powerpc.cc
|
@ -41,6 +41,7 @@
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
|
#include "attributes.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -100,13 +101,14 @@ public:
|
||||||
uniq_(object_id++), special_(0), relatoc_(0), toc_(0),
|
uniq_(object_id++), special_(0), relatoc_(0), toc_(0),
|
||||||
has_small_toc_reloc_(false), opd_valid_(false),
|
has_small_toc_reloc_(false), opd_valid_(false),
|
||||||
e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(),
|
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);
|
this->set_abiversion(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Powerpc_relobj()
|
~Powerpc_relobj()
|
||||||
{ }
|
{ delete this->attributes_section_data_; }
|
||||||
|
|
||||||
// Read the symbols then set up st_other vector.
|
// Read the symbols then set up st_other vector.
|
||||||
void
|
void
|
||||||
|
@ -388,6 +390,11 @@ public:
|
||||||
ppc64_local_entry_offset(unsigned int symndx) const
|
ppc64_local_entry_offset(unsigned int symndx) const
|
||||||
{ return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); }
|
{ 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:
|
private:
|
||||||
struct Opd_ent
|
struct Opd_ent
|
||||||
{
|
{
|
||||||
|
@ -458,6 +465,9 @@ private:
|
||||||
|
|
||||||
// ELF st_other field for local symbols.
|
// ELF st_other field for local symbols.
|
||||||
std::vector<unsigned char> st_other_;
|
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>
|
template<int size, bool big_endian>
|
||||||
|
@ -469,13 +479,14 @@ public:
|
||||||
Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
|
Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
|
||||||
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
|
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
|
||||||
: Sized_dynobj<size, big_endian>(name, input_file, offset, 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);
|
this->set_abiversion(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Powerpc_dynobj()
|
~Powerpc_dynobj()
|
||||||
{ }
|
{ delete this->attributes_section_data_; }
|
||||||
|
|
||||||
// Call Sized_dynobj::do_read_symbols to read the symbols then
|
// Call Sized_dynobj::do_read_symbols to read the symbols then
|
||||||
// read .opd from a dynamic object, filling in opd_ent_ vector,
|
// read .opd from a dynamic object, filling in opd_ent_ vector,
|
||||||
|
@ -534,6 +545,11 @@ public:
|
||||||
void
|
void
|
||||||
set_abiversion(int ver);
|
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:
|
private:
|
||||||
// Used to specify extent of executable sections.
|
// Used to specify extent of executable sections.
|
||||||
struct Sec_info
|
struct Sec_info
|
||||||
|
@ -574,6 +590,9 @@ private:
|
||||||
// corresponding to the address. Note that in dynamic objects,
|
// corresponding to the address. Note that in dynamic objects,
|
||||||
// offset is *not* relative to the section.
|
// offset is *not* relative to the section.
|
||||||
std::vector<Opd_ent> opd_ent_;
|
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
|
// 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),
|
has_tls_get_addr_opt_(false),
|
||||||
relax_failed_(false), relax_fail_count_(0),
|
relax_failed_(false), relax_fail_count_(0),
|
||||||
stub_group_size_(0), savres_section_(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
|
stk_linker() const
|
||||||
{ return this->abiversion() < 2 ? 32 : 8; }
|
{ 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:
|
private:
|
||||||
|
|
||||||
class Track_tls
|
class Track_tls
|
||||||
|
@ -1647,6 +1672,15 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
||||||
Symbol* tls_get_addr_;
|
Symbol* tls_get_addr_;
|
||||||
// If optimizing __tls_get_addr calls, the "__tls_get_addr_opt" symbol.
|
// If optimizing __tls_get_addr calls, the "__tls_get_addr_opt" symbol.
|
||||||
Symbol* tls_get_addr_opt_;
|
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<>
|
template<>
|
||||||
|
@ -2312,6 +2346,8 @@ void
|
||||||
Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
||||||
{
|
{
|
||||||
this->base_read_symbols(sd);
|
this->base_read_symbols(sd);
|
||||||
|
if (this->input_file()->format() != Input_file::FORMAT_ELF)
|
||||||
|
return;
|
||||||
if (size == 64)
|
if (size == 64)
|
||||||
{
|
{
|
||||||
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
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>
|
template<int size, bool big_endian>
|
||||||
|
@ -2376,9 +2462,26 @@ void
|
||||||
Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
||||||
{
|
{
|
||||||
this->base_read_symbols(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)
|
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* const pshdrs = sd->section_headers->data();
|
||||||
const unsigned char* namesu = sd->section_names->data();
|
const unsigned char* namesu = sd->section_names->data();
|
||||||
const char* names = reinterpret_cast<const char*>(namesu);
|
const char* names = reinterpret_cast<const char*>(namesu);
|
||||||
|
@ -8298,7 +8401,7 @@ template<int size, bool big_endian>
|
||||||
void
|
void
|
||||||
Target_powerpc<size, big_endian>::do_finalize_sections(
|
Target_powerpc<size, big_endian>::do_finalize_sections(
|
||||||
Layout* layout,
|
Layout* layout,
|
||||||
const Input_objects*,
|
const Input_objects* input_objects,
|
||||||
Symbol_table* symtab)
|
Symbol_table* symtab)
|
||||||
{
|
{
|
||||||
if (parameters->doing_static_link())
|
if (parameters->doing_static_link())
|
||||||
|
@ -8401,6 +8504,243 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
|
||||||
// relocs.
|
// relocs.
|
||||||
if (this->copy_relocs_.any_saved_relocs())
|
if (this->copy_relocs_.any_saved_relocs())
|
||||||
this->copy_relocs_.emit(this->rela_dyn_section(layout));
|
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
|
// Emit any saved relocs, and mark toc entries using any of these
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue