blob: af025af32ffc97a125249f52ceacd1ce2d1fe8a0 [file] [log] [blame]
/* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
Copyright (C) 1994-2021 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 "elf/ppc64.h"
#include "dwarf2dbg.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
#define TARGET_REG_NAMES_P false
#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) ^ 0x8000) - 0x8000)
/* For the time being on ppc64, don't report overflow on @h and @ha
applied to constants. */
#define REPORT_OVERFLOW_HI 0
static bool reg_names_p = TARGET_REG_NAMES_P;
static void ppc_macro (char *, const struct powerpc_macro *);
static void ppc_byte (int);
#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
static void ppc_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_rdata (int);
static void ppc_elf_lcomm (int);
static void ppc_elf_localentry (int);
static void ppc_elf_abiversion (int);
static void ppc_elf_gnu_attribute (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;
/* Value for ELF e_flags EF_PPC64_ABI. */
unsigned int ppc_abiversion = 0;
#ifdef OBJ_ELF
/* Flags set on encountering toc relocs. */
static enum {
has_large_toc_reloc = 1,
has_small_toc_reloc = 2
} toc_reloc_types;
#endif
/* Warn on emitting data to code sections. */
int warn_476;
uint64_t last_insn;
segT last_seg;
subsegT last_subseg;
/* 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", cons, 8 },
{ "rdata", ppc_elf_rdata, 0 },
{ "rodata", ppc_elf_rdata, 0 },
{ "lcomm", ppc_elf_lcomm, 0 },
{ "localentry", ppc_elf_localentry, 0 },
{ "abiversion", ppc_elf_abiversion, 0 },
{ "gnu_attribute", ppc_elf_gnu_attribute, 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
{
const char *name;
unsigned short value;
unsigned short flags;
};
/* 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
xer has the value 1
lr has the value 8
ctr has the value 9
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[] =
{
/* VSX accumulators. */
{ "a0", 0, PPC_OPERAND_ACC },
{ "a1", 1, PPC_OPERAND_ACC },
{ "a2", 2, PPC_OPERAND_ACC },
{ "a3", 3, PPC_OPERAND_ACC },
{ "a4", 4, PPC_OPERAND_ACC },
{ "a5", 5, PPC_OPERAND_ACC },
{ "a6", 6, PPC_OPERAND_ACC },
{ "a7", 7, PPC_OPERAND_ACC },
/* Condition Registers */
{ "cr.0", 0, PPC_OPERAND_CR_REG },
{ "cr.1", 1, PPC_OPERAND_CR_REG },
{ "cr.2", 2, PPC_OPERAND_CR_REG },
{ "cr.3", 3, PPC_OPERAND_CR_REG },
{ "cr.4", 4, PPC_OPERAND_CR_REG },
{ "cr.5", 5, PPC_OPERAND_CR_REG },
{ "cr.6", 6, PPC_OPERAND_CR_REG },
{ "cr.7", 7, PPC_OPERAND_CR_REG },
{ "cr0", 0, PPC_OPERAND_CR_REG },
{ "cr1", 1, PPC_OPERAND_CR_REG },
{ "cr2", 2, PPC_OPERAND_CR_REG },
{ "cr3", 3, PPC_OPERAND_CR_REG },
{ "cr4", 4, PPC_OPERAND_CR_REG },
{ "cr5", 5, PPC_OPERAND_CR_REG },
{ "cr6", 6, PPC_OPERAND_CR_REG },
{ "cr7", 7, PPC_OPERAND_CR_REG },
{ "ctr", 9, PPC_OPERAND_SPR },
{ "dar", 19, PPC_OPERAND_SPR },
{ "dec", 22, PPC_OPERAND_SPR },
{ "dsisr", 18, PPC_OPERAND_SPR },
/* Floating point registers */
{ "f.0", 0, PPC_OPERAND_FPR },
{ "f.1", 1, PPC_OPERAND_FPR },
{ "f.10", 10, PPC_OPERAND_FPR },
{ "f.11", 11, PPC_OPERAND_FPR },
{ "f.12", 12, PPC_OPERAND_FPR },
{ "f.13", 13, PPC_OPERAND_FPR },
{ "f.14", 14, PPC_OPERAND_FPR },
{ "f.15", 15, PPC_OPERAND_FPR },
{ "f.16", 16, PPC_OPERAND_FPR },
{ "f.17", 17, PPC_OPERAND_FPR },
{ "f.18", 18, PPC_OPERAND_FPR },
{ "f.19", 19, PPC_OPERAND_FPR },
{ "f.2", 2, PPC_OPERAND_FPR },
{ "f.20", 20, PPC_OPERAND_FPR },
{ "f.21", 21, PPC_OPERAND_FPR },
{ "f.22", 22, PPC_OPERAND_FPR },
{ "f.23", 23, PPC_OPERAND_FPR },
{ "f.24", 24, PPC_OPERAND_FPR },
{ "f.25", 25, PPC_OPERAND_FPR },
{ "f.26", 26, PPC_OPERAND_FPR },
{ "f.27", 27, PPC_OPERAND_FPR },
{ "f.28", 28, PPC_OPERAND_FPR },
{ "f.29", 29, PPC_OPERAND_FPR },
{ "f.3", 3, PPC_OPERAND_FPR },
{ "f.30", 30, PPC_OPERAND_FPR },
{ "f.31", 31, PPC_OPERAND_FPR },
{ "f.32", 32, PPC_OPERAND_VSR },
{ "f.33", 33, PPC_OPERAND_VSR },
{ "f.34", 34, PPC_OPERAND_VSR },
{ "f.35", 35, PPC_OPERAND_VSR },
{ "f.36", 36, PPC_OPERAND_VSR },
{ "f.37", 37, PPC_OPERAND_VSR },
{ "f.38", 38, PPC_OPERAND_VSR },
{ "f.39", 39, PPC_OPERAND_VSR },
{ "f.4", 4, PPC_OPERAND_FPR },
{ "f.40", 40, PPC_OPERAND_VSR },
{ "f.41", 41, PPC_OPERAND_VSR },
{ "f.42", 42, PPC_OPERAND_VSR },
{ "f.43", 43, PPC_OPERAND_VSR },
{ "f.44", 44, PPC_OPERAND_VSR },
{ "f.45", 45, PPC_OPERAND_VSR },
{ "f.46", 46, PPC_OPERAND_VSR },
{ "f.47", 47, PPC_OPERAND_VSR },
{ "f.48", 48, PPC_OPERAND_VSR },
{ "f.49", 49, PPC_OPERAND_VSR },
{ "f.5", 5, PPC_OPERAND_FPR },
{ "f.50", 50, PPC_OPERAND_VSR },
{ "f.51", 51, PPC_OPERAND_VSR },
{ "f.52", 52, PPC_OPERAND_VSR },
{ "f.53", 53, PPC_OPERAND_VSR },
{ "f.54", 54, PPC_OPERAND_VSR },
{ "f.55", 55, PPC_OPERAND_VSR },
{ "f.56", 56, PPC_OPERAND_VSR },
{ "f.57", 57, PPC_OPERAND_VSR },
{ "f.58", 58, PPC_OPERAND_VSR },
{ "f.59", 59, PPC_OPERAND_VSR },
{ "f.6", 6, PPC_OPERAND_FPR },
{ "f.60", 60, PPC_OPERAND_VSR },
{ "f.61", 61, PPC_OPERAND_VSR },
{ "f.62", 62, PPC_OPERAND_VSR },
{ "f.63", 63, PPC_OPERAND_VSR },
{ "f.7", 7, PPC_OPERAND_FPR },
{ "f.8", 8, PPC_OPERAND_FPR },
{ "f.9", 9, PPC_OPERAND_FPR },
{ "f0", 0, PPC_OPERAND_FPR },
{ "f1", 1, PPC_OPERAND_FPR },
{ "f10", 10, PPC_OPERAND_FPR },
{ "f11", 11, PPC_OPERAND_FPR },
{ "f12", 12, PPC_OPERAND_FPR },
{ "f13", 13, PPC_OPERAND_FPR },
{ "f14", 14, PPC_OPERAND_FPR },
{ "f15", 15, PPC_OPERAND_FPR },
{ "f16", 16, PPC_OPERAND_FPR },
{ "f17", 17, PPC_OPERAND_FPR },
{ "f18", 18, PPC_OPERAND_FPR },
{ "f19", 19, PPC_OPERAND_FPR },
{ "f2", 2, PPC_OPERAND_FPR },
{ "f20", 20, PPC_OPERAND_FPR },
{ "f21", 21, PPC_OPERAND_FPR },
{ "f22", 22, PPC_OPERAND_FPR },
{ "f23", 23, PPC_OPERAND_FPR },
{ "f24", 24, PPC_OPERAND_FPR },
{ "f25", 25, PPC_OPERAND_FPR },
{ "f26", 26, PPC_OPERAND_FPR },
{ "f27", 27, PPC_OPERAND_FPR },
{ "f28", 28, PPC_OPERAND_FPR },
{ "f29", 29, PPC_OPERAND_FPR },
{ "f3", 3, PPC_OPERAND_FPR },
{ "f30", 30, PPC_OPERAND_FPR },
{ "f31", 31, PPC_OPERAND_FPR },
{ "f32", 32, PPC_OPERAND_VSR },
{ "f33", 33, PPC_OPERAND_VSR },
{ "f34", 34, PPC_OPERAND_VSR },
{ "f35", 35, PPC_OPERAND_VSR },
{ "f36", 36, PPC_OPERAND_VSR },
{ "f37", 37, PPC_OPERAND_VSR },
{ "f38", 38, PPC_OPERAND_VSR },
{ "f39", 39, PPC_OPERAND_VSR },
{ "f4", 4, PPC_OPERAND_FPR },
{ "f40", 40, PPC_OPERAND_VSR },
{ "f41", 41, PPC_OPERAND_VSR },
{ "f42", 42, PPC_OPERAND_VSR },
{ "f43", 43, PPC_OPERAND_VSR },
{ "f44", 44, PPC_OPERAND_VSR },
{ "f45", 45, PPC_OPERAND_VSR },
{ "f46", 46, PPC_OPERAND_VSR },
{ "f47", 47, PPC_OPERAND_VSR },
{ "f48", 48, PPC_OPERAND_VSR },
{ "f49", 49, PPC_OPERAND_VSR },
{ "f5", 5, PPC_OPERAND_FPR },
{ "f50", 50, PPC_OPERAND_VSR },
{ "f51", 51, PPC_OPERAND_VSR },
{ "f52", 52, PPC_OPERAND_VSR },
{ "f53", 53, PPC_OPERAND_VSR },
{ "f54", 54, PPC_OPERAND_VSR },
{ "f55", 55, PPC_OPERAND_VSR },
{ "f56", 56, PPC_OPERAND_VSR },
{ "f57", 57, PPC_OPERAND_VSR },
{ "f58", 58, PPC_OPERAND_VSR },
{ "f59", 59, PPC_OPERAND_VSR },
{ "f6", 6, PPC_OPERAND_FPR },
{ "f60", 60, PPC_OPERAND_VSR },
{ "f61", 61, PPC_OPERAND_VSR },
{ "f62", 62, PPC_OPERAND_VSR },
{ "f63", 63, PPC_OPERAND_VSR },
{ "f7", 7, PPC_OPERAND_FPR },
{ "f8", 8, PPC_OPERAND_FPR },
{ "f9", 9, PPC_OPERAND_FPR },
/* Quantization registers used with pair single instructions. */
{ "gqr.0", 0, PPC_OPERAND_GQR },
{ "gqr.1", 1, PPC_OPERAND_GQR },
{ "gqr.2", 2, PPC_OPERAND_GQR },
{ "gqr.3", 3, PPC_OPERAND_GQR },
{ "gqr.4", 4, PPC_OPERAND_GQR },
{ "gqr.5", 5, PPC_OPERAND_GQR },
{ "gqr.6", 6, PPC_OPERAND_GQR },
{ "gqr.7", 7, PPC_OPERAND_GQR },
{ "gqr0", 0, PPC_OPERAND_GQR },
{ "gqr1", 1, PPC_OPERAND_GQR },
{ "gqr2", 2, PPC_OPERAND_GQR },
{ "gqr3", 3, PPC_OPERAND_GQR },
{ "gqr4", 4, PPC_OPERAND_GQR },
{ "gqr5", 5, PPC_OPERAND_GQR },
{ "gqr6", 6, PPC_OPERAND_GQR },
{ "gqr7", 7, PPC_OPERAND_GQR },
{ "lr", 8, PPC_OPERAND_SPR },
/* General Purpose Registers */
{ "r.0", 0, PPC_OPERAND_GPR },
{ "r.1", 1, PPC_OPERAND_GPR },
{ "r.10", 10, PPC_OPERAND_GPR },
{ "r.11", 11, PPC_OPERAND_GPR },
{ "r.12", 12, PPC_OPERAND_GPR },
{ "r.13", 13, PPC_OPERAND_GPR },
{ "r.14", 14, PPC_OPERAND_GPR },
{ "r.15", 15, PPC_OPERAND_GPR },
{ "r.16", 16, PPC_OPERAND_GPR },
{ "r.17", 17, PPC_OPERAND_GPR },
{ "r.18", 18, PPC_OPERAND_GPR },
{ "r.19", 19, PPC_OPERAND_GPR },
{ "r.2", 2, PPC_OPERAND_GPR },
{ "r.20", 20, PPC_OPERAND_GPR },
{ "r.21", 21, PPC_OPERAND_GPR },
{ "r.22", 22, PPC_OPERAND_GPR },
{ "r.23", 23, PPC_OPERAND_GPR },
{ "r.24", 24, PPC_OPERAND_GPR },
{ "r.25", 25, PPC_OPERAND_GPR },
{ "r.26", 26, PPC_OPERAND_GPR },
{ "r.27", 27, PPC_OPERAND_GPR },
{ "r.28", 28, PPC_OPERAND_GPR },
{ "r.29", 29, PPC_OPERAND_GPR },
{ "r.3", 3, PPC_OPERAND_GPR },
{ "r.30", 30, PPC_OPERAND_GPR },
{ "r.31", 31, PPC_OPERAND_GPR },
{ "r.4", 4, PPC_OPERAND_GPR },
{ "r.5", 5, PPC_OPERAND_GPR },
{ "r.6", 6, PPC_OPERAND_GPR },
{ "r.7", 7, PPC_OPERAND_GPR },
{ "r.8", 8, PPC_OPERAND_GPR },
{ "r.9", 9, PPC_OPERAND_GPR },
{ "r.sp", 1, PPC_OPERAND_GPR },
{ "r.toc", 2, PPC_OPERAND_GPR },
{ "r0", 0, PPC_OPERAND_GPR },
{ "r1", 1, PPC_OPERAND_GPR },
{ "r10", 10, PPC_OPERAND_GPR },
{ "r11", 11, PPC_OPERAND_GPR },
{ "r12", 12, PPC_OPERAND_GPR },
{ "r13", 13, PPC_OPERAND_GPR },
{ "r14", 14, PPC_OPERAND_GPR },
{ "r15", 15, PPC_OPERAND_GPR },
{ "r16", 16, PPC_OPERAND_GPR },
{ "r17", 17, PPC_OPERAND_GPR },
{ "r18", 18, PPC_OPERAND_GPR },
{ "r19", 19, PPC_OPERAND_GPR },
{ "r2", 2, PPC_OPERAND_GPR },
{ "r20", 20, PPC_OPERAND_GPR },
{ "r21", 21, PPC_OPERAND_GPR },
{ "r22", 22, PPC_OPERAND_GPR },
{ "r23", 23, PPC_OPERAND_GPR },
{ "r24", 24, PPC_OPERAND_GPR },
{ "r25", 25, PPC_OPERAND_GPR },
{ "r26", 26, PPC_OPERAND_GPR },
{ "r27", 27, PPC_OPERAND_GPR },
{ "r28", 28, PPC_OPERAND_GPR },
{ "r29", 29, PPC_OPERAND_GPR },
{ "r3", 3, PPC_OPERAND_GPR },
{ "r30", 30, PPC_OPERAND_GPR },
{ "r31", 31, PPC_OPERAND_GPR },
{ "r4", 4, PPC_OPERAND_GPR },
{ "r5", 5, PPC_OPERAND_GPR },
{ "r6", 6, PPC_OPERAND_GPR },
{ "r7", 7, PPC_OPERAND_GPR },
{ "r8", 8, PPC_OPERAND_GPR },
{ "r9", 9, PPC_OPERAND_GPR },
{ "rtoc", 2, PPC_OPERAND_GPR },
{ "sdr1", 25, PPC_OPERAND_SPR },
{ "sp", 1, PPC_OPERAND_GPR },
{ "srr0", 26, PPC_OPERAND_SPR },
{ "srr1", 27, PPC_OPERAND_SPR },
/* Vector (Altivec/VMX) registers */
{ "v.0", 0, PPC_OPERAND_VR },
{ "v.1", 1, PPC_OPERAND_VR },
{ "v.10", 10, PPC_OPERAND_VR },
{ "v.11", 11, PPC_OPERAND_VR },
{ "v.12", 12, PPC_OPERAND_VR },
{ "v.13", 13, PPC_OPERAND_VR },
{ "v.14", 14, PPC_OPERAND_VR },
{ "v.15", 15, PPC_OPERAND_VR },
{ "v.16", 16, PPC_OPERAND_VR },
{ "v.17", 17, PPC_OPERAND_VR },
{ "v.18", 18, PPC_OPERAND_VR },
{ "v.19", 19, PPC_OPERAND_VR },
{ "v.2", 2, PPC_OPERAND_VR },
{ "v.20", 20, PPC_OPERAND_VR },
{ "v.21", 21, PPC_OPERAND_VR },
{ "v.22", 22, PPC_OPERAND_VR },
{ "v.23", 23, PPC_OPERAND_VR },
{ "v.24", 24, PPC_OPERAND_VR },
{ "v.25", 25, PPC_OPERAND_VR },
{ "v.26", 26, PPC_OPERAND_VR },
{ "v.27", 27, PPC_OPERAND_VR },
{ "v.28", 28, PPC_OPERAND_VR },
{ "v.29", 29, PPC_OPERAND_VR },
{ "v.3", 3, PPC_OPERAND_VR },
{ "v.30", 30, PPC_OPERAND_VR },
{ "v.31", 31, PPC_OPERAND_VR },
{ "v.4", 4, PPC_OPERAND_VR },
{ "v.5", 5, PPC_OPERAND_VR },
{ "v.6", 6, PPC_OPERAND_VR },
{ "v.7", 7, PPC_OPERAND_VR },
{ "v.8", 8, PPC_OPERAND_VR },
{ "v.9", 9, PPC_OPERAND_VR },
{ "v0", 0, PPC_OPERAND_VR },
{ "v1", 1, PPC_OPERAND_VR },
{ "v10", 10, PPC_OPERAND_VR },
{ "v11", 11, PPC_OPERAND_VR },
{ "v12", 12, PPC_OPERAND_VR },
{ "v13", 13, PPC_OPERAND_VR },
{ "v14", 14, PPC_OPERAND_VR },
{ "v15", 15, PPC_OPERAND_VR },
{ "v16", 16, PPC_OPERAND_VR },
{ "v17", 17, PPC_OPERAND_VR },
{ "v18", 18, PPC_OPERAND_VR },
{ "v19", 19, PPC_OPERAND_VR },
{ "v2", 2, PPC_OPERAND_VR },
{ "v20", 20, PPC_OPERAND_VR },
{ "v21", 21, PPC_OPERAND_VR },
{ "v22", 22, PPC_OPERAND_VR },
{ "v23", 23, PPC_OPERAND_VR },
{ "v24", 24, PPC_OPERAND_VR },
{ "v25", 25, PPC_OPERAND_VR },
{ "v26", 26, PPC_OPERAND_VR },
{ "v27", 27, PPC_OPERAND_VR },
{ "v28", 28, PPC_OPERAND_VR },
{ "v29", 29, PPC_OPERAND_VR },
{ "v3", 3, PPC_OPERAND_VR },
{ "v30", 30, PPC_OPERAND_VR },
{ "v31", 31, PPC_OPERAND_VR },
{ "v4", 4, PPC_OPERAND_VR },
{ "v5", 5, PPC_OPERAND_VR },
{ "v6", 6, PPC_OPERAND_VR },
{ "v7", 7, PPC_OPERAND_VR },
{ "v8", 8, PPC_OPERAND_VR },
{ "v9", 9, PPC_OPERAND_VR },
/* Vector Scalar (VSX) registers (ISA 2.06). */
{ "vs.0", 0, PPC_OPERAND_VSR },
{ "vs.1", 1, PPC_OPERAND_VSR },
{ "vs.10", 10, PPC_OPERAND_VSR },
{ "vs.11", 11, PPC_OPERAND_VSR },
{ "vs.12", 12, PPC_OPERAND_VSR },
{ "vs.13", 13, PPC_OPERAND_VSR },
{ "vs.14", 14, PPC_OPERAND_VSR },
{ "vs.15", 15, PPC_OPERAND_VSR },
{ "vs.16", 16, PPC_OPERAND_VSR },
{ "vs.17", 17, PPC_OPERAND_VSR },
{ "vs.18", 18, PPC_OPERAND_VSR },
{ "vs.19", 19, PPC_OPERAND_VSR },
{ "vs.2", 2, PPC_OPERAND_VSR },
{ "vs.20", 20, PPC_OPERAND_VSR },
{ "vs.21", 21, PPC_OPERAND_VSR },
{ "vs.22", 22, PPC_OPERAND_VSR },
{ "vs.23", 23, PPC_OPERAND_VSR },
{ "vs.24", 24, PPC_OPERAND_VSR },
{ "vs.25", 25, PPC_OPERAND_VSR },
{ "vs.26", 26, PPC_OPERAND_VSR },
{ "vs.27", 27, PPC_OPERAND_VSR },
{ "vs.28", 28, PPC_OPERAND_VSR },
{ "vs.29", 29, PPC_OPERAND_VSR },
{ "vs.3", 3, PPC_OPERAND_VSR },
{ "vs.30", 30, PPC_OPERAND_VSR },
{ "vs.31", 31, PPC_OPERAND_VSR },
{ "vs.32", 32, PPC_OPERAND_VSR },
{ "vs.33", 33, PPC_OPERAND_VSR },
{ "vs.34", 34, PPC_OPERAND_VSR },
{ "vs.35", 35, PPC_OPERAND_VSR },
{ "vs.36", 36, PPC_OPERAND_VSR },
{ "vs.37", 37, PPC_OPERAND_VSR },
{ "vs.38", 38, PPC_OPERAND_VSR },
{ "vs.39", 39, PPC_OPERAND_VSR },
{ "vs.4", 4, PPC_OPERAND_VSR },
{ "vs.40", 40, PPC_OPERAND_VSR },
{ "vs.41", 41, PPC_OPERAND_VSR },
{ "vs.42", 42, PPC_OPERAND_VSR },
{ "vs.43", 43, PPC_OPERAND_VSR },
{ "vs.44", 44, PPC_OPERAND_VSR },
{ "vs.45", 45, PPC_OPERAND_VSR },
{ "vs.46", 46, PPC_OPERAND_VSR },
{ "vs.47", 47, PPC_OPERAND_VSR },
{ "vs.48", 48, PPC_OPERAND_VSR },
{ "vs.49", 49, PPC_OPERAND_VSR },
{ "vs.5", 5, PPC_OPERAND_VSR },
{ "vs.50", 50, PPC_OPERAND_VSR },
{ "vs.51", 51, PPC_OPERAND_VSR },
{ "vs.52", 52, PPC_OPERAND_VSR },
{ "vs.53", 53, PPC_OPERAND_VSR },
{ "vs.54", 54, PPC_OPERAND_VSR },
{ "vs.55", 55, PPC_OPERAND_VSR },
{ "vs.56", 56, PPC_OPERAND_VSR },
{ "vs.57", 57, PPC_OPERAND_VSR },
{ "vs.58", 58, PPC_OPERAND_VSR },
{ "vs.59", 59, PPC_OPERAND_VSR },
{ "vs.6", 6, PPC_OPERAND_VSR },
{ "vs.60", 60, PPC_OPERAND_VSR },
{ "vs.61", 61, PPC_OPERAND_VSR },
{ "vs.62", 62, PPC_OPERAND_VSR },
{ "vs.63", 63, PPC_OPERAND_VSR },
{ "vs.7", 7, PPC_OPERAND_VSR },
{ "vs.8", 8, PPC_OPERAND_VSR },
{ "vs.9", 9, PPC_OPERAND_VSR },
{ "vs0", 0, PPC_OPERAND_VSR },
{ "vs1", 1, PPC_OPERAND_VSR },
{ "vs10", 10, PPC_OPERAND_VSR },
{ "vs11", 11, PPC_OPERAND_VSR },
{ "vs12", 12, PPC_OPERAND_VSR },
{ "vs13", 13, PPC_OPERAND_VSR },
{ "vs14", 14, PPC_OPERAND_VSR },
{ "vs15", 15, PPC_OPERAND_VSR },
{ "vs16", 16, PPC_OPERAND_VSR },
{ "vs17", 17, PPC_OPERAND_VSR },
{ "vs18", 18, PPC_OPERAND_VSR },
{ "vs19", 19, PPC_OPERAND_VSR },
{ "vs2", 2, PPC_OPERAND_VSR },
{ "vs20", 20, PPC_OPERAND_VSR },
{ "vs21", 21, PPC_OPERAND_VSR },
{ "vs22", 22, PPC_OPERAND_VSR },
{ "vs23", 23, PPC_OPERAND_VSR },
{ "vs24", 24, PPC_OPERAND_VSR },
{ "vs25", 25, PPC_OPERAND_VSR },
{ "vs26", 26, PPC_OPERAND_VSR },
{ "vs27", 27, PPC_OPERAND_VSR },
{ "vs28", 28, PPC_OPERAND_VSR },
{ "vs29", 29, PPC_OPERAND_VSR },
{ "vs3", 3, PPC_OPERAND_VSR },
{ "vs30", 30, PPC_OPERAND_VSR },
{ "vs31", 31, PPC_OPERAND_VSR },
{ "vs32", 32, PPC_OPERAND_VSR },
{ "vs33", 33, PPC_OPERAND_VSR },
{ "vs34", 34, PPC_OPERAND_VSR },
{ "vs35", 35, PPC_OPERAND_VSR },
{ "vs36", 36, PPC_OPERAND_VSR },
{ "vs37", 37, PPC_OPERAND_VSR },
{ "vs38", 38, PPC_OPERAND_VSR },
{ "vs39", 39, PPC_OPERAND_VSR },
{ "vs4", 4, PPC_OPERAND_VSR },
{ "vs40", 40, PPC_OPERAND_VSR },
{ "vs41", 41, PPC_OPERAND_VSR },
{ "vs42", 42, PPC_OPERAND_VSR },
{ "vs43", 43, PPC_OPERAND_VSR },
{ "vs44", 44, PPC_OPERAND_VSR },
{ "vs45", 45, PPC_OPERAND_VSR },
{ "vs46", 46, PPC_OPERAND_VSR },
{ "vs47", 47, PPC_OPERAND_VSR },
{ "vs48", 48, PPC_OPERAND_VSR },
{ "vs49", 49, PPC_OPERAND_VSR },
{ "vs5", 5, PPC_OPERAND_VSR },
{ "vs50", 50, PPC_OPERAND_VSR },
{ "vs51", 51, PPC_OPERAND_VSR },
{ "vs52", 52, PPC_OPERAND_VSR },
{ "vs53", 53, PPC_OPERAND_VSR },
{ "vs54", 54, PPC_OPERAND_VSR },
{ "vs55", 55, PPC_OPERAND_VSR },
{ "vs56", 56, PPC_OPERAND_VSR },
{ "vs57", 57, PPC_OPERAND_VSR },
{ "vs58", 58, PPC_OPERAND_VSR },
{ "vs59", 59, PPC_OPERAND_VSR },
{ "vs6", 6, PPC_OPERAND_VSR },
{ "vs60", 60, PPC_OPERAND_VSR },
{ "vs61", 61, PPC_OPERAND_VSR },
{ "vs62", 62, PPC_OPERAND_VSR },
{ "vs63", 63, PPC_OPERAND_VSR },
{ "vs7", 7, PPC_OPERAND_VSR },
{ "vs8", 8, PPC_OPERAND_VSR },
{ "vs9", 9, PPC_OPERAND_VSR },
{ "xer", 1, PPC_OPERAND_SPR }
};
#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
/* Given NAME, find the register number associated with that name, return
the integer value associated with the given name or -1 on failure. */
static const struct pd_reg *
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];
}
while (low <= high);
return NULL;
}
/*
* Summary of register_name.
*
* in: Input_line_pointer points to 1st char of operand.
*
* out: A expressionS.
* The operand may have been a register: in this case, X_op == O_register,
* X_add_number is set to the register number, and truth is returned.
* Input_line_pointer->(next non-blank) char after operand, or is in its
* original state.
*/
static bool
register_name (expressionS *expressionP)
{
const struct pd_reg *reg;
char *name;
char *start;
char c;
/* Find the spelling of the operand. */
start = name = input_line_pointer;
if (name[0] == '%' && ISALPHA (name[1]))
name = ++input_line_pointer;
else if (!reg_names_p || !ISALPHA (name[0]))
return false;
c = get_symbol_name (&name);
reg = 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 != NULL)
{
expressionP->X_op = O_register;
expressionP->X_add_number = reg->value;
expressionP->X_md = reg->flags;
/* Make the rest nice. */
expressionP->X_add_symbol = NULL;
expressionP->X_op_symbol = NULL;
return true;
}
/* Reset the line as if we had not done anything. */
input_line_pointer = start;
return false;
}
/* This function is called for each symbol seen in an expression. It
handles the special parsing which PowerPC assemblers are supposed
to use for condition codes. */
/* Whether to do the special parsing. */
static bool cr_operand;
/* Names to recognize in a condition code. This table is sorted. */
static const struct pd_reg cr_names[] =
{
{ "cr0", 0, PPC_OPERAND_CR_REG },
{ "cr1", 1, PPC_OPERAND_CR_REG },
{ "cr2", 2, PPC_OPERAND_CR_REG },
{ "cr3", 3, PPC_OPERAND_CR_REG },
{ "cr4", 4, PPC_OPERAND_CR_REG },
{ "cr5", 5, PPC_OPERAND_CR_REG },
{ "cr6", 6, PPC_OPERAND_CR_REG },
{ "cr7", 7, PPC_OPERAND_CR_REG },
{ "eq", 2, PPC_OPERAND_CR_BIT },
{ "gt", 1, PPC_OPERAND_CR_BIT },
{ "lt", 0, PPC_OPERAND_CR_BIT },
{ "so", 3, PPC_OPERAND_CR_BIT },
{ "un", 3, PPC_OPERAND_CR_BIT }
};
/* Parsing function. This returns non-zero if it recognized an
expression. */
int
ppc_parse_name (const char *name, expressionS *exp)
{
const struct pd_reg *reg;
if (! cr_operand)
return 0;
if (*name == '%')
++name;
reg = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
name);
if (reg == NULL)
return 0;
exp->X_op = O_register;
exp->X_add_number = reg->value;
exp->X_md = reg->flags;
return 1;
}
/* Propagate X_md and check register expressions. This is to support
condition codes like 4*cr5+eq. */
int
ppc_optimize_expr (expressionS *left, operatorT op, expressionS *right)
{
/* Accept 4*cr<n> and cr<n>*4. */
if (op == O_multiply
&& ((right->X_op == O_register
&& right->X_md == PPC_OPERAND_CR_REG
&& left->X_op == O_constant
&& left->X_add_number == 4)
|| (left->X_op == O_register
&& left->X_md == PPC_OPERAND_CR_REG
&& right->X_op == O_constant
&& right->X_add_number == 4)))
{
left->X_op = O_register;
left->X_md = PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT;
left->X_add_number *= right->X_add_number;
return 1;
}
/* Accept the above plus <cr bit>, and <cr bit> plus the above. */
if (right->X_op == O_register
&& left->X_op == O_register
&& op == O_add
&& ((right->X_md == PPC_OPERAND_CR_BIT
&& left->X_md == (PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT))
|| (right->X_md == (PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT)
&& left->X_md == PPC_OPERAND_CR_BIT)))
{
left->X_md = PPC_OPERAND_CR_BIT;
right->X_op = O_constant;
return 0;
}
/* Accept reg +/- constant. */
if (left->X_op == O_register
&& !((op == O_add || op == O_subtract) && right->X_op == O_constant))
as_warn (_("invalid register expression"));
/* Accept constant + reg. */
if (right->X_op == O_register)
{
if (op == O_add && left->X_op == O_constant)
left->X_md = right->X_md;
else
as_warn (_("invalid register expression"));
}
return 0;
}
/* Local variables. */
/* Whether to target xcoff64/elf64. */
static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
/* Opcode hash table. */
static htab_t ppc_hash;
/* Macro hash table. */
static htab_t ppc_macro_hash;
#ifdef OBJ_ELF
/* What type of shared library support to use. */
static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE;
/* 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 bool 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, .data or .bss sections
of the final output file. We handle this by using different
subsegments within these main segments.
.tdata and .tbss sections only have one type of csects for now,
but it's better to follow the same construction like the others. */
struct ppc_xcoff_section ppc_xcoff_text_section;
struct ppc_xcoff_section ppc_xcoff_data_section;
struct ppc_xcoff_section ppc_xcoff_bss_section;
struct ppc_xcoff_section ppc_xcoff_tdata_section;
struct ppc_xcoff_section ppc_xcoff_tbss_section;
/* Return true if the ppc_xcoff_section structure is already
initialized. */
static bool
ppc_xcoff_section_is_initialized (struct ppc_xcoff_section *section)
{
return section->segment != NULL;
}
/* Initialize a ppc_xcoff_section.
Dummy symbols are used to ensure the position of .text over .data
and .tdata. Moreover, they allow all algorithms here to be sure that
csects isn't NULL. These symbols won't be output. */
static void
ppc_init_xcoff_section (struct ppc_xcoff_section *s, segT seg)
{
s->segment = seg;
s->next_subsegment = 2;
s->csects = symbol_make ("dummy\001");
symbol_get_tc (s->csects)->within = s->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 OBJ_ELF
symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
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},
{"ppc476-workaround", no_argument, &warn_476, 1},
{"no-ppc476-workaround", no_argument, &warn_476, 0},
{NULL, no_argument, NULL, 0}
};
const size_t md_longopts_size = sizeof (md_longopts);
int
md_parse_option (int c, const 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);
/* "raw" is only valid for the disassembler. */
if (new_cpu != 0 && (new_cpu & PPC_OPCODE_RAW) == 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, "no-vle") == 0)
{
sticky &= ~PPC_OPCODE_VLE;
new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, "booke");
new_cpu &= ~PPC_OPCODE_VLE;
ppc_cpu = new_cpu;
}
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;
}
else if (strcmp (arg, "spe2") == 0)
{
ppc_cpu |= PPC_OPCODE_SPE2;
}
#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;
case 0:
break;
default:
return 0;
}
return 1;
}
static int
is_ppc64_target (const bfd_target *targ, void *data ATTRIBUTE_UNUSED)
{
switch (targ->flavour)
{
#ifdef OBJ_ELF
case bfd_target_elf_flavour:
return startswith (targ->name, "elf64-powerpc");
#endif
#ifdef OBJ_XCOFF
case bfd_target_xcoff_flavour:
return (strcmp (targ->name, "aixcoff64-rs6000") == 0
|| strcmp (targ->name, "aix5coff64-rs6000") == 0);
#endif
default:
return 0;
}
}
void
md_show_usage (FILE *stream)
{
fprintf (stream, _("\
PowerPC options:\n"));
fprintf (stream, _("\
-a32 generate ELF32/XCOFF32\n"));
if (bfd_iterate_over_targets (is_ppc64_target, NULL))
fprintf (stream, _("\
-a64 generate ELF64/XCOFF64\n"));
fprintf (stream, _("\
-u ignored\n"));
fprintf (stream, _("\
-mpwrx, -mpwr2 generate code for POWER/2 (RIOS2)\n"));
fprintf (stream, _("\
-mpwr generate code for POWER (RIOS1)\n"));
fprintf (stream, _("\
-m601 generate code for PowerPC 601\n"));
fprintf (stream, _("\
-mppc, -mppc32, -m603, -m604\n\
generate code for PowerPC 603/604\n"));
fprintf (stream, _("\
-m403 generate code for PowerPC 403\n"));
fprintf (stream, _("\
-m405 generate code for PowerPC 405\n"));
fprintf (stream, _("\
-m440 generate code for PowerPC 440\n"));
fprintf (stream, _("\
-m464 generate code for PowerPC 464\n"));
fprintf (stream, _("\
-m476 generate code for PowerPC 476\n"));
fprintf (stream, _("\
-m7400, -m7410, -m7450, -m7455\n\
generate code for PowerPC 7400/7410/7450/7455\n"));
fprintf (stream, _("\
-m750cl, -mgekko, -mbroadway\n\
generate code for PowerPC 750cl/Gekko/Broadway\n"));
fprintf (stream, _("\
-m821, -m850, -m860 generate code for PowerPC 821/850/860\n"));
fprintf (stream, _("\
-mppc64, -m620 generate code for PowerPC 620/625/630\n"));
fprintf (stream, _("\
-mppc64bridge generate code for PowerPC 64, including bridge insns\n"));
fprintf (stream, _("\
-mbooke generate code for 32-bit PowerPC BookE\n"));
fprintf (stream, _("\
-ma2 generate code for A2 architecture\n"));
fprintf (stream, _("\
-mpower4, -mpwr4 generate code for Power4 architecture\n"));
fprintf (stream, _("\
-mpower5, -mpwr5, -mpwr5x\n\
generate code for Power5 architecture\n"));
fprintf (stream, _("\
-mpower6, -mpwr6 generate code for Power6 architecture\n"));
fprintf (stream, _("\
-mpower7, -mpwr7 generate code for Power7 architecture\n"));
fprintf (stream, _("\
-mpower8, -mpwr8 generate code for Power8 architecture\n"));
fprintf (stream, _("\
-mpower9, -mpwr9 generate code for Power9 architecture\n"));
fprintf (stream, _("\
-mpower10, -mpwr10 generate code for Power10 architecture\n"));
fprintf (stream, _("\
-mcell generate code for Cell Broadband Engine architecture\n"));
fprintf (stream, _("\
-mcom generate code for Power/PowerPC common instructions\n"));
fprintf (stream, _("\
-many generate code for any architecture (PWR/PWRX/PPC)\n"));
fprintf (stream, _("\
-maltivec generate code for AltiVec\n"));
fprintf (stream, _("\
-mvsx generate code for Vector-Scalar (VSX) instructions\n"));
fprintf (stream, _("\
-me300 generate code for PowerPC e300 family\n"));
fprintf (stream, _("\
-me500, -me500x2 generate code for Motorola e500 core complex\n"));
fprintf (stream, _("\
-me500mc, generate code for Freescale e500mc core complex\n"));
fprintf (stream, _("\
-me500mc64, generate code for Freescale e500mc64 core complex\n"));
fprintf (stream, _("\
-me5500, generate code for Freescale e5500 core complex\n"));
fprintf (stream, _("\
-me6500, generate code for Freescale e6500 core complex\n"));
fprintf (stream, _("\
-mspe generate code for Motorola SPE instructions\n"));
fprintf (stream, _("\
-mspe2 generate code for Freescale SPE2 instructions\n"));
fprintf (stream, _("\
-mvle generate code for Freescale VLE instructions\n"));
fprintf (stream, _("\
-mtitan generate code for AppliedMicro Titan core complex\n"));
fprintf (stream, _("\
-mregnames Allow symbolic names for registers\n"));
fprintf (stream, _("\
-mno-regnames Do not allow symbolic names for registers\n"));
#ifdef OBJ_ELF
fprintf (stream, _("\
-mrelocatable support for GCC's -mrelocatble option\n"));
fprintf (stream, _("\
-mrelocatable-lib support for GCC's -mrelocatble-lib option\n"));
fprintf (stream, _("\
-memb set PPC_EMB bit in ELF flags\n"));
fprintf (stream, _("\
-mlittle, -mlittle-endian, -le\n\
generate code for a little endian machine\n"));
fprintf (stream, _("\
-mbig, -mbig-endian, -be\n\
generate code for a big endian machine\n"));
fprintf (stream, _("\
-msolaris generate code for Solaris\n"));
fprintf (stream, _("\
-mno-solaris do not generate code for Solaris\n"));
fprintf (stream, _("\
-K PIC set EF_PPC_RELOCATABLE_LIB in ELF flags\n"));
fprintf (stream, _("\
-V print assembler version number\n"));
fprintf (stream, _("\
-Qy, -Qn ignored\n"));
#endif
fprintf (stream, _("\
-nops=count when aligning, more than COUNT nops uses a branch\n"));
fprintf (stream, _("\
-ppc476-workaround warn if emitting data to code sections\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)
if (target_big_endian)
ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_64;
else
/* The minimum supported cpu for 64-bit little-endian is power8. */
ppc_cpu |= ppc_parse_cpu (ppc_cpu, &sticky, "power8");
else if (startswith (default_os, "aix")
&& default_os[3] >= '4' && default_os[3] <= '9')
ppc_cpu |= PPC_OPCODE_COMMON;
else if (startswith (default_os, "aix3"))
ppc_cpu |= PPC_OPCODE_POWER;
else if (strcmp (default_cpu, "rs6000") == 0)
ppc_cpu |= PPC_OPCODE_POWER;
else if (startswith (default_cpu, "powerpc"))
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 (startswith (default_cpu, "powerpc"))
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 const char*
ppc_target_format (void)
{
#ifdef OBJ_COFF
#if 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 bool
insn_validate (const struct powerpc_opcode *op)
{
const unsigned char *o;
uint64_t 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)
{
bool optional = false;
if (*o >= num_powerpc_operands)
{
as_bad (_("operand index error for %s"), op->name);
return true;
}
else
{
uint64_t mask;
const struct powerpc_operand *operand = &powerpc_operands[*o];
if (operand->shift == (int) PPC_OPSHIFT_INV)
{
const char *errmsg;
uint64_t val;
errmsg = NULL;
val = -1;
if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
val = -val;
else if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
val += 1;
mask = (*operand->insert) (0, val, ppc_cpu, &errmsg);
}
else if (operand->shift >= 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;
if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
optional = true;
else if (optional)
{
as_bad (_("non-optional operand %d follows optional operand in %s"),
(int) (o - op->operands), op->name);
return true;
}
}
}
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;
bool bad_insn = false;
if (ppc_hash != NULL)
htab_delete (ppc_hash);
if (ppc_macro_hash != NULL)
htab_delete (ppc_macro_hash);
/* Insert the opcodes into a hash table. */
ppc_hash = str_htab_create ();
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)
{
uint64_t mask = powerpc_operands[i].bitm;
uint64_t 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)
{
unsigned int new_opcode = PPC_OP (op[0].opcode);
#ifdef PRINT_OPCODE_TABLE
printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
op->name, (unsigned int) (op - powerpc_opcodes),
new_opcode, (unsigned long long) op->opcode,
(unsigned long long) op->mask, (unsigned long long) op->flags);
#endif
/* The major opcodes had better be sorted. Code in the disassembler
assumes the insns are sorted according to major opcode. */
if (op != powerpc_opcodes
&& new_opcode < PPC_OP (op[-1].opcode))
{
as_bad (_("major opcode is not sorted for %s"), op->name);
bad_insn = true;
}
if ((op->flags & PPC_OPCODE_VLE) != 0)
{
as_bad (_("%s is enabled by vle flag"), op->name);
bad_insn = true;
}
if (PPC_OP (op->opcode) != 4
&& PPC_OP (op->opcode) != 31
&& (op->deprecated & PPC_OPCODE_VLE) == 0)
{
as_bad (_("%s not disabled by vle flag"), op->name);
bad_insn = true;
}
bad_insn |= insn_validate (op);
}
if ((ppc_cpu & op->flags) != 0
&& !(ppc_cpu & op->deprecated)
&& str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
{
as_bad (_("duplicate %s"), op->name);
bad_insn = true;
}
}
if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
for (op = powerpc_opcodes; op < op_end; op++)
str_hash_insert (ppc_hash, op->name, op, 0);
op_end = prefix_opcodes + prefix_num_opcodes;
for (op = prefix_opcodes; op < op_end; op++)
{
if (ENABLE_CHECKING)
{
unsigned int new_opcode = PPC_PREFIX_SEG (op[0].opcode);
#ifdef PRINT_OPCODE_TABLE
printf ("%-14s\t#%04u\tmajor op/2: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
op->name, (unsigned int) (op - prefix_opcodes),
new_opcode, (unsigned long long) op->opcode,
(unsigned long long) op->mask, (unsigned long long) op->flags);
#endif
/* The major opcodes had better be sorted. Code in the disassembler
assumes the insns are sorted according to major opcode. */
if (op != prefix_opcodes
&& new_opcode < PPC_PREFIX_SEG (op[-1].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)
&& str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
{
as_bad (_("duplicate %s"), op->name);
bad_insn = true;
}
}
if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
for (op = prefix_opcodes; op < op_end; op++)
str_hash_insert (ppc_hash, op->name, op, 0);
op_end = vle_opcodes + vle_num_opcodes;
for (op = vle_opcodes; op < op_end; op++)
{
if (ENABLE_CHECKING)
{
unsigned new_seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
#ifdef PRINT_OPCODE_TABLE
printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
op->name, (unsigned int) (op - vle_opcodes),
(unsigned int) new_seg, (unsigned long long) op->opcode,
(unsigned long long) op->mask, (unsigned long long) op->flags);
#endif
/* The major opcodes had better be sorted. Code in the disassembler
assumes the insns are sorted according to major opcode. */
if (op != vle_opcodes
&& new_seg < VLE_OP_TO_SEG (VLE_OP (op[-1].opcode, op[-1].mask)))
{
as_bad (_("major opcode is not sorted for %s"), op->name);
bad_insn = true;
}
bad_insn |= insn_validate (op);
}
if ((ppc_cpu & op->flags) != 0
&& !(ppc_cpu & op->deprecated)
&& str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
{
as_bad (_("duplicate %s"), op->name);
bad_insn = true;
}
}
/* SPE2 instructions */
if ((ppc_cpu & PPC_OPCODE_SPE2) == PPC_OPCODE_SPE2)
{
op_end = spe2_opcodes + spe2_num_opcodes;
for (op = spe2_opcodes; op < op_end; op++)
{
if (ENABLE_CHECKING)
{
if (op != spe2_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);
/* 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)
&& str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
{
as_bad (_("duplicate %s"), op->name);
bad_insn = true;
}
}
for (op = spe2_opcodes; op < op_end; op++)
str_hash_insert (ppc_hash, op->name, op, 0);
}
/* Insert the macros into a hash table. */
ppc_macro_hash = str_htab_create ();
macro_end = powerpc_macros + powerpc_num_macros;
for (macro = powerpc_macros; macro < macro_end; macro++)
if (((macro->flags & ppc_cpu) != 0
|| (ppc_cpu & PPC_OPCODE_ANY) != 0)
&& str_hash_insert (ppc_macro_hash, macro->name, macro, 0) != NULL)
{
as_bad (_("duplicate %s"), macro->name);
bad_insn = true;
}
if (bad_insn)
abort ();
}
/* 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 XCOFF sections with .text in first, as it's creating dummy symbols
to serve as initial csects. This forces the text csects to precede the
data csects. These symbols will not be output. */
ppc_init_xcoff_section (&ppc_xcoff_text_section, text_section);
ppc_init_xcoff_section (&ppc_xcoff_data_section, data_section);
ppc_init_xcoff_section (&ppc_xcoff_bss_section, bss_section);
#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 (APUINFO_SECTION_NAME, 0);
bfd_set_section_flags (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_LABEL);
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 uint64_t
ppc_insert_operand (uint64_t insn,
const struct powerpc_operand *operand,
int64_t val,
ppc_cpu_t cpu,
const char *file,
unsigned int line)
{
int64_t min, max, right;
max = operand->bitm;
right = max & -max;
min = 0;
if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0)
{
/* Extend the allowed range for addis to [-32768, 65535].
Similarly for cmpli and some VLE high part insns. For 64-bit
it would be good to disable this for signed fields since the
value is sign extended into the high 32 bits of the register.
If the value is, say, an address, then we might care about
the high bits. However, gcc as of 2014-06 uses unsigned
values when loading the high part of 64-bit constants using
lis. */
min = ~(max >> 1) & -right;
}
else if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
{
max = (max >> 1) & -right;
min = ~max & -right;
}
if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
max++;
if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
{
int64_t 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. We only do this for operands that are 32-bits or
smaller. */
if (val > max
&& (operand->bitm & ~0xffffffffULL) == 0
&& (val - (1LL << 32)) >= min
&& (val - (1LL << 32)) <= max
&& ((val - (1LL << 32)) & (right - 1)) == 0)
val = val - (1LL << 32);
/* Similarly, people write expressions like ~(1<<15), and expect
this to be OK for a 32-bit unsigned value. */
else if (val < min
&& (operand->bitm & ~0xffffffffULL) == 0
&& (val + (1LL << 32)) >= min
&& (val + (1LL << 32)) <= max
&& ((val + (1LL << 32)) & (right - 1)) == 0)
val = val + (1LL << 32);
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, val, cpu, &errmsg);
if (errmsg != (const char *) NULL)
as_bad_where (file, line, "%s", errmsg);
}
else if (operand->shift >= 0)
insn |= (val & operand->bitm) << operand->shift;
else
insn |= (val & operand->bitm) >> -operand->shift;
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 {
const 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 ("high", BFD_RELOC_PPC64_ADDR16_HIGH),
MAP64 ("higha", BFD_RELOC_PPC64_ADDR16_HIGHA),
MAP64 ("higher", BFD_RELOC_PPC64_HIGHER),
MAP64 ("highera", BFD_RELOC_PPC64_HIGHER_S),
MAP64 ("highest", BFD_RELOC_PPC64_HIGHEST),
MAP64 ("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@high", BFD_RELOC_PPC64_DTPREL16_HIGH),
MAP64 ("dtprel@higha", BFD_RELOC_PPC64_DTPREL16_HIGHA),
MAP64 ("dtprel@higher", BFD_RELOC_PPC64_DTPREL16_HIGHER),
MAP64 ("dtprel@highera", BFD_RELOC_PPC64_DTPREL16_HIGHERA),
MAP64 ("dtprel@highest", BFD_RELOC_PPC64_DTPREL16_HIGHEST),
MAP64 ("dtprel@highesta", BFD_RELOC_PPC64_DTPREL16_HIGHESTA),
MAP64 ("localentry", BFD_RELOC_PPC64_ADDR64_LOCAL),
MAP64 ("tprel@high", BFD_RELOC_PPC64_TPREL16_HIGH),
MAP64 ("tprel@higha", BFD_RELOC_PPC64_TPREL16_HIGHA),
MAP64 ("tprel@higher", BFD_RELOC_PPC64_TPREL16_HIGHER),
MAP64 ("tprel@highera", BFD_RELOC_PPC64_TPREL16_HIGHERA),
MAP64 ("tprel@highest", BFD_RELOC_PPC64_TPREL16_HIGHEST),
MAP64 ("tprel@highesta", BFD_RELOC_PPC64_TPREL16_HIGHESTA),
MAP64 ("notoc", BFD_RELOC_PPC64_REL24_NOTOC),
MAP64 ("pcrel", BFD_RELOC_PPC64_PCREL34),
MAP64 ("got@pcrel", BFD_RELOC_PPC64_GOT_PCREL34),
MAP64 ("plt@pcrel", BFD_RELOC_PPC64_PLT_PCREL34),
MAP64 ("tls@pcrel", BFD_RELOC_PPC64_TLS_PCREL),
MAP64 ("got@tlsgd@pcrel", BFD_RELOC_PPC64_GOT_TLSGD_PCREL34),
MAP64 ("got@tlsld@pcrel", BFD_RELOC_PPC64_GOT_TLSLD_PCREL34),
MAP64 ("got@tprel@pcrel", BFD_RELOC_PPC64_GOT_TPREL_PCREL34),
MAP64 ("got@dtprel@pcrel", BFD_RELOC_PPC64_GOT_DTPREL_PCREL34),
MAP64 ("higher34", BFD_RELOC_PPC64_ADDR16_HIGHER34),
MAP64 ("highera34", BFD_RELOC_PPC64_ADDR16_HIGHERA34),
MAP64 ("highest34", BFD_RELOC_PPC64_ADDR16_HIGHEST34),
MAP64 ("highesta34", BFD_RELOC_PPC64_ADDR16_HIGHESTA34),
{ (char *) 0, 0, 0, 0, BFD_RELOC_NONE }
};
if (*str++ != '@')
return BFD_RELOC_NONE;
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_op == O_big || 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 (_("symbol+offset@%s means symbol@%s+offset"),
ptr->string, ptr->string);
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@%s not supported"), ptr->string);
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_op != O_big)
{
exp_p->X_add_number += new_exp.X_add_number;
str = input_line_pointer;
}
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_NONE;
}
/* Support @got, etc. on constants emitted via .short, .int etc. */
bfd_reloc_code_real_type
ppc_elf_parse_cons (expressionS *exp, unsigned int nbytes)
{
expression (exp);
if (nbytes >= 2 && *input_line_pointer == '@')
return ppc_elf_suffix (&input_line_pointer, exp);
return BFD_RELOC_NONE;
}
/* Warn when emitting data to code sections, unless we are emitting
a relocation that ld --ppc476-workaround uses to recognise data
*and* there was an unconditional branch prior to the data. */
void
ppc_elf_cons_fix_check (expressionS *exp ATTRIBUTE_UNUSED,
unsigned int nbytes, fixS *fix)
{
if (warn_476
&& (now_seg->flags & SEC_CODE) != 0
&& (nbytes != 4
|| fix == NULL
|| !(fix->fx_r_type == BFD_RELOC_32
|| fix->fx_r_type == BFD_RELOC_CTOR
|| fix->fx_r_type == BFD_RELOC_32_PCREL)
|| !(last_seg == now_seg && last_subseg == now_subseg)
|| !((last_insn & (0x3f << 26)) == (18u << 26)
|| ((last_insn & (0x3f << 26)) == (16u << 26)
&& (last_insn & (0x14 << 21)) == (0x14 << 21))
|| ((last_insn & (0x3f << 26)) == (19u << 26)
&& (last_insn & (0x3ff << 1)) == (16u << 1)
&& (last_insn & (0x14 << 21)) == (0x14 << 21)))))
{
/* Flag that we've warned. */
if (fix != NULL)
fix->fx_tcbit = 1;
as_warn (_("data in executable section"));
}
}
/* 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;
c = get_symbol_name (&name);
/* Just after name is now '\0'. */
p = input_line_pointer;
*p = c;
SKIP_WHITESPACE_AFTER_NAME ();
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 ();
}
/* Pseudo op to set symbol local entry point. */
static void
ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
{
char *name;
char c = get_symbol_name (&name);
char *p;
expressionS exp;
symbolS *sym;
asymbol *bfdsym;
elf_symbol_type *elfsym;
p = input_line_pointer;
*p = c;
SKIP_WHITESPACE_AFTER_NAME ();
if (*input_line_pointer != ',')
{
*p = 0;
as_bad (_("expected comma after name `%s' in .localentry directive"),
name);
*p = c;
ignore_rest_of_line ();
return;
}
input_line_pointer++;
expression (&exp);
if (exp.X_op == O_absent)
{
as_bad (_("missing expression in .localentry directive"));
exp.X_op = O_constant;
exp.X_add_number = 0;
}
*p = 0;
sym = symbol_find_or_make (name);
*p = c;
if (resolve_expression (&exp)
&& exp.X_op == O_constant)
{
unsigned int encoded, ok;
ok = 1;
if (exp.X_add_number == 1 || exp.X_add_number == 7)
encoded = exp.X_add_number << STO_PPC64_LOCAL_BIT;
else
{
encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number);
if (exp.X_add_number != (offsetT) PPC64_LOCAL_ENTRY_OFFSET (encoded))
{
as_bad (_(".localentry expression for `%s' "
"is not a valid power of 2"), S_GET_NAME (sym));
ok = 0;
}
}
if (ok)
{
bfdsym = symbol_get_bfdsym (sym);
elfsym = elf_symbol_from (bfdsym);
gas_assert (elfsym);
elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK;
elfsym->internal_elf_sym.st_other |= encoded;
if (ppc_abiversion == 0)
ppc_abiversion = 2;
}
}
else
as_bad (_(".localentry expression for `%s' "
"does not evaluate to a constant"), S_GET_NAME (sym));
demand_empty_rest_of_line ();
}
/* Pseudo op to set ABI version. */
static void
ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
{
expressionS exp;
expression (&exp);
if (exp.X_op == O_absent)
{
as_bad (_("missing expression in .abiversion directive"));
exp.X_op = O_constant;
exp.X_add_number = 0;
}
if (resolve_expression (&exp)
&& exp.X_op == O_constant)
ppc_abiversion = exp.X_add_number;
else
as_bad (_(".abiversion expression does not evaluate to a constant"));
demand_empty_rest_of_line ();
}
/* Parse a .gnu_attribute directive. */
static void
ppc_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
{
int tag = obj_elf_vendor_attribute (OBJ_ATTR_GNU);
/* Check validity of defined powerpc tags. */
if (tag == Tag_GNU_Power_ABI_FP
|| tag == Tag_GNU_Power_ABI_Vector
|| tag == Tag_GNU_Power_ABI_Struct_Return)
{
unsigned int val;
val = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, tag);
if ((tag == Tag_GNU_Power_ABI_FP && val > 15)
|| (tag == Tag_GNU_Power_ABI_Vector && val > 3)
|| (tag == Tag_GNU_Power_ABI_Struct_Return && val > 2))
as_warn (_("unknown .gnu_attribute value"));
}
}
/* Set ABI version in output file. */
void
ppc_elf_end (void)
{
if (ppc_obj64 && ppc_abiversion != 0)
{
elf_elfheader (stdoutput)->e_flags &= ~EF_PPC64_ABI;
elf_elfheader (stdoutput)->e_flags |= ppc_abiversion & EF_PPC64_ABI;
}
/* Any selection of opcodes based on ppc_cpu after gas has finished
parsing the file is invalid. md_apply_fix and ppc_handle_align
must select opcodes based on the machine in force at the point
where the fixup or alignment frag was created, not the machine in
force at the end of file. */
ppc_cpu = 0;
}
/* 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_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;
name = S_GET_NAME (symp);
if (name[0] == '.')
continue;
if (! S_IS_WEAK (symp)
|| S_IS_DEFINED (symp))
continue;
dotname = concat (".", name, (char *) NULL);
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 (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 OBJ_XCOFF
/* Parse XCOFF relocations. */
static bfd_reloc_code_real_type
ppc_xcoff_suffix (char **str_p)
{
struct map_bfd {
const 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_PPC_TOC16_LO),
MAP ("u", BFD_RELOC_PPC_TOC16_HI),
MAP32 ("ie", BFD_RELOC_PPC_TLSIE),
MAP32 ("ld", BFD_RELOC_PPC_TLSLD),
MAP32 ("le", BFD_RELOC_PPC_TLSLE),
MAP32 ("m", BFD_RELOC_PPC_TLSM),
MAP32 ("ml", BFD_RELOC_PPC_TLSML),
MAP64 ("ie", BFD_RELOC_PPC64_TLSIE),
MAP64 ("ld", BFD_RELOC_PPC64_TLSLD),
MAP64 ("le", BFD_RELOC_PPC64_TLSLE),
MAP64 ("m", BFD_RELOC_PPC64_TLSM),
MAP64 ("ml", BFD_RELOC_PPC64_TLSML),
};
if (*str++ != '@')
return BFD_RELOC_NONE;
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))
{
*str_p = str;
return (bfd_reloc_code_real_type) ptr->reloc;
}
return BFD_RELOC_NONE;
}
/* Restore XCOFF addis instruction to ELF format.
AIX often generates addis instructions using "addis RT,D(RA)"
format instead of the ELF "addis RT,RA,SI" one.
On entry RT_E is at the comma after RT, D_E is at the open
parenthesis after D, and RA_E is at the close parenthesis after RA. */
static void
ppc_xcoff_fixup_addis (char *rt_e, char *d_e, char *ra_e)
{
size_t ra_size = ra_e - d_e - 1;
char *save_ra = xmalloc (ra_size);
/* Copy RA. */
memcpy (save_ra, d_e + 1, ra_size);
/* Shuffle D to make room for RA, copying the comma too. */
memmove (rt_e + ra_size + 1, rt_e, d_e - rt_e);
/* Erase the trailing ')', keeping any rubbish for potential errors. */
memmove (ra_e, ra_e + 1, strlen (ra_e));
/* Write RA back. */
memcpy (rt_e + 1, save_ra, ra_size);
free (save_ra);
}
/* Support @ie, etc. on constants emitted via .short, .int etc. */
bfd_reloc_code_real_type
ppc_xcoff_parse_cons (expressionS *exp, unsigned int nbytes)
{
expression (exp);
if (nbytes >= 2 && *input_line_pointer == '@')
return ppc_xcoff_suffix (&input_line_pointer);
/* There isn't any @ symbol for default TLS relocations (R_TLS). */
if (exp->X_add_symbol != NULL
&& (symbol_get_tc (exp->X_add_symbol)->symbol_class == XMC_TL
|| symbol_get_tc (exp->X_add_symbol)->symbol_class == XMC_UL))
return (ppc_obj64 ? BFD_RELOC_PPC64_TLSGD: BFD_RELOC_PPC_TLSGD);
return BFD_RELOC_NONE;
}
#endif /* OBJ_XCOFF */
#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_TE
|| 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 = XNEWVEC (unsigned long, ppc_apuinfo_num_alloc);
}
else
{
ppc_apuinfo_num_alloc += 4;
ppc_apuinfo_list = XRESIZEVEC (unsigned long, ppc_apuinfo_list,
ppc_apuinfo_num_alloc);
}
}
ppc_apuinfo_list[ppc_apuinfo_num++] = APUID (apu, version);
}
#undef APUID
#endif
/* Various frobbings of labels and their addresses. */
/* Symbols labelling the current insn. */
struct insn_label_list
{
struct insn_label_list *next;
symbolS *label;
};
static struct insn_label_list *insn_labels;
static struct insn_label_list *free_insn_labels;
static void
ppc_record_label (symbolS *sym)
{
struct insn_label_list *l;
if (free_insn_labels == NULL)
l = XNEW (struct insn_label_list);
else
{
l = free_insn_labels;
free_insn_labels = l->next;
}
l->label = sym;
l->next = insn_labels;
insn_labels = l;
}