PPC gold doesn't check for overflow properly

Corrects overflow test for rel14, addr14, rel24, addr24 branch relocs,
and prints an information message to give a hint as to how a branch
that can't reach a stub might be cured.

bfd/
	* elf64-ppc.c (group_sections): Init stub14_group_size from
	--stub-group-size parameter divided by 1024.
gold/
	* powerpc.cc (Stub_control::Stub_control): Init stub14_group_size_
	from --stub-group-size parameter divided by 1024.
	(Powerpc_relocate_functions::rela, rela_ua): Add fieldsize
	template parameter.  Update all uses.
	(Target_powerpc::Relocate::relocate): Rename has_plt_value to
	has_stub_value.  Set for long branches.  Don't report overflow for
	branch to undefined weak symbols.  Print info message on
	overflowing branch to stub.
This commit is contained in:
Alan Modra 2014-11-20 19:01:23 +10:30
parent 1e269e9b8f
commit 0cfb071748
4 changed files with 66 additions and 38 deletions

View File

@ -1,3 +1,8 @@
2014-11-20 Alan Modra <amodra@gmail.com>
* elf64-ppc.c (group_sections): Init stub14_group_size from
--stub-group-size parameter divided by 1024.
2014-11-20 Alan Modra <amodra@gmail.com>
* elf32-ppc.c (ppc_elf_relax_section): Correct ppc476 workaround

View File

@ -11805,7 +11805,7 @@ group_sections (struct ppc_link_hash_table *htab,
bfd_boolean suppress_size_errors;
suppress_size_errors = FALSE;
stub14_group_size = stub_group_size;
stub14_group_size = stub_group_size >> 10;
if (stub_group_size == 1)
{
/* Default values. */

View File

@ -1,3 +1,14 @@
2014-11-20 Alan Modra <amodra@gmail.com>
* powerpc.cc (Stub_control::Stub_control): Init stub14_group_size_
from --stub-group-size parameter divided by 1024.
(Powerpc_relocate_functions::rela, rela_ua): Add fieldsize
template parameter. Update all uses.
(Target_powerpc::Relocate::relocate): Rename has_plt_value to
has_stub_value. Set for long branches. Don't report overflow for
branch to undefined weak symbols. Print info message on
overflowing branch to stub.
2014-11-20 Alan Modra <amodra@gmail.com>
* powerpc.cc (Target_powerpc::do_relax): Add __go_go to thread_starters.

View File

@ -1524,58 +1524,58 @@ private:
}
// Do a simple RELA relocation
template<int valsize>
template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view, Address value, Overflow_check overflow)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
elfcpp::Swap<valsize, big_endian>::writeval(wv, value);
elfcpp::Swap<fieldsize, big_endian>::writeval(wv, value);
return overflowed<valsize>(value, overflow);
}
template<int valsize>
template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view,
unsigned int right_shift,
typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
// Do a simple RELA relocation, unaligned.
template<int valsize>
template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view, Address value, Overflow_check overflow)
{
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, value);
elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, value);
return overflowed<valsize>(value, overflow);
}
template<int valsize>
template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view,
unsigned int right_shift,
typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype
Valtype;
Valtype val = elfcpp::Swap<valsize, big_endian>::readval(view);
Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, val | reloc);
elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
@ -1583,28 +1583,29 @@ public:
// R_PPC64_ADDR64: (Symbol + Addend)
static inline void
addr64(unsigned char* view, Address value)
{ This::template rela<64>(view, value, CHECK_NONE); }
{ This::template rela<64,64>(view, value, CHECK_NONE); }
// R_PPC64_UADDR64: (Symbol + Addend) unaligned
static inline void
addr64_u(unsigned char* view, Address value)
{ This::template rela_ua<64>(view, value, CHECK_NONE); }
{ This::template rela_ua<64,64>(view, value, CHECK_NONE); }
// R_POWERPC_ADDR32: (Symbol + Addend)
static inline Status
addr32(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela<32>(view, value, overflow); }
{ return This::template rela<32,32>(view, value, overflow); }
// R_POWERPC_UADDR32: (Symbol + Addend) unaligned
static inline Status
addr32_u(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela_ua<32>(view, value, overflow); }
{ return This::template rela_ua<32,32>(view, value, overflow); }
// R_POWERPC_ADDR24: (Symbol + Addend) & 0x3fffffc
static inline Status
addr24(unsigned char* view, Address value, Overflow_check overflow)
{
Status stat = This::template rela<32>(view, 0, 0x03fffffc, value, overflow);
Status stat = This::template rela<32,26>(view, 0, 0x03fffffc,
value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
@ -1613,18 +1614,18 @@ public:
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff
static inline Status
addr16(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela<16>(view, value, overflow); }
{ return This::template rela<16,16>(view, value, overflow); }
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff, unaligned
static inline Status
addr16_u(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela_ua<16>(view, value, overflow); }
{ return This::template rela_ua<16,16>(view, value, overflow); }
// R_POWERPC_ADDR16_DS: (Symbol + Addend) & 0xfffc
static inline Status
addr16_ds(unsigned char* view, Address value, Overflow_check overflow)
{
Status stat = This::template rela<16>(view, 0, 0xfffc, value, overflow);
Status stat = This::template rela<16,16>(view, 0, 0xfffc, value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
@ -1633,7 +1634,7 @@ public:
// R_POWERPC_ADDR16_HI: ((Symbol + Addend) >> 16) & 0xffff
static inline void
addr16_hi(unsigned char* view, Address value)
{ This::template rela<16>(view, 16, 0xffff, value, CHECK_NONE); }
{ This::template rela<16,16>(view, 16, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff
static inline void
@ -1643,7 +1644,7 @@ public:
// R_POWERPC_ADDR16_HIGHER: ((Symbol + Addend) >> 32) & 0xffff
static inline void
addr16_hi2(unsigned char* view, Address value)
{ This::template rela<16>(view, 32, 0xffff, value, CHECK_NONE); }
{ This::template rela<16,16>(view, 32, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHERA: ((Symbol + Addend + 0x8000) >> 32) & 0xffff
static inline void
@ -1653,7 +1654,7 @@ public:
// R_POWERPC_ADDR16_HIGHEST: ((Symbol + Addend) >> 48) & 0xffff
static inline void
addr16_hi3(unsigned char* view, Address value)
{ This::template rela<16>(view, 48, 0xffff, value, CHECK_NONE); }
{ This::template rela<16,16>(view, 48, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHESTA: ((Symbol + Addend + 0x8000) >> 48) & 0xffff
static inline void
@ -1664,7 +1665,7 @@ public:
static inline Status
addr14(unsigned char* view, Address value, Overflow_check overflow)
{
Status stat = This::template rela<32>(view, 0, 0xfffc, value, overflow);
Status stat = This::template rela<32,16>(view, 0, 0xfffc, value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
@ -2362,7 +2363,7 @@ class Stub_control
// the stubbed branches.
Stub_control(int32_t size)
: state_(NO_GROUP), stub_group_size_(abs(size)),
stub14_group_size_(abs(size)),
stub14_group_size_(abs(size) >> 10),
stubs_always_before_branch_(size < 0), suppress_size_errors_(false),
group_end_addr_(0), owner_(NULL), output_section_(NULL)
{
@ -6702,7 +6703,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
bool has_plt_value = false;
bool has_stub_value = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
if ((gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
@ -6741,7 +6742,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
gold_assert(off != invalid_address);
value = stub_table->stub_address() + off;
}
has_plt_value = true;
has_stub_value = true;
}
if (r_type == elfcpp::R_POWERPC_GOT16
@ -6772,7 +6773,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
else if (gsym != NULL
&& (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24)
&& has_plt_value)
&& has_stub_value)
{
if (size == 64)
{
@ -7077,7 +7078,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
value = psymval->value(object, rela.get_r_addend());
}
}
else if (!has_plt_value)
else if (!has_stub_value)
{
Address addend = 0;
unsigned int dest_shndx;
@ -7115,8 +7116,11 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
{
Address off = stub_table->find_long_branch_entry(object, value);
if (off != invalid_address)
value = (stub_table->stub_address() + stub_table->plt_size()
+ off);
{
value = (stub_table->stub_address() + stub_table->plt_size()
+ off);
has_stub_value = true;
}
}
}
}
@ -7667,9 +7671,17 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
r_type);
break;
}
if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK)
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("relocation overflow"));
if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK
&& !has_stub_value
&& !(gsym != NULL
&& gsym->is_weak_undefined()
&& is_branch_reloc(r_type)))
{
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("relocation overflow"));
if (has_stub_value)
gold_info(_("try relinking with a smaller --stub-group-size"));
}
return true;
}