[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.
This commit is contained in:
Alan Modra 2020-10-09 16:56:33 +10:30
parent 4290b0ab2b
commit fa40fbe484
4 changed files with 50 additions and 18 deletions

View 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> 2020-10-09 H.J. Lu <hongjiu.lu@intel.com>
PR gas/26703 PR gas/26703

View file

@ -14259,8 +14259,6 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
} }
val = sym->st_value + ent->addend; 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) if (sym_sec != NULL && sym_sec->output_section != NULL)
val += sym_sec->output_offset + sym_sec->output_section->vma; val += sym_sec->output_offset + sym_sec->output_section->vma;

View 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> 2020-10-09 Alan Modra <amodra@gmail.com>
* powerpc.cc (is_got_reloc): New function. * powerpc.cc (is_got_reloc): New function.

View file

@ -2767,8 +2767,6 @@ Powerpc_relobj<size, big_endian>::do_relocate_sections(
if (this->local_has_plt_offset(i)) if (this->local_has_plt_offset(i))
{ {
Address value = this->local_symbol_value(i, 0); 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); size_t off = this->local_plt_offset(i);
elfcpp::Swap<size, big_endian>::writeval(oview + off, value); elfcpp::Swap<size, big_endian>::writeval(oview + off, value);
modified = true; modified = true;
@ -3539,6 +3537,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
from += (this->object_->output_section(this->shndx_)->address() from += (this->object_->output_section(this->shndx_)->address()
+ this->offset_); + this->offset_);
Address to; Address to;
unsigned int other;
if (gsym != NULL) if (gsym != NULL)
{ {
switch (gsym->source()) switch (gsym->source())
@ -3566,8 +3565,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
to = symtab->compute_final_value<size>(gsym, &status); to = symtab->compute_final_value<size>(gsym, &status);
if (status != Symbol_table::CFVS_OK) if (status != Symbol_table::CFVS_OK)
return true; return true;
if (size == 64) other = gsym->nonvis() >> 3;
to += this->object_->ppc64_local_entry_offset(gsym);
} }
else else
{ {
@ -3584,8 +3582,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|| !symval.has_output_value()) || !symval.has_output_value())
return true; return true;
to = symval.value(this->object_, 0); to = symval.value(this->object_, 0);
if (size == 64) other = this->object_->st_other(this->r_sym_) >> 5;
to += this->object_->ppc64_local_entry_offset(this->r_sym_);
} }
if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24)) if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
to += this->addend_; to += this->addend_;
@ -3598,7 +3595,11 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
&to, &dest_shndx)) &to, &dest_shndx))
return true; 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 if (delta + max_branch_offset >= 2 * max_branch_offset
|| (size == 64 || (size == 64
&& this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC && 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()); && gsym->output_data() == target->savres_section());
ok = stub_table->add_long_branch_entry(this->object_, ok = stub_table->add_long_branch_entry(this->object_,
this->r_type_, this->r_type_,
from, to, save_res); from, to, other, save_res);
} }
} }
if (!ok) 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) Branch_stub_ent(unsigned int off, bool notoc, bool save_res)
: off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res), : off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res),
tocoff_(0) other_(0), tocoff_(0)
{ } { }
unsigned int off_; unsigned int off_;
@ -4696,6 +4697,7 @@ class Stub_table : public Output_relaxed_input_section
unsigned int notoc_ : 1; unsigned int notoc_ : 1;
unsigned int toc_ : 1; unsigned int toc_ : 1;
unsigned int save_res_ : 1; unsigned int save_res_ : 1;
unsigned int other_ : 3;
unsigned int tocoff_ : 8; unsigned int tocoff_ : 8;
}; };
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; 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. // Add a long branch stub.
bool bool
add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, 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* const Branch_stub_ent*
find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, 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, unsigned int r_type,
Address from, Address from,
Address to, Address to,
unsigned int other,
bool save_res) bool save_res)
{ {
Branch_stub_key key(object, to); Branch_stub_key key(object, to);
@ -5301,6 +5304,8 @@ Stub_table<size, big_endian>::add_long_branch_entry(
this->need_resize_ = true; this->need_resize_ = true;
p.first->second.toc_ = 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_); gold_assert(save_res == p.first->second.save_res_);
if (p.second || (this->resizing_ && !p.first->second.iter_)) 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) if (off + (1 << 25) < 2 << 25)
return bytes + 4; return bytes + 4;
if (!this->targ_->power10_stubs() if (!this->targ_->power10_stubs()
@ -6377,6 +6383,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
} }
if (bs->second.toc_) if (bs->second.toc_)
{ {
delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
if (delta + (1 << 25) >= 2 << 25) if (delta + (1 << 25) >= 2 << 25)
{ {
Address brlt_addr Address brlt_addr
@ -6410,6 +6417,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
} }
else else
{ {
if (!bs->second.notoc_)
delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25) if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
{ {
unsigned char* startp = p; unsigned char* startp = p;
@ -6640,6 +6649,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
p = oview + off; p = oview + off;
Address loc = this->stub_address() + off; Address loc = this->stub_address() + off;
Address delta = bs->first.dest_ - loc; Address delta = bs->first.dest_ - loc;
if (!bs->second.notoc_)
delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
if (bs->second.notoc_) if (bs->second.notoc_)
{ {
unsigned char* startp = p; unsigned char* startp = p;
@ -11039,14 +11050,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
|| r_type == elfcpp::R_POWERPC_PLT16_HA))) || r_type == elfcpp::R_POWERPC_PLT16_HA)))
addend = rela.get_r_addend(); addend = rela.get_r_addend();
value = psymval->value(object, addend); value = psymval->value(object, addend);
unsigned int local_ent = 0;
if (size == 64 && is_branch_reloc<size>(r_type)) if (size == 64 && is_branch_reloc<size>(r_type))
{ {
if (target->abiversion() >= 2) if (target->abiversion() >= 2)
{ {
if (gsym != NULL) if (gsym != NULL)
value += object->ppc64_local_entry_offset(gsym); local_ent = object->ppc64_local_entry_offset(gsym);
else else
value += object->ppc64_local_entry_offset(r_sym); local_ent = object->ppc64_local_entry_offset(r_sym);
} }
else else
{ {
@ -11055,9 +11067,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
&value, &dest_shndx); &value, &dest_shndx);
} }
} }
Address max_branch_offset = max_branch_delta<size>(r_type); Address max_branch = max_branch_delta<size>(r_type);
if (max_branch_offset != 0 if (max_branch != 0
&& (value - address + max_branch_offset >= 2 * max_branch_offset && (value + local_ent - address + max_branch >= 2 * max_branch
|| (size == 64 || (size == 64
&& r_type == elfcpp::R_PPC64_REL24_NOTOC && r_type == elfcpp::R_PPC64_REL24_NOTOC
&& (gsym != NULL && (gsym != NULL
@ -11082,12 +11094,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
+ ent->off_); + ent->off_);
if (size == 64 if (size == 64
&& r_type != elfcpp::R_PPC64_REL24_NOTOC) && r_type != elfcpp::R_PPC64_REL24_NOTOC)
value += ent->tocoff_; value += (elfcpp::ppc64_decode_local_entry(ent->other_)
+ ent->tocoff_);
} }
has_stub_value = true; has_stub_value = true;
} }
} }
} }
if (!has_stub_value)
value += local_ent;
} }
switch (r_type) switch (r_type)