[GOLD] powerpc64 ODR violation check

Alan Modra amodra@gmail.com
Fri Mar 8 03:54:00 GMT 2013


Revised version, with .opd parse for dynamic objects.  With Cary's
debug_msg fix for powerpc, this passes all debug_msg tests, so it
looks like ODR checking is now functional for PowerPC64 gold.

Some notes:
- Symbol_location.offset is *not* section relative for a dynobj.
Comment fixed.
- Moving find_shdr to Object requires specifying template parameters
explicitly since the arguments don't allow a compiler to deduce them.
I didn't know how to do that using this->find_shdr() syntax, or if it
is even possible.

	* gold.cc (queue_middle_tasks): Move detect_odr_violations..
	* layout.cc (Layout_task_runner::run): ..to here.
	* symtab.h (struct Symbol_location): Extract from..
	(class Symbol_table): ..here.
	* symtab.cc (Symbol_table::linenos_from_loc): Invoke function_location.
	* target.h (class Target): Add function_location and
	do_function_location functions.
	(class Sized_target): Add do_function_location.
	* object.h (class Sized_relobj_file): Move find_shdr..
	(class Object): ..to here.
	* object.cc: Likewise.  Update to suit.  Instantiate.
	(Sized_relobj_file::find_eh_frame): Update find_shdr call.
	* powerpc.cc (class Powerpc_dynobj): New.
	(Target_powerpc::do_function_location): New function.
	(Powerpc_relobj::do_find_special_sections): Update find_shdr call.
	(Powerpc_dynobj::do_read_symbols): New function.
	(Target_powerpc::do_make_elf_object): Make a Powerpc_dynobj.

Index: gold/gold.cc
===================================================================
RCS file: /cvs/src/src/gold/gold.cc,v
retrieving revision 1.103
diff -u -p -r1.103 gold.cc
--- gold/gold.cc	17 Oct 2012 11:58:39 -0000	1.103
+++ gold/gold.cc	8 Mar 2013 00:45:51 -0000
@@ -653,10 +653,6 @@ queue_middle_tasks(const General_options
   // dynamic objects that it depends upon.
   input_objects->check_dynamic_dependencies();
 
-  // See if any of the input definitions violate the One Definition Rule.
-  // TODO: if this is too slow, do this as a task, rather than inline.
-  symtab->detect_odr_violations(task, options.output_file_name());
-
   // Do the --no-undefined-version check.
   if (!parameters->options().undefined_version())
     {
Index: gold/layout.cc
===================================================================
RCS file: /cvs/src/src/gold/layout.cc,v
retrieving revision 1.245
diff -u -p -r1.245 layout.cc
--- gold/layout.cc	24 Jan 2013 18:49:54 -0000	1.245
+++ gold/layout.cc	8 Mar 2013 00:45:51 -0000
@@ -317,6 +317,10 @@ Layout::Relaxation_debug_check::verify_s
 void
 Layout_task_runner::run(Workqueue* workqueue, const Task* task)
 {
+  // See if any of the input definitions violate the One Definition Rule.
+  // TODO: if this is too slow, do this as a task, rather than inline.
+  this->symtab_->detect_odr_violations(task, this->options_.output_file_name());
+
   Layout* layout = this->layout_;
   off_t file_size = layout->finalize(this->input_objects_,
 				     this->symtab_,
Index: gold/symtab.h
===================================================================
RCS file: /cvs/src/src/gold/symtab.h,v
retrieving revision 1.128
diff -u -p -r1.128 symtab.h
--- gold/symtab.h	18 Aug 2012 11:12:50 -0000	1.128
+++ gold/symtab.h	8 Mar 2013 03:41:06 -0000
@@ -1180,6 +1180,25 @@ struct Define_symbol_in_segment
   bool only_if_ref;
 };
 
+// Specify an object/section/offset location.  Used by ODR code.
+
+struct Symbol_location
+{
+  // Object where the symbol is defined.
+  Object* object;
+  // Section-in-object where the symbol is defined.
+  unsigned int shndx;
+  // For relocatable objects, offset-in-section where the symbol is defined.
+  // For dynamic objects, address where the symbol is defined.
+  off_t offset;
+  bool operator==(const Symbol_location& that) const
+  {
+    return (this->object == that.object
+	    && this->shndx == that.shndx
+	    && this->offset == that.offset);
+  }
+};
+
 // This class manages warnings.  Warnings are a GNU extension.  When
 // we see a section named .gnu.warning.SYM in an object file, and if
 // we wind using the definition of SYM from that object file, then we
@@ -1599,19 +1618,6 @@ class Symbol_table
   // the locations the symbols is (weakly) defined (and certain other
   // conditions are met).  This map will be used later to detect
   // possible One Definition Rule (ODR) violations.
-  struct Symbol_location
-  {
-    Object* object;         // Object where the symbol is defined.
-    unsigned int shndx;     // Section-in-object where the symbol is defined.
-    off_t offset;           // Offset-in-section where the symbol is defined.
-    bool operator==(const Symbol_location& that) const
-    {
-      return (this->object == that.object
-              && this->shndx == that.shndx
-              && this->offset == that.offset);
-    }
-  };
-
   struct Symbol_location_hash
   {
     size_t operator()(const Symbol_location& loc) const
Index: gold/symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.168
diff -u -p -r1.168 symtab.cc
--- gold/symtab.cc	9 Sep 2012 03:43:51 -0000	1.168
+++ gold/symtab.cc	8 Mar 2013 00:45:51 -0000
@@ -3175,9 +3175,11 @@ Symbol_table::linenos_from_loc(const Tas
   Task_lock_obj<Object> tl(task, loc.object);
 
   std::vector<std::string> result;
+  Symbol_location code_loc = loc;
+  parameters->target().function_location(&code_loc);
   // 16 is the size of the object-cache that one_addr2line should use.
   std::string canonical_result = Dwarf_line_info::one_addr2line(
-      loc.object, loc.shndx, loc.offset, 16, &result);
+      code_loc.object, code_loc.shndx, code_loc.offset, 16, &result);
   if (!canonical_result.empty())
     result.push_back(canonical_result);
   return result;
Index: gold/target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.73
diff -u -p -r1.73 target.h
--- gold/target.h	27 Feb 2013 23:11:56 -0000	1.73
+++ gold/target.h	8 Mar 2013 00:45:51 -0000
@@ -61,6 +61,7 @@ class Output_data_got_base;
 class Output_section;
 class Input_objects;
 class Task;
+struct Symbol_location;
 
 // The abstract class for target specific handling.
 
@@ -286,6 +287,12 @@ class Target
   tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const
   { return do_tls_offset_for_global(gsym, got_indx); }
 
+  // For targets that use function descriptors, if LOC is the location
+  // of a function, modify it to point at the function entry location.
+  void
+  function_location(Symbol_location* loc) const
+  { return do_function_location(loc); }
+
   // Return whether this target can use relocation types to determine
   // if a function's address is taken.
   bool
@@ -575,6 +582,9 @@ class Target
   do_tls_offset_for_global(Symbol*, unsigned int) const
   { gold_unreachable(); }
 
+  virtual void
+  do_function_location(Symbol_location*) const = 0;
+
   // Virtual function which may be overriden by the child class.
   virtual bool
   do_can_check_for_function_pointers() const
@@ -1009,6 +1019,10 @@ class Sized_target : public Target
 		      Object*, unsigned int,
 		      typename elfcpp::Elf_types<size>::Elf_Addr) const
   { }
+
+  virtual void
+  do_function_location(Symbol_location*) const
+  { }
 };
 
 } // End namespace gold.
Index: gold/object.h
===================================================================
RCS file: /cvs/src/src/gold/object.h,v
retrieving revision 1.122
diff -u -p -r1.122 object.h
--- gold/object.h	23 Oct 2012 21:34:58 -0000	1.122
+++ gold/object.h	8 Mar 2013 00:45:51 -0000
@@ -881,6 +881,16 @@ class Object
   read_section_data(elfcpp::Elf_file<size, big_endian, Object>*,
 		    Read_symbols_data*);
 
+  // Find the section header with the given NAME.  If HDR is non-NULL
+  // then it is a section header returned from a previous call to this
+  // function and the next section header with the same name will be
+  // returned.
+  template<int size, bool big_endian>
+  const unsigned char*
+  find_shdr(const unsigned char* /*pshdrs*/, const char* /*name*/,
+	    const char* /*names*/, section_size_type /*names_size*/,
+	    const unsigned char* /*hdr*/) const;
+
   // Let the child class initialize the xindex object directly.
   void
   set_xindex(Xindex* xindex)
@@ -2161,15 +2171,6 @@ class Sized_relobj_file : public Sized_r
   Address
   map_to_kept_section(unsigned int shndx, bool* found) const;
 
-  // Find the section header with the given NAME.  If HDR is non-NULL
-  // then it is a section header returned from a previous call to this
-  // function and the next section header with the same name will be
-  // returned.
-  const unsigned char*
-  find_shdr(const unsigned char* pshdrs, const char* name,
-	    const char* names, section_size_type names_size,
-	    const unsigned char* hdr) const;
-
   // Compute final local symbol value.  R_SYM is the local symbol index.
   // LV_IN points to a local symbol value containing the input value.
   // LV_OUT points to a local symbol value storing the final output value,
Index: gold/object.cc
===================================================================
RCS file: /cvs/src/src/gold/object.cc,v
retrieving revision 1.161
diff -u -p -r1.161 object.cc
--- gold/object.cc	18 Oct 2012 05:36:02 -0000	1.161
+++ gold/object.cc	8 Mar 2013 00:45:51 -0000
@@ -517,15 +517,16 @@ Sized_relobj_file<size, big_endian>::che
 
 template<int size, bool big_endian>
 const unsigned char*
-Sized_relobj_file<size, big_endian>::find_shdr(
+Object::find_shdr(
     const unsigned char* pshdrs,
     const char* name,
     const char* names,
     section_size_type names_size,
     const unsigned char* hdr) const
 {
+  const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
   const unsigned int shnum = this->shnum();
-  const unsigned char* hdr_end = pshdrs + This::shdr_size * shnum;
+  const unsigned char* hdr_end = pshdrs + shdr_size * shnum;
   size_t sh_name = 0;
 
   while (1)
@@ -533,7 +534,7 @@ Sized_relobj_file<size, big_endian>::fin
       if (hdr)
 	{
 	  // We found HDR last time we were called, continue looking.
-	  typename This::Shdr shdr(hdr);
+	  typename elfcpp::Shdr<size, big_endian> shdr(hdr);
 	  sh_name = shdr.get_sh_name();
 	}
       else
@@ -557,13 +558,13 @@ Sized_relobj_file<size, big_endian>::fin
 	    return hdr;
 	}
 
-      hdr += This::shdr_size;
+      hdr += shdr_size;
       while (hdr < hdr_end)
 	{
-	  typename This::Shdr shdr(hdr);
+	  typename elfcpp::Shdr<size, big_endian> shdr(hdr);
 	  if (shdr.get_sh_name() == sh_name)
 	    return hdr;
-	  hdr += This::shdr_size;
+	  hdr += shdr_size;
 	}
       hdr = NULL;
       if (sh_name == 0)
@@ -585,7 +586,8 @@ Sized_relobj_file<size, big_endian>::fin
 
   while (1)
     {
-      s = this->find_shdr(pshdrs, ".eh_frame", names, names_size, s);
+      s = Object::find_shdr<size, big_endian>(pshdrs, ".eh_frame",
+					      names, names_size, s);
       if (s == NULL)
 	return false;
 
@@ -3163,6 +3165,10 @@ template
 void
 Object::read_section_data<32, false>(elfcpp::Elf_file<32, false, Object>*,
 				     Read_symbols_data*);
+template
+const unsigned char*
+Object::find_shdr<32,false>(const unsigned char*, const char*, const char*,
+			    section_size_type, const unsigned char*) const;
 #endif
 
 #ifdef HAVE_TARGET_32_BIG
@@ -3170,6 +3176,10 @@ template
 void
 Object::read_section_data<32, true>(elfcpp::Elf_file<32, true, Object>*,
 				    Read_symbols_data*);
+template
+const unsigned char*
+Object::find_shdr<32,true>(const unsigned char*, const char*, const char*,
+			   section_size_type, const unsigned char*) const;
 #endif
 
 #ifdef HAVE_TARGET_64_LITTLE
@@ -3177,6 +3187,10 @@ template
 void
 Object::read_section_data<64, false>(elfcpp::Elf_file<64, false, Object>*,
 				     Read_symbols_data*);
+template
+const unsigned char*
+Object::find_shdr<64,false>(const unsigned char*, const char*, const char*,
+			    section_size_type, const unsigned char*) const;
 #endif
 
 #ifdef HAVE_TARGET_64_BIG
@@ -3184,6 +3198,10 @@ template
 void
 Object::read_section_data<64, true>(elfcpp::Elf_file<64, true, Object>*,
 				    Read_symbols_data*);
+template
+const unsigned char*
+Object::find_shdr<64,true>(const unsigned char*, const char*, const char*,
+			   section_size_type, const unsigned char*) const;
 #endif
 
 #ifdef HAVE_TARGET_32_LITTLE
Index: gold/powerpc.cc
===================================================================
RCS file: /cvs/src/src/gold/powerpc.cc,v
retrieving revision 1.85
diff -u -p -r1.85 powerpc.cc
--- gold/powerpc.cc	6 Mar 2013 12:28:47 -0000	1.85
+++ gold/powerpc.cc	8 Mar 2013 03:48:14 -0000
@@ -23,6 +23,7 @@
 
 #include "gold.h"
 
+#include <set>
 #include <algorithm>
 #include "elfcpp.h"
 #include "dwarf.h"
@@ -320,6 +321,109 @@ private:
 };
 
 template<int size, bool big_endian>
+class Powerpc_dynobj : public Sized_dynobj<size, big_endian>
+{
+public:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
+  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), opd_ent_()
+  { }
+
+  ~Powerpc_dynobj()
+  { }
+
+  // Call Sized_dynobj::do_read_symbols to read the symbols then
+  // read .opd from a dynamic object, filling in opd_ent_ vector,
+  void
+  do_read_symbols(Read_symbols_data*);
+
+  // The .opd section shndx.
+  unsigned int
+  opd_shndx() const
+  {
+    return this->opd_shndx_;
+  }
+
+  // The .opd section address.
+  unsigned int
+  opd_address() const
+  {
+    return this->opd_address_;
+  }
+
+  // Init OPD entry arrays.
+  void
+  init_opd(size_t opd_size)
+  {
+    size_t count = this->opd_ent_ndx(opd_size);
+    this->opd_ent_.resize(count);
+  }
+
+  // Return section and offset of function entry for .opd + R_OFF.
+  unsigned int
+  get_opd_ent(Address r_off, Address* value = NULL) const
+  {
+    size_t ndx = this->opd_ent_ndx(r_off);
+    gold_assert(ndx < this->opd_ent_.size());
+    gold_assert(this->opd_ent_[ndx].shndx != 0);
+    if (value != NULL)
+      *value = this->opd_ent_[ndx].off;
+    return this->opd_ent_[ndx].shndx;
+  }
+
+  // Set section and offset of function entry for .opd + R_OFF.
+  void
+  set_opd_ent(Address r_off, unsigned int shndx, Address value)
+  {
+    size_t ndx = this->opd_ent_ndx(r_off);
+    gold_assert(ndx < this->opd_ent_.size());
+    this->opd_ent_[ndx].shndx = shndx;
+    this->opd_ent_[ndx].off = value;
+  }
+
+private:
+  // Used to specify extent of executable sections.
+  struct Sec_info
+  {
+    Sec_info(Address start_, Address len_, unsigned int shndx_)
+      : start(start_), len(len_), shndx(shndx_)
+    { }
+
+    bool
+    operator<(const Sec_info& that) const
+    { return this->start < that.start; }
+
+    Address start;
+    Address len;
+    unsigned int shndx;
+  };
+
+  struct Opd_ent
+  {
+    unsigned int shndx;
+    Address off;
+  };
+
+  // Return index into opd_ent_ array for .opd entry at OFF.
+  size_t
+  opd_ent_ndx(size_t off) const
+  { return off >> 4;}
+
+  // For 64-bit the .opd section shndx and address.
+  unsigned int opd_shndx_;
+  Address opd_address_;
+
+  // The first 8-byte word of an OPD entry gives the address of the
+  // entry point of the function.  Records the section and offset
+  // corresponding to the address.  Note that in dynamic objects,
+  // offset is *not* relative to the section.
+  std::vector<Opd_ent> opd_ent_;
+};
+
+template<int size, bool big_endian>
 class Target_powerpc : public Sized_target<size, big_endian>
 {
  public:
@@ -448,6 +552,9 @@ class Target_powerpc : public Sized_targ
   int64_t
   do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
 
+  void
+  do_function_location(Symbol_location*) const;
+
   // Relocate a section.
   void
   relocate_section(const Relocate_info<size, big_endian>*,
@@ -1518,8 +1625,8 @@ Powerpc_relobj<size, big_endian>::do_fin
   section_size_type names_size = sd->section_names_size;
   const unsigned char* s;
 
-  s = this->find_shdr(pshdrs, size == 32 ? ".got2" : ".opd",
-		      names, names_size, NULL);
+  s = Object::find_shdr<size, big_endian>(pshdrs, size == 32 ? ".got2" : ".opd",
+					  names, names_size, NULL);
   if (s != NULL)
     {
       unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
@@ -1634,6 +1741,104 @@ Powerpc_relobj<size, big_endian>::do_rea
     }
 }
 
+// Call Sized_dynobj::do_read_symbols to read the symbols then
+// read .opd from a dynamic object, filling in opd_ent_ vector,
+
+template<int size, bool big_endian>
+void
+Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+{
+  Sized_dynobj<size, big_endian>::do_read_symbols(sd);
+  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);
+      const unsigned char* s = NULL;
+      const unsigned char* opd;
+      section_size_type opd_size;
+
+      // Find and read .opd section.
+      while (1)
+	{
+	  s = Object::find_shdr<size, big_endian>(pshdrs, ".opd", names,
+						  sd->section_names_size, s);
+	  if (s == NULL)
+	    return;
+
+	  typename elfcpp::Shdr<size, big_endian> shdr(s);
+	  if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
+	      && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0)
+	    {
+	      this->opd_shndx_ = (s - pshdrs) / shdr_size;
+	      this->opd_address_ = shdr.get_sh_addr();
+	      opd_size = convert_to_section_size_type(shdr.get_sh_size());
+	      opd = this->get_view(shdr.get_sh_offset(), opd_size,
+				   true, false);
+	      break;
+	    }
+	}
+
+      // Build set of executable sections.
+      // Using a set is probably overkill.  There is likely to be only
+      // a few executable sections, typically .init, .text and .fini,
+      // and they are generally grouped together.
+      typedef std::set<Sec_info> Exec_sections;
+      Exec_sections exec_sections;
+      s = pshdrs;
+      for (unsigned int i = 1; i < this->shnum(); ++i, s += shdr_size)
+	{
+	  typename elfcpp::Shdr<size, big_endian> shdr(s);
+	  if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
+	      && ((shdr.get_sh_flags()
+		   & (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR))
+		  == (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR))
+	      && shdr.get_sh_size() != 0)
+	    {
+	      exec_sections.insert(Sec_info(shdr.get_sh_addr(),
+					    shdr.get_sh_size(), i));
+	    }
+	}
+      if (exec_sections.empty())
+	return;
+
+      // Look over the OPD entries.  This is complicated by the fact
+      // that some binaries will use two-word entries while others
+      // will use the standard three-word entries.  In most cases
+      // the third word (the environment pointer for languages like
+      // Pascal) is unused and will be zero.  If the third word is
+      // used it should not be pointing into executable sections,
+      // I think.
+      this->init_opd(opd_size);
+      for (const unsigned char* p = opd; p < opd + opd_size; p += 8)
+	{
+	  typedef typename elfcpp::Swap<64, big_endian>::Valtype Valtype;
+	  const Valtype* valp = reinterpret_cast<const Valtype*>(p);
+	  Valtype val = elfcpp::Swap<64, big_endian>::readval(valp);
+	  if (val == 0)
+	    // Chances are that this is the third word of an OPD entry.
+	    continue;
+	  typename Exec_sections::const_iterator e
+	    = exec_sections.upper_bound(Sec_info(val, 0, 0));
+	  if (e != exec_sections.begin())
+	    {
+	      --e;
+	      if (e->start <= val && val < e->start + e->len)
+		{
+		  // We have an address in an executable section.
+		  // VAL ought to be the function entry, set it up.
+		  this->set_opd_ent(p - opd, e->shndx, val);
+		  // Skip second word of OPD entry, the TOC pointer.
+		  p += 8;
+		}
+	    }
+	  // If we didn't match any executable sections, we likely
+	  // have a non-zero third word in the OPD entry.
+	}
+    }
+}
+
 // Set up some symbols.
 
 template<int size, bool big_endian>
@@ -1706,8 +1911,8 @@ Target_powerpc<size, big_endian>::do_mak
     }
   else if (et == elfcpp::ET_DYN)
     {
-      Sized_dynobj<size, big_endian>* obj =
-	new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
+      Powerpc_dynobj<size, big_endian>* obj =
+	new Powerpc_dynobj<size, big_endian>(name, input_file, offset, ehdr);
       obj->setup();
       return obj;
     }
@@ -5581,6 +5786,42 @@ Target_powerpc<size, big_endian>::do_gc_
     }
 }
 
+// For a symbol location in .opd, set LOC to the location of the
+// function entry.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_function_location(
+    Symbol_location* loc) const
+{
+  if (size == 64)
+    {
+      if (loc->object->is_dynamic())
+	{
+	  Powerpc_dynobj<size, big_endian>* ppc_object
+	    = static_cast<Powerpc_dynobj<size, big_endian>*>(loc->object);
+	  if (loc->shndx == ppc_object->opd_shndx())
+	    {
+	      Address dest_off;
+	      Address off = loc->offset - ppc_object->opd_address();
+	      loc->shndx = ppc_object->get_opd_ent(off, &dest_off);
+	      loc->offset = dest_off;
+	    }
+	}
+      else
+	{
+	  const Powerpc_relobj<size, big_endian>* ppc_object
+	    = static_cast<const Powerpc_relobj<size, big_endian>*>(loc->object);
+	  if (loc->shndx == ppc_object->opd_shndx())
+	    {
+	      Address dest_off;
+	      loc->shndx = ppc_object->get_opd_ent(loc->offset, &dest_off);
+	      loc->offset = dest_off;
+	    }
+	}
+    }
+}
+
 // Scan relocations for a section.
 
 template<int size, bool big_endian>

-- 
Alan Modra
Australia Development Lab, IBM



More information about the Binutils mailing list