* target-reloc.h (scan_relocs): Call scan.local for relocs
authorAlan Modra <amodra@gmail.com>
Wed, 12 Sep 2012 22:43:54 +0000 (22:43 +0000)
committerAlan Modra <amodra@gmail.com>
Wed, 12 Sep 2012 22:43:54 +0000 (22:43 +0000)
against symbols in discarded sections.  Pass is_discarded
param.
* arm.cc, * i386.cc, * sparc.cc, * x86_64.cc (Target_*::Scan::local):
Add is_discarded param.
* powerpc (Target_powerpc::Scan::local): Likewise.  Use
is_discarded to flag opd entry as discarded.  Don't emit dyn
relocs on such entries.
(Target_powerpc::Scan::global): Similarly detect and handle
such opd entries.
(Powerpc_relobj): Replace opd_ent_shndx_ and opd_ent_off_ with
opd_ent_.  Update all uses.
(Powerpc_relobj::get_opd_discard, set_opd_discard): New functions.
(Target_powerpc::relocate_section): Zero out discarded opd
entry relocs.

gold/ChangeLog
gold/arm.cc
gold/i386.cc
gold/powerpc.cc
gold/sparc.cc
gold/target-reloc.h
gold/x86_64.cc

index 9ad5a88d8c22cc76defa0bbc42acc89d5b7be618..153a87d575ec23744d392928ea3c4532ef6f8259 100644 (file)
@@ -1,3 +1,21 @@
+2012-09-13  Alan Modra  <amodra@gmail.com>
+
+       * target-reloc.h (scan_relocs): Call scan.local for relocs
+       against symbols in discarded sections.  Pass is_discarded
+       param.
+       * arm.cc, * i386.cc, * sparc.cc, * x86_64.cc (Target_*::Scan::local):
+       Add is_discarded param.
+       * powerpc (Target_powerpc::Scan::local): Likewise.  Use
+       is_discarded to flag opd entry as discarded.  Don't emit dyn
+       relocs on such entries.
+       (Target_powerpc::Scan::global): Similarly detect and handle
+       such opd entries.
+       (Powerpc_relobj): Replace opd_ent_shndx_ and opd_ent_off_ with
+       opd_ent_.  Update all uses.
+       (Powerpc_relobj::get_opd_discard, set_opd_discard): New functions.
+       (Target_powerpc::relocate_section): Zero out discarded opd
+       entry relocs.
+
 2012-09-12  Ian Lance Taylor  <iant@google.com>
 
        PR gold/14570
index 351c6fe1484cd75825399bd9e1520534cc22cd2c..d874ce02795e7d4a2cdf1742cdd6d29c26af5d15 100644 (file)
@@ -2551,7 +2551,8 @@ class Target_arm : public Sized_target<32, big_endian>
          unsigned int data_shndx,
          Output_section* output_section,
          const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
-         const elfcpp::Sym<32, big_endian>& lsym);
+         const elfcpp::Sym<32, big_endian>& lsym,
+         bool is_discarded);
 
     inline void
     global(Symbol_table* symtab, Layout* layout, Target_arm* target,
@@ -7857,8 +7858,12 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
                                    Output_section* output_section,
                                    const elfcpp::Rel<32, big_endian>& reloc,
                                    unsigned int r_type,
-                                   const elfcpp::Sym<32, big_endian>& lsym)
+                                   const elfcpp::Sym<32, big_endian>& lsym,
+                                   bool is_discarded)
 {
+  if (is_discarded)
+    return;
+
   r_type = get_real_reloc_type(r_type);
   switch (r_type)
     {
index 91611a13396243b5f869ecdc89bd25a26413c155..47779d0e8cd4532c8934b113cfc006f950c00d42 100644 (file)
@@ -538,7 +538,8 @@ class Target_i386 : public Sized_target<32, false>
          unsigned int data_shndx,
          Output_section* output_section,
          const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
-         const elfcpp::Sym<32, false>& lsym);
+         const elfcpp::Sym<32, false>& lsym,
+         bool is_discarded);
 
     inline void
     global(Symbol_table* symtab, Layout* layout, Target_i386* target,
@@ -1702,15 +1703,19 @@ Target_i386::Scan::reloc_needs_plt_for_ifunc(
 
 inline void
 Target_i386::Scan::local(Symbol_table* symtab,
-                               Layout* layout,
-                               Target_i386* target,
-                               Sized_relobj_file<32, false>* object,
-                               unsigned int data_shndx,
-                               Output_section* output_section,
-                               const elfcpp::Rel<32, false>& reloc,
-                               unsigned int r_type,
-                               const elfcpp::Sym<32, false>& lsym)
+                        Layout* layout,
+                        Target_i386* target,
+                        Sized_relobj_file<32, false>* object,
+                        unsigned int data_shndx,
+                        Output_section* output_section,
+                        const elfcpp::Rel<32, false>& reloc,
+                        unsigned int r_type,
+                        const elfcpp::Sym<32, false>& lsym,
+                        bool is_discarded)
 {
+  if (is_discarded)
+    return;
+
   // A local STT_GNU_IFUNC symbol may require a PLT entry.
   if (lsym.get_st_type() == elfcpp::STT_GNU_IFUNC
       && this->reloc_needs_plt_for_ifunc(object, r_type))
index 31f5ddf752f760a583f03679d2e3ca3416d4ba2e..62e850f3383ce813c34537fe91f718a45a64ea65 100644 (file)
@@ -65,8 +65,7 @@ public:
   Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
                 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
-      special_(0), opd_ent_shndx_(), opd_ent_off_(), access_from_map_(),
-      opd_valid_(false)
+      special_(0), opd_valid_(false), opd_ent_(), access_from_map_()
   { }
 
   ~Powerpc_relobj()
@@ -97,8 +96,7 @@ public:
   init_opd(size_t opd_size)
   {
     size_t count = this->opd_ent_ndx(opd_size);
-    this->opd_ent_shndx_.resize(count);
-    this->opd_ent_off_.reserve(count);
+    this->opd_ent_.resize(count);
   }
 
   // Return section and offset of function entry for .opd + R_OFF.
@@ -106,11 +104,11 @@ public:
   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_shndx_.size());
-    gold_assert(this->opd_ent_shndx_[ndx] != 0);
+    gold_assert(ndx < this->opd_ent_.size());
+    gold_assert(this->opd_ent_[ndx].shndx != 0);
     if (value != NULL)
-      *value = this->opd_ent_off_[ndx];
-    return this->opd_ent_shndx_[ndx];
+      *value = this->opd_ent_[ndx].off;
+    return this->opd_ent_[ndx].shndx;
   }
 
   // Set section and offset of function entry for .opd + R_OFF.
@@ -118,9 +116,27 @@ public:
   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_shndx_.size());
-    this->opd_ent_shndx_[ndx] = shndx;
-    this->opd_ent_off_[ndx] = value;
+    gold_assert(ndx < this->opd_ent_.size());
+    this->opd_ent_[ndx].shndx = shndx;
+    this->opd_ent_[ndx].off = value;
+  }
+
+  // Return discard flag for .opd + R_OFF.
+  bool
+  get_opd_discard(Address r_off) const
+  {
+    size_t ndx = this->opd_ent_ndx(r_off);
+    gold_assert(ndx < this->opd_ent_.size());
+    return this->opd_ent_[ndx].discard;
+  }
+
+  // Set discard flag for .opd + R_OFF.
+  void
+  set_opd_discard(Address r_off)
+  {
+    size_t ndx = this->opd_ent_ndx(r_off);
+    gold_assert(ndx < this->opd_ent_.size());
+    this->opd_ent_[ndx].discard = true;
   }
 
   Access_from*
@@ -165,38 +181,47 @@ public:
   { return 0x8000; }
 
 private:
-  // Return index into opd_ent_shndx or opd_ent_off array for .opd entry
-  // at OFF.  .opd entries are 24 bytes long, but they can be spaced
-  // 16 bytes apart when the language doesn't use the last 8-byte
-  // word, the environment pointer.  Thus dividing the entry section
-  // offset by 16 will give an index into opd_ent_shndx_ and
-  // opd_ent_off_ that works for either layout of .opd.  (It leaves
-  // some elements of the vectors unused when .opd entries are spaced
-  // 24 bytes apart, but we don't know the spacing until relocations
-  // are processed, and in any case it is possible for an object to
-  // have some entries spaced 16 bytes apart and others 24 bytes apart.)
+  struct Opd_ent
+  {
+    unsigned int shndx;
+    bool discard;
+    Offset off;
+  };
+
+  // Return index into opd_ent_ array for .opd entry at OFF.
+  // .opd entries are 24 bytes long, but they can be spaced 16 bytes
+  // apart when the language doesn't use the last 8-byte word, the
+  // environment pointer.  Thus dividing the entry section offset by
+  // 16 will give an index into opd_ent_ that works for either layout
+  // of .opd.  (It leaves some elements of the vector unused when .opd
+  // entries are spaced 24 bytes apart, but we don't know the spacing
+  // until relocations are processed, and in any case it is possible
+  // for an object to have some entries spaced 16 bytes apart and
+  // others 24 bytes apart.)
   size_t
   opd_ent_ndx(size_t off) const
   { return off >> 4;}
 
   // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
   unsigned int special_;
+
+  // Set at the start of gc_process_relocs, when we know opd_ent_
+  // vector is valid.  The flag could be made atomic and set in
+  // do_read_relocs with memory_order_release and then tested with
+  // memory_order_acquire, potentially resulting in fewer entries in
+  // access_from_map_.
+  bool opd_valid_;
+
   // The first 8-byte word of an OPD entry gives the address of the
   // entry point of the function.  Relocatable object files have a
-  // relocation on this word.  The following two vectors record the
+  // relocation on this word.  The following vector records the
   // section and offset specified by these relocations.
-  std::vector<unsigned int> opd_ent_shndx_;
-  std::vector<Offset> opd_ent_off_;
+  std::vector<Opd_ent> opd_ent_;
+
   // References made to this object's .opd section when running
-  // gc_process_relocs for another object, before the opd_ent vectors
-  // are valid for this object.
+  // gc_process_relocs for another object, before the opd_ent_ vector
+  // is valid for this object.
   Access_from access_from_map_;
-  // Set at the start of gc_process_relocs, when we know opd_ent
-  // vectors are valid.  The flag could be made atomic and set in
-  // do_read_relocs with memory_order_release and then tested with
-  // memory_order_acquire, potentially resulting in fewer entries in
-  // access_from_map_.
-  bool opd_valid_;
 };
 
 template<int size, bool big_endian>
@@ -413,6 +438,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
   class Scan
   {
   public:
+    typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
     Scan()
       : issued_non_pic_error_(false)
     { }
@@ -426,7 +453,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
          unsigned int data_shndx,
          Output_section* output_section,
          const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
-         const elfcpp::Sym<size, big_endian>& lsym);
+         const elfcpp::Sym<size, big_endian>& lsym,
+         bool is_discarded);
 
     inline void
     global(Symbol_table* symtab, Layout* layout, Target_powerpc* target,
@@ -2361,11 +2389,21 @@ Target_powerpc<size, big_endian>::Scan::local(
     Output_section* output_section,
     const elfcpp::Rela<size, big_endian>& reloc,
     unsigned int r_type,
-    const elfcpp::Sym<size, big_endian>& /* lsym */)
+    const elfcpp::Sym<size, big_endian>& /* lsym */,
+    bool is_discarded)
 {
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(object);
 
+  if (is_discarded)
+    {
+      if (size == 64
+         && data_shndx == ppc_object->opd_shndx()
+         && r_type == elfcpp::R_PPC64_ADDR64)
+       ppc_object->set_opd_discard(reloc.get_r_offset());
+      return;
+    }
+
   switch (r_type)
     {
     case elfcpp::R_POWERPC_NONE:
@@ -2382,13 +2420,19 @@ Target_powerpc<size, big_endian>::Scan::local(
          = target->got_section(symtab, layout);
        if (parameters->options().output_is_position_independent())
          {
+           Address off = reloc.get_r_offset();
+           if (size == 64
+               && data_shndx == ppc_object->opd_shndx()
+               && ppc_object->get_opd_discard(off - 8))
+             break;
+
            Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+           Powerpc_relobj<size, big_endian>* symobj = ppc_object;
            rela_dyn->add_output_section_relative(got->output_section(),
                                                  elfcpp::R_POWERPC_RELATIVE,
                                                  output_section,
-                                                 object, data_shndx,
-                                                 reloc.get_r_offset(),
-                                                 ppc_object->toc_base_offset());
+                                                 object, data_shndx, off,
+                                                 symobj->toc_base_offset());
          }
       }
       break;
@@ -2658,6 +2702,12 @@ Target_powerpc<size, big_endian>::Scan::global(
          = target->got_section(symtab, layout);
        if (parameters->options().output_is_position_independent())
          {
+           Address off = reloc.get_r_offset();
+           if (size == 64
+               && data_shndx == ppc_object->opd_shndx()
+               && ppc_object->get_opd_discard(off - 8))
+             break;
+
            Reloc_section* rela_dyn = target->rela_dyn_section(layout);
            Powerpc_relobj<size, big_endian>* symobj = ppc_object;
            if (data_shndx != ppc_object->opd_shndx())
@@ -2666,14 +2716,22 @@ Target_powerpc<size, big_endian>::Scan::global(
            rela_dyn->add_output_section_relative(got->output_section(),
                                                  elfcpp::R_POWERPC_RELATIVE,
                                                  output_section,
-                                                 object, data_shndx,
-                                                 reloc.get_r_offset(),
+                                                 object, data_shndx, off,
                                                  symobj->toc_base_offset());
          }
       }
       break;
 
     case elfcpp::R_PPC64_ADDR64:
+      if (size == 64
+         && data_shndx == ppc_object->opd_shndx()
+         && (gsym->is_defined_in_discarded_section()
+             || gsym->object() != object))
+       {
+         ppc_object->set_opd_discard(reloc.get_r_offset());
+         break;
+       }
+      // Fall thru
     case elfcpp::R_PPC64_UADDR64:
     case elfcpp::R_POWERPC_ADDR32:
     case elfcpp::R_POWERPC_UADDR32:
@@ -4038,6 +4096,43 @@ Target_powerpc<size, big_endian>::relocate_section(
 
   gold_assert(sh_type == elfcpp::SHT_RELA);
 
+  unsigned char *opd_rel = NULL;
+  Powerpc_relobj<size, big_endian>* const object
+    = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
+  if (size == 64
+      && relinfo->data_shndx == object->opd_shndx())
+    {
+      // Rewrite opd relocs, omitting those for discarded sections
+      // to silence gold::relocate_section errors.
+      const int reloc_size
+       = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+      opd_rel = new unsigned char[reloc_count * reloc_size];
+      const unsigned char* rrel = prelocs;
+      unsigned char* wrel = opd_rel;
+
+      for (size_t i = 0;
+          i < reloc_count;
+          ++i, rrel += reloc_size, wrel += reloc_size)
+       {
+         typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
+           reloc(rrel);
+         typename elfcpp::Elf_types<size>::Elf_WXword r_info
+           = reloc.get_r_info();
+         unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+         Address r_off = reloc.get_r_offset();
+         if (r_type == elfcpp::R_PPC64_TOC)
+           r_off -= 8;
+         bool is_discarded = object->get_opd_discard(r_off);
+
+         // Reloc number is reported in some errors, so keep all relocs.
+         if (is_discarded)
+           memset(wrel, 0, reloc_size);
+         else
+           memcpy(wrel, rrel, reloc_size);
+       }
+      prelocs = opd_rel;
+    }
+
   gold::relocate_section<size, big_endian, Powerpc, elfcpp::SHT_RELA,
                         Powerpc_relocate>(
     relinfo,
@@ -4050,6 +4145,9 @@ Target_powerpc<size, big_endian>::relocate_section(
     address,
     view_size,
     reloc_symbol_changes);
+
+  if (opd_rel != NULL)
+    delete[] opd_rel;
 }
 
 class Powerpc_scan_relocatable_reloc
index 04a88bf21bea5939ce1774f50aacda0cf844b3ee..71dd3d53c30a82faad335c4fccd6c1593730fc06 100644 (file)
@@ -237,7 +237,8 @@ class Target_sparc : public Sized_target<size, big_endian>
          unsigned int data_shndx,
          Output_section* output_section,
          const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
-         const elfcpp::Sym<size, big_endian>& lsym);
+         const elfcpp::Sym<size, big_endian>& lsym,
+         bool is_discarded);
 
     inline void
     global(Symbol_table* symtab, Layout* layout, Target_sparc* target,
@@ -2240,8 +2241,12 @@ Target_sparc<size, big_endian>::Scan::local(
                        Output_section* output_section,
                        const elfcpp::Rela<size, big_endian>& reloc,
                        unsigned int r_type,
-                       const elfcpp::Sym<size, big_endian>& lsym)
+                       const elfcpp::Sym<size, big_endian>& lsym,
+                       bool is_discarded)
 {
+  if (is_discarded)
+    return;
+
   bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
   unsigned int orig_r_type = r_type;
   r_type &= 0xff;
index 96f2614f0087b7a9e73c8667ac12bd2bc2d8facc..5e6dba7724861d8614806f3a93a2c22d13a79e28 100644 (file)
@@ -81,30 +81,25 @@ scan_relocs(
          unsigned int shndx = lsym.get_st_shndx();
          bool is_ordinary;
          shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
-         if (is_ordinary
-             && shndx != elfcpp::SHN_UNDEF
-             && !object->is_section_included(shndx)
-              && !symtab->is_section_folded(object, shndx))
-           {
-             // RELOC is a relocation against a local symbol in a
-             // section we are discarding.  We can ignore this
-             // relocation.  It will eventually become a reloc
-             // against the value zero.
-             //
-             // FIXME: We should issue a warning if this is an
-             // allocated section; is this the best place to do it?
-             // 
-             // FIXME: The old GNU linker would in some cases look
-             // for the linkonce section which caused this section to
-             // be discarded, and, if the other section was the same
-             // size, change the reloc to refer to the other section.
-             // That seems risky and weird to me, and I don't know of
-             // any case where it is actually required.
-
-             continue;
-           }
+         // If RELOC is a relocation against a local symbol in a
+         // section we are discarding then we can ignore it.  It will
+         // eventually become a reloc against the value zero.
+         //
+         // FIXME: We should issue a warning if this is an
+         // allocated section; is this the best place to do it?
+         // 
+         // FIXME: The old GNU linker would in some cases look
+         // for the linkonce section which caused this section to
+         // be discarded, and, if the other section was the same
+         // size, change the reloc to refer to the other section.
+         // That seems risky and weird to me, and I don't know of
+         // any case where it is actually required.
+         bool is_discarded = (is_ordinary
+                              && shndx != elfcpp::SHN_UNDEF
+                              && !object->is_section_included(shndx)
+                              && !symtab->is_section_folded(object, shndx));
          scan.local(symtab, layout, target, object, data_shndx,
-                    output_section, reloc, r_type, lsym);
+                    output_section, reloc, r_type, lsym, is_discarded);
        }
       else
        {
index 1712beb21771a04da6e13018a171f00d29c04721..5914160b2ad000650fbcdf2efac8534564795ad9 100644 (file)
@@ -676,7 +676,8 @@ class Target_x86_64 : public Sized_target<size, false>
          unsigned int data_shndx,
          Output_section* output_section,
          const elfcpp::Rela<size, false>& reloc, unsigned int r_type,
-         const elfcpp::Sym<size, false>& lsym);
+         const elfcpp::Sym<size, false>& lsym,
+         bool is_discarded);
 
     inline void
     global(Symbol_table* symtab, Layout* layout, Target_x86_64* target,
@@ -2270,8 +2271,12 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
                                 Output_section* output_section,
                                 const elfcpp::Rela<size, false>& reloc,
                                 unsigned int r_type,
-                                const elfcpp::Sym<size, false>& lsym)
+                                const elfcpp::Sym<size, false>& lsym,
+                                bool is_discarded)
 {
+  if (is_discarded)
+    return;
+
   // A local STT_GNU_IFUNC symbol may require a PLT entry.
   bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
   if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))