/* 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-2015 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of GAS, the GNU Assembler.
#ifdef OBJ_ELF
#include "elf/ppc.h"
+#include "elf/ppc64.h"
#include "dwarf2dbg.h"
#endif
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;
#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
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;
\f
/* The target specific pseudo-ops which we support. */
#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
#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);
}
break;
+ case 0:
+ break;
+
default:
return 0;
}
-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"));
}
\f
/* Set ppc_cpu if it is not already set. */
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;
}
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),
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
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 (exp.X_op == O_symbol
- && *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. */
align2 = 0;
record_alignment (bss_section, align2);
- subseg_set (bss_section, 0);
+ subseg_set (bss_section, 1);
if (align2)
frag_align (align2, 0, 0);
if (S_GET_SEGMENT (symbolP) == bss_section)
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. */
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
/* 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:
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:
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 */
}
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] == '(')
{
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;
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;
}
}
- 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)
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;
&& 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)
{
- if (ppc_is_toc_sym (ex.X_add_symbol))
+ reloc = BFD_RELOC_16;
+#if defined OBJ_XCOFF || defined OBJ_ELF
+ /* Note: the symbol may be not yet defined. */
+ 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)
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
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);
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;
insn_length,
&fixups[i].exp,
(operand->flags & PPC_OPERAND_RELATIVE) != 0,
- BFD_RELOC_UNUSED);
+ BFD_RELOC_NONE);
}
fixP->fx_pcrel_adjust = fixups[i].opindex;
}
static void
ppc_byte (int ignore ATTRIBUTE_UNUSED)
{
+ int count = 0;
+
if (*input_line_pointer != '\"')
{
cons (1);
}
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 ();
}
\f
char *lcomm_name;
char lcomm_endc;
- if (size <= 4)
- align = 2;
- else
- align = 3;
-
/* The third argument to .lcomm appears to be the real local
common symbol to create. References to the symbol named in
the first argument are turned into references to the third
lcomm_sym = symbol_find_or_make (lcomm_name);
*input_line_pointer = lcomm_endc;
+
+ /* The fourth argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ {
+ if (size <= 4)
+ align = 2;
+ else
+ align = 3;
+ }
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 2;
+ }
+ }
}
*end_name = '\0';
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;
}
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
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;
value = fixP->fx_offset;
fixP->fx_done = 1;
}
+
+ /* During parsing of instructions, a TOC16 reloc is generated for
+ instructions such as 'lwz RT,SYM(RB)' if SYM is a symbol defined
+ in the toc. But at parse time, SYM may be not yet defined, so
+ check again here. */
+ if (fixP->fx_r_type == BFD_RELOC_16
+ && fixP->fx_addsy != NULL
+ && ppc_is_toc_sym (fixP->fx_addsy))
+ fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
#endif
}
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;
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:
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;
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:
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:
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. */
fixP->fx_addnumber =
- bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy))
- S_GET_VALUE (ppc_toc_csect);
+ /* Set *valP to avoid errors. */
+ *valP = value;
#endif
}
#endif