aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2020-10-09 16:56:33 +1030
committerAndreas K. Hüttel <dilfridge@gentoo.org>2020-10-21 01:01:31 +0300
commit3cbd8f9c0565d407d9682d40e3b948951f4c0547 (patch)
tree08b03fb805fd5c0d81dc135fb3d93bb70a3710bc
parent[GOLD] internal error in relocate, at powerpc.cc:10473 (diff)
downloadbinutils-gdb-3cbd8f9c0565d407d9682d40e3b948951f4c0547.tar.gz
binutils-gdb-3cbd8f9c0565d407d9682d40e3b948951f4c0547.tar.bz2
binutils-gdb-3cbd8f9c0565d407d9682d40e3b948951f4c0547.zip
[GOLD] Power10 segv due to wild r2
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. (cherry picked from commit fa40fbe484954c560ab1c0ff4bc1b2eeb1511344) (cherry picked from commit 43fb3c78d42692620400eb76a6b6aa87b37f7693)
-rw-r--r--bfd/ChangeLog5
-rw-r--r--bfd/elf64-ppc.c2
-rw-r--r--gold/ChangeLog14
-rw-r--r--gold/powerpc.cc47
4 files changed, 50 insertions, 18 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index ac66598c2d..2bca0948f6 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -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-07 H.J. Lu <hongjiu.lu@intel.com>
PR ld/26711
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 0aeda47e57..ec8c85eba5 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -14255,8 +14255,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;
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 0d7576bffc..03d99a52fe 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,5 +1,19 @@
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.
(Target_powerpc::Relocate::relocate): Use it here, exclude GOT
relocs when looking for stubs.
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index f9eb4f98bd..476fb4e588 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -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)