* object.h (Sized_relobj_file::adjust_local_symbol,
authorAlan Modra <amodra@gmail.com>
Tue, 25 Sep 2012 00:59:25 +0000 (00:59 +0000)
committerAlan Modra <amodra@gmail.com>
Tue, 25 Sep 2012 00:59:25 +0000 (00:59 +0000)
do_adjust_local_symbol): New functions.
* object.cc (Sized_relobj_file::do_count_local_symbols): Use the above.
* powerpc.cc (Powerpc_relobj::do_adjust_local_symbol): New function.
(Powerpc_relobj::scan_opd_relocs): Warn on unexpected opd relocs
and irregular opd entry spacing.
(Powerpc_relobj::do_read_relocs): Add opd size checks.
(Global_symbol_visitor_opd): New functor.
(Target_powerpc::do_finalize_sections): Omit global symbols defined
on deleted opd entries.

gold/ChangeLog
gold/object.cc
gold/object.h
gold/powerpc.cc

index 64290044f840aa07a0e383e0ce93b4ce4ca68a5e..b7226b10b20d2d9031167498031c68eb7a71438c 100644 (file)
@@ -1,3 +1,16 @@
+2012-09-25  Alan Modra  <amodra@gmail.com>
+
+       * object.h (Sized_relobj_file::adjust_local_symbol,
+       do_adjust_local_symbol): New functions.
+       * object.cc (Sized_relobj_file::do_count_local_symbols): Use the above.
+       * powerpc.cc (Powerpc_relobj::do_adjust_local_symbol): New function.
+       (Powerpc_relobj::scan_opd_relocs): Warn on unexpected opd relocs
+       and irregular opd entry spacing.
+       (Powerpc_relobj::do_read_relocs): Add opd size checks.
+       (Global_symbol_visitor_opd): New functor.
+       (Target_powerpc::do_finalize_sections): Omit global symbols defined
+       on deleted opd entries.
+
 2012-09-15  Jiong Wang  <jiwang@tilera.com>
 
        * tilegx.cc: New file.
index 6ee10f522d74899cc80040baf6a7ce459c166831..3492f466d9fe30ca702f03aac65dfdbd449a1491 100644 (file)
@@ -2110,7 +2110,8 @@ Sized_relobj_file<size, big_endian>::do_count_local_symbols(Stringpool* pool,
          continue;
        }
 
-      if (sym.get_st_type() == elfcpp::STT_SECTION)
+      if (sym.get_st_type() == elfcpp::STT_SECTION
+         || !this->adjust_local_symbol(&lv))
        {
          lv.set_no_output_symtab_entry();
          gold_assert(!lv.needs_output_dynsym_entry());
index a507204d2b782ba6baac43d91e81841d11039c8c..522b63f44b67865a45a798ed344e994a378aad13 100644 (file)
@@ -2112,6 +2112,12 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
   void
   set_local_plt_offset(unsigned int symndx, unsigned int plt_offset);
 
+  // Adjust this local symbol value.  Return false if the symbol
+  // should be discarded from the output file.
+  bool
+  adjust_local_symbol(Symbol_value<size>* lv) const
+  { return this->do_adjust_local_symbol(lv); }
+
   // Return the name of the symbol that spans the given offset in the
   // specified section in this object.  This is used only for error
   // messages and is not particularly efficient.
@@ -2381,6 +2387,12 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
                       const unsigned char* pshdrs, Output_file* of,
                       Views* pviews);
 
+  // Adjust this local symbol value.  Return false if the symbol
+  // should be discarded from the output file.
+  virtual bool
+  do_adjust_local_symbol(Symbol_value<size>*) const
+  { return true; }
+
   // Allow a child to set output local symbol count.
   void
   set_output_local_symbol_count(unsigned int value)
index 62e850f3383ce813c34537fe91f718a45a64ea65..2392abeaa79bf2a1ffc7a313ad45fa6098b25b1b 100644 (file)
@@ -174,6 +174,22 @@ public:
   bool
   do_find_special_sections(Read_symbols_data* sd);
 
+  // Adjust this local symbol value.  Return false if the symbol
+  // should be discarded from the output file.
+  bool
+  do_adjust_local_symbol(Symbol_value<size>* lv) const
+  {
+    if (size == 64 && this->opd_shndx() != 0)
+      {
+       bool is_ordinary;
+       if (lv->input_shndx(&is_ordinary) != this->opd_shndx())
+         return true;
+       if (this->get_opd_discard(lv->input_value()))
+         return false;
+      }
+    return true;
+  }
+
   // Return offset in output GOT section that this object will use
   // as a TOC pointer.  Won't be just a constant with multi-toc support.
   Address
@@ -1183,6 +1199,9 @@ Powerpc_relobj<size, big_endian>::scan_opd_relocs(
       const int reloc_size
        = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
       const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+      Address expected_off = 0;
+      bool regular = true;
+      unsigned int opd_ent_size = 0;
 
       for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
        {
@@ -1209,8 +1228,37 @@ Powerpc_relobj<size, big_endian>::scan_opd_relocs(
                                                       &is_ordinary);
              this->set_opd_ent(reloc.get_r_offset(), shndx,
                                value + reloc.get_r_addend());
+             if (i == 2)
+               {
+                 expected_off = reloc.get_r_offset();
+                 opd_ent_size = expected_off;
+               }
+             else if (expected_off != reloc.get_r_offset())
+               regular = false;
+             expected_off += opd_ent_size;
+           }
+         else if (r_type == elfcpp::R_PPC64_TOC)
+           {
+             if (expected_off - opd_ent_size + 8 != reloc.get_r_offset())
+               regular = false;
+           }
+         else
+           {
+             gold_warning(_("%s: unexpected reloc type %u in .opd section"),
+                          this->name().c_str(), r_type);
+             regular = false;
            }
        }
+      if (reloc_count <= 2)
+       opd_ent_size = this->section_size(this->opd_shndx());
+      if (opd_ent_size != 24 && opd_ent_size != 16)
+       regular = false;
+      if (!regular)
+       {
+         gold_warning(_("%s: .opd is not a regular array of opd entries"),
+                      this->name().c_str());
+         opd_ent_size = 0;
+       }
     }
 }
 
@@ -1227,9 +1275,14 @@ Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
        {
          if (p->data_shndx == this->opd_shndx())
            {
-             this->init_opd(this->section_size(this->opd_shndx()));
-             this->scan_opd_relocs(p->reloc_count, p->contents->data(),
-                                   rd->local_symbols->data());
+             uint64_t opd_size = this->section_size(this->opd_shndx());
+             gold_assert(opd_size == static_cast<size_t>(opd_size));
+             if (opd_size != 0)
+               {
+                 this->init_opd(opd_size);
+                 this->scan_opd_relocs(p->reloc_count, p->contents->data(),
+                                       rd->local_symbols->data());
+               }
              break;
            }
        }
@@ -3204,6 +3257,38 @@ Target_powerpc<size, big_endian>::scan_relocs(
     plocal_symbols);
 }
 
+// Functor class for processing the global symbol table.
+// Removes symbols defined on discarded opd entries.
+
+template<bool big_endian>
+class Global_symbol_visitor_opd
+{
+ public:
+  Global_symbol_visitor_opd()
+  { }
+
+  void
+  operator()(Sized_symbol<64>* sym)
+  {
+    if (sym->has_symtab_index()
+       || sym->source() != Symbol::FROM_OBJECT
+       || !sym->in_real_elf())
+      return;
+
+    Powerpc_relobj<64, big_endian>* symobj
+      = static_cast<Powerpc_relobj<64, big_endian>*>(sym->object());
+    if (symobj->is_dynamic()
+       || symobj->opd_shndx() == 0)
+      return;
+
+    bool is_ordinary;
+    unsigned int shndx = sym->shndx(&is_ordinary);
+    if (shndx == symobj->opd_shndx()
+       && symobj->get_opd_discard(sym->value()))
+      sym->set_symtab_index(-1U);
+  }
+};
+
 // Finalize the sections.
 
 template<int size, bool big_endian>
@@ -3211,8 +3296,14 @@ void
 Target_powerpc<size, big_endian>::do_finalize_sections(
     Layout* layout,
     const Input_objects*,
-    Symbol_table*)
+    Symbol_table* symtab)
 {
+  if (size == 64)
+    {
+      typedef Global_symbol_visitor_opd<big_endian> Symbol_visitor;
+      symtab->for_all_symbols<64, Symbol_visitor>(Symbol_visitor());
+    }
+
   // Fill in some more dynamic tags.
   const Reloc_section* rel_plt = (this->plt_ == NULL
                                  ? NULL