aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2014-03-07 10:14:30 +1030
committerAlan Modra <amodra@gmail.com>2014-03-08 12:57:58 +1030
commitb80eed39e2e813c37cffcb873dc4fdd03381383c (patch)
treeedc04cceb7807daabb69a1d68be3d0d96b7c8576 /gold
parentc5164cbc322e77c331fee199cc0359269e952b5d (diff)
downloadgdb-b80eed39e2e813c37cffcb873dc4fdd03381383c.zip
gdb-b80eed39e2e813c37cffcb873dc4fdd03381383c.tar.gz
gdb-b80eed39e2e813c37cffcb873dc4fdd03381383c.tar.bz2
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.
Diffstat (limited to 'gold')
-rw-r--r--gold/ChangeLog12
-rw-r--r--gold/powerpc.cc84
2 files changed, 78 insertions, 18 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index c3c3866..ddf18d6 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -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>
Update copyright years.
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 6e9ddd6..0589d0c 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -1446,7 +1446,10 @@ public:
{
CHECK_NONE,
CHECK_SIGNED,
- CHECK_BITFIELD
+ CHECK_UNSIGNED,
+ CHECK_BITFIELD,
+ CHECK_LOW_INSN,
+ CHECK_HIGH_INSN
};
enum Status
@@ -1472,12 +1475,20 @@ private:
template<int valsize>
static inline bool
- has_overflow_bitfield(Address value)
+ has_overflow_unsigned(Address value)
{
Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1);
limit <<= ((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>
@@ -1489,6 +1500,11 @@ private:
if (has_overflow_signed<valsize>(value))
return STATUS_OVERFLOW;
}
+ else if (overflow == CHECK_UNSIGNED)
+ {
+ if (has_overflow_unsigned<valsize>(value))
+ return STATUS_OVERFLOW;
+ }
else if (overflow == CHECK_BITFIELD)
{
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;
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->data_shdr);
switch (r_type)
{
case elfcpp::R_POWERPC_ADDR32:
@@ -7272,16 +7289,19 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
overflow = Reloc::CHECK_SIGNED;
break;
- case elfcpp::R_POWERPC_ADDR24:
- case elfcpp::R_POWERPC_ADDR16:
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;
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_HA:
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_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
- if (size == 32)
- break;
- case elfcpp::R_POWERPC_REL24:
- case elfcpp::R_PPC_PLTREL24:
- case elfcpp::R_PPC_LOCAL24PC:
+ if (size != 32)
+ overflow = Reloc::CHECK_HIGH_INSN;
+ break;
+
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_TPREL16:
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_DTPREL16_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_BRTAKEN:
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;
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
= Powerpc_relocate_functions<size, big_endian>::STATUS_OK;
switch (r_type)