Better overflow checking for powerpc64 relocations

R_PPC64_ADDR16 is used in three contexts:
- .short data relocation
- 16-bit signed insn fields, eg. addi
- 16-bit unsigned insn fields, eg. ori
In the first case we want to allow both signed and unsigned 16-bit
values, the latter two ought to error if the field exceeds the range
of values allowed for 16-bit signed and unsigned integers
respectively.  These conflicting requirements meant that ld had to
choose the least restrictive overflow checks, and thus it is possible
to construct testcases where an addi field overflows but is not
reported by ld.  Many relocations dealing with 16-bit insn fields have
this problem.  What's more, some relocations that are only ever used
for signed fields of instructions woodenly copied the lax overflow
checking of R_PPC64_ADDR16.

bfd/
	* elf64-ppc.c (ppc64_elf_howto_raw): Use complain_overflow_signed
	for R_PPC64_ADDR14, R_PPC64_ADDR14_BRTAKEN, R_PPC64_ADDR14_BRNTAKEN,
	R_PPC64_SECTOFF, R_PPC64_ADDR16_DS, R_PPC64_SECTOFF_DS,
	R_PPC64_REL16 entries.  Use complain_overflow_dont for R_PPC64_TOC.
	(ppc64_elf_relocate_section): Modify overflow test for 16-bit
	fields in instructions to signed/unsigned according to whether
	the field takes a signed or unsigned value.
gold/
	* powerpc.cc (Powerpc_relocate_functions::Overflow_check): Add
	CHECK_UNSIGNED, CHECK_LOW_INSN, CHECK_HIGH_INSN.
	(Powerpc_relocate_functions::has_overflow_unsigned): New function.
	(Powerpc_relocate_functions::has_overflow_bitfield,
	overflowed): Use the above.
	(Target_powerpc::Relocate::relocate): Correct overflow checking
	for a number of relocations.  Modify overflow test for 16-bit
	fields in instructions to signed/unsigned according to whether
	the field takes a signed or unsigned value.
This commit is contained in:
Alan Modra 2014-03-07 10:14:30 +10:30
parent c5164cbc32
commit b80eed39e2
4 changed files with 132 additions and 38 deletions

View File

@ -1,3 +1,13 @@
2014-03-08 Alan Modra <amodra@gmail.com>
* elf64-ppc.c (ppc64_elf_howto_raw): Use complain_overflow_signed
for R_PPC64_ADDR14, R_PPC64_ADDR14_BRTAKEN, R_PPC64_ADDR14_BRNTAKEN,
R_PPC64_SECTOFF, R_PPC64_ADDR16_DS, R_PPC64_SECTOFF_DS,
R_PPC64_REL16 entries. Use complain_overflow_dont for R_PPC64_TOC.
(ppc64_elf_relocate_section): Modify overflow test for 16-bit
fields in instructions to signed/unsigned according to whether
the field takes a signed or unsigned value.
2014-03-07 Pedro Alves <palves@redhat.com> 2014-03-07 Pedro Alves <palves@redhat.com>
* rs6000-core.c (rs6000coff_core_p): Cast pointers to bfd_vma * rs6000-core.c (rs6000coff_core_p): Cast pointers to bfd_vma

View File

@ -357,7 +357,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_branch_reloc, /* special_function */ ppc64_elf_branch_reloc, /* special_function */
"R_PPC64_ADDR14", /* name */ "R_PPC64_ADDR14", /* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -374,7 +374,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_brtaken_reloc, /* special_function */ ppc64_elf_brtaken_reloc, /* special_function */
"R_PPC64_ADDR14_BRTAKEN",/* name */ "R_PPC64_ADDR14_BRTAKEN",/* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -391,7 +391,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_brtaken_reloc, /* special_function */ ppc64_elf_brtaken_reloc, /* special_function */
"R_PPC64_ADDR14_BRNTAKEN",/* name */ "R_PPC64_ADDR14_BRNTAKEN",/* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -632,7 +632,6 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
32, /* bitsize */ 32, /* bitsize */
TRUE, /* pc_relative */ TRUE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
/* FIXME: Verify. Was complain_overflow_bitfield. */
complain_overflow_signed, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */ bfd_elf_generic_reloc, /* special_function */
"R_PPC64_REL32", /* name */ "R_PPC64_REL32", /* name */
@ -727,7 +726,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_sectoff_reloc, /* special_function */ ppc64_elf_sectoff_reloc, /* special_function */
"R_PPC64_SECTOFF", /* name */ "R_PPC64_SECTOFF", /* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -1015,7 +1014,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
64, /* bitsize */ 64, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_dont, /* complain_on_overflow */
ppc64_elf_toc64_reloc, /* special_function */ ppc64_elf_toc64_reloc, /* special_function */
"R_PPC64_TOC", /* name */ "R_PPC64_TOC", /* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -1103,7 +1102,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */ bfd_elf_generic_reloc, /* special_function */
"R_PPC64_ADDR16_DS", /* name */ "R_PPC64_ADDR16_DS", /* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -1178,7 +1177,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
FALSE, /* pc_relative */ FALSE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
ppc64_elf_sectoff_reloc, /* special_function */ ppc64_elf_sectoff_reloc, /* special_function */
"R_PPC64_SECTOFF_DS", /* name */ "R_PPC64_SECTOFF_DS", /* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -1950,7 +1949,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
16, /* bitsize */ 16, /* bitsize */
TRUE, /* pc_relative */ TRUE, /* pc_relative */
0, /* bitpos */ 0, /* bitpos */
complain_overflow_bitfield, /* complain_on_overflow */ complain_overflow_signed, /* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */ bfd_elf_generic_reloc, /* special_function */
"R_PPC64_REL16", /* name */ "R_PPC64_REL16", /* name */
FALSE, /* partial_inplace */ FALSE, /* partial_inplace */
@ -12943,6 +12942,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
bfd_vma max_br_offset; bfd_vma max_br_offset;
bfd_vma from; bfd_vma from;
const Elf_Internal_Rela orig_rel = *rel; const Elf_Internal_Rela orig_rel = *rel;
reloc_howto_type *howto;
struct reloc_howto_struct alt_howto;
r_type = ELF64_R_TYPE (rel->r_info); r_type = ELF64_R_TYPE (rel->r_info);
r_symndx = ELF64_R_SYM (rel->r_info); r_symndx = ELF64_R_SYM (rel->r_info);
@ -14507,6 +14508,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
} }
/* Do any further special processing. */ /* Do any further special processing. */
howto = ppc64_elf_howto_table[(int) r_type];
switch (r_type) switch (r_type)
{ {
default: default:
@ -14581,7 +14583,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
info->callbacks->einfo info->callbacks->einfo
(_("%P: %H: error: %s not a multiple of %u\n"), (_("%P: %H: error: %s not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset, input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[r_type]->name, howto->name,
mask + 1); mask + 1);
bfd_set_error (bfd_error_bad_value); bfd_set_error (bfd_error_bad_value);
ret = FALSE; ret = FALSE;
@ -14602,23 +14604,45 @@ ppc64_elf_relocate_section (bfd *output_bfd,
info->callbacks->einfo info->callbacks->einfo
(_("%P: %H: unresolvable %s against `%T'\n"), (_("%P: %H: unresolvable %s against `%T'\n"),
input_bfd, input_section, rel->r_offset, input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[(int) r_type]->name, howto->name,
h->elf.root.root.string); h->elf.root.root.string);
ret = FALSE; ret = FALSE;
} }
r = _bfd_final_link_relocate (ppc64_elf_howto_table[(int) r_type], /* 16-bit fields in insns mostly have signed values, but a
input_bfd, few insns have 16-bit unsigned values. Really, we should
input_section, have different reloc types. */
contents, if (howto->complain_on_overflow != complain_overflow_dont
rel->r_offset, && howto->dst_mask == 0xffff
relocation, && (input_section->flags & SEC_CODE) != 0)
addend); {
enum complain_overflow complain = complain_overflow_signed;
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
if (howto->rightshift == 0
? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
|| (insn & (0x3f << 26)) == 24u << 26 /* ori */
|| (insn & (0x3f << 26)) == 26u << 26 /* xori */
|| (insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
: ((insn & (0x3f << 26)) == 29u << 26 /* andis */
|| (insn & (0x3f << 26)) == 25u << 26 /* oris */
|| (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
complain = complain_overflow_unsigned;
if (howto->complain_on_overflow != complain)
{
alt_howto = *howto;
alt_howto.complain_on_overflow = complain;
howto = &alt_howto;
}
}
r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
rel->r_offset, relocation, addend);
if (r != bfd_reloc_ok) if (r != bfd_reloc_ok)
{ {
char *more_info = NULL; char *more_info = NULL;
const char *reloc_name = ppc64_elf_howto_table[r_type]->name; const char *reloc_name = howto->name;
if (reloc_dest != DEST_NORMAL) if (reloc_dest != DEST_NORMAL)
{ {
@ -14638,7 +14662,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
continue; continue;
if (h != NULL if (h != NULL
&& h->elf.root.type == bfd_link_hash_undefweak && h->elf.root.type == bfd_link_hash_undefweak
&& ppc64_elf_howto_table[r_type]->pc_relative) && howto->pc_relative)
{ {
/* Assume this is a call protected by other code that /* Assume this is a call protected by other code that
detects the symbol is undefined. If this is the case, detects the symbol is undefined. If this is the case,

View File

@ -1,3 +1,15 @@
2014-03-07 Alan Modra <amodra@gmail.com>
* powerpc.cc (Powerpc_relocate_functions::Overflow_check): Add
CHECK_UNSIGNED, CHECK_LOW_INSN, CHECK_HIGH_INSN.
(Powerpc_relocate_functions::has_overflow_unsigned): New function.
(Powerpc_relocate_functions::has_overflow_bitfield,
overflowed): Use the above.
(Target_powerpc::Relocate::relocate): Correct overflow checking
for a number of relocations. Modify overflow test for 16-bit
fields in instructions to signed/unsigned according to whether
the field takes a signed or unsigned value.
2014-03-05 Alan Modra <amodra@gmail.com> 2014-03-05 Alan Modra <amodra@gmail.com>
Update copyright years. Update copyright years.

View File

@ -1446,7 +1446,10 @@ public:
{ {
CHECK_NONE, CHECK_NONE,
CHECK_SIGNED, CHECK_SIGNED,
CHECK_BITFIELD CHECK_UNSIGNED,
CHECK_BITFIELD,
CHECK_LOW_INSN,
CHECK_HIGH_INSN
}; };
enum Status enum Status
@ -1472,12 +1475,20 @@ private:
template<int valsize> template<int valsize>
static inline bool static inline bool
has_overflow_bitfield(Address value) has_overflow_unsigned(Address value)
{ {
Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1); Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1);
limit <<= ((valsize - 1) >> 1); limit <<= ((valsize - 1) >> 1);
limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1)); limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1));
return value > (limit << 1) - 1 && value + limit > (limit << 1) - 1; return value > (limit << 1) - 1;
}
template<int valsize>
static inline bool
has_overflow_bitfield(Address value)
{
return (has_overflow_unsigned<valsize>(value)
&& has_overflow_signed<valsize>(value));
} }
template<int valsize> template<int valsize>
@ -1489,6 +1500,11 @@ private:
if (has_overflow_signed<valsize>(value)) if (has_overflow_signed<valsize>(value))
return STATUS_OVERFLOW; return STATUS_OVERFLOW;
} }
else if (overflow == CHECK_UNSIGNED)
{
if (has_overflow_unsigned<valsize>(value))
return STATUS_OVERFLOW;
}
else if (overflow == CHECK_BITFIELD) else if (overflow == CHECK_BITFIELD)
{ {
if (has_overflow_bitfield<valsize>(value)) if (has_overflow_bitfield<valsize>(value))
@ -7259,6 +7275,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
} }
typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE; typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE;
elfcpp::Shdr<size, big_endian> shdr(relinfo->data_shdr);
switch (r_type) switch (r_type)
{ {
case elfcpp::R_POWERPC_ADDR32: case elfcpp::R_POWERPC_ADDR32:
@ -7272,16 +7289,19 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
overflow = Reloc::CHECK_SIGNED; overflow = Reloc::CHECK_SIGNED;
break; break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_UADDR16: case elfcpp::R_POWERPC_UADDR16:
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
overflow = Reloc::CHECK_BITFIELD; overflow = Reloc::CHECK_BITFIELD;
break; break;
case elfcpp::R_POWERPC_ADDR16:
// We really should have three separate relocations,
// one for 16-bit data, one for insns with 16-bit signed fields,
// and one for insns with 16-bit unsigned fields.
overflow = Reloc::CHECK_BITFIELD;
if ((shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0)
overflow = Reloc::CHECK_LOW_INSN;
break;
case elfcpp::R_POWERPC_ADDR16_HI: case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_ADDR16_HA: case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_GOT16_HI: case elfcpp::R_POWERPC_GOT16_HI:
@ -7308,17 +7328,31 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_POWERPC_GOT_DTPREL16_HA: case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
case elfcpp::R_POWERPC_REL16_HI: case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA: case elfcpp::R_POWERPC_REL16_HA:
if (size == 32) if (size != 32)
break; overflow = Reloc::CHECK_HIGH_INSN;
case elfcpp::R_POWERPC_REL24: break;
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_REL16: case elfcpp::R_POWERPC_REL16:
case elfcpp::R_PPC64_TOC16: case elfcpp::R_PPC64_TOC16:
case elfcpp::R_POWERPC_GOT16: case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_SECTOFF: case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_TPREL16: case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_DTPREL16: case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16:
overflow = Reloc::CHECK_LOW_INSN;
break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_PPC64_TPREL16_DS: case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_DTPREL16_DS: case elfcpp::R_PPC64_DTPREL16_DS:
case elfcpp::R_PPC64_TOC16_DS: case elfcpp::R_PPC64_TOC16_DS:
@ -7327,14 +7361,28 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_POWERPC_REL14: case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN: case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN: case elfcpp::R_POWERPC_REL14_BRNTAKEN:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16:
overflow = Reloc::CHECK_SIGNED; overflow = Reloc::CHECK_SIGNED;
break; break;
} }
if (overflow == Reloc::CHECK_LOW_INSN
|| overflow == Reloc::CHECK_HIGH_INSN)
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
overflow = Reloc::CHECK_SIGNED;
if (overflow == Reloc::CHECK_LOW_INSN
? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
|| (insn & (0x3f << 26)) == 24u << 26 /* ori */
|| (insn & (0x3f << 26)) == 26u << 26 /* xori */
|| (insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
: ((insn & (0x3f << 26)) == 29u << 26 /* andis */
|| (insn & (0x3f << 26)) == 25u << 26 /* oris */
|| (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
overflow = Reloc::CHECK_UNSIGNED;
}
typename Powerpc_relocate_functions<size, big_endian>::Status status typename Powerpc_relocate_functions<size, big_endian>::Status status
= Powerpc_relocate_functions<size, big_endian>::STATUS_OK; = Powerpc_relocate_functions<size, big_endian>::STATUS_OK;
switch (r_type) switch (r_type)