diff options
Diffstat (limited to 'binutils-2.25/gas/config/tc-ppc.c')
-rw-r--r-- | binutils-2.25/gas/config/tc-ppc.c | 703 |
1 files changed, 467 insertions, 236 deletions
diff --git a/binutils-2.25/gas/config/tc-ppc.c b/binutils-2.25/gas/config/tc-ppc.c index 6b54f5ad..189a22ba 100644 --- a/binutils-2.25/gas/config/tc-ppc.c +++ b/binutils-2.25/gas/config/tc-ppc.c @@ -1,7 +1,5 @@ /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000) - Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Free Software Foundation, Inc. + Copyright (C) 1994-2014 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. This file is part of GAS, the GNU Assembler. @@ -29,6 +27,7 @@ #ifdef OBJ_ELF #include "elf/ppc.h" +#include "elf/ppc64.h" #include "dwarf2dbg.h" #endif @@ -86,7 +85,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; @@ -126,9 +129,10 @@ static void ppc_vbyte (int); #endif #ifdef OBJ_ELF -static void ppc_elf_cons (int); static void ppc_elf_rdata (int); static void ppc_elf_lcomm (int); +static void ppc_elf_localentry (int); +static void ppc_elf_abiversion (int); #endif #ifdef TE_PE @@ -199,11 +203,20 @@ unsigned long nop_limit = 4; ppc_cpu_t ppc_cpu = 0; ppc_cpu_t sticky = 0; +/* Value for ELF e_flags EF_PPC64_ABI. */ +unsigned int ppc_abiversion = 0; + /* Flags set on encountering toc relocs. */ enum { has_large_toc_reloc = 1, has_small_toc_reloc = 2 } toc_reloc_types; + +/* Warn on emitting data to code sections. */ +int warn_476; +unsigned long last_insn; +segT last_seg; +subsegT last_subseg; /* The target specific pseudo-ops which we support. */ @@ -249,14 +262,12 @@ const pseudo_typeS md_pseudo_table[] = #endif #ifdef OBJ_ELF - { "llong", ppc_elf_cons, 8 }, - { "quad", ppc_elf_cons, 8 }, - { "long", ppc_elf_cons, 4 }, - { "word", ppc_elf_cons, 2 }, - { "short", ppc_elf_cons, 2 }, + { "llong", cons, 8 }, { "rdata", ppc_elf_rdata, 0 }, { "rodata", ppc_elf_rdata, 0 }, { "lcomm", ppc_elf_lcomm, 0 }, + { "localentry", ppc_elf_localentry, 0 }, + { "abiversion", ppc_elf_abiversion, 0 }, #endif #ifdef TE_PE @@ -1060,6 +1071,8 @@ const char *const md_shortopts = "um:"; #define OPTION_NOPS (OPTION_MD_BASE + 0) const struct option md_longopts[] = { {"nops", required_argument, NULL, OPTION_NOPS}, + {"ppc476-workaround", no_argument, &warn_476, 1}, + {"no-ppc476-workaround", no_argument, &warn_476, 0}, {NULL, no_argument, NULL, 0} }; const size_t md_longopts_size = sizeof (md_longopts); @@ -1238,6 +1251,9 @@ md_parse_option (int c, char *arg) } break; + case 0: + break; + default: return 0; } @@ -1311,7 +1327,8 @@ PowerPC options:\n\ -Qy, -Qn ignored\n")); #endif fprintf (stream, _("\ --nops=count when aligning, more than COUNT nops uses a branch\n")); +-nops=count when aligning, more than COUNT nops uses a branch\n\ +-ppc476-workaround warn if emitting data to code sections\n")); } /* Set ppc_cpu if it is not already set. */ @@ -1764,10 +1781,23 @@ ppc_insert_operand (unsigned long insn, right = max & -max; min = 0; - if ((operand->flags & PPC_OPERAND_SIGNED) != 0) + if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0) + { + /* Extend the allowed range for addis to [-65536, 65535]. + Similarly for some VLE high part insns. For 64-bit it + would be good to disable this for signed fields since the + value is sign extended into the high 32 bits of the register. + If the value is, say, an address, then we might care about + the high bits. However, gcc as of 2014-06 uses unsigned + values when loading the high part of 64-bit constants using + lis. + Use the same extended range for cmpli, to allow at least + [-32768, 65535]. */ + min = ~max & -right; + } + else if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { - if ((operand->flags & PPC_OPERAND_SIGNOPT) == 0) - max = (max >> 1) & -right; + max = (max >> 1) & -right; min = ~max & -right; } @@ -1923,6 +1953,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,19 +1964,24 @@ 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 ("localentry", BFD_RELOC_PPC64_ADDR64_LOCAL), + 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), MAP64 ("tprel@highesta", BFD_RELOC_PPC64_TPREL16_HIGHESTA), - { (char *) 0, 0, 0, 0, BFD_RELOC_UNUSED } + { (char *) 0, 0, 0, 0, BFD_RELOC_NONE } }; if (*str++ != '@') - return BFD_RELOC_UNUSED; + return BFD_RELOC_NONE; for (ch = *str, str2 = ident; (str2 < ident + sizeof (ident) - 1 @@ -2030,63 +2067,49 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p) return (bfd_reloc_code_real_type) reloc; } - return BFD_RELOC_UNUSED; + return BFD_RELOC_NONE; } -/* Like normal .long/.short/.word, except support @got, etc. - Clobbers input_line_pointer, checks end-of-line. */ -static void -ppc_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long, 8=.llong */) -{ - expressionS exp; - bfd_reloc_code_real_type reloc; - - if (is_it_end_of_statement ()) - { - demand_empty_rest_of_line (); - return; - } +/* Support @got, etc. on constants emitted via .short, .int etc. */ - do - { - expression (&exp); - if (*input_line_pointer == '@' - && (reloc = ppc_elf_suffix (&input_line_pointer, - &exp)) != BFD_RELOC_UNUSED) - { - reloc_howto_type *reloc_howto; - int size; +bfd_reloc_code_real_type +ppc_elf_parse_cons (expressionS *exp, unsigned int nbytes) +{ + expression (exp); + if (nbytes >= 2 && *input_line_pointer == '@') + return ppc_elf_suffix (&input_line_pointer, exp); + return BFD_RELOC_NONE; +} - reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc); - size = bfd_get_reloc_size (reloc_howto); +/* Warn when emitting data to code sections, unless we are emitting + a relocation that ld --ppc476-workaround uses to recognise data + *and* there was an unconditional branch prior to the data. */ - if (size > nbytes) - { - as_bad (_("%s relocations do not fit in %d bytes\n"), - reloc_howto->name, nbytes); - } - else - { - char *p; - int offset; - - p = frag_more (nbytes); - memset (p, 0, nbytes); - offset = 0; - if (target_big_endian) - offset = nbytes - size; - fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, - &exp, 0, reloc); - } - } - else - emit_expr (&exp, (unsigned int) nbytes); +void +ppc_elf_cons_fix_check (expressionS *exp ATTRIBUTE_UNUSED, + unsigned int nbytes, fixS *fix) +{ + if (warn_476 + && (now_seg->flags & SEC_CODE) != 0 + && (nbytes != 4 + || fix == NULL + || !(fix->fx_r_type == BFD_RELOC_32 + || fix->fx_r_type == BFD_RELOC_CTOR + || fix->fx_r_type == BFD_RELOC_32_PCREL) + || !(last_seg == now_seg && last_subseg == now_subseg) + || !((last_insn & (0x3f << 26)) == (18u << 26) + || ((last_insn & (0x3f << 26)) == (16u << 26) + && (last_insn & (0x14 << 21)) == (0x14 << 21)) + || ((last_insn & (0x3f << 26)) == (19u << 26) + && (last_insn & (0x3ff << 1)) == (16u << 1) + && (last_insn & (0x14 << 21)) == (0x14 << 21))))) + { + /* Flag that we've warned. */ + if (fix != NULL) + fix->fx_tcbit = 1; + + as_warn (_("data in executable section")); } - while (*input_line_pointer++ == ','); - - /* Put terminator back into stream. */ - input_line_pointer--; - demand_empty_rest_of_line (); } /* Solaris pseduo op to change to the .rodata section. */ @@ -2210,6 +2233,101 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED) demand_empty_rest_of_line (); } +/* Pseudo op to set symbol local entry point. */ +static void +ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED) +{ + char *name = input_line_pointer; + char c = get_symbol_end (); + char *p; + expressionS exp; + symbolS *sym; + asymbol *bfdsym; + elf_symbol_type *elfsym; + + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + *p = 0; + as_bad (_("expected comma after name `%s' in .localentry directive"), + name); + *p = c; + ignore_rest_of_line (); + return; + } + input_line_pointer++; + expression (&exp); + if (exp.X_op == O_absent) + { + as_bad (_("missing expression in .localentry directive")); + exp.X_op = O_constant; + exp.X_add_number = 0; + } + *p = 0; + sym = symbol_find_or_make (name); + *p = c; + + if (resolve_expression (&exp) + && exp.X_op == O_constant) + { + unsigned char encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number); + + if (exp.X_add_number != (offsetT) PPC64_LOCAL_ENTRY_OFFSET (encoded)) + as_bad (_(".localentry expression for `%s' " + "is not a valid power of 2"), S_GET_NAME (sym)); + else + { + bfdsym = symbol_get_bfdsym (sym); + elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym); + gas_assert (elfsym); + elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK; + elfsym->internal_elf_sym.st_other |= encoded; + if (ppc_abiversion == 0) + ppc_abiversion = 2; + } + } + else + as_bad (_(".localentry expression for `%s' " + "does not evaluate to a constant"), S_GET_NAME (sym)); + + demand_empty_rest_of_line (); +} + +/* Pseudo op to set ABI version. */ +static void +ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED) +{ + expressionS exp; + + expression (&exp); + if (exp.X_op == O_absent) + { + as_bad (_("missing expression in .abiversion directive")); + exp.X_op = O_constant; + exp.X_add_number = 0; + } + + if (resolve_expression (&exp) + && exp.X_op == O_constant) + ppc_abiversion = exp.X_add_number; + else + as_bad (_(".abiversion expression does not evaluate to a constant")); + demand_empty_rest_of_line (); +} + +/* Set ABI version in output file. */ +void +ppc_elf_end (void) +{ + if (ppc_obj64 && ppc_abiversion != 0) + { + elf_elfheader (stdoutput)->e_flags &= ~EF_PPC64_ABI; + elf_elfheader (stdoutput)->e_flags |= ppc_abiversion & EF_PPC64_ABI; + } +} + /* Validate any relocations emitted for -mrelocatable, possibly adding fixups for word relocations in writable segments, so we can adjust them at runtime. */ @@ -2226,8 +2344,7 @@ ppc_elf_validate_fix (fixS *fixp, segT seg) return; case SHLIB_MRELOCATABLE: - if (fixp->fx_r_type <= BFD_RELOC_UNUSED - && fixp->fx_r_type != BFD_RELOC_16_GOTOFF + if (fixp->fx_r_type != BFD_RELOC_16_GOTOFF && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF @@ -2727,12 +2844,12 @@ md_assemble (char *str) /* FIXME: these next two specifically specify 32/64 bit toc entries. We don't support them today. Is this the right way to say that? */ - toc_reloc = BFD_RELOC_UNUSED; + toc_reloc = BFD_RELOC_NONE; as_bad (_("unimplemented toc32 expression modifier")); break; case must_be_64: /* FIXME: see above. */ - toc_reloc = BFD_RELOC_UNUSED; + toc_reloc = BFD_RELOC_NONE; as_bad (_("unimplemented toc64 expression modifier")); break; default: @@ -2802,7 +2919,7 @@ md_assemble (char *str) bfd_reloc_code_real_type reloc; char *orig_str = str; - if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) + if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE) switch (reloc) { default: @@ -2810,55 +2927,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 */ @@ -2867,7 +3005,7 @@ md_assemble (char *str) } else { - bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED; + bfd_reloc_code_real_type reloc = BFD_RELOC_NONE; #ifdef OBJ_ELF if (ex.X_op == O_symbol && str[0] == '(') { @@ -2884,7 +3022,7 @@ md_assemble (char *str) expression (&tls_exp); if (tls_exp.X_op == O_symbol) { - reloc = BFD_RELOC_UNUSED; + reloc = BFD_RELOC_NONE; if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0) { reloc = BFD_RELOC_PPC_TLSGD; @@ -2895,7 +3033,7 @@ md_assemble (char *str) reloc = BFD_RELOC_PPC_TLSLD; input_line_pointer += 7; } - if (reloc != BFD_RELOC_UNUSED) + if (reloc != BFD_RELOC_NONE) { SKIP_WHITESPACE (); str = input_line_pointer; @@ -2912,7 +3050,7 @@ md_assemble (char *str) } } - if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) + if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE) { /* Some TLS tweaks. */ switch (reloc) @@ -3008,116 +3146,21 @@ md_assemble (char *str) break; } } - - /* For the absolute forms of branches, convert the PC - relative form back into the absolute. */ - if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) - { - switch (reloc) - { - case BFD_RELOC_PPC_B26: - reloc = BFD_RELOC_PPC_BA26; - break; - case BFD_RELOC_PPC_B16: - reloc = BFD_RELOC_PPC_BA16; - break; - case BFD_RELOC_PPC_B16_BRTAKEN: - reloc = BFD_RELOC_PPC_BA16_BRTAKEN; - break; - case BFD_RELOC_PPC_B16_BRNTAKEN: - reloc = BFD_RELOC_PPC_BA16_BRNTAKEN; - break; - default: - break; - } - } - - switch (reloc) - { - case BFD_RELOC_PPC_TOC16: - toc_reloc_types |= has_small_toc_reloc; - break; - case BFD_RELOC_PPC64_TOC16_LO: - case BFD_RELOC_PPC64_TOC16_HI: - case BFD_RELOC_PPC64_TOC16_HA: - toc_reloc_types |= has_large_toc_reloc; - break; - default: - break; - } - - if ((operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0) - { - switch (reloc) - { - case BFD_RELOC_16: - reloc = BFD_RELOC_PPC64_ADDR16_DS; - break; - case BFD_RELOC_LO16: - reloc = BFD_RELOC_PPC64_ADDR16_LO_DS; - break; - case BFD_RELOC_16_GOTOFF: - reloc = BFD_RELOC_PPC64_GOT16_DS; - break; - case BFD_RELOC_LO16_GOTOFF: - reloc = BFD_RELOC_PPC64_GOT16_LO_DS; - break; - case BFD_RELOC_LO16_PLTOFF: - reloc = BFD_RELOC_PPC64_PLT16_LO_DS; - break; - case BFD_RELOC_16_BASEREL: - reloc = BFD_RELOC_PPC64_SECTOFF_DS; - break; - case BFD_RELOC_LO16_BASEREL: - reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS; - break; - case BFD_RELOC_PPC_TOC16: - reloc = BFD_RELOC_PPC64_TOC16_DS; - break; - case BFD_RELOC_PPC64_TOC16_LO: - reloc = BFD_RELOC_PPC64_TOC16_LO_DS; - break; - case BFD_RELOC_PPC64_PLTGOT16: - reloc = BFD_RELOC_PPC64_PLTGOT16_DS; - break; - case BFD_RELOC_PPC64_PLTGOT16_LO: - reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS; - break; - case BFD_RELOC_PPC_DTPREL16: - reloc = BFD_RELOC_PPC64_DTPREL16_DS; - break; - case BFD_RELOC_PPC_DTPREL16_LO: - reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS; - break; - case BFD_RELOC_PPC_TPREL16: - reloc = BFD_RELOC_PPC64_TPREL16_DS; - break; - case BFD_RELOC_PPC_TPREL16_LO: - reloc = BFD_RELOC_PPC64_TPREL16_LO_DS; - break; - case BFD_RELOC_PPC_GOT_DTPREL16: - case BFD_RELOC_PPC_GOT_DTPREL16_LO: - case BFD_RELOC_PPC_GOT_TPREL16: - case BFD_RELOC_PPC_GOT_TPREL16_LO: - break; - default: - as_bad (_("unsupported relocation for DS offset field")); - break; - } - } } #endif /* OBJ_ELF */ - if (reloc != BFD_RELOC_UNUSED) + if (reloc != BFD_RELOC_NONE) ; /* Determine a BFD reloc value based on the operand information. We are only prepared to turn a few of the operands into relocs. */ - else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 + else if ((operand->flags & (PPC_OPERAND_RELATIVE + | PPC_OPERAND_ABSOLUTE)) != 0 && operand->bitm == 0x3fffffc && operand->shift == 0) reloc = BFD_RELOC_PPC_B26; - else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 + else if ((operand->flags & (PPC_OPERAND_RELATIVE + | PPC_OPERAND_ABSOLUTE)) != 0 && operand->bitm == 0xfffc && operand->shift == 0) reloc = BFD_RELOC_PPC_B16; @@ -3133,40 +3176,126 @@ md_assemble (char *str) && operand->bitm == 0x1fffffe && operand->shift == 0) reloc = BFD_RELOC_PPC_VLE_REL24; - else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 - && operand->bitm == 0x3fffffc - && operand->shift == 0) - reloc = BFD_RELOC_PPC_BA26; - else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 - && operand->bitm == 0xfffc - && operand->shift == 0) - reloc = BFD_RELOC_PPC_BA16; -#if defined (OBJ_XCOFF) || defined (OBJ_ELF) - else if ((operand->flags & PPC_OPERAND_PARENS) != 0 + else if ((operand->flags & PPC_OPERAND_NEGATIVE) == 0 && (operand->bitm & 0xfff0) == 0xfff0 && operand->shift == 0) { + reloc = BFD_RELOC_16; +#if defined OBJ_XCOFF || defined OBJ_ELF /* Note: the symbol may be not yet defined. */ - if (ppc_is_toc_sym (ex.X_add_symbol)) + if ((operand->flags & PPC_OPERAND_PARENS) != 0 + && ppc_is_toc_sym (ex.X_add_symbol)) { reloc = BFD_RELOC_PPC_TOC16; #ifdef OBJ_ELF - if (ppc_obj64 - && (operand->flags & PPC_OPERAND_DS) != 0) - reloc = BFD_RELOC_PPC64_TOC16_DS; + as_warn (_("assuming %s on symbol"), + ppc_obj64 ? "@toc" : "@xgot"); #endif } - else +#endif + } + + /* For the absolute forms of branches, convert the PC + relative form back into the absolute. */ + if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + { + switch (reloc) { - reloc = BFD_RELOC_16; + case BFD_RELOC_PPC_B26: + reloc = BFD_RELOC_PPC_BA26; + break; + case BFD_RELOC_PPC_B16: + reloc = BFD_RELOC_PPC_BA16; + break; #ifdef OBJ_ELF - if (ppc_obj64 - && (operand->flags & PPC_OPERAND_DS) != 0) - reloc = BFD_RELOC_PPC64_ADDR16_DS; + case BFD_RELOC_PPC_B16_BRTAKEN: + reloc = BFD_RELOC_PPC_BA16_BRTAKEN; + break; + case BFD_RELOC_PPC_B16_BRNTAKEN: + reloc = BFD_RELOC_PPC_BA16_BRNTAKEN; + break; #endif + default: + break; } } -#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */ + +#ifdef OBJ_ELF + switch (reloc) + { + case BFD_RELOC_PPC_TOC16: + toc_reloc_types |= has_small_toc_reloc; + break; + case BFD_RELOC_PPC64_TOC16_LO: + case BFD_RELOC_PPC64_TOC16_HI: + case BFD_RELOC_PPC64_TOC16_HA: + toc_reloc_types |= has_large_toc_reloc; + break; + default: + break; + } + + if (ppc_obj64 + && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0) + { + switch (reloc) + { + case BFD_RELOC_16: + reloc = BFD_RELOC_PPC64_ADDR16_DS; + break; + case BFD_RELOC_LO16: + reloc = BFD_RELOC_PPC64_ADDR16_LO_DS; + break; + case BFD_RELOC_16_GOTOFF: + reloc = BFD_RELOC_PPC64_GOT16_DS; + break; + case BFD_RELOC_LO16_GOTOFF: + reloc = BFD_RELOC_PPC64_GOT16_LO_DS; + break; + case BFD_RELOC_LO16_PLTOFF: + reloc = BFD_RELOC_PPC64_PLT16_LO_DS; + break; + case BFD_RELOC_16_BASEREL: + reloc = BFD_RELOC_PPC64_SECTOFF_DS; + break; + case BFD_RELOC_LO16_BASEREL: + reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS; + break; + case BFD_RELOC_PPC_TOC16: + reloc = BFD_RELOC_PPC64_TOC16_DS; + break; + case BFD_RELOC_PPC64_TOC16_LO: + reloc = BFD_RELOC_PPC64_TOC16_LO_DS; + break; + case BFD_RELOC_PPC64_PLTGOT16: + reloc = BFD_RELOC_PPC64_PLTGOT16_DS; + break; + case BFD_RELOC_PPC64_PLTGOT16_LO: + reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS; + break; + case BFD_RELOC_PPC_DTPREL16: + reloc = BFD_RELOC_PPC64_DTPREL16_DS; + break; + case BFD_RELOC_PPC_DTPREL16_LO: + reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS; + break; + case BFD_RELOC_PPC_TPREL16: + reloc = BFD_RELOC_PPC64_TPREL16_DS; + break; + case BFD_RELOC_PPC_TPREL16_LO: + reloc = BFD_RELOC_PPC64_TPREL16_LO_DS; + break; + case BFD_RELOC_PPC_GOT_DTPREL16: + case BFD_RELOC_PPC_GOT_DTPREL16_LO: + case BFD_RELOC_PPC_GOT_TPREL16: + case BFD_RELOC_PPC_GOT_TPREL16_LO: + break; + default: + as_bad (_("unsupported relocation for DS offset field")); + break; + } + } +#endif /* We need to generate a fixup for this expression. */ if (fc >= MAX_INSN_FIXUPS) @@ -3240,7 +3369,12 @@ md_assemble (char *str) ppc_apuinfo_section_add (PPC_APUINFO_CACHELCK, 1); if (opcode->flags & PPC_OPCODE_RFMCI) ppc_apuinfo_section_add (PPC_APUINFO_RFMCI, 1); - if (opcode->flags & PPC_OPCODE_VLE) + /* Only set the VLE flag if the instruction has been pulled via + the VLE instruction set. This way the flag is guaranteed to + be set for VLE-only instructions or for VLE-only processors, + however it'll remain clear for dual-mode instructions on + dual-mode and, more importantly, standard-mode processors. */ + if ((ppc_cpu & opcode->flags) == PPC_OPCODE_VLE) ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1); } #endif @@ -3272,6 +3406,9 @@ md_assemble (char *str) frag_now->insn_addr = addr_mod; frag_now->has_code = 1; md_number_to_chars (f, insn, insn_length); + last_insn = insn; + last_seg = now_seg; + last_subseg = now_subseg; #ifdef OBJ_ELF dwarf2_emit_insn (insn_length); @@ -3281,7 +3418,7 @@ md_assemble (char *str) for (i = 0; i < fc; i++) { fixS *fixP; - if (fixups[i].reloc != BFD_RELOC_UNUSED) + if (fixups[i].reloc != BFD_RELOC_NONE) { reloc_howto_type *reloc_howto; int size; @@ -3314,7 +3451,7 @@ md_assemble (char *str) insn_length, &fixups[i].exp, (operand->flags & PPC_OPERAND_RELATIVE) != 0, - BFD_RELOC_UNUSED); + BFD_RELOC_NONE); } fixP->fx_pcrel_adjust = fixups[i].opindex; } @@ -3429,6 +3566,8 @@ ppc_section_flags (flagword flags, bfd_vma attr ATTRIBUTE_UNUSED, int type) static void ppc_byte (int ignore ATTRIBUTE_UNUSED) { + int count = 0; + if (*input_line_pointer != '\"') { cons (1); @@ -3452,8 +3591,11 @@ ppc_byte (int ignore ATTRIBUTE_UNUSED) } FRAG_APPEND_1_CHAR (c); + ++count; } + if (warn_476 && count != 0 && (now_seg->flags & SEC_CODE) != 0) + as_warn (_("data in executable section")); demand_empty_rest_of_line (); } @@ -6159,6 +6301,22 @@ ppc_force_relocation (fixS *fix) case BFD_RELOC_24_PLT_PCREL: case BFD_RELOC_PPC64_TOC: return 1; + case BFD_RELOC_PPC_B26: + case BFD_RELOC_PPC_BA26: + case BFD_RELOC_PPC_B16: + case BFD_RELOC_PPC_BA16: + /* All branch fixups targeting a localentry symbol must + force a relocation. */ + if (fix->fx_addsy) + { + asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy); + elf_symbol_type *elfsym + = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym); + gas_assert (elfsym); + if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0) + return 1; + } + break; default: break; } @@ -6173,6 +6331,32 @@ ppc_force_relocation (fixS *fix) int ppc_fix_adjustable (fixS *fix) { + switch (fix->fx_r_type) + { + /* All branch fixups targeting a localentry symbol must + continue using the symbol. */ + case BFD_RELOC_PPC_B26: + case BFD_RELOC_PPC_BA26: + case BFD_RELOC_PPC_B16: + case BFD_RELOC_PPC_BA16: + case BFD_RELOC_PPC_B16_BRTAKEN: + case BFD_RELOC_PPC_B16_BRNTAKEN: + case BFD_RELOC_PPC_BA16_BRTAKEN: + case BFD_RELOC_PPC_BA16_BRNTAKEN: + if (fix->fx_addsy) + { + asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy); + elf_symbol_type *elfsym + = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym); + gas_assert (elfsym); + if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0) + return 0; + } + break; + default: + break; + } + return (fix->fx_r_type != BFD_RELOC_16_GOTOFF && fix->fx_r_type != BFD_RELOC_LO16_GOTOFF && fix->fx_r_type != BFD_RELOC_HI16_GOTOFF @@ -6296,7 +6480,7 @@ ppc_handle_align (struct frag *fragP) fixups we generated by the calls to fix_new_exp, above. */ void -md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +md_apply_fix (fixS *fixP, valueT *valP, segT seg) { valueT value = * valP; offsetT fieldval; @@ -6390,25 +6574,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 +6681,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: @@ -6614,7 +6828,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) return; gas_assert (fixP->fx_addsy != NULL); - if (fixP->fx_r_type == BFD_RELOC_UNUSED) + if (fixP->fx_r_type == BFD_RELOC_NONE) { char *sfile; unsigned int sline; @@ -6660,6 +6874,9 @@ 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: + case BFD_RELOC_PPC64_ADDR64_LOCAL: break; case BFD_RELOC_PPC_DTPMOD: @@ -6736,10 +6953,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: @@ -6772,6 +6993,16 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) if (fixP->fx_size && APPLY_RELOC) md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where, fieldval, fixP->fx_size); + if (warn_476 + && (seg->flags & SEC_CODE) != 0 + && fixP->fx_size == 4 + && fixP->fx_done + && !fixP->fx_tcbit + && (fixP->fx_r_type == BFD_RELOC_32 + || fixP->fx_r_type == BFD_RELOC_CTOR + || fixP->fx_r_type == BFD_RELOC_32_PCREL)) + as_warn_where (fixP->fx_file, fixP->fx_line, + _("data in executable section")); } /* We are only able to convert some relocs to pc-relative. */ |