/* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
- Copyright (C) 1994-2021 Free Software Foundation, Inc.
+ Copyright (C) 1994-2024 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
This file is part of GAS, the GNU Assembler.
static bool reg_names_p = TARGET_REG_NAMES_P;
-static void ppc_macro (char *, const struct powerpc_macro *);
static void ppc_byte (int);
#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
static void ppc_csect (int);
static void ppc_dwsect (int);
static void ppc_change_csect (symbolS *, offsetT);
+static void ppc_file (int);
static void ppc_function (int);
static void ppc_extern (int);
+static void ppc_globl (int);
static void ppc_lglobl (int);
static void ppc_ref (int);
static void ppc_section (int);
static void ppc_toc (int);
static void ppc_xcoff_cons (int);
static void ppc_vbyte (int);
+static void ppc_weak (int);
+static void ppc_GNU_visibility (int);
#endif
#ifdef OBJ_ELF
{ "ei", ppc_biei, 1 },
{ "es", ppc_es, 0 },
{ "extern", ppc_extern, 0 },
+ { "file", ppc_file, 0 },
{ "function", ppc_function, 0 },
+ { "globl", ppc_globl, 0 },
{ "lglobl", ppc_lglobl, 0 },
{ "ref", ppc_ref, 0 },
{ "rename", ppc_rename, 0 },
{ "word", ppc_xcoff_cons, 1 },
{ "short", ppc_xcoff_cons, 1 },
{ "vbyte", ppc_vbyte, 0 },
+ { "weak", ppc_weak, 0 },
+
+ /* Enable GNU syntax for symbol visibility. */
+ {"internal", ppc_GNU_visibility, SYM_V_INTERNAL},
+ {"hidden", ppc_GNU_visibility, SYM_V_HIDDEN},
+ {"protected", ppc_GNU_visibility, SYM_V_PROTECTED},
#endif
#ifdef OBJ_ELF
/* Structure to hold information about predefined registers. */
struct pd_reg
{
- const char *name;
+ char name[6];
unsigned short value;
unsigned short flags;
};
{ "dec", 22, PPC_OPERAND_SPR },
{ "dsisr", 18, PPC_OPERAND_SPR },
+ /* Dense Math Registers. */
+ { "dm0", 0, PPC_OPERAND_DMR },
+ { "dm1", 1, PPC_OPERAND_DMR },
+ { "dm2", 2, PPC_OPERAND_DMR },
+ { "dm3", 3, PPC_OPERAND_DMR },
+ { "dm4", 4, PPC_OPERAND_DMR },
+ { "dm5", 5, PPC_OPERAND_DMR },
+ { "dm6", 6, PPC_OPERAND_DMR },
+ { "dm7", 7, PPC_OPERAND_DMR },
+
/* Floating point registers */
{ "f.0", 0, PPC_OPERAND_FPR },
{ "f.1", 1, PPC_OPERAND_FPR },
{ "xer", 1, PPC_OPERAND_SPR }
};
-#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
-
/* Given NAME, find the register number associated with that name, return
the integer value associated with the given name or -1 on failure. */
return NULL;
}
-/*
- * Summary of register_name.
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out: A expressionS.
- * The operand may have been a register: in this case, X_op == O_register,
- * X_add_number is set to the register number, and truth is returned.
- * Input_line_pointer->(next non-blank) char after operand, or is in its
- * original state.
- */
+/* Called for a non-symbol, non-number operand. Handles %reg. */
-static bool
-register_name (expressionS *expressionP)
+void
+md_operand (expressionS *expressionP)
{
const struct pd_reg *reg;
char *name;
char *start;
char c;
- /* Find the spelling of the operand. */
- start = name = input_line_pointer;
- if (name[0] == '%' && ISALPHA (name[1]))
- name = ++input_line_pointer;
+ if (input_line_pointer[0] != '%' || !ISALPHA (input_line_pointer[1]))
+ return;
- else if (!reg_names_p || !ISALPHA (name[0]))
- return false;
+ start = input_line_pointer;
+ ++input_line_pointer;
c = get_symbol_name (&name);
- reg = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
-
- /* Put back the delimiting char. */
+ reg = reg_name_search (pre_defined_registers,
+ ARRAY_SIZE (pre_defined_registers), name);
*input_line_pointer = c;
- /* Look to see if it's in the register table. */
if (reg != NULL)
{
expressionP->X_op = O_register;
expressionP->X_add_number = reg->value;
expressionP->X_md = reg->flags;
-
- /* Make the rest nice. */
- expressionP->X_add_symbol = NULL;
- expressionP->X_op_symbol = NULL;
- return true;
}
-
- /* Reset the line as if we had not done anything. */
- input_line_pointer = start;
- return false;
+ else
+ input_line_pointer = start;
}
-\f
-/* This function is called for each symbol seen in an expression. It
- handles the special parsing which PowerPC assemblers are supposed
- to use for condition codes. */
/* Whether to do the special parsing. */
static bool cr_operand;
-/* Names to recognize in a condition code. This table is sorted. */
-static const struct pd_reg cr_names[] =
+/* Extra names to recognise in a condition code. This table is sorted. */
+static const struct pd_reg cr_cond[] =
{
- { "cr0", 0, PPC_OPERAND_CR_REG },
- { "cr1", 1, PPC_OPERAND_CR_REG },
- { "cr2", 2, PPC_OPERAND_CR_REG },
- { "cr3", 3, PPC_OPERAND_CR_REG },
- { "cr4", 4, PPC_OPERAND_CR_REG },
- { "cr5", 5, PPC_OPERAND_CR_REG },
- { "cr6", 6, PPC_OPERAND_CR_REG },
- { "cr7", 7, PPC_OPERAND_CR_REG },
{ "eq", 2, PPC_OPERAND_CR_BIT },
{ "gt", 1, PPC_OPERAND_CR_BIT },
{ "lt", 0, PPC_OPERAND_CR_BIT },
{ "un", 3, PPC_OPERAND_CR_BIT }
};
-/* Parsing function. This returns non-zero if it recognized an
- expression. */
+/* This function is called for each symbol seen in an expression. It
+ handles the special parsing which PowerPC assemblers are supposed
+ to use for condition codes, and recognises other registers when
+ -mregnames. */
-int
-ppc_parse_name (const char *name, expressionS *exp)
+void
+ppc_parse_name (const char *name, expressionS *exp, enum expr_mode mode)
{
- const struct pd_reg *reg;
+ const struct pd_reg *reg = NULL;
- if (! cr_operand)
- return 0;
-
- if (*name == '%')
- ++name;
- reg = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
- name);
- if (reg == NULL)
- return 0;
+ if (cr_operand)
+ reg = reg_name_search (cr_cond, ARRAY_SIZE (cr_cond), name);
+ if (reg == NULL && (cr_operand || reg_names_p))
+ reg = reg_name_search (pre_defined_registers,
+ ARRAY_SIZE (pre_defined_registers), name);
+ if (reg != NULL)
+ {
+ exp->X_op = O_register;
+ exp->X_add_number = reg->value;
+ exp->X_md = reg->flags;
+ return;
+ }
- exp->X_op = O_register;
- exp->X_add_number = reg->value;
- exp->X_md = reg->flags;
+ /* The following replaces code in expr.c operand() after the
+ md_parse_name call. There is too much difference between targets
+ in the way X_md is used to move this code into expr.c. If you
+ do, you'll get failures on x86 due to uninitialised X_md fields,
+ failures on alpha and other targets due to creating register
+ symbols as O_constant rather than O_register, and failures on arc
+ and others due to expecting expr() to leave X_md alone. */
+ symbolS *sym = symbol_find_or_make (name);
- return 1;
+ /* If we have an absolute symbol or a reg, then we know its value
+ now. Copy the symbol value expression to propagate X_md. */
+ bool done = false;
+ if (mode != expr_defer
+ && !S_FORCE_RELOC (sym, 0))
+ {
+ segT segment = S_GET_SEGMENT (sym);
+ if (segment == absolute_section || segment == reg_section)
+ {
+ resolve_symbol_value (sym);
+ *exp = *symbol_get_value_expression (sym);
+ done = true;
+ }
+ }
+ if (!done)
+ {
+ exp->X_op = O_symbol;
+ exp->X_add_symbol = sym;
+ exp->X_add_number = 0;
+ }
}
/* Propagate X_md and check register expressions. This is to support
}
/* Accept the above plus <cr bit>, and <cr bit> plus the above. */
- if (right->X_op == O_register
+ if (op == O_add
&& left->X_op == O_register
- && op == O_add
+ && right->X_op == O_register
&& ((right->X_md == PPC_OPERAND_CR_BIT
&& left->X_md == (PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT))
|| (right->X_md == (PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT)
}
/* Accept reg +/- constant. */
- if (left->X_op == O_register
+ if (left && left->X_op == O_register
&& !((op == O_add || op == O_subtract) && right->X_op == O_constant))
as_warn (_("invalid register expression"));
/* Whether to target xcoff64/elf64. */
static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
+/* A separate obstack for use by ppc_hash, so that we can quickly
+ throw away hash table memory . */
+struct obstack insn_obstack;
+
/* Opcode hash table. */
static htab_t ppc_hash;
-/* Macro hash table. */
-static htab_t ppc_macro_hash;
-
#ifdef OBJ_ELF
/* What type of shared library support to use. */
static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE;
/* Initialize a ppc_xcoff_section.
Dummy symbols are used to ensure the position of .text over .data
- and .tdata. These symbols won't be output. */
+ and .tdata. Moreover, they allow all algorithms here to be sure that
+ csects isn't NULL. These symbols won't be output. */
static void
-ppc_init_xcoff_section (struct ppc_xcoff_section *s, segT seg,
- bool need_dummy)
+ppc_init_xcoff_section (struct ppc_xcoff_section *s, segT seg)
{
s->segment = seg;
s->next_subsegment = 2;
- if (need_dummy)
- {
- s->csects = symbol_make ("dummy\001");
- symbol_get_tc (s->csects)->within = s->csects;
- }
+ s->csects = symbol_make ("dummy\001");
+ symbol_get_tc (s->csects)->within = s->csects;
}
/* The current csect. */
fprintf (stream, _("\
-mpower10, -mpwr10 generate code for Power10 architecture\n"));
fprintf (stream, _("\
+-mpower11, -mpwr11 generate code for Power11 architecture\n"));
+ fprintf (stream, _("\
+-mlibresoc generate code for Libre-SOC architecture\n"));
+ fprintf (stream, _("\
+-mfuture generate code for 'future' architecture\n"));
+ fprintf (stream, _("\
-mcell generate code for Cell Broadband Engine architecture\n"));
fprintf (stream, _("\
-mcom generate code for Power/PowerPC common instructions\n"));
enum bfd_architecture
ppc_arch (void)
{
- const char *default_cpu = TARGET_CPU;
ppc_set_cpu ();
+#ifdef OBJ_ELF
+ return bfd_arch_powerpc;
+#else
if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
return bfd_arch_powerpc;
if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
return bfd_arch_rs6000;
if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
{
- if (strcmp (default_cpu, "rs6000") == 0)
- return bfd_arch_rs6000;
- else if (startswith (default_cpu, "powerpc"))
+ const char *default_cpu = TARGET_CPU;
+ if (startswith (default_cpu, "powerpc"))
return bfd_arch_powerpc;
}
-
- as_fatal (_("neither Power nor PowerPC opcodes were selected."));
- return bfd_arch_unknown;
+ return bfd_arch_rs6000;
+#endif
}
unsigned long
static bool
insn_validate (const struct powerpc_opcode *op)
{
- const unsigned char *o;
+ const ppc_opindex_t *o;
uint64_t omask = op->mask;
/* The mask had better not trim off opcode bits. */
if (operand->shift == (int) PPC_OPSHIFT_INV)
{
const char *errmsg;
- int64_t val;
+ uint64_t val;
errmsg = NULL;
val = -1;
if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
val = -val;
- else if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
- val += 1;
mask = (*operand->insert) (0, val, ppc_cpu, &errmsg);
}
+ else if (operand->shift == (int) PPC_OPSHIFT_SH6)
+ mask = (0x1f << 11) | 0x2;
else if (operand->shift >= 0)
mask = operand->bitm << operand->shift;
else
return false;
}
-/* Insert opcodes and macros into hash tables. Called at startup and
- for .machine pseudo. */
+static void *
+insn_calloc (size_t n, size_t size)
+{
+ size_t amt = n * size;
+ void *ret = obstack_alloc (&insn_obstack, amt);
+ memset (ret, 0, amt);
+ return ret;
+}
+
+/* Insert opcodes into hash tables. Called at startup and for
+ .machine pseudo. */
static void
ppc_setup_opcodes (void)
{
const struct powerpc_opcode *op;
const struct powerpc_opcode *op_end;
- const struct powerpc_macro *macro;
- const struct powerpc_macro *macro_end;
bool bad_insn = false;
if (ppc_hash != NULL)
- htab_delete (ppc_hash);
- if (ppc_macro_hash != NULL)
- htab_delete (ppc_macro_hash);
+ {
+ htab_delete (ppc_hash);
+ _obstack_free (&insn_obstack, NULL);
+ }
+
+ obstack_begin (&insn_obstack, chunksize);
/* Insert the opcodes into a hash table. */
- ppc_hash = str_htab_create ();
+ ppc_hash = htab_create_alloc (5000, hash_string_tuple, eq_string_tuple,
+ NULL, insn_calloc, NULL);
if (ENABLE_CHECKING)
{
unsigned int i;
/* An index into powerpc_operands is stored in struct fix
- fx_pcrel_adjust which is 8 bits wide. */
- gas_assert (num_powerpc_operands < 256);
+ fx_pcrel_adjust which is a 16 bit field. */
+ gas_assert (num_powerpc_operands <= PPC_OPINDEX_MAX + 1);
/* Check operand masks. Code here and in the disassembler assumes
all the 1's in the mask are contiguous. */
for (i = 0; i < num_powerpc_operands; ++i)
{
uint64_t mask = powerpc_operands[i].bitm;
+ unsigned long flags = powerpc_operands[i].flags;
uint64_t right_bit;
unsigned int j;
+ if ((flags & PPC_OPERAND_PLUS1) != 0
+ && (flags & PPC_OPERAND_NONZERO) != 0)
+ as_bad ("mutually exclusive operand flags");
+
right_bit = mask & -mask;
mask += right_bit;
right_bit = mask & -mask;
unsigned int new_opcode = PPC_OP (op[0].opcode);
#ifdef PRINT_OPCODE_TABLE
- printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+ printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx"
+ "\tmask: 0x%llx\tflags: 0x%llx\n",
op->name, (unsigned int) (op - powerpc_opcodes),
new_opcode, (unsigned long long) op->opcode,
- (unsigned long long) op->mask, (unsigned long long) op->flags);
+ (unsigned long long) op->mask,
+ (unsigned long long) op->flags);
#endif
/* The major opcodes had better be sorted. Code in the disassembler
unsigned int new_opcode = PPC_PREFIX_SEG (op[0].opcode);
#ifdef PRINT_OPCODE_TABLE
- printf ("%-14s\t#%04u\tmajor op/2: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+ printf ("%-14s\t#%04u\tmajor op/2: 0x%x\top: 0x%llx"
+ "\tmask: 0x%llx\tflags: 0x%llx\n",
op->name, (unsigned int) (op - prefix_opcodes),
new_opcode, (unsigned long long) op->opcode,
- (unsigned long long) op->mask, (unsigned long long) op->flags);
+ (unsigned long long) op->mask,
+ (unsigned long long) op->flags);
#endif
/* The major opcodes had better be sorted. Code in the disassembler
for (op = prefix_opcodes; op < op_end; op++)
str_hash_insert (ppc_hash, op->name, op, 0);
- op_end = vle_opcodes + vle_num_opcodes;
- for (op = vle_opcodes; op < op_end; op++)
+ if ((ppc_cpu & (PPC_OPCODE_VLE | PPC_OPCODE_ANY)) != 0)
{
- if (ENABLE_CHECKING)
+ unsigned int prev_seg = 0;
+ unsigned int seg;
+
+ op_end = vle_opcodes + vle_num_opcodes;
+ for (op = vle_opcodes; op < op_end; op++)
{
- unsigned new_seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
+ if (ENABLE_CHECKING)
+ {
+ seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
#ifdef PRINT_OPCODE_TABLE
- printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
- op->name, (unsigned int) (op - vle_opcodes),
- (unsigned int) new_seg, (unsigned long long) op->opcode,
- (unsigned long long) op->mask, (unsigned long long) op->flags);
+ printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx"
+ "\tmask: 0x%llx\tflags: 0x%llx\n",
+ op->name, (unsigned int) (op - vle_opcodes),
+ (unsigned int) seg, (unsigned long long) op->opcode,
+ (unsigned long long) op->mask,
+ (unsigned long long) op->flags);
#endif
- /* The major opcodes had better be sorted. Code in the disassembler
- assumes the insns are sorted according to major opcode. */
- if (op != vle_opcodes
- && new_seg < VLE_OP_TO_SEG (VLE_OP (op[-1].opcode, op[-1].mask)))
- {
- as_bad (_("major opcode is not sorted for %s"), op->name);
- bad_insn = true;
+ if (seg < prev_seg)
+ {
+ as_bad (_("major opcode is not sorted for %s"), op->name);
+ bad_insn = true;
+ }
+ prev_seg = seg;
+ bad_insn |= insn_validate (op);
}
- bad_insn |= insn_validate (op);
+ str_hash_insert (ppc_hash, op->name, op, 0);
}
+ }
- if ((ppc_cpu & op->flags) != 0
- && !(ppc_cpu & op->deprecated)
- && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
+ /* LSP instructions */
+ if ((ppc_cpu & (PPC_OPCODE_LSP | PPC_OPCODE_ANY)) != 0)
+ {
+ unsigned int prev_seg = 0;
+ unsigned int seg;
+ op_end = lsp_opcodes + lsp_num_opcodes;
+ for (op = lsp_opcodes; op < op_end; op++)
{
- as_bad (_("duplicate %s"), op->name);
- bad_insn = true;
+ if (ENABLE_CHECKING)
+ {
+ seg = LSP_OP_TO_SEG (op->opcode);
+ if (seg < prev_seg)
+ {
+ as_bad (_("opcode is not sorted for %s"), op->name);
+ bad_insn = true;
+ }
+ prev_seg = seg;
+ bad_insn |= insn_validate (op);
+ }
+
+ str_hash_insert (ppc_hash, op->name, op, 0);
}
}
/* SPE2 instructions */
- if ((ppc_cpu & PPC_OPCODE_SPE2) == PPC_OPCODE_SPE2)
+ if ((ppc_cpu & (PPC_OPCODE_SPE2 | PPC_OPCODE_ANY)) != 0)
{
+ unsigned int prev_seg = 0;
+ unsigned int seg;
op_end = spe2_opcodes + spe2_num_opcodes;
for (op = spe2_opcodes; op < op_end; op++)
{
if (ENABLE_CHECKING)
{
- if (op != spe2_opcodes)
+ seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
+ if (seg < prev_seg)
{
- unsigned old_seg, new_seg;
-
- old_seg = VLE_OP (op[-1].opcode, op[-1].mask);
- old_seg = VLE_OP_TO_SEG (old_seg);
- new_seg = VLE_OP (op[0].opcode, op[0].mask);
- new_seg = VLE_OP_TO_SEG (new_seg);
-
- /* The major opcodes had better be sorted. Code in the
- disassembler assumes the insns are sorted according to
- major opcode. */
- if (new_seg < old_seg)
- {
as_bad (_("major opcode is not sorted for %s"), op->name);
bad_insn = true;
- }
}
-
+ prev_seg = seg;
bad_insn |= insn_validate (op);
}
- if ((ppc_cpu & op->flags) != 0
- && !(ppc_cpu & op->deprecated)
- && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
- {
- as_bad (_("duplicate %s"), op->name);
- bad_insn = true;
- }
+ str_hash_insert (ppc_hash, op->name, op, 0);
}
-
- for (op = spe2_opcodes; op < op_end; op++)
- str_hash_insert (ppc_hash, op->name, op, 0);
}
- /* Insert the macros into a hash table. */
- ppc_macro_hash = str_htab_create ();
-
- macro_end = powerpc_macros + powerpc_num_macros;
- for (macro = powerpc_macros; macro < macro_end; macro++)
- if (((macro->flags & ppc_cpu) != 0
- || (ppc_cpu & PPC_OPCODE_ANY) != 0)
- && str_hash_insert (ppc_macro_hash, macro->name, macro, 0) != NULL)
- {
- as_bad (_("duplicate %s"), macro->name);
- bad_insn = true;
- }
-
if (bad_insn)
abort ();
}
/* Create XCOFF sections with .text in first, as it's creating dummy symbols
to serve as initial csects. This forces the text csects to precede the
data csects. These symbols will not be output. */
- ppc_init_xcoff_section (&ppc_xcoff_text_section, text_section, true);
- ppc_init_xcoff_section (&ppc_xcoff_data_section, data_section, true);
- ppc_init_xcoff_section (&ppc_xcoff_bss_section, bss_section, false);
+ ppc_init_xcoff_section (&ppc_xcoff_text_section, text_section);
+ ppc_init_xcoff_section (&ppc_xcoff_data_section, data_section);
+ ppc_init_xcoff_section (&ppc_xcoff_bss_section, bss_section);
#endif
}
+void
+ppc_md_end (void)
+{
+ if (ppc_hash)
+ {
+ htab_delete (ppc_hash);
+ _obstack_free (&insn_obstack, NULL);
+ }
+ ppc_hash = NULL;
+}
+
void
ppc_cleanup (void)
{
max = (max >> 1) & -right;
min = ~max & -right;
}
+ else if ((operand->flags & PPC_OPERAND_NONZERO) != 0)
+ {
+ ++min;
+ ++max;
+ }
if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
max++;
if (errmsg != (const char *) NULL)
as_bad_where (file, line, "%s", errmsg);
}
- else if (operand->shift >= 0)
- insn |= (val & operand->bitm) << operand->shift;
else
- insn |= (val & operand->bitm) >> -operand->shift;
+ {
+ if ((operand->flags & PPC_OPERAND_NONZERO) != 0)
+ --val;
+ if (operand->shift >= 0)
+ insn |= (val & operand->bitm) << operand->shift;
+ else
+ insn |= (val & operand->bitm) >> -operand->shift;
+ }
return insn;
}
{
int reloc = ptr->reloc;
- if (!ppc_obj64 && exp_p->X_add_number != 0)
+ if (!ppc_obj64 && (exp_p->X_op == O_big || exp_p->X_add_number != 0))
{
switch (reloc)
{
input_line_pointer = str;
expression (&new_exp);
- if (new_exp.X_op == O_constant)
+ if (new_exp.X_op == O_constant && exp_p->X_op != O_big)
{
exp_p->X_add_number += new_exp.X_add_number;
str = input_line_pointer;
}
-
- if (&input_line_pointer != str_p)
- input_line_pointer = orig_line;
+ input_line_pointer = orig_line;
}
*str_p = str;
exp_p->X_add_symbol = &abs_symbol;
}
+ if (reloc == BFD_RELOC_PPC64_REL24_NOTOC
+ && (ppc_cpu & PPC_OPCODE_POWER10) == 0)
+ reloc = BFD_RELOC_PPC64_REL24_P9NOTOC;
+
return (bfd_reloc_code_real_type) reloc;
}
/* Set ABI version in output file. */
void
-ppc_elf_end (void)
+ppc_elf_md_finish (void)
{
if (ppc_obj64 && ppc_abiversion != 0)
{
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
&symbol_rootP, &symbol_lastP);
+ /* Update last csect symbol. */
symbol_get_tc (ppc_current_csect)->within = sym;
- symbol_get_tc (sym)->within = ppc_current_csect;
+
+ /* Some labels like .bs are using within differently.
+ So avoid changing it, if it's already set. */
+ if (symbol_get_tc (sym)->within == NULL)
+ symbol_get_tc (sym)->within = ppc_current_csect;
}
#endif
case BFD_RELOC_32_PCREL:
case BFD_RELOC_32_PLT_PCREL:
case BFD_RELOC_PPC64_REL24_NOTOC:
+ case BFD_RELOC_PPC64_REL24_P9NOTOC:
#ifndef OBJ_XCOFF
case BFD_RELOC_PPC_B16:
#endif
char *s;
const struct powerpc_opcode *opcode;
uint64_t insn;
- const unsigned char *opindex_ptr;
+ const ppc_opindex_t *opindex_ptr;
int need_paren;
int next_opindex;
struct ppc_fixup fixups[MAX_INSN_FIXUPS];
opcode = (const struct powerpc_opcode *) str_hash_find (ppc_hash, str);
if (opcode == (const struct powerpc_opcode *) NULL)
{
- const struct powerpc_macro *macro;
-
- macro = (const struct powerpc_macro *) str_hash_find (ppc_macro_hash,
- str);
- if (macro == (const struct powerpc_macro *) NULL)
- as_bad (_("unrecognized opcode: `%s'"), str);
- else
- ppc_macro (s, macro);
-
+ as_bad (_("unrecognized opcode: `%s'"), str);
ppc_clear_labels ();
return;
}
{
if (num_optional_operands == 0)
{
- const unsigned char *optr;
+ const ppc_opindex_t *optr;
int total = 0;
int provided = 0;
int omitted;
}
if (--num_optional_provided < 0)
{
- int64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu,
- num_optional_provided);
+ uint64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu,
+ num_optional_provided);
if (operand->insert)
{
insn = (*operand->insert) (insn, val, ppc_cpu, &errmsg);
/* Gather the operand. */
hold = input_line_pointer;
input_line_pointer = str;
-
- if ((reg_names_p
- && (((operand->flags & PPC_OPERAND_CR_BIT) != 0)
- || ((operand->flags & PPC_OPERAND_CR_REG) != 0)))
- || !register_name (&ex))
- {
- char save_lex = lex_type['%'];
-
- if (((operand->flags & PPC_OPERAND_CR_REG) != 0)
- || (operand->flags & PPC_OPERAND_CR_BIT) != 0)
- {
- cr_operand = true;
- lex_type['%'] |= LEX_BEGIN_NAME;
- }
- expression (&ex);
- cr_operand = false;
- lex_type['%'] = save_lex;
- }
-
+ cr_operand = ((operand->flags & PPC_OPERAND_CR_BIT) != 0
+ || (operand->flags & PPC_OPERAND_CR_REG) != 0);
+ expression (&ex);
+ cr_operand = false;
str = input_line_pointer;
input_line_pointer = hold;
+ resolve_register (&ex);
+
if (ex.X_op == O_illegal)
as_bad (_("illegal operand"));
else if (ex.X_op == O_absent)
& ~operand->flags
& (PPC_OPERAND_GPR | PPC_OPERAND_FPR | PPC_OPERAND_VR
| PPC_OPERAND_VSR | PPC_OPERAND_CR_BIT | PPC_OPERAND_CR_REG
- | PPC_OPERAND_SPR | PPC_OPERAND_GQR | PPC_OPERAND_ACC)) != 0
+ | PPC_OPERAND_SPR | PPC_OPERAND_GQR | PPC_OPERAND_ACC
+ | PPC_OPERAND_DMR)) != 0
&& !((ex.X_md & PPC_OPERAND_GPR) != 0
&& ex.X_add_number != 0
&& (operand->flags & PPC_OPERAND_GPR_0) != 0))
insn = ppc_insert_operand (insn, operand, ex.X_add_number,
ppc_cpu, (char *) NULL, 0);
}
- else if (ex.X_op == O_constant)
+ else if (ex.X_op == O_constant
+ || (ex.X_op == O_big && ex.X_add_number > 0))
{
+ uint64_t val;
+ if (ex.X_op == O_constant)
+ {
+ val = ex.X_add_number;
+ if (sizeof (ex.X_add_number) < sizeof (val)
+ && (ex.X_add_number < 0) != ex.X_extrabit)
+ val = val ^ ((addressT) -1 ^ (uint64_t) -1);
+ }
+ else
+ val = generic_bignum_to_int64 ();
#ifdef OBJ_ELF
/* Allow @HA, @L, @H on constants. */
- bfd_reloc_code_real_type reloc;
char *orig_str = str;
+ bfd_reloc_code_real_type reloc = ppc_elf_suffix (&str, &ex);
- if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
+ if (ex.X_op == O_constant)
+ {
+ val = ex.X_add_number;
+ if (sizeof (ex.X_add_number) < sizeof (val)
+ && (ex.X_add_number < 0) != ex.X_extrabit)
+ val = val ^ ((addressT) -1 ^ (uint64_t) -1);
+ }
+ if (reloc != BFD_RELOC_NONE)
switch (reloc)
{
default:
break;
case BFD_RELOC_LO16:
- ex.X_add_number &= 0xffff;
+ val &= 0xffff;
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
case BFD_RELOC_HI16:
if (REPORT_OVERFLOW_HI && ppc_obj64)
{
/* PowerPC64 @h is tested for overflow. */
- ex.X_add_number = (addressT) ex.X_add_number >> 16;
+ val = val >> 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;
+ uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1;
+ val = (val ^ sign) - sign;
}
break;
}
/* Fallthru */
case BFD_RELOC_PPC64_ADDR16_HIGH:
- ex.X_add_number = PPC_HI (ex.X_add_number);
+ val = PPC_HI (val);
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
case BFD_RELOC_HI16_S:
if (REPORT_OVERFLOW_HI && ppc_obj64)
{
/* PowerPC64 @ha is tested for overflow. */
- ex.X_add_number
- = ((addressT) ex.X_add_number + 0x8000) >> 16;
+ val = (val + 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;
+ uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1;
+ val = (val ^ sign) - sign;
}
break;
}
/* Fallthru */
case BFD_RELOC_PPC64_ADDR16_HIGHA:
- ex.X_add_number = PPC_HA (ex.X_add_number);
+ val = PPC_HA (val);
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
case BFD_RELOC_PPC64_HIGHER:
- ex.X_add_number = PPC_HIGHER (ex.X_add_number);
+ val = PPC_HIGHER (val);
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
case BFD_RELOC_PPC64_HIGHER_S:
- ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
+ val = PPC_HIGHERA (val);
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
case BFD_RELOC_PPC64_HIGHEST:
- ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
+ val = PPC_HIGHEST (val);
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
case BFD_RELOC_PPC64_HIGHEST_S:
- ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
+ val = PPC_HIGHESTA (val);
if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
- ex.X_add_number = SEX16 (ex.X_add_number);
+ val = SEX16 (val);
break;
}
#endif /* OBJ_ELF */
- insn = ppc_insert_operand (insn, operand, ex.X_add_number,
- ppc_cpu, (char *) NULL, 0);
+ insn = ppc_insert_operand (insn, operand, val, ppc_cpu, NULL, 0);
}
else
{
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)
+ if (ppc_cpu & opcode->flags & PPC_OPCODE_VLE)
{
ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
if (elf_section_data (now_seg) != NULL)
insn_length = 4;
if ((ppc_cpu & PPC_OPCODE_VLE) != 0 && PPC_OP_SE_VLE (insn))
insn_length = 2;
- else if ((opcode->flags & PPC_OPCODE_POWER10) != 0
- && PPC_PREFIX_P (insn))
+ else if (PPC_PREFIX_P (insn))
{
struct insn_label_list *l;
boundaries. */
frag_align_code (6, 4);
record_alignment (now_seg, 6);
+#ifdef OBJ_XCOFF
+ /* Update alignment of the containing csect. */
+ if (symbol_get_tc (ppc_current_csect)->align < 6)
+ symbol_get_tc (ppc_current_csect)->align = 6;
+#endif
/* Update "dot" in any expressions used by this instruction, and
a label attached to the instruction. By "attached" we mean
fixP->fx_pcrel_adjust = fixups[i].opindex;
}
}
-
-/* Handle a macro. Gather all the operands, transform them as
- described by the macro, and call md_assemble recursively. All the
- operands are separated by commas; we don't accept parentheses
- around operands here. */
-
-static void
-ppc_macro (char *str, const struct powerpc_macro *macro)
-{
- char *operands[10];
- unsigned int count;
- char *s;
- unsigned int len;
- const char *format;
- unsigned int arg;
- char *send;
- char *complete;
-
- /* Gather the users operands into the operands array. */
- count = 0;
- s = str;
- while (1)
- {
- if (count >= sizeof operands / sizeof operands[0])
- break;
- operands[count++] = s;
- s = strchr (s, ',');
- if (s == (char *) NULL)
- break;
- *s++ = '\0';
- }
-
- if (count != macro->operands)
- {
- as_bad (_("wrong number of operands"));
- return;
- }
-
- /* Work out how large the string must be (the size is unbounded
- because it includes user input). */
- len = 0;
- format = macro->format;
- while (*format != '\0')
- {
- if (*format != '%')
- {
- ++len;
- ++format;
- }
- else
- {
- arg = strtol (format + 1, &send, 10);
- know (send != format && arg < count);
- len += strlen (operands[arg]);
- format = send;
- }
- }
-
- /* Put the string together. */
- complete = s = XNEWVEC (char, len + 1);
- format = macro->format;
- while (*format != '\0')
- {
- if (*format != '%')
- *s++ = *format++;
- else
- {
- arg = strtol (format + 1, &send, 10);
- strcpy (s, operands[arg]);
- s += strlen (s);
- format = send;
- }
- }
- *s = '\0';
-
- /* Assemble the constructed instruction. */
- md_assemble (complete);
- free (complete);
-}
\f
#ifdef OBJ_ELF
/* For ELF, add support for SHT_ORDERED. */
to handle symbol suffixes for such symbols. */
static bool ppc_stab_symbol;
+/* Retrieve the visiblity input for pseudo-ops having ones. */
+static unsigned short
+ppc_xcoff_get_visibility (void) {
+ SKIP_WHITESPACE();
+
+ if (startswith (input_line_pointer, "exported"))
+ {
+ input_line_pointer += 8;
+ return SYM_V_EXPORTED;
+ }
+
+ if (startswith (input_line_pointer, "hidden"))
+ {
+ input_line_pointer += 6;
+ return SYM_V_HIDDEN;
+ }
+
+ if (startswith (input_line_pointer, "internal"))
+ {
+ input_line_pointer += 8;
+ return SYM_V_INTERNAL;
+ }
+
+ if (startswith (input_line_pointer, "protected"))
+ {
+ input_line_pointer += 9;
+ return SYM_V_PROTECTED;
+ }
+
+ return 0;
+}
+
+/* Retrieve visiblity using GNU syntax. */
+static void ppc_GNU_visibility (int visibility) {
+ int c;
+ char *name;
+ symbolS *symbolP;
+ coff_symbol_type *coffsym;
+
+ do
+ {
+ if ((name = read_symbol_name ()) == NULL)
+ break;
+ symbolP = symbol_find_or_make (name);
+ free (name);
+ coffsym = coffsymbol (symbol_get_bfdsym (symbolP));
+
+ coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+ coffsym->native->u.syment.n_type |= visibility;
+
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer ++;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
/* The .comm and .lcomm pseudo-ops for XCOFF. XCOFF puts common
symbols in the .bss segment as though they were local common
symbols, and uses a different smclas. The native Aix 4.3.3 assembler
symbolS *lcomm_sym = NULL;
symbolS *sym;
char *pfrag;
+ unsigned short visibility = 0;
struct ppc_xcoff_section *section;
endc = get_symbol_name (&name);
as_warn (_("ignoring bad alignment"));
align = 2;
}
+
+ /* The fourth argument to .comm is the visibility. */
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ visibility = ppc_xcoff_get_visibility ();
+ if (!visibility)
+ {
+ as_bad (_("Unknown visibility field in .comm"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
}
}
else
section = &ppc_xcoff_tbss_section;
if (!ppc_xcoff_section_is_initialized (section))
{
- ppc_init_xcoff_section (section,
- subseg_new (".tbss", 0), false);
+ ppc_init_xcoff_section (section, subseg_new (".tbss", 0));
bfd_set_section_flags (section->segment,
SEC_ALLOC | SEC_THREAD_LOCAL);
seg_info (section->segment)->bss = 1;
symbol_get_frag (lcomm_sym)->fr_offset += size;
}
+ if (!lcomm && visibility)
+ {
+ /* Add visibility to .comm symbol. */
+ coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+ coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+ coffsym->native->u.syment.n_type |= visibility;
+ }
+
subseg_set (current_seg, current_subseg);
demand_empty_rest_of_line ();
/* Create .tdata section if not yet done. */
if (!ppc_xcoff_section_is_initialized (section))
{
- ppc_init_xcoff_section (section, subseg_new (".tdata", 0),
- true);
+ ppc_init_xcoff_section (section, subseg_new (".tdata", 0));
bfd_set_section_flags (section->segment, SEC_ALLOC
| SEC_LOAD | SEC_RELOC | SEC_DATA
| SEC_THREAD_LOCAL);
/* Create .tbss section if not yet done. */
if (!ppc_xcoff_section_is_initialized (section))
{
- ppc_init_xcoff_section (section, subseg_new (".tbss", 0),
- false);
+ ppc_init_xcoff_section (section, subseg_new (".tbss", 0));
bfd_set_section_flags (section->segment, SEC_ALLOC |
SEC_THREAD_LOCAL);
seg_info (section->segment)->bss = 1;
flagword oldflags;
const struct xcoff_dwsect_name *dw = &xcoff_dwsect_names[idx];
- sec = subseg_new (dw->name, subseg);
+ sec = subseg_new (dw->xcoff_name, subseg);
oldflags = bfd_section_flags (sec);
if (oldflags == SEC_NO_FLAGS)
{
else
{
/* Create a new dw subsection. */
- subseg = XNEW (struct dw_subsection);
+ subseg = XCNEW (struct dw_subsection);
if (opt_label == NULL)
{
ppc_extern (int ignore ATTRIBUTE_UNUSED)
{
char *name;
- char endc;
+ symbolS *sym;
- endc = get_symbol_name (&name);
+ if ((name = read_symbol_name ()) == NULL)
+ return;
- (void) symbol_find_or_make (name);
+ sym = symbol_find_or_make (name);
+ free (name);
- (void) restore_line_pointer (endc);
+ if (*input_line_pointer == ',')
+ {
+ unsigned short visibility;
+ coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+
+ input_line_pointer++;
+ visibility = ppc_xcoff_get_visibility ();
+ if (!visibility)
+ {
+ as_bad (_("Unknown visibility field in .extern"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+ coffsym->native->u.syment.n_type |= visibility;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* XCOFF semantic for .globl says that the second parameter is
+ the symbol visibility. */
+
+static void
+ppc_globl (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ symbolS *sym;
+
+ if ((name = read_symbol_name ()) == NULL)
+ return;
+
+ sym = symbol_find_or_make (name);
+ free (name);
+ S_SET_EXTERNAL (sym);
+
+ if (*input_line_pointer == ',')
+ {
+ unsigned short visibility;
+ coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+
+ input_line_pointer++;
+ visibility = ppc_xcoff_get_visibility ();
+ if (!visibility)
+ {
+ as_bad (_("Unknown visibility field in .globl"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+ coffsym->native->u.syment.n_type |= visibility;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* XCOFF semantic for .weak says that the second parameter is
+ the symbol visibility. */
+
+static void
+ppc_weak (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ symbolS *sym;
+
+ if ((name = read_symbol_name ()) == NULL)
+ return;
+
+ sym = symbol_find_or_make (name);
+ free (name);
+ S_SET_WEAK (sym);
+
+ if (*input_line_pointer == ',')
+ {
+ unsigned short visibility;
+ coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+
+ input_line_pointer++;
+ visibility = ppc_xcoff_get_visibility ();
+ if (!visibility)
+ {
+ as_bad (_("Unknown visibility field in .weak"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+ coffsym->native->u.syment.n_type |= visibility;
+ }
demand_empty_rest_of_line ();
}
as_bad (_(".stabx of storage class stsym must be within .bs/.es"));
symbol_get_tc (sym)->within = ppc_current_block;
- symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block;
}
}
demand_empty_rest_of_line ();
}
+/* The .file pseudo-op. On XCOFF, .file can have several parameters
+ which are being added to the symbol table to provide additional
+ information. */
+
+static void
+ppc_file (int ignore ATTRIBUTE_UNUSED)
+{
+ char *sfname, *s1 = NULL, *s2 = NULL, *s3 = NULL;
+ int length, auxnb = 1;
+
+ /* Some assemblers tolerate immediately following '"'. */
+ if ((sfname = demand_copy_string (&length)) != 0)
+ {
+ coff_symbol_type *coffsym;
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ s1 = demand_copy_string (&length);
+ auxnb++;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ s2 = demand_copy_string (&length);
+ auxnb++;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ s3 = demand_copy_string (&length);
+ auxnb++;
+ }
+ }
+ }
+
+ /* Use coff dot_file creation and adjust auxiliary entries. */
+ c_dot_file_symbol (sfname);
+ S_SET_NUMBER_AUXILIARY (symbol_rootP, auxnb);
+ coffsym = coffsymbol (symbol_get_bfdsym (symbol_rootP));
+ coffsym->native[1].u.auxent.x_file.x_ftype = XFT_FN;
+
+ if (s1)
+ {
+ coffsym->native[2].u.auxent.x_file.x_ftype = XFT_CT;
+ coffsym->native[2].extrap = s1;
+ }
+ if (s2)
+ {
+ coffsym->native[3].u.auxent.x_file.x_ftype = XFT_CV;
+ coffsym->native[3].extrap = s2;
+ }
+ if (s3)
+ {
+ coffsym->native[4].u.auxent.x_file.x_ftype = XFT_CD;
+ coffsym->native[4].extrap = s3;
+ }
+
+ demand_empty_rest_of_line ();
+ }
+}
+
/* The .function pseudo-op. This takes several arguments. The first
argument seems to be the external name of the symbol. The second
argument seems to be the label for the start of the function. gcc
expression (& exp);
if (*input_line_pointer == ',')
{
- /* The fifth argument is the function size. */
+ /* The fifth argument is the function size.
+ If it's omitted, the size will be the containing csect.
+ This will be donce during ppc_frob_symtab. */
++input_line_pointer;
symbol_get_tc (ext_sym)->u.size
= symbol_new ("L0\001", absolute_section,
}
void
-ppc_xcoff_end (void)
+ppc_xcoff_md_finish (void)
{
int i;
if (cpu_string != NULL)
{
ppc_cpu_t old_cpu = ppc_cpu;
- ppc_cpu_t new_cpu;
char *p;
for (p = cpu_string; *p != 0; p++)
else
ppc_cpu = cpu_history[--curr_hist];
}
- else if ((new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, cpu_string)) != 0)
- ppc_cpu = new_cpu;
else
- as_bad (_("invalid machine `%s'"), cpu_string);
+ {
+ ppc_cpu_t new_cpu;
+ /* Not using the global "sticky" variable here results in
+ none of the extra functional unit command line options,
+ -many, -maltivec, -mspe, -mspe2, -mvle, -mvsx, being in
+ force after selecting a new cpu with .machine.
+ ".machine altivec" and other extra functional unit
+ options do not count as a new machine, instead they add
+ to currently selected opcodes. */
+ ppc_cpu_t machine_sticky = 0;
+ /* Unfortunately, some versions of gcc emit a .machine
+ directive very near the start of the compiler's assembly
+ output file. This is bad because it overrides user -Wa
+ cpu selection. Worse, there are versions of gcc that
+ emit the *wrong* cpu, not even respecting the -mcpu given
+ to gcc. See gcc pr101393. And to compound the problem,
+ as of 20220222 gcc doesn't pass the correct cpu option to
+ gas on the command line. See gcc pr59828. Hack around
+ this by keeping sticky options for an early .machine. */
+ asection *sec;
+ for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
+ {
+ segment_info_type *info = seg_info (sec);
+ /* Are the frags for this section perturbed from their
+ initial state? Even .align will count here. */
+ if (info != NULL
+ && (info->frchainP->frch_root != info->frchainP->frch_last
+ || info->frchainP->frch_root->fr_type != rs_fill
+ || info->frchainP->frch_root->fr_fix != 0))
+ break;
+ }
+ new_cpu = ppc_parse_cpu (ppc_cpu,
+ sec == NULL ? &sticky : &machine_sticky,
+ cpu_string);
+ if (new_cpu != 0)
+ ppc_cpu = new_cpu;
+ else
+ as_bad (_("invalid machine `%s'"), cpu_string);
+ }
if (ppc_cpu != old_cpu)
ppc_setup_opcodes ();
/* Discard symbols that should not be included in the output symbol
table. */
if (! symbol_used_in_reloc_p (sym)
+ && S_GET_STORAGE_CLASS (sym) != C_DWARF
&& ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0
|| (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
&& ! symbol_get_tc (sym)->output
if (SF_GET_FUNCTION (sym))
{
- if (ppc_last_function != (symbolS *) NULL)
- as_bad (_("two .function pseudo-ops with no intervening .ef"));
+ /* Make sure coff_last_function is reset. Otherwise, we won't create
+ the auxent for the next function. */
+ coff_last_function = 0;
ppc_last_function = sym;
if (symbol_get_tc (sym)->u.size != (symbolS *) NULL)
{
SA_SET_SYM_FSIZE (sym,
(long) S_GET_VALUE (symbol_get_tc (sym)->u.size));
}
+ else
+ {
+ /* Size of containing csect. */
+ symbolS* within = symbol_get_tc (sym)->within;
+ coff_symbol_type *csect = coffsymbol (symbol_get_bfdsym (within));
+ combined_entry_type *csectaux
+ = &csect->native[S_GET_NUMBER_AUXILIARY(within)];
+
+ SA_SET_SYM_FSIZE (sym, csectaux->u.auxent.x_csect.x_scnlen.u64);
+ }
}
else if (S_GET_STORAGE_CLASS (sym) == C_FCN
&& strcmp (S_GET_NAME (sym), ".ef") == 0)
|| S_GET_STORAGE_CLASS (sym) == C_HIDEXT)
{
int i;
- union internal_auxent *a;
+ combined_entry_type *a;
/* Create a csect aux. */
i = S_GET_NUMBER_AUXILIARY (sym);
S_SET_NUMBER_AUXILIARY (sym, i + 1);
- a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].u.auxent;
+ a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1];
if (symbol_get_tc (sym)->symbol_class == XMC_TC0)
{
/* This is the TOC table. */
know (strcmp (S_GET_NAME (sym), "TOC") == 0);
- a->x_csect.x_scnlen.l = 0;
- a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ a->u.auxent.x_csect.x_scnlen.u64 = 0;
+ a->u.auxent.x_csect.x_smtyp = (2 << 3) | XTY_SD;
}
else if (symbol_get_tc (sym)->subseg != 0)
{
/* This is a csect symbol. x_scnlen is the size of the
csect. */
if (symbol_get_tc (sym)->next == (symbolS *) NULL)
- a->x_csect.x_scnlen.l = (bfd_section_size (S_GET_SEGMENT (sym))
- - S_GET_VALUE (sym));
+ a->u.auxent.x_csect.x_scnlen.u64
+ = bfd_section_size (S_GET_SEGMENT (sym)) - S_GET_VALUE (sym);
else
{
resolve_symbol_value (symbol_get_tc (sym)->next);
- a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next)
- - S_GET_VALUE (sym));
+ a->u.auxent.x_csect.x_scnlen.u64
+ = S_GET_VALUE (symbol_get_tc (sym)->next) - S_GET_VALUE (sym);
}
- a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD;
+ if (symbol_get_tc (sym)->symbol_class == XMC_BS
+ || symbol_get_tc (sym)->symbol_class == XMC_UL)
+ a->u.auxent.x_csect.x_smtyp
+ = (symbol_get_tc (sym)->align << 3) | XTY_CM;
+ else
+ a->u.auxent.x_csect.x_smtyp
+ = (symbol_get_tc (sym)->align << 3) | XTY_SD;
}
else if (S_GET_SEGMENT (sym) == bss_section
|| S_GET_SEGMENT (sym) == ppc_xcoff_tbss_section.segment)
{
/* This is a common symbol. */
- a->x_csect.x_scnlen.l = symbol_get_frag (sym)->fr_offset;
- a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM;
+ a->u.auxent.x_csect.x_scnlen.u64 = symbol_get_frag (sym)->fr_offset;
+ a->u.auxent.x_csect.x_smtyp
+ = (symbol_get_tc (sym)->align << 3) | XTY_CM;
if (S_GET_SEGMENT (sym) == ppc_xcoff_tbss_section.segment)
symbol_get_tc (sym)->symbol_class = XMC_UL;
else if (S_IS_EXTERNAL (sym))
/* This is an absolute symbol. The csect will be created by
ppc_adjust_symtab. */
ppc_saw_abs = true;
- a->x_csect.x_smtyp = XTY_LD;
+ a->u.auxent.x_csect.x_smtyp = XTY_LD;
if (symbol_get_tc (sym)->symbol_class == -1)
symbol_get_tc (sym)->symbol_class = XMC_XO;
}
else if (! S_IS_DEFINED (sym))
{
/* This is an external symbol. */
- a->x_csect.x_scnlen.l = 0;
- a->x_csect.x_smtyp = XTY_ER;
+ a->u.auxent.x_csect.x_scnlen.u64 = 0;
+ a->u.auxent.x_csect.x_smtyp = XTY_ER;
}
else if (ppc_is_toc_sym (sym))
{
|| (!ppc_is_toc_sym (next)))
{
if (ppc_after_toc_frag == (fragS *) NULL)
- a->x_csect.x_scnlen.l = (bfd_section_size (data_section)
- - S_GET_VALUE (sym));
+ a->u.auxent.x_csect.x_scnlen.u64
+ = bfd_section_size (data_section) - S_GET_VALUE (sym);
else
- a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address
- - S_GET_VALUE (sym));
+ a->u.auxent.x_csect.x_scnlen.u64
+ = ppc_after_toc_frag->fr_address - S_GET_VALUE (sym);
}
else
{
resolve_symbol_value (next);
- a->x_csect.x_scnlen.l = (S_GET_VALUE (next)
- - S_GET_VALUE (sym));
+ a->u.auxent.x_csect.x_scnlen.u64
+ = S_GET_VALUE (next) - S_GET_VALUE (sym);
}
- a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ a->u.auxent.x_csect.x_smtyp = (2 << 3) | XTY_SD;
}
else
{
if (csect == (symbolS *) NULL)
{
as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym));
- a->x_csect.x_scnlen.l = 0;
+ a->u.auxent.x_csect.x_scnlen.u64 = 0;
}
else
{
csect = symbol_get_tc (csect)->next;
}
- a->x_csect.x_scnlen.p =
- coffsymbol (symbol_get_bfdsym (csect))->native;
- coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].fix_scnlen =
- 1;
+ a->u.auxent.x_csect.x_scnlen.p
+ = coffsymbol (symbol_get_bfdsym (csect))->native;
+ a->fix_scnlen = 1;
}
- a->x_csect.x_smtyp = XTY_LD;
+ a->u.auxent.x_csect.x_smtyp = XTY_LD;
}
- a->x_csect.x_parmhash = 0;
- a->x_csect.x_snhash = 0;
+ a->u.auxent.x_csect.x_parmhash = 0;
+ a->u.auxent.x_csect.x_snhash = 0;
if (symbol_get_tc (sym)->symbol_class == -1)
- a->x_csect.x_smclas = XMC_PR;
+ a->u.auxent.x_csect.x_smclas = XMC_PR;
else
- a->x_csect.x_smclas = symbol_get_tc (sym)->symbol_class;
- a->x_csect.x_stab = 0;
- a->x_csect.x_snstab = 0;
+ a->u.auxent.x_csect.x_smclas = symbol_get_tc (sym)->symbol_class;
+ a->u.auxent.x_csect.x_stab = 0;
+ a->u.auxent.x_csect.x_snstab = 0;
/* Don't let the COFF backend resort these symbols. */
symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END;
return 0;
}
-/* Adjust the symbol table. This creates csect symbols for all
- absolute symbols. */
+/* Adjust the symbol table. */
void
ppc_adjust_symtab (void)
{
symbolS *sym;
+ symbolS *anchorSym;
+
+ /* Make sure C_DWARF symbols come right after C_FILE.
+ As the C_FILE might not be defined yet and as C_DWARF
+ might already be ordered, we insert them before the
+ first symbol which isn't a C_FILE or a C_DWARF. */
+ for (anchorSym = symbol_rootP; anchorSym != NULL;
+ anchorSym = symbol_next (anchorSym))
+ {
+ if (S_GET_STORAGE_CLASS (anchorSym) != C_FILE
+ && S_GET_STORAGE_CLASS (anchorSym) != C_DWARF)
+ break;
+ }
+
+ sym = anchorSym;
+ while (sym != NULL)
+ {
+ if (S_GET_STORAGE_CLASS (sym) != C_DWARF)
+ {
+ sym = symbol_next (sym);
+ continue;
+ }
+
+ symbolS* tsym = sym;
+ sym = symbol_next (sym);
+
+ symbol_remove (tsym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (tsym, anchorSym, &symbol_rootP, &symbol_lastP);
+ }
+
+ /* Create csect symbols for all absolute symbols. */
if (! ppc_saw_abs)
return;
{
symbolS *csect;
int i;
- union internal_auxent *a;
+ combined_entry_type *a;
if (S_GET_SEGMENT (sym) != absolute_section)
continue;
S_SET_STORAGE_CLASS (csect, C_HIDEXT);
i = S_GET_NUMBER_AUXILIARY (csect);
S_SET_NUMBER_AUXILIARY (csect, i + 1);
- a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1].u.auxent;
- a->x_csect.x_scnlen.l = 0;
- a->x_csect.x_smtyp = XTY_SD;
- a->x_csect.x_parmhash = 0;
- a->x_csect.x_snhash = 0;
- a->x_csect.x_smclas = XMC_XO;
- a->x_csect.x_stab = 0;
- a->x_csect.x_snstab = 0;
+ a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1];
+ a->u.auxent.x_csect.x_scnlen.u64 = 0;
+ a->u.auxent.x_csect.x_smtyp = XTY_SD;
+ a->u.auxent.x_csect.x_parmhash = 0;
+ a->u.auxent.x_csect.x_snhash = 0;
+ a->u.auxent.x_csect.x_smclas = XMC_XO;
+ a->u.auxent.x_csect.x_stab = 0;
+ a->u.auxent.x_csect.x_snstab = 0;
symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP);
i = S_GET_NUMBER_AUXILIARY (sym);
- a = &coffsymbol (symbol_get_bfdsym (sym))->native[i].u.auxent;
- a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native;
- coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1;
+ a = &coffsymbol (symbol_get_bfdsym (sym))->native[i];
+ a->u.auxent.x_csect.x_scnlen.p
+ = coffsymbol (symbol_get_bfdsym (csect))->native;
+ a->fix_scnlen = 1;
}
ppc_saw_abs = false;
#ifdef OBJ_XCOFF
+/* Return the surrending csect for sym when possible. */
+
+static symbolS*
+ppc_get_csect_to_adjust (symbolS *sym)
+{
+ if (sym == NULL)
+ return NULL;
+
+ valueT val = resolve_symbol_value (sym);
+ TC_SYMFIELD_TYPE *tc = symbol_get_tc (sym);
+ segT symseg = S_GET_SEGMENT (sym);
+
+ if (tc->subseg == 0
+ && tc->symbol_class != XMC_TC0
+ && tc->symbol_class != XMC_TC
+ && tc->symbol_class != XMC_TE
+ && symseg != bss_section
+ && symseg != ppc_xcoff_tbss_section.segment
+ /* Don't adjust if this is a reloc in the toc section. */
+ && (symseg != data_section
+ || ppc_toc_csect == NULL
+ || val < ppc_toc_frag->fr_address
+ || (ppc_after_toc_frag != NULL
+ && val >= ppc_after_toc_frag->fr_address)))
+ {
+ symbolS* csect = tc->within;
+
+ /* If the symbol was not declared by a label (eg: a section symbol),
+ use the section instead of the csect. This doesn't happen in
+ normal AIX assembly code. */
+ if (csect == NULL)
+ csect = seg_info (symseg)->sym;
+
+ return csect;
+ }
+
+ return NULL;
+}
+
/* This is called to see whether a fixup should be adjusted to use a
section symbol. We take the opportunity to change a fixup against
a symbol in the TOC subsegment into a reloc against the
{
valueT val = resolve_symbol_value (fix->fx_addsy);
segT symseg = S_GET_SEGMENT (fix->fx_addsy);
- TC_SYMFIELD_TYPE *tc;
+ symbolS* csect;
if (symseg == absolute_section)
return 0;
}
/* Possibly adjust the reloc to be against the csect. */
- tc = symbol_get_tc (fix->fx_addsy);
- if (tc->subseg == 0
- && tc->symbol_class != XMC_TC0
- && tc->symbol_class != XMC_TC
- && tc->symbol_class != XMC_TE
- && symseg != bss_section
- && symseg != ppc_xcoff_tbss_section.segment
- /* Don't adjust if this is a reloc in the toc section. */
- && (symseg != data_section
- || ppc_toc_csect == NULL
- || val < ppc_toc_frag->fr_address
- || (ppc_after_toc_frag != NULL
- && val >= ppc_after_toc_frag->fr_address)))
+ if ((csect = ppc_get_csect_to_adjust (fix->fx_addsy)) != NULL)
{
- symbolS *csect = tc->within;
-
- /* If the symbol was not declared by a label (eg: a section symbol),
- use the section instead of the csect. This doesn't happen in
- normal AIX assembly code. */
- if (csect == NULL)
- csect = seg_info (symseg)->sym;
-
fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
fix->fx_addsy = csect;
+ }
- return 0;
+ if ((csect = ppc_get_csect_to_adjust (fix->fx_subsy)) != NULL)
+ {
+ fix->fx_offset -= resolve_symbol_value (fix->fx_subsy)
+ - symbol_get_frag (csect)->fr_address;
+ fix->fx_subsy = csect;
}
/* Adjust a reloc against a .lcomm symbol to be against the base
.lcomm. */
if (symseg == bss_section
- && ! S_IS_EXTERNAL (fix->fx_addsy))
+ && ! S_IS_EXTERNAL (fix->fx_addsy)
+ && symbol_get_tc (fix->fx_addsy)->subseg == 0)
{
symbolS *sy = symbol_get_frag (fix->fx_addsy)->fr_symbol;
int
ppc_force_relocation (fixS *fix)
{
- /* Branch prediction relocations must force a relocation, as must
- the vtable description relocs. */
switch (fix->fx_r_type)
{
case BFD_RELOC_PPC_B16_BRTAKEN:
case BFD_RELOC_PPC_BA16_BRNTAKEN:
case BFD_RELOC_24_PLT_PCREL:
case BFD_RELOC_PPC64_TOC:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16_GOTOFF:
+ case BFD_RELOC_HI16_GOTOFF:
+ case BFD_RELOC_HI16_S_GOTOFF:
+ case BFD_RELOC_LO16_PLTOFF:
+ case BFD_RELOC_HI16_PLTOFF:
+ case BFD_RELOC_HI16_S_PLTOFF:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_16_BASEREL:
+ case BFD_RELOC_LO16_BASEREL:
+ case BFD_RELOC_HI16_BASEREL:
+ case BFD_RELOC_HI16_S_BASEREL:
+ case BFD_RELOC_PPC_TOC16:
+ case BFD_RELOC_PPC64_TOC16_LO:
+ case BFD_RELOC_PPC64_TOC16_HI:
+ case BFD_RELOC_PPC64_TOC16_HA:
+ case BFD_RELOC_PPC64_PLTGOT16:
+ case BFD_RELOC_PPC64_PLTGOT16_LO:
+ case BFD_RELOC_PPC64_PLTGOT16_HI:
+ case BFD_RELOC_PPC64_PLTGOT16_HA:
+ case BFD_RELOC_PPC64_GOT16_DS:
+ case BFD_RELOC_PPC64_GOT16_LO_DS:
+ case BFD_RELOC_PPC64_PLT16_LO_DS:
+ case BFD_RELOC_PPC64_SECTOFF_DS:
+ case BFD_RELOC_PPC64_SECTOFF_LO_DS:
+ case BFD_RELOC_PPC64_TOC16_DS:
+ case BFD_RELOC_PPC64_TOC16_LO_DS:
+ case BFD_RELOC_PPC64_PLTGOT16_DS:
+ case BFD_RELOC_PPC64_PLTGOT16_LO_DS:
+ case BFD_RELOC_PPC_EMB_NADDR16:
+ case BFD_RELOC_PPC_EMB_NADDR16_LO:
+ case BFD_RELOC_PPC_EMB_NADDR16_HI:
+ case BFD_RELOC_PPC_EMB_NADDR16_HA:
+ case BFD_RELOC_PPC_EMB_SDAI16:
+ case BFD_RELOC_PPC_EMB_SDA2I16:
+ case BFD_RELOC_PPC_EMB_SDA2REL:
+ case BFD_RELOC_PPC_EMB_SDA21:
+ case BFD_RELOC_PPC_EMB_MRKREF:
+ case BFD_RELOC_PPC_EMB_RELSEC16:
+ case BFD_RELOC_PPC_EMB_RELST_LO:
+ case BFD_RELOC_PPC_EMB_RELST_HI:
+ case BFD_RELOC_PPC_EMB_RELST_HA:
+ case BFD_RELOC_PPC_EMB_BIT_FLD:
+ case BFD_RELOC_PPC_EMB_RELSDA:
+ case BFD_RELOC_PPC_VLE_SDA21:
+ case BFD_RELOC_PPC_VLE_SDA21_LO:
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+ case BFD_RELOC_PPC64_PLT_PCREL34:
+ case BFD_RELOC_PPC64_GOT_PCREL34:
return 1;
case BFD_RELOC_PPC_B26:
case BFD_RELOC_PPC_BA26:
case BFD_RELOC_PPC_B16:
case BFD_RELOC_PPC_BA16:
case BFD_RELOC_PPC64_REL24_NOTOC:
+ case BFD_RELOC_PPC64_REL24_P9NOTOC:
/* All branch fixups targeting a localentry symbol must
force a relocation. */
if (fix->fx_addsy)
case BFD_RELOC_PPC_BA16_BRTAKEN:
case BFD_RELOC_PPC_BA16_BRNTAKEN:
case BFD_RELOC_PPC64_REL24_NOTOC:
+ case BFD_RELOC_PPC64_REL24_P9NOTOC:
if (fix->fx_addsy)
{
asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
if (fixP->fx_pcrel_adjust != 0)
{
/* This is a fixup on an instruction. */
- int opindex = fixP->fx_pcrel_adjust & 0xff;
+ ppc_opindex_t opindex = fixP->fx_pcrel_adjust & PPC_OPINDEX_MAX;
operand = &powerpc_operands[opindex];
#ifdef OBJ_XCOFF
case BFD_RELOC_PPC64_TLSM:
gas_assert (fixP->fx_addsy != NULL);
S_SET_THREAD_LOCAL (fixP->fx_addsy);
- fieldval = 0;
break;
- /* TLSML relocations are targeting a XMC_TC symbol named
- "_$TLSML". We can't check earlier because the relocation
- can target any symbol name which will be latter .rename
- to "_$TLSML". */
+ /* Officially, R_TLSML relocations must be from a TOC entry
+ targeting itself. In practice, this TOC entry is always
+ named (or .rename) "_$TLSML".
+ Thus, as it doesn't seem possible to retrieve the symbol
+ being relocated here, we simply check that the symbol
+ targeted by R_TLSML is indeed a TOC entry named "_$TLSML".
+ FIXME: Find a way to correctly check R_TLSML relocations
+ as described above. */
case BFD_RELOC_PPC_TLSML:
case BFD_RELOC_PPC64_TLSML:
gas_assert (fixP->fx_addsy != NULL);
- if (strcmp (symbol_get_tc (fixP->fx_addsy)->real_name, "_$TLSML") != 0)
- {
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("R_TLSML relocation doesn't target a "
- "symbol named \"_$TLSML\". %s"), S_GET_NAME(fixP->fx_addsy));
- }
+ if ((symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC
+ || symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TE)
+ && strcmp (symbol_get_tc (fixP->fx_addsy)->real_name, "_$TLSML") != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("R_TLSML relocation doesn't target a "
+ "TOC entry named \"_$TLSML\": %s"), S_GET_NAME(fixP->fx_addsy));
fieldval = 0;
break;
symbol_get_bfdsym (fixP->fx_addsy)->flags |= BSF_KEEP;
}
#else
- if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16
- && fixP->fx_r_type != BFD_RELOC_PPC_TOC16_HI
- && fixP->fx_r_type != BFD_RELOC_PPC_TOC16_LO)
- fixP->fx_addnumber = 0;
- else
+ if (fixP->fx_r_type == BFD_RELOC_PPC_TOC16
+ || fixP->fx_r_type == BFD_RELOC_PPC_TOC16_HI
+ || fixP->fx_r_type == BFD_RELOC_PPC_TOC16_LO)
{
/* We want to use the offset within the toc, not the actual VMA
of the symbol. */
/* Set *valP to avoid errors. */
*valP = value;
}
+ else if (fixP->fx_r_type == BFD_RELOC_PPC_TLSM
+ || fixP->fx_r_type == BFD_RELOC_PPC64_TLSM
+ || fixP->fx_r_type == BFD_RELOC_PPC_TLSML
+ || fixP->fx_r_type == BFD_RELOC_PPC64_TLSML)
+ /* AIX ld expects the section contents for these relocations
+ to be zero. Arrange for that to occur when
+ bfd_install_relocation is called. */
+ fixP->fx_addnumber = (- bfd_section_vma (S_GET_SEGMENT (fixP->fx_addsy))
+ - S_GET_VALUE (fixP->fx_addsy)
+ - fieldval);
+ else
+ fixP->fx_addnumber = 0;
#endif
}
/* Generate a reloc for a fixup. */
-arelent *
+arelent **
tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
{
+ static arelent *relocs[3];
arelent *reloc;
- reloc = XNEW (arelent);
+ relocs[0] = reloc = XNEW (arelent);
+ relocs[1] = NULL;
reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
as_bad_where (fixp->fx_file, fixp->fx_line,
_("reloc %d not supported by object file format"),
(int) fixp->fx_r_type);
- return NULL;
+ relocs[0] = NULL;
}
reloc->addend = fixp->fx_addnumber;
- return reloc;
+ if (fixp->fx_subsy != NULL)
+ {
+ relocs[1] = reloc = XNEW (arelent);
+ relocs[2] = NULL;
+
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_PPC_NEG);
+ reloc->addend = fixp->fx_addnumber;
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_subtract (fixp);
+ free (relocs[1]->sym_ptr_ptr);
+ free (relocs[1]);
+ free (relocs[0]->sym_ptr_ptr);
+ free (relocs[0]);
+ relocs[0] = NULL;
+ }
+ }
+
+
+ return relocs;
}
void