From f9c6b9078c54ea0f018b673e2ff128e61a0aa666 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Tue, 29 Oct 2013 16:53:25 +1030 Subject: Report overflow on PowerPC64 @h and @ha relocations. This changes the behaviour of @h and @ha on PowerPC64 to report errors on 32-bit overflow. The motivation for this change is that on PowerPC64, most uses of @h and @ha modifiers and their corresponding relocations are to build up 32-bit offsets. We'd like to know when such offsets overflow. Only rarely do people use @h or @ha with the high 32-bit modifiers to build a 64-bit constant. Those uses will now need to use two new modifiers, @high and @higha, if the constant isn't known at assembly time. For now, we won't report overflow at assembly time.. This also fixes an error when applying some of the HIGHER and HIGHEST relocations. include/elf/ * ppc64.h (R_PPC64_ADDR16_HIGH, R_PPC64_ADDR16_HIGHA, R_PPC64_TPREL16_HIGH, R_PPC64_TPREL16_HIGHA, R_PPC64_DTPREL16_HIGH, R_PPC64_DTPREL16_HIGHA): New. (IS_PPC64_TLS_RELOC): Match new tls relocs. bfd/ * reloc.c (BFD_RELOC_PPC64_ADDR16_HIGH, BFD_RELOC_PPC64_ADDR16_HIGHA, BFD_RELOC_PPC64_TPREL16_HIGH, BFD_RELOC_PPC64_TPREL16_HIGHA, BFD_RELOC_PPC64_DTPREL16_HIGH, BFD_RELOC_PPC64_DTPREL16_HIGHA): New. * elf64-ppc.c (ppc64_elf_howto_raw): Add entries for new relocs. Make all _HA and _HI relocs report signed overflow. (ppc64_elf_reloc_type_lookup): Handle new relocs. (must_be_dyn_reloc, ppc64_elf_check_relocs): Likewise. (dec_dynrel_count, ppc64_elf_relocate_section): Likewise. (ppc64_elf_relocate_section): Don't apply 0x8000 adjust to R_PPC64_TPREL16_HIGHER, R_PPC64_TPREL16_HIGHEST, R_PPC64_DTPREL16_HIGHER, and R_PPC64_DTPREL16_HIGHEST. * libbfd.h: Regenerate. * bfd-in2.h: Regenerate. gas/ * config/tc-ppc.c (SEX16): Don't mask. (REPORT_OVERFLOW_HI): Define as zero. (ppc_elf_suffix): Support @high, @higha, @dtprel@high, @dtprel@higha, @tprel@high, and @tprel@higha modifiers. (md_assemble): Ignore X_unsigned when applying 16-bit insn fields. Add (disabled) code to check @h and @ha reloc overflow for powerpc64. Handle new relocs. (md_apply_fix): Similarly. elfcpp/ * powerpc.h (R_PPC64_ADDR16_HIGH, R_PPC64_ADDR16_HIGHA, R_PPC64_TPREL16_HIGH, R_PPC64_TPREL16_HIGHA, R_PPC64_DTPREL16_HIGH, R_PPC64_DTPREL16_HIGHA): Define. gold/ * powerpc.cc (Target_powerpc::Scan::check_non_pic): Handle new relocs. (Target_powerpc::Scan::global, local): Likewise. (Target_powerpc::Relocate::relocate): Likewise. Check for overflow on all ppc64 @h and @ha relocs. --- gas/config/tc-ppc.c | 139 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 103 insertions(+), 36 deletions(-) (limited to 'gas/config') diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index 6b54f5a..34afc91 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -86,7 +86,11 @@ static int set_target_endian = 0; compensating for #lo being treated as a signed number. */ #define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000) -#define SEX16(val) ((((val) & 0xffff) ^ 0x8000) - 0x8000) +#define SEX16(val) (((val) ^ 0x8000) - 0x8000) + +/* For the time being on ppc64, don't report overflow on @h and @ha + applied to constants. */ +#define REPORT_OVERFLOW_HI 0 static bfd_boolean reg_names_p = TARGET_REG_NAMES_P; @@ -1923,6 +1927,8 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p) MAP32 ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD), MAP32 ("relsda", BFD_RELOC_PPC_EMB_RELSDA), MAP32 ("xgot", BFD_RELOC_PPC_TOC16), + MAP64 ("high", BFD_RELOC_PPC64_ADDR16_HIGH), + MAP64 ("higha", BFD_RELOC_PPC64_ADDR16_HIGHA), MAP64 ("higher", BFD_RELOC_PPC64_HIGHER), MAP64 ("highera", BFD_RELOC_PPC64_HIGHER_S), MAP64 ("highest", BFD_RELOC_PPC64_HIGHEST), @@ -1932,10 +1938,14 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p) MAP64 ("toc@l", BFD_RELOC_PPC64_TOC16_LO), MAP64 ("toc@h", BFD_RELOC_PPC64_TOC16_HI), MAP64 ("toc@ha", BFD_RELOC_PPC64_TOC16_HA), + MAP64 ("dtprel@high", BFD_RELOC_PPC64_DTPREL16_HIGH), + MAP64 ("dtprel@higha", BFD_RELOC_PPC64_DTPREL16_HIGHA), MAP64 ("dtprel@higher", BFD_RELOC_PPC64_DTPREL16_HIGHER), MAP64 ("dtprel@highera", BFD_RELOC_PPC64_DTPREL16_HIGHERA), MAP64 ("dtprel@highest", BFD_RELOC_PPC64_DTPREL16_HIGHEST), MAP64 ("dtprel@highesta", BFD_RELOC_PPC64_DTPREL16_HIGHESTA), + MAP64 ("tprel@high", BFD_RELOC_PPC64_TPREL16_HIGH), + MAP64 ("tprel@higha", BFD_RELOC_PPC64_TPREL16_HIGHA), MAP64 ("tprel@higher", BFD_RELOC_PPC64_TPREL16_HIGHER), MAP64 ("tprel@highera", BFD_RELOC_PPC64_TPREL16_HIGHERA), MAP64 ("tprel@highest", BFD_RELOC_PPC64_TPREL16_HIGHEST), @@ -2810,55 +2820,76 @@ md_assemble (char *str) break; case BFD_RELOC_LO16: - /* X_unsigned is the default, so if the user has done - something which cleared it, we always produce a - signed value. */ - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number &= 0xffff; - else + ex.X_add_number &= 0xffff; + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_HI16: - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number = PPC_HI (ex.X_add_number); - else - ex.X_add_number = SEX16 (PPC_HI (ex.X_add_number)); + if (REPORT_OVERFLOW_HI && ppc_obj64) + { + /* PowerPC64 @h is tested for overflow. */ + ex.X_add_number = (addressT) ex.X_add_number >> 16; + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + { + addressT sign = (((addressT) -1 >> 16) + 1) >> 1; + ex.X_add_number + = ((addressT) ex.X_add_number ^ sign) - sign; + } + break; + } + /* Fall thru */ + + case BFD_RELOC_PPC64_ADDR16_HIGH: + ex.X_add_number = PPC_HI (ex.X_add_number); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_HI16_S: - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number = PPC_HA (ex.X_add_number); - else - ex.X_add_number = SEX16 (PPC_HA (ex.X_add_number)); + if (REPORT_OVERFLOW_HI && ppc_obj64) + { + /* PowerPC64 @ha is tested for overflow. */ + ex.X_add_number + = ((addressT) ex.X_add_number + 0x8000) >> 16; + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + { + addressT sign = (((addressT) -1 >> 16) + 1) >> 1; + ex.X_add_number + = ((addressT) ex.X_add_number ^ sign) - sign; + } + break; + } + /* Fall thru */ + + case BFD_RELOC_PPC64_ADDR16_HIGHA: + ex.X_add_number = PPC_HA (ex.X_add_number); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_PPC64_HIGHER: - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number = PPC_HIGHER (ex.X_add_number); - else - ex.X_add_number = SEX16 (PPC_HIGHER (ex.X_add_number)); + ex.X_add_number = PPC_HIGHER (ex.X_add_number); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_PPC64_HIGHER_S: - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number = PPC_HIGHERA (ex.X_add_number); - else - ex.X_add_number = SEX16 (PPC_HIGHERA (ex.X_add_number)); + ex.X_add_number = PPC_HIGHERA (ex.X_add_number); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_PPC64_HIGHEST: - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number = PPC_HIGHEST (ex.X_add_number); - else - ex.X_add_number = SEX16 (PPC_HIGHEST (ex.X_add_number)); + ex.X_add_number = PPC_HIGHEST (ex.X_add_number); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + ex.X_add_number = SEX16 (ex.X_add_number); break; case BFD_RELOC_PPC64_HIGHEST_S: - if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED)) - ex.X_add_number = PPC_HIGHESTA (ex.X_add_number); - else - ex.X_add_number = SEX16 (PPC_HIGHESTA (ex.X_add_number)); + ex.X_add_number = PPC_HIGHESTA (ex.X_add_number); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + ex.X_add_number = SEX16 (ex.X_add_number); break; } #endif /* OBJ_ELF */ @@ -6390,25 +6421,51 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) fieldval = value & 0xffff; sign_extend_16: if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0) - fieldval = (fieldval ^ 0x8000) - 0x8000; + fieldval = SEX16 (fieldval); fixP->fx_no_overflow = 1; break; + case BFD_RELOC_HI16: + case BFD_RELOC_HI16_PCREL: #ifdef OBJ_ELF + if (REPORT_OVERFLOW_HI && ppc_obj64) + { + fieldval = value >> 16; + if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0) + { + valueT sign = (((valueT) -1 >> 16) + 1) >> 1; + fieldval = ((valueT) fieldval ^ sign) - sign; + } + break; + } + /* Fall thru */ + case BFD_RELOC_PPC_VLE_HI16A: case BFD_RELOC_PPC_VLE_HI16D: + case BFD_RELOC_PPC64_ADDR16_HIGH: #endif - case BFD_RELOC_HI16: - case BFD_RELOC_HI16_PCREL: fieldval = PPC_HI (value); goto sign_extend_16; + case BFD_RELOC_HI16_S: + case BFD_RELOC_HI16_S_PCREL: #ifdef OBJ_ELF + if (REPORT_OVERFLOW_HI && ppc_obj64) + { + fieldval = (value + 0x8000) >> 16; + if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0) + { + valueT sign = (((valueT) -1 >> 16) + 1) >> 1; + fieldval = ((valueT) fieldval ^ sign) - sign; + } + break; + } + /* Fall thru */ + case BFD_RELOC_PPC_VLE_HA16A: case BFD_RELOC_PPC_VLE_HA16D: + case BFD_RELOC_PPC64_ADDR16_HIGHA: #endif - case BFD_RELOC_HI16_S: - case BFD_RELOC_HI16_S_PCREL: fieldval = PPC_HA (value); goto sign_extend_16; @@ -6471,10 +6528,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_PPC_GOT_DTPREL16_HA: case BFD_RELOC_PPC64_TPREL16_DS: case BFD_RELOC_PPC64_TPREL16_LO_DS: + case BFD_RELOC_PPC64_TPREL16_HIGH: + case BFD_RELOC_PPC64_TPREL16_HIGHA: case BFD_RELOC_PPC64_TPREL16_HIGHER: case BFD_RELOC_PPC64_TPREL16_HIGHERA: case BFD_RELOC_PPC64_TPREL16_HIGHEST: case BFD_RELOC_PPC64_TPREL16_HIGHESTA: + case BFD_RELOC_PPC64_DTPREL16_HIGH: + case BFD_RELOC_PPC64_DTPREL16_HIGHA: case BFD_RELOC_PPC64_DTPREL16_DS: case BFD_RELOC_PPC64_DTPREL16_LO_DS: case BFD_RELOC_PPC64_DTPREL16_HIGHER: @@ -6660,6 +6721,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_PPC64_HIGHER_S: case BFD_RELOC_PPC64_HIGHEST: case BFD_RELOC_PPC64_HIGHEST_S: + case BFD_RELOC_PPC64_ADDR16_HIGH: + case BFD_RELOC_PPC64_ADDR16_HIGHA: break; case BFD_RELOC_PPC_DTPMOD: @@ -6736,10 +6799,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) case BFD_RELOC_PPC64_TOC16_LO: case BFD_RELOC_PPC64_TOC16_HI: case BFD_RELOC_PPC64_TOC16_HA: + case BFD_RELOC_PPC64_DTPREL16_HIGH: + case BFD_RELOC_PPC64_DTPREL16_HIGHA: case BFD_RELOC_PPC64_DTPREL16_HIGHER: case BFD_RELOC_PPC64_DTPREL16_HIGHERA: case BFD_RELOC_PPC64_DTPREL16_HIGHEST: case BFD_RELOC_PPC64_DTPREL16_HIGHESTA: + case BFD_RELOC_PPC64_TPREL16_HIGH: + case BFD_RELOC_PPC64_TPREL16_HIGHA: case BFD_RELOC_PPC64_TPREL16_HIGHER: case BFD_RELOC_PPC64_TPREL16_HIGHERA: case BFD_RELOC_PPC64_TPREL16_HIGHEST: -- cgit v1.1