[PATCH] gold: Maintain sparc ELF header bits properly

David Miller davem@davemloft.net
Fri Apr 20 22:16:00 GMT 2012


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>



More information about the Binutils mailing list