| /* 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 ®s[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; |
| } |
| |
|