* gold.cc (queue_middle_tasks): Move detect_odr_violations..
authorAlan Modra <amodra@gmail.com>
Sun, 10 Mar 2013 23:08:18 +0000 (23:08 +0000)
committerAlan Modra <amodra@gmail.com>
Sun, 10 Mar 2013 23:08:18 +0000 (23:08 +0000)
* 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.

gold/ChangeLog
gold/gold.cc
gold/layout.cc
gold/object.cc
gold/object.h
gold/powerpc.cc
gold/symtab.cc
gold/symtab.h
gold/target.h

index bbd4eb3..fcbfed4 100644 (file)
@@ -1,3 +1,23 @@
+2013-03-11  Alan Modra  <amodra@gmail.com>
+
+       * 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.
+
 2013-03-08  Ian Lance Taylor  <iant@google.com>
 
        * options.cc (General_options::string_to_object_format): Accept
index 30d623e..d26dc2e 100644 (file)
@@ -653,10 +653,6 @@ queue_middle_tasks(const General_options& 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 1eb2cc0..faa8f71 100644 (file)
@@ -317,6 +317,10 @@ Layout::Relaxation_debug_check::verify_sections(
 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 3a4f9f8..3434303 100644 (file)
@@ -517,15 +517,16 @@ Sized_relobj_file<size, big_endian>::check_eh_frame_flags(
 
 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>::find_shdr(
       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>::find_shdr(
            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>::find_eh_frame(
 
   while (1)
     {
-      s = this->find_shdr(pshdrs, ".eh_frame", names, names_size, s);
+      s = this->template 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 01c3c6f..a2baecc 100644 (file)
@@ -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_relobj<size, big_endian>
   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 a978e7a..b8ff86d 100644 (file)
@@ -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.
+  Address
+  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_target<size, big_endian>
   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,9 @@ Powerpc_relobj<size, big_endian>::do_find_special_sections(
   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 = this->template 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 +1742,105 @@ Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
     }
 }
 
+// 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 = this->template 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 +1913,8 @@ Target_powerpc<size, big_endian>::do_make_elf_object(
     }
   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 +5788,42 @@ Target_powerpc<size, big_endian>::do_gc_mark_symbol(
     }
 }
 
+// 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>
index 8fadd1d..1fad11e 100644 (file)
@@ -3175,9 +3175,11 @@ Symbol_table::linenos_from_loc(const Task* task,
   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 f26d4b9..040be95 100644 (file)
@@ -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 d75711e..6523c04 100644 (file)
@@ -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.