[GOLD] Power10 segv due to wild r2
authorAlan Modra <amodra@gmail.com>
Fri, 9 Oct 2020 06:26:33 +0000 (16:56 +1030)
committerAlan Modra <amodra@gmail.com>
Fri, 9 Oct 2020 12:54:17 +0000 (23:24 +1030)
Calling non-pcrel functions from pcrel code requires a stub to set up
r2.  Gold created the stub, but an "optimisation" made the stub jump
to the function local entry, ie. r2 was not initialised.

This patch fixes that long branch stub problem, and another that might
occur for plt call stubs to local functions.

bfd/
* elf64-ppc.c (write_plt_relocs_for_local_syms): Don't do local
entry offset optimisation.
gold/
* powerpc.cc (Powerpc_relobj::do_relocate_sections): Don't do
local entry offset optimisation for lplt_section.
(Target_powerpc::Branch_info::make_stub): Don't add local
entry offset to long branch dest passed to
add_long_branch_entry.  Do pass st_other bits.
(Stub_table::Branch_stub_ent): Add "other_" field.
(Stub_table::add_long_branch_entry): Add "other" param, and
save.
(Stub_table::branch_stub_size): Adjust long branch offset.
(Stub_table::do_write): Likewise.
(Target_powerpc::Relocate::relocate): Likewise.

bfd/ChangeLog
bfd/elf64-ppc.c
gold/ChangeLog
gold/powerpc.cc

index 9650b3d827c3453ca37559ed2ed52eafb7970164..3ff3b1e23959c1982b70fadb6a99d2da080ec3da 100644 (file)
@@ -1,3 +1,8 @@
+2020-10-09  Alan Modra  <amodra@gmail.com>
+
+       * elf64-ppc.c (write_plt_relocs_for_local_syms): Don't do local
+       entry offset optimisation.
+
 2020-10-09  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR gas/26703
index fa0c7f96e7916b2e8e40f7ff6c8fb8ce3910f24b..c30215b55070ccf4ccdadd885169f3d823ac96d9 100644 (file)
@@ -14259,8 +14259,6 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
                }
 
              val = sym->st_value + ent->addend;
-             if (ELF_ST_TYPE (sym->st_info) != STT_GNU_IFUNC)
-               val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other);
              if (sym_sec != NULL && sym_sec->output_section != NULL)
                val += sym_sec->output_offset + sym_sec->output_section->vma;
 
index 0c00d96f4eb7408b1f76a7cc53cc7ad5872a0c10..25248a6267a0624b574ae570ac8a4a8d5cf8dfad 100644 (file)
@@ -1,3 +1,17 @@
+2020-10-09  Alan Modra  <amodra@gmail.com>
+
+       * powerpc.cc (Powerpc_relobj::do_relocate_sections): Don't do
+       local entry offset optimisation for lplt_section.
+       (Target_powerpc::Branch_info::make_stub): Don't add local
+       entry offset to long branch dest passed to
+       add_long_branch_entry.  Do pass st_other bits.
+       (Stub_table::Branch_stub_ent): Add "other_" field.
+       (Stub_table::add_long_branch_entry): Add "other" param, and
+       save.
+       (Stub_table::branch_stub_size): Adjust long branch offset.
+       (Stub_table::do_write): Likewise.
+       (Target_powerpc::Relocate::relocate): Likewise.
+
 2020-10-09  Alan Modra  <amodra@gmail.com>
 
        * powerpc.cc (is_got_reloc): New function.
index f9eb4f98bdace607373e0ca3ad80f9e9cb5bbf2c..476fb4e588d85f383b4d68bd9746d9ebb6be4bba 100644 (file)
@@ -2767,8 +2767,6 @@ Powerpc_relobj<size, big_endian>::do_relocate_sections(
            if (this->local_has_plt_offset(i))
              {
                Address value = this->local_symbol_value(i, 0);
-               if (size == 64)
-                 value += ppc64_local_entry_offset(i);
                size_t off = this->local_plt_offset(i);
                elfcpp::Swap<size, big_endian>::writeval(oview + off, value);
                modified = true;
@@ -3539,6 +3537,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
       from += (this->object_->output_section(this->shndx_)->address()
               + this->offset_);
       Address to;
+      unsigned int other;
       if (gsym != NULL)
        {
          switch (gsym->source())
@@ -3566,8 +3565,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
          to = symtab->compute_final_value<size>(gsym, &status);
          if (status != Symbol_table::CFVS_OK)
            return true;
-         if (size == 64)
-           to += this->object_->ppc64_local_entry_offset(gsym);
+         other = gsym->nonvis() >> 3;
        }
       else
        {
@@ -3584,8 +3582,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
              || !symval.has_output_value())
            return true;
          to = symval.value(this->object_, 0);
-         if (size == 64)
-           to += this->object_->ppc64_local_entry_offset(this->r_sym_);
+         other = this->object_->st_other(this->r_sym_) >> 5;
        }
       if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
        to += this->addend_;
@@ -3598,7 +3595,11 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
                                         &to, &dest_shndx))
            return true;
        }
-      Address delta = to - from;
+      unsigned int local_ent = 0;
+      if (size == 64
+         && this->r_type_ != elfcpp::R_PPC64_REL24_NOTOC)
+       local_ent = elfcpp::ppc64_decode_local_entry(other);
+      Address delta = to + local_ent - from;
       if (delta + max_branch_offset >= 2 * max_branch_offset
          || (size == 64
              && this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
@@ -3620,7 +3621,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
                           && gsym->output_data() == target->savres_section());
          ok = stub_table->add_long_branch_entry(this->object_,
                                                 this->r_type_,
-                                                from, to, save_res);
+                                                from, to, other, save_res);
        }
     }
   if (!ok)
@@ -4688,7 +4689,7 @@ class Stub_table : public Output_relaxed_input_section
   {
     Branch_stub_ent(unsigned int off, bool notoc, bool save_res)
       : off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res),
-       tocoff_(0)
+       other_(0), tocoff_(0)
     { }
 
     unsigned int off_;
@@ -4696,6 +4697,7 @@ class Stub_table : public Output_relaxed_input_section
     unsigned int notoc_ : 1;
     unsigned int toc_ : 1;
     unsigned int save_res_ : 1;
+    unsigned int other_ : 3;
     unsigned int tocoff_ : 8;
   };
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
@@ -4762,7 +4764,7 @@ class Stub_table : public Output_relaxed_input_section
   // Add a long branch stub.
   bool
   add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
-                       unsigned int, Address, Address, bool);
+                       unsigned int, Address, Address, unsigned int, bool);
 
   const Branch_stub_ent*
   find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
@@ -5282,6 +5284,7 @@ Stub_table<size, big_endian>::add_long_branch_entry(
     unsigned int r_type,
     Address from,
     Address to,
+    unsigned int other,
     bool save_res)
 {
   Branch_stub_key key(object, to);
@@ -5301,6 +5304,8 @@ Stub_table<size, big_endian>::add_long_branch_entry(
        this->need_resize_ = true;
       p.first->second.toc_ = true;
     }
+  if (p.first->second.other_ == 0)
+    p.first->second.other_ = other;
   gold_assert(save_res == p.first->second.save_res_);
   if (p.second || (this->resizing_ && !p.first->second.iter_))
     {
@@ -6198,6 +6203,7 @@ Stub_table<size, big_endian>::branch_stub_size(
        }
     }
 
+  off += elfcpp::ppc64_decode_local_entry(p->second.other_);
   if (off + (1 << 25) < 2 << 25)
     return bytes + 4;
   if (!this->targ_->power10_stubs()
@@ -6377,6 +6383,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                }
              if (bs->second.toc_)
                {
+                 delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
                  if (delta + (1 << 25) >= 2 << 25)
                    {
                      Address brlt_addr
@@ -6410,6 +6417,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
            }
          else
            {
+             if (!bs->second.notoc_)
+               delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
              if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
                {
                  unsigned char* startp = p;
@@ -6640,6 +6649,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
          p = oview + off;
          Address loc = this->stub_address() + off;
          Address delta = bs->first.dest_ - loc;
+         if (!bs->second.notoc_)
+           delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
          if (bs->second.notoc_)
            {
              unsigned char* startp = p;
@@ -11039,14 +11050,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                || r_type == elfcpp::R_POWERPC_PLT16_HA)))
        addend = rela.get_r_addend();
       value = psymval->value(object, addend);
+      unsigned int local_ent = 0;
       if (size == 64 && is_branch_reloc<size>(r_type))
        {
          if (target->abiversion() >= 2)
            {
              if (gsym != NULL)
-               value += object->ppc64_local_entry_offset(gsym);
+               local_ent = object->ppc64_local_entry_offset(gsym);
              else
-               value += object->ppc64_local_entry_offset(r_sym);
+               local_ent = object->ppc64_local_entry_offset(r_sym);
            }
          else
            {
@@ -11055,9 +11067,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                                        &value, &dest_shndx);
            }
        }
-      Address max_branch_offset = max_branch_delta<size>(r_type);
-      if (max_branch_offset != 0
-         && (value - address + max_branch_offset >= 2 * max_branch_offset
+      Address max_branch = max_branch_delta<size>(r_type);
+      if (max_branch != 0
+         && (value + local_ent - address + max_branch >= 2 * max_branch
              || (size == 64
                  && r_type == elfcpp::R_PPC64_REL24_NOTOC
                  && (gsym != NULL
@@ -11082,12 +11094,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                               + ent->off_);
                      if (size == 64
                          && r_type != elfcpp::R_PPC64_REL24_NOTOC)
-                       value += ent->tocoff_;
+                       value += (elfcpp::ppc64_decode_local_entry(ent->other_)
+                                 + ent->tocoff_);
                    }
                  has_stub_value = true;
                }
            }
        }
+      if (!has_stub_value)
+       value += local_ent;
     }
 
   switch (r_type)