This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] gold: Maintain sparc ELF header bits properly
- From: David Miller <davem at davemloft dot net>
- To: binutils at sourceware dot org
- Date: Fri, 20 Apr 2012 18:14:00 -0400 (EDT)
- Subject: [PATCH] gold: Maintain sparc ELF header bits properly
We have several things we need to maintain in the ELF headers on
sparc. Specifically:
1) We have two machine codes for 32-bit sparc, one for binaries that
only use pre-V9 instructions (EM_SPARC) and one for binaries that
make use of V9 instructions (EM_SPARC32PLUS).
They are otherwise identical, and it would therefore be silly to
instantiate a completely new sparc target instance just to update
the machine code field for this purpose.
Our policy is that when we have a mix of EM_SPARC and EM_SPARC32PLUS
objects going into a link, we mark the final result EM_SPARC32PLUS.
2) The elf header flags field maintains what cpu specific features the
object makes use of. We simply accumulate those into the final
value.
3) The elf header flags also mark the required memory model of the
object. When we have a mix of requirements, we mark the final
binary with the most restrictive memory model. From least to
most restrictive the order is RMO, PSO, TSO.
4) Finally, the elf header flags have a little-endian data bit which
is superfluous since that's implicit from the generic endian
setting in the ELF header. We just make sure the bit is set
properly.
Since I have to frob both the machine code and the elf flags, I just
do this work by hand using do_make_elf_object and do_adjust_elf_header
overrides. The former does the accumulation scanning of the ELF
header values in the input objects, and the latter places the final
values into the final ELF header.
It really would have been nice if I could just call down into the
default implementations after I did the sparc specific bits, but
it's not possible to just simply say:
this->do_make_elf_object_implementation(...)
from the sparc target override currently.
Anywyas, ok to commit?
gold/
* sparc.cc (class Target_sparc): Add elf_machine_, elf_flags_,
and elf_flags_set_.
(Target_sparc::Target_sparc): Initialize new fields.
(Target_sparc::do_make_elf_object): New function.
(Target_sparc::do_adjust_elf_header): New function.
diff --git a/gold/sparc.cc b/gold/sparc.cc
index 762da42..4773b6e 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -60,7 +60,9 @@ class Target_sparc : public Sized_target<size, big_endian>
: Sized_target<size, big_endian>(&sparc_info),
got_(NULL), plt_(NULL), rela_dyn_(NULL), rela_ifunc_(NULL),
copy_relocs_(elfcpp::R_SPARC_COPY), dynbss_(NULL),
- got_mod_index_offset_(-1U), tls_get_addr_sym_(NULL)
+ got_mod_index_offset_(-1U), tls_get_addr_sym_(NULL),
+ elf_machine_(sparc_info.machine_code), elf_flags_(0),
+ elf_flags_set_(false)
{
}
@@ -206,6 +208,15 @@ class Target_sparc : public Sized_target<size, big_endian>
unsigned int
plt_entry_size() const;
+ protected:
+ // Make an ELF object.
+ Object*
+ do_make_elf_object(const std::string&, Input_file*, off_t,
+ const elfcpp::Ehdr<size, big_endian>& ehdr);
+
+ void
+ do_adjust_elf_header(unsigned char* view, int len) const;
+
private:
// The class which scans relocations.
@@ -432,6 +443,12 @@ class Target_sparc : public Sized_target<size, big_endian>
unsigned int got_mod_index_offset_;
// Cached pointer to __tls_get_addr symbol
Symbol* tls_get_addr_sym_;
+ // Accumulated elf machine type
+ elfcpp::Elf_Half elf_machine_;
+ // Accumulated elf header flags
+ elfcpp::Elf_Word elf_flags_;
+ // Whether elf_flags_ has been set for the first time yet
+ bool elf_flags_set_;
};
template<>
@@ -4071,6 +4088,134 @@ Target_sparc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
return this->plt_section()->address() + gsym->plt_offset();
}
+// do_make_elf_object to override the same function in the base class.
+// We need to use a target-specific sub-class of
+// Sized_relobj_file<size, big_endian> to process SPARC specific bits
+// of the ELF headers. Hence we need to have our own ELF object creation.
+
+template<int size, bool big_endian>
+Object*
+Target_sparc<size, big_endian>::do_make_elf_object(
+ const std::string& name,
+ Input_file* input_file,
+ off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
+{
+ elfcpp::Elf_Word omm, mm, flags = ehdr.get_e_flags();
+ elfcpp::Elf_Half machine = ehdr.get_e_machine();
+
+ switch (machine)
+ {
+ case elfcpp::EM_SPARC32PLUS:
+ this->elf_machine_ = elfcpp::EM_SPARC32PLUS;
+ break;
+
+ case elfcpp::EM_SPARC:
+ case elfcpp::EM_SPARCV9:
+ break;
+
+ default:
+ break;
+ }
+
+ if (!this->elf_flags_set_)
+ {
+ this->elf_flags_ = flags;
+ this->elf_flags_set_ = true;
+ }
+ else
+ {
+ // Accumulate cpu feature bits.
+ this->elf_flags_ |= (flags & (elfcpp::EF_SPARC_32PLUS
+ | elfcpp::EF_SPARC_SUN_US1
+ | elfcpp::EF_SPARC_HAL_R1
+ | elfcpp::EF_SPARC_SUN_US3));
+
+ // Bump the memory model setting to the most restrictive
+ // one we encounter.
+ omm = (this->elf_flags_ & elfcpp::EF_SPARCV9_MM);
+ mm = (flags & elfcpp::EF_SPARCV9_MM);
+ if (omm != mm)
+ {
+ if (mm == elfcpp::EF_SPARCV9_TSO)
+ {
+ this->elf_flags_ &= ~elfcpp::EF_SPARCV9_MM;
+ this->elf_flags_ |= elfcpp::EF_SPARCV9_TSO;
+ }
+ else if (mm == elfcpp::EF_SPARCV9_PSO
+ && omm == elfcpp::EF_SPARCV9_RMO)
+ {
+ this->elf_flags_ &= ~elfcpp::EF_SPARCV9_MM;
+ this->elf_flags_ |= elfcpp::EF_SPARCV9_PSO;
+ }
+ }
+ }
+
+ // Validate that the little-endian flag matches how we've
+ // been instantiated.
+ if (!(flags & elfcpp::EF_SPARC_LEDATA) != big_endian)
+ {
+ if (big_endian)
+ gold_error(_("%s: Little endian elf flag set on BE object"),
+ name.c_str());
+ else
+ gold_error(_("%s: Little endian elf flag clear on LE object"),
+ name.c_str());
+ }
+
+ int et = ehdr.get_e_type();
+ // ET_EXEC files are valid input for --just-symbols/-R,
+ // and we treat them as relocatable objects.
+ if (et == elfcpp::ET_REL
+ || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
+ {
+ Sized_relobj_file<size, big_endian>* obj =
+ new Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr);
+ obj->setup();
+ return obj;
+ }
+ else if (et == elfcpp::ET_DYN)
+ {
+ Sized_dynobj<size, big_endian>* obj =
+ new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
+ obj->setup();
+ return obj;
+ }
+ else
+ {
+ gold_error(_("%s: unsupported ELF file type %d"),
+ name.c_str(), et);
+ return NULL;
+ }
+}
+
+// Adjust ELF file header.
+
+template<int size, bool big_endian>
+void
+Target_sparc<size, big_endian>::do_adjust_elf_header(
+ unsigned char* view,
+ int len) const
+{
+ gold_assert(len == elfcpp::Elf_sizes<size>::ehdr_size);
+
+ elfcpp::Ehdr_write<size, big_endian> oehdr(view);
+ elfcpp::ELFOSABI osabi = this->osabi();
+
+ oehdr.put_e_machine(this->elf_machine_);
+ oehdr.put_e_flags(this->elf_flags_);
+
+ if (osabi != elfcpp::ELFOSABI_NONE)
+ {
+ elfcpp::Ehdr<size, big_endian> ehdr(view);
+ unsigned char e_ident[elfcpp::EI_NIDENT];
+ memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
+
+ e_ident[elfcpp::EI_OSABI] = osabi;
+
+ oehdr.put_e_ident(e_ident);
+ }
+}
+
// The selector for sparc object files.
template<int size, bool big_endian>