| /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000) |
| Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 |
| 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 2, 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, 59 Temple Place - Suite 330, Boston, MA |
| 02111-1307, USA. */ |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include "as.h" |
| #include "subsegs.h" |
| |
| #include "opcode/ppc.h" |
| |
| #ifdef OBJ_ELF |
| #include "elf/ppc.h" |
| #include "dwarf2dbg.h" |
| #endif |
| |
| #ifdef TE_PE |
| #include "coff/pe.h" |
| #endif |
| |
| /* This is the assembler for the PowerPC or POWER (RS/6000) chips. */ |
| |
| /* Tell the main code what the endianness is. */ |
| extern int target_big_endian; |
| |
| /* Whether or not, we've set target_big_endian. */ |
| static int set_target_endian = 0; |
| |
| /* Whether to use user friendly register names. */ |
| #ifndef TARGET_REG_NAMES_P |
| #ifdef TE_PE |
| #define TARGET_REG_NAMES_P true |
| #else |
| #define TARGET_REG_NAMES_P false |
| #endif |
| #endif |
| |
| static boolean reg_names_p = TARGET_REG_NAMES_P; |
| |
| static boolean register_name PARAMS ((expressionS *)); |
| static void ppc_set_cpu PARAMS ((void)); |
| static unsigned long ppc_insert_operand |
| PARAMS ((unsigned long insn, const struct powerpc_operand *operand, |
| offsetT val, char *file, unsigned int line)); |
| static void ppc_macro PARAMS ((char *str, const struct powerpc_macro *macro)); |
| static void ppc_byte PARAMS ((int)); |
| static int ppc_is_toc_sym PARAMS ((symbolS *sym)); |
| static void ppc_tc PARAMS ((int)); |
| |
| #ifdef OBJ_XCOFF |
| static void ppc_comm PARAMS ((int)); |
| static void ppc_bb PARAMS ((int)); |
| static void ppc_bc PARAMS ((int)); |
| static void ppc_bf PARAMS ((int)); |
| static void ppc_biei PARAMS ((int)); |
| static void ppc_bs PARAMS ((int)); |
| static void ppc_eb PARAMS ((int)); |
| static void ppc_ec PARAMS ((int)); |
| static void ppc_ef PARAMS ((int)); |
| static void ppc_es PARAMS ((int)); |
| static void ppc_csect PARAMS ((int)); |
| static void ppc_change_csect PARAMS ((symbolS *)); |
| static void ppc_function PARAMS ((int)); |
| static void ppc_extern PARAMS ((int)); |
| static void ppc_lglobl PARAMS ((int)); |
| static void ppc_section PARAMS ((int)); |
| static void ppc_named_section PARAMS ((int)); |
| static void ppc_stabx PARAMS ((int)); |
| static void ppc_rename PARAMS ((int)); |
| static void ppc_toc PARAMS ((int)); |
| static void ppc_xcoff_cons PARAMS ((int)); |
| static void ppc_machine PARAMS ((int)); |
| static void ppc_vbyte PARAMS ((int)); |
| #endif |
| |
| #ifdef OBJ_ELF |
| static bfd_reloc_code_real_type ppc_elf_suffix PARAMS ((char **, expressionS *)); |
| static void ppc_elf_cons PARAMS ((int)); |
| static void ppc_elf_rdata PARAMS ((int)); |
| static void ppc_elf_lcomm PARAMS ((int)); |
| static void ppc_elf_validate_fix PARAMS ((fixS *, segT)); |
| #endif |
| |
| #ifdef TE_PE |
| static void ppc_set_current_section PARAMS ((segT)); |
| static void ppc_previous PARAMS ((int)); |
| static void ppc_pdata PARAMS ((int)); |
| static void ppc_ydata PARAMS ((int)); |
| static void ppc_reldata PARAMS ((int)); |
| static void ppc_rdata PARAMS ((int)); |
| static void ppc_ualong PARAMS ((int)); |
| static void ppc_znop PARAMS ((int)); |
| static void ppc_pe_comm PARAMS ((int)); |
| static void ppc_pe_section PARAMS ((int)); |
| static void ppc_pe_function PARAMS ((int)); |
| static void ppc_pe_tocd PARAMS ((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"; |
| |
| /* 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 }, |
| { "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 }, |
| { "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 }, |
| { "machine", ppc_machine, 0 }, |
| #endif |
| |
| #ifdef OBJ_ELF |
| { "long", ppc_elf_cons, 4 }, |
| { "word", ppc_elf_cons, 2 }, |
| { "short", ppc_elf_cons, 2 }, |
| { "rdata", ppc_elf_rdata, 0 }, |
| { "rodata", ppc_elf_rdata, 0 }, |
| { "lcomm", ppc_elf_lcomm, 0 }, |
| { "file", dwarf2_directive_file, 0 }, |
| { "loc", dwarf2_directive_loc, 0 }, |
| #endif |
| |
| #ifdef TE_PE |
| /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format */ |
| { "previous", ppc_previous, 0 }, |
| { "pdata", ppc_pdata, 0 }, |
| { "ydata", ppc_ydata, 0 }, |
| { "reldata", ppc_reldata, 0 }, |
| { "rdata", ppc_rdata, 0 }, |
| { "ualong", ppc_ualong, 0 }, |
| { "znop", ppc_znop, 0 }, |
| { "comm", ppc_pe_comm, 0 }, |
| { "lcomm", ppc_pe_comm, 1 }, |
| { "section", ppc_pe_section, 0 }, |
| { "function", ppc_pe_function,0 }, |
| { "tocd", ppc_pe_tocd, 0 }, |
| #endif |
| |
| /* This pseudo-op is used even when not generating XCOFF output. */ |
| { "tc", ppc_tc, 0 }, |
| |
| { NULL, NULL, 0 } |
| }; |
| |
| |
| /* Predefined register names if -mregnames (or default for Windows NT). */ |
| /* In general, there are lots of them, in an attempt to be compatible */ |
| /* with a number of other Windows NT assemblers. */ |
| |
| /* Structure to hold information about predefined registers. */ |
| struct pd_reg |
| { |
| char *name; |
| int value; |
| }; |
| |
| /* List of registers that are pre-defined: |
| |
| Each general register has predefined names of the form: |
| 1. r<reg_num> which has the value <reg_num>. |
| 2. r.<reg_num> which has the value <reg_num>. |
| |
| Each floating point register has predefined names of the form: |
| 1. f<reg_num> which has the value <reg_num>. |
| 2. f.<reg_num> which has the value <reg_num>. |
| |
| Each vector unit register has predefined names of the form: |
| 1. v<reg_num> which has the value <reg_num>. |
| 2. v.<reg_num> which has the value <reg_num>. |
| |
| Each condition register has predefined names of the form: |
| 1. cr<reg_num> which has the value <reg_num>. |
| 2. cr.<reg_num> which has the value <reg_num>. |
| |
| There are individual registers as well: |
| sp or r.sp has the value 1 |
| rtoc or r.toc has the value 2 |
| fpscr has the value 0 |
| xer has the value 1 |
| lr has the value 8 |
| ctr has the value 9 |
| pmr has the value 0 |
| dar has the value 19 |
| dsisr has the value 18 |
| dec has the value 22 |
| sdr1 has the value 25 |
| srr0 has the value 26 |
| srr1 has the value 27 |
| |
| The table is sorted. Suitable for searching by a binary search. */ |
| |
| static const struct pd_reg pre_defined_registers[] = |
| { |
| { "cr.0", 0 }, /* Condition Registers */ |
| { "cr.1", 1 }, |
| { "cr.2", 2 }, |
| { "cr.3", 3 }, |
| { "cr.4", 4 }, |
| { "cr.5", 5 }, |
| { "cr.6", 6 }, |
| { "cr.7", 7 }, |
| |
| { "cr0", 0 }, |
| { "cr1", 1 }, |
| { "cr2", 2 }, |
| { "cr3", 3 }, |
| { "cr4", 4 }, |
| { "cr5", 5 }, |
| { "cr6", 6 }, |
| { "cr7", 7 }, |
| |
| { "ctr", 9 }, |
| |
| { "dar", 19 }, /* Data Access Register */ |
| { "dec", 22 }, /* Decrementer */ |
| { "dsisr", 18 }, /* Data Storage Interrupt Status Register */ |
| |
| { "f.0", 0 }, /* Floating point registers */ |
| { "f.1", 1 }, |
| { "f.10", 10 }, |
| { "f.11", 11 }, |
| { "f.12", 12 }, |
| { "f.13", 13 }, |
| { "f.14", 14 }, |
| { "f.15", 15 }, |
| { "f.16", 16 }, |
| { "f.17", 17 }, |
| { "f.18", 18 }, |
| { "f.19", 19 }, |
| { "f.2", 2 }, |
| { "f.20", 20 }, |
| { "f.21", 21 }, |
| { "f.22", 22 }, |
| { "f.23", 23 }, |
| { "f.24", 24 }, |
| { "f.25", 25 }, |
| { "f.26", 26 }, |
| { "f.27", 27 }, |
| { "f.28", 28 }, |
| { "f.29", 29 }, |
| { "f.3", 3 }, |
| { "f.30", 30 }, |
| { "f.31", 31 }, |
| { "f.4", 4 }, |
| { "f.5", 5 }, |
| { "f.6", 6 }, |
| { "f.7", 7 }, |
| { "f.8", 8 }, |
| { "f.9", 9 }, |
| |
| { "f0", 0 }, |
| { "f1", 1 }, |
| { "f10", 10 }, |
| { "f11", 11 }, |
| { "f12", 12 }, |
| { "f13", 13 }, |
| { "f14", 14 }, |
| { "f15", 15 }, |
| { "f16", 16 }, |
| { "f17", 17 }, |
| { "f18", 18 }, |
| { "f19", 19 }, |
| { "f2", 2 }, |
| { "f20", 20 }, |
| { "f21", 21 }, |
| { "f22", 22 }, |
| { "f23", 23 }, |
| { "f24", 24 }, |
| { "f25", 25 }, |
| { "f26", 26 }, |
| { "f27", 27 }, |
| { "f28", 28 }, |
| { "f29", 29 }, |
| { "f3", 3 }, |
| { "f30", 30 }, |
| { "f31", 31 }, |
| { "f4", 4 }, |
| { "f5", 5 }, |
| { "f6", 6 }, |
| { "f7", 7 }, |
| { "f8", 8 }, |
| { "f9", 9 }, |
| |
| { "fpscr", 0 }, |
| |
| { "lr", 8 }, /* Link Register */ |
| |
| { "pmr", 0 }, |
| |
| { "r.0", 0 }, /* General Purpose Registers */ |
| { "r.1", 1 }, |
| { "r.10", 10 }, |
| { "r.11", 11 }, |
| { "r.12", 12 }, |
| { "r.13", 13 }, |
| { "r.14", 14 }, |
| { "r.15", 15 }, |
| { "r.16", 16 }, |
| { "r.17", 17 }, |
| { "r.18", 18 }, |
| { "r.19", 19 }, |
| { "r.2", 2 }, |
| { "r.20", 20 }, |
| { "r.21", 21 }, |
| { "r.22", 22 }, |
| { "r.23", 23 }, |
| { "r.24", 24 }, |
| { "r.25", 25 }, |
| { "r.26", 26 }, |
| { "r.27", 27 }, |
| { "r.28", 28 }, |
| { "r.29", 29 }, |
| { "r.3", 3 }, |
| { "r.30", 30 }, |
| { "r.31", 31 }, |
| { "r.4", 4 }, |
| { "r.5", 5 }, |
| { "r.6", 6 }, |
| { "r.7", 7 }, |
| { "r.8", 8 }, |
| { "r.9", 9 }, |
| |
| { "r.sp", 1 }, /* Stack Pointer */ |
| |
| { "r.toc", 2 }, /* Pointer to the table of contents */ |
| |
| { "r0", 0 }, /* More general purpose registers */ |
| { "r1", 1 }, |
| { "r10", 10 }, |
| { "r11", 11 }, |
| { "r12", 12 }, |
| { "r13", 13 }, |
| { "r14", 14 }, |
| { "r15", 15 }, |
| { "r16", 16 }, |
| { "r17", 17 }, |
| { "r18", 18 }, |
| { "r19", 19 }, |
| { "r2", 2 }, |
| { "r20", 20 }, |
| { "r21", 21 }, |
| { "r22", 22 }, |
| { "r23", 23 }, |
| { "r24", 24 }, |
| { "r25", 25 }, |
| { "r26", 26 }, |
| { "r27", 27 }, |
| { "r28", 28 }, |
| { "r29", 29 }, |
| { "r3", 3 }, |
| { "r30", 30 }, |
| { "r31", 31 }, |
| { "r4", 4 }, |
| { "r5", 5 }, |
| { "r6", 6 }, |
| { "r7", 7 }, |
| { "r8", 8 }, |
| { "r9", 9 }, |
| |
| { "rtoc", 2 }, /* Table of contents */ |
| |
| { "sdr1", 25 }, /* Storage Description Register 1 */ |
| |
| { "sp", 1 }, |
| |
| { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */ |
| { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */ |
| |
| { "v.0", 0 }, /* Vector registers */ |
| { "v.1", 1 }, |
| { "v.10", 10 }, |
| { "v.11", 11 }, |
| { "v.12", 12 }, |
| { "v.13", 13 }, |
| { "v.14", 14 }, |
| { "v.15", 15 }, |
| { "v.16", 16 }, |
| { "v.17", 17 }, |
| { "v.18", 18 }, |
| { "v.19", 19 }, |
| { "v.2", 2 }, |
| { "v.20", 20 }, |
| { "v.21", 21 }, |
| { "v.22", 22 }, |
| { "v.23", 23 }, |
| { "v.24", 24 }, |
| { "v.25", 25 }, |
| { "v.26", 26 }, |
| { "v.27", 27 }, |
| { "v.28", 28 }, |
| { "v.29", 29 }, |
| { "v.3", 3 }, |
| { "v.30", 30 }, |
| { "v.31", 31 }, |
| { "v.4", 4 }, |
| { "v.5", 5 }, |
| { "v.6", 6 }, |
| { "v.7", 7 }, |
| { "v.8", 8 }, |
| { "v.9", 9 }, |
| |
| { "v0", 0 }, |
| { "v1", 1 }, |
| { "v10", 10 }, |
| { "v11", 11 }, |
| { "v12", 12 }, |
| { "v13", 13 }, |
| { "v14", 14 }, |
| { "v15", 15 }, |
| { "v16", 16 }, |
| { "v17", 17 }, |
| { "v18", 18 }, |
| { "v19", 19 }, |
| { "v2", 2 }, |
| { "v20", 20 }, |
| { "v21", 21 }, |
| { "v22", 22 }, |
| { "v23", 23 }, |
| { "v24", 24 }, |
| { "v25", 25 }, |
| { "v26", 26 }, |
| { "v27", 27 }, |
| { "v28", 28 }, |
| { "v29", 29 }, |
| { "v3", 3 }, |
| { "v30", 30 }, |
| { "v31", 31 }, |
| { "v4", 4 }, |
| { "v5", 5 }, |
| { "v6", 6 }, |
| { "v7", 7 }, |
| { "v8", 8 }, |
| { "v9", 9 }, |
| |
| { "xer", 1 }, |
| |
| }; |
| |
| #define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg)) |
| |
| /* Given NAME, find the register number associated with that name, return |
| the integer value associated with the given name or -1 on failure. */ |
| |
| static int reg_name_search |
| PARAMS ((const struct pd_reg *, int, const char * name)); |
| |
| static int |
| reg_name_search (regs, regcount, name) |
| const struct pd_reg *regs; |
| int regcount; |
| const char *name; |
| { |
| int middle, low, high; |
| int cmp; |
| |
| low = 0; |
| high = regcount - 1; |
| |
| do |
| { |
| middle = (low + high) / 2; |
| cmp = strcasecmp (name, regs[middle].name); |
| if (cmp < 0) |
| high = middle - 1; |
| else if (cmp > 0) |
| low = middle + 1; |
| else |
| return regs[middle].value; |
| } |
| while (low <= high); |
| |
| return -1; |
| } |
| |
| /* |
| * Summary of register_name(). |
| * |
| * in: Input_line_pointer points to 1st char of operand. |
| * |
| * out: A expressionS. |
| * The operand may have been a register: in this case, X_op == O_register, |
| * X_add_number is set to the register number, and truth is returned. |
| * Input_line_pointer->(next non-blank) char after operand, or is in its |
| * original state. |
| */ |
| |
| static boolean |
| register_name (expressionP) |
| expressionS *expressionP; |
| { |
| int reg_number; |
| char *name; |
| char *start; |
| char c; |
| |
| /* Find the spelling of the operand */ |
| start = name = input_line_pointer; |
| if (name[0] == '%' && isalpha (name[1])) |
| name = ++input_line_pointer; |
| |
| else if (!reg_names_p || !isalpha (name[0])) |
| return false; |
| |
| c = get_symbol_end (); |
| reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name); |
| |
| /* look to see if it's in the register table */ |
| if (reg_number >= 0) |
| { |
| expressionP->X_op = O_register; |
| expressionP->X_add_number = reg_number; |
| |
| /* make the rest nice */ |
| expressionP->X_add_symbol = NULL; |
| expressionP->X_op_symbol = NULL; |
| *input_line_pointer = c; /* put back the delimiting char */ |
| return true; |
| } |
| else |
| { |
| /* reset the line as if we had not done anything */ |
| *input_line_pointer = c; /* put back the delimiting char */ |
| input_line_pointer = start; /* reset input_line pointer */ |
| 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 boolean cr_operand; |
| |
| /* Names to recognize in a condition code. This table is sorted. */ |
| static const struct pd_reg cr_names[] = |
| { |
| { "cr0", 0 }, |
| { "cr1", 1 }, |
| { "cr2", 2 }, |
| { "cr3", 3 }, |
| { "cr4", 4 }, |
| { "cr5", 5 }, |
| { "cr6", 6 }, |
| { "cr7", 7 }, |
| { "eq", 2 }, |
| { "gt", 1 }, |
| { "lt", 0 }, |
| { "so", 3 }, |
| { "un", 3 } |
| }; |
| |
| /* Parsing function. This returns non-zero if it recognized an |
| expression. */ |
| |
| int |
| ppc_parse_name (name, expr) |
| const char *name; |
| expressionS *expr; |
| { |
| int val; |
| |
| if (! cr_operand) |
| return 0; |
| |
| val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0], |
| name); |
| if (val < 0) |
| return 0; |
| |
| expr->X_op = O_constant; |
| expr->X_add_number = val; |
| |
| return 1; |
| } |
| |
| /* Local variables. */ |
| |
| /* The type of processor we are assembling for. This is one or more |
| of the PPC_OPCODE flags defined in opcode/ppc.h. */ |
| static int ppc_cpu = 0; |
| |
| /* The size of the processor we are assembling for. This is either |
| PPC_OPCODE_32 or PPC_OPCODE_64. */ |
| static unsigned long ppc_size = PPC_OPCODE_32; |
| |
| /* Whether to target xcoff64 */ |
| static int ppc_xcoff64 = 0; |
| |
| /* Opcode hash table. */ |
| static struct hash_control *ppc_hash; |
| |
| /* Macro hash table. */ |
| static struct hash_control *ppc_macro_hash; |
| |
| #ifdef OBJ_ELF |
| /* What type of shared library support to use */ |
| static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE; |
| |
| /* Flags to set in the elf header */ |
| static flagword ppc_flags = 0; |
| |
| /* Whether this is Solaris or not. */ |
| #ifdef TARGET_SOLARIS_COMMENT |
| #define SOLARIS_P true |
| #else |
| #define SOLARIS_P false |
| #endif |
| |
| static boolean msolaris = SOLARIS_P; |
| #endif |
| |
| #ifdef OBJ_XCOFF |
| |
| /* The RS/6000 assembler uses the .csect pseudo-op to generate code |
| using a bunch of different sections. These assembler sections, |
| however, are all encompassed within the .text or .data sections of |
| the final output file. We handle this by using different |
| subsegments within these main segments. */ |
| |
| /* Next subsegment to allocate within the .text segment. */ |
| static subsegT ppc_text_subsegment = 2; |
| |
| /* Linked list of csects in the text section. */ |
| static symbolS *ppc_text_csects; |
| |
| /* Next subsegment to allocate within the .data segment. */ |
| static subsegT ppc_data_subsegment = 2; |
| |
| /* Linked list of csects in the data section. */ |
| static symbolS *ppc_data_csects; |
| |
| /* The current csect. */ |
| static symbolS *ppc_current_csect; |
| |
| /* The RS/6000 assembler uses a TOC which holds addresses of functions |
| and variables. Symbols are put in the TOC with the .tc pseudo-op. |
| A special relocation is used when accessing TOC entries. We handle |
| the TOC as a subsegment within the .data segment. We set it up if |
| we see a .toc pseudo-op, and save the csect symbol here. */ |
| static symbolS *ppc_toc_csect; |
| |
| /* The first frag in the TOC subsegment. */ |
| static fragS *ppc_toc_frag; |
| |
| /* The first frag in the first subsegment after the TOC in the .data |
| segment. NULL if there are no subsegments after the TOC. */ |
| static fragS *ppc_after_toc_frag; |
| |
| /* The current static block. */ |
| static symbolS *ppc_current_block; |
| |
| /* The COFF debugging section; set by md_begin. This is not the |
| .debug section, but is instead the secret BFD section which will |
| cause BFD to set the section number of a symbol to N_DEBUG. */ |
| static asection *ppc_coff_debug_section; |
| |
| #endif /* OBJ_XCOFF */ |
| |
| #ifdef TE_PE |
| |
| /* Various sections that we need for PE coff support. */ |
| static segT ydata_section; |
| static segT pdata_section; |
| static segT reldata_section; |
| static segT rdata_section; |
| static segT tocdata_section; |
| |
| /* The current section and the previous section. See ppc_previous. */ |
| static segT ppc_previous_section; |
| static segT ppc_current_section; |
| |
| #endif /* TE_PE */ |
| |
| #ifdef OBJ_ELF |
| symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */ |
| #endif /* OBJ_ELF */ |
| |
| #ifdef OBJ_ELF |
| CONST char *md_shortopts = "b:l:usm:K:VQ:"; |
| #else |
| CONST char *md_shortopts = "um:"; |
| #endif |
| struct option md_longopts[] = { |
| {NULL, no_argument, NULL, 0} |
| }; |
| size_t md_longopts_size = sizeof (md_longopts); |
| |
| int |
| md_parse_option (c, arg) |
| int c; |
| char *arg; |
| { |
| 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; |
| } |
| 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) |
| ppc_xcoff64 = 1; |
| else if (strcmp (arg, "32") == 0) |
| ppc_xcoff64 = 0; |
| else |
| return 0; |
| break; |
| |
| case 'm': |
| /* -mpwrx and -mpwr2 mean to assemble for the IBM POWER/2 |
| (RIOS2). */ |
| if (strcmp (arg, "pwrx") == 0 || strcmp (arg, "pwr2") == 0) |
| ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_POWER2; |
| /* -mpwr means to assemble for the IBM POWER (RIOS1). */ |
| else if (strcmp (arg, "pwr") == 0) |
| ppc_cpu = PPC_OPCODE_POWER; |
| /* -m601 means to assemble for the Motorola PowerPC 601, which includes |
| instructions that are holdovers from the Power. */ |
| else if (strcmp (arg, "601") == 0) |
| ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_601; |
| /* -mppc, -mppc32, -m603, and -m604 mean to assemble for the |
| Motorola PowerPC 603/604. */ |
| else if (strcmp (arg, "ppc") == 0 |
| || strcmp (arg, "ppc32") == 0 |
| || strcmp (arg, "403") == 0 |
| || strcmp (arg, "405") == 0 |
| || strcmp (arg, "603") == 0 |
| || strcmp (arg, "604") == 0) |
| ppc_cpu = PPC_OPCODE_PPC; |
| else if (strcmp (arg, "7400") == 0) |
| ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_ALTIVEC; |
| /* -mppc64 and -m620 mean to assemble for the 64-bit PowerPC |
| 620. */ |
| else if (strcmp (arg, "ppc64") == 0 || strcmp (arg, "620") == 0) |
| { |
| ppc_cpu = PPC_OPCODE_PPC; |
| ppc_size = PPC_OPCODE_64; |
| } |
| else if (strcmp (arg, "ppc64bridge") == 0) |
| { |
| ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_64_BRIDGE; |
| ppc_size = PPC_OPCODE_64; |
| } |
| /* -mcom means assemble for the common intersection between Power |
| and PowerPC. At present, we just allow the union, rather |
| than the intersection. */ |
| else if (strcmp (arg, "com") == 0) |
| ppc_cpu = PPC_OPCODE_COMMON; |
| /* -many means to assemble for any architecture (PWR/PWRX/PPC). */ |
| else if (strcmp (arg, "any") == 0) |
| ppc_cpu = PPC_OPCODE_ANY; |
| |
| 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 endianess */ |
| else if (strcmp (arg, "little") == 0 || strcmp (arg, "little-endian") == 0) |
| { |
| target_big_endian = 0; |
| set_target_endian = 1; |
| } |
| |
| else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0) |
| { |
| target_big_endian = 1; |
| set_target_endian = 1; |
| } |
| |
| else if (strcmp (arg, "solaris") == 0) |
| { |
| msolaris = true; |
| ppc_comment_chars = ppc_solaris_comment_chars; |
| } |
| |
| else if (strcmp (arg, "no-solaris") == 0) |
| { |
| msolaris = false; |
| ppc_comment_chars = ppc_eabi_comment_chars; |
| } |
| #endif |
| else |
| { |
| as_bad (_("invalid switch -m%s"), arg); |
| return 0; |
| } |
| break; |
| |
| #ifdef OBJ_ELF |
| /* -V: SVR4 argument to print version ID. */ |
| case 'V': |
| print_version_id (); |
| break; |
| |
| /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section |
| should be emitted or not. FIXME: Not implemented. */ |
| case 'Q': |
| break; |
| |
| /* Solaris takes -s to specify that .stabs go in a .stabs section, |
| rather than .stabs.excl, which is ignored by the linker. |
| FIXME: Not implemented. */ |
| case 's': |
| if (arg) |
| return 0; |
| |
| break; |
| #endif |
| |
| default: |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| void |
| md_show_usage (stream) |
| FILE *stream; |
| { |
| fprintf (stream, _("\ |
| PowerPC options:\n\ |
| -u ignored\n\ |
| -mpwrx, -mpwr2 generate code for IBM POWER/2 (RIOS2)\n\ |
| -mpwr generate code for IBM POWER (RIOS1)\n\ |
| -m601 generate code for Motorola PowerPC 601\n\ |
| -mppc, -mppc32, -m403, -m405, -m603, -m604\n\ |
| generate code for Motorola PowerPC 603/604\n\ |
| -mppc64, -m620 generate code for Motorola PowerPC 620\n\ |
| -mppc64bridge generate code for PowerPC 64, including bridge insns\n\ |
| -mcom generate code Power/PowerPC common instructions\n\ |
| -many generate code for any architecture (PWR/PWRX/PPC)\n\ |
| -mregnames Allow symbolic names for registers\n\ |
| -mno-regnames Do not allow symbolic names for registers\n")); |
| #ifdef OBJ_ELF |
| fprintf (stream, _("\ |
| -mrelocatable support for GCC's -mrelocatble option\n\ |
| -mrelocatable-lib support for GCC's -mrelocatble-lib option\n\ |
| -memb set PPC_EMB bit in ELF flags\n\ |
| -mlittle, -mlittle-endian\n\ |
| generate code for a little endian machine\n\ |
| -mbig, -mbig-endian generate code for a big endian machine\n\ |
| -msolaris generate code for Solaris\n\ |
| -mno-solaris do not generate code for Solaris\n\ |
| -V print assembler version number\n\ |
| -Qy, -Qn ignored\n")); |
| #endif |
| } |
| |
| /* Set ppc_cpu if it is not already set. */ |
| |
| static void |
| ppc_set_cpu () |
| { |
| const char *default_os = TARGET_OS; |
| const char *default_cpu = TARGET_CPU; |
| |
| if (ppc_cpu == 0) |
| { |
| if (strncmp (default_os, "aix", 3) == 0 |
| && default_os[3] >= '4' && default_os[3] <= '9') |
| ppc_cpu = PPC_OPCODE_COMMON; |
| else if (strncmp (default_os, "aix3", 4) == 0) |
| ppc_cpu = PPC_OPCODE_POWER; |
| else if (strcmp (default_cpu, "rs6000") == 0) |
| ppc_cpu = PPC_OPCODE_POWER; |
| else if (strcmp (default_cpu, "powerpc") == 0 |
| || strcmp (default_cpu, "powerpcle") == 0) |
| ppc_cpu = PPC_OPCODE_PPC; |
| else |
| as_fatal (_("Unknown default cpu = %s, os = %s"), default_cpu, default_os); |
| } |
| } |
| |
| /* Figure out the BFD architecture to use. */ |
| |
| enum bfd_architecture |
| ppc_arch () |
| { |
| const char *default_cpu = TARGET_CPU; |
| ppc_set_cpu (); |
| |
| if ((ppc_cpu & PPC_OPCODE_PPC) != 0) |
| return bfd_arch_powerpc; |
| else if ((ppc_cpu & PPC_OPCODE_POWER) != 0) |
| return bfd_arch_rs6000; |
| else if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0) |
| { |
| if (strcmp (default_cpu, "rs6000") == 0) |
| return bfd_arch_rs6000; |
| else if (strcmp (default_cpu, "powerpc") == 0 |
| || strcmp (default_cpu, "powerpcle") == 0) |
| return bfd_arch_powerpc; |
| } |
| |
| as_fatal (_("Neither Power nor PowerPC opcodes were selected.")); |
| return bfd_arch_unknown; |
| } |
| |
| unsigned long |
| ppc_mach () |
| { |
| return (ppc_size == PPC_OPCODE_64) ? 620 : 0; |
| } |
| |
| int |
| ppc_subseg_align() |
| { |
| return (ppc_xcoff64) ? 3 : 2; |
| } |
| |
| extern char* |
| ppc_target_format() |
| { |
| #ifdef OBJ_COFF |
| #ifdef TE_PE |
| return (target_big_endian ? "pe-powerpc" : "pe-powerpcle"); |
| #elif TE_POWERMAC |
| #else |
| return (ppc_xcoff64 ? "aixcoff64-rs6000" : "aixcoff-rs6000"); |
| #endif |
| #ifdef TE_POWERMAC |
| return "xcoff-powermac"; |
| #endif |
| #endif |
| #ifdef OBJ_ELF |
| return (target_big_endian ? "elf32-powerpc" : "elf32-powerpcle"); |
| #endif |
| } |
| |
| /* 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 () |
| { |
| register const struct powerpc_opcode *op; |
| const struct powerpc_opcode *op_end; |
| const struct powerpc_macro *macro; |
| const struct powerpc_macro *macro_end; |
| boolean dup_insn = false; |
| |
| ppc_set_cpu (); |
| |
| #ifdef OBJ_ELF |
| /* Set the ELF flags if desired. */ |
| if (ppc_flags && !msolaris) |
| bfd_set_private_flags (stdoutput, ppc_flags); |
| #endif |
| |
| /* Insert the opcodes into a hash table. */ |
| ppc_hash = hash_new (); |
| |
| op_end = powerpc_opcodes + powerpc_num_opcodes; |
| for (op = powerpc_opcodes; op < op_end; op++) |
| { |
| know ((op->opcode & op->mask) == op->opcode); |
| |
| if ((op->flags & ppc_cpu) != 0 |
| && ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0 |
| || (op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == ppc_size |
| || (ppc_cpu & PPC_OPCODE_64_BRIDGE) != 0)) |
| { |
| const char *retval; |
| |
| retval = hash_insert (ppc_hash, op->name, (PTR) op); |
| if (retval != (const char *) NULL) |
| { |
| /* Ignore Power duplicates for -m601 */ |
| if ((ppc_cpu & PPC_OPCODE_601) != 0 |
| && (op->flags & PPC_OPCODE_POWER) != 0) |
| continue; |
| |
| as_bad (_("Internal assembler error for instruction %s"), op->name); |
| dup_insn = true; |
| } |
| } |
| } |
| |
| /* Insert the macros into a hash table. */ |
| ppc_macro_hash = hash_new (); |
| |
| macro_end = powerpc_macros + powerpc_num_macros; |
| for (macro = powerpc_macros; macro < macro_end; macro++) |
| { |
| if ((macro->flags & ppc_cpu) != 0) |
| { |
| const char *retval; |
| |
| retval = hash_insert (ppc_macro_hash, macro->name, (PTR) macro); |
| if (retval != (const char *) NULL) |
| { |
| as_bad (_("Internal assembler error for macro %s"), macro->name); |
| dup_insn = true; |
| } |
| } |
| } |
| |
| if (dup_insn) |
| abort (); |
| |
| /* Tell the main code what the endianness is if it is not overidden by the user. */ |
| if (!set_target_endian) |
| { |
| set_target_endian = 1; |
| target_big_endian = PPC_BIG_ENDIAN; |
| } |
| |
| #ifdef OBJ_XCOFF |
| ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG); |
| |
| /* Create dummy symbols to serve as initial csects. This forces the |
| text csects to precede the data csects. These symbols will not |
| be output. */ |
| ppc_text_csects = symbol_make ("dummy\001"); |
| symbol_get_tc (ppc_text_csects)->within = ppc_text_csects; |
| ppc_data_csects = symbol_make ("dummy\001"); |
| symbol_get_tc (ppc_data_csects)->within = ppc_data_csects; |
| #endif |
| |
| #ifdef TE_PE |
| |
| ppc_current_section = text_section; |
| ppc_previous_section = 0; |
| |
| #endif |
| } |
| |
| /* Insert an operand value into an instruction. */ |
| |
| static unsigned long |
| ppc_insert_operand (insn, operand, val, file, line) |
| unsigned long insn; |
| const struct powerpc_operand *operand; |
| offsetT val; |
| char *file; |
| unsigned int line; |
| { |
| if (operand->bits != 32) |
| { |
| long min, max; |
| offsetT test; |
| |
| if ((operand->flags & PPC_OPERAND_SIGNED) != 0) |
| { |
| if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0) |
| max = (1 << operand->bits) - 1; |
| else |
| max = (1 << (operand->bits - 1)) - 1; |
| min = - (1 << (operand->bits - 1)); |
| |
| if (ppc_size == PPC_OPCODE_32) |
| { |
| /* Some people write 32 bit hex constants with the sign |
| extension done by hand. 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. */ |
| if (val > 0 |
| && (val & (offsetT) 0x80000000) != 0 |
| && (val & (offsetT) 0xffffffff) == val) |
| { |
| val -= 0x80000000; |
| val -= 0x80000000; |
| } |
| } |
| } |
| else |
| { |
| max = (1 << operand->bits) - 1; |
| min = 0; |
| } |
| |
| if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0) |
| test = - val; |
| else |
| test = val; |
| |
| if (test < (offsetT) min || test > (offsetT) max) |
| { |
| const char *err = |
| _("operand out of range (%s not between %ld and %ld)"); |
| char buf[100]; |
| |
| sprint_value (buf, test); |
| if (file == (char *) NULL) |
| as_bad (err, buf, min, max); |
| else |
| as_bad_where (file, line, err, buf, min, max); |
| } |
| } |
| |
| if (operand->insert) |
| { |
| const char *errmsg; |
| |
| errmsg = NULL; |
| insn = (*operand->insert) (insn, (long) val, &errmsg); |
| if (errmsg != (const char *) NULL) |
| as_bad (errmsg); |
| } |
| else |
| insn |= (((long) val & ((1 << operand->bits) - 1)) |
| << operand->shift); |
| |
| return insn; |
| } |
| |
| |
| #ifdef OBJ_ELF |
| /* Parse @got, etc. and return the desired relocation. */ |
| static bfd_reloc_code_real_type |
| ppc_elf_suffix (str_p, exp_p) |
| char **str_p; |
| expressionS *exp_p; |
| { |
| struct map_bfd { |
| char *string; |
| int length; |
| bfd_reloc_code_real_type reloc; |
| }; |
| |
| char ident[20]; |
| char *str = *str_p; |
| char *str2; |
| int ch; |
| int len; |
| struct map_bfd *ptr; |
| |
| #define MAP(str,reloc) { str, sizeof (str)-1, reloc } |
| |
| static 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 ("fixup", BFD_RELOC_CTOR), /* warnings with -mrelocatable */ |
| MAP ("plt", BFD_RELOC_24_PLT_PCREL), |
| MAP ("pltrel24", BFD_RELOC_24_PLT_PCREL), |
| MAP ("copy", BFD_RELOC_PPC_COPY), |
| MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT), |
| MAP ("local24pc", BFD_RELOC_PPC_LOCAL24PC), |
| MAP ("local", BFD_RELOC_PPC_LOCAL24PC), |
| MAP ("pltrel", BFD_RELOC_32_PLT_PCREL), |
| MAP ("plt@l", BFD_RELOC_LO16_PLTOFF), |
| MAP ("plt@h", BFD_RELOC_HI16_PLTOFF), |
| MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF), |
| MAP ("sdarel", BFD_RELOC_GPREL16), |
| MAP ("sectoff", BFD_RELOC_32_BASEREL), |
| MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL), |
| MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL), |
| MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL), |
| MAP ("naddr", BFD_RELOC_PPC_EMB_NADDR32), |
| MAP ("naddr16", BFD_RELOC_PPC_EMB_NADDR16), |
| MAP ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO), |
| MAP ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI), |
| MAP ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA), |
| MAP ("sdai16", BFD_RELOC_PPC_EMB_SDAI16), |
| MAP ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL), |
| MAP ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16), |
| MAP ("sda21", BFD_RELOC_PPC_EMB_SDA21), |
| MAP ("mrkref", BFD_RELOC_PPC_EMB_MRKREF), |
| MAP ("relsect", BFD_RELOC_PPC_EMB_RELSEC16), |
| MAP ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO), |
| MAP ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI), |
| MAP ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA), |
| MAP ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD), |
| MAP ("relsda", BFD_RELOC_PPC_EMB_RELSDA), |
| MAP ("xgot", BFD_RELOC_PPC_TOC16), |
| |
| { (char *)0, 0, BFD_RELOC_UNUSED } |
| }; |
| |
| if (*str++ != '@') |
| return BFD_RELOC_UNUSED; |
| |
| for (ch = *str, str2 = ident; |
| (str2 < ident + sizeof (ident) - 1 |
| && (isalnum (ch) || ch == '@')); |
| ch = *++str) |
| { |
| *str2++ = (islower (ch)) ? ch : 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) |
| { |
| if (exp_p->X_add_number != 0 |
| && (ptr->reloc == BFD_RELOC_16_GOTOFF |
| || ptr->reloc == BFD_RELOC_LO16_GOTOFF |
| || ptr->reloc == BFD_RELOC_HI16_GOTOFF |
| || ptr->reloc == BFD_RELOC_HI16_S_GOTOFF)) |
| as_warn (_("identifier+constant@got means identifier@got+constant")); |
| |
| /* Now check for identifier@suffix+constant */ |
| if (*str == '-' || *str == '+') |
| { |
| char *orig_line = input_line_pointer; |
| expressionS new_exp; |
| |
| input_line_pointer = str; |
| expression (&new_exp); |
| if (new_exp.X_op == O_constant) |
| { |
| exp_p->X_add_number += new_exp.X_add_number; |
| str = input_line_pointer; |
| } |
| |
| if (&input_line_pointer != str_p) |
| input_line_pointer = orig_line; |
| } |
| |
| *str_p = str; |
| return ptr->reloc; |
| } |
| |
| return BFD_RELOC_UNUSED; |
| } |
| |
| /* Like normal .long/.short/.word, except support @got, etc. */ |
| /* clobbers input_line_pointer, checks */ |
| /* end-of-line. */ |
| static void |
| ppc_elf_cons (nbytes) |
| register int nbytes; /* 1=.byte, 2=.word, 4=.long */ |
| { |
| expressionS exp; |
| bfd_reloc_code_real_type reloc; |
| |
| if (is_it_end_of_statement ()) |
| { |
| demand_empty_rest_of_line (); |
| return; |
| } |
| |
| do |
| { |
| expression (&exp); |
| if (exp.X_op == O_symbol |
| && *input_line_pointer == '@' |
| && (reloc = ppc_elf_suffix (&input_line_pointer, &exp)) != BFD_RELOC_UNUSED) |
| { |
| reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc); |
| int size = bfd_get_reloc_size (reloc_howto); |
| |
| if (size > nbytes) |
| as_bad (_("%s relocations do not fit in %d bytes\n"), reloc_howto->name, nbytes); |
| |
| else |
| { |
| register char *p = frag_more ((int) nbytes); |
| int offset = nbytes - size; |
| |
| fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, &exp, 0, reloc); |
| } |
| } |
| else |
| emit_expr (&exp, (unsigned int) nbytes); |
| } |
| while (*input_line_pointer++ == ','); |
| |
| input_line_pointer--; /* Put terminator back into stream. */ |
| demand_empty_rest_of_line (); |
| } |
| |
| /* Solaris pseduo op to change to the .rodata section. */ |
| static void |
| ppc_elf_rdata (xxx) |
| 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(xxx) |
| int xxx ATTRIBUTE_UNUSED; |
| { |
| register char *name; |
| register char c; |
| register char *p; |
| offsetT size; |
| register symbolS *symbolP; |
| offsetT align; |
| segT old_sec; |
| int old_subsec; |
| char *pfrag; |
| int align2; |
| |
| name = input_line_pointer; |
| c = get_symbol_end (); |
| |
| /* just after name is now '\0' */ |
| p = input_line_pointer; |
| *p = c; |
| SKIP_WHITESPACE (); |
| if (*input_line_pointer != ',') |
| { |
| as_bad (_("Expected comma after symbol-name: rest of line ignored.")); |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| input_line_pointer++; /* skip ',' */ |
| if ((size = get_absolute_expression ()) < 0) |
| { |
| as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size); |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| /* The third argument to .lcomm is the alignment. */ |
| if (*input_line_pointer != ',') |
| align = 8; |
| else |
| { |
| ++input_line_pointer; |
| align = get_absolute_expression (); |
| if (align <= 0) |
| { |
| as_warn (_("ignoring bad alignment")); |
| align = 8; |
| } |
| } |
| |
| *p = 0; |
| symbolP = symbol_find_or_make (name); |
| *p = c; |
| |
| if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP)) |
| { |
| as_bad (_("Ignoring attempt to re-define symbol `%s'."), |
| S_GET_NAME (symbolP)); |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size) |
| { |
| as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."), |
| S_GET_NAME (symbolP), |
| (long) S_GET_VALUE (symbolP), |
| (long) size); |
| |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| /* allocate_bss: */ |
| old_sec = now_seg; |
| old_subsec = now_subseg; |
| if (align) |
| { |
| /* convert to a power of 2 alignment */ |
| for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2); |
| if (align != 1) |
| { |
| as_bad (_("Common alignment not a power of 2")); |
| ignore_rest_of_line (); |
| return; |
| } |
| } |
| else |
| align2 = 0; |
| |
| record_alignment (bss_section, align2); |
| subseg_set (bss_section, 0); |
| if (align2) |
| frag_align (align2, 0, 0); |
| if (S_GET_SEGMENT (symbolP) == bss_section) |
| symbol_get_frag (symbolP)->fr_symbol = 0; |
| symbol_set_frag (symbolP, frag_now); |
| pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size, |
| (char *) 0); |
| *pfrag = 0; |
| S_SET_SIZE (symbolP, size); |
| S_SET_SEGMENT (symbolP, bss_section); |
| subseg_set (old_sec, old_subsec); |
| demand_empty_rest_of_line (); |
| } |
| |
| /* Validate any relocations emitted for -mrelocatable, possibly adding |
| fixups for word relocations in writable segments, so we can adjust |
| them at runtime. */ |
| static void |
| ppc_elf_validate_fix (fixp, seg) |
| fixS *fixp; |
| segT seg; |
| { |
| if (fixp->fx_done || fixp->fx_pcrel) |
| return; |
| |
| switch (shlib) |
| { |
| case SHLIB_NONE: |
| case SHLIB_PIC: |
| return; |
| |
| case SHLIB_MRELOCATABLE: |
| if (fixp->fx_r_type <= BFD_RELOC_UNUSED |
| && fixp->fx_r_type != BFD_RELOC_16_GOTOFF |
| && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF |
| && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF |
| && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF |
| && fixp->fx_r_type != BFD_RELOC_32_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 |
| && 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), ".stab") != 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; |
| } |
| } |
| #endif /* OBJ_ELF */ |
| |
| #ifdef TE_PE |
| |
| /* |
| * Summary of parse_toc_entry(). |
| * |
| * in: Input_line_pointer points to the '[' in one of: |
| * |
| * [toc] [tocv] [toc32] [toc64] |
| * |
| * Anything else is an error of one kind or another. |
| * |
| * out: |
| * return value: success or failure |
| * toc_kind: kind of toc reference |
| * input_line_pointer: |
| * success: first char after the ']' |
| * failure: unchanged |
| * |
| * settings: |
| * |
| * [toc] - rv == success, toc_kind = default_toc |
| * [tocv] - rv == success, toc_kind = data_in_toc |
| * [toc32] - rv == success, toc_kind = must_be_32 |
| * [toc64] - rv == success, toc_kind = must_be_64 |
| * |
| */ |
| |
| enum toc_size_qualifier |
| { |
| default_toc, /* The toc cell constructed should be the system default size */ |
| data_in_toc, /* This is a direct reference to a toc cell */ |
| must_be_32, /* The toc cell constructed must be 32 bits wide */ |
| must_be_64 /* The toc cell constructed must be 64 bits wide */ |
| }; |
| |
| static int |
| parse_toc_entry(toc_kind) |
| enum toc_size_qualifier *toc_kind; |
| { |
| char *start; |
| char *toc_spec; |
| char c; |
| enum toc_size_qualifier t; |
| |
| /* save the input_line_pointer */ |
| start = input_line_pointer; |
| |
| /* skip over the '[' , and whitespace */ |
| ++input_line_pointer; |
| SKIP_WHITESPACE (); |
| |
| /* find the spelling of the operand */ |
| toc_spec = input_line_pointer; |
| c = get_symbol_end (); |
| |
| if (strcmp(toc_spec, "toc") == 0) |
| { |
| t = default_toc; |
| } |
| else if (strcmp(toc_spec, "tocv") == 0) |
| { |
| t = data_in_toc; |
| } |
| else if (strcmp(toc_spec, "toc32") == 0) |
| { |
| t = must_be_32; |
| } |
| else if (strcmp(toc_spec, "toc64") == 0) |
| { |
| t = must_be_64; |
| } |
| else |
| { |
| as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec); |
| *input_line_pointer = c; /* put back the delimiting char */ |
| input_line_pointer = start; /* reset input_line pointer */ |
| return 0; |
| } |
| |
| /* now find the ']' */ |
| *input_line_pointer = c; /* put back the delimiting char */ |
| |
| SKIP_WHITESPACE (); /* leading whitespace could be there. */ |
| c = *input_line_pointer++; /* input_line_pointer->past char in c. */ |
| |
| if (c != ']') |
| { |
| as_bad (_("syntax error: expected `]', found `%c'"), c); |
| input_line_pointer = start; /* reset input_line pointer */ |
| return 0; |
| } |
| |
| *toc_kind = t; /* set return value */ |
| return 1; |
| } |
| #endif |
| |
| |
| /* We need to keep a list of fixups. We can't simply generate them as |
| we go, because that would require us to first create the frag, and |
| that would screw up references to ``.''. */ |
| |
| struct ppc_fixup |
| { |
| expressionS exp; |
| int opindex; |
| bfd_reloc_code_real_type reloc; |
| }; |
| |
| #define MAX_INSN_FIXUPS (5) |
| |
| /* This routine is called for each instruction to be assembled. */ |
| |
| void |
| md_assemble (str) |
| char *str; |
| { |
| char *s; |
| const struct powerpc_opcode *opcode; |
| unsigned long insn; |
| const unsigned char *opindex_ptr; |
| int skip_optional; |
| int need_paren; |
| int next_opindex; |
| struct ppc_fixup fixups[MAX_INSN_FIXUPS]; |
| int fc; |
| char *f; |
| int i; |
| #ifdef OBJ_ELF |
| bfd_reloc_code_real_type reloc; |
| #endif |
| |
| /* Get the opcode. */ |
| for (s = str; *s != '\0' && ! isspace (*s); s++) |
| ; |
| if (*s != '\0') |
| *s++ = '\0'; |
| |
| /* Look up the opcode in the hash table. */ |
| opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str); |
| if (opcode == (const struct powerpc_opcode *) NULL) |
| { |
| const struct powerpc_macro *macro; |
| |
| macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str); |
| if (macro == (const struct powerpc_macro *) NULL) |
| as_bad (_("Unrecognized opcode: `%s'"), str); |
| else |
| ppc_macro (s, macro); |
| |
| return; |
| } |
| |
| insn = opcode->opcode; |
| |
| str = s; |
| while (isspace (*str)) |
| ++str; |
| |
| /* PowerPC operands are just expressions. The only real issue is |
| that a few operand types are optional. All cases which might use |
| an optional operand separate the operands only with commas (in |
| some cases parentheses are used, as in ``lwz 1,0(1)'' but such |
| cases never have optional operands). There is never more than |
| one optional operand for an instruction. So, before we start |
| seriously parsing the operands, we check to see if we have an |
| optional operand, and, if we do, we count the number of commas to |
| see whether the operand should be omitted. */ |
| skip_optional = 0; |
| for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) |
| { |
| const struct powerpc_operand *operand; |
| |
| operand = &powerpc_operands[*opindex_ptr]; |
| if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) |
| { |
| unsigned int opcount; |
| |
| /* There is an optional operand. Count the number of |
| commas in the input line. */ |
| if (*str == '\0') |
| opcount = 0; |
| else |
| { |
| opcount = 1; |
| s = str; |
| while ((s = strchr (s, ',')) != (char *) NULL) |
| { |
| ++opcount; |
| ++s; |
| } |
| } |
| |
| /* If there are fewer operands in the line then are called |
| for by the instruction, we want to skip the optional |
| operand. */ |
| if (opcount < strlen (opcode->operands)) |
| skip_optional = 1; |
| |
| break; |
| } |
| } |
| |
| /* Gather the operands. */ |
| need_paren = 0; |
| next_opindex = 0; |
| fc = 0; |
| for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) |
| { |
| const struct powerpc_operand *operand; |
| const char *errmsg; |
| char *hold; |
| expressionS ex; |
| char endc; |
| |
| if (next_opindex == 0) |
| operand = &powerpc_operands[*opindex_ptr]; |
| else |
| { |
| operand = &powerpc_operands[next_opindex]; |
| next_opindex = 0; |
| } |
| |
| errmsg = NULL; |
| |
| /* If this is a fake operand, then we do not expect anything |
| from the input. */ |
| if ((operand->flags & PPC_OPERAND_FAKE) != 0) |
| { |
| insn = (*operand->insert) (insn, 0L, &errmsg); |
| if (errmsg != (const char *) NULL) |
| as_bad (errmsg); |
| continue; |
| } |
| |
| /* If this is an optional operand, and we are skipping it, just |
| insert a zero. */ |
| if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 |
| && skip_optional) |
| { |
| if (operand->insert) |
| { |
| insn = (*operand->insert) (insn, 0L, &errmsg); |
| if (errmsg != (const char *) NULL) |
| as_bad (errmsg); |
| } |
| if ((operand->flags & PPC_OPERAND_NEXT) != 0) |
| next_opindex = *opindex_ptr + 1; |
| continue; |
| } |
| |
| /* Gather the operand. */ |
| hold = input_line_pointer; |
| input_line_pointer = str; |
| |
| #ifdef TE_PE |
| if (*input_line_pointer == '[') |
| { |
| /* We are expecting something like the second argument here: |
| |
| lwz r4,[toc].GS.0.static_int(rtoc) |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| The argument following the `]' must be a symbol name, and the |
| register must be the toc register: 'rtoc' or '2' |
| |
| The effect is to 0 as the displacement field |
| in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or |
| the appropriate variation) reloc against it based on the symbol. |
| The linker will build the toc, and insert the resolved toc offset. |
| |
| Note: |
| o The size of the toc entry is currently assumed to be |
| 32 bits. This should not be assumed to be a hard coded |
| number. |
| o In an effort to cope with a change from 32 to 64 bits, |
| there are also toc entries that are specified to be |
| either 32 or 64 bits: |
| lwz r4,[toc32].GS.0.static_int(rtoc) |
| lwz r4,[toc64].GS.0.static_int(rtoc) |
| These demand toc entries of the specified size, and the |
| instruction probably requires it. |
| */ |
| |
| int valid_toc; |
| enum toc_size_qualifier toc_kind; |
| bfd_reloc_code_real_type toc_reloc; |
| |
| /* go parse off the [tocXX] part */ |
| valid_toc = parse_toc_entry(&toc_kind); |
| |
| if (!valid_toc) |
| { |
| /* Note: message has already been issued. */ |
| /* FIXME: what sort of recovery should we do? */ |
| /* demand_rest_of_line(); return; ? */ |
| } |
| |
| /* Now get the symbol following the ']' */ |
| expression(&ex); |
| |
| switch (toc_kind) |
| { |
| case default_toc: |
| /* In this case, we may not have seen the symbol yet, since */ |
| /* it is allowed to appear on a .extern or .globl or just be */ |
| /* a label in the .data section. */ |
| toc_reloc = BFD_RELOC_PPC_TOC16; |
| break; |
| case data_in_toc: |
| /* 1. The symbol must be defined and either in the toc */ |
| /* section, or a global. */ |
| /* 2. The reloc generated must have the TOCDEFN flag set in */ |
| /* upper bit mess of the reloc type. */ |
| /* FIXME: It's a little confusing what the tocv qualifier can */ |
| /* be used for. At the very least, I've seen three */ |
| /* uses, only one of which I'm sure I can explain. */ |
| if (ex.X_op == O_symbol) |
| { |
| assert (ex.X_add_symbol != NULL); |
| if (symbol_get_bfdsym (ex.X_add_symbol)->section |
| != tocdata_section) |
| { |
| as_bad(_("[tocv] symbol is not a toc symbol")); |
| } |
| } |
| |
| toc_reloc = BFD_RELOC_PPC_TOC16; |
| break; |
| case must_be_32: |
| /* FIXME: these next two specifically specify 32/64 bit toc */ |
| /* entries. We don't support them today. Is this the */ |
| /* right way to say that? */ |
| toc_reloc = BFD_RELOC_UNUSED; |
| as_bad (_("Unimplemented toc32 expression modifier")); |
| break; |
| case must_be_64: |
| /* FIXME: see above */ |
| toc_reloc = BFD_RELOC_UNUSED; |
| as_bad (_("Unimplemented toc64 expression modifier")); |
| break; |
| default: |
| fprintf (stderr, |
| _("Unexpected return value [%d] from parse_toc_entry!\n"), |
| toc_kind); |
| abort (); |
| break; |
| } |
| |
| /* We need to generate a fixup for this expression. */ |
| if (fc >= MAX_INSN_FIXUPS) |
| as_fatal (_("too many fixups")); |
| |
| fixups[fc].reloc = toc_reloc; |
| fixups[fc].exp = ex; |
| fixups[fc].opindex = *opindex_ptr; |
| ++fc; |
| |
| /* Ok. We've set up the fixup for the instruction. Now make it |
| look like the constant 0 was found here */ |
| ex.X_unsigned = 1; |
| ex.X_op = O_constant; |
| ex.X_add_number = 0; |
| ex.X_add_symbol = NULL; |
| ex.X_op_symbol = NULL; |
| } |
| |
| else |
| #endif /* TE_PE */ |
| { |
| if (! register_name (&ex)) |
| { |
| if ((operand->flags & PPC_OPERAND_CR) != 0) |
| cr_operand = true; |
| expression (&ex); |
| cr_operand = false; |
| } |
| } |
| |
| str = input_line_pointer; |
| input_line_pointer = hold; |
| |
| if (ex.X_op == O_illegal) |
| as_bad (_("illegal operand")); |
| else if (ex.X_op == O_absent) |
| as_bad (_("missing operand")); |
| else if (ex.X_op == O_register) |
| { |
| insn = ppc_insert_operand (insn, operand, ex.X_add_number, |
| (char *) NULL, 0); |
| } |
| else if (ex.X_op == O_constant) |
| { |
| #ifdef OBJ_ELF |
| /* Allow @HA, @L, @H on constants. */ |
| char *orig_str = str; |
| |
| if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) |
| switch (reloc) |
| { |
| default: |
| str = orig_str; |
| break; |
| |
| case BFD_RELOC_LO16: |
| /* X_unsigned is the default, so if the user has done |
| something which cleared it, we always produce a |
| signed value. */ |
| if (ex.X_unsigned |
| && (operand->flags & PPC_OPERAND_SIGNED) == 0) |
| ex.X_add_number &= 0xffff; |
| else |
| ex.X_add_number = (((ex.X_add_number & 0xffff) |
| ^ 0x8000) |
| - 0x8000); |
| break; |
| |
| case BFD_RELOC_HI16: |
| ex.X_add_number = (ex.X_add_number >> 16) & 0xffff; |
| break; |
| |
| case BFD_RELOC_HI16_S: |
| ex.X_add_number = ((((ex.X_add_number >> 16) & 0xffff) |
| + ((ex.X_add_number >> 15) & 1)) |
| & 0xffff); |
| break; |
| } |
| #endif |
| insn = ppc_insert_operand (insn, operand, ex.X_add_number, |
| (char *) NULL, 0); |
| } |
| #ifdef OBJ_ELF |
| else if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) |
| { |
| /* For the absoulte forms of branchs, convert the PC relative form back into |
| the absolute. */ |
| if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) |
| { |
| switch (reloc) |
| { |
| case BFD_RELOC_PPC_B26: |
| reloc = BFD_RELOC_PPC_BA26; |
| break; |
| case BFD_RELOC_PPC_B16: |
| reloc = BFD_RELOC_PPC_BA16; |
| break; |
| case BFD_RELOC_PPC_B16_BRTAKEN: |
| reloc = BFD_RELOC_PPC_BA16_BRTAKEN; |
| break; |
| case BFD_RELOC_PPC_B16_BRNTAKEN: |
| reloc = BFD_RELOC_PPC_BA16_BRNTAKEN; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* We need to generate a fixup for this expression. */ |
| if (fc >= MAX_INSN_FIXUPS) |
| as_fatal (_("too many fixups")); |
| fixups[fc].exp = ex; |
| fixups[fc].opindex = 0; |
| fixups[fc].reloc = reloc; |
| ++fc; |
| } |
| #endif /* OBJ_ELF */ |
| |
| else |
| { |
| /* We need to generate a fixup for this expression. */ |
| if (fc >= MAX_INSN_FIXUPS) |
| as_fatal (_("too many fixups")); |
| fixups[fc].exp = ex; |
| fixups[fc].opindex = *opindex_ptr; |
| fixups[fc].reloc = BFD_RELOC_UNUSED; |
| ++fc; |
| } |
| |
| if (need_paren) |
| { |
| endc = ')'; |
| need_paren = 0; |
| } |
| else if ((operand->flags & PPC_OPERAND_PARENS) != 0) |
| { |
| endc = '('; |
| need_paren = 1; |
| } |
| else |
| endc = ','; |
| |
| /* The call to expression should have advanced str past any |
| whitespace. */ |
| if (*str != endc |
| && (endc != ',' || *str != '\0')) |
| { |
| as_bad (_("syntax error; found `%c' but expected `%c'"), *str, endc); |
| break; |
| } |
| |
| if (*str != '\0') |
| ++str; |
| } |
| |
| while (isspace (*str)) |
| ++str; |
| |
| if (*str != '\0') |
| as_bad (_("junk at end of line: `%s'"), str); |
| |
| /* Write out the instruction. */ |
| f = frag_more (4); |
| md_number_to_chars (f, insn, 4); |
| |
| #ifdef OBJ_ELF |
| dwarf2_emit_insn (4); |
| #endif |
| |
| /* Create any fixups. At this point we do not use a |
| bfd_reloc_code_real_type, but instead just use the |
| BFD_RELOC_UNUSED plus the operand index. This lets us easily |
| handle fixups for any operand type, although that is admittedly |
| not a very exciting feature. We pick a BFD reloc type in |
| md_apply_fix. */ |
| for (i = 0; i < fc; i++) |
| { |
| const struct powerpc_operand *operand; |
| |
| operand = &powerpc_operands[fixups[i].opindex]; |
| if (fixups[i].reloc != BFD_RELOC_UNUSED) |
| { |
| reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc); |
| int size; |
| int offset; |
| fixS *fixP; |
| |
| if (!reloc_howto) |
| abort (); |
| |
| size = bfd_get_reloc_size (reloc_howto); |
| offset = target_big_endian ? (4 - size) : 0; |
| |
| if (size < 1 || size > 4) |
| abort (); |
| |
| fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset, size, |
| &fixups[i].exp, reloc_howto->pc_relative, |
| fixups[i].reloc); |
| |
| /* Turn off complaints that the addend is too large for things like |
| foo+100000@ha. */ |
| switch (fixups[i].reloc) |
| { |
| case BFD_RELOC_16_GOTOFF: |
| case BFD_RELOC_PPC_TOC16: |
| case BFD_RELOC_LO16: |
| case BFD_RELOC_HI16: |
| case BFD_RELOC_HI16_S: |
| fixP->fx_no_overflow = 1; |
| break; |
| default: |
| break; |
| } |
| } |
| else |
| fix_new_exp (frag_now, f - frag_now->fr_literal, 4, |
| &fixups[i].exp, |
| (operand->flags & PPC_OPERAND_RELATIVE) != 0, |
| ((bfd_reloc_code_real_type) |
| (fixups[i].opindex + (int) BFD_RELOC_UNUSED))); |
| } |
| } |
| |
| /* Handle a macro. Gather all the operands, transform them as |
| described by the macro, and call md_assemble recursively. All the |
| operands are separated by commas; we don't accept parentheses |
| around operands here. */ |
| |
| static void |
| ppc_macro (str, macro) |
| char *str; |
| const struct powerpc_macro *macro; |
| { |
| char *operands[10]; |
| unsigned int count; |
| char *s; |
| unsigned int len; |
| const char *format; |
| int arg; |
| char *send; |
| char *complete; |
| |
| /* Gather the users operands into the operands array. */ |
| count = 0; |
| s = str; |
| while (1) |
| { |
| if (count >= sizeof operands / sizeof operands[0]) |
| break; |
| operands[count++] = s; |
| s = strchr (s, ','); |
| if (s == (char *) NULL) |
| break; |
| *s++ = '\0'; |
| } |
| |
| if (count != macro->operands) |
| { |
| as_bad (_("wrong number of operands")); |
| return; |
| } |
| |
| /* Work out how large the string must be (the size is unbounded |
| because it includes user input). */ |
| len = 0; |
| format = macro->format; |
| while (*format != '\0') |
| { |
| if (*format != '%') |
| { |
| ++len; |
| ++format; |
| } |
| else |
| { |
| arg = strtol (format + 1, &send, 10); |
| know (send != format && arg >= 0 && arg < count); |
| len += strlen (operands[arg]); |
| format = send; |
| } |
| } |
| |
| /* Put the string together. */ |
| complete = s = (char *) alloca (len + 1); |
| format = macro->format; |
| while (*format != '\0') |
| { |
| if (*format != '%') |
| *s++ = *format++; |
| else |
| { |
| arg = strtol (format + 1, &send, 10); |
| strcpy (s, operands[arg]); |
| s += strlen (s); |
| format = send; |
| } |
| } |
| *s = '\0'; |
| |
| /* Assemble the constructed instruction. */ |
| md_assemble (complete); |
| } |
| |
| #ifdef OBJ_ELF |
| /* For ELF, add support for SHF_EXCLUDE and SHT_ORDERED */ |
| |
| int |
| ppc_section_letter (letter, ptr_msg) |
| int letter; |
| char **ptr_msg; |
| { |
| if (letter == 'e') |
| return SHF_EXCLUDE; |
| |
| *ptr_msg = _("Bad .section directive: want a,w,x,e in string"); |
| return 0; |
| } |
| |
| int |
| ppc_section_word (str, len) |
| char *str; |
| size_t len; |
| { |
| if (len == 7 && strncmp (str, "exclude", 7) == 0) |
| return SHF_EXCLUDE; |
| |
| return -1; |
| } |
| |
| int |
| ppc_section_type (str, len) |
| char *str; |
| size_t len; |
| { |
| if (len == 7 && strncmp (str, "ordered", 7) == 0) |
| return SHT_ORDERED; |
| |
| return -1; |
| } |
| |
| int |
| ppc_section_flags (flags, attr, type) |
| int flags; |
| int attr; |
| int type; |
| { |
| if (type == SHT_ORDERED) |
| flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES; |
| |
| if (attr & SHF_EXCLUDE) |
| flags |= SEC_EXCLUDE; |
| |
| return flags; |
| } |
| #endif /* OBJ_ELF */ |
| |
| |
| /* Pseudo-op handling. */ |
| |
| /* The .byte pseudo-op. This is similar to the normal .byte |
| pseudo-op, but it can also take a single ASCII string. */ |
| |
| static void |
| ppc_byte (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| if (*input_line_pointer != '\"') |
| { |
| cons (1); |
| return; |
| } |
| |
| /* Gather characters. A real double quote is doubled. Unusual |
| characters are not permitted. */ |
| ++input_line_pointer; |
| while (1) |
| { |
| char c; |
| |
| c = *input_line_pointer++; |
| |
| if (c == '\"') |
| { |
| if (*input_line_pointer != '\"') |
| break; |
| ++input_line_pointer; |
| } |
| |
| FRAG_APPEND_1_CHAR (c); |
| } |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| #ifdef OBJ_XCOFF |
| |
| /* XCOFF specific pseudo-op handling. */ |
| |
| /* This is set if we are creating a .stabx symbol, since we don't want |
| to handle symbol suffixes for such symbols. */ |
| static boolean ppc_stab_symbol; |
| |
| /* The .comm and .lcomm pseudo-ops for XCOFF. XCOFF puts common |
| symbols in the .bss segment as though they were local common |
| symbols, and uses a different smclas. The native Aix 4.3.3 assember |
| aligns .comm and .lcomm to 4 bytes. */ |
| |
| static void |
| ppc_comm (lcomm) |
| int lcomm; |
| { |
| asection *current_seg = now_seg; |
| subsegT current_subseg = now_subseg; |
| char *name; |
| char endc; |
| char *end_name; |
| offsetT size; |
| offsetT align; |
| symbolS *lcomm_sym = NULL; |
| symbolS *sym; |
| char *pfrag; |
| |
| name = input_line_pointer; |
| endc = get_symbol_end (); |
| end_name = input_line_pointer; |
| *end_name = endc; |
| |
| if (*input_line_pointer != ',') |
| { |
| as_bad (_("missing size")); |
| ignore_rest_of_line (); |
| return; |
| } |
| ++input_line_pointer; |
| |
| size = get_absolute_expression (); |
| if (size < 0) |
| { |
| as_bad (_("negative size")); |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| if (! lcomm) |
| { |
| /* The third argument to .comm is the alignment. */ |
| if (*input_line_pointer != ',') |
| align = 2; |
| else |
| { |
| ++input_line_pointer; |
| align = get_absolute_expression (); |
| if (align <= 0) |
| { |
| as_warn (_("ignoring bad alignment")); |
| align = 2; |
| } |
| } |
| } |
| else |
| { |
| char *lcomm_name; |
| char lcomm_endc; |
| |
| if (size <= 4) |
| align = 2; |
| else |
| align = 3; |
| |
| /* The third argument to .lcomm appears to be the real local |
| common symbol to create. References to the symbol named in |
| the first argument are turned into references to the third |
| argument. */ |
| if (*input_line_pointer != ',') |
| { |
| as_bad (_("missing real symbol name")); |
| ignore_rest_of_line (); |
| return; |
| } |
| ++input_line_pointer; |
| |
| lcomm_name = input_line_pointer; |
| lcomm_endc = get_symbol_end (); |
| |
| lcomm_sym = symbol_find_or_make (lcomm_name); |
| |
| *input_line_pointer = lcomm_endc; |
| } |
| |
| *end_name = '\0'; |
| sym = symbol_find_or_make (name); |
| *end_name = endc; |
| |
| if (S_IS_DEFINED (sym) |
| || S_GET_VALUE (sym) != 0) |
| { |
| as_bad (_("attempt to redefine symbol")); |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| record_alignment (bss_section, align); |
| |
| if (! lcomm |
| || ! S_IS_DEFINED (lcomm_sym)) |
| { |
| symbolS *def_sym; |
| offsetT def_size; |
| |
| if (! lcomm) |
| { |
| def_sym = sym; |
| def_size = size; |
| S_SET_EXTERNAL (sym); |
| } |
| else |
| { |
| symbol_get_tc (lcomm_sym)->output = 1; |
| def_sym = lcomm_sym; |
| def_size = 0; |
| } |
| |
| subseg_set (bss_section, 1); |
| frag_align (align, 0, 0); |
| |
| symbol_set_frag (def_sym, frag_now); |
| pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym, |
| def_size, (char *) NULL); |
| *pfrag = 0; |
| S_SET_SEGMENT (def_sym, bss_section); |
| symbol_get_tc (def_sym)->align = align; |
| } |
| else if (lcomm) |
| { |
| /* Align the size of lcomm_sym. */ |
| symbol_get_frag (lcomm_sym)->fr_offset = |
| ((symbol_get_frag (lcomm_sym)->fr_offset + (1 << align) - 1) |
| &~ ((1 << align) - 1)); |
| if (align > symbol_get_tc (lcomm_sym)->align) |
| symbol_get_tc (lcomm_sym)->align = align; |
| } |
| |
| if (lcomm) |
| { |
| /* Make sym an offset from lcomm_sym. */ |
| S_SET_SEGMENT (sym, bss_section); |
| symbol_set_frag (sym, symbol_get_frag (lcomm_sym)); |
| S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset); |
| symbol_get_frag (lcomm_sym)->fr_offset += size; |
| } |
| |
| subseg_set (current_seg, current_subseg); |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* The .csect pseudo-op. This switches us into a different |
| subsegment. The first argument is a symbol whose value is the |
| start of the .csect. In COFF, csect symbols get special aux |
| entries defined by the x_csect field of union internal_auxent. The |
| optional second argument is the alignment (the default is 2). */ |
| |
| static void |
| ppc_csect (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| char *name; |
| char endc; |
| symbolS *sym; |
| |
| name = input_line_pointer; |
| endc = get_symbol_end (); |
| |
| sym = symbol_find_or_make (name); |
| |
| *input_line_pointer = endc; |
| |
| if (S_GET_NAME (sym)[0] == '\0') |
| { |
| /* An unnamed csect is assumed to be [PR]. */ |
| symbol_get_tc (sym)->class = XMC_PR; |
| } |
| |
| ppc_change_csect (sym); |
| |
| if (*input_line_pointer == ',') |
| { |
| ++input_line_pointer; |
| symbol_get_tc (sym)->align = get_absolute_expression (); |
| } |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* Change to a different csect. */ |
| |
| static void |
| ppc_change_csect (sym) |
| symbolS *sym; |
| { |
| if (S_IS_DEFINED (sym)) |
| subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg); |
| else |
| { |
| symbolS **list_ptr; |
| int after_toc; |
| int hold_chunksize; |
| symbolS *list; |
| |
| /* This is a new csect. We need to look at the symbol class to |
| figure out whether it should go in the text section or the |
| data section. */ |
| after_toc = 0; |
| switch (symbol_get_tc (sym)->class) |
| { |
| case XMC_PR: |
| case XMC_RO: |
| case XMC_DB: |
| case XMC_GL: |
| case XMC_XO: |
| case XMC_SV: |
| case XMC_TI: |
| case XMC_TB: |
| S_SET_SEGMENT (sym, text_section); |
| symbol_get_tc (sym)->subseg = ppc_text_subsegment; |
| ++ppc_text_subsegment; |
| list_ptr = &ppc_text_csects; |
| break; |
| case XMC_RW: |
| case XMC_TC0: |
| case XMC_TC: |
| case XMC_DS: |
| case XMC_UA: |
| case XMC_BS: |
| case XMC_UC: |
| if (ppc_toc_csect != NULL |
| && (symbol_get_tc (ppc_toc_csect)->subseg + 1 |
| == ppc_data_subsegment)) |
| after_toc = 1; |
| S_SET_SEGMENT (sym, data_section); |
| symbol_get_tc (sym)->subseg = ppc_data_subsegment; |
| ++ppc_data_subsegment; |
| list_ptr = &ppc_data_csects; |
| break; |
| default: |
| abort (); |
| } |
| |
| /* We set the obstack chunk size to a small value before |
| changing subsegments, so that we don't use a lot of memory |
| space for what may be a small section. */ |
| hold_chunksize = chunksize; |
| chunksize = 64; |
| |
| subseg_new (segment_name (S_GET_SEGMENT (sym)), |
| symbol_get_tc (sym)->subseg); |
| |
| chunksize = hold_chunksize; |
| |
| if (after_toc) |
| ppc_after_toc_frag = frag_now; |
| |
| symbol_set_frag (sym, frag_now); |
| S_SET_VALUE (sym, (valueT) frag_now_fix ()); |
| |
| symbol_get_tc (sym)->align = (ppc_xcoff64) ? 3 : 2; |
| symbol_get_tc (sym)->output = 1; |
| symbol_get_tc (sym)->within = sym; |
| |
| for (list = *list_ptr; |
| symbol_get_tc (list)->next != (symbolS *) NULL; |
| list = symbol_get_tc (list)->next) |
| ; |
| symbol_get_tc (list)->next = sym; |
| |
| symbol_remove (sym, &symbol_rootP, &symbol_lastP); |
| symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP, |
| &symbol_lastP); |
| } |
| |
| ppc_current_csect = sym; |
| } |
| |
| /* This function handles the .text and .data pseudo-ops. These |
| pseudo-ops aren't really used by XCOFF; we implement them for the |
| convenience of people who aren't used to XCOFF. */ |
| |
| static void |
| ppc_section (type) |
| int type; |
| { |
| const char *name; |
| symbolS *sym; |
| |
| if (type == 't') |
| name = ".text[PR]"; |
| else if (type == 'd') |
| name = ".data[RW]"; |
| else |
| abort (); |
| |
| sym = symbol_find_or_make (name); |
| |
| ppc_change_csect (sym); |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* This function handles the .section pseudo-op. This is mostly to |
| give an error, since XCOFF only supports .text, .data and .bss, but |
| we do permit the user to name the text or data section. */ |
| |
| static void |
| ppc_named_section (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| char *user_name; |
| const char *real_name; |
| char c; |
| symbolS *sym; |
| |
| user_name = input_line_pointer; |
| c = get_symbol_end (); |
| |
| if (strcmp (user_name, ".text") == 0) |
| real_name = ".text[PR]"; |
| else if (strcmp (user_name, ".data") == 0) |
| real_name = ".data[RW]"; |
| else |
| { |
| as_bad (_("The XCOFF file format does not support arbitrary sections")); |
| *input_line_pointer = c; |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| *input_line_pointer = c; |
| |
| sym = symbol_find_or_make (real_name); |
| |
| ppc_change_csect (sym); |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* The .extern pseudo-op. We create an undefined symbol. */ |
| |
| static void |
| ppc_extern (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| char *name; |
| char endc; |
| |
| name = input_line_pointer; |
| endc = get_symbol_end (); |
| |
| (void) symbol_find_or_make (name); |
| |
| *input_line_pointer = endc; |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* The .lglobl pseudo-op. Keep the symbol in the symbol table. */ |
| |
| static void |
| ppc_lglobl (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| char *name; |
| char endc; |
| symbolS *sym; |
| |
| name = input_line_pointer; |
| endc = get_symbol_end (); |
| |
| sym = symbol_find_or_make (name); |
| |
| *input_line_pointer = endc; |
| |
| symbol_get_tc (sym)->output = 1; |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* The .rename pseudo-op. The RS/6000 assembler can rename symbols, |
| although I don't know why it bothers. */ |
| |
| static void |
| ppc_rename (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| char *name; |
| char endc; |
| symbolS *sym; |
| int len; |
| |
| name = input_line_pointer; |
| endc = get_symbol_end (); |
| |
| sym = symbol_find_or_make (name); |
| |
| *input_line_pointer = endc; |
| |
| if (*input_line_pointer != ',') |
| { |
| as_bad (_("missing rename string")); |
| ignore_rest_of_line (); |
| return; |
| } |
| ++input_line_pointer; |
| |
| symbol_get_tc (sym)->real_name = demand_copy_C_string (&len); |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| /* The .stabx pseudo-op. This is similar to a normal .stabs |
| pseudo-op, but slightly different. A sample is |
| .stabx "main:F-1",.main,142,0 |
| The first argument is the symbol name to create. The second is the |
| value, and the third is the storage class. The fourth seems to be |
| always zero, and I am assuming it is the type. */ |
| |
| static void |
| ppc_stabx (ignore) |
| int ignore ATTRIBUTE_UNUSED; |
| { |
| char *name; |
| int len; |
| symbolS *sym; |
| expressionS exp; |
| |
| name = demand_copy_C_string (&len); |
| |
| if (*input_line_pointer != ',') |
| { |
| as_bad (_("missing value")); |
| return; |
| } |
| ++input_line_pointer; |
| |
| ppc_stab_symbol = true; |
| sym = symbol_make (name); |
| ppc_stab_symbol = false; |
| |
| symbol_get_tc (sym)->real_name = name; |
| |
| (void) expression (&exp); |
| |
| switch (exp.X_op) |
| { |
| case O_illegal: |
| case O_absent: |
| case O_big: |
| as_bad (_("illegal .stabx expression; zero assumed")); |
| exp.X_add_number = 0; |
| /* Fall through. */ |
| case O_constant: |
| S_SET_VALUE (sym, (valueT) exp.X_add_number); |
| symbol_set_frag (sym, &zero_address_frag); |
| break; |
| |
| case O_symbol: |
| if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section) |
| symbol_set_value_expression (sym, &exp); |
| else |
| { |
| S_SET_VALUE (sym, |
| exp.X_add_number + S_GET_VALUE (exp.X_add_symbol)); |
| symbol_set_frag (sym, symbol_get_frag (exp.X_add_symbol)); |
| } |
| break; |
| |
| default: |
| /* The value is some complex expression. This will probably |
| fail at some later point, but this is probably the right |
| thing to do here. */ |
| symbol_set_value_expression (sym, &exp); |
| break; |
| } |
| |
| S_SET_SEGMENT (sym, ppc_coff_debug_sect
|