summaryrefslogtreecommitdiffstats
path: root/binutils-2.25/gas/config/tc-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-2.25/gas/config/tc-ppc.c')
-rw-r--r--binutils-2.25/gas/config/tc-ppc.c6962
1 files changed, 6962 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-ppc.c b/binutils-2.25/gas/config/tc-ppc.c
new file mode 100644
index 00000000..6b54f5ad
--- /dev/null
+++ b/binutils-2.25/gas/config/tc-ppc.c
@@ -0,0 +1,6962 @@
+/* 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.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "dw2gencfi.h"
+#include "opcode/ppc.h"
+
+#ifdef OBJ_ELF
+#include "elf/ppc.h"
+#include "dwarf2dbg.h"
+#endif
+
+#ifdef TE_PE
+#include "coff/pe.h"
+#endif
+
+#ifdef OBJ_XCOFF
+#include "coff/xcoff.h"
+#include "libxcoff.h"
+#endif
+
+/* This is the assembler for the PowerPC or POWER (RS/6000) chips. */
+
+/* Tell the main code what the endianness is. */
+extern int target_big_endian;
+
+/* Whether or not, we've set target_big_endian. */
+static int set_target_endian = 0;
+
+/* Whether to use user friendly register names. */
+#ifndef TARGET_REG_NAMES_P
+#ifdef TE_PE
+#define TARGET_REG_NAMES_P TRUE
+#else
+#define TARGET_REG_NAMES_P FALSE
+#endif
+#endif
+
+/* Macros for calculating LO, HI, HA, HIGHER, HIGHERA, HIGHEST,
+ HIGHESTA. */
+
+/* #lo(value) denotes the least significant 16 bits of the indicated. */
+#define PPC_LO(v) ((v) & 0xffff)
+
+/* #hi(value) denotes bits 16 through 31 of the indicated value. */
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+
+/* #ha(value) denotes the high adjusted value: bits 16 through 31 of
+ the indicated value, compensating for #lo() being treated as a
+ signed number. */
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
+/* #higher(value) denotes bits 32 through 47 of the indicated value. */
+#define PPC_HIGHER(v) (((v) >> 16 >> 16) & 0xffff)
+
+/* #highera(value) denotes bits 32 through 47 of the indicated value,
+ compensating for #lo() being treated as a signed number. */
+#define PPC_HIGHERA(v) PPC_HIGHER ((v) + 0x8000)
+
+/* #highest(value) denotes bits 48 through 63 of the indicated value. */
+#define PPC_HIGHEST(v) (((v) >> 24 >> 24) & 0xffff)
+
+/* #highesta(value) denotes bits 48 through 63 of the indicated value,
+ compensating for #lo being treated as a signed number. */
+#define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000)
+
+#define SEX16(val) ((((val) & 0xffff) ^ 0x8000) - 0x8000)
+
+static bfd_boolean 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_tc (int);
+static void ppc_machine (int);
+#endif
+
+#ifdef OBJ_XCOFF
+static void ppc_comm (int);
+static void ppc_bb (int);
+static void ppc_bc (int);
+static void ppc_bf (int);
+static void ppc_biei (int);
+static void ppc_bs (int);
+static void ppc_eb (int);
+static void ppc_ec (int);
+static void ppc_ef (int);
+static void ppc_es (int);
+static void ppc_csect (int);
+static void ppc_dwsect (int);
+static void ppc_change_csect (symbolS *, offsetT);
+static void ppc_function (int);
+static void ppc_extern (int);
+static void ppc_lglobl (int);
+static void ppc_ref (int);
+static void ppc_section (int);
+static void ppc_named_section (int);
+static void ppc_stabx (int);
+static void ppc_rename (int);
+static void ppc_toc (int);
+static void ppc_xcoff_cons (int);
+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);
+#endif
+
+#ifdef TE_PE
+static void ppc_previous (int);
+static void ppc_pdata (int);
+static void ppc_ydata (int);
+static void ppc_reldata (int);
+static void ppc_rdata (int);
+static void ppc_ualong (int);
+static void ppc_znop (int);
+static void ppc_pe_comm (int);
+static void ppc_pe_section (int);
+static void ppc_pe_function (int);
+static void ppc_pe_tocd (int);
+#endif
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+#ifdef OBJ_ELF
+/* This string holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The macro
+ tc_comment_chars points to this. We use this, rather than the
+ usual comment_chars, so that we can switch for Solaris conventions. */
+static const char ppc_solaris_comment_chars[] = "#!";
+static const char ppc_eabi_comment_chars[] = "#";
+
+#ifdef TARGET_SOLARIS_COMMENT
+const char *ppc_comment_chars = ppc_solaris_comment_chars;
+#else
+const char *ppc_comment_chars = ppc_eabi_comment_chars;
+#endif
+#else
+const char comment_chars[] = "#";
+#endif
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+/* Anything that can start an operand needs to be mentioned here,
+ to stop the input scrubber eating whitespace. */
+const char ppc_symbol_chars[] = "%[";
+
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */
+int ppc_cie_data_alignment;
+
+/* The dwarf2 minimum instruction length. */
+int ppc_dwarf2_line_min_insn_length;
+
+/* More than this number of nops in an alignment op gets a branch
+ instead. */
+unsigned long nop_limit = 4;
+
+/* The type of processor we are assembling for. This is one or more
+ of the PPC_OPCODE flags defined in opcode/ppc.h. */
+ppc_cpu_t ppc_cpu = 0;
+ppc_cpu_t sticky = 0;
+
+/* Flags set on encountering toc relocs. */
+enum {
+ has_large_toc_reloc = 1,
+ has_small_toc_reloc = 2
+} toc_reloc_types;
+
+/* The target specific pseudo-ops which we support. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Pseudo-ops which must be overridden. */
+ { "byte", ppc_byte, 0 },
+
+#ifdef OBJ_XCOFF
+ /* Pseudo-ops specific to the RS/6000 XCOFF format. Some of these
+ legitimately belong in the obj-*.c file. However, XCOFF is based
+ on COFF, and is only implemented for the RS/6000. We just use
+ obj-coff.c, and add what we need here. */
+ { "comm", ppc_comm, 0 },
+ { "lcomm", ppc_comm, 1 },
+ { "bb", ppc_bb, 0 },
+ { "bc", ppc_bc, 0 },
+ { "bf", ppc_bf, 0 },
+ { "bi", ppc_biei, 0 },
+ { "bs", ppc_bs, 0 },
+ { "csect", ppc_csect, 0 },
+ { "dwsect", ppc_dwsect, 0 },
+ { "data", ppc_section, 'd' },
+ { "eb", ppc_eb, 0 },
+ { "ec", ppc_ec, 0 },
+ { "ef", ppc_ef, 0 },
+ { "ei", ppc_biei, 1 },
+ { "es", ppc_es, 0 },
+ { "extern", ppc_extern, 0 },
+ { "function", ppc_function, 0 },
+ { "lglobl", ppc_lglobl, 0 },
+ { "ref", ppc_ref, 0 },
+ { "rename", ppc_rename, 0 },
+ { "section", ppc_named_section, 0 },
+ { "stabx", ppc_stabx, 0 },
+ { "text", ppc_section, 't' },
+ { "toc", ppc_toc, 0 },
+ { "long", ppc_xcoff_cons, 2 },
+ { "llong", ppc_xcoff_cons, 3 },
+ { "word", ppc_xcoff_cons, 1 },
+ { "short", ppc_xcoff_cons, 1 },
+ { "vbyte", ppc_vbyte, 0 },
+#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 },
+ { "rdata", ppc_elf_rdata, 0 },
+ { "rodata", ppc_elf_rdata, 0 },
+ { "lcomm", ppc_elf_lcomm, 0 },
+#endif
+
+#ifdef TE_PE
+ /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format. */
+ { "previous", ppc_previous, 0 },
+ { "pdata", ppc_pdata, 0 },
+ { "ydata", ppc_ydata, 0 },
+ { "reldata", ppc_reldata, 0 },
+ { "rdata", ppc_rdata, 0 },
+ { "ualong", ppc_ualong, 0 },
+ { "znop", ppc_znop, 0 },
+ { "comm", ppc_pe_comm, 0 },
+ { "lcomm", ppc_pe_comm, 1 },
+ { "section", ppc_pe_section, 0 },
+ { "function", ppc_pe_function,0 },
+ { "tocd", ppc_pe_tocd, 0 },
+#endif
+
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+ { "tc", ppc_tc, 0 },
+ { "machine", ppc_machine, 0 },
+#endif
+
+ { NULL, NULL, 0 }
+};
+
+
+/* Predefined register names if -mregnames (or default for Windows NT).
+ In general, there are lots of them, in an attempt to be compatible
+ with a number of other Windows NT assemblers. */
+
+/* Structure to hold information about predefined registers. */
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* List of registers that are pre-defined:
+
+ Each general register has predefined names of the form:
+ 1. r<reg_num> which has the value <reg_num>.
+ 2. r.<reg_num> which has the value <reg_num>.
+
+ Each floating point register has predefined names of the form:
+ 1. f<reg_num> which has the value <reg_num>.
+ 2. f.<reg_num> which has the value <reg_num>.
+
+ Each vector unit register has predefined names of the form:
+ 1. v<reg_num> which has the value <reg_num>.
+ 2. v.<reg_num> which has the value <reg_num>.
+
+ Each condition register has predefined names of the form:
+ 1. cr<reg_num> which has the value <reg_num>.
+ 2. cr.<reg_num> which has the value <reg_num>.
+
+ There are individual registers as well:
+ sp or r.sp has the value 1
+ rtoc or r.toc has the value 2
+ fpscr has the value 0
+ xer has the value 1
+ lr has the value 8
+ ctr has the value 9
+ pmr has the value 0
+ dar has the value 19
+ dsisr has the value 18
+ dec has the value 22
+ sdr1 has the value 25
+ srr0 has the value 26
+ srr1 has the value 27
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ { "cr.0", 0 }, /* Condition Registers */
+ { "cr.1", 1 },
+ { "cr.2", 2 },
+ { "cr.3", 3 },
+ { "cr.4", 4 },
+ { "cr.5", 5 },
+ { "cr.6", 6 },
+ { "cr.7", 7 },
+
+ { "cr0", 0 },
+ { "cr1", 1 },
+ { "cr2", 2 },
+ { "cr3", 3 },
+ { "cr4", 4 },
+ { "cr5", 5 },
+ { "cr6", 6 },
+ { "cr7", 7 },
+
+ { "ctr", 9 },
+
+ { "dar", 19 }, /* Data Access Register */
+ { "dec", 22 }, /* Decrementer */
+ { "dsisr", 18 }, /* Data Storage Interrupt Status Register */
+
+ { "f.0", 0 }, /* Floating point registers */
+ { "f.1", 1 },
+ { "f.10", 10 },
+ { "f.11", 11 },
+ { "f.12", 12 },
+ { "f.13", 13 },
+ { "f.14", 14 },
+ { "f.15", 15 },
+ { "f.16", 16 },
+ { "f.17", 17 },
+ { "f.18", 18 },
+ { "f.19", 19 },
+ { "f.2", 2 },
+ { "f.20", 20 },
+ { "f.21", 21 },
+ { "f.22", 22 },
+ { "f.23", 23 },
+ { "f.24", 24 },
+ { "f.25", 25 },
+ { "f.26", 26 },
+ { "f.27", 27 },
+ { "f.28", 28 },
+ { "f.29", 29 },
+ { "f.3", 3 },
+ { "f.30", 30 },
+ { "f.31", 31 },
+
+ { "f.32", 32 }, /* Extended floating point scalar registers (ISA 2.06). */
+ { "f.33", 33 },
+ { "f.34", 34 },
+ { "f.35", 35 },
+ { "f.36", 36 },
+ { "f.37", 37 },
+ { "f.38", 38 },
+ { "f.39", 39 },
+ { "f.4", 4 },
+ { "f.40", 40 },
+ { "f.41", 41 },
+ { "f.42", 42 },
+ { "f.43", 43 },
+ { "f.44", 44 },
+ { "f.45", 45 },
+ { "f.46", 46 },
+ { "f.47", 47 },
+ { "f.48", 48 },
+ { "f.49", 49 },
+ { "f.5", 5 },
+ { "f.50", 50 },
+ { "f.51", 51 },
+ { "f.52", 52 },
+ { "f.53", 53 },
+ { "f.54", 54 },
+ { "f.55", 55 },
+ { "f.56", 56 },
+ { "f.57", 57 },
+ { "f.58", 58 },
+ { "f.59", 59 },
+ { "f.6", 6 },
+ { "f.60", 60 },
+ { "f.61", 61 },
+ { "f.62", 62 },
+ { "f.63", 63 },
+ { "f.7", 7 },
+ { "f.8", 8 },
+ { "f.9", 9 },
+
+ { "f0", 0 },
+ { "f1", 1 },
+ { "f10", 10 },
+ { "f11", 11 },
+ { "f12", 12 },
+ { "f13", 13 },
+ { "f14", 14 },
+ { "f15", 15 },
+ { "f16", 16 },
+ { "f17", 17 },
+ { "f18", 18 },
+ { "f19", 19 },
+ { "f2", 2 },
+ { "f20", 20 },
+ { "f21", 21 },
+ { "f22", 22 },
+ { "f23", 23 },
+ { "f24", 24 },
+ { "f25", 25 },
+ { "f26", 26 },
+ { "f27", 27 },
+ { "f28", 28 },
+ { "f29", 29 },
+ { "f3", 3 },
+ { "f30", 30 },
+ { "f31", 31 },
+
+ { "f32", 32 }, /* Extended floating point scalar registers (ISA 2.06). */
+ { "f33", 33 },
+ { "f34", 34 },
+ { "f35", 35 },
+ { "f36", 36 },
+ { "f37", 37 },
+ { "f38", 38 },
+ { "f39", 39 },
+ { "f4", 4 },
+ { "f40", 40 },
+ { "f41", 41 },
+ { "f42", 42 },
+ { "f43", 43 },
+ { "f44", 44 },
+ { "f45", 45 },
+ { "f46", 46 },
+ { "f47", 47 },
+ { "f48", 48 },
+ { "f49", 49 },
+ { "f5", 5 },
+ { "f50", 50 },
+ { "f51", 51 },
+ { "f52", 52 },
+ { "f53", 53 },
+ { "f54", 54 },
+ { "f55", 55 },
+ { "f56", 56 },
+ { "f57", 57 },
+ { "f58", 58 },
+ { "f59", 59 },
+ { "f6", 6 },
+ { "f60", 60 },
+ { "f61", 61 },
+ { "f62", 62 },
+ { "f63", 63 },
+ { "f7", 7 },
+ { "f8", 8 },
+ { "f9", 9 },
+
+ { "fpscr", 0 },
+
+ /* Quantization registers used with pair single instructions. */
+ { "gqr.0", 0 },
+ { "gqr.1", 1 },
+ { "gqr.2", 2 },
+ { "gqr.3", 3 },
+ { "gqr.4", 4 },
+ { "gqr.5", 5 },
+ { "gqr.6", 6 },
+ { "gqr.7", 7 },
+ { "gqr0", 0 },
+ { "gqr1", 1 },
+ { "gqr2", 2 },
+ { "gqr3", 3 },
+ { "gqr4", 4 },
+ { "gqr5", 5 },
+ { "gqr6", 6 },
+ { "gqr7", 7 },
+
+ { "lr", 8 }, /* Link Register */
+
+ { "pmr", 0 },
+
+ { "r.0", 0 }, /* General Purpose Registers */
+ { "r.1", 1 },
+ { "r.10", 10 },
+ { "r.11", 11 },
+ { "r.12", 12 },
+ { "r.13", 13 },
+ { "r.14", 14 },
+ { "r.15", 15 },
+ { "r.16", 16 },
+ { "r.17", 17 },
+ { "r.18", 18 },
+ { "r.19", 19 },
+ { "r.2", 2 },
+ { "r.20", 20 },
+ { "r.21", 21 },
+ { "r.22", 22 },
+ { "r.23", 23 },
+ { "r.24", 24 },
+ { "r.25", 25 },
+ { "r.26", 26 },
+ { "r.27", 27 },
+ { "r.28", 28 },
+ { "r.29", 29 },
+ { "r.3", 3 },
+ { "r.30", 30 },
+ { "r.31", 31 },
+ { "r.4", 4 },
+ { "r.5", 5 },
+ { "r.6", 6 },
+ { "r.7", 7 },
+ { "r.8", 8 },
+ { "r.9", 9 },
+
+ { "r.sp", 1 }, /* Stack Pointer */
+
+ { "r.toc", 2 }, /* Pointer to the table of contents */
+
+ { "r0", 0 }, /* More general purpose registers */
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r16", 16 },
+ { "r17", 17 },
+ { "r18", 18 },
+ { "r19", 19 },
+ { "r2", 2 },
+ { "r20", 20 },
+ { "r21", 21 },
+ { "r22", 22 },
+ { "r23", 23 },
+ { "r24", 24 },
+ { "r25", 25 },
+ { "r26", 26 },
+ { "r27", 27 },
+ { "r28", 28 },
+ { "r29", 29 },
+ { "r3", 3 },
+ { "r30", 30 },
+ { "r31", 31 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+
+ { "rtoc", 2 }, /* Table of contents */
+
+ { "sdr1", 25 }, /* Storage Description Register 1 */
+
+ { "sp", 1 },
+
+ { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */
+ { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */
+
+ { "v.0", 0 }, /* Vector (Altivec/VMX) registers */
+ { "v.1", 1 },
+ { "v.10", 10 },
+ { "v.11", 11 },
+ { "v.12", 12 },
+ { "v.13", 13 },
+ { "v.14", 14 },
+ { "v.15", 15 },
+ { "v.16", 16 },
+ { "v.17", 17 },
+ { "v.18", 18 },
+ { "v.19", 19 },
+ { "v.2", 2 },
+ { "v.20", 20 },
+ { "v.21", 21 },
+ { "v.22", 22 },
+ { "v.23", 23 },
+ { "v.24", 24 },
+ { "v.25", 25 },
+ { "v.26", 26 },
+ { "v.27", 27 },
+ { "v.28", 28 },
+ { "v.29", 29 },
+ { "v.3", 3 },
+ { "v.30", 30 },
+ { "v.31", 31 },
+ { "v.4", 4 },
+ { "v.5", 5 },
+ { "v.6", 6 },
+ { "v.7", 7 },
+ { "v.8", 8 },
+ { "v.9", 9 },
+
+ { "v0", 0 },
+ { "v1", 1 },
+ { "v10", 10 },
+ { "v11", 11 },
+ { "v12", 12 },
+ { "v13", 13 },
+ { "v14", 14 },
+ { "v15", 15 },
+ { "v16", 16 },
+ { "v17", 17 },
+ { "v18", 18 },
+ { "v19", 19 },
+ { "v2", 2 },
+ { "v20", 20 },
+ { "v21", 21 },
+ { "v22", 22 },
+ { "v23", 23 },
+ { "v24", 24 },
+ { "v25", 25 },
+ { "v26", 26 },
+ { "v27", 27 },
+ { "v28", 28 },
+ { "v29", 29 },
+ { "v3", 3 },
+ { "v30", 30 },
+ { "v31", 31 },
+ { "v4", 4 },
+ { "v5", 5 },
+ { "v6", 6 },
+ { "v7", 7 },
+ { "v8", 8 },
+ { "v9", 9 },
+
+ { "vs.0", 0 }, /* Vector Scalar (VSX) registers (ISA 2.06). */
+ { "vs.1", 1 },
+ { "vs.10", 10 },
+ { "vs.11", 11 },
+ { "vs.12", 12 },
+ { "vs.13", 13 },
+ { "vs.14", 14 },
+ { "vs.15", 15 },
+ { "vs.16", 16 },
+ { "vs.17", 17 },
+ { "vs.18", 18 },
+ { "vs.19", 19 },
+ { "vs.2", 2 },
+ { "vs.20", 20 },
+ { "vs.21", 21 },
+ { "vs.22", 22 },
+ { "vs.23", 23 },
+ { "vs.24", 24 },
+ { "vs.25", 25 },
+ { "vs.26", 26 },
+ { "vs.27", 27 },
+ { "vs.28", 28 },
+ { "vs.29", 29 },
+ { "vs.3", 3 },
+ { "vs.30", 30 },
+ { "vs.31", 31 },
+ { "vs.32", 32 },
+ { "vs.33", 33 },
+ { "vs.34", 34 },
+ { "vs.35", 35 },
+ { "vs.36", 36 },
+ { "vs.37", 37 },
+ { "vs.38", 38 },
+ { "vs.39", 39 },
+ { "vs.4", 4 },
+ { "vs.40", 40 },
+ { "vs.41", 41 },
+ { "vs.42", 42 },
+ { "vs.43", 43 },
+ { "vs.44", 44 },
+ { "vs.45", 45 },
+ { "vs.46", 46 },
+ { "vs.47", 47 },
+ { "vs.48", 48 },
+ { "vs.49", 49 },
+ { "vs.5", 5 },
+ { "vs.50", 50 },
+ { "vs.51", 51 },
+ { "vs.52", 52 },
+ { "vs.53", 53 },
+ { "vs.54", 54 },
+ { "vs.55", 55 },
+ { "vs.56", 56 },
+ { "vs.57", 57 },
+ { "vs.58", 58 },
+ { "vs.59", 59 },
+ { "vs.6", 6 },
+ { "vs.60", 60 },
+ { "vs.61", 61 },
+ { "vs.62", 62 },
+ { "vs.63", 63 },
+ { "vs.7", 7 },
+ { "vs.8", 8 },
+ { "vs.9", 9 },
+
+ { "vs0", 0 },
+ { "vs1", 1 },
+ { "vs10", 10 },
+ { "vs11", 11 },
+ { "vs12", 12 },
+ { "vs13", 13 },
+ { "vs14", 14 },
+ { "vs15", 15 },
+ { "vs16", 16 },
+ { "vs17", 17 },
+ { "vs18", 18 },
+ { "vs19", 19 },
+ { "vs2", 2 },
+ { "vs20", 20 },
+ { "vs21", 21 },
+ { "vs22", 22 },
+ { "vs23", 23 },
+ { "vs24", 24 },
+ { "vs25", 25 },
+ { "vs26", 26 },
+ { "vs27", 27 },
+ { "vs28", 28 },
+ { "vs29", 29 },
+ { "vs3", 3 },
+ { "vs30", 30 },
+ { "vs31", 31 },
+ { "vs32", 32 },
+ { "vs33", 33 },
+ { "vs34", 34 },
+ { "vs35", 35 },
+ { "vs36", 36 },
+ { "vs37", 37 },
+ { "vs38", 38 },
+ { "vs39", 39 },
+ { "vs4", 4 },
+ { "vs40", 40 },
+ { "vs41", 41 },
+ { "vs42", 42 },
+ { "vs43", 43 },
+ { "vs44", 44 },
+ { "vs45", 45 },
+ { "vs46", 46 },
+ { "vs47", 47 },
+ { "vs48", 48 },
+ { "vs49", 49 },
+ { "vs5", 5 },
+ { "vs50", 50 },
+ { "vs51", 51 },
+ { "vs52", 52 },
+ { "vs53", 53 },
+ { "vs54", 54 },
+ { "vs55", 55 },
+ { "vs56", 56 },
+ { "vs57", 57 },
+ { "vs58", 58 },
+ { "vs59", 59 },
+ { "vs6", 6 },
+ { "vs60", 60 },
+ { "vs61", 61 },
+ { "vs62", 62 },
+ { "vs63", 63 },
+ { "vs7", 7 },
+ { "vs8", 8 },
+ { "vs9", 9 },
+
+ { "xer", 1 },
+
+};
+
+#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. */
+
+static int
+reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/*
+ * 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.
+ */
+
+static bfd_boolean
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ 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;
+
+ else if (!reg_names_p || !ISALPHA (name[0]))
+ return FALSE;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* 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;
+}
+
+/* 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 bfd_boolean cr_operand;
+
+/* Names to recognize in a condition code. This table is sorted. */
+static const struct pd_reg cr_names[] =
+{
+ { "cr0", 0 },
+ { "cr1", 1 },
+ { "cr2", 2 },
+ { "cr3", 3 },
+ { "cr4", 4 },
+ { "cr5", 5 },
+ { "cr6", 6 },
+ { "cr7", 7 },
+ { "eq", 2 },
+ { "gt", 1 },
+ { "lt", 0 },
+ { "so", 3 },
+ { "un", 3 }
+};
+
+/* Parsing function. This returns non-zero if it recognized an
+ expression. */
+
+int
+ppc_parse_name (const char *name, expressionS *exp)
+{
+ int val;
+
+ if (! cr_operand)
+ return 0;
+
+ if (*name == '%')
+ ++name;
+ val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
+ name);
+ if (val < 0)
+ return 0;
+
+ exp->X_op = O_constant;
+ exp->X_add_number = val;
+
+ return 1;
+}
+
+/* Local variables. */
+
+/* Whether to target xcoff64/elf64. */
+static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
+
+/* Opcode hash table. */
+static struct hash_control *ppc_hash;
+
+/* Macro hash table. */
+static struct hash_control *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;
+
+/* Flags to set in the elf header. */
+static flagword ppc_flags = 0;
+
+/* Whether this is Solaris or not. */
+#ifdef TARGET_SOLARIS_COMMENT
+#define SOLARIS_P TRUE
+#else
+#define SOLARIS_P FALSE
+#endif
+
+static bfd_boolean msolaris = SOLARIS_P;
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* The RS/6000 assembler uses the .csect pseudo-op to generate code
+ using a bunch of different sections. These assembler sections,
+ however, are all encompassed within the .text or .data sections of
+ the final output file. We handle this by using different
+ subsegments within these main segments. */
+
+/* Next subsegment to allocate within the .text segment. */
+static subsegT ppc_text_subsegment = 2;
+
+/* Linked list of csects in the text section. */
+static symbolS *ppc_text_csects;
+
+/* Next subsegment to allocate within the .data segment. */
+static subsegT ppc_data_subsegment = 2;
+
+/* Linked list of csects in the data section. */
+static symbolS *ppc_data_csects;
+
+/* The current csect. */
+static symbolS *ppc_current_csect;
+
+/* The RS/6000 assembler uses a TOC which holds addresses of functions
+ and variables. Symbols are put in the TOC with the .tc pseudo-op.
+ A special relocation is used when accessing TOC entries. We handle
+ the TOC as a subsegment within the .data segment. We set it up if
+ we see a .toc pseudo-op, and save the csect symbol here. */
+static symbolS *ppc_toc_csect;
+
+/* The first frag in the TOC subsegment. */
+static fragS *ppc_toc_frag;
+
+/* The first frag in the first subsegment after the TOC in the .data
+ segment. NULL if there are no subsegments after the TOC. */
+static fragS *ppc_after_toc_frag;
+
+/* The current static block. */
+static symbolS *ppc_current_block;
+
+/* The COFF debugging section; set by md_begin. This is not the
+ .debug section, but is instead the secret BFD section which will
+ cause BFD to set the section number of a symbol to N_DEBUG. */
+static asection *ppc_coff_debug_section;
+
+/* Structure to set the length field of the dwarf sections. */
+struct dw_subsection {
+ /* Subsections are simply linked. */
+ struct dw_subsection *link;
+
+ /* The subsection number. */
+ subsegT subseg;
+
+ /* Expression to compute the length of the section. */
+ expressionS end_exp;
+};
+
+static struct dw_section {
+ /* Corresponding section. */
+ segT sect;
+
+ /* Simply linked list of subsections with a label. */
+ struct dw_subsection *list_subseg;
+
+ /* The anonymous subsection. */
+ struct dw_subsection *anon_subseg;
+} dw_sections[XCOFF_DWSECT_NBR_NAMES];
+#endif /* OBJ_XCOFF */
+
+#ifdef TE_PE
+
+/* Various sections that we need for PE coff support. */
+static segT ydata_section;
+static segT pdata_section;
+static segT reldata_section;
+static segT rdata_section;
+static segT tocdata_section;
+
+/* The current section and the previous section. See ppc_previous. */
+static segT ppc_previous_section;
+static segT ppc_current_section;
+
+#endif /* TE_PE */
+
+#ifdef OBJ_ELF
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
+#define PPC_APUINFO_ISEL 0x40
+#define PPC_APUINFO_PMR 0x41
+#define PPC_APUINFO_RFMCI 0x42
+#define PPC_APUINFO_CACHELCK 0x43
+#define PPC_APUINFO_SPE 0x100
+#define PPC_APUINFO_EFS 0x101
+#define PPC_APUINFO_BRLOCK 0x102
+#define PPC_APUINFO_VLE 0x104
+
+/*
+ * We keep a list of APUinfo
+ */
+unsigned long *ppc_apuinfo_list;
+unsigned int ppc_apuinfo_num;
+unsigned int ppc_apuinfo_num_alloc;
+#endif /* OBJ_ELF */
+
+#ifdef OBJ_ELF
+const char *const md_shortopts = "b:l:usm:K:VQ:";
+#else
+const char *const md_shortopts = "um:";
+#endif
+#define OPTION_NOPS (OPTION_MD_BASE + 0)
+const struct option md_longopts[] = {
+ {"nops", required_argument, NULL, OPTION_NOPS},
+ {NULL, no_argument, NULL, 0}
+};
+const size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ ppc_cpu_t new_cpu;
+
+ switch (c)
+ {
+ case 'u':
+ /* -u means that any undefined symbols should be treated as
+ external, which is the default for gas anyhow. */
+ break;
+
+#ifdef OBJ_ELF
+ case 'l':
+ /* Solaris as takes -le (presumably for little endian). For completeness
+ sake, recognize -be also. */
+ if (strcmp (arg, "e") == 0)
+ {
+ target_big_endian = 0;
+ set_target_endian = 1;
+ if (ppc_cpu & PPC_OPCODE_VLE)
+ as_bad (_("the use of -mvle requires big endian."));
+ }
+ else
+ return 0;
+
+ break;
+
+ case 'b':
+ if (strcmp (arg, "e") == 0)
+ {
+ target_big_endian = 1;
+ set_target_endian = 1;
+ }
+ else
+ return 0;
+
+ break;
+
+ case 'K':
+ /* Recognize -K PIC. */
+ if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0)
+ {
+ shlib = SHLIB_PIC;
+ ppc_flags |= EF_PPC_RELOCATABLE_LIB;
+ }
+ else
+ return 0;
+
+ break;
+#endif
+
+ /* a64 and a32 determine whether to use XCOFF64 or XCOFF32. */
+ case 'a':
+ if (strcmp (arg, "64") == 0)
+ {
+#ifdef BFD64
+ ppc_obj64 = 1;
+ if (ppc_cpu & PPC_OPCODE_VLE)
+ as_bad (_("the use of -mvle requires -a32."));
+#else
+ as_fatal (_("%s unsupported"), "-a64");
+#endif
+ }
+ else if (strcmp (arg, "32") == 0)
+ ppc_obj64 = 0;
+ else
+ return 0;
+ break;
+
+ case 'm':
+ new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, arg);
+ if (new_cpu != 0)
+ {
+ ppc_cpu = new_cpu;
+ if (strcmp (arg, "vle") == 0)
+ {
+ if (set_target_endian && target_big_endian == 0)
+ as_bad (_("the use of -mvle requires big endian."));
+ if (ppc_obj64)
+ as_bad (_("the use of -mvle requires -a32."));
+ }
+ }
+
+ else if (strcmp (arg, "regnames") == 0)
+ reg_names_p = TRUE;
+
+ else if (strcmp (arg, "no-regnames") == 0)
+ reg_names_p = FALSE;
+
+#ifdef OBJ_ELF
+ /* -mrelocatable/-mrelocatable-lib -- warn about initializations
+ that require relocation. */
+ else if (strcmp (arg, "relocatable") == 0)
+ {
+ shlib = SHLIB_MRELOCATABLE;
+ ppc_flags |= EF_PPC_RELOCATABLE;
+ }
+
+ else if (strcmp (arg, "relocatable-lib") == 0)
+ {
+ shlib = SHLIB_MRELOCATABLE;
+ ppc_flags |= EF_PPC_RELOCATABLE_LIB;
+ }
+
+ /* -memb, set embedded bit. */
+ else if (strcmp (arg, "emb") == 0)
+ ppc_flags |= EF_PPC_EMB;
+
+ /* -mlittle/-mbig set the endianness. */
+ else if (strcmp (arg, "little") == 0
+ || strcmp (arg, "little-endian") == 0)
+ {
+ target_big_endian = 0;
+ set_target_endian = 1;
+ if (ppc_cpu & PPC_OPCODE_VLE)
+ as_bad (_("the use of -mvle requires big endian."));
+ }
+
+ else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0)
+ {
+ target_big_endian = 1;
+ set_target_endian = 1;
+ }
+
+ else if (strcmp (arg, "solaris") == 0)
+ {
+ msolaris = TRUE;
+ ppc_comment_chars = ppc_solaris_comment_chars;
+ }
+
+ else if (strcmp (arg, "no-solaris") == 0)
+ {
+ msolaris = FALSE;
+ ppc_comment_chars = ppc_eabi_comment_chars;
+ }
+#endif
+ else
+ {
+ as_bad (_("invalid switch -m%s"), arg);
+ return 0;
+ }
+ break;
+
+#ifdef OBJ_ELF
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ /* Solaris takes -s to specify that .stabs go in a .stabs section,
+ rather than .stabs.excl, which is ignored by the linker.
+ FIXME: Not implemented. */
+ case 's':
+ if (arg)
+ return 0;
+
+ break;
+#endif
+
+ case OPTION_NOPS:
+ {
+ char *end;
+ nop_limit = strtoul (optarg, &end, 0);
+ if (*end)
+ as_bad (_("--nops needs a numeric argument"));
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+PowerPC options:\n\
+-a32 generate ELF32/XCOFF32\n\
+-a64 generate ELF64/XCOFF64\n\
+-u ignored\n\
+-mpwrx, -mpwr2 generate code for POWER/2 (RIOS2)\n\
+-mpwr generate code for POWER (RIOS1)\n\
+-m601 generate code for PowerPC 601\n\
+-mppc, -mppc32, -m603, -m604\n\
+ generate code for PowerPC 603/604\n\
+-m403 generate code for PowerPC 403\n\
+-m405 generate code for PowerPC 405\n\
+-m440 generate code for PowerPC 440\n\
+-m464 generate code for PowerPC 464\n\
+-m476 generate code for PowerPC 476\n\
+-m7400, -m7410, -m7450, -m7455\n\
+ generate code for PowerPC 7400/7410/7450/7455\n\
+-m750cl generate code for PowerPC 750cl\n"));
+ fprintf (stream, _("\
+-mppc64, -m620 generate code for PowerPC 620/625/630\n\
+-mppc64bridge generate code for PowerPC 64, including bridge insns\n\
+-mbooke generate code for 32-bit PowerPC BookE\n\
+-ma2 generate code for A2 architecture\n\
+-mpower4, -mpwr4 generate code for Power4 architecture\n\
+-mpower5, -mpwr5, -mpwr5x\n\
+ generate code for Power5 architecture\n\
+-mpower6, -mpwr6 generate code for Power6 architecture\n\
+-mpower7, -mpwr7 generate code for Power7 architecture\n\
+-mpower8, -mpwr8 generate code for Power8 architecture\n\
+-mcell generate code for Cell Broadband Engine architecture\n\
+-mcom generate code Power/PowerPC common instructions\n\
+-many generate code for any architecture (PWR/PWRX/PPC)\n"));
+ fprintf (stream, _("\
+-maltivec generate code for AltiVec\n\
+-mvsx generate code for Vector-Scalar (VSX) instructions\n\
+-mhtm generate code for Hardware Transactional Memory\n\
+-me300 generate code for PowerPC e300 family\n\
+-me500, -me500x2 generate code for Motorola e500 core complex\n\
+-me500mc, generate code for Freescale e500mc core complex\n\
+-me500mc64, generate code for Freescale e500mc64 core complex\n\
+-me5500, generate code for Freescale e5500 core complex\n\
+-me6500, generate code for Freescale e6500 core complex\n\
+-mspe generate code for Motorola SPE instructions\n\
+-mvle generate code for Freescale VLE instructions\n\
+-mtitan generate code for AppliedMicro Titan core complex\n\
+-mregnames Allow symbolic names for registers\n\
+-mno-regnames Do not allow symbolic names for registers\n"));
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+-mrelocatable support for GCC's -mrelocatble option\n\
+-mrelocatable-lib support for GCC's -mrelocatble-lib option\n\
+-memb set PPC_EMB bit in ELF flags\n\
+-mlittle, -mlittle-endian, -le\n\
+ generate code for a little endian machine\n\
+-mbig, -mbig-endian, -be\n\
+ generate code for a big endian machine\n\
+-msolaris generate code for Solaris\n\
+-mno-solaris do not generate code for Solaris\n\
+-K PIC set EF_PPC_RELOCATABLE_LIB in ELF flags\n\
+-V print assembler version number\n\
+-Qy, -Qn ignored\n"));
+#endif
+ fprintf (stream, _("\
+-nops=count when aligning, more than COUNT nops uses a branch\n"));
+}
+
+/* Set ppc_cpu if it is not already set. */
+
+static void
+ppc_set_cpu (void)
+{
+ const char *default_os = TARGET_OS;
+ const char *default_cpu = TARGET_CPU;
+
+ if ((ppc_cpu & ~(ppc_cpu_t) PPC_OPCODE_ANY) == 0)
+ {
+ if (ppc_obj64)
+ ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_64;
+ else if (strncmp (default_os, "aix", 3) == 0
+ && default_os[3] >= '4' && default_os[3] <= '9')
+ ppc_cpu |= PPC_OPCODE_COMMON;
+ else if (strncmp (default_os, "aix3", 4) == 0)
+ ppc_cpu |= PPC_OPCODE_POWER;
+ else if (strcmp (default_cpu, "rs6000") == 0)
+ ppc_cpu |= PPC_OPCODE_POWER;
+ else if (strncmp (default_cpu, "powerpc", 7) == 0)
+ ppc_cpu |= PPC_OPCODE_PPC;
+ else
+ as_fatal (_("unknown default cpu = %s, os = %s"),
+ default_cpu, default_os);
+ }
+}
+
+/* Figure out the BFD architecture to use. This function and ppc_mach
+ are called well before md_begin, when the output file is opened. */
+
+enum bfd_architecture
+ppc_arch (void)
+{
+ const char *default_cpu = TARGET_CPU;
+ ppc_set_cpu ();
+
+ if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
+ return bfd_arch_powerpc;
+ if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+ return bfd_arch_powerpc;
+ if ((ppc_cpu & PPC_OPCODE_POWER) != 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 (strncmp (default_cpu, "powerpc", 7) == 0)
+ return bfd_arch_powerpc;
+ }
+
+ as_fatal (_("neither Power nor PowerPC opcodes were selected."));
+ return bfd_arch_unknown;
+}
+
+unsigned long
+ppc_mach (void)
+{
+ if (ppc_obj64)
+ return bfd_mach_ppc64;
+ else if (ppc_arch () == bfd_arch_rs6000)
+ return bfd_mach_rs6k;
+ else if (ppc_cpu & PPC_OPCODE_TITAN)
+ return bfd_mach_ppc_titan;
+ else if (ppc_cpu & PPC_OPCODE_VLE)
+ return bfd_mach_ppc_vle;
+ else
+ return bfd_mach_ppc;
+}
+
+extern char*
+ppc_target_format (void)
+{
+#ifdef OBJ_COFF
+#ifdef TE_PE
+ return target_big_endian ? "pe-powerpc" : "pe-powerpcle";
+#elif TE_POWERMAC
+ return "xcoff-powermac";
+#else
+# ifdef TE_AIX5
+ return (ppc_obj64 ? "aix5coff64-rs6000" : "aixcoff-rs6000");
+# else
+ return (ppc_obj64 ? "aixcoff64-rs6000" : "aixcoff-rs6000");
+# endif
+#endif
+#endif
+#ifdef OBJ_ELF
+# ifdef TE_FreeBSD
+ return (ppc_obj64 ? "elf64-powerpc-freebsd" : "elf32-powerpc-freebsd");
+# elif defined (TE_VXWORKS)
+ return "elf32-powerpc-vxworks";
+# else
+ return (target_big_endian
+ ? (ppc_obj64 ? "elf64-powerpc" : "elf32-powerpc")
+ : (ppc_obj64 ? "elf64-powerpcle" : "elf32-powerpcle"));
+# endif
+#endif
+}
+
+/* Validate one entry in powerpc_opcodes[] or vle_opcodes[].
+ Return TRUE if there's a problem, otherwise FALSE. */
+
+static bfd_boolean
+insn_validate (const struct powerpc_opcode *op)
+{
+ const unsigned char *o;
+ unsigned long omask = op->mask;
+
+ /* The mask had better not trim off opcode bits. */
+ if ((op->opcode & omask) != op->opcode)
+ {
+ as_bad (_("mask trims opcode bits for %s"), op->name);
+ return TRUE;
+ }
+
+ /* The operands must not overlap the opcode or each other. */
+ for (o = op->operands; *o; ++o)
+ {
+ if (*o >= num_powerpc_operands)
+ {
+ as_bad (_("operand index error for %s"), op->name);
+ return TRUE;
+ }
+ else
+ {
+ const struct powerpc_operand *operand = &powerpc_operands[*o];
+ if (operand->shift != PPC_OPSHIFT_INV)
+ {
+ unsigned long mask;
+
+ if (operand->shift >= 0)
+ mask = operand->bitm << operand->shift;
+ else
+ mask = operand->bitm >> -operand->shift;
+ if (omask & mask)
+ {
+ as_bad (_("operand %d overlap in %s"),
+ (int) (o - op->operands), op->name);
+ return TRUE;
+ }
+ omask |= mask;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* Insert opcodes and macros 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;
+ bfd_boolean bad_insn = FALSE;
+
+ if (ppc_hash != NULL)
+ hash_die (ppc_hash);
+ if (ppc_macro_hash != NULL)
+ hash_die (ppc_macro_hash);
+
+ /* Insert the opcodes into a hash table. */
+ ppc_hash = hash_new ();
+
+ 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);
+
+ /* 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)
+ {
+ unsigned long mask = powerpc_operands[i].bitm;
+ unsigned long right_bit;
+ unsigned int j;
+
+ right_bit = mask & -mask;
+ mask += right_bit;
+ right_bit = mask & -mask;
+ if (mask != right_bit)
+ {
+ as_bad (_("powerpc_operands[%d].bitm invalid"), i);
+ bad_insn = TRUE;
+ }
+ for (j = i + 1; j < num_powerpc_operands; ++j)
+ if (memcmp (&powerpc_operands[i], &powerpc_operands[j],
+ sizeof (powerpc_operands[0])) == 0)
+ {
+ as_bad (_("powerpc_operands[%d] duplicates powerpc_operands[%d]"),
+ j, i);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ op_end = powerpc_opcodes + powerpc_num_opcodes;
+ for (op = powerpc_opcodes; op < op_end; op++)
+ {
+ if (ENABLE_CHECKING)
+ {
+ if (op != powerpc_opcodes)
+ {
+ int old_opcode = PPC_OP (op[-1].opcode);
+ int new_opcode = PPC_OP (op[0].opcode);
+
+#ifdef PRINT_OPCODE_TABLE
+ printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+ op->name, (unsigned int) (op - powerpc_opcodes),
+ (unsigned int) new_opcode, (unsigned int) op->opcode,
+ (unsigned int) 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 (new_opcode < old_opcode)
+ {
+ as_bad (_("major opcode is not sorted for %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+ bad_insn |= insn_validate (op);
+ }
+
+ if ((ppc_cpu & op->flags) != 0
+ && !(ppc_cpu & op->deprecated))
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_hash, op->name, (void *) op);
+ if (retval != NULL)
+ {
+ as_bad (_("duplicate instruction %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
+ for (op = powerpc_opcodes; op < op_end; op++)
+ hash_insert (ppc_hash, op->name, (void *) op);
+
+ op_end = vle_opcodes + vle_num_opcodes;
+ for (op = vle_opcodes; op < op_end; op++)
+ {
+ if (ENABLE_CHECKING)
+ {
+ if (op != vle_opcodes)
+ {
+ 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);
+
+#ifdef PRINT_OPCODE_TABLE
+ printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+ op->name, (unsigned int) (op - powerpc_opcodes),
+ (unsigned int) new_seg, (unsigned int) op->opcode,
+ (unsigned int) 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 (new_seg < old_seg)
+ {
+ as_bad (_("major opcode is not sorted for %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+
+ bad_insn |= insn_validate (op);
+ }
+
+ if ((ppc_cpu & op->flags) != 0
+ && !(ppc_cpu & op->deprecated))
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_hash, op->name, (void *) op);
+ if (retval != NULL)
+ {
+ as_bad (_("duplicate instruction %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+ for (op = vle_opcodes; op < op_end; op++)
+ hash_insert (ppc_hash, op->name, (void *) op);
+
+ /* Insert the macros into a hash table. */
+ ppc_macro_hash = hash_new ();
+
+ 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)
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_macro_hash, macro->name, (void *) macro);
+ if (retval != (const char *) NULL)
+ {
+ as_bad (_("duplicate macro %s"), macro->name);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ if (bad_insn)
+ abort ();
+}
+
+/* This function is called when the assembler starts up. It is called
+ after the options have been parsed and the output file has been
+ opened. */
+
+void
+md_begin (void)
+{
+ ppc_set_cpu ();
+
+ ppc_cie_data_alignment = ppc_obj64 ? -8 : -4;
+ ppc_dwarf2_line_min_insn_length = (ppc_cpu & PPC_OPCODE_VLE) ? 2 : 4;
+
+#ifdef OBJ_ELF
+ /* Set the ELF flags if desired. */
+ if (ppc_flags && !msolaris)
+ bfd_set_private_flags (stdoutput, ppc_flags);
+#endif
+
+ ppc_setup_opcodes ();
+
+ /* Tell the main code what the endianness is if it is not overridden
+ by the user. */
+ if (!set_target_endian)
+ {
+ set_target_endian = 1;
+ target_big_endian = PPC_BIG_ENDIAN;
+ }
+
+#ifdef OBJ_XCOFF
+ ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG);
+
+ /* Create dummy symbols to serve as initial csects. This forces the
+ text csects to precede the data csects. These symbols will not
+ be output. */
+ ppc_text_csects = symbol_make ("dummy\001");
+ symbol_get_tc (ppc_text_csects)->within = ppc_text_csects;
+ ppc_data_csects = symbol_make ("dummy\001");
+ symbol_get_tc (ppc_data_csects)->within = ppc_data_csects;
+#endif
+
+#ifdef TE_PE
+
+ ppc_current_section = text_section;
+ ppc_previous_section = 0;
+
+#endif
+}
+
+void
+ppc_cleanup (void)
+{
+#ifdef OBJ_ELF
+ if (ppc_apuinfo_list == NULL)
+ return;
+
+ /* Ok, so write the section info out. We have this layout:
+
+ byte data what
+ ---- ---- ----
+ 0 8 length of "APUinfo\0"
+ 4 (n*4) number of APU's (4 bytes each)
+ 8 2 note type 2
+ 12 "APUinfo\0" name
+ 20 APU#1 first APU's info
+ 24 APU#2 second APU's info
+ ... ...
+ */
+ {
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ asection *apuinfo_secp = (asection *) NULL;
+ unsigned int i;
+
+ /* Create the .PPC.EMB.apuinfo section. */
+ apuinfo_secp = subseg_new (".PPC.EMB.apuinfo", 0);
+ bfd_set_section_flags (stdoutput,
+ apuinfo_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) 8, 4);
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) ppc_apuinfo_num * 4, 4);
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) 2, 4);
+
+ p = frag_more (8);
+ strcpy (p, "APUinfo");
+
+ for (i = 0; i < ppc_apuinfo_num; i++)
+ {
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) ppc_apuinfo_list[i], 4);
+ }
+
+ frag_align (2, 0, 0);
+
+ /* We probably can't restore the current segment, for there likely
+ isn't one yet... */
+ if (seg && subseg)
+ subseg_set (seg, subseg);
+ }
+#endif
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned long
+ppc_insert_operand (unsigned long insn,
+ const struct powerpc_operand *operand,
+ offsetT val,
+ ppc_cpu_t cpu,
+ char *file,
+ unsigned int line)
+{
+ long min, max, right;
+
+ max = operand->bitm;
+ right = max & -max;
+ min = 0;
+
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ if ((operand->flags & PPC_OPERAND_SIGNOPT) == 0)
+ max = (max >> 1) & -right;
+ min = ~max & -right;
+ }
+
+ if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
+ max++;
+
+ if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+ {
+ long tmp = min;
+ min = -max;
+ max = -tmp;
+ }
+
+ if (min <= max)
+ {
+ /* Some people write constants with the sign extension done by
+ hand but only up to 32 bits. This shouldn't really be valid,
+ but, to permit this code to assemble on a 64-bit host, we
+ sign extend the 32-bit value to 64 bits if so doing makes the
+ value valid. */
+ if (val > max
+ && (offsetT) (val - 0x80000000 - 0x80000000) >= min
+ && (offsetT) (val - 0x80000000 - 0x80000000) <= max
+ && ((val - 0x80000000 - 0x80000000) & (right - 1)) == 0)
+ val = val - 0x80000000 - 0x80000000;
+
+ /* Similarly, people write expressions like ~(1<<15), and expect
+ this to be OK for a 32-bit unsigned value. */
+ else if (val < min
+ && (offsetT) (val + 0x80000000 + 0x80000000) >= min
+ && (offsetT) (val + 0x80000000 + 0x80000000) <= max
+ && ((val + 0x80000000 + 0x80000000) & (right - 1)) == 0)
+ val = val + 0x80000000 + 0x80000000;
+
+ else if (val < min
+ || val > max
+ || (val & (right - 1)) != 0)
+ as_bad_value_out_of_range (_("operand"), val, min, max, file, line);
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg;
+
+ errmsg = NULL;
+ insn = (*operand->insert) (insn, (long) val, cpu, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad_where (file, line, "%s", errmsg);
+ }
+ else if (operand->shift >= 0)
+ insn |= ((long) val & operand->bitm) << operand->shift;
+ else
+ insn |= ((long) val & operand->bitm) >> -operand->shift;
+
+ return insn;
+}
+
+
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation. */
+static bfd_reloc_code_real_type
+ppc_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ struct map_bfd {
+ char *string;
+ unsigned int length : 8;
+ unsigned int valid32 : 1;
+ unsigned int valid64 : 1;
+ unsigned int reloc;
+ };
+
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ const struct map_bfd *ptr;
+
+#define MAP(str, reloc) { str, sizeof (str) - 1, 1, 1, reloc }
+#define MAP32(str, reloc) { str, sizeof (str) - 1, 1, 0, reloc }
+#define MAP64(str, reloc) { str, sizeof (str) - 1, 0, 1, reloc }
+
+ static const struct map_bfd mapping[] = {
+ MAP ("l", BFD_RELOC_LO16),
+ MAP ("h", BFD_RELOC_HI16),
+ MAP ("ha", BFD_RELOC_HI16_S),
+ MAP ("brtaken", BFD_RELOC_PPC_B16_BRTAKEN),
+ MAP ("brntaken", BFD_RELOC_PPC_B16_BRNTAKEN),
+ MAP ("got", BFD_RELOC_16_GOTOFF),
+ MAP ("got@l", BFD_RELOC_LO16_GOTOFF),
+ MAP ("got@h", BFD_RELOC_HI16_GOTOFF),
+ MAP ("got@ha", BFD_RELOC_HI16_S_GOTOFF),
+ MAP ("plt@l", BFD_RELOC_LO16_PLTOFF),
+ MAP ("plt@h", BFD_RELOC_HI16_PLTOFF),
+ MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF),
+ MAP ("copy", BFD_RELOC_PPC_COPY),
+ MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT),
+ MAP ("sectoff", BFD_RELOC_16_BASEREL),
+ MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL),
+ MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL),
+ MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL),
+ MAP ("tls", BFD_RELOC_PPC_TLS),
+ MAP ("dtpmod", BFD_RELOC_PPC_DTPMOD),
+ MAP ("dtprel", BFD_RELOC_PPC_DTPREL),
+ MAP ("dtprel@l", BFD_RELOC_PPC_DTPREL16_LO),
+ MAP ("dtprel@h", BFD_RELOC_PPC_DTPREL16_HI),
+ MAP ("dtprel@ha", BFD_RELOC_PPC_DTPREL16_HA),
+ MAP ("tprel", BFD_RELOC_PPC_TPREL),
+ MAP ("tprel@l", BFD_RELOC_PPC_TPREL16_LO),
+ MAP ("tprel@h", BFD_RELOC_PPC_TPREL16_HI),
+ MAP ("tprel@ha", BFD_RELOC_PPC_TPREL16_HA),
+ MAP ("got@tlsgd", BFD_RELOC_PPC_GOT_TLSGD16),
+ MAP ("got@tlsgd@l", BFD_RELOC_PPC_GOT_TLSGD16_LO),
+ MAP ("got@tlsgd@h", BFD_RELOC_PPC_GOT_TLSGD16_HI),
+ MAP ("got@tlsgd@ha", BFD_RELOC_PPC_GOT_TLSGD16_HA),
+ MAP ("got@tlsld", BFD_RELOC_PPC_GOT_TLSLD16),
+ MAP ("got@tlsld@l", BFD_RELOC_PPC_GOT_TLSLD16_LO),
+ MAP ("got@tlsld@h", BFD_RELOC_PPC_GOT_TLSLD16_HI),
+ MAP ("got@tlsld@ha", BFD_RELOC_PPC_GOT_TLSLD16_HA),
+ MAP ("got@dtprel", BFD_RELOC_PPC_GOT_DTPREL16),
+ MAP ("got@dtprel@l", BFD_RELOC_PPC_GOT_DTPREL16_LO),
+ MAP ("got@dtprel@h", BFD_RELOC_PPC_GOT_DTPREL16_HI),
+ MAP ("got@dtprel@ha", BFD_RELOC_PPC_GOT_DTPREL16_HA),
+ MAP ("got@tprel", BFD_RELOC_PPC_GOT_TPREL16),
+ MAP ("got@tprel@l", BFD_RELOC_PPC_GOT_TPREL16_LO),
+ MAP ("got@tprel@h", BFD_RELOC_PPC_GOT_TPREL16_HI),
+ MAP ("got@tprel@ha", BFD_RELOC_PPC_GOT_TPREL16_HA),
+ MAP32 ("fixup", BFD_RELOC_CTOR),
+ MAP32 ("plt", BFD_RELOC_24_PLT_PCREL),
+ MAP32 ("pltrel24", BFD_RELOC_24_PLT_PCREL),
+ MAP32 ("local24pc", BFD_RELOC_PPC_LOCAL24PC),
+ MAP32 ("local", BFD_RELOC_PPC_LOCAL24PC),
+ MAP32 ("pltrel", BFD_RELOC_32_PLT_PCREL),
+ MAP32 ("sdarel", BFD_RELOC_GPREL16),
+ MAP32 ("sdarel@l", BFD_RELOC_PPC_VLE_SDAREL_LO16A),
+ MAP32 ("sdarel@h", BFD_RELOC_PPC_VLE_SDAREL_HI16A),
+ MAP32 ("sdarel@ha", BFD_RELOC_PPC_VLE_SDAREL_HA16A),
+ MAP32 ("naddr", BFD_RELOC_PPC_EMB_NADDR32),
+ MAP32 ("naddr16", BFD_RELOC_PPC_EMB_NADDR16),
+ MAP32 ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO),
+ MAP32 ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI),
+ MAP32 ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA),
+ MAP32 ("sdai16", BFD_RELOC_PPC_EMB_SDAI16),
+ MAP32 ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL),
+ MAP32 ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16),
+ MAP32 ("sda21", BFD_RELOC_PPC_EMB_SDA21),
+ MAP32 ("sda21@l", BFD_RELOC_PPC_VLE_SDA21_LO),
+ MAP32 ("mrkref", BFD_RELOC_PPC_EMB_MRKREF),
+ MAP32 ("relsect", BFD_RELOC_PPC_EMB_RELSEC16),
+ MAP32 ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO),
+ MAP32 ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI),
+ MAP32 ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA),
+ MAP32 ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD),
+ MAP32 ("relsda", BFD_RELOC_PPC_EMB_RELSDA),
+ MAP32 ("xgot", BFD_RELOC_PPC_TOC16),
+ MAP64 ("higher", BFD_RELOC_PPC64_HIGHER),
+ MAP64 ("highera", BFD_RELOC_PPC64_HIGHER_S),
+ MAP64 ("highest", BFD_RELOC_PPC64_HIGHEST),
+ MAP64 ("highesta", BFD_RELOC_PPC64_HIGHEST_S),
+ MAP64 ("tocbase", BFD_RELOC_PPC64_TOC),
+ MAP64 ("toc", BFD_RELOC_PPC_TOC16),
+ 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@higher", BFD_RELOC_PPC64_DTPREL16_HIGHER),
+ MAP64 ("dtprel@highera", BFD_RELOC_PPC64_DTPREL16_HIGHERA),
+ MAP64 ("dtprel@highest", BFD_RELOC_PPC64_DTPREL16_HIGHEST),
+ MAP64 ("dtprel@highesta", BFD_RELOC_PPC64_DTPREL16_HIGHESTA),
+ MAP64 ("tprel@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 }
+ };
+
+ if (*str++ != '@')
+ return BFD_RELOC_UNUSED;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = TOLOWER (ch);
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->string[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->string, ptr->length) == 0
+ && (ppc_obj64 ? ptr->valid64 : ptr->valid32))
+ {
+ int reloc = ptr->reloc;
+
+ if (!ppc_obj64 && exp_p->X_add_number != 0)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16_GOTOFF:
+ case BFD_RELOC_HI16_GOTOFF:
+ case BFD_RELOC_HI16_S_GOTOFF:
+ as_warn (_("identifier+constant@got means "
+ "identifier@got+constant"));
+ break;
+
+ case BFD_RELOC_PPC_GOT_TLSGD16:
+ case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+ case BFD_RELOC_PPC_GOT_TLSLD16:
+ case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16_HI:
+ case BFD_RELOC_PPC_GOT_TPREL16_HA:
+ as_bad (_("symbol+offset not supported for got tls"));
+ break;
+ }
+ }
+
+ /* Now check for identifier@suffix+constant. */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ 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;
+ }
+ *str_p = str;
+
+ if (reloc == (int) BFD_RELOC_PPC64_TOC
+ && exp_p->X_op == O_symbol
+ && strcmp (S_GET_NAME (exp_p->X_add_symbol), ".TOC.") == 0)
+ {
+ /* Change the symbol so that the dummy .TOC. symbol can be
+ omitted from the object file. */
+ exp_p->X_add_symbol = &abs_symbol;
+ }
+
+ return (bfd_reloc_code_real_type) reloc;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+/* 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;
+ }
+
+ 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;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ size = bfd_get_reloc_size (reloc_howto);
+
+ 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);
+ }
+ 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. */
+static void
+ppc_elf_rdata (int xxx)
+{
+ char *save_line = input_line_pointer;
+ static char section[] = ".rodata\n";
+
+ /* Just pretend this is .section .rodata */
+ input_line_pointer = section;
+ obj_elf_section (xxx);
+
+ input_line_pointer = save_line;
+}
+
+/* Pseudo op to make file scope bss items. */
+static void
+ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+ offsetT align;
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_bad (_("length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Allocate_bss. */
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ if (align)
+ {
+ /* Convert to a power of 2 alignment. */
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2);
+ if (align != 1)
+ {
+ as_bad (_("common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ record_alignment (bss_section, align2);
+ subseg_set (bss_section, 1);
+ if (align2)
+ frag_align (align2, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, bss_section);
+ subseg_set (old_sec, old_subsec);
+ demand_empty_rest_of_line ();
+}
+
+/* Validate any relocations emitted for -mrelocatable, possibly adding
+ fixups for word relocations in writable segments, so we can adjust
+ them at runtime. */
+static void
+ppc_elf_validate_fix (fixS *fixp, segT seg)
+{
+ if (fixp->fx_done || fixp->fx_pcrel)
+ return;
+
+ switch (shlib)
+ {
+ case SHLIB_NONE:
+ case SHLIB_PIC:
+ return;
+
+ case SHLIB_MRELOCATABLE:
+ if (fixp->fx_r_type <= BFD_RELOC_UNUSED
+ && 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
+ && fixp->fx_r_type != BFD_RELOC_16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_LO16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL
+ && (seg->flags & SEC_LOAD) != 0
+ && strcmp (segment_name (seg), ".got2") != 0
+ && strcmp (segment_name (seg), ".dtors") != 0
+ && strcmp (segment_name (seg), ".ctors") != 0
+ && strcmp (segment_name (seg), ".fixup") != 0
+ && strcmp (segment_name (seg), ".gcc_except_table") != 0
+ && strcmp (segment_name (seg), ".eh_frame") != 0
+ && strcmp (segment_name (seg), ".ex_shared") != 0)
+ {
+ if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0
+ || fixp->fx_r_type != BFD_RELOC_CTOR)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("relocation cannot be done when using -mrelocatable"));
+ }
+ }
+ return;
+ }
+}
+
+/* Prevent elf_frob_file_before_adjust removing a weak undefined
+ function descriptor sym if the corresponding code sym is used. */
+
+void
+ppc_frob_file_before_adjust (void)
+{
+ symbolS *symp;
+ asection *toc;
+
+ if (!ppc_obj64)
+ return;
+
+ for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+ {
+ const char *name;
+ char *dotname;
+ symbolS *dotsym;
+ size_t len;
+
+ name = S_GET_NAME (symp);
+ if (name[0] == '.')
+ continue;
+
+ if (! S_IS_WEAK (symp)
+ || S_IS_DEFINED (symp))
+ continue;
+
+ len = strlen (name) + 1;
+ dotname = xmalloc (len + 1);
+ dotname[0] = '.';
+ memcpy (dotname + 1, name, len);
+ dotsym = symbol_find_noref (dotname, 1);
+ free (dotname);
+ if (dotsym != NULL && (symbol_used_p (dotsym)
+ || symbol_used_in_reloc_p (dotsym)))
+ symbol_mark_used (symp);
+
+ }
+
+ toc = bfd_get_section_by_name (stdoutput, ".toc");
+ if (toc != NULL
+ && toc_reloc_types != has_large_toc_reloc
+ && bfd_section_size (stdoutput, toc) > 0x10000)
+ as_warn (_("TOC section size exceeds 64k"));
+}
+
+/* .TOC. used in an opd entry as .TOC.@tocbase doesn't need to be
+ emitted. Other uses of .TOC. will cause the symbol to be marked
+ with BSF_KEEP in md_apply_fix. */
+
+void
+ppc_elf_adjust_symtab (void)
+{
+ if (ppc_obj64)
+ {
+ symbolS *symp;
+ symp = symbol_find (".TOC.");
+ if (symp != NULL)
+ {
+ asymbol *bsym = symbol_get_bfdsym (symp);
+ if ((bsym->flags & BSF_KEEP) == 0)
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
+ }
+}
+#endif /* OBJ_ELF */
+
+#ifdef TE_PE
+
+/*
+ * Summary of parse_toc_entry.
+ *
+ * in: Input_line_pointer points to the '[' in one of:
+ *
+ * [toc] [tocv] [toc32] [toc64]
+ *
+ * Anything else is an error of one kind or another.
+ *
+ * out:
+ * return value: success or failure
+ * toc_kind: kind of toc reference
+ * input_line_pointer:
+ * success: first char after the ']'
+ * failure: unchanged
+ *
+ * settings:
+ *
+ * [toc] - rv == success, toc_kind = default_toc
+ * [tocv] - rv == success, toc_kind = data_in_toc
+ * [toc32] - rv == success, toc_kind = must_be_32
+ * [toc64] - rv == success, toc_kind = must_be_64
+ *
+ */
+
+enum toc_size_qualifier
+{
+ default_toc, /* The toc cell constructed should be the system default size */
+ data_in_toc, /* This is a direct reference to a toc cell */
+ must_be_32, /* The toc cell constructed must be 32 bits wide */
+ must_be_64 /* The toc cell constructed must be 64 bits wide */
+};
+
+static int
+parse_toc_entry (enum toc_size_qualifier *toc_kind)
+{
+ char *start;
+ char *toc_spec;
+ char c;
+ enum toc_size_qualifier t;
+
+ /* Save the input_line_pointer. */
+ start = input_line_pointer;
+
+ /* Skip over the '[' , and whitespace. */
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ /* Find the spelling of the operand. */
+ toc_spec = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcmp (toc_spec, "toc") == 0)
+ {
+ t = default_toc;
+ }
+ else if (strcmp (toc_spec, "tocv") == 0)
+ {
+ t = data_in_toc;
+ }
+ else if (strcmp (toc_spec, "toc32") == 0)
+ {
+ t = must_be_32;
+ }
+ else if (strcmp (toc_spec, "toc64") == 0)
+ {
+ t = must_be_64;
+ }
+ else
+ {
+ as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec);
+ *input_line_pointer = c;
+ input_line_pointer = start;
+ return 0;
+ }
+
+ /* Now find the ']'. */
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE (); /* leading whitespace could be there. */
+ c = *input_line_pointer++; /* input_line_pointer->past char in c. */
+
+ if (c != ']')
+ {
+ as_bad (_("syntax error: expected `]', found `%c'"), c);
+ input_line_pointer = start;
+ return 0;
+ }
+
+ *toc_kind = t;
+ return 1;
+}
+#endif
+
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+/* See whether a symbol is in the TOC section. */
+
+static int
+ppc_is_toc_sym (symbolS *sym)
+{
+#ifdef OBJ_XCOFF
+ return (symbol_get_tc (sym)->symbol_class == XMC_TC
+ || symbol_get_tc (sym)->symbol_class == XMC_TC0);
+#endif
+#ifdef OBJ_ELF
+ const char *sname = segment_name (S_GET_SEGMENT (sym));
+ if (ppc_obj64)
+ return strcmp (sname, ".toc") == 0;
+ else
+ return strcmp (sname, ".got") == 0;
+#endif
+}
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+
+
+#ifdef OBJ_ELF
+#define APUID(a,v) ((((a) & 0xffff) << 16) | ((v) & 0xffff))
+static void
+ppc_apuinfo_section_add (unsigned int apu, unsigned int version)
+{
+ unsigned int i;
+
+ /* Check we don't already exist. */
+ for (i = 0; i < ppc_apuinfo_num; i++)
+ if (ppc_apuinfo_list[i] == APUID (apu, version))
+ return;
+
+ if (ppc_apuinfo_num == ppc_apuinfo_num_alloc)
+ {
+ if (ppc_apuinfo_num_alloc == 0)
+ {
+ ppc_apuinfo_num_alloc = 4;
+ ppc_apuinfo_list = (unsigned long *)
+ xmalloc (sizeof (unsigned long) * ppc_apuinfo_num_alloc);
+ }
+ else
+ {
+ ppc_apuinfo_num_alloc += 4;
+ ppc_apuinfo_list = (unsigned long *) xrealloc (ppc_apuinfo_list,
+ sizeof (unsigned long) * ppc_apuinfo_num_alloc);
+ }
+ }
+ ppc_apuinfo_list[ppc_apuinfo_num++] = APUID (apu, version);
+}
+#undef APUID
+#endif
+
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct ppc_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+#define MAX_INSN_FIXUPS (5)
+
+/* Form I16L. */
+#define E_OR2I_INSN 0x7000C000
+#define E_AND2I_DOT_INSN 0x7000C800
+#define E_OR2IS_INSN 0x7000D000
+#define E_LIS_INSN 0x7000E000
+#define E_AND2IS_DOT_INSN 0x7000E800
+
+/* Form I16A. */
+#define E_ADD2I_DOT_INSN 0x70008800
+#define E_ADD2IS_INSN 0x70009000
+#define E_CMP16I_INSN 0x70009800
+#define E_MULL2I_INSN 0x7000A000
+#define E_CMPL16I_INSN 0x7000A800
+#define E_CMPH16I_INSN 0x7000B000
+#define E_CMPHL16I_INSN 0x7000B800
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (char *str)
+{
+ char *s;
+ const struct powerpc_opcode *opcode;
+ unsigned long insn;
+ const unsigned char *opindex_ptr;
+ int skip_optional;
+ int need_paren;
+ int next_opindex;
+ struct ppc_fixup fixups[MAX_INSN_FIXUPS];
+ int fc;
+ char *f;
+ int addr_mod;
+ int i;
+ unsigned int insn_length;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str);
+ if (opcode == (const struct powerpc_opcode *) NULL)
+ {
+ const struct powerpc_macro *macro;
+
+ macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str);
+ if (macro == (const struct powerpc_macro *) NULL)
+ as_bad (_("unrecognized opcode: `%s'"), str);
+ else
+ ppc_macro (s, macro);
+
+ return;
+ }
+
+ insn = opcode->opcode;
+
+ str = s;
+ while (ISSPACE (*str))
+ ++str;
+
+ /* PowerPC operands are just expressions. The only real issue is
+ that a few operand types are optional. All cases which might use
+ an optional operand separate the operands only with commas (in some
+ cases parentheses are used, as in ``lwz 1,0(1)'' but such cases never
+ have optional operands). Most instructions with optional operands
+ have only one. Those that have more than one optional operand can
+ take either all their operands or none. So, before we start seriously
+ parsing the operands, we check to see if we have optional operands,
+ and if we do, we count the number of commas to see which operands
+ have been omitted. */
+ skip_optional = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct powerpc_operand *operand;
+
+ operand = &powerpc_operands[*opindex_ptr];
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
+ {
+ unsigned int opcount;
+ unsigned int num_operands_expected;
+
+ /* There is an optional operand. Count the number of
+ commas in the input line. */
+ if (*str == '\0')
+ opcount = 0;
+ else
+ {
+ opcount = 1;
+ s = str;
+ while ((s = strchr (s, ',')) != (char *) NULL)
+ {
+ ++opcount;
+ ++s;
+ }
+ }
+
+ /* Compute the number of expected operands.
+ Do not count fake operands. */
+ for (num_operands_expected = 0, i = 0; opcode->operands[i]; i ++)
+ if ((powerpc_operands [opcode->operands[i]].flags & PPC_OPERAND_FAKE) == 0)
+ ++ num_operands_expected;
+
+ /* If there are fewer operands in the line then are called
+ for by the instruction, we want to skip the optional
+ operands. */
+ if (opcount < num_operands_expected)
+ skip_optional = 1;
+
+ break;
+ }
+ }
+
+ /* Gather the operands. */
+ need_paren = 0;
+ next_opindex = 0;
+ fc = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct powerpc_operand *operand;
+ const char *errmsg;
+ char *hold;
+ expressionS ex;
+ char endc;
+
+ if (next_opindex == 0)
+ operand = &powerpc_operands[*opindex_ptr];
+ else
+ {
+ operand = &powerpc_operands[next_opindex];
+ next_opindex = 0;
+ }
+ errmsg = NULL;
+
+ /* If this is a fake operand, then we do not expect anything
+ from the input. */
+ if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+ {
+ insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad ("%s", errmsg);
+ continue;
+ }
+
+ /* If this is an optional operand, and we are skipping it, just
+ insert a zero. */
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+ && skip_optional)
+ {
+ if (operand->insert)
+ {
+ insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad ("%s", errmsg);
+ }
+ if ((operand->flags & PPC_OPERAND_NEXT) != 0)
+ next_opindex = *opindex_ptr + 1;
+ continue;
+ }
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+#ifdef TE_PE
+ if (*input_line_pointer == '[')
+ {
+ /* We are expecting something like the second argument here:
+ *
+ * lwz r4,[toc].GS.0.static_int(rtoc)
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * The argument following the `]' must be a symbol name, and the
+ * register must be the toc register: 'rtoc' or '2'
+ *
+ * The effect is to 0 as the displacement field
+ * in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or
+ * the appropriate variation) reloc against it based on the symbol.
+ * The linker will build the toc, and insert the resolved toc offset.
+ *
+ * Note:
+ * o The size of the toc entry is currently assumed to be
+ * 32 bits. This should not be assumed to be a hard coded
+ * number.
+ * o In an effort to cope with a change from 32 to 64 bits,
+ * there are also toc entries that are specified to be
+ * either 32 or 64 bits:
+ * lwz r4,[toc32].GS.0.static_int(rtoc)
+ * lwz r4,[toc64].GS.0.static_int(rtoc)
+ * These demand toc entries of the specified size, and the
+ * instruction probably requires it.
+ */
+
+ int valid_toc;
+ enum toc_size_qualifier toc_kind;
+ bfd_reloc_code_real_type toc_reloc;
+
+ /* Go parse off the [tocXX] part. */
+ valid_toc = parse_toc_entry (&toc_kind);
+
+ if (!valid_toc)
+ {
+ ignore_rest_of_line ();
+ break;
+ }
+
+ /* Now get the symbol following the ']'. */
+ expression (&ex);
+
+ switch (toc_kind)
+ {
+ case default_toc:
+ /* In this case, we may not have seen the symbol yet,
+ since it is allowed to appear on a .extern or .globl
+ or just be a label in the .data section. */
+ toc_reloc = BFD_RELOC_PPC_TOC16;
+ break;
+ case data_in_toc:
+ /* 1. The symbol must be defined and either in the toc
+ section, or a global.
+ 2. The reloc generated must have the TOCDEFN flag set
+ in upper bit mess of the reloc type.
+ FIXME: It's a little confusing what the tocv
+ qualifier can be used for. At the very least, I've
+ seen three uses, only one of which I'm sure I can
+ explain. */
+ if (ex.X_op == O_symbol)
+ {
+ gas_assert (ex.X_add_symbol != NULL);
+ if (symbol_get_bfdsym (ex.X_add_symbol)->section
+ != tocdata_section)
+ {
+ as_bad (_("[tocv] symbol is not a toc symbol"));
+ }
+ }
+
+ toc_reloc = BFD_RELOC_PPC_TOC16;
+ break;
+ case must_be_32:
+ /* 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;
+ as_bad (_("unimplemented toc32 expression modifier"));
+ break;
+ case must_be_64:
+ /* FIXME: see above. */
+ toc_reloc = BFD_RELOC_UNUSED;
+ as_bad (_("unimplemented toc64 expression modifier"));
+ break;
+ default:
+ fprintf (stderr,
+ _("Unexpected return value [%d] from parse_toc_entry!\n"),
+ toc_kind);
+ abort ();
+ break;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].reloc = toc_reloc;
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ ++fc;
+
+ /* Ok. We've set up the fixup for the instruction. Now make it
+ look like the constant 0 was found here. */
+ ex.X_unsigned = 1;
+ ex.X_op = O_constant;
+ ex.X_add_number = 0;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ }
+
+ else
+#endif /* TE_PE */
+ {
+ 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;
+ }
+ }
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (ex.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (ex.X_op == O_absent)
+ as_bad (_("missing operand"));
+ else if (ex.X_op == O_register)
+ {
+ insn = ppc_insert_operand (insn, operand, ex.X_add_number,
+ ppc_cpu, (char *) NULL, 0);
+ }
+ else if (ex.X_op == O_constant)
+ {
+#ifdef OBJ_ELF
+ /* Allow @HA, @L, @H on constants. */
+ bfd_reloc_code_real_type reloc;
+ char *orig_str = str;
+
+ if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+ switch (reloc)
+ {
+ default:
+ str = orig_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 = 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ break;
+ }
+#endif /* OBJ_ELF */
+ insn = ppc_insert_operand (insn, operand, ex.X_add_number,
+ ppc_cpu, (char *) NULL, 0);
+ }
+ else
+ {
+ bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
+#ifdef OBJ_ELF
+ if (ex.X_op == O_symbol && str[0] == '(')
+ {
+ const char *sym_name = S_GET_NAME (ex.X_add_symbol);
+ if (sym_name[0] == '.')
+ ++sym_name;
+
+ if (strcasecmp (sym_name, "__tls_get_addr") == 0)
+ {
+ expressionS tls_exp;
+
+ hold = input_line_pointer;
+ input_line_pointer = str + 1;
+ expression (&tls_exp);
+ if (tls_exp.X_op == O_symbol)
+ {
+ reloc = BFD_RELOC_UNUSED;
+ if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
+ {
+ reloc = BFD_RELOC_PPC_TLSGD;
+ input_line_pointer += 7;
+ }
+ else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0)
+ {
+ reloc = BFD_RELOC_PPC_TLSLD;
+ input_line_pointer += 7;
+ }
+ if (reloc != BFD_RELOC_UNUSED)
+ {
+ SKIP_WHITESPACE ();
+ str = input_line_pointer;
+
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = tls_exp;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+ }
+ input_line_pointer = hold;
+ }
+ }
+
+ if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+ {
+ /* Some TLS tweaks. */
+ switch (reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_PPC_TLS:
+ if (!_bfd_elf_ppc_at_tls_transform (opcode->opcode, 0))
+ as_bad (_("@tls may not be used with \"%s\" operands"),
+ opcode->name);
+ else if (operand->shift != 11)
+ as_bad (_("@tls may only be used in last operand"));
+ else
+ insn = ppc_insert_operand (insn, operand,
+ ppc_obj64 ? 13 : 2,
+ ppc_cpu, (char *) NULL, 0);
+ break;
+
+ /* We'll only use the 32 (or 64) bit form of these relocations
+ in constants. Instructions get the 16 bit form. */
+ case BFD_RELOC_PPC_DTPREL:
+ reloc = BFD_RELOC_PPC_DTPREL16;
+ break;
+ case BFD_RELOC_PPC_TPREL:
+ reloc = BFD_RELOC_PPC_TPREL16;
+ break;
+ }
+
+ /* If VLE-mode convert LO/HI/HA relocations. */
+ if (opcode->flags & PPC_OPCODE_VLE)
+ {
+ int tmp_insn = insn & opcode->mask;
+
+ int use_d_reloc = (tmp_insn == E_OR2I_INSN
+ || tmp_insn == E_AND2I_DOT_INSN
+ || tmp_insn == E_OR2IS_INSN
+ || tmp_insn == E_LIS_INSN
+ || tmp_insn == E_AND2IS_DOT_INSN);
+
+
+ int use_a_reloc = (tmp_insn == E_ADD2I_DOT_INSN
+ || tmp_insn == E_ADD2IS_INSN
+ || tmp_insn == E_CMP16I_INSN
+ || tmp_insn == E_MULL2I_INSN
+ || tmp_insn == E_CMPL16I_INSN
+ || tmp_insn == E_CMPH16I_INSN
+ || tmp_insn == E_CMPHL16I_INSN);
+
+ switch (reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_PPC_EMB_SDA21:
+ reloc = BFD_RELOC_PPC_VLE_SDA21;
+ break;
+
+ case BFD_RELOC_LO16:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_LO16D;
+ else if (use_a_reloc)
+ reloc = BFD_RELOC_PPC_VLE_LO16A;
+ break;
+
+ case BFD_RELOC_HI16:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HI16D;
+ else if (use_a_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HI16A;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HA16D;
+ else if (use_a_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HA16A;
+ break;
+
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_SDAREL_LO16D;
+ break;
+
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_SDAREL_HI16D;
+ break;
+
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_SDAREL_HA16D;
+ 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)
+ ;
+ /* 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
+ && operand->bitm == 0x3fffffc
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_B26;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bitm == 0xfffc
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_B16;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bitm == 0x1fe
+ && operand->shift == -1)
+ reloc = BFD_RELOC_PPC_VLE_REL8;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bitm == 0xfffe
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_VLE_REL15;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && 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
+ && (operand->bitm & 0xfff0) == 0xfff0
+ && operand->shift == 0)
+ {
+ /* Note: the symbol may be not yet defined. */
+ if (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;
+#endif
+ }
+ else
+ {
+ reloc = BFD_RELOC_16;
+#ifdef OBJ_ELF
+ if (ppc_obj64
+ && (operand->flags & PPC_OPERAND_DS) != 0)
+ reloc = BFD_RELOC_PPC64_ADDR16_DS;
+#endif
+ }
+ }
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+
+ if (need_paren)
+ {
+ endc = ')';
+ need_paren = 0;
+ /* If expecting more operands, then we want to see "),". */
+ if (*str == endc && opindex_ptr[1] != 0)
+ {
+ do
+ ++str;
+ while (ISSPACE (*str));
+ endc = ',';
+ }
+ }
+ else if ((operand->flags & PPC_OPERAND_PARENS) != 0)
+ {
+ endc = '(';
+ need_paren = 1;
+ }
+ else
+ endc = ',';
+
+ /* The call to expression should have advanced str past any
+ whitespace. */
+ if (*str != endc
+ && (endc != ',' || *str != '\0'))
+ {
+ if (*str == '\0')
+ as_bad (_("syntax error; end of line, expected `%c'"), endc);
+ else
+ as_bad (_("syntax error; found `%c', expected `%c'"), *str, endc);
+ break;
+ }
+
+ if (*str != '\0')
+ ++str;
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+#ifdef OBJ_ELF
+ /* Do we need/want an APUinfo section? */
+ if ((ppc_cpu & (PPC_OPCODE_E500 | PPC_OPCODE_E500MC | PPC_OPCODE_VLE)) != 0
+ && !ppc_obj64)
+ {
+ /* These are all version "1". */
+ if (opcode->flags & PPC_OPCODE_SPE)
+ ppc_apuinfo_section_add (PPC_APUINFO_SPE, 1);
+ if (opcode->flags & PPC_OPCODE_ISEL)
+ ppc_apuinfo_section_add (PPC_APUINFO_ISEL, 1);
+ if (opcode->flags & PPC_OPCODE_EFS)
+ ppc_apuinfo_section_add (PPC_APUINFO_EFS, 1);
+ if (opcode->flags & PPC_OPCODE_BRLOCK)
+ ppc_apuinfo_section_add (PPC_APUINFO_BRLOCK, 1);
+ if (opcode->flags & PPC_OPCODE_PMR)
+ ppc_apuinfo_section_add (PPC_APUINFO_PMR, 1);
+ if (opcode->flags & PPC_OPCODE_CACHELCK)
+ 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)
+ ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
+ }
+#endif
+
+ /* Write out the instruction. */
+ /* Differentiate between two and four byte insns. */
+ if (ppc_mach () == bfd_mach_ppc_vle)
+ {
+ if (PPC_OP_SE_VLE (insn))
+ insn_length = 2;
+ else
+ insn_length = 4;
+ addr_mod = frag_now_fix () & 1;
+ }
+ else
+ {
+ insn_length = 4;
+ addr_mod = frag_now_fix () & 3;
+ }
+ /* All instructions can start on a 2 byte boundary for VLE. */
+ f = frag_more (insn_length);
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ {
+ if (ppc_mach() == bfd_mach_ppc_vle)
+ as_bad (_("instruction address is not a multiple of 2"));
+ else
+ as_bad (_("instruction address is not a multiple of 4"));
+ }
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+ md_number_to_chars (f, insn, insn_length);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (insn_length);
+#endif
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ fixS *fixP;
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+ int offset;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+ offset = target_big_endian ? (insn_length - size) : 0;
+
+ if (size < 1 || size > 4)
+ abort ();
+
+ fixP = fix_new_exp (frag_now,
+ f - frag_now->fr_literal + offset,
+ size,
+ &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+ }
+ else
+ {
+ const struct powerpc_operand *operand;
+
+ operand = &powerpc_operands[fixups[i].opindex];
+ fixP = fix_new_exp (frag_now,
+ f - frag_now->fr_literal,
+ insn_length,
+ &fixups[i].exp,
+ (operand->flags & PPC_OPERAND_RELATIVE) != 0,
+ BFD_RELOC_UNUSED);
+ }
+ 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 = (char *) alloca (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);
+}
+
+#ifdef OBJ_ELF
+/* For ELF, add support for SHT_ORDERED. */
+
+int
+ppc_section_type (char *str, size_t len)
+{
+ if (len == 7 && strncmp (str, "ordered", 7) == 0)
+ return SHT_ORDERED;
+
+ return -1;
+}
+
+int
+ppc_section_flags (flagword flags, bfd_vma attr ATTRIBUTE_UNUSED, int type)
+{
+ if (type == SHT_ORDERED)
+ flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES;
+
+ return flags;
+}
+#endif /* OBJ_ELF */
+
+
+/* Pseudo-op handling. */
+
+/* The .byte pseudo-op. This is similar to the normal .byte
+ pseudo-op, but it can also take a single ASCII string. */
+
+static void
+ppc_byte (int ignore ATTRIBUTE_UNUSED)
+{
+ if (*input_line_pointer != '\"')
+ {
+ cons (1);
+ return;
+ }
+
+ /* Gather characters. A real double quote is doubled. Unusual
+ characters are not permitted. */
+ ++input_line_pointer;
+ while (1)
+ {
+ char c;
+
+ c = *input_line_pointer++;
+
+ if (c == '\"')
+ {
+ if (*input_line_pointer != '\"')
+ break;
+ ++input_line_pointer;
+ }
+
+ FRAG_APPEND_1_CHAR (c);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+#ifdef OBJ_XCOFF
+
+/* XCOFF specific pseudo-op handling. */
+
+/* This is set if we are creating a .stabx symbol, since we don't want
+ to handle symbol suffixes for such symbols. */
+static bfd_boolean ppc_stab_symbol;
+
+/* 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
+ aligns .comm and .lcomm to 4 bytes. */
+
+static void
+ppc_comm (int lcomm)
+{
+ asection *current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ char *name;
+ char endc;
+ char *end_name;
+ offsetT size;
+ offsetT align;
+ symbolS *lcomm_sym = NULL;
+ symbolS *sym;
+ char *pfrag;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing size"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ size = get_absolute_expression ();
+ if (size < 0)
+ {
+ as_bad (_("negative size"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (! lcomm)
+ {
+ /* The third argument to .comm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 2;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 2;
+ }
+ }
+ }
+ else
+ {
+ char *lcomm_name;
+ char lcomm_endc;
+
+ /* 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
+ argument. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing real symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ lcomm_name = input_line_pointer;
+ lcomm_endc = get_symbol_end ();
+
+ 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';
+ sym = symbol_find_or_make (name);
+ *end_name = endc;
+
+ if (S_IS_DEFINED (sym)
+ || S_GET_VALUE (sym) != 0)
+ {
+ as_bad (_("attempt to redefine symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ record_alignment (bss_section, align);
+
+ if (! lcomm
+ || ! S_IS_DEFINED (lcomm_sym))
+ {
+ symbolS *def_sym;
+ offsetT def_size;
+
+ if (! lcomm)
+ {
+ def_sym = sym;
+ def_size = size;
+ S_SET_EXTERNAL (sym);
+ }
+ else
+ {
+ symbol_get_tc (lcomm_sym)->output = 1;
+ def_sym = lcomm_sym;
+ def_size = 0;
+ }
+
+ subseg_set (bss_section, 1);
+ frag_align (align, 0, 0);
+
+ symbol_set_frag (def_sym, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym,
+ def_size, (char *) NULL);
+ *pfrag = 0;
+ S_SET_SEGMENT (def_sym, bss_section);
+ symbol_get_tc (def_sym)->align = align;
+ }
+ else if (lcomm)
+ {
+ /* Align the size of lcomm_sym. */
+ symbol_get_frag (lcomm_sym)->fr_offset =
+ ((symbol_get_frag (lcomm_sym)->fr_offset + (1 << align) - 1)
+ &~ ((1 << align) - 1));
+ if (align > symbol_get_tc (lcomm_sym)->align)
+ symbol_get_tc (lcomm_sym)->align = align;
+ }
+
+ if (lcomm)
+ {
+ /* Make sym an offset from lcomm_sym. */
+ S_SET_SEGMENT (sym, bss_section);
+ symbol_set_frag (sym, symbol_get_frag (lcomm_sym));
+ S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset);
+ symbol_get_frag (lcomm_sym)->fr_offset += size;
+ }
+
+ subseg_set (current_seg, current_subseg);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .csect pseudo-op. This switches us into a different
+ subsegment. The first argument is a symbol whose value is the
+ start of the .csect. In COFF, csect symbols get special aux
+ entries defined by the x_csect field of union internal_auxent. The
+ optional second argument is the alignment (the default is 2). */
+
+static void
+ppc_csect (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+ offsetT align;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (S_GET_NAME (sym)[0] == '\0')
+ {
+ /* An unnamed csect is assumed to be [PR]. */
+ symbol_get_tc (sym)->symbol_class = XMC_PR;
+ }
+
+ align = 2;
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ }
+
+ ppc_change_csect (sym, align);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Change to a different csect. */
+
+static void
+ppc_change_csect (symbolS *sym, offsetT align)
+{
+ if (S_IS_DEFINED (sym))
+ subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg);
+ else
+ {
+ symbolS **list_ptr;
+ int after_toc;
+ int hold_chunksize;
+ symbolS *list;
+ int is_code;
+ segT sec;
+
+ /* This is a new csect. We need to look at the symbol class to
+ figure out whether it should go in the text section or the
+ data section. */
+ after_toc = 0;
+ is_code = 0;
+ switch (symbol_get_tc (sym)->symbol_class)
+ {
+ case XMC_PR:
+ case XMC_RO:
+ case XMC_DB:
+ case XMC_GL:
+ case XMC_XO:
+ case XMC_SV:
+ case XMC_TI:
+ case XMC_TB:
+ S_SET_SEGMENT (sym, text_section);
+ symbol_get_tc (sym)->subseg = ppc_text_subsegment;
+ ++ppc_text_subsegment;
+ list_ptr = &ppc_text_csects;
+ is_code = 1;
+ break;
+ case XMC_RW:
+ case XMC_TC0:
+ case XMC_TC:
+ case XMC_DS:
+ case XMC_UA:
+ case XMC_BS:
+ case XMC_UC:
+ if (ppc_toc_csect != NULL
+ && (symbol_get_tc (ppc_toc_csect)->subseg + 1
+ == ppc_data_subsegment))
+ after_toc = 1;
+ S_SET_SEGMENT (sym, data_section);
+ symbol_get_tc (sym)->subseg = ppc_data_subsegment;
+ ++ppc_data_subsegment;
+ list_ptr = &ppc_data_csects;
+ break;
+ default:
+ abort ();
+ }
+
+ /* We set the obstack chunk size to a small value before
+ changing subsegments, so that we don't use a lot of memory
+ space for what may be a small section. */
+ hold_chunksize = chunksize;
+ chunksize = 64;
+
+ sec = subseg_new (segment_name (S_GET_SEGMENT (sym)),
+ symbol_get_tc (sym)->subseg);
+
+ chunksize = hold_chunksize;
+
+ if (after_toc)
+ ppc_after_toc_frag = frag_now;
+
+ record_alignment (sec, align);
+ if (is_code)
+ frag_align_code (align, 0);
+ else
+ frag_align (align, 0, 0);
+
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
+ symbol_get_tc (sym)->align = align;
+ symbol_get_tc (sym)->output = 1;
+ symbol_get_tc (sym)->within = sym;
+
+ for (list = *list_ptr;
+ symbol_get_tc (list)->next != (symbolS *) NULL;
+ list = symbol_get_tc (list)->next)
+ ;
+ symbol_get_tc (list)->next = sym;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP,
+ &symbol_lastP);
+ }
+
+ ppc_current_csect = sym;
+}
+
+static void
+ppc_change_debug_section (unsigned int idx, subsegT subseg)
+{
+ segT sec;
+ flagword oldflags;
+ const struct xcoff_dwsect_name *dw = &xcoff_dwsect_names[idx];
+
+ sec = subseg_new (dw->name, subseg);
+ oldflags = bfd_get_section_flags (stdoutput, sec);
+ if (oldflags == SEC_NO_FLAGS)
+ {
+ /* Just created section. */
+ gas_assert (dw_sections[idx].sect == NULL);
+
+ bfd_set_section_flags (stdoutput, sec, SEC_DEBUGGING);
+ bfd_set_section_alignment (stdoutput, sec, 0);
+ dw_sections[idx].sect = sec;
+ }
+
+ /* Not anymore in a csect. */
+ ppc_current_csect = NULL;
+}
+
+/* The .dwsect pseudo-op. Defines a DWARF section. Syntax is:
+ .dwsect flag [, opt-label ]
+*/
+
+static void
+ppc_dwsect (int ignore ATTRIBUTE_UNUSED)
+{
+ offsetT flag;
+ symbolS *opt_label;
+ const struct xcoff_dwsect_name *dw;
+ struct dw_subsection *subseg;
+ struct dw_section *dws;
+ int i;
+
+ /* Find section. */
+ flag = get_absolute_expression ();
+ dw = NULL;
+ for (i = 0; i < XCOFF_DWSECT_NBR_NAMES; i++)
+ if (xcoff_dwsect_names[i].flag == flag)
+ {
+ dw = &xcoff_dwsect_names[i];
+ break;
+ }
+
+ /* Parse opt-label. */
+ if (*input_line_pointer == ',')
+ {
+ const char *label;
+ char c;
+
+ ++input_line_pointer;
+
+ label = input_line_pointer;
+ c = get_symbol_end ();
+ opt_label = symbol_find_or_make (label);
+ *input_line_pointer = c;
+ }
+ else
+ opt_label = NULL;
+
+ demand_empty_rest_of_line ();
+
+ /* Return now in case of unknown subsection. */
+ if (dw == NULL)
+ {
+ as_bad (_("no known dwarf XCOFF section for flag 0x%08x\n"),
+ (unsigned)flag);
+ return;
+ }
+
+ /* Find the subsection. */
+ dws = &dw_sections[i];
+ subseg = NULL;
+ if (opt_label != NULL && S_IS_DEFINED (opt_label))
+ {
+ /* Sanity check (note that in theory S_GET_SEGMENT mustn't be null). */
+ if (dws->sect == NULL || S_GET_SEGMENT (opt_label) != dws->sect)
+ {
+ as_bad (_("label %s was not defined in this dwarf section"),
+ S_GET_NAME (opt_label));
+ subseg = dws->anon_subseg;
+ opt_label = NULL;
+ }
+ else
+ subseg = symbol_get_tc (opt_label)->u.dw;
+ }
+
+ if (subseg != NULL)
+ {
+ /* Switch to the subsection. */
+ ppc_change_debug_section (i, subseg->subseg);
+ }
+ else
+ {
+ /* Create a new dw subsection. */
+ subseg = (struct dw_subsection *)
+ xmalloc (sizeof (struct dw_subsection));
+
+ if (opt_label == NULL)
+ {
+ /* The anonymous one. */
+ subseg->subseg = 0;
+ subseg->link = NULL;
+ dws->anon_subseg = subseg;
+ }
+ else
+ {
+ /* A named one. */
+ if (dws->list_subseg != NULL)
+ subseg->subseg = dws->list_subseg->subseg + 1;
+ else
+ subseg->subseg = 1;
+
+ subseg->link = dws->list_subseg;
+ dws->list_subseg = subseg;
+ symbol_get_tc (opt_label)->u.dw = subseg;
+ }
+
+ ppc_change_debug_section (i, subseg->subseg);
+
+ if (dw->def_size)
+ {
+ /* Add the length field. */
+ expressionS *exp = &subseg->end_exp;
+ int sz;
+
+ if (opt_label != NULL)
+ symbol_set_value_now (opt_label);
+
+ /* Add the length field. Note that according to the AIX assembler
+ manual, the size of the length field is 4 for powerpc32 but
+ 12 for powerpc64. */
+ if (ppc_obj64)
+ {
+ /* Write the 64bit marker. */
+ md_number_to_chars (frag_more (4), -1, 4);
+ }
+
+ exp->X_op = O_subtract;
+ exp->X_op_symbol = symbol_temp_new_now ();
+ exp->X_add_symbol = symbol_temp_make ();
+
+ sz = ppc_obj64 ? 8 : 4;
+ exp->X_add_number = -sz;
+ emit_expr (exp, sz);
+ }
+ }
+}
+
+/* This function handles the .text and .data pseudo-ops. These
+ pseudo-ops aren't really used by XCOFF; we implement them for the
+ convenience of people who aren't used to XCOFF. */
+
+static void
+ppc_section (int type)
+{
+ const char *name;
+ symbolS *sym;
+
+ if (type == 't')
+ name = ".text[PR]";
+ else if (type == 'd')
+ name = ".data[RW]";
+ else
+ abort ();
+
+ sym = symbol_find_or_make (name);
+
+ ppc_change_csect (sym, 2);
+
+ demand_empty_rest_of_line ();
+}
+
+/* This function handles the .section pseudo-op. This is mostly to
+ give an error, since XCOFF only supports .text, .data and .bss, but
+ we do permit the user to name the text or data section. */
+
+static void
+ppc_named_section (int ignore ATTRIBUTE_UNUSED)
+{
+ char *user_name;
+ const char *real_name;
+ char c;
+ symbolS *sym;
+
+ user_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcmp (user_name, ".text") == 0)
+ real_name = ".text[PR]";
+ else if (strcmp (user_name, ".data") == 0)
+ real_name = ".data[RW]";
+ else
+ {
+ as_bad (_("the XCOFF file format does not support arbitrary sections"));
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = c;
+
+ sym = symbol_find_or_make (real_name);
+
+ ppc_change_csect (sym, 2);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .extern pseudo-op. We create an undefined symbol. */
+
+static void
+ppc_extern (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ (void) symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .lglobl pseudo-op. Keep the symbol in the symbol table. */
+
+static void
+ppc_lglobl (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ symbol_get_tc (sym)->output = 1;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ref pseudo-op. It takes a list of symbol names and inserts R_REF
+ relocations at the beginning of the current csect.
+
+ (In principle, there's no reason why the relocations _have_ to be at
+ the beginning. Anywhere in the csect would do. However, inserting
+ at the beginning is what the native assmebler does, and it helps to
+ deal with cases where the .ref statements follow the section contents.)
+
+ ??? .refs don't work for empty .csects. However, the native assembler
+ doesn't report an error in this case, and neither yet do we. */
+
+static void
+ppc_ref (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+
+ if (ppc_current_csect == NULL)
+ {
+ as_bad (_(".ref outside .csect"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ fix_at_start (symbol_get_frag (ppc_current_csect), 0,
+ symbol_find_or_make (name), 0, FALSE, BFD_RELOC_NONE);
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ as_bad (_("missing symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .rename pseudo-op. The RS/6000 assembler can rename symbols,
+ although I don't know why it bothers. */
+
+static void
+ppc_rename (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+ int len;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing rename string"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ symbol_get_tc (sym)->real_name = demand_copy_C_string (&len);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .stabx pseudo-op. This is similar to a normal .stabs
+ pseudo-op, but slightly different. A sample is
+ .stabx "main:F-1",.main,142,0
+ The first argument is the symbol name to create. The second is the
+ value, and the third is the storage class. The fourth seems to be
+ always zero, and I am assuming it is the type. */
+
+static void
+ppc_stabx (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int len;
+ symbolS *sym;
+ expressionS exp;
+
+ name = demand_copy_C_string (&len);
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing value"));
+ return;
+ }
+ ++input_line_pointer;
+
+ ppc_stab_symbol = TRUE;
+ sym = symbol_make (name);
+ ppc_stab_symbol = FALSE;
+
+ symbol_get_tc (sym)->real_name = name;
+
+ (void) expression (&exp);
+
+ switch (exp.X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_big:
+ as_bad (_("illegal .stabx expression; zero assumed"));
+ exp.X_add_number = 0;
+ /* Fall through. */
+ case O_constant:
+ S_SET_VALUE (sym, (valueT) exp.X_add_number);
+ symbol_set_frag (sym, &zero_address_frag);
+ break;
+
+ case O_symbol:
+ if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section)
+ symbol_set_value_expression (sym, &exp);
+ else
+ {
+ S_SET_VALUE (sym,
+ exp.X_add_number + S_GET_VALUE (exp.X_add_symbol));
+ symbol_set_frag (sym, symbol_get_frag (exp.X_add_symbol));
+ }
+ break;
+
+ default:
+ /* The value is some complex expression. This will probably
+ fail at some later point, but this is probably the right
+ thing to do here. */
+ symbol_set_value_expression (sym, &exp);
+ break;
+ }
+
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing class"));
+ return;
+ }
+ ++input_line_pointer;
+
+ S_SET_STORAGE_CLASS (sym, get_absolute_expression ());
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing type"));
+ return;
+ }
+ ++input_line_pointer;
+
+ S_SET_DATA_TYPE (sym, get_absolute_expression ());
+
+ symbol_get_tc (sym)->output = 1;
+
+ if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+ {
+ /* In this case :
+
+ .bs name
+ .stabx "z",arrays_,133,0
+ .es
+
+ .comm arrays_,13768,3
+
+ resolve_symbol_value will copy the exp's "within" into sym's when the
+ offset is 0. Since this seems to be corner case problem,
+ only do the correction for storage class C_STSYM. A better solution
+ would be to have the tc field updated in ppc_symbol_new_hook. */
+
+ if (exp.X_op == O_symbol)
+ {
+ if (ppc_current_block == NULL)
+ 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;
+ }
+ }
+
+ if (exp.X_op != O_symbol
+ || ! S_IS_EXTERNAL (exp.X_add_symbol)
+ || S_GET_SEGMENT (exp.X_add_symbol) != bss_section)
+ ppc_frob_label (sym);
+ else
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, exp.X_add_symbol, &symbol_rootP, &symbol_lastP);
+ if (symbol_get_tc (ppc_current_csect)->within == exp.X_add_symbol)
+ symbol_get_tc (ppc_current_csect)->within = sym;
+ }
+
+ 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
+ uses the same name for both. I have no idea what the third and
+ fourth arguments are meant to be. The optional fifth argument is
+ an expression for the size of the function. In COFF this symbol
+ gets an aux entry like that used for a csect. */
+
+static void
+ppc_function (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ char *s;
+ symbolS *ext_sym;
+ symbolS *lab_sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ /* Ignore any [PR] suffix. */
+ name = ppc_canonicalize_symbol_name (name);
+ s = strchr (name, '[');
+ if (s != (char *) NULL
+ && strcmp (s + 1, "PR]") == 0)
+ *s = '\0';
+
+ ext_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ lab_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (ext_sym != lab_sym)
+ {
+ expressionS exp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = lab_sym;
+ exp.X_op_symbol = NULL;
+ exp.X_add_number = 0;
+ exp.X_unsigned = 0;
+ symbol_set_value_expression (ext_sym, &exp);
+ }
+
+ if (symbol_get_tc (ext_sym)->symbol_class == -1)
+ symbol_get_tc (ext_sym)->symbol_class = XMC_PR;
+ symbol_get_tc (ext_sym)->output = 1;
+
+ if (*input_line_pointer == ',')
+ {
+ expressionS exp;
+
+ /* Ignore the third argument. */
+ ++input_line_pointer;
+ expression (& exp);
+ if (*input_line_pointer == ',')
+ {
+ /* Ignore the fourth argument. */
+ ++input_line_pointer;
+ expression (& exp);
+ if (*input_line_pointer == ',')
+ {
+ /* The fifth argument is the function size. */
+ ++input_line_pointer;
+ symbol_get_tc (ext_sym)->u.size = symbol_new
+ ("L0\001", absolute_section,(valueT) 0, &zero_address_frag);
+ pseudo_set (symbol_get_tc (ext_sym)->u.size);
+ }
+ }
+ }
+
+ S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
+ SF_SET_FUNCTION (ext_sym);
+ SF_SET_PROCESS (ext_sym);
+ coff_add_linesym (ext_sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bf pseudo-op. This is just like a COFF C_FCN symbol named
+ ".bf". If the pseudo op .bi was seen before .bf, patch the .bi sym
+ with the correct line number */
+
+static symbolS *saved_bi_sym = 0;
+
+static void
+ppc_bf (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".bf");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_FCN);
+
+ coff_line_base = get_absolute_expression ();
+
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, coff_line_base);
+
+ /* Line number for bi. */
+ if (saved_bi_sym)
+ {
+ S_SET_VALUE (saved_bi_sym, coff_n_line_nos);
+ saved_bi_sym = 0;
+ }
+
+
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ef pseudo-op. This is just like a COFF C_FCN symbol named
+ ".ef", except that the line number is absolute, not relative to the
+ most recent ".bf" symbol. */
+
+static void
+ppc_ef (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".ef");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_FCN);
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bi and .ei pseudo-ops. These take a string argument and
+ generates a C_BINCL or C_EINCL symbol, which goes at the start of
+ the symbol list. The value of .bi will be know when the next .bf
+ is encountered. */
+
+static void
+ppc_biei (int ei)
+{
+ static symbolS *last_biei;
+
+ char *name;
+ int len;
+ symbolS *sym;
+ symbolS *look;
+
+ name = demand_copy_C_string (&len);
+
+ /* The value of these symbols is actually file offset. Here we set
+ the value to the index into the line number entries. In
+ ppc_frob_symbols we set the fix_line field, which will cause BFD
+ to do the right thing. */
+
+ sym = symbol_make (name);
+ /* obj-coff.c currently only handles line numbers correctly in the
+ .text section. */
+ S_SET_SEGMENT (sym, text_section);
+ S_SET_VALUE (sym, coff_n_line_nos);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+
+ S_SET_STORAGE_CLASS (sym, ei ? C_EINCL : C_BINCL);
+ symbol_get_tc (sym)->output = 1;
+
+ /* Save bi. */
+ if (ei)
+ saved_bi_sym = 0;
+ else
+ saved_bi_sym = sym;
+
+ for (look = last_biei ? last_biei : symbol_rootP;
+ (look != (symbolS *) NULL
+ && (S_GET_STORAGE_CLASS (look) == C_FILE
+ || S_GET_STORAGE_CLASS (look) == C_BINCL
+ || S_GET_STORAGE_CLASS (look) == C_EINCL));
+ look = symbol_next (look))
+ ;
+ if (look != (symbolS *) NULL)
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (sym, look, &symbol_rootP, &symbol_lastP);
+ last_biei = sym;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bs pseudo-op. This generates a C_BSTAT symbol named ".bs".
+ There is one argument, which is a csect symbol. The value of the
+ .bs symbol is the index of this csect symbol. */
+
+static void
+ppc_bs (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *csect;
+ symbolS *sym;
+
+ if (ppc_current_block != NULL)
+ as_bad (_("nested .bs blocks"));
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ csect = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ sym = symbol_make (".bs");
+ S_SET_SEGMENT (sym, now_seg);
+ S_SET_STORAGE_CLASS (sym, C_BSTAT);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ symbol_get_tc (sym)->output = 1;
+
+ symbol_get_tc (sym)->within = csect;
+
+ ppc_frob_label (sym);
+
+ ppc_current_block = sym;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .es pseudo-op. Generate a C_ESTART symbol named .es. */
+
+static void
+ppc_es (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ if (ppc_current_block == NULL)
+ as_bad (_(".es without preceding .bs"));
+
+ sym = symbol_make (".es");
+ S_SET_SEGMENT (sym, now_seg);
+ S_SET_STORAGE_CLASS (sym, C_ESTAT);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ ppc_current_block = NULL;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bb pseudo-op. Generate a C_BLOCK symbol named .bb, with a
+ line number. */
+
+static void
+ppc_bb (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".bb");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_BLOCK);
+
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+
+ symbol_get_tc (sym)->output = 1;
+
+ SF_SET_PROCESS (sym);
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .eb pseudo-op. Generate a C_BLOCK symbol named .eb, with a
+ line number. */
+
+static void
+ppc_eb (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".eb");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_BLOCK);
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+ symbol_get_tc (sym)->output = 1;
+
+ SF_SET_PROCESS (sym);
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bc pseudo-op. This just creates a C_BCOMM symbol with a
+ specified name. */
+
+static void
+ppc_bc (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int len;
+ symbolS *sym;
+
+ name = demand_copy_C_string (&len);
+ sym = symbol_make (name);
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ S_SET_STORAGE_CLASS (sym, C_BCOMM);
+ S_SET_VALUE (sym, 0);
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ec pseudo-op. This just creates a C_ECOMM symbol. */
+
+static void
+ppc_ec (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".ec");
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ S_SET_STORAGE_CLASS (sym, C_ECOMM);
+ S_SET_VALUE (sym, 0);
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .toc pseudo-op. Switch to the .toc subsegment. */
+
+static void
+ppc_toc (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ppc_toc_csect != (symbolS *) NULL)
+ subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg);
+ else
+ {
+ subsegT subseg;
+ symbolS *sym;
+ symbolS *list;
+
+ subseg = ppc_data_subsegment;
+ ++ppc_data_subsegment;
+
+ subseg_new (segment_name (data_section), subseg);
+ ppc_toc_frag = frag_now;
+
+ sym = symbol_find_or_make ("TOC[TC0]");
+ symbol_set_frag (sym, frag_now);
+ S_SET_SEGMENT (sym, data_section);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+ symbol_get_tc (sym)->subseg = subseg;
+ symbol_get_tc (sym)->output = 1;
+ symbol_get_tc (sym)->within = sym;
+
+ ppc_toc_csect = sym;
+
+ for (list = ppc_data_csects;
+ symbol_get_tc (list)->next != (symbolS *) NULL;
+ list = symbol_get_tc (list)->next)
+ ;
+ symbol_get_tc (list)->next = sym;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP,
+ &symbol_lastP);
+ }
+
+ ppc_current_csect = ppc_toc_csect;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The AIX assembler automatically aligns the operands of a .long or
+ .short pseudo-op, and we want to be compatible. */
+
+static void
+ppc_xcoff_cons (int log_size)
+{
+ frag_align (log_size, 0, 0);
+ record_alignment (now_seg, log_size);
+ cons (1 << log_size);
+}
+
+static void
+ppc_vbyte (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ int byte_count;
+
+ (void) expression (&exp);
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("non-constant byte count"));
+ return;
+ }
+
+ byte_count = exp.X_add_number;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing value"));
+ return;
+ }
+
+ ++input_line_pointer;
+ cons (byte_count);
+}
+
+void
+ppc_xcoff_end (void)
+{
+ int i;
+
+ for (i = 0; i < XCOFF_DWSECT_NBR_NAMES; i++)
+ {
+ struct dw_section *dws = &dw_sections[i];
+ struct dw_subsection *dwss;
+
+ if (dws->anon_subseg)
+ {
+ dwss = dws->anon_subseg;
+ dwss->link = dws->list_subseg;
+ }
+ else
+ dwss = dws->list_subseg;
+
+ for (; dwss != NULL; dwss = dwss->link)
+ if (dwss->end_exp.X_add_symbol != NULL)
+ {
+ subseg_set (dws->sect, dwss->subseg);
+ symbol_set_value_now (dwss->end_exp.X_add_symbol);
+ }
+ }
+}
+
+#endif /* OBJ_XCOFF */
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+
+/* The .tc pseudo-op. This is used when generating either XCOFF or
+ ELF. This takes two or more arguments.
+
+ When generating XCOFF output, the first argument is the name to
+ give to this location in the toc; this will be a symbol with class
+ TC. The rest of the arguments are N-byte values to actually put at
+ this location in the TOC; often there is just one more argument, a
+ relocatable symbol reference. The size of the value to store
+ depends on target word size. A 32-bit target uses 4-byte values, a
+ 64-bit target uses 8-byte values.
+
+ When not generating XCOFF output, the arguments are the same, but
+ the first argument is simply ignored. */
+
+static void
+ppc_tc (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_XCOFF
+
+ /* Define the TOC symbol name. */
+ {
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ if (ppc_toc_csect == (symbolS *) NULL
+ || ppc_toc_csect != ppc_current_csect)
+ {
+ as_bad (_(".tc not in .toc section"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (S_IS_DEFINED (sym))
+ {
+ symbolS *label;
+
+ label = symbol_get_tc (ppc_current_csect)->within;
+ if (symbol_get_tc (label)->symbol_class != XMC_TC0)
+ {
+ as_bad (_(".tc with no label"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ S_SET_SEGMENT (label, S_GET_SEGMENT (sym));
+ symbol_set_frag (label, symbol_get_frag (sym));
+ S_SET_VALUE (label, S_GET_VALUE (sym));
+
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+
+ return;
+ }
+
+ S_SET_SEGMENT (sym, now_seg);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+ symbol_get_tc (sym)->symbol_class = XMC_TC;
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+ }
+
+#endif /* OBJ_XCOFF */
+#ifdef OBJ_ELF
+ int align;
+
+ /* Skip the TOC symbol name. */
+ while (is_part_of_name (*input_line_pointer)
+ || *input_line_pointer == ' '
+ || *input_line_pointer == '['
+ || *input_line_pointer == ']'
+ || *input_line_pointer == '{'
+ || *input_line_pointer == '}')
+ ++input_line_pointer;
+
+ /* Align to a four/eight byte boundary. */
+ align = ppc_obj64 ? 3 : 2;
+ frag_align (align, 0, 0);
+ record_alignment (now_seg, align);
+#endif /* OBJ_ELF */
+
+ if (*input_line_pointer != ',')
+ demand_empty_rest_of_line ();
+ else
+ {
+ ++input_line_pointer;
+ cons (ppc_obj64 ? 8 : 4);
+ }
+}
+
+/* Pseudo-op .machine. */
+
+static void
+ppc_machine (int ignore ATTRIBUTE_UNUSED)
+{
+ char *cpu_string;
+#define MAX_HISTORY 100
+ static ppc_cpu_t *cpu_history;
+ static int curr_hist;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ int len;
+ cpu_string = demand_copy_C_string (&len);
+ }
+ else
+ {
+ char c;
+ cpu_string = input_line_pointer;
+ c = get_symbol_end ();
+ cpu_string = xstrdup (cpu_string);
+ *input_line_pointer = c;
+ }
+
+ 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++)
+ *p = TOLOWER (*p);
+
+ if (strcmp (cpu_string, "push") == 0)
+ {
+ if (cpu_history == NULL)
+ cpu_history = xmalloc (MAX_HISTORY * sizeof (*cpu_history));
+
+ if (curr_hist >= MAX_HISTORY)
+ as_bad (_(".machine stack overflow"));
+ else
+ cpu_history[curr_hist++] = ppc_cpu;
+ }
+ else if (strcmp (cpu_string, "pop") == 0)
+ {
+ if (curr_hist <= 0)
+ as_bad (_(".machine stack underflow"));
+ 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);
+
+ if (ppc_cpu != old_cpu)
+ ppc_setup_opcodes ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+
+#ifdef TE_PE
+
+/* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format. */
+
+/* Set the current section. */
+static void
+ppc_set_current_section (segT new)
+{
+ ppc_previous_section = ppc_current_section;
+ ppc_current_section = new;
+}
+
+/* pseudo-op: .previous
+ behaviour: toggles the current section with the previous section.
+ errors: None
+ warnings: "No previous section" */
+
+static void
+ppc_previous (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ppc_previous_section == NULL)
+ {
+ as_warn (_("no previous section to return to, ignored."));
+ return;
+ }
+
+ subseg_set (ppc_previous_section, 0);
+
+ ppc_set_current_section (ppc_previous_section);
+}
+
+/* pseudo-op: .pdata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .pdata "adr3"
+ a - don't know -- maybe a misprint
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+
+ commentary:
+ Tag index tables (also known as the function table) for exception
+ handling, debugging, etc. */
+
+static void
+ppc_pdata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (pdata_section == 0)
+ {
+ pdata_section = subseg_new (".pdata", 0);
+
+ bfd_set_section_flags (stdoutput, pdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, pdata_section, 2);
+ }
+ else
+ {
+ pdata_section = subseg_new (".pdata", 0);
+ }
+ ppc_set_current_section (pdata_section);
+}
+
+/* pseudo-op: .ydata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .ydata "drw3"
+ a - don't know -- maybe a misprint
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+ commentary:
+ Tag tables (also known as the scope table) for exception handling,
+ debugging, etc. */
+
+static void
+ppc_ydata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ydata_section == 0)
+ {
+ ydata_section = subseg_new (".ydata", 0);
+ bfd_set_section_flags (stdoutput, ydata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, ydata_section, 3);
+ }
+ else
+ {
+ ydata_section = subseg_new (".ydata", 0);
+ }
+ ppc_set_current_section (ydata_section);
+}
+
+/* pseudo-op: .reldata
+ behaviour: predefined read write data section
+ double word aligned (4-byte)
+ FIXME: relocation is applied to it
+ FIXME: what's the difference between this and .data?
+ errors: None
+ warnings: None
+ initial: .section .reldata "drw3"
+ d - initialized data
+ r - readable
+ w - writeable
+ 3 - double word aligned (that would be 8 byte boundary)
+
+ commentary:
+ Like .data, but intended to hold data subject to relocation, such as
+ function descriptors, etc. */
+
+static void
+ppc_reldata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (reldata_section == 0)
+ {
+ reldata_section = subseg_new (".reldata", 0);
+
+ bfd_set_section_flags (stdoutput, reldata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_DATA));
+
+ bfd_set_section_alignment (stdoutput, reldata_section, 2);
+ }
+ else
+ {
+ reldata_section = subseg_new (".reldata", 0);
+ }
+ ppc_set_current_section (reldata_section);
+}
+
+/* pseudo-op: .rdata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .rdata "dr3"
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary) */
+
+static void
+ppc_rdata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (rdata_section == 0)
+ {
+ rdata_section = subseg_new (".rdata", 0);
+ bfd_set_section_flags (stdoutput, rdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, rdata_section, 2);
+ }
+ else
+ {
+ rdata_section = subseg_new (".rdata", 0);
+ }
+ ppc_set_current_section (rdata_section);
+}
+
+/* pseudo-op: .ualong
+ behaviour: much like .int, with the exception that no alignment is
+ performed.
+ FIXME: test the alignment statement
+ errors: None
+ warnings: None */
+
+static void
+ppc_ualong (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Try for long. */
+ cons (4);
+}
+
+/* pseudo-op: .znop <symbol name>
+ behaviour: Issue a nop instruction
+ Issue a IMAGE_REL_PPC_IFGLUE relocation against it, using
+ the supplied symbol name.
+ errors: None
+ warnings: Missing symbol name */
+
+static void
+ppc_znop (int ignore ATTRIBUTE_UNUSED)
+{
+ unsigned long insn;
+ const struct powerpc_opcode *opcode;
+ char *f;
+ symbolS *sym;
+ char *symbol_name;
+ char c;
+ char *name;
+
+ /* Strip out the symbol name. */
+ symbol_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - symbol_name + 1);
+ strcpy (name, symbol_name);
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop");
+
+ /* Stick in the nop. */
+ insn = opcode->opcode;
+
+ /* Write out the instruction. */
+ f = frag_more (4);
+ md_number_to_chars (f, insn, 4);
+ fix_new (frag_now,
+ f - frag_now->fr_literal,
+ 4,
+ sym,
+ 0,
+ 0,
+ BFD_RELOC_16_GOT_PCREL);
+
+}
+
+/* pseudo-op:
+ behaviour:
+ errors:
+ warnings: */
+
+static void
+ppc_pe_comm (int lcomm)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT temp;
+ symbolS *symbolP;
+ offsetT align;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (! lcomm)
+ {
+ /* The third argument to .comm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 3;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 3;
+ }
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP))
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) temp)
+ as_bad (_("length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) temp);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) temp);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * implement the .section pseudo op:
+ * .section name {, "flags"}
+ * ^ ^
+ * | +--- optional flags: 'b' for bss
+ * | 'i' for info
+ * +-- section name 'l' for lib
+ * 'n' for noload
+ * 'o' for over
+ * 'w' for data
+ * 'd' (apparently m88k for data)
+ * 'x' for text
+ * But if the argument is not a quoted string, treat it as a
+ * subsegment number.
+ *
+ * FIXME: this is a copy of the section processing from obj-coff.c, with
+ * additions/changes for the moto-pas assembler support. There are three
+ * categories:
+ *
+ * FIXME: I just noticed this. This doesn't work at all really. It it
+ * setting bits that bfd probably neither understands or uses. The
+ * correct approach (?) will have to incorporate extra fields attached
+ * to the section to hold the system specific stuff. (krk)
+ *
+ * Section Contents:
+ * 'a' - unknown - referred to in documentation, but no definition supplied
+ * 'c' - section has code
+ * 'd' - section has initialized data
+ * 'u' - section has uninitialized data
+ * 'i' - section contains directives (info)
+ * 'n' - section can be discarded
+ * 'R' - remove section at link time
+ *
+ * Section Protection:
+ * 'r' - section is readable
+ * 'w' - section is writeable
+ * 'x' - section is executable
+ * 's' - section is sharable
+ *
+ * Section Alignment:
+ * '0' - align to byte boundary
+ * '1' - align to halfword undary
+ * '2' - align to word boundary
+ * '3' - align to doubleword boundary
+ * '4' - align to quadword boundary
+ * '5' - align to 32 byte boundary
+ * '6' - align to 64 byte boundary
+ *
+ */
+
+void
+ppc_pe_section (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Strip out the section name. */
+ char *section_name;
+ char c;
+ char *name;
+ unsigned int exp;
+ flagword flags;
+ segT sec;
+ int align;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ exp = 0;
+ flags = SEC_NO_FLAGS;
+
+ if (strcmp (name, ".idata$2") == 0)
+ {
+ align = 0;
+ }
+ else if (strcmp (name, ".idata$3") == 0)
+ {
+ align = 0;
+ }
+ else if (strcmp (name, ".idata$4") == 0)
+ {
+ align = 2;
+ }
+ else if (strcmp (name, ".idata$5") == 0)
+ {
+ align = 2;
+ }
+ else if (strcmp (name, ".idata$6") == 0)
+ {
+ align = 1;
+ }
+ else
+ /* Default alignment to 16 byte boundary. */
+ align = 4;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ exp = get_absolute_expression ();
+ else
+ {
+ ++input_line_pointer;
+ while (*input_line_pointer != '"'
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ /* Section Contents */
+ case 'a': /* unknown */
+ as_bad (_("unsupported section attribute -- 'a'"));
+ break;
+ case 'c': /* code section */
+ flags |= SEC_CODE;
+ break;
+ case 'd': /* section has initialized data */
+ flags |= SEC_DATA;
+ break;
+ case 'u': /* section has uninitialized data */
+ /* FIXME: This is IMAGE_SCN_CNT_UNINITIALIZED_DATA
+ in winnt.h */
+ flags |= SEC_ROM;
+ break;
+ case 'i': /* section contains directives (info) */
+ /* FIXME: This is IMAGE_SCN_LNK_INFO
+ in winnt.h */
+ flags |= SEC_HAS_CONTENTS;
+ break;
+ case 'n': /* section can be discarded */
+ flags &=~ SEC_LOAD;
+ break;
+ case 'R': /* Remove section at link time */
+ flags |= SEC_NEVER_LOAD;
+ break;
+#if IFLICT_BRAIN_DAMAGE
+ /* Section Protection */
+ case 'r': /* section is readable */
+ flags |= IMAGE_SCN_MEM_READ;
+ break;
+ case 'w': /* section is writeable */
+ flags |= IMAGE_SCN_MEM_WRITE;
+ break;
+ case 'x': /* section is executable */
+ flags |= IMAGE_SCN_MEM_EXECUTE;
+ break;
+ case 's': /* section is sharable */
+ flags |= IMAGE_SCN_MEM_SHARED;
+ break;
+
+ /* Section Alignment */
+ case '0': /* align to byte boundary */
+ flags |= IMAGE_SCN_ALIGN_1BYTES;
+ align = 0;
+ break;
+ case '1': /* align to halfword boundary */
+ flags |= IMAGE_SCN_ALIGN_2BYTES;
+ align = 1;
+ break;
+ case '2': /* align to word boundary */
+ flags |= IMAGE_SCN_ALIGN_4BYTES;
+ align = 2;
+ break;
+ case '3': /* align to doubleword boundary */
+ flags |= IMAGE_SCN_ALIGN_8BYTES;
+ align = 3;
+ break;
+ case '4': /* align to quadword boundary */
+ flags |= IMAGE_SCN_ALIGN_16BYTES;
+ align = 4;
+ break;
+ case '5': /* align to 32 byte boundary */
+ flags |= IMAGE_SCN_ALIGN_32BYTES;
+ align = 5;
+ break;
+ case '6': /* align to 64 byte boundary */
+ flags |= IMAGE_SCN_ALIGN_64BYTES;
+ align = 6;
+ break;
+#endif
+ default:
+ as_bad (_("unknown section attribute '%c'"),
+ *input_line_pointer);
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (*input_line_pointer == '"')
+ ++input_line_pointer;
+ }
+ }
+
+ sec = subseg_new (name, (subsegT) exp);
+
+ ppc_set_current_section (sec);
+
+ if (flags != SEC_NO_FLAGS)
+ {
+ if (! bfd_set_section_flags (stdoutput, sec, flags))
+ as_bad (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+ }
+
+ bfd_set_section_alignment (stdoutput, sec, align);
+}
+
+static void
+ppc_pe_function (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *ext_sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ ext_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
+ SF_SET_FUNCTION (ext_sym);
+ SF_SET_PROCESS (ext_sym);
+ coff_add_linesym (ext_sym);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+ppc_pe_tocd (int ignore ATTRIBUTE_UNUSED)
+{
+ if (tocdata_section == 0)
+ {
+ tocdata_section = subseg_new (".tocd", 0);
+ /* FIXME: section flags won't work. */
+ bfd_set_section_flags (stdoutput, tocdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA));
+
+ bfd_set_section_alignment (stdoutput, tocdata_section, 2);
+ }
+ else
+ {
+ rdata_section = subseg_new (".tocd", 0);
+ }
+
+ ppc_set_current_section (tocdata_section);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Don't adjust TOC relocs to use the section symbol. */
+
+int
+ppc_pe_fix_adjustable (fixS *fix)
+{
+ return fix->fx_r_type != BFD_RELOC_PPC_TOC16;
+}
+
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* XCOFF specific symbol and file handling. */
+
+/* Canonicalize the symbol name. We use the to force the suffix, if
+ any, to use square brackets, and to be in upper case. */
+
+char *
+ppc_canonicalize_symbol_name (char *name)
+{
+ char *s;
+
+ if (ppc_stab_symbol)
+ return name;
+
+ for (s = name; *s != '\0' && *s != '{' && *s != '['; s++)
+ ;
+ if (*s != '\0')
+ {
+ char brac;
+
+ if (*s == '[')
+ brac = ']';
+ else
+ {
+ *s = '[';
+ brac = '}';
+ }
+
+ for (s++; *s != '\0' && *s != brac; s++)
+ *s = TOUPPER (*s);
+
+ if (*s == '\0' || s[1] != '\0')
+ as_bad (_("bad symbol suffix"));
+
+ *s = ']';
+ }
+
+ return name;
+}
+
+/* Set the class of a symbol based on the suffix, if any. This is
+ called whenever a new symbol is created. */
+
+void
+ppc_symbol_new_hook (symbolS *sym)
+{
+ struct ppc_tc_sy *tc;
+ const char *s;
+
+ tc = symbol_get_tc (sym);
+ tc->next = NULL;
+ tc->output = 0;
+ tc->symbol_class = -1;
+ tc->real_name = NULL;
+ tc->subseg = 0;
+ tc->align = 0;
+ tc->u.size = NULL;
+ tc->u.dw = NULL;
+ tc->within = NULL;
+
+ if (ppc_stab_symbol)
+ return;
+
+ s = strchr (S_GET_NAME (sym), '[');
+ if (s == (const char *) NULL)
+ {
+ /* There is no suffix. */
+ return;
+ }
+
+ ++s;
+
+ switch (s[0])
+ {
+ case 'B':
+ if (strcmp (s, "BS]") == 0)
+ tc->symbol_class = XMC_BS;
+ break;
+ case 'D':
+ if (strcmp (s, "DB]") == 0)
+ tc->symbol_class = XMC_DB;
+ else if (strcmp (s, "DS]") == 0)
+ tc->symbol_class = XMC_DS;
+ break;
+ case 'G':
+ if (strcmp (s, "GL]") == 0)
+ tc->symbol_class = XMC_GL;
+ break;
+ case 'P':
+ if (strcmp (s, "PR]") == 0)
+ tc->symbol_class = XMC_PR;
+ break;
+ case 'R':
+ if (strcmp (s, "RO]") == 0)
+ tc->symbol_class = XMC_RO;
+ else if (strcmp (s, "RW]") == 0)
+ tc->symbol_class = XMC_RW;
+ break;
+ case 'S':
+ if (strcmp (s, "SV]") == 0)
+ tc->symbol_class = XMC_SV;
+ break;
+ case 'T':
+ if (strcmp (s, "TC]") == 0)
+ tc->symbol_class = XMC_TC;
+ else if (strcmp (s, "TI]") == 0)
+ tc->symbol_class = XMC_TI;
+ else if (strcmp (s, "TB]") == 0)
+ tc->symbol_class = XMC_TB;
+ else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0)
+ tc->symbol_class = XMC_TC0;
+ break;
+ case 'U':
+ if (strcmp (s, "UA]") == 0)
+ tc->symbol_class = XMC_UA;
+ else if (strcmp (s, "UC]") == 0)
+ tc->symbol_class = XMC_UC;
+ break;
+ case 'X':
+ if (strcmp (s, "XO]") == 0)
+ tc->symbol_class = XMC_XO;
+ break;
+ }
+
+ if (tc->symbol_class == -1)
+ as_bad (_("unrecognized symbol suffix"));
+}
+
+/* Set the class of a label based on where it is defined. This
+ handles symbols without suffixes. Also, move the symbol so that it
+ follows the csect symbol. */
+
+void
+ppc_frob_label (symbolS *sym)
+{
+ if (ppc_current_csect != (symbolS *) NULL)
+ {
+ if (symbol_get_tc (sym)->symbol_class == -1)
+ symbol_get_tc (sym)->symbol_class = symbol_get_tc (ppc_current_csect)->symbol_class;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
+ &symbol_rootP, &symbol_lastP);
+ symbol_get_tc (ppc_current_csect)->within = sym;
+ symbol_get_tc (sym)->within = ppc_current_csect;
+ }
+
+#ifdef OBJ_ELF
+ dwarf2_emit_label (sym);
+#endif
+}
+
+/* This variable is set by ppc_frob_symbol if any absolute symbols are
+ seen. It tells ppc_adjust_symtab whether it needs to look through
+ the symbols. */
+
+static bfd_boolean ppc_saw_abs;
+
+/* Change the name of a symbol just before writing it out. Set the
+ real name if the .rename pseudo-op was used. Otherwise, remove any
+ class suffix. Return 1 if the symbol should not be included in the
+ symbol table. */
+
+int
+ppc_frob_symbol (symbolS *sym)
+{
+ static symbolS *ppc_last_function;
+ static symbolS *set_end;
+
+ /* Discard symbols that should not be included in the output symbol
+ table. */
+ if (! symbol_used_in_reloc_p (sym)
+ && ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0
+ || (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
+ && ! symbol_get_tc (sym)->output
+ && S_GET_STORAGE_CLASS (sym) != C_FILE)))
+ return 1;
+
+ /* This one will disappear anyway. Don't make a csect sym for it. */
+ if (sym == abs_section_sym)
+ return 1;
+
+ if (symbol_get_tc (sym)->real_name != (char *) NULL)
+ S_SET_NAME (sym, symbol_get_tc (sym)->real_name);
+ else
+ {
+ const char *name;
+ const char *s;
+
+ name = S_GET_NAME (sym);
+ s = strchr (name, '[');
+ if (s != (char *) NULL)
+ {
+ unsigned int len;
+ char *snew;
+
+ len = s - name;
+ snew = xmalloc (len + 1);
+ memcpy (snew, name, len);
+ snew[len] = '\0';
+
+ S_SET_NAME (sym, snew);
+ }
+ }
+
+ if (set_end != (symbolS *) NULL)
+ {
+ SA_SET_SYM_ENDNDX (set_end, sym);
+ set_end = NULL;
+ }
+
+ if (SF_GET_FUNCTION (sym))
+ {
+ if (ppc_last_function != (symbolS *) NULL)
+ as_bad (_("two .function pseudo-ops with no intervening .ef"));
+ ppc_last_function = sym;
+ if (symbol_get_tc (sym)->u.size != (symbolS *) NULL)
+ {
+ resolve_symbol_value (symbol_get_tc (sym)->u.size);
+ SA_SET_SYM_FSIZE (sym,
+ (long) S_GET_VALUE (symbol_get_tc (sym)->u.size));
+ }
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_FCN
+ && strcmp (S_GET_NAME (sym), ".ef") == 0)
+ {
+ if (ppc_last_function == (symbolS *) NULL)
+ as_bad (_(".ef with no preceding .function"));
+ else
+ {
+ set_end = ppc_last_function;
+ ppc_last_function = NULL;
+
+ /* We don't have a C_EFCN symbol, but we need to force the
+ COFF backend to believe that it has seen one. */
+ coff_last_function = NULL;
+ }
+ }
+
+ if (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
+ && (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) == 0
+ && S_GET_STORAGE_CLASS (sym) != C_FILE
+ && S_GET_STORAGE_CLASS (sym) != C_FCN
+ && S_GET_STORAGE_CLASS (sym) != C_BLOCK
+ && S_GET_STORAGE_CLASS (sym) != C_BSTAT
+ && S_GET_STORAGE_CLASS (sym) != C_ESTAT
+ && S_GET_STORAGE_CLASS (sym) != C_BINCL
+ && S_GET_STORAGE_CLASS (sym) != C_EINCL
+ && S_GET_SEGMENT (sym) != ppc_coff_debug_section)
+ S_SET_STORAGE_CLASS (sym, C_HIDEXT);
+
+ if (S_GET_STORAGE_CLASS (sym) == C_EXT
+ || S_GET_STORAGE_CLASS (sym) == C_AIX_WEAKEXT
+ || S_GET_STORAGE_CLASS (sym) == C_HIDEXT)
+ {
+ int i;
+ union internal_auxent *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;
+ 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;
+ }
+ 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 (stdoutput,
+ 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->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD;
+ }
+ else if (S_GET_SEGMENT (sym) == bss_section)
+ {
+ /* 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;
+ if (S_IS_EXTERNAL (sym))
+ symbol_get_tc (sym)->symbol_class = XMC_RW;
+ else
+ symbol_get_tc (sym)->symbol_class = XMC_BS;
+ }
+ else if (S_GET_SEGMENT (sym) == absolute_section)
+ {
+ /* 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;
+ 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;
+ }
+ else if (symbol_get_tc (sym)->symbol_class == XMC_TC)
+ {
+ symbolS *next;
+
+ /* This is a TOC definition. x_scnlen is the size of the
+ TOC entry. */
+ next = symbol_next (sym);
+ while (symbol_get_tc (next)->symbol_class == XMC_TC0)
+ next = symbol_next (next);
+ if (next == (symbolS *) NULL
+ || symbol_get_tc (next)->symbol_class != XMC_TC)
+ {
+ if (ppc_after_toc_frag == (fragS *) NULL)
+ a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
+ data_section)
+ - S_GET_VALUE (sym));
+ else
+ a->x_csect.x_scnlen.l = (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->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ }
+ else
+ {
+ symbolS *csect;
+
+ /* This is a normal symbol definition. x_scnlen is the
+ symbol index of the containing csect. */
+ if (S_GET_SEGMENT (sym) == text_section)
+ csect = ppc_text_csects;
+ else if (S_GET_SEGMENT (sym) == data_section)
+ csect = ppc_data_csects;
+ else
+ abort ();
+
+ /* Skip the initial dummy symbol. */
+ csect = symbol_get_tc (csect)->next;
+
+ if (csect == (symbolS *) NULL)
+ {
+ as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym));
+ a->x_csect.x_scnlen.l = 0;
+ }
+ else
+ {
+ while (symbol_get_tc (csect)->next != (symbolS *) NULL)
+ {
+ resolve_symbol_value (symbol_get_tc (csect)->next);
+ if (S_GET_VALUE (symbol_get_tc (csect)->next)
+ > S_GET_VALUE (sym))
+ break;
+ 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->x_csect.x_smtyp = XTY_LD;
+ }
+
+ a->x_csect.x_parmhash = 0;
+ a->x_csect.x_snhash = 0;
+ if (symbol_get_tc (sym)->symbol_class == -1)
+ a->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;
+
+ /* Don't let the COFF backend resort these symbols. */
+ symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END;
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_BSTAT)
+ {
+ /* We want the value to be the symbol index of the referenced
+ csect symbol. BFD will do that for us if we set the right
+ flags. */
+ asymbol *bsym = symbol_get_bfdsym (symbol_get_tc (sym)->within);
+ combined_entry_type *c = coffsymbol (bsym)->native;
+
+ S_SET_VALUE (sym, (valueT) (size_t) c);
+ coffsymbol (symbol_get_bfdsym (sym))->native->fix_value = 1;
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+ {
+ symbolS *block;
+ valueT base;
+
+ block = symbol_get_tc (sym)->within;
+ if (block)
+ {
+ /* The value is the offset from the enclosing csect. */
+ symbolS *csect;
+
+ csect = symbol_get_tc (block)->within;
+ resolve_symbol_value (csect);
+ base = S_GET_VALUE (csect);
+ }
+ else
+ base = 0;
+
+ S_SET_VALUE (sym, S_GET_VALUE (sym) - base);
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_BINCL
+ || S_GET_STORAGE_CLASS (sym) == C_EINCL)
+ {
+ /* We want the value to be a file offset into the line numbers.
+ BFD will do that for us if we set the right flags. We have
+ already set the value correctly. */
+ coffsymbol (symbol_get_bfdsym (sym))->native->fix_line = 1;
+ }
+
+ return 0;
+}
+
+/* Adjust the symbol table. This creates csect symbols for all
+ absolute symbols. */
+
+void
+ppc_adjust_symtab (void)
+{
+ symbolS *sym;
+
+ if (! ppc_saw_abs)
+ return;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ symbolS *csect;
+ int i;
+ union internal_auxent *a;
+
+ if (S_GET_SEGMENT (sym) != absolute_section)
+ continue;
+
+ csect = symbol_create (".abs[XO]", absolute_section,
+ S_GET_VALUE (sym), &zero_address_frag);
+ symbol_get_bfdsym (csect)->value = S_GET_VALUE (sym);
+ 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;
+
+ 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;
+ }
+
+ ppc_saw_abs = FALSE;
+}
+
+/* Set the VMA for a section. This is called on all the sections in
+ turn. */
+
+void
+ppc_frob_section (asection *sec)
+{
+ static bfd_vma vma = 0;
+
+ /* Dwarf sections start at 0. */
+ if (bfd_get_section_flags (NULL, sec) & SEC_DEBUGGING)
+ return;
+
+ vma = md_section_align (sec, vma);
+ bfd_set_section_vma (stdoutput, sec, vma);
+ vma += bfd_section_size (stdoutput, sec);
+}
+
+#endif /* OBJ_XCOFF */
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ return ieee_md_atof (type, litp, sizep, target_big_endian);
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Align a section (I don't know why this is machine dependent). */
+
+valueT
+md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT addr)
+{
+#ifdef OBJ_ELF
+ return addr;
+#else
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+#endif
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixS *fixp, segT sec ATTRIBUTE_UNUSED)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+#ifdef OBJ_XCOFF
+
+/* 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
+ corresponding .tc symbol. */
+
+int
+ppc_fix_adjustable (fixS *fix)
+{
+ valueT val = resolve_symbol_value (fix->fx_addsy);
+ segT symseg = S_GET_SEGMENT (fix->fx_addsy);
+ TC_SYMFIELD_TYPE *tc;
+
+ if (symseg == absolute_section)
+ return 0;
+
+ /* Always adjust symbols in debugging sections. */
+ if (bfd_get_section_flags (stdoutput, symseg) & SEC_DEBUGGING)
+ return 1;
+
+ if (ppc_toc_csect != (symbolS *) NULL
+ && fix->fx_addsy != ppc_toc_csect
+ && symseg == data_section
+ && val >= ppc_toc_frag->fr_address
+ && (ppc_after_toc_frag == (fragS *) NULL
+ || val < ppc_after_toc_frag->fr_address))
+ {
+ symbolS *sy;
+
+ for (sy = symbol_next (ppc_toc_csect);
+ sy != (symbolS *) NULL;
+ sy = symbol_next (sy))
+ {
+ TC_SYMFIELD_TYPE *sy_tc = symbol_get_tc (sy);
+
+ if (sy_tc->symbol_class == XMC_TC0)
+ continue;
+ if (sy_tc->symbol_class != XMC_TC)
+ break;
+ if (val == resolve_symbol_value (sy))
+ {
+ fix->fx_addsy = sy;
+ fix->fx_addnumber = val - ppc_toc_frag->fr_address;
+ return 0;
+ }
+ }
+
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("symbol in .toc does not match any .tc"));
+ }
+
+ /* 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
+ && symseg != bss_section
+ /* 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;
+
+ fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
+ fix->fx_addsy = csect;
+
+ return 0;
+ }
+
+ /* Adjust a reloc against a .lcomm symbol to be against the base
+ .lcomm. */
+ if (symseg == bss_section
+ && ! S_IS_EXTERNAL (fix->fx_addsy))
+ {
+ symbolS *sy = symbol_get_frag (fix->fx_addsy)->fr_symbol;
+
+ fix->fx_offset += val - resolve_symbol_value (sy);
+ fix->fx_addsy = sy;
+ }
+
+ return 0;
+}
+
+/* A reloc from one csect to another must be kept. The assembler
+ will, of course, keep relocs between sections, and it will keep
+ absolute relocs, but we need to force it to keep PC relative relocs
+ between two csects in the same section. */
+
+int
+ppc_force_relocation (fixS *fix)
+{
+ /* At this point fix->fx_addsy should already have been converted to
+ a csect symbol. If the csect does not include the fragment, then
+ we need to force the relocation. */
+ if (fix->fx_pcrel
+ && fix->fx_addsy != NULL
+ && symbol_get_tc (fix->fx_addsy)->subseg != 0
+ && ((symbol_get_frag (fix->fx_addsy)->fr_address
+ > fix->fx_frag->fr_address)
+ || (symbol_get_tc (fix->fx_addsy)->next != NULL
+ && (symbol_get_frag (symbol_get_tc (fix->fx_addsy)->next)->fr_address
+ <= fix->fx_frag->fr_address))))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+void
+ppc_new_dot_label (symbolS *sym)
+{
+ /* Anchor this label to the current csect for relocations. */
+ symbol_get_tc (sym)->within = ppc_current_csect;
+}
+
+#endif /* OBJ_XCOFF */
+
+#ifdef OBJ_ELF
+/* If this function returns non-zero, it guarantees that a relocation
+ will be emitted for a fixup. */
+
+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_B16_BRNTAKEN:
+ case BFD_RELOC_PPC_BA16_BRTAKEN:
+ case BFD_RELOC_PPC_BA16_BRNTAKEN:
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_PPC64_TOC:
+ return 1;
+ default:
+ break;
+ }
+
+ if (fix->fx_r_type >= BFD_RELOC_PPC_TLS
+ && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA)
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+int
+ppc_fix_adjustable (fixS *fix)
+{
+ 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
+ && fix->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
+ && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_DS
+ && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_LO_DS
+ && fix->fx_r_type != BFD_RELOC_GPREL16
+ && fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT
+ && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY
+ && !(fix->fx_r_type >= BFD_RELOC_PPC_TLS
+ && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA));
+}
+#endif
+
+void
+ppc_frag_check (struct frag *fragP)
+{
+ if (!fragP->has_code)
+ return;
+
+ if (ppc_mach() == bfd_mach_ppc_vle)
+ {
+ if (((fragP->fr_address + fragP->insn_addr) & 1) != 0)
+ as_bad (_("instruction address is not a multiple of 2"));
+ }
+ else
+ {
+ if (((fragP->fr_address + fragP->insn_addr) & 3) != 0)
+ as_bad (_("instruction address is not a multiple of 4"));
+ }
+}
+
+/* Implement HANDLE_ALIGN. This writes the NOP pattern into an
+ rs_align_code frag. */
+
+void
+ppc_handle_align (struct frag *fragP)
+{
+ valueT count = (fragP->fr_next->fr_address
+ - (fragP->fr_address + fragP->fr_fix));
+
+ if (ppc_mach() == bfd_mach_ppc_vle && count != 0 && (count & 1) == 0)
+ {
+ char *dest = fragP->fr_literal + fragP->fr_fix;
+
+ fragP->fr_var = 2;
+ md_number_to_chars (dest, 0x4400, 2);
+ }
+ else if (count != 0 && (count & 3) == 0)
+ {
+ char *dest = fragP->fr_literal + fragP->fr_fix;
+
+ fragP->fr_var = 4;
+
+ if (count > 4 * nop_limit && count < 0x2000000)
+ {
+ struct frag *rest;
+
+ /* Make a branch, then follow with nops. Insert another
+ frag to handle the nops. */
+ md_number_to_chars (dest, 0x48000000 + count, 4);
+ count -= 4;
+ if (count == 0)
+ return;
+
+ rest = xmalloc (SIZEOF_STRUCT_FRAG + 4);
+ memcpy (rest, fragP, SIZEOF_STRUCT_FRAG);
+ fragP->fr_next = rest;
+ fragP = rest;
+ rest->fr_address += rest->fr_fix + 4;
+ rest->fr_fix = 0;
+ /* If we leave the next frag as rs_align_code we'll come here
+ again, resulting in a bunch of branches rather than a
+ branch followed by nops. */
+ rest->fr_type = rs_align;
+ dest = rest->fr_literal;
+ }
+
+ md_number_to_chars (dest, 0x60000000, 4);
+
+ if ((ppc_cpu & PPC_OPCODE_POWER6) != 0
+ || (ppc_cpu & PPC_OPCODE_POWER7) != 0
+ || (ppc_cpu & PPC_OPCODE_POWER8) != 0)
+ {
+ /* For power6, power7 and power8, we want the last nop to be a group
+ terminating one. Do this by inserting an rs_fill frag immediately
+ after this one, with its address set to the last nop location.
+ This will automatically reduce the number of nops in the current
+ frag by one. */
+ if (count > 4)
+ {
+ struct frag *group_nop = xmalloc (SIZEOF_STRUCT_FRAG + 4);
+
+ memcpy (group_nop, fragP, SIZEOF_STRUCT_FRAG);
+ group_nop->fr_address = group_nop->fr_next->fr_address - 4;
+ group_nop->fr_fix = 0;
+ group_nop->fr_offset = 1;
+ group_nop->fr_type = rs_fill;
+ fragP->fr_next = group_nop;
+ dest = group_nop->fr_literal;
+ }
+
+ if ((ppc_cpu & PPC_OPCODE_POWER7) != 0
+ || (ppc_cpu & PPC_OPCODE_POWER8) != 0)
+ {
+ if (ppc_cpu & PPC_OPCODE_E500MC)
+ /* e500mc group terminating nop: "ori 0,0,0". */
+ md_number_to_chars (dest, 0x60000000, 4);
+ else
+ /* power7/power8 group terminating nop: "ori 2,2,0". */
+ md_number_to_chars (dest, 0x60420000, 4);
+ }
+ else
+ /* power6 group terminating nop: "ori 1,1,0". */
+ md_number_to_chars (dest, 0x60210000, 4);
+ }
+ }
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the calls to fix_new_exp, above. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ valueT value = * valP;
+ offsetT fieldval;
+ const struct powerpc_operand *operand;
+
+#ifdef OBJ_ELF
+ if (fixP->fx_addsy != NULL)
+ {
+ /* Hack around bfd_install_relocation brain damage. */
+ if (fixP->fx_pcrel)
+ value += fixP->fx_frag->fr_address + fixP->fx_where;
+ }
+ else
+ fixP->fx_done = 1;
+#else
+ /* FIXME FIXME FIXME: The value we are passed in *valP includes
+ the symbol values. If we are doing this relocation the code in
+ write.c is going to call bfd_install_relocation, which is also
+ going to use the symbol value. That means that if the reloc is
+ fully resolved we want to use *valP since bfd_install_relocation is
+ not being used.
+ However, if the reloc is not fully resolved we do not want to
+ use *valP, and must use fx_offset instead. If the relocation
+ is PC-relative, we then need to re-apply md_pcrel_from_section
+ to this new relocation value. */
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ else
+ {
+ value = fixP->fx_offset;
+ if (fixP->fx_pcrel)
+ value -= md_pcrel_from_section (fixP, seg);
+ }
+#endif
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ }
+
+ operand = NULL;
+ if (fixP->fx_pcrel_adjust != 0)
+ {
+ /* This is a fixup on an instruction. */
+ int opindex = fixP->fx_pcrel_adjust & 0xff;
+
+ operand = &powerpc_operands[opindex];
+#ifdef OBJ_XCOFF
+ /* An instruction like `lwz 9,sym(30)' when `sym' is not a TOC symbol
+ does not generate a reloc. It uses the offset of `sym' within its
+ csect. Other usages, such as `.long sym', generate relocs. This
+ is the documented behaviour of non-TOC symbols. */
+ if ((operand->flags & PPC_OPERAND_PARENS) != 0
+ && (operand->bitm & 0xfff0) == 0xfff0
+ && operand->shift == 0
+ && (operand->insert == NULL || ppc_obj64)
+ && fixP->fx_addsy != NULL
+ && symbol_get_tc (fixP->fx_addsy)->subseg != 0
+ && symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC
+ && symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC0
+ && S_GET_SEGMENT (fixP->fx_addsy) != bss_section)
+ {
+ 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
+ }
+
+ /* Calculate value to be stored in field. */
+ fieldval = value;
+ switch (fixP->fx_r_type)
+ {
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC64_ADDR16_LO_DS:
+ case BFD_RELOC_PPC_VLE_LO16A:
+ case BFD_RELOC_PPC_VLE_LO16D:
+#endif
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_LO16_PCREL:
+ fieldval = value & 0xffff;
+ sign_extend_16:
+ if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+ fieldval = (fieldval ^ 0x8000) - 0x8000;
+ fixP->fx_no_overflow = 1;
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC_VLE_HI16A:
+ case BFD_RELOC_PPC_VLE_HI16D:
+#endif
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_PCREL:
+ fieldval = PPC_HI (value);
+ goto sign_extend_16;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC_VLE_HA16A:
+ case BFD_RELOC_PPC_VLE_HA16D:
+#endif
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_HI16_S_PCREL:
+ fieldval = PPC_HA (value);
+ goto sign_extend_16;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC64_HIGHER:
+ fieldval = PPC_HIGHER (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_PPC64_HIGHER_S:
+ fieldval = PPC_HIGHERA (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_PPC64_HIGHEST:
+ fieldval = PPC_HIGHEST (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_PPC64_HIGHEST_S:
+ fieldval = PPC_HIGHESTA (value);
+ goto sign_extend_16;
+#endif
+
+ default:
+ break;
+ }
+
+ if (operand != NULL)
+ {
+ /* Handle relocs in an insn. */
+ char *where;
+ unsigned long insn;
+
+ switch (fixP->fx_r_type)
+ {
+#ifdef OBJ_ELF
+ /* The following relocs can't be calculated by the assembler.
+ Leave the field zero. */
+ case BFD_RELOC_PPC_TPREL16:
+ case BFD_RELOC_PPC_TPREL16_LO:
+ case BFD_RELOC_PPC_TPREL16_HI:
+ case BFD_RELOC_PPC_TPREL16_HA:
+ case BFD_RELOC_PPC_DTPREL16:
+ case BFD_RELOC_PPC_DTPREL16_LO:
+ case BFD_RELOC_PPC_DTPREL16_HI:
+ case BFD_RELOC_PPC_DTPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TLSGD16:
+ case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+ case BFD_RELOC_PPC_GOT_TLSLD16:
+ case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16_HI:
+ case BFD_RELOC_PPC_GOT_TPREL16_HA:
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+ 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_HIGHER:
+ case BFD_RELOC_PPC64_TPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_TPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+ case BFD_RELOC_PPC64_DTPREL16_DS:
+ case BFD_RELOC_PPC64_DTPREL16_LO_DS:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHER:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+ gas_assert (fixP->fx_addsy != NULL);
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ fieldval = 0;
+ break;
+
+ /* These also should leave the field zero for the same
+ reason. Note that older versions of gas wrote values
+ here. If we want to go back to the old behaviour, then
+ all _LO and _LO_DS cases will need to be treated like
+ BFD_RELOC_LO16_PCREL above. Similarly for _HI etc. */
+ 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:
+ gas_assert (fixP->fx_addsy != NULL);
+ /* Fall thru */
+
+ case BFD_RELOC_PPC_TLS:
+ case BFD_RELOC_PPC_TLSGD:
+ case BFD_RELOC_PPC_TLSLD:
+ fieldval = 0;
+ break;
+#endif
+
+#ifdef OBJ_XCOFF
+ case BFD_RELOC_PPC_B16:
+ /* Adjust the offset to the instruction boundary. */
+ fieldval += 2;
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+#ifdef OBJ_ELF
+/* powerpc uses RELA style relocs, so if emitting a reloc the field
+ contents can stay at zero. */
+#define APPLY_RELOC fixP->fx_done
+#else
+#define APPLY_RELOC 1
+#endif
+ if ((fieldval != 0 && APPLY_RELOC) || operand->insert != NULL)
+ {
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ {
+ if (fixP->fx_size == 4)
+ insn = bfd_getb32 ((unsigned char *) where);
+ else
+ insn = bfd_getb16 ((unsigned char *) where);
+ }
+ else
+ {
+ if (fixP->fx_size == 4)
+ insn = bfd_getl32 ((unsigned char *) where);
+ else
+ insn = bfd_getl16 ((unsigned char *) where);
+ }
+ insn = ppc_insert_operand (insn, operand, fieldval,
+ fixP->tc_fix_data.ppc_cpu,
+ fixP->fx_file, fixP->fx_line);
+ if (target_big_endian)
+ {
+ if (fixP->fx_size == 4)
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putb16 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ else
+ {
+ if (fixP->fx_size == 4)
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl16 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ }
+
+ if (fixP->fx_done)
+ /* Nothing else to do here. */
+ return;
+
+ gas_assert (fixP->fx_addsy != NULL);
+ if (fixP->fx_r_type == BFD_RELOC_UNUSED)
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an expression
+ symbol. */
+ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsupported relocation against %s"),
+ S_GET_NAME (fixP->fx_addsy));
+ fixP->fx_done = 1;
+ return;
+ }
+ }
+ else
+ {
+ /* Handle relocs in data. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_VTABLE_INHERIT:
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ /* Fall thru */
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+#ifdef OBJ_ELF
+ /* These can appear with @l etc. in data. */
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_LO16_PCREL:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_PCREL:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_PPC64_HIGHER:
+ case BFD_RELOC_PPC64_HIGHER_S:
+ case BFD_RELOC_PPC64_HIGHEST:
+ case BFD_RELOC_PPC64_HIGHEST_S:
+ break;
+
+ case BFD_RELOC_PPC_DTPMOD:
+ case BFD_RELOC_PPC_TPREL:
+ case BFD_RELOC_PPC_DTPREL:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ /* Just punt all of these to the linker. */
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ 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_PPC_COPY:
+ case BFD_RELOC_PPC_GLOB_DAT:
+ 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_TLS:
+ case BFD_RELOC_PPC_DTPREL16_LO:
+ case BFD_RELOC_PPC_DTPREL16_HI:
+ case BFD_RELOC_PPC_DTPREL16_HA:
+ case BFD_RELOC_PPC_TPREL16_LO:
+ case BFD_RELOC_PPC_TPREL16_HI:
+ case BFD_RELOC_PPC_TPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TLSGD16:
+ case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+ case BFD_RELOC_PPC_GOT_TLSLD16:
+ case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16_HI:
+ case BFD_RELOC_PPC_GOT_TPREL16_HA:
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_PPC_LOCAL24PC:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+ case BFD_RELOC_PPC_EMB_NADDR32:
+ 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_SDA2REL:
+ case BFD_RELOC_PPC_EMB_SDA2I16:
+ case BFD_RELOC_PPC_EMB_SDA21:
+ case BFD_RELOC_PPC_VLE_SDA21_LO:
+ 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_PPC64_TOC:
+ 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_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_HIGHER:
+ case BFD_RELOC_PPC64_TPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_TPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+ fixP->fx_done = 0;
+ break;
+#endif
+
+#ifdef OBJ_XCOFF
+ case BFD_RELOC_NONE:
+#endif
+ case BFD_RELOC_CTOR:
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_8:
+ break;
+
+ default:
+ fprintf (stderr,
+ _("Gas failure, reloc value %d\n"), fixP->fx_r_type);
+ fflush (stderr);
+ abort ();
+ }
+
+ if (fixP->fx_size && APPLY_RELOC)
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ fieldval, fixP->fx_size);
+ }
+
+ /* We are only able to convert some relocs to pc-relative. */
+ if (!fixP->fx_done && fixP->fx_pcrel)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_LO16:
+ fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
+ break;
+
+ case BFD_RELOC_HI16:
+ fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
+ break;
+
+ case BFD_RELOC_64:
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ break;
+
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ break;
+
+ /* Some of course are already pc-relative. */
+ case BFD_RELOC_LO16_PCREL:
+ case BFD_RELOC_HI16_PCREL:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_PPC_B16:
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ case BFD_RELOC_PPC_B26:
+ case BFD_RELOC_PPC_LOCAL24PC:
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_64_PLT_PCREL:
+ case BFD_RELOC_PPC_VLE_REL8:
+ case BFD_RELOC_PPC_VLE_REL15:
+ case BFD_RELOC_PPC_VLE_REL24:
+ break;
+
+ default:
+ if (fixP->fx_addsy)
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an
+ expression symbol. */
+ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must"
+ " be resolved"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot emit PC relative %s relocation"
+ " against %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type),
+ S_GET_NAME (fixP->fx_addsy));
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unable to resolve expression"));
+ fixP->fx_done = 1;
+ break;
+ }
+ }
+
+#ifdef OBJ_ELF
+ ppc_elf_validate_fix (fixP, seg);
+ fixP->fx_addnumber = value;
+
+ /* PowerPC uses RELA relocs, ie. the reloc addend is stored separately
+ from the section contents. If we are going to be emitting a reloc
+ then the section contents are immaterial, so don't warn if they
+ happen to overflow. Leave such warnings to ld. */
+ if (!fixP->fx_done)
+ {
+ fixP->fx_no_overflow = 1;
+
+ /* Arrange to emit .TOC. as a normal symbol if used in anything
+ but .TOC.@tocbase. */
+ if (ppc_obj64
+ && fixP->fx_r_type != BFD_RELOC_PPC64_TOC
+ && fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), ".TOC.") == 0)
+ symbol_get_bfdsym (fixP->fx_addsy)->flags |= BSF_KEEP;
+ }
+#else
+ if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16)
+ fixP->fx_addnumber = 0;
+ else
+ {
+#ifdef TE_PE
+ fixP->fx_addnumber = 0;
+#else
+ /* We want to use the offset within the toc, not the actual VMA
+ of the symbol. */
+ 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
+}
+
+/* Generate a reloc for a fixup. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_addnumber;
+
+ return reloc;
+}
+
+void
+ppc_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (1, 0);
+}
+
+int
+tc_ppc_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int i;
+ const char *p;
+ char *q;
+ static struct { char *name; int dw2regnum; } regnames[] =
+ {
+ { "sp", 1 }, { "r.sp", 1 }, { "rtoc", 2 }, { "r.toc", 2 },
+ { "mq", 64 }, { "lr", 65 }, { "ctr", 66 }, { "ap", 67 },
+ { "cr", 70 }, { "xer", 76 }, { "vrsave", 109 }, { "vscr", 110 },
+ { "spe_acc", 111 }, { "spefscr", 112 }
+ };
+
+ for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+ if (strcmp (regnames[i].name, regname) == 0)
+ return regnames[i].dw2regnum;
+
+ if (regname[0] == 'r' || regname[0] == 'f' || regname[0] == 'v')
+ {
+ p = regname + 1 + (regname[1] == '.');
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 32)
+ return -1;
+ if (regname[0] == 'f')
+ regnum += 32;
+ else if (regname[0] == 'v')
+ regnum += 77;
+ }
+ else if (regname[0] == 'c' && regname[1] == 'r')
+ {
+ p = regname + 2 + (regname[2] == '.');
+ if (p[0] < '0' || p[0] > '7' || p[1])
+ return -1;
+ regnum = p[0] - '0' + 68;
+ }
+ return regnum;
+}