blob: ef708714a16b2f6f33724295565954ba070c8de4 [file] [log] [blame]
/* 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